FIR Filtering beignings

This commit is contained in:
2025-09-27 15:44:52 +02:00
parent fab0e5cc62
commit 4a6d79c0b4
11 changed files with 161 additions and 105 deletions

BIN
sine.wav

Binary file not shown.

View File

@ -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,

View File

@ -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
}
}

View File

@ -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
View File

@ -0,0 +1,2 @@
mod fir;
mod impulse_response;

55
src/filtering/fir.rs Normal file
View 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(),
)
}
}
}

View 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
View 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> {}
}

View File

@ -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,
]
}

View File

@ -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)
}
}

View File

@ -1,4 +1,4 @@
pub fn rectangular(t: f32) -> f32 {
pub fn rectangular(_: f32) -> f32 {
1.
}