Compare commits
2 Commits
e1c7ba7865
...
2d49da5cee
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d49da5cee | |||
| 1513fd694d |
4486
Cargo.lock
generated
4486
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -4,4 +4,8 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
eframe = "0.34.3"
|
||||||
|
egui = "0.34.3"
|
||||||
|
egui_plot = "0.35.0"
|
||||||
|
indicatif = "0.18.4"
|
||||||
num-complex = "0.4.6"
|
num-complex = "0.4.6"
|
||||||
|
|||||||
42
src/dc_blocker.rs
Normal file
42
src/dc_blocker.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
use crate::fm_demod::Sample;
|
||||||
|
|
||||||
|
pub struct DcBlocker<I> {
|
||||||
|
pub inner: I,
|
||||||
|
pub x_prev: Sample,
|
||||||
|
pub y_prev: Sample,
|
||||||
|
pub alpha: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> DcBlocker<I> {
|
||||||
|
pub fn new(inner: I, alpha: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
inner,
|
||||||
|
x_prev: 0.0,
|
||||||
|
y_prev: 0.0,
|
||||||
|
alpha,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_sample(&mut self, x_n: Sample) -> Sample {
|
||||||
|
let y_n = x_n - self.x_prev + self.alpha * self.y_prev;
|
||||||
|
|
||||||
|
self.x_prev = x_n;
|
||||||
|
self.y_prev = y_n;
|
||||||
|
|
||||||
|
y_n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, E> Iterator for DcBlocker<I>
|
||||||
|
where
|
||||||
|
I: Iterator<Item = Result<Sample, E>>,
|
||||||
|
{
|
||||||
|
type Item = Result<Sample, E>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match self.inner.next()? {
|
||||||
|
Ok(sample) => Some(Ok(self.process_sample(sample))),
|
||||||
|
Err(e) => Some(Err(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -28,12 +28,14 @@ pub struct FileSource {
|
|||||||
pub chunk_samples_size: usize,
|
pub chunk_samples_size: usize,
|
||||||
// raw_buffer position (in samples)
|
// raw_buffer position (in samples)
|
||||||
pub cursor: usize,
|
pub cursor: usize,
|
||||||
|
// The length of the file in samples
|
||||||
|
pub len: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileSource {
|
impl FileSource {
|
||||||
pub fn new(file_path: &str, chunk_samples_size: usize) -> Result<Self, Box<dyn Error>> {
|
pub fn new(file_path: &str, chunk_samples_size: usize) -> Result<Self, Box<dyn Error>> {
|
||||||
let file = File::open(file_path)?;
|
let file = File::open(file_path)?;
|
||||||
|
let len = file.metadata().unwrap().len() / 2;
|
||||||
// Init buffer with size 16 Mo
|
// Init buffer with size 16 Mo
|
||||||
let reader = BufReader::with_capacity(16 * 1024 * 1024, file);
|
let reader = BufReader::with_capacity(16 * 1024 * 1024, file);
|
||||||
|
|
||||||
@ -45,13 +47,20 @@ impl FileSource {
|
|||||||
raw_buffer: raw_reader,
|
raw_buffer: raw_reader,
|
||||||
chunk_samples_size,
|
chunk_samples_size,
|
||||||
cursor: chunk_samples_size,
|
cursor: chunk_samples_size,
|
||||||
|
len,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ExactSizeIterator for FileSource {}
|
||||||
|
|
||||||
impl Iterator for FileSource {
|
impl Iterator for FileSource {
|
||||||
type Item = Result<IqSample, Box<dyn Error>>;
|
type Item = Result<IqSample, Box<dyn Error>>;
|
||||||
|
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
(self.len as usize, Some(self.len as usize))
|
||||||
|
}
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.cursor >= self.chunk_samples_size {
|
if self.cursor >= self.chunk_samples_size {
|
||||||
// Buffer read chunk
|
// Buffer read chunk
|
||||||
|
|||||||
39
src/low_pass.rs
Normal file
39
src/low_pass.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use crate::fm_demod::Sample;
|
||||||
|
|
||||||
|
pub struct LowPass<I> {
|
||||||
|
inner: I,
|
||||||
|
y_prev: Sample,
|
||||||
|
alpha: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> LowPass<I> {
|
||||||
|
pub fn new(inner: I, alpha: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
inner,
|
||||||
|
y_prev: 0.0,
|
||||||
|
alpha,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_sample(&mut self, x_n: Sample) -> Sample {
|
||||||
|
let y_n = self.alpha * x_n + (1.0 - self.alpha) * self.y_prev;
|
||||||
|
|
||||||
|
self.y_prev = y_n;
|
||||||
|
|
||||||
|
y_n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, E> Iterator for LowPass<I>
|
||||||
|
where
|
||||||
|
I: Iterator<Item = Result<Sample, E>>,
|
||||||
|
{
|
||||||
|
type Item = Result<Sample, E>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match self.inner.next()? {
|
||||||
|
Ok(sample) => Some(Ok(self.process_sample(sample))),
|
||||||
|
Err(e) => Some(Err(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/main.rs
64
src/main.rs
@ -1,29 +1,65 @@
|
|||||||
use crate::iq_reader::FileSource;
|
use crate::iq_reader::FileSource;
|
||||||
use crate::pipeline::DspPipelineExt;
|
use crate::pipeline::{AfterDemodPiplineExt, DspPipelineExt};
|
||||||
use std::error::Error;
|
use egui_plot::{Line, Plot};
|
||||||
|
use indicatif::ProgressIterator;
|
||||||
|
|
||||||
mod agc;
|
mod agc;
|
||||||
|
mod dc_blocker;
|
||||||
mod fir;
|
mod fir;
|
||||||
mod fm_demod;
|
mod fm_demod;
|
||||||
mod iq_reader;
|
mod iq_reader;
|
||||||
|
mod low_pass;
|
||||||
mod pipeline;
|
mod pipeline;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> eframe::Result {
|
||||||
let source = FileSource::new("test.iq", 32769)?;
|
let source = FileSource::new("test.iq", 32769).unwrap();
|
||||||
|
|
||||||
// Fir coefs
|
|
||||||
let taps = [0.5; 64];
|
let taps = [0.5; 64];
|
||||||
|
|
||||||
let pipeline = source
|
let samples: Vec<f32> = source
|
||||||
|
.progress()
|
||||||
.agc(20_000_000.0, 1.0, 0.001, 100.0)
|
.agc(20_000_000.0, 1.0, 0.001, 100.0)
|
||||||
.fir::<64>(taps, 4)
|
//.fir::<64>(taps, 4)
|
||||||
.demodulate();
|
.demodulate()
|
||||||
|
.dc_block(0.995)
|
||||||
|
.lowpass(0.75)
|
||||||
|
.filter_map(|r| r.ok())
|
||||||
|
.collect();
|
||||||
|
|
||||||
for phase_r in pipeline {
|
eframe::run_ui_native("DSP", Default::default(), move |ui, _frame| {
|
||||||
let phase = phase_r?;
|
egui::CentralPanel::default().show(ui, |ui| {
|
||||||
// println!("phase : {}", phase);
|
Plot::new("plot").show(ui, |plot_ui| {
|
||||||
|
plot_ui.line(Line::new(
|
||||||
|
"Signal",
|
||||||
|
samples
|
||||||
|
.iter()
|
||||||
|
.take(10000)
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, x)| [i as f64, *x as f64])
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
// fn main() -> Result<(), Box<dyn Error>> {
|
||||||
}
|
// let source = FileSource::new("test.iq", 32769)?;
|
||||||
|
//
|
||||||
|
// // Fir coefs
|
||||||
|
// let taps = [0.5; 64];
|
||||||
|
//
|
||||||
|
// let pipeline = source
|
||||||
|
// .agc(20_000_000.0, 1.0, 0.001, 100.0)
|
||||||
|
// .fir::<64>(taps, 4)
|
||||||
|
// .demodulate()
|
||||||
|
// .dc_block(0.995)
|
||||||
|
// .lowpass(0.75);
|
||||||
|
//
|
||||||
|
// for s_r in pipeline {
|
||||||
|
// let s = s_r?;
|
||||||
|
// println!("phase : {}", s);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
use crate::agc::Agc;
|
use crate::agc::Agc;
|
||||||
|
use crate::dc_blocker::DcBlocker;
|
||||||
use crate::fir::Fir;
|
use crate::fir::Fir;
|
||||||
use crate::fm_demod::FmDemod;
|
use crate::fm_demod::{FmDemod, Sample};
|
||||||
use crate::iq_reader::IqSample;
|
use crate::iq_reader::IqSample;
|
||||||
|
use crate::low_pass::LowPass;
|
||||||
|
|
||||||
pub trait DspPipelineExt<E>: Iterator<Item = Result<IqSample, E>> + Sized {
|
pub trait DspPipelineExt<E>: Iterator<Item = Result<IqSample, E>> + Sized {
|
||||||
fn agc(self, sample_rate: f32, target_power: f32, min_gain: f32, max_gain: f32) -> Agc<Self> {
|
fn agc(self, sample_rate: f32, target_power: f32, min_gain: f32, max_gain: f32) -> Agc<Self> {
|
||||||
@ -18,3 +20,15 @@ pub trait DspPipelineExt<E>: Iterator<Item = Result<IqSample, E>> + Sized {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<I, E> DspPipelineExt<E> for I where I: Iterator<Item = Result<IqSample, E>> {}
|
impl<I, E> DspPipelineExt<E> for I where I: Iterator<Item = Result<IqSample, E>> {}
|
||||||
|
|
||||||
|
pub trait AfterDemodPiplineExt<E>: Iterator<Item = Result<Sample, E>> + Sized {
|
||||||
|
fn dc_block(self, alpha: f32) -> DcBlocker<Self> {
|
||||||
|
DcBlocker::new(self, alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lowpass(self, alpha: f32) -> LowPass<Self> {
|
||||||
|
LowPass::new(self, alpha)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, E> AfterDemodPiplineExt<E> for I where I: Iterator<Item = Result<Sample, E>> {}
|
||||||
|
|||||||
Reference in New Issue
Block a user