Added more primitives for singnal processing
Added python script to convert to images
This commit is contained in:
parent
5a31e4ff4e
commit
9625dd9689
|
@ -0,0 +1,44 @@
|
|||
pub struct FIRFilter<'a> {
|
||||
coeffs: Vec<f32>,
|
||||
state: Vec<f32>,
|
||||
pos: usize,
|
||||
iterator: Box<Iterator<Item=f32> + 'a>
|
||||
}
|
||||
|
||||
impl<'a> FIRFilter<'a> {
|
||||
pub fn from<I>(iterator: I, coeffs: Vec<f32>) -> FIRFilter<'a> where I: Iterator<Item=f32> + '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<Self::Item> {
|
||||
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)
|
||||
}
|
||||
}
|
227
src/main.rs
227
src/main.rs
|
@ -1,136 +1,20 @@
|
|||
extern crate hound;
|
||||
|
||||
type FileReader = std::io::BufReader<std::fs::File>;
|
||||
|
||||
fn float_sample_iterator<'a>(reader: &'a mut hound::WavReader<FileReader>)
|
||||
-> Box<Iterator<Item=f32> + 'a> {
|
||||
match reader.spec().sample_format {
|
||||
hound::SampleFormat::Float => Box::new(reader.samples::<f32>().map(|x| x.unwrap())),
|
||||
hound::SampleFormat::Int => match reader.spec().bits_per_sample {
|
||||
8 => Box::new(reader.samples::<i8>().map(|x| (x.unwrap() as f32) / (i16::max_value() as f32))),
|
||||
16 => Box::new(reader.samples::<i16>().map(|x| (x.unwrap() as f32) / (i16::max_value() as f32))),
|
||||
32 => Box::new(reader.samples::<i32>().map(|x| (x.unwrap() as f32) / (i32::max_value() as f32))),
|
||||
_ => panic!("Unsupported sample rate")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Upsampler<'a> {
|
||||
factor: u16,
|
||||
state: u16,
|
||||
iterator: Box<Iterator<Item=f32> + 'a>
|
||||
}
|
||||
|
||||
impl<'a> Upsampler<'a> {
|
||||
fn from<I>(iterator: I, factor: u16) -> Upsampler<'a> where I: Iterator<Item=f32> + 'a {
|
||||
Upsampler {
|
||||
factor: factor,
|
||||
state: 0,
|
||||
iterator: Box::new(iterator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Upsampler<'a> {
|
||||
type Item = f32;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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<Iterator<Item=f32> + 'a>
|
||||
}
|
||||
|
||||
impl<'a> Downsampler<'a> {
|
||||
fn from<I>(iterator: I, factor: u16) -> Downsampler<'a> where I: Iterator<Item=f32> + 'a {
|
||||
Downsampler {
|
||||
factor: factor,
|
||||
iterator: Box::new(iterator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Downsampler<'a> {
|
||||
type Item = f32;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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<f32>,
|
||||
state: Vec<f32>,
|
||||
pos: usize,
|
||||
iterator: Box<Iterator<Item=f32> + 'a>
|
||||
}
|
||||
|
||||
impl<'a> FIRFilter<'a> {
|
||||
fn from<I>(iterator: I, coeffs: Vec<f32>) -> FIRFilter<'a> where I: Iterator<Item=f32> + '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<Self::Item> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
pub struct Mixer<'a> {
|
||||
iterator1: Box<Iterator<Item=f32> + 'a>,
|
||||
iterator2: Box<Iterator<Item=f32> + 'a>
|
||||
}
|
||||
|
||||
impl<'a> Mixer<'a> {
|
||||
pub fn from<I,L>(iterator1: I, iterator2: L) -> Mixer<'a>
|
||||
where I: Iterator<Item=f32> + 'a, L: Iterator<Item=f32> + 'a {
|
||||
Mixer {
|
||||
iterator1: Box::new(iterator1),
|
||||
iterator2: Box::new(iterator2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Mixer<'a> {
|
||||
type Item = f32;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
pub struct Upsampler<'a> {
|
||||
factor: u16,
|
||||
state: u16,
|
||||
iterator: Box<Iterator<Item=f32> + 'a>
|
||||
}
|
||||
|
||||
impl<'a> Upsampler<'a> {
|
||||
pub fn from<I>(iterator: I, factor: u16) -> Upsampler<'a> where I: Iterator<Item=f32> + 'a {
|
||||
Upsampler {
|
||||
factor: factor,
|
||||
state: 0,
|
||||
iterator: Box::new(iterator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Upsampler<'a> {
|
||||
type Item = f32;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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<Iterator<Item=f32> + 'a>
|
||||
}
|
||||
|
||||
impl<'a> Downsampler<'a> {
|
||||
pub fn from<I>(iterator: I, factor: u16) -> Downsampler<'a> where I: Iterator<Item=f32> + 'a {
|
||||
Downsampler {
|
||||
factor: factor,
|
||||
iterator: Box::new(iterator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Downsampler<'a> {
|
||||
type Item = f32;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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<Self::Item> {
|
||||
let result = self.amplitude * self.phase.sin();
|
||||
self.phase += 2.0 * std::f32::consts::PI * self.freq / self.sample_freq;
|
||||
|
||||
Some(result)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
use std;
|
||||
|
||||
extern crate hound;
|
||||
|
||||
type FileReader = std::io::BufReader<std::fs::File>;
|
||||
|
||||
pub fn float_sample_iterator<'a>(reader: &'a mut hound::WavReader<FileReader>)
|
||||
-> Box<Iterator<Item=f32> + 'a> {
|
||||
match reader.spec().sample_format {
|
||||
hound::SampleFormat::Float => Box::new(reader.samples::<f32>().map(|x| x.unwrap())),
|
||||
hound::SampleFormat::Int => match reader.spec().bits_per_sample {
|
||||
8 => Box::new(reader.samples::<i8>().map(|x| (x.unwrap() as f32) / (i16::max_value() as f32))),
|
||||
16 => Box::new(reader.samples::<i16>().map(|x| (x.unwrap() as f32) / (i16::max_value() as f32))),
|
||||
32 => Box::new(reader.samples::<i32>().map(|x| (x.unwrap() as f32) / (i32::max_value() as f32))),
|
||||
_ => panic!("Unsupported sample rate")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")
|
Loading…
Reference in New Issue