Framed reception
This commit is contained in:
@ -1,2 +1,3 @@
|
||||
pub mod fir;
|
||||
pub mod dc_block;
|
||||
pub mod impulse_response;
|
||||
|
||||
34
src/filtering/dc_block.rs
Normal file
34
src/filtering/dc_block.rs
Normal 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
|
||||
}
|
||||
}
|
||||
97
src/main.rs
97
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::<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",
|
||||
|
||||
Reference in New Issue
Block a user