Correlation bfsk fir design

This commit is contained in:
2025-09-28 20:41:13 +02:00
parent 4a6d79c0b4
commit 1445887f2f
15 changed files with 4032 additions and 137 deletions

View File

@ -3,6 +3,7 @@
use std::{
f32::consts::PI,
fs::File,
i16,
io::{Read, Write},
ops::{Add, Div, Mul, Sub},
};
@ -21,33 +22,22 @@ use complex::Complex;
use complex::Complex32;
use fft::DFTAlgorithm;
use nco::Nco;
use plotters::prelude::*;
use eframe::egui::{self, Color32, debug_text::print};
use egui_plot::{self, Bar, BarChart, Legend, Line, LineStyle, Plot, PlotPoints};
use plotters::style::Color;
use crate::{
bfsk::BFSKDem,
fft::{
FFT, FFTDirection, create_fft,
dft::NaiveDFT,
mixed_radix::MixedRadixFFT,
prime_factors,
rader::{RaderFFT, compute_prime_primitive_root, exp_mod},
rader2::{Rader2FFT, next_pow2},
radix2::Radix2FFT,
windows,
fft::{FFT, dft::NaiveDFT},
filtering::{
fir::FIRFilter,
impulse_response::design::{self, frequency_response, ir_from_transfer_function},
},
iq::IQSampler,
units::frequency::{self, hz_to_rad_per_sample},
};
struct QuickLCG(i32);
impl QuickLCG {
pub fn seed(val: i32) -> QuickLCG {
QuickLCG(val % 10)
}
pub fn next(&mut self) -> i32 {
self.0 = self.0.overflowing_mul(9321).0.overflowing_add(5672).0 % 10;
self.0
}
}
// Utilities
fn map<T>(input: T, in_min: T, in_max: T, out_min: T, out_max: T) -> T
where
@ -58,17 +48,185 @@ where
fn main() {
modulate();
//œtest();
println!("Demodulating");
//demodulate();
let native_options = eframe::NativeOptions::default();
let _ = eframe::run_native("Egui", native_options, Box::new(|cc| Ok(Box::new(EguiApp::new(cc)))));
}
#[derive(Default)]
struct EguiApp {
samples: Vec<f32>,
iq: Vec<Complex32>,
dem: Vec<f32>,
}
const BAUD_RATE: u32 = 1000;
impl EguiApp {
fn new(cc: &eframe::CreationContext<'_>) -> Self {
let mut reader = hound::WavReader::open("audio/modulated.wav").unwrap();
let samples = reader.samples::<i16>();
let input_test = samples
.take(10_000)
.map(|x| x.unwrap() as f32 / i16::MAX as f32)
.collect::<Vec<_>>();
// Modulation parameters
let frequency = 1700.;
let deviation = 500.;
// Data parameters
let sample_rate = 48000;
let baud_rate = BAUD_RATE;
let mut iq_sampler = IQSampler::new(hz_to_rad_per_sample(frequency, sample_rate as f32));
let iq = input_test
.iter()
.map(|x| iq_sampler.sample(*x))
.collect::<Vec<_>>();
let mut dem = BFSKDem::new(
sample_rate / baud_rate,
hz_to_rad_per_sample(deviation, sample_rate as f32),
);
let mut demodulated = vec![];
for x in iq.chunks((sample_rate / baud_rate) as usize) {
let samples = x
.iter()
.copied()
.chain(std::iter::repeat(Complex32::zero()))
.take((sample_rate / baud_rate) as usize)
.collect::<Vec<_>>();
demodulated.push(dem.demod(&samples));
}
Self {
samples: input_test.clone(),
iq,
dem: demodulated
.iter()
.map(|b| if *b { 1. } else { 0. })
.collect(),
}
}
}
impl eframe::App for EguiApp {
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("Hello World!");
Plot::new("Fourrier transform")
.legend(Legend::default())
.show(ui, |plot_ui| {
plot_ui.line(
Line::new(
"Transfer function",
self.samples
.iter()
.enumerate()
.map(|(i, x)| [i as f64 / self.samples.len() as f64, *x as f64])
.collect::<Vec<_>>(),
)
.width(2.),
);
plot_ui.line(
Line::new(
"RE",
self.iq
.iter()
.enumerate()
.map(|(i, x)| [i as f64 / self.iq.len() as f64, x.re as f64])
.collect::<Vec<_>>(),
)
.width(2.),
);
plot_ui.line(
Line::new(
"IM",
self.iq
.iter()
.enumerate()
.map(|(i, x)| [i as f64 / self.iq.len() as f64, x.im as f64])
.collect::<Vec<_>>(),
)
.width(2.),
);
plot_ui.line(
Line::new(
"Bits",
self.iq
.iter()
.enumerate()
.map(|(i, x)| {
[
i as f64 / self.iq.len() as f64,
self.dem[(self.dem.len() as f32 * i as f32
/ self.iq.len() as f32)
.floor()
as usize]
as f64,
]
})
.collect::<Vec<_>>(),
)
.width(2.),
);
})
});
}
}
fn demodulate() {
let mut reader = hound::WavReader::open("audio/modulated.wav").unwrap();
let samples = reader.samples::<i16>();
// Modulation parameters
let frequency = 1700.;
let deviation = 500.;
// Data parameter
let sample_rate = 48000;
let baud_rate = BAUD_RATE;
let mut iq_sampler = IQSampler::new(hz_to_rad_per_sample(frequency, sample_rate as f32));
let iq_samples = samples
.map(|x| iq_sampler.sample(x.unwrap() as f32 / i16::MAX as f32))
.collect::<Vec<_>>();
let mut dem = BFSKDem::new(
sample_rate / baud_rate,
hz_to_rad_per_sample(deviation, sample_rate as f32),
);
let mut bits = vec![];
for x in iq_samples.chunks((sample_rate / baud_rate) as usize) {
// Zero pad
//let zero_padded = x.iter().copied().chain(std::iter::repeat(Complex32::zero())).take(sample_rate as usize / baud_rate as usize).collect::<Vec<_>>();
//bits.push(dem.demod(&zero_padded));
bits.push(dem.demod(x));
}
assert!(bits.len() % 8 == 0);
let mut out_file = File::create("out.txt").unwrap();
for x in bits.chunks(8) {
out_file.write_all(&[!bits_to_byte(x)]).unwrap();
}
}
fn modulate() {
// Modulation parameters
let frequency = 2000.;
let frequency = 1700.;
let deviation = 500.;
// Data parameters
let sample_rate = 44100;
let baud_rate = 400;
// Data parameter
let sample_rate = 48000;
let baud_rate = BAUD_RATE;
// File to modulate
let f = File::open("s.txt").unwrap();
@ -92,7 +250,7 @@ fn modulate() {
sample_format: hound::SampleFormat::Int,
};
let mut writer = hound::WavWriter::create("sine.wav", spec).unwrap();
let mut writer = hound::WavWriter::create("audio/modulated.wav", spec).unwrap();
for (s, up) in modulator.zip(lo) {
let sample = (s * up).re; // Project to I coords
let amplitude = i16::MAX as f32;
@ -113,3 +271,27 @@ fn byte_to_bits(byte: u8) -> Vec<bool> {
(byte >> 7) & 1 == 1,
]
}
/*
fn bits_to_byte(bits: &[bool]) -> u8 {
bits[7] as u8 |
bits[6] as u8 >> 1 |
bits[5] as u8 >> 2 |
bits[4] as u8 >> 3 |
bits[3] as u8 >> 4 |
bits[2] as u8 >> 5 |
bits[1] as u8 >> 6 |
bits[0] as u8 >> 7
}
*/
fn bits_to_byte(bits: &[bool]) -> u8 {
bits[0] as u8
| (bits[1] as u8) << 1
| (bits[2] as u8) << 2
| (bits[3] as u8) << 3
| (bits[4] as u8) << 4
| (bits[5] as u8) << 5
| (bits[6] as u8) << 6
| (bits[7] as u8) << 7
}