Encapsulated early late gate
This commit is contained in:
@ -2,7 +2,7 @@
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::complex::Complex32;
|
||||
use crate::{complex::Complex32, filtering::impulse_response};
|
||||
|
||||
pub struct FIRFilter {
|
||||
size: usize,
|
||||
@ -13,8 +13,6 @@ pub struct FIRFilter {
|
||||
|
||||
impl FIRFilter {
|
||||
pub fn new(impulse_response: &[Complex32]) -> Self {
|
||||
let normalization = impulse_response.iter().copied().sum::<Complex32>().mag(); // DC normalization
|
||||
println!("normalization factor {}", normalization);
|
||||
FIRFilter {
|
||||
size: impulse_response.len(),
|
||||
impulse_response: impulse_response.iter().copied().collect(),
|
||||
@ -25,12 +23,17 @@ impl FIRFilter {
|
||||
.reduce(|acc, e| acc.max(e))
|
||||
.unwrap(),
|
||||
*/
|
||||
normalization: normalization.max(0.001), // DC normalization
|
||||
normalization: 1., // DC normalization
|
||||
// TODO: Maybe we'd want other types of normalization (per frequency)
|
||||
taps: VecDeque::from(vec![Complex32::zero(); impulse_response.len()]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn normalize_sum(&mut self)
|
||||
{
|
||||
self.normalization = self.impulse_response.iter().copied().sum::<Complex32>().mag()
|
||||
}
|
||||
|
||||
pub fn next(&mut self, next: Complex32) -> Complex32 {
|
||||
let _ = self.taps.pop_front();
|
||||
|
||||
|
||||
@ -26,11 +26,15 @@ impl IQSampler
|
||||
}
|
||||
|
||||
let ir = crate::filtering::impulse_response::design::ir_from_transfer_function(&transfer_function, fir_length, windows::blackmann);
|
||||
let mut low_pass_i = FIRFilter::new(&ir);
|
||||
let mut low_pass_q = FIRFilter::new(&ir);
|
||||
low_pass_i.normalize_sum();
|
||||
low_pass_q.normalize_sum();
|
||||
|
||||
IQSampler {
|
||||
local_oscillator: Nco::new(center_freq),
|
||||
low_pass_i: FIRFilter::new(&ir),
|
||||
low_pass_q: FIRFilter::new(&ir)
|
||||
low_pass_i,
|
||||
low_pass_q
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
128
src/main.rs
128
src/main.rs
@ -38,7 +38,7 @@ use nco::Nco;
|
||||
use eframe::egui::{self, Color32, Context, Vec2b, debug_text::print};
|
||||
use egui_plot::{self, Bar, BarChart, Legend, Line, LineStyle, Plot, PlotPoints, VLine};
|
||||
use plotters::style::Color;
|
||||
use rand::Rng;
|
||||
use rand::{seq::index::sample, Rng};
|
||||
|
||||
use crate::{
|
||||
bfsk::BFSKDem,
|
||||
@ -81,6 +81,7 @@ fn main() {
|
||||
let (tx, rx) = sync_channel::<f32>(4096);
|
||||
|
||||
// Build input stream
|
||||
/*
|
||||
let stream = device
|
||||
.build_input_stream(
|
||||
&config.into(),
|
||||
@ -94,12 +95,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,7 +125,6 @@ fn main() {
|
||||
}
|
||||
writer.finalize().unwrap();
|
||||
});
|
||||
*/
|
||||
|
||||
let native_options = eframe::NativeOptions::default();
|
||||
let _ = eframe::run_native(
|
||||
@ -134,6 +134,75 @@ fn main() {
|
||||
);
|
||||
}
|
||||
|
||||
// Early late gate
|
||||
struct ELGate
|
||||
{
|
||||
samples_per_symbol: f32,
|
||||
buffer: VecDeque<f32>, // Store baseband, matched filtered samples,
|
||||
|
||||
loop_filter: FIRFilter,
|
||||
delta: f32,
|
||||
next_sample: f32,
|
||||
current_position: f32,
|
||||
}
|
||||
|
||||
impl ELGate
|
||||
{
|
||||
pub fn new(samples_per_symbol: f32, loop_filter: FIRFilter) -> Self
|
||||
{
|
||||
Self
|
||||
{
|
||||
samples_per_symbol,
|
||||
loop_filter,
|
||||
buffer: VecDeque::with_capacity(2 * samples_per_symbol.ceil() as usize),
|
||||
delta: 0.5,
|
||||
next_sample: samples_per_symbol,
|
||||
current_position: 0.
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next(&mut self, sample: f32) -> Option<bool>
|
||||
{
|
||||
Some(self.next_eye(sample)?.0) // Ignore eye
|
||||
}
|
||||
|
||||
pub fn next_eye(&mut self, sample: f32) -> Option<(bool, Vec<f32>)>
|
||||
{
|
||||
self.buffer.push_front(sample);
|
||||
self.current_position += 1.;
|
||||
if self.current_position >= self.next_sample
|
||||
{
|
||||
// Sample center, early late
|
||||
let early_id = (self.samples_per_symbol / 2. + self.samples_per_symbol * self.delta).floor().min(self.buffer.len() as f32 - 1.) as usize;
|
||||
let late_id = (self.samples_per_symbol / 2. - self.samples_per_symbol * self.delta).floor().max(0.) as usize;
|
||||
let sample_id = (self.samples_per_symbol / 2.) as usize;
|
||||
|
||||
let early_sample = self.buffer[early_id];
|
||||
let late_sample = self.buffer[late_id];
|
||||
let sample = self.buffer[sample_id];
|
||||
|
||||
let error = (early_sample - late_sample) * sample;
|
||||
|
||||
// Remove until only current sample is in buffer (the next sample might need data from
|
||||
// current sample if error advances sample position)
|
||||
while self.buffer.len() > self.samples_per_symbol as usize
|
||||
{
|
||||
let _ = self.buffer.pop_back();
|
||||
}
|
||||
|
||||
// Predict next length based on error
|
||||
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())))
|
||||
}
|
||||
else
|
||||
{
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn demodulator(
|
||||
rx: Receiver<f32>,
|
||||
eye_sender: Sender<Vec<f32>>,
|
||||
@ -173,18 +242,26 @@ fn demodulator(
|
||||
.collect::<Vec<_>>();
|
||||
let mut matched_filter_pos = FIRFilter::new(&corellator_pos);
|
||||
let mut matched_filter_neg = FIRFilter::new(&corellator_neg);
|
||||
matched_filter_pos.normalize_sum();
|
||||
matched_filter_neg.normalize_sum();
|
||||
|
||||
let mut elg_buffer = VecDeque::new();
|
||||
let mut sps = sample_per_symbol as f32;
|
||||
let delta = 0.5;
|
||||
let loop_p = 0.8;
|
||||
let loop_i = 0.2;
|
||||
let mut loop_filter = FIRFilter::new(&[Complex32::new(1., 0.); 30]);
|
||||
let mut matched_filter = FIRFilter::new(&[Complex32::new(1., 0.); 20]);
|
||||
let mut current_position = 0.;
|
||||
let mut next_sample = (sps as f32) / 2.;
|
||||
let loop_p = 0.3;
|
||||
let loop_i = 0.1;
|
||||
let mut matched_filter = FIRFilter::new(&[Complex32::new(1., 0.); 40]);
|
||||
matched_filter.normalize_sum();
|
||||
|
||||
//let mut dem = vec![];
|
||||
let mut loop_filter_ir = vec![Complex32::new(loop_i, 0.); 30];
|
||||
let len = loop_filter_ir.len();
|
||||
//loop_filter_ir[0] = Complex32::new(loop_p + loop_i, 0.);
|
||||
loop_filter_ir[len - 1] = Complex32::new(loop_p + loop_i, 0.);
|
||||
|
||||
let mut loop_filter = FIRFilter::new(&loop_filter_ir);
|
||||
//loop_filter.normalize_sum();
|
||||
let mut elg = ELGate::new(
|
||||
sample_per_symbol as f32,
|
||||
loop_filter
|
||||
);
|
||||
|
||||
// Timing recovery
|
||||
while let Ok(real_sample) = rx.recv() {
|
||||
let iq = iq_sampler.sample(real_sample);
|
||||
@ -194,30 +271,11 @@ fn demodulator(
|
||||
let pos_energy = matched_filter_pos.next(iq);
|
||||
|
||||
let matched = matched_filter.next_real(pos_energy.mag() - neg_energy.mag());
|
||||
//dem.push(matched);
|
||||
elg_buffer.push_front(matched);
|
||||
current_position += 1.;
|
||||
if current_position >= next_sample + sps / 2. {
|
||||
// Compute current error
|
||||
let early_id = (((sps / 2.) + sps * delta) as usize).min(elg_buffer.len() - 1);
|
||||
let late_id = (((sps / 2.) - sps * delta) as usize).max(0);
|
||||
let error =
|
||||
elg_buffer[(sps / 2.) as usize] * (elg_buffer[early_id] - elg_buffer[late_id]);
|
||||
|
||||
next_sample +=
|
||||
sps - loop_p * error - loop_i * loop_filter.next(Complex32::new(error, 0.)).re;
|
||||
while elg_buffer.len() > sps as usize {
|
||||
elg_buffer.pop_back();
|
||||
}
|
||||
let _ = eye_sender.send(Vec::from(elg_buffer.clone()));
|
||||
//elg_buffer.clear();
|
||||
if let Some((_, eye)) = elg.next_eye(matched)
|
||||
{
|
||||
let _ = eye_sender.send(eye);
|
||||
ctx.request_repaint();
|
||||
/*
|
||||
if dem.len() > 10_000 {
|
||||
let _ = eye_sender.send(dem.clone());
|
||||
break;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user