From 9625dd96892267349fe7be5a0dd42bb77d773553 Mon Sep 17 00:00:00 2001 From: LongHairedHacker Date: Thu, 24 Nov 2016 18:02:53 +0100 Subject: [PATCH] Added more primitives for singnal processing Added python script to convert to images --- src/firfilter.rs | 44 +++++++++ src/main.rs | 227 ++++++++++++++++------------------------------ src/mixer.rs | 32 +++++++ src/resamplers.rs | 64 +++++++++++++ src/sinegen.rs | 30 ++++++ src/utils.rs | 18 ++++ to_image.py | 31 +++++++ 7 files changed, 299 insertions(+), 147 deletions(-) create mode 100644 src/firfilter.rs create mode 100644 src/mixer.rs create mode 100644 src/resamplers.rs create mode 100644 src/sinegen.rs create mode 100644 src/utils.rs create mode 100644 to_image.py diff --git a/src/firfilter.rs b/src/firfilter.rs new file mode 100644 index 0000000..4ec3fd3 --- /dev/null +++ b/src/firfilter.rs @@ -0,0 +1,44 @@ +pub struct FIRFilter<'a> { + coeffs: Vec, + state: Vec, + pos: usize, + iterator: Box + 'a> +} + +impl<'a> FIRFilter<'a> { + pub fn from(iterator: I, coeffs: Vec) -> FIRFilter<'a> where I: Iterator + 'a { + let mut state = Vec::new(); + for _ in 0..coeffs.len() { + state.push(0.0); + } + + FIRFilter { + coeffs: coeffs, + state: state, + pos: 0, + iterator: Box::new(iterator) + } + } +} + +impl<'a> Iterator for FIRFilter<'a> { + type Item = f32; + + fn next(&mut self) -> Option { + let cur = match self.iterator.next() { + Some(x) => x, + None => return None + }; + + self.pos = (self.pos + 1) % self.coeffs.len(); + self.state[self.pos] = cur; + + let mut result = 0.0; + for i in 0..self.coeffs.len() { + let pos = (self.pos + self.coeffs.len() - i) % self.coeffs.len(); + result += self.state[pos] * self.coeffs[i]; + }; + + Some(result) + } +} diff --git a/src/main.rs b/src/main.rs index 84d607a..402532b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,136 +1,20 @@ extern crate hound; -type FileReader = std::io::BufReader; - -fn float_sample_iterator<'a>(reader: &'a mut hound::WavReader) - -> Box + 'a> { - match reader.spec().sample_format { - hound::SampleFormat::Float => Box::new(reader.samples::().map(|x| x.unwrap())), - hound::SampleFormat::Int => match reader.spec().bits_per_sample { - 8 => Box::new(reader.samples::().map(|x| (x.unwrap() as f32) / (i16::max_value() as f32))), - 16 => Box::new(reader.samples::().map(|x| (x.unwrap() as f32) / (i16::max_value() as f32))), - 32 => Box::new(reader.samples::().map(|x| (x.unwrap() as f32) / (i32::max_value() as f32))), - _ => panic!("Unsupported sample rate") - } - } -} - -struct Upsampler<'a> { - factor: u16, - state: u16, - iterator: Box + 'a> -} - -impl<'a> Upsampler<'a> { - fn from(iterator: I, factor: u16) -> Upsampler<'a> where I: Iterator + 'a { - Upsampler { - factor: factor, - state: 0, - iterator: Box::new(iterator) - } - } -} - -impl<'a> Iterator for Upsampler<'a> { - type Item = f32; - - fn next(&mut self) -> Option { - let result = if self.state == 0 { - self.iterator.next() - } - else { - Some(0.0) - }; - self.state = (self.state + 1) % self.factor; - - return result; - } -} - - - -struct Downsampler<'a> { - factor: u16, - iterator: Box + 'a> -} - -impl<'a> Downsampler<'a> { - fn from(iterator: I, factor: u16) -> Downsampler<'a> where I: Iterator + 'a { - Downsampler { - factor: factor, - iterator: Box::new(iterator) - } - } -} - -impl<'a> Iterator for Downsampler<'a> { - type Item = f32; - - fn next(&mut self) -> Option { - let mut result = 0.0; - for _ in 0..self.factor { - match self.iterator.next() { - Some(x) => result += x, - None => return None - } - } - result /= self.factor as f32; - - return Some(result); - } -} - - - -struct FIRFilter<'a> { - coeffs: Vec, - state: Vec, - pos: usize, - iterator: Box + 'a> -} - -impl<'a> FIRFilter<'a> { - fn from(iterator: I, coeffs: Vec) -> FIRFilter<'a> where I: Iterator + 'a { - let mut state = Vec::new(); - for _ in 0..coeffs.len() { - state.push(0.0); - } - - FIRFilter { - coeffs: coeffs, - state: state, - pos: 0, - iterator: Box::new(iterator) - } - } -} - -impl<'a> Iterator for FIRFilter<'a> { - type Item = f32; - - fn next(&mut self) -> Option { - let cur = match self.iterator.next() { - Some(x) => x, - None => return None - }; - - self.pos = (self.pos + 1) % self.coeffs.len(); - self.state[self.pos] = cur; - - let mut result = 0.0; - for i in 0..self.coeffs.len() { - let pos = (self.pos + self.coeffs.len() - i) % self.coeffs.len(); - result += self.state[pos] * self.coeffs[i]; - }; - - Some(result) - } -} +mod utils; +mod sinegen; +mod firfilter; +mod resamplers; +mod mixer; +use utils::float_sample_iterator; +use sinegen::SineGenerator; +use firfilter::FIRFilter; +use mixer::Mixer; fn main() { - let carrier_freq = 2400; + let carrier_freq = 2400.0; + let sample_freq = 48000.0; let mut reader = match hound::WavReader::open("noaa19_short.wav") { Err(e) => panic!("Could not open inputfile: {}", e), @@ -146,26 +30,75 @@ fn main() { let samples = float_sample_iterator(&mut reader); - let coeffs = vec![1.73203081e-03, 3.68489420e-03, -1.61573864e-03, -4.83850760e-03, - 1.26938317e-03, 6.13073242e-03, -6.37488600e-04, -7.54064630e-03, - -3.41166003e-04, 9.04137653e-03, 1.73642240e-03, -1.06008349e-02, - -3.63238422e-03, 1.21827130e-02, 6.13805128e-03, -1.37477000e-02, - -9.40748113e-03, 1.52548738e-02, 1.36795184e-02, -1.66632054e-02, - -1.93616657e-02, 1.79331113e-02, 2.72262912e-02, -1.90279842e-02, - -3.89431985e-02, 1.99156328e-02, 5.88894574e-02, -2.05695633e-02, - -1.03195587e-01, 2.09700453e-02, 3.17333203e-01, 4.78895090e-01, - 3.17333203e-01, 2.09700453e-02, -1.03195587e-01, -2.05695633e-02, - 5.88894574e-02, 1.99156328e-02, -3.89431985e-02, -1.90279842e-02, - 2.72262912e-02, 1.79331113e-02, -1.93616657e-02, -1.66632054e-02, - 1.36795184e-02, 1.52548738e-02, -9.40748113e-03, -1.37477000e-02, - 6.13805128e-03, 1.21827130e-02, -3.63238422e-03, -1.06008349e-02, - 1.73642240e-03, 9.04137653e-03, -3.41166003e-04, -7.54064630e-03, - -6.37488600e-04, 6.13073242e-03, 1.26938317e-03, -4.83850760e-03, - -1.61573864e-03, 3.68489420e-03, 1.73203081e-03]; + let coeffs = vec![ -7.383784e-03, + -3.183046e-03, + 2.255039e-03, + 7.461166e-03, + 1.091908e-02, + 1.149109e-02, + 8.769802e-03, + 3.252932e-03, + -3.720606e-03, + -1.027446e-02, + -1.447403e-02, + -1.486427e-02, + -1.092423e-02, + -3.307958e-03, + 6.212477e-03, + 1.511364e-02, + 2.072873e-02, + 2.096037e-02, + 1.492345e-02, + 3.347624e-03, + -1.138407e-02, + -2.560252e-02, + -3.507114e-02, + -3.591225e-02, + -2.553830e-02, + -3.371569e-03, + 2.882645e-02, + 6.711368e-02, + 1.060042e-01, + 1.394643e-01, + 1.620650e-01, + 1.700462e-01, + 1.620650e-01, + 1.394643e-01, + 1.060042e-01, + 6.711368e-02, + 2.882645e-02, + -3.371569e-03, + -2.553830e-02, + -3.591225e-02, + -3.507114e-02, + -2.560252e-02, + -1.138407e-02, + 3.347624e-03, + 1.492345e-02, + 2.096037e-02, + 2.072873e-02, + 1.511364e-02, + 6.212477e-03, + -3.307958e-03, + -1.092423e-02, + -1.486427e-02, + -1.447403e-02, + -1.027446e-02, + -3.720606e-03, + 3.252932e-03, + 8.769802e-03, + 1.149109e-02, + 1.091908e-02, + 7.461166e-03, + 2.255039e-03, + -3.183046e-03, + -7.383784e-03]; - let filter = FIRFilter::from(samples, coeffs); + let sine_gen = SineGenerator::new(carrier_freq, 1.0, sample_freq); + let mixer = Mixer::from(sine_gen, samples); + let filter = FIRFilter::from(mixer, coeffs); let spec = hound::WavSpec { channels: 1, @@ -173,13 +106,13 @@ fn main() { bits_per_sample: 32, sample_format: hound::SampleFormat::Int, }; - let mut writer = hound::WavWriter::create("lowpass.wav", spec).unwrap(); + let mut writer = hound::WavWriter::create("demod.wav", spec).unwrap(); for sample in filter { - println!("{}", sample); + //println!("{}", sample); - let amplitude = i32::max_value() as f32; + let amplitude = (i32::max_value() as f32) * 0.8; //About 1dB headroom writer.write_sample((sample * amplitude) as i32).unwrap(); } } diff --git a/src/mixer.rs b/src/mixer.rs new file mode 100644 index 0000000..62837fa --- /dev/null +++ b/src/mixer.rs @@ -0,0 +1,32 @@ +pub struct Mixer<'a> { + iterator1: Box + 'a>, + iterator2: Box + 'a> +} + +impl<'a> Mixer<'a> { + pub fn from(iterator1: I, iterator2: L) -> Mixer<'a> + where I: Iterator + 'a, L: Iterator + 'a { + Mixer { + iterator1: Box::new(iterator1), + iterator2: Box::new(iterator2) + } + } +} + +impl<'a> Iterator for Mixer<'a> { + type Item = f32; + + fn next(&mut self) -> Option { + let val1 = match self.iterator1.next() { + Some(x) => x, + None => return None + }; + + let val2 = match self.iterator2.next() { + Some(x) => x, + None => return None + }; + + return Some(val1 * val2); + } +} diff --git a/src/resamplers.rs b/src/resamplers.rs new file mode 100644 index 0000000..8a79ac4 --- /dev/null +++ b/src/resamplers.rs @@ -0,0 +1,64 @@ +pub struct Upsampler<'a> { + factor: u16, + state: u16, + iterator: Box + 'a> +} + +impl<'a> Upsampler<'a> { + pub fn from(iterator: I, factor: u16) -> Upsampler<'a> where I: Iterator + 'a { + Upsampler { + factor: factor, + state: 0, + iterator: Box::new(iterator) + } + } +} + +impl<'a> Iterator for Upsampler<'a> { + type Item = f32; + + fn next(&mut self) -> Option { + let result = if self.state == 0 { + self.iterator.next() + } + else { + Some(0.0) + }; + self.state = (self.state + 1) % self.factor; + + return result; + } +} + + + +pub struct Downsampler<'a> { + factor: u16, + iterator: Box + 'a> +} + +impl<'a> Downsampler<'a> { + pub fn from(iterator: I, factor: u16) -> Downsampler<'a> where I: Iterator + 'a { + Downsampler { + factor: factor, + iterator: Box::new(iterator) + } + } +} + +impl<'a> Iterator for Downsampler<'a> { + type Item = f32; + + fn next(&mut self) -> Option { + let mut result = 0.0; + for _ in 0..self.factor { + match self.iterator.next() { + Some(x) => result += x, + None => return None + } + } + result /= self.factor as f32; + + return Some(result); + } +} diff --git a/src/sinegen.rs b/src/sinegen.rs new file mode 100644 index 0000000..08dffa1 --- /dev/null +++ b/src/sinegen.rs @@ -0,0 +1,30 @@ +use std; + +pub struct SineGenerator { + freq: f32, + amplitude: f32, + sample_freq: f32, + phase: f32 +} + +impl SineGenerator { + pub fn new(freq: f32, amplitude: f32, sample_freq: f32) -> SineGenerator { + SineGenerator { + freq: freq, + amplitude: amplitude, + sample_freq: sample_freq, + phase: 0.0 + } + } +} + +impl Iterator for SineGenerator { + type Item = f32; + + fn next(&mut self) -> Option { + let result = self.amplitude * self.phase.sin(); + self.phase += 2.0 * std::f32::consts::PI * self.freq / self.sample_freq; + + Some(result) + } +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..6f718cf --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,18 @@ +use std; + +extern crate hound; + +type FileReader = std::io::BufReader; + +pub fn float_sample_iterator<'a>(reader: &'a mut hound::WavReader) + -> Box + 'a> { + match reader.spec().sample_format { + hound::SampleFormat::Float => Box::new(reader.samples::().map(|x| x.unwrap())), + hound::SampleFormat::Int => match reader.spec().bits_per_sample { + 8 => Box::new(reader.samples::().map(|x| (x.unwrap() as f32) / (i16::max_value() as f32))), + 16 => Box::new(reader.samples::().map(|x| (x.unwrap() as f32) / (i16::max_value() as f32))), + 32 => Box::new(reader.samples::().map(|x| (x.unwrap() as f32) / (i32::max_value() as f32))), + _ => panic!("Unsupported sample rate") + } + } +} diff --git a/to_image.py b/to_image.py new file mode 100644 index 0000000..c3b311d --- /dev/null +++ b/to_image.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python2 + +import numpy +from scipy.signal import firls, lfilter, resample_poly +from scipy.io import wavfile +import scipy.misc +import matplotlib.pyplot as plt + + +f_carrier = 2400.0 + +f_lim = 4160.0 +trans_width = 500.0 + +f_samp, raw = wavfile.read("demod.wav") + +f_samp *= 1.0 +p_samp = 1.0/f_samp +duration = p_samp * raw.size +samples_per_line = 2080.0 + +resampled = resample_poly(raw, 13, 150) + + +missing_elements = int(numpy.ceil(resampled.size / samples_per_line) * samples_per_line) - resampled.size +padded = numpy.append(resampled, [0] * missing_elements) + + +image = numpy.reshape(padded, (padded.size / samples_per_line, samples_per_line)) + +scipy.misc.toimage(image).save("noaa.png")