Working early late: 3Kb/s
This commit is contained in:
276
src/main.rs
276
src/main.rs
@ -23,8 +23,8 @@ use complex::Complex32;
|
||||
use fft::DFTAlgorithm;
|
||||
use nco::Nco;
|
||||
|
||||
use eframe::egui::{self, Color32, debug_text::print};
|
||||
use egui_plot::{self, Bar, BarChart, Legend, Line, LineStyle, Plot, PlotPoints};
|
||||
use eframe::egui::{self, Color32, Context, debug_text::print};
|
||||
use egui_plot::{self, Bar, BarChart, Legend, Line, LineStyle, Plot, PlotPoints, VLine};
|
||||
use plotters::style::Color;
|
||||
|
||||
use crate::{
|
||||
@ -49,9 +49,15 @@ where
|
||||
fn main() {
|
||||
modulate();
|
||||
println!("Demodulating");
|
||||
//demodulate();
|
||||
demodulate();
|
||||
|
||||
return;
|
||||
let native_options = eframe::NativeOptions::default();
|
||||
let _ = eframe::run_native("Egui", native_options, Box::new(|cc| Ok(Box::new(EguiApp::new(cc)))));
|
||||
let _ = eframe::run_native(
|
||||
"Egui",
|
||||
native_options,
|
||||
Box::new(|cc| Ok(Box::new(EguiApp::new(cc)))),
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -60,15 +66,19 @@ struct EguiApp {
|
||||
|
||||
iq: Vec<Complex32>,
|
||||
dem: Vec<f32>,
|
||||
vlines: Vec<f32>,
|
||||
elg_sampling: Vec<f32>,
|
||||
eye_diagram: Vec<Vec<f32>>,
|
||||
}
|
||||
const BAUD_RATE: u32 = 1000;
|
||||
const BAUD_RATE: u32 = 3200;
|
||||
|
||||
impl EguiApp {
|
||||
fn new(cc: &eframe::CreationContext<'_>) -> Self {
|
||||
let mut reader = hound::WavReader::open("audio/modulated.wav").unwrap();
|
||||
let samples = reader.samples::<i16>();
|
||||
let sample_count = 10_000;
|
||||
let input_test = samples
|
||||
.take(10_000)
|
||||
.take(sample_count)
|
||||
.map(|x| x.unwrap() as f32 / i16::MAX as f32)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@ -79,6 +89,11 @@ impl EguiApp {
|
||||
// Data parameters
|
||||
let sample_rate = 48000;
|
||||
let baud_rate = BAUD_RATE;
|
||||
let sample_per_symbol = sample_rate / baud_rate;
|
||||
let vlines = (0..sample_count)
|
||||
.filter(|i| i % sample_per_symbol as usize == 0)
|
||||
.map(|x| x as f32 / sample_count as f32)
|
||||
.collect();
|
||||
|
||||
let mut iq_sampler = IQSampler::new(hz_to_rad_per_sample(frequency, sample_rate as f32));
|
||||
let iq = input_test
|
||||
@ -86,29 +101,82 @@ impl EguiApp {
|
||||
.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![];
|
||||
let mut nco_pos = Nco::new(hz_to_rad_per_sample(deviation, sample_rate as f32));
|
||||
let mut nco_neg = Nco::new(hz_to_rad_per_sample(-deviation, sample_rate as f32));
|
||||
let corellator_pos = (0..(sample_per_symbol * 1))
|
||||
.map(|_| {
|
||||
nco_pos.step();
|
||||
nco_pos.cexp()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let corellator_neg = (0..(sample_per_symbol * 1))
|
||||
.map(|_| {
|
||||
nco_neg.step();
|
||||
nco_neg.cexp()
|
||||
})
|
||||
.collect::<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));
|
||||
let mut matched_filter_pos = FIRFilter::new(&corellator_pos);
|
||||
let mut matched_filter_neg = FIRFilter::new(&corellator_neg);
|
||||
|
||||
let mut dem = vec![];
|
||||
|
||||
let mut loop_filter = FIRFilter::new(&[Complex32::new(1., 0.); 1]);
|
||||
for x in &iq {
|
||||
let pos = matched_filter_pos.next(x.clone());
|
||||
let neg = matched_filter_neg.next(*x);
|
||||
dem.push(
|
||||
loop_filter
|
||||
.next(Complex32::new(pos.mag() - neg.mag(), 0.))
|
||||
.re,
|
||||
);
|
||||
}
|
||||
|
||||
// Symbol recovery
|
||||
|
||||
let mut sample_ids = vec![];
|
||||
let delta = 0.5;
|
||||
let alpha = 0.01;
|
||||
let mut current_sps = sample_per_symbol as f32;
|
||||
let mut current_position = current_sps / 2.;
|
||||
|
||||
let mut eye_diagram = vec![];
|
||||
while current_position < dem.len() as f32 {
|
||||
// Sample before after
|
||||
let early_id = (current_position - (delta * current_sps)).max(0.).floor() as u32;
|
||||
let late_id = (current_position + (delta * current_sps)).max(0.).floor() as u32;
|
||||
if late_id as usize >= dem.len() {
|
||||
break;
|
||||
}
|
||||
let early = dem[early_id as usize];
|
||||
let late = dem[late_id as usize];
|
||||
let error = early * early - late * late;
|
||||
current_sps -= alpha * error;
|
||||
|
||||
sample_ids.push(current_position);
|
||||
|
||||
let eye = ((current_position - current_sps).max(0.) as usize
|
||||
..(current_position + current_sps).min(sample_count as f32) as usize)
|
||||
.map(|i| dem[i])
|
||||
.collect();
|
||||
|
||||
eye_diagram.push(eye);
|
||||
|
||||
current_position += current_sps;
|
||||
}
|
||||
|
||||
let elg_sampling = sample_ids
|
||||
.iter()
|
||||
.map(|x| *x / sample_count as f32)
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
samples: input_test.clone(),
|
||||
iq,
|
||||
dem: demodulated
|
||||
.iter()
|
||||
.map(|b| if *b { 1. } else { 0. })
|
||||
.collect(),
|
||||
dem,
|
||||
vlines,
|
||||
elg_sampling,
|
||||
eye_diagram,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -120,9 +188,41 @@ impl eframe::App for EguiApp {
|
||||
Plot::new("Fourrier transform")
|
||||
.legend(Legend::default())
|
||||
.show(ui, |plot_ui| {
|
||||
for (i, eye) in self.eye_diagram.iter().enumerate().skip(1) {
|
||||
plot_ui.line(
|
||||
Line::new(
|
||||
"eye",
|
||||
eye.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| [i as f64 / eye.len() as f64, *x as f64])
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.width(1.)
|
||||
.color(Color32::RED),
|
||||
);
|
||||
}
|
||||
|
||||
self.vlines.iter().for_each(|l| {
|
||||
plot_ui.vline(
|
||||
VLine::new("Boundaries", *l as f64)
|
||||
.color(Color32::LIGHT_BLUE)
|
||||
.width(0.5)
|
||||
.style(LineStyle::dashed_dense()),
|
||||
);
|
||||
});
|
||||
|
||||
self.elg_sampling.iter().for_each(|l| {
|
||||
plot_ui.vline(
|
||||
VLine::new("ELG", *l as f64)
|
||||
.color(Color32::RED)
|
||||
.width(0.5)
|
||||
.style(LineStyle::dotted_dense()),
|
||||
);
|
||||
});
|
||||
|
||||
plot_ui.line(
|
||||
Line::new(
|
||||
"Transfer function",
|
||||
"Passband",
|
||||
self.samples
|
||||
.iter()
|
||||
.enumerate()
|
||||
@ -134,44 +234,11 @@ impl eframe::App for EguiApp {
|
||||
|
||||
plot_ui.line(
|
||||
Line::new(
|
||||
"RE",
|
||||
self.iq
|
||||
"Demodulated",
|
||||
self.dem
|
||||
.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,
|
||||
]
|
||||
})
|
||||
.map(|(i, x)| [i as f64 / self.iq.len() as f64, *x as f64])
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.width(2.),
|
||||
@ -189,33 +256,88 @@ fn demodulate() {
|
||||
let frequency = 1700.;
|
||||
let deviation = 500.;
|
||||
|
||||
// Data parameter
|
||||
// Data parameters
|
||||
let sample_rate = 48000;
|
||||
let baud_rate = BAUD_RATE;
|
||||
let sample_per_symbol = sample_rate / baud_rate;
|
||||
|
||||
let mut iq_sampler = IQSampler::new(hz_to_rad_per_sample(frequency, sample_rate as f32));
|
||||
let iq_samples = samples
|
||||
let iq = 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![];
|
||||
let mut nco_pos = Nco::new(hz_to_rad_per_sample(deviation, sample_rate as f32));
|
||||
let mut nco_neg = Nco::new(hz_to_rad_per_sample(-deviation, sample_rate as f32));
|
||||
let corellator_pos = (0..(sample_per_symbol * 1))
|
||||
.map(|_| {
|
||||
nco_pos.step();
|
||||
nco_pos.cexp()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let corellator_neg = (0..(sample_per_symbol * 1))
|
||||
.map(|_| {
|
||||
nco_neg.step();
|
||||
nco_neg.cexp()
|
||||
})
|
||||
.collect::<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));
|
||||
let mut matched_filter_pos = FIRFilter::new(&corellator_pos);
|
||||
let mut matched_filter_neg = FIRFilter::new(&corellator_neg);
|
||||
|
||||
let mut dem = vec![];
|
||||
|
||||
for x in &iq {
|
||||
let pos = matched_filter_pos.next(x.clone());
|
||||
let neg = matched_filter_neg.next(*x);
|
||||
dem.push(pos.mag() - neg.mag());
|
||||
}
|
||||
|
||||
assert!(bits.len() % 8 == 0);
|
||||
// Symbol recovery
|
||||
|
||||
let mut bits = vec![];
|
||||
let delta = 0.5;
|
||||
let alpha = 0.01;
|
||||
let mut current_sps = sample_per_symbol as f32;
|
||||
let mut current_position = current_sps / 2.;
|
||||
|
||||
while current_position < dem.len() as f32 {
|
||||
// Sample before after
|
||||
let early_id = (current_position - (delta * current_sps)).max(0.).floor() as u32;
|
||||
let late_id = (current_position + (delta * current_sps)).max(0.).floor() as u32;
|
||||
if late_id as usize >= dem.len() {
|
||||
break;
|
||||
}
|
||||
let early = dem[early_id as usize];
|
||||
let late = dem[late_id as usize];
|
||||
let error = early * early - late * late;
|
||||
current_sps -= alpha * error;
|
||||
|
||||
bits.push(dem[current_position.floor() as usize] > 0.);
|
||||
|
||||
current_position += current_sps;
|
||||
}
|
||||
|
||||
//assert!(bits.len() % 8 == 0);
|
||||
|
||||
let mut out_file = File::create("out.txt").unwrap();
|
||||
let mut strip = 0;
|
||||
|
||||
let bit_slice = bits.as_slice();
|
||||
for i in 0..100 {
|
||||
let byte = bits_to_byte(&bit_slice[(i as usize)..(i as usize + 8)]);
|
||||
if byte == 0b01010111u8 {
|
||||
strip = i + 8;
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..strip {
|
||||
bits.remove(i as usize);
|
||||
}
|
||||
for x in bits.chunks(8) {
|
||||
out_file.write_all(&[!bits_to_byte(x)]).unwrap();
|
||||
if x.len() != 8 {
|
||||
break;
|
||||
}
|
||||
out_file.write_all(&[bits_to_byte(x)]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,7 +352,11 @@ fn modulate() {
|
||||
|
||||
// File to modulate
|
||||
let f = File::open("s.txt").unwrap();
|
||||
let mut bitstream = f.bytes().flat_map(|b| byte_to_bits(b.unwrap()));
|
||||
let mut bitstream = std::iter::repeat_n(0b01010101u8, 1)
|
||||
.chain(std::iter::repeat_n(0b01010111u8, 1))
|
||||
.chain(f.bytes().map(|x| x.unwrap()))
|
||||
.chain(std::iter::repeat_n(0u8, 1))
|
||||
.flat_map(byte_to_bits);
|
||||
|
||||
let mut modulator = BFSKMod::new(
|
||||
sample_rate / baud_rate,
|
||||
|
||||
Reference in New Issue
Block a user