Bunch'o things
This commit is contained in:
388
src/main.rs
388
src/main.rs
@ -1,102 +1,59 @@
|
||||
use std::any;
|
||||
use std::collections::VecDeque;
|
||||
use std::collections::btree_map::RangeMut;
|
||||
use std::f64::consts::PI;
|
||||
use std::fmt::Display;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::time::Duration;
|
||||
|
||||
use cpal::default_host;
|
||||
use cpal::traits::DeviceTrait;
|
||||
use cpal::traits::HostTrait;
|
||||
use eframe::NativeOptions;
|
||||
use egui::Color32;
|
||||
use egui::IntoAtoms;
|
||||
use egui_plot::Line;
|
||||
use egui_plot::Plot;
|
||||
use egui_plot::PlotPoint;
|
||||
use egui_plot::PlotPoints;
|
||||
use ntw_dsp::Frequency;
|
||||
use ntw_dsp::blocks::early_late_gate::EarlyLateGate;
|
||||
use ntw_dsp::blocks::early_late_gate::EyeExtractor;
|
||||
use ntw_dsp::blocks::fft::Fft;
|
||||
use ntw_dsp::blocks::fir::Fir;
|
||||
use ntw_dsp::blocks::fir::FirFft;
|
||||
use ntw_dsp::blocks::iir::SimpleDcBlock;
|
||||
use ntw_dsp::blocks::iq::IqSampler;
|
||||
use ntw_dsp::blocks::iter::FiniteIterSource;
|
||||
use ntw_dsp::blocks::map::Map;
|
||||
use ntw_dsp::blocks::math::Multiplier;
|
||||
use ntw_dsp::blocks::nco::ComplexNco;
|
||||
use ntw_dsp::blocks::oscillator::ComplexOscillator;
|
||||
use ntw_dsp::blocks::tee::Tee;
|
||||
use ntw_dsp::blocks::utilities::ChannelSink;
|
||||
use ntw_dsp::blocks::utilities::ChannelSource;
|
||||
use ntw_dsp::blocks::utilities::Repeat;
|
||||
use ntw_dsp::blocks::utilities::SimpleAgc;
|
||||
use ntw_dsp::blocks::utilities::SimpleSquelch;
|
||||
use ntw_dsp::blocks::utilities::Windows;
|
||||
use ntw_dsp::filtering::fir::estimate_fir_length;
|
||||
use ntw_dsp::filtering::impulse_response::ImpulseResponse;
|
||||
use ntw_dsp::filtering::impulse_response::window;
|
||||
use ntw_dsp::generation::Nco;
|
||||
use ntw_flowgraph::Block;
|
||||
use ntw_flowgraph::BlockResult;
|
||||
use ntw_flowgraph::BlockWork;
|
||||
use ntw_flowgraph::graph;
|
||||
use ntw_flowgraph::graph::Graph;
|
||||
use ntw_flowgraph::inout::In;
|
||||
use ntw_flowgraph::inout::Out;
|
||||
use ntw_flowgraph::inout::Stream;
|
||||
use ntw_flowgraph_macros::Block;
|
||||
use num::Complex;
|
||||
use num::One;
|
||||
use num::Zero;
|
||||
use num::complex::Complex32;
|
||||
use rand::random;
|
||||
use ringbuf::traits::Consumer;
|
||||
use ringbuf::traits::Observer;
|
||||
use ringbuf::traits::Producer;
|
||||
|
||||
#[derive(Block)]
|
||||
pub struct VecSource
|
||||
{
|
||||
vector: Vec<u32>,
|
||||
|
||||
#[output]
|
||||
out: Out<u32>,
|
||||
}
|
||||
|
||||
impl BlockWork for VecSource
|
||||
{
|
||||
fn work(&mut self)
|
||||
{
|
||||
while let Some(element) = self.vector.pop()
|
||||
{
|
||||
match self.out.rb.try_push(element)
|
||||
{
|
||||
Ok(()) =>
|
||||
{}
|
||||
Err(x) =>
|
||||
{
|
||||
self.vector.push(x);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ready(&self) -> bool
|
||||
{
|
||||
!self.vector.is_empty() && self.out.vacant_len() > 0
|
||||
}
|
||||
}
|
||||
|
||||
impl VecSource
|
||||
{
|
||||
pub fn new(vector: Vec<u32>) -> (VecSource, In<u32>)
|
||||
{
|
||||
let (out, stream) = Stream::make(16);
|
||||
(VecSource { vector, out }, stream)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Block)]
|
||||
pub struct Adder
|
||||
{
|
||||
#[input]
|
||||
in_a: In<u32>,
|
||||
|
||||
#[input]
|
||||
in_b: In<u32>,
|
||||
|
||||
#[output]
|
||||
out: Out<u32>,
|
||||
}
|
||||
|
||||
impl BlockWork for Adder
|
||||
{
|
||||
fn work(&mut self)
|
||||
{
|
||||
while let Some(a) = self.in_a.rb.try_pop()
|
||||
&& let Some(b) = self.in_b.rb.try_pop()
|
||||
&& self.out.rb.vacant_len() > 0
|
||||
{
|
||||
let _ = self.out.try_push(a + b);
|
||||
}
|
||||
}
|
||||
|
||||
fn ready(&self) -> bool
|
||||
{
|
||||
self.in_a.available_len() > 0 && self.in_b.available_len() > 0 && self.out.vacant_len() > 0
|
||||
}
|
||||
}
|
||||
|
||||
impl Adder
|
||||
{
|
||||
pub fn new(in_a: In<u32>, in_b: In<u32>) -> (Adder, In<u32>)
|
||||
{
|
||||
let (out, stream) = Stream::make(16);
|
||||
(Adder { in_a, in_b, out }, stream)
|
||||
}
|
||||
}
|
||||
use rustfft::FftDirection;
|
||||
|
||||
#[derive(Block)]
|
||||
pub struct PrintSink<T>
|
||||
@ -107,15 +64,16 @@ pub struct PrintSink<T>
|
||||
|
||||
impl<T: Display> BlockWork for PrintSink<T>
|
||||
{
|
||||
fn work(&mut self)
|
||||
fn work(&mut self) -> BlockResult
|
||||
{
|
||||
if let Some(x) = self.stream.rb.try_pop()
|
||||
{
|
||||
println!("{x}");
|
||||
}
|
||||
BlockResult::Ok
|
||||
}
|
||||
|
||||
fn ready(&self) -> bool
|
||||
fn ready(&mut self) -> bool
|
||||
{
|
||||
self.stream.available_len() > 0
|
||||
}
|
||||
@ -129,17 +87,245 @@ impl<T> PrintSink<T>
|
||||
}
|
||||
}
|
||||
|
||||
const SAMPLE_RATE: usize = 48_000;
|
||||
const FREQUENCY_OFFSET: usize = 1000;
|
||||
const CARRIER_FREQUENCY: usize = 1700;
|
||||
const SAMPLES_PER_SYMBOL: usize = 40;
|
||||
|
||||
fn main()
|
||||
{
|
||||
let (nco, out) = ComplexNco::<f32>::new(Nco::new(Frequency::from_rad(0.001)));
|
||||
let (map, im) = Map::new(out, |x| x.im);
|
||||
let printer = PrintSink::new(im);
|
||||
|
||||
let mut graph = Graph::new();
|
||||
|
||||
graph.add_block(printer);
|
||||
graph.add_block(map);
|
||||
graph.add_block(nco);
|
||||
|
||||
graph.run();
|
||||
modulate();
|
||||
demodulate();
|
||||
}
|
||||
|
||||
fn demodulate()
|
||||
{
|
||||
let host = cpal::default_host();
|
||||
let device = host
|
||||
.default_input_device()
|
||||
.expect("no output device available");
|
||||
println!("input device: {}", device.description().unwrap());
|
||||
let mut supported_configs_range = device
|
||||
.supported_input_configs()
|
||||
.expect("error while querying configs");
|
||||
let supported_config = supported_configs_range
|
||||
.next()
|
||||
.expect("no supported config?!")
|
||||
.with_sample_rate(SAMPLE_RATE as u32);
|
||||
|
||||
// Synthetise baseband filter
|
||||
let baseband_fir_len = estimate_fir_length(500., SAMPLE_RATE as f32) as usize;
|
||||
let cutoff_bin =
|
||||
((Frequency::from_frequency(FREQUENCY_OFFSET as f64 + 100., SAMPLE_RATE as f64).as_rad()
|
||||
/ (2. * PI))
|
||||
* baseband_fir_len as f64) as usize;
|
||||
let mut transfer_function = vec![Complex32::zero(); baseband_fir_len];
|
||||
for i in 0..cutoff_bin
|
||||
{
|
||||
transfer_function[i] = Complex32::one();
|
||||
transfer_function[baseband_fir_len - 1 - i] = Complex32::one();
|
||||
}
|
||||
let baseband_fir = ImpulseResponse::from_transfer_function(transfer_function.as_slice())
|
||||
.normalized()
|
||||
.windowed(window::blackmann);
|
||||
|
||||
let mut reader = hound::WavReader::open("mod.wav").unwrap();
|
||||
let (audio_tx, audio_rx) = std::sync::mpsc::sync_channel(100_000);
|
||||
let (eye_tx, eye_rx) = std::sync::mpsc::channel();
|
||||
|
||||
let (input_block, input) = ChannelSource::new(audio_rx);
|
||||
let (iq_sampler, iq) = IqSampler::<f32>::new(
|
||||
input,
|
||||
Frequency::from_frequency(CARRIER_FREQUENCY as f64, SAMPLE_RATE as f64),
|
||||
);
|
||||
let (fir_block, iq) = Fir::new(iq, baseband_fir);
|
||||
let (sq_block, iq) = SimpleSquelch::new(iq, 300, 0.005);
|
||||
//let (agc_block, iq) = SimpleAgc::new(iq, 200);
|
||||
let (window_block, windows) = Windows::<_, 2>::new(iq);
|
||||
let (diff_block, angle) = Map::new(windows, |[a, b]| (a / b).arg() * 15.);
|
||||
//let (dc_block, angle) = SimpleDcBlock::new(angle, 0.99.into());
|
||||
let (low_pass_block, angle) = Fir::new(
|
||||
angle,
|
||||
ImpulseResponse(vec![1.; SAMPLES_PER_SYMBOL / 2]).normalized(),
|
||||
);
|
||||
|
||||
let mut pi = vec![1. / 30.; 50];
|
||||
*pi.last_mut().unwrap() = 1.;
|
||||
let (elg_block, samples) = EarlyLateGate::new(angle, ImpulseResponse(pi), SAMPLES_PER_SYMBOL);
|
||||
let eye_block = EyeExtractor::new(samples, eye_tx, SAMPLES_PER_SYMBOL);
|
||||
|
||||
let graph = graph![
|
||||
input_block,
|
||||
iq_sampler,
|
||||
window_block,
|
||||
diff_block,
|
||||
elg_block,
|
||||
low_pass_block,
|
||||
eye_block,
|
||||
fir_block,
|
||||
sq_block,
|
||||
//dc_block //fir_block
|
||||
];
|
||||
graph.run();
|
||||
|
||||
// let samples = reader.samples().map(|x| x.unwrap()).collect::<Vec<i16>>();
|
||||
// std::thread::spawn(move || {
|
||||
// loop
|
||||
// {
|
||||
// samples
|
||||
// .iter()
|
||||
// //.skip(rand::random_range(0..100))
|
||||
// .map(|x| *x as f32 / i16::MAX as f32)
|
||||
// .for_each(|x| audio_tx.send(x).unwrap());
|
||||
// }
|
||||
// });
|
||||
|
||||
let stream = device.build_input_stream(
|
||||
&supported_config.into(),
|
||||
move |data: &[f32], _: &cpal::InputCallbackInfo| {
|
||||
data.iter().for_each(|x| {
|
||||
let _ = audio_tx.send(*x / 2.);
|
||||
});
|
||||
},
|
||||
move |err| {
|
||||
// react to errors here.
|
||||
},
|
||||
None, // None=blocking, Some(Duration)=timeout
|
||||
);
|
||||
|
||||
let mut eyes = VecDeque::new();
|
||||
println!("Running ui");
|
||||
eframe::run_simple_native("Demod", NativeOptions::default(), move |ctx, _frame| {
|
||||
while let Ok(x) = eye_rx.try_recv()
|
||||
{
|
||||
eyes.push_back(x);
|
||||
}
|
||||
|
||||
while eyes.len() > 300
|
||||
{
|
||||
eyes.pop_front();
|
||||
}
|
||||
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
egui_plot::Plot::new("Demod").show(ui, |plot_ui| {
|
||||
for eye in eyes.iter()
|
||||
{
|
||||
plot_ui.line(
|
||||
Line::new(
|
||||
"Angle",
|
||||
eye.iter()
|
||||
.enumerate()
|
||||
.map(|(i, x)| [i as f64, *x as f64])
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.color(Color32::GREEN),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
ctx.request_repaint();
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn modulate()
|
||||
{
|
||||
// Simple FSK modulation
|
||||
let data = (0u8..=255u8)
|
||||
.map(|_| rand::random::<u8>())
|
||||
.flat_map(to_bits); // Data to modulate
|
||||
let frequency_offset = Frequency::from_frequency(FREQUENCY_OFFSET as f64, SAMPLE_RATE as f64); // Bandwidth
|
||||
|
||||
let (bit_stream, bits) = FiniteIterSource::new(data, 255 * 8); // Iterator on data to modulate
|
||||
let (map, freq) = Map::new(bits, move |bit| [1., -1.][bit as usize]); // Computes frequency offset based on bit
|
||||
let (hold, freq) = Repeat::new(freq, SAMPLES_PER_SYMBOL); // Holds that frequency for 100 samples/symbol
|
||||
let (freq_fir, freq) = Fir::new(
|
||||
freq,
|
||||
ImpulseResponse(
|
||||
(0..SAMPLES_PER_SYMBOL)
|
||||
.map(|t| gaussian(0.3, t as f32 / SAMPLES_PER_SYMBOL as f32))
|
||||
.collect(),
|
||||
)
|
||||
.normalized(),
|
||||
);
|
||||
let (bit_to_freq, freq) = Map::new(freq, |x| {
|
||||
Frequency::from_frequency(FREQUENCY_OFFSET as f64 * x as f64, SAMPLE_RATE as f64)
|
||||
});
|
||||
let (baseband_nco, baseband) = ComplexNco::<f32>::new(freq); // Baseband oscillatore
|
||||
let (local_o, lo) = ComplexOscillator::<f32>::new(Nco::new(Frequency::from_frequency(
|
||||
CARRIER_FREQUENCY as f64,
|
||||
SAMPLE_RATE as f64,
|
||||
))); // Upconverter
|
||||
|
||||
let (prod_block, passband) = Multiplier::<_, _, Complex<f32>>::new(baseband, lo); // Multiply
|
||||
let (output, rx) = ChannelSink::new(passband); // Output in channel
|
||||
let graph = graph![
|
||||
bit_stream,
|
||||
map,
|
||||
output,
|
||||
baseband_nco,
|
||||
local_o,
|
||||
hold,
|
||||
prod_block,
|
||||
freq_fir,
|
||||
bit_to_freq
|
||||
];
|
||||
graph.run();
|
||||
|
||||
let mut output = vec![];
|
||||
while let Ok(x) = rx.recv()
|
||||
{
|
||||
output.push(x);
|
||||
}
|
||||
|
||||
println!("length: {}", output.len());
|
||||
|
||||
let spec = hound::WavSpec {
|
||||
channels: 1,
|
||||
sample_rate: 48000,
|
||||
bits_per_sample: 16,
|
||||
|
||||
sample_format: hound::SampleFormat::Int,
|
||||
};
|
||||
let mut writer = hound::WavWriter::create("mod.wav", spec).unwrap();
|
||||
for t in output
|
||||
{
|
||||
let amplitude = i16::MAX as f32;
|
||||
writer
|
||||
.write_sample(((t.re + rand::random::<f32>() * 0.0) * amplitude) as i16)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bits(n: u8) -> [bool; 8]
|
||||
{
|
||||
[
|
||||
(n & 1) == 1,
|
||||
(n >> 1) & 1 == 1,
|
||||
(n >> 2) & 1 == 1,
|
||||
(n >> 3) & 1 == 1,
|
||||
(n >> 4) & 1 == 1,
|
||||
(n >> 5) & 1 == 1,
|
||||
(n >> 6) & 1 == 1,
|
||||
(n >> 7) & 1 == 1,
|
||||
]
|
||||
}
|
||||
|
||||
pub fn from_bits(n: [bool; 8]) -> u8
|
||||
{
|
||||
(n[0] as u8)
|
||||
| ((n[1] as u8) << 1)
|
||||
| ((n[2] as u8) << 2)
|
||||
| ((n[3] as u8) << 3)
|
||||
| ((n[4] as u8) << 4)
|
||||
| ((n[5] as u8) << 5)
|
||||
| ((n[6] as u8) << 6)
|
||||
| ((n[7] as u8) << 7)
|
||||
}
|
||||
|
||||
pub fn gaussian(sigma: f32, t: f32) -> f32
|
||||
{
|
||||
let sq = (t - 0.5) / sigma;
|
||||
(-sq * sq).exp()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user