FIR Filtering beignings
This commit is contained in:
@ -46,6 +46,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Iterator<Item = bool>> Iterator for BFSKMod<'a, T> {
|
||||
type Item = Complex32;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.step_modulate()
|
||||
}
|
||||
}
|
||||
|
||||
// FSK Demodulator (dumb non coherent + no symbol timing recovery)
|
||||
pub struct BFSKDem {
|
||||
samples_per_bit: u32,
|
||||
|
||||
@ -2,7 +2,7 @@ use std::{
|
||||
f32::consts::PI,
|
||||
fmt::Display,
|
||||
iter::Sum,
|
||||
ops::{Add, Div, Mul, Neg, Sub},
|
||||
ops::{Add, Deref, Div, Mul, Neg, Sub},
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@ -130,3 +130,11 @@ impl Sum for Complex32 {
|
||||
iter.fold(Complex32::zero(), |acc, el| acc + el)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Complex<T> {
|
||||
type Target = Complex<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@ pub mod mixed_radix;
|
||||
pub mod rader;
|
||||
pub mod rader2;
|
||||
pub mod radix2;
|
||||
pub mod windows;
|
||||
|
||||
use crate::{
|
||||
complex::Complex32,
|
||||
|
||||
2
src/filtering.rs
Normal file
2
src/filtering.rs
Normal file
@ -0,0 +1,2 @@
|
||||
mod fir;
|
||||
mod impulse_response;
|
||||
55
src/filtering/fir.rs
Normal file
55
src/filtering/fir.rs
Normal file
@ -0,0 +1,55 @@
|
||||
// Finite impulse response filters
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::complex::Complex32;
|
||||
|
||||
pub struct FIRFilter<T>
|
||||
where
|
||||
T: Iterator<Item = Complex32>,
|
||||
{
|
||||
size: usize,
|
||||
impulse_response: Box<[Complex32]>,
|
||||
taps: VecDeque<Complex32>,
|
||||
input: T,
|
||||
}
|
||||
|
||||
impl<T> FIRFilter<T>
|
||||
where
|
||||
T: Iterator<Item = Complex32>,
|
||||
{
|
||||
pub fn new(impulse_response: &[Complex32], input: T) -> Self {
|
||||
FIRFilter {
|
||||
size: impulse_response.len(),
|
||||
impulse_response: impulse_response.iter().copied().collect(),
|
||||
taps: VecDeque::from(vec![Complex32::zero(); impulse_response.len()]),
|
||||
input,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Iterator for FIRFilter<T>
|
||||
where
|
||||
T: Iterator<Item = Complex32>,
|
||||
{
|
||||
type Item = Complex32;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let _ = self.taps.pop_front();
|
||||
|
||||
let sample = self.input.next();
|
||||
if let None = sample {
|
||||
self.taps.push_back(Complex32::zero());
|
||||
None
|
||||
} else {
|
||||
self.taps.push_back(sample.unwrap());
|
||||
Some(
|
||||
self.taps
|
||||
.iter()
|
||||
.zip(self.impulse_response.iter())
|
||||
.map(|(tap, coef)| *tap * *coef)
|
||||
.sum(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
8
src/filtering/impulse_response.rs
Normal file
8
src/filtering/impulse_response.rs
Normal file
@ -0,0 +1,8 @@
|
||||
// Utilities for impulse response design
|
||||
|
||||
pub struct WindowIRDesigner {
|
||||
window: fn(f32) -> f32,
|
||||
|
||||
// Ideal requested transfer function
|
||||
transfer_function: Box<[Complex32]>,
|
||||
}
|
||||
37
src/iq.rs
Normal file
37
src/iq.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use crate::{complex::Complex32, nco::Nco};
|
||||
|
||||
pub struct IQSampler<T>
|
||||
where
|
||||
T: Iterator<Item = f32>,
|
||||
{
|
||||
passband: T,
|
||||
local_oscillator: Nco,
|
||||
}
|
||||
|
||||
impl<T> IQSampler<T>
|
||||
where
|
||||
T: Iterator<Item = f32>,
|
||||
{
|
||||
fn new(passband: T, center_freq: f32) -> Self {
|
||||
IQSampler {
|
||||
passband,
|
||||
local_oscillator: Nco::new(center_freq),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_from_lo(passband: T, local_oscillator: Nco) -> Self {
|
||||
IQSampler {
|
||||
passband,
|
||||
local_oscillator,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Iterator for IQSampler<T>
|
||||
where
|
||||
T: Iterator<Item = f32>,
|
||||
{
|
||||
type Item = Complex32;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {}
|
||||
}
|
||||
133
src/main.rs
133
src/main.rs
@ -10,8 +10,11 @@ use std::{
|
||||
mod bfsk;
|
||||
mod complex;
|
||||
pub mod fft;
|
||||
mod filtering;
|
||||
mod iq;
|
||||
mod nco;
|
||||
mod units;
|
||||
mod windows;
|
||||
|
||||
use bfsk::BFSKMod;
|
||||
use complex::Complex;
|
||||
@ -59,114 +62,54 @@ fn main() {
|
||||
}
|
||||
|
||||
fn modulate() {
|
||||
// Modulation parameters
|
||||
let frequency = 2000.;
|
||||
let deviation = 500.;
|
||||
|
||||
// Data parameters
|
||||
let sample_rate = 44100;
|
||||
let frequency = 2000.0; //HZ
|
||||
let bandwidth = 400.0; //HZ
|
||||
println!("deviation: {}", PI * (bandwidth / sample_rate as f32));
|
||||
|
||||
let path = "s.txt";
|
||||
let file = File::open(path).unwrap();
|
||||
let mut bit_stream = file.bytes().flat_map(|byte| {
|
||||
let byte = byte.unwrap();
|
||||
[
|
||||
byte & 1 == 1,
|
||||
(byte >> 1) & 1 == 1,
|
||||
(byte >> 2) & 1 == 1,
|
||||
(byte >> 3) & 1 == 1,
|
||||
(byte >> 4) & 1 == 1,
|
||||
(byte >> 5) & 1 == 1,
|
||||
(byte >> 6) & 1 == 1,
|
||||
(byte >> 7) & 1 == 1,
|
||||
]
|
||||
});
|
||||
//let mut bit_stream = (0..22000).map(|_| false).chain((0..22000).map(|_| true));
|
||||
//let mut bit_stream = (0..22000).flat_map(|_| [true, false]);
|
||||
|
||||
let baud_rate = 400;
|
||||
println!("{} samples/bit", sample_rate / baud_rate);
|
||||
let mut bfsk = BFSKMod::new(
|
||||
|
||||
// File to modulate
|
||||
let f = File::open("s.txt").unwrap();
|
||||
let mut bitstream = f.bytes().flat_map(|b| byte_to_bits(b.unwrap()));
|
||||
|
||||
let mut modulator = BFSKMod::new(
|
||||
sample_rate / baud_rate,
|
||||
PI * (bandwidth / sample_rate as f32),
|
||||
&mut bit_stream,
|
||||
units::frequency::hz_to_rad_per_sample(deviation, sample_rate as f32),
|
||||
&mut bitstream,
|
||||
);
|
||||
|
||||
let mut lo = Nco::new(units::frequency::hz_to_rad_per_sample(
|
||||
frequency,
|
||||
sample_rate as f32,
|
||||
));
|
||||
|
||||
let spec = hound::WavSpec {
|
||||
channels: 1,
|
||||
sample_rate,
|
||||
bits_per_sample: 16,
|
||||
sample_format: hound::SampleFormat::Int,
|
||||
};
|
||||
|
||||
let mut writer = hound::WavWriter::create("sine.wav", spec).unwrap();
|
||||
|
||||
let mut lo = Nco::new(2. * PI * (frequency / sample_rate as f32));
|
||||
|
||||
let prev = Complex::new(0., 0.);
|
||||
let alpha = 1.0 - (-2.0 * PI * ((1.5 * 0.5 * bandwidth) / sample_rate as f32));
|
||||
let mut output_samples = vec![];
|
||||
while let Some(sample) = bfsk.step_modulate() {
|
||||
for (s, up) in modulator.zip(lo) {
|
||||
let sample = (s * up).re; // Project to I coords
|
||||
let amplitude = i16::MAX as f32;
|
||||
let c_sample = sample * lo.cexp();
|
||||
//let c_sample = sample;
|
||||
|
||||
//let filtered = prev + (c_sample - prev) * alpha;
|
||||
output_samples.push(c_sample);
|
||||
writer
|
||||
.write_sample((amplitude * c_sample.re) as i16)
|
||||
.unwrap();
|
||||
lo.step();
|
||||
writer.write_sample((sample * amplitude) as i16).unwrap();
|
||||
}
|
||||
writer.finalize().unwrap();
|
||||
|
||||
let mut of = File::create("out.txt").unwrap();
|
||||
|
||||
let mut fft = FFT::new(110, windows::bartlett);
|
||||
fft.execute(&output_samples);
|
||||
|
||||
let mut csv = File::create("out.csv").unwrap();
|
||||
for x in fft.get_output() {
|
||||
csv.write_all(format!("{},\n", x.mag()).as_bytes()).unwrap();
|
||||
}
|
||||
|
||||
let mut bits = vec![];
|
||||
let mut lodem = Nco::new(-2. * PI * (frequency / sample_rate as f32));
|
||||
let mut demod = BFSKDem::new(
|
||||
sample_rate / baud_rate,
|
||||
PI * (bandwidth / sample_rate as f32),
|
||||
);
|
||||
for chunk in output_samples.chunks((sample_rate / baud_rate) as usize) {
|
||||
let base_chunk: Vec<Complex32> = chunk
|
||||
.iter()
|
||||
.map(|x| {
|
||||
lodem.step();
|
||||
*x * lodem.cexp()
|
||||
})
|
||||
.collect();
|
||||
let bit = demod.demod(base_chunk.as_slice());
|
||||
bits.push(bit);
|
||||
//println!("{:?}", bit)
|
||||
}
|
||||
|
||||
for b in bits.chunks(8) {
|
||||
/*
|
||||
of.write_all(&[(b[7] as u8)
|
||||
| ((b[6] as u8) << 1)
|
||||
| ((b[5] as u8) << 2)
|
||||
| ((b[4] as u8) << 3)
|
||||
| ((b[3] as u8) << 4)
|
||||
| ((b[2] as u8) << 5)
|
||||
| ((b[1] as u8) << 6)
|
||||
| ((b[0] as u8) << 7)])
|
||||
.unwrap();
|
||||
*/
|
||||
|
||||
of.write_all(&[(b[0] as u8)
|
||||
| ((b[1] as u8) << 1)
|
||||
| ((b[2] as u8) << 2)
|
||||
| ((b[3] as u8) << 3)
|
||||
| ((b[4] as u8) << 4)
|
||||
| ((b[5] as u8) << 5)
|
||||
| ((b[6] as u8) << 6)
|
||||
| ((b[7] as u8) << 7)])
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn byte_to_bits(byte: u8) -> Vec<bool> {
|
||||
vec![
|
||||
byte & 1 == 1,
|
||||
(byte >> 1) & 1 == 1,
|
||||
(byte >> 2) & 1 == 1,
|
||||
(byte >> 3) & 1 == 1,
|
||||
(byte >> 4) & 1 == 1,
|
||||
(byte >> 5) & 1 == 1,
|
||||
(byte >> 6) & 1 == 1,
|
||||
(byte >> 7) & 1 == 1,
|
||||
]
|
||||
}
|
||||
|
||||
10
src/units.rs
10
src/units.rs
@ -1,17 +1,13 @@
|
||||
// Provides nice unit conversions
|
||||
|
||||
|
||||
mod frequency
|
||||
{
|
||||
pub mod frequency {
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub fn hz_to_rad_per_sample(hz: f32, sample_rate: f32) -> f32
|
||||
{
|
||||
pub fn hz_to_rad_per_sample(hz: f32, sample_rate: f32) -> f32 {
|
||||
2. * PI * hz / sample_rate
|
||||
}
|
||||
|
||||
pub fn rad_per_sample_to_hz(rad: f32, sample_rate: f32) -> f32
|
||||
{
|
||||
pub fn rad_per_sample_to_hz(rad: f32, sample_rate: f32) -> f32 {
|
||||
(rad * sample_rate) / (2. * PI)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
pub fn rectangular(t: f32) -> f32 {
|
||||
pub fn rectangular(_: f32) -> f32 {
|
||||
1.
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user