Framed reception

This commit is contained in:
2025-10-03 12:27:35 +02:00
parent e7bb36d655
commit 102e25da80
3 changed files with 95 additions and 37 deletions

View File

@ -1,2 +1,3 @@
pub mod fir;
pub mod dc_block;
pub mod impulse_response;

34
src/filtering/dc_block.rs Normal file
View File

@ -0,0 +1,34 @@
use crate::complex::Complex32;
pub struct DCBlocker
{
alpha: f32,
last_input: Complex32,
last_output: Complex32,
}
impl DCBlocker
{
pub fn new(alpha: f32) -> Self
{
DCBlocker
{
alpha,
last_input: Complex32::zero(),
last_output: Complex32::zero()
}
}
pub fn next(&mut self, sample: Complex32) -> Complex32
{
let output = sample - self.last_input + self.last_output * self.alpha;
self.last_output = output;
self.last_input = sample;
output
}
pub fn next_real(&mut self, sample: f32) -> f32
{
self.next(Complex32::new(sample, 0.)).re
}
}

View File

@ -5,7 +5,7 @@ use std::{
f32::consts::PI,
fs::File,
i16,
io::{self, Read, Write},
io::{self, stdout, Read, Write},
ops::{Add, Div, Mul, Sub},
os::unix::thread,
sync::{
@ -34,17 +34,16 @@ use cpal::{
use fft::DFTAlgorithm;
use nco::Nco;
use eframe::egui::{self, debug_text::print, decode_animated_image_uri, Color32, Context, Vec2b};
use egui_plot::{self, Bar, BarChart, Legend, Line, LineStyle, Plot, PlotPoints, VLine};
use eframe::{egui::{self, debug_text::print, decode_animated_image_uri, Color32, Context, Vec2b}, glow::TOP_LEVEL_ARRAY_STRIDE};
use egui_plot::{self, AxisHints, Bar, BarChart, HLine, Legend, Line, LineStyle, Plot, PlotPoints, VLine};
use plotters::style::Color;
use rand::{seq::index::sample, Rng};
use crate::{
bfsk::BFSKDem,
fft::{FFT, dft::NaiveDFT},
fft::{dft::NaiveDFT, FFT},
filtering::{
fir::FIRFilter,
impulse_response::design::{self, frequency_response, ir_from_transfer_function},
dc_block::DCBlocker, fir::FIRFilter, impulse_response::design::{self, frequency_response, ir_from_transfer_function}
},
iq::IQSampler,
units::frequency::{self, hz_to_rad_per_sample},
@ -57,10 +56,10 @@ where
{
((input - in_min.clone()) / (in_max - in_min)) * (out_max - out_min.clone()) + out_min
}
const BAUD_RATE: u32 = 1000;
const BAUD_RATE: u32 = 1200;
fn main() {
modulate();
//modulate();
//demodulate();
//return;
// Set up CPAL
@ -80,13 +79,12 @@ fn main() {
let (tx, rx) = sync_channel::<f32>(4096);
// Build input stream
/*
let stream = device
.build_input_stream(
&config.into(),
move |data: &[f32], _| {
for x in data.iter() {
let _ = tx.send(*x * 10.); // non-blocking send
let _ = tx.send(*x * 30.); // non-blocking send
}
},
move |err| eprintln!("Stream error: {}", err),
@ -94,12 +92,12 @@ fn main() {
)
.unwrap();
stream.play().unwrap();
*/
let (eye_tx, eye_rx) = mpsc::channel::<Vec<f32>>();
let (ctx_tx, ctx_rx) = mpsc::channel::<Arc<egui::Context>>();
std::thread::spawn(move || demodulator(rx, eye_tx, ctx_rx));
/*
std::thread::spawn(move || {
let spec = hound::WavSpec {
channels: 1,
@ -124,6 +122,7 @@ fn main() {
}
writer.finalize().unwrap();
});
*/
let native_options = eframe::NativeOptions::default();
let _ = eframe::run_native(
@ -160,12 +159,12 @@ impl ELGate
}
}
pub fn next(&mut self, sample: f32) -> Option<bool>
pub fn next(&mut self, sample: f32) -> Option<f32>
{
Some(self.next_eye(sample)?.0) // Ignore eye
}
pub fn next_eye(&mut self, sample: f32) -> Option<(bool, Vec<f32>)>
pub fn next_eye(&mut self, sample: f32) -> Option<(f32, Vec<f32>)>
{
self.buffer.push_front(sample);
self.current_position += 1.;
@ -193,7 +192,7 @@ impl ELGate
self.current_position = self.next_sample - self.next_sample.floor();
self.next_sample = self.samples_per_symbol - self.loop_filter.next_real(error);
Some((sample > 0., Vec::from(self.buffer.clone())))
Some((sample, Vec::from(self.buffer.clone())))
}
else
{
@ -244,9 +243,9 @@ fn demodulator(
matched_filter_pos.normalize_sum();
matched_filter_neg.normalize_sum();
let loop_p = 0.3;
let loop_i = 0.1;
let mut matched_filter = FIRFilter::new(&[Complex32::new(1., 0.); 40]);
let loop_p = 0.003;
let loop_i = 0.001;
let mut matched_filter = FIRFilter::new(&[Complex32::new(1., 0.); 20]);
matched_filter.normalize_sum();
let mut loop_filter_ir = vec![Complex32::new(loop_i, 0.); 30];
@ -260,6 +259,8 @@ fn demodulator(
sample_per_symbol as f32,
loop_filter
);
let mut dc_blocker = DCBlocker::new(0.999);
// Timing recovery
let mut preamble_count = 0;
@ -272,36 +273,52 @@ fn demodulator(
let neg_energy = matched_filter_neg.next(iq);
let pos_energy = matched_filter_pos.next(iq);
let matched = matched_filter.next_real(pos_energy.mag() - neg_energy.mag());
let matched = matched_filter.next_real(dc_blocker.next_real(pos_energy.mag() - neg_energy.mag()));
if let Some((bit, eye)) = elg.next_eye(matched)
if let Some((elg_sample, eye)) = elg.next_eye(matched)
{
let _ = eye_sender.send(eye);
ctx.request_repaint();
last <<= 1;
last |= bit as u8;
if preamble_count >= 2
//last >>= 1;
//last |= (!bit as u8) << 7;
let bit = elg_sample > 0.;
if elg_sample*elg_sample > 0.005
{
bit_index += 1;
if bit_index == 8
last >>= 1;
last |= (bit as u8) << 7;
//last <<= 1;
//last |= ((bit) as u8);
if preamble_count >= 2
{
if last == 4
bit_index += 1;
if bit_index >= 8
{
println!();
preamble_count = 0;
}
else
{
print!("{}", last as char);
bit_index = 0;
if last == 4
{
print!(" -- EOT");
println!();
preamble_count = 0;
bit_index = 0;
last = 0;
}
else
{
print!("{}", last as char);
bit_index = 0;
let _ = stdout().flush();
}
}
}
else if last == 0xD8
{
preamble_count += 1;
if preamble_count == 2
{
println!("Incoming: ");
}
}
}
if last == 0xD8
{
preamble_count += 1;
}
}
}
@ -340,11 +357,17 @@ impl eframe::App for EguiApp {
while self.eyes.len() > max_eyes {
self.eyes.pop_front();
}
let axis_hints = AxisHints::new_x()
.min_thickness(2.);
Plot::new("Eye")
.legend(Legend::default())
.show_axes(Vec2b::TRUE)
.custom_x_axes(vec![axis_hints])
.show(ui, |plot_ui| {
//plot_ui.set_auto_bounds(Vec2b { x: false, y: false });
plot_ui.hline(HLine::new("", 0.).color(Color32::DARK_RED).width(2.));
for eye in self.eyes.iter() {
let line = Line::new(
"Eye",