Merges transmit/recept
This commit is contained in:
172
src/main.rs
172
src/main.rs
@ -5,11 +5,13 @@ use std::{
|
||||
f32::consts::PI,
|
||||
fs::File,
|
||||
i16,
|
||||
io::{self, stdout, Read, Write},
|
||||
io::{self, Read, Write, stdout},
|
||||
ops::{Add, Div, Mul, Sub},
|
||||
os::unix::thread,
|
||||
sync::{
|
||||
atomic::{AtomicU32, Ordering}, mpsc::{self, channel, sync_channel, Receiver, Sender, TryRecvError}, Arc
|
||||
Arc,
|
||||
atomic::{AtomicU32, Ordering},
|
||||
mpsc::{self, Receiver, Sender, TryRecvError, channel, sync_channel},
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
@ -34,16 +36,23 @@ use cpal::{
|
||||
use fft::DFTAlgorithm;
|
||||
use nco::Nco;
|
||||
|
||||
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 eframe::{
|
||||
egui::{self, Color32, Context, Vec2b, debug_text::print, decode_animated_image_uri},
|
||||
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 rand::{Rng, seq::index::sample};
|
||||
|
||||
use crate::{
|
||||
bfsk::BFSKDem,
|
||||
fft::{dft::NaiveDFT, FFT},
|
||||
fft::{FFT, dft::NaiveDFT},
|
||||
filtering::{
|
||||
dc_block::DCBlocker, 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},
|
||||
@ -133,8 +142,7 @@ fn main() {
|
||||
}
|
||||
|
||||
// Early late gate
|
||||
struct ELGate
|
||||
{
|
||||
struct ELGate {
|
||||
samples_per_symbol: f32,
|
||||
buffer: VecDeque<f32>, // Store baseband, matched filtered samples,
|
||||
|
||||
@ -144,35 +152,33 @@ struct ELGate
|
||||
current_position: f32,
|
||||
}
|
||||
|
||||
impl ELGate
|
||||
{
|
||||
pub fn new(samples_per_symbol: f32, loop_filter: FIRFilter) -> Self
|
||||
{
|
||||
Self
|
||||
{
|
||||
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.
|
||||
current_position: 0.,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next(&mut self, sample: f32) -> Option<f32>
|
||||
{
|
||||
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<(f32, Vec<f32>)>
|
||||
{
|
||||
self.buffer.push_front(sample);
|
||||
pub fn next_eye(&mut self, sample: f32) -> Option<(f32, Vec<f32>)> {
|
||||
self.buffer.push_front(sample);
|
||||
self.current_position += 1.;
|
||||
if self.current_position >= self.next_sample
|
||||
{
|
||||
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 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];
|
||||
@ -183,8 +189,7 @@ impl ELGate
|
||||
|
||||
// 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
|
||||
{
|
||||
while self.buffer.len() > self.samples_per_symbol as usize {
|
||||
let _ = self.buffer.pop_back();
|
||||
}
|
||||
|
||||
@ -193,9 +198,7 @@ impl ELGate
|
||||
self.next_sample = self.samples_per_symbol - self.loop_filter.next_real(error);
|
||||
|
||||
Some((sample, Vec::from(self.buffer.clone())))
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
@ -255,13 +258,10 @@ fn demodulator(
|
||||
|
||||
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
|
||||
);
|
||||
let mut elg = ELGate::new(sample_per_symbol as f32, loop_filter);
|
||||
|
||||
let mut dc_blocker = DCBlocker::new(0.999);
|
||||
|
||||
|
||||
// Timing recovery
|
||||
let mut preamble_count = 0;
|
||||
let mut bit_index = 0;
|
||||
@ -273,49 +273,40 @@ fn demodulator(
|
||||
let neg_energy = matched_filter_neg.next(iq);
|
||||
let pos_energy = matched_filter_pos.next(iq);
|
||||
|
||||
let matched = matched_filter.next_real(dc_blocker.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((elg_sample, 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) << 7;
|
||||
let bit = elg_sample > 0.;
|
||||
if elg_sample*elg_sample > 0.005
|
||||
{
|
||||
if elg_sample * elg_sample > 0.005 {
|
||||
last >>= 1;
|
||||
last |= (bit as u8) << 7;
|
||||
//last <<= 1;
|
||||
//last |= ((bit) as u8);
|
||||
|
||||
if preamble_count >= 2
|
||||
{
|
||||
if preamble_count >= 2 {
|
||||
bit_index += 1;
|
||||
if bit_index >= 8
|
||||
{
|
||||
if last == 4
|
||||
{
|
||||
if bit_index >= 8 {
|
||||
if last == 4 {
|
||||
print!(" -- EOT");
|
||||
println!();
|
||||
preamble_count = 0;
|
||||
bit_index = 0;
|
||||
last = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
print!("{}", last as char);
|
||||
bit_index = 0;
|
||||
let _ = stdout().flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if last == 0xD8
|
||||
{
|
||||
} else if last == 0xD8 {
|
||||
preamble_count += 1;
|
||||
if preamble_count == 2
|
||||
{
|
||||
if preamble_count == 2 {
|
||||
println!("Incoming: ");
|
||||
}
|
||||
}
|
||||
@ -357,9 +348,8 @@ impl eframe::App for EguiApp {
|
||||
while self.eyes.len() > max_eyes {
|
||||
self.eyes.pop_front();
|
||||
}
|
||||
|
||||
let axis_hints = AxisHints::new_x()
|
||||
.min_thickness(2.);
|
||||
|
||||
let axis_hints = AxisHints::new_x().min_thickness(2.);
|
||||
|
||||
Plot::new("Eye")
|
||||
.legend(Legend::default())
|
||||
@ -490,23 +480,29 @@ fn modulate() {
|
||||
let device = host.default_output_device().unwrap();
|
||||
let mut supported_configs_range = device.supported_output_configs().unwrap();
|
||||
let supported_config = supported_configs_range
|
||||
.find(|config| config.sample_format() == cpal::SampleFormat::F32
|
||||
&& config.min_sample_rate().0 <= 48000
|
||||
&& config.max_sample_rate().0 >= 48000)
|
||||
.find(|config| {
|
||||
config.sample_format() == cpal::SampleFormat::F32
|
||||
&& config.min_sample_rate().0 <= 48000
|
||||
&& config.max_sample_rate().0 >= 48000
|
||||
})
|
||||
.expect("Device does not support 48kHz f32 output");
|
||||
let config = supported_config.with_sample_rate(cpal::SampleRate(48_000)).config();
|
||||
let config = supported_config
|
||||
.with_sample_rate(cpal::SampleRate(48_000))
|
||||
.config();
|
||||
|
||||
|
||||
loop
|
||||
{
|
||||
loop {
|
||||
let mut buffer = String::new();
|
||||
let stdin = io::stdin(); // We get `Stdin` here.
|
||||
stdin.read_line(&mut buffer).unwrap();
|
||||
|
||||
for c in buffer.bytes() {
|
||||
print!("{}", c as char);
|
||||
}
|
||||
println!();
|
||||
|
||||
// Construct payload
|
||||
let mut bitstream =
|
||||
std::iter::repeat_n(0b01010101u8, 32)
|
||||
.chain(std::iter::repeat_n(0b11011000, 1))
|
||||
let mut bitstream = std::iter::repeat_n(0b01010101u8, 64)
|
||||
.chain(std::iter::repeat_n(0xD8, 2))
|
||||
.chain(buffer.bytes())
|
||||
.chain(std::iter::repeat_n(4u8, 32))
|
||||
.flat_map(byte_to_bits);
|
||||
@ -518,37 +514,37 @@ fn modulate() {
|
||||
);
|
||||
|
||||
let mut lo = Nco::new(units::frequency::hz_to_rad_per_sample(
|
||||
frequency,
|
||||
sample_rate as f32,
|
||||
frequency,
|
||||
sample_rate as f32,
|
||||
));
|
||||
|
||||
// To send
|
||||
let stream = modulator.zip(lo)
|
||||
let stream = modulator
|
||||
.zip(lo)
|
||||
.map(|(s, up)| (s * up).re)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let sample_clock = Arc::new(AtomicU32::new(0));
|
||||
let (tx, rx) = channel::<()>();
|
||||
|
||||
let stream = device.build_output_stream(
|
||||
&config,
|
||||
move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
|
||||
for d in data.iter_mut()
|
||||
{
|
||||
if sample_clock.load(Ordering::Relaxed) as usize == stream.len()
|
||||
{
|
||||
tx.send(()).unwrap();
|
||||
break;
|
||||
let stream = device
|
||||
.build_output_stream(
|
||||
&config,
|
||||
move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
|
||||
for d in data.iter_mut() {
|
||||
if sample_clock.load(Ordering::Relaxed) as usize == stream.len() {
|
||||
tx.send(()).unwrap();
|
||||
break;
|
||||
}
|
||||
*d = stream[sample_clock.fetch_add(1, Ordering::Relaxed) as usize]
|
||||
}
|
||||
*d = stream[sample_clock.fetch_add(1, Ordering::Relaxed) as usize]
|
||||
}
|
||||
|
||||
},
|
||||
move |err| {
|
||||
eprintln!("Stream error: {}", err);
|
||||
},
|
||||
None
|
||||
).unwrap();
|
||||
},
|
||||
move |err| {
|
||||
eprintln!("Stream error: {}", err);
|
||||
},
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
stream.play().unwrap();
|
||||
let _ = rx.recv();
|
||||
|
||||
Reference in New Issue
Block a user