diff --git a/src/filtering.rs b/src/filtering.rs index 25d91da..13c402f 100644 --- a/src/filtering.rs +++ b/src/filtering.rs @@ -1,2 +1,3 @@ pub mod fir; +pub mod dc_block; pub mod impulse_response; diff --git a/src/filtering/dc_block.rs b/src/filtering/dc_block.rs new file mode 100644 index 0000000..8a7db9f --- /dev/null +++ b/src/filtering/dc_block.rs @@ -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 + } +} diff --git a/src/main.rs b/src/main.rs index 7629344..d19a30e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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::(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::>(); let (ctx_tx, ctx_rx) = mpsc::channel::>(); 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 + pub fn next(&mut self, sample: f32) -> Option { Some(self.next_eye(sample)?.0) // Ignore eye } - pub fn next_eye(&mut self, sample: f32) -> Option<(bool, Vec)> + pub fn next_eye(&mut self, sample: f32) -> Option<(f32, Vec)> { 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",