Compare commits
3 Commits
d8012551cd
...
e1c7ba7865
| Author | SHA1 | Date | |
|---|---|---|---|
| e1c7ba7865 | |||
| 2e76bae04f | |||
| d2bc12d5fd |
20
src/agc.rs
20
src/agc.rs
@ -1,4 +1,3 @@
|
|||||||
use crate::iq_reader::IqChunk;
|
|
||||||
use crate::iq_reader::IqSample;
|
use crate::iq_reader::IqSample;
|
||||||
|
|
||||||
// Automatic Gain Control
|
// Automatic Gain Control
|
||||||
@ -51,10 +50,9 @@ impl<I> Agc<I> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_chunk(&mut self, chunk: &mut IqChunk) {
|
pub fn process_sample(&mut self, sample: IqSample) -> IqSample {
|
||||||
for z in chunk.samples.iter_mut() {
|
let i = sample.re;
|
||||||
let i = z.re;
|
let q = sample.im;
|
||||||
let q = z.im;
|
|
||||||
|
|
||||||
// Instant Power
|
// Instant Power
|
||||||
let inst_power = i * i + q * q;
|
let inst_power = i * i + q * q;
|
||||||
@ -84,23 +82,19 @@ impl<I> Agc<I> {
|
|||||||
|
|
||||||
self.current_gain = final_gain;
|
self.current_gain = final_gain;
|
||||||
|
|
||||||
*z = IqSample::new(i * final_gain, q * final_gain);
|
IqSample::new(i * final_gain, q * final_gain)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, E> Iterator for Agc<I>
|
impl<I, E> Iterator for Agc<I>
|
||||||
where
|
where
|
||||||
I: Iterator<Item = Result<IqChunk, E>>,
|
I: Iterator<Item = Result<IqSample, E>>,
|
||||||
{
|
{
|
||||||
type Item = Result<IqChunk, E>;
|
type Item = Result<IqSample, E>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
match self.inner.next()? {
|
match self.inner.next()? {
|
||||||
Ok(mut chunk) => {
|
Ok(sample) => Some(Ok(self.process_sample(sample))),
|
||||||
self.process_chunk(&mut chunk);
|
|
||||||
Some(Ok(chunk))
|
|
||||||
}
|
|
||||||
Err(e) => Some(Err(e)),
|
Err(e) => Some(Err(e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
37
src/fir.rs
37
src/fir.rs
@ -1,5 +1,5 @@
|
|||||||
// Finite Impulse response + Decimation
|
// Finite Impulse response + Decimation
|
||||||
use crate::{iq_reader::IqChunk, iq_reader::IqSample, utils::ring_buffer::RingBuffer};
|
use crate::{iq_reader::IqSample, utils::ring_buffer::RingBuffer};
|
||||||
|
|
||||||
pub struct Fir<I, const N: usize> {
|
pub struct Fir<I, const N: usize> {
|
||||||
inner: I,
|
inner: I,
|
||||||
@ -27,40 +27,43 @@ impl<I, const N: usize> Fir<I, N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_chunk(&mut self, chunk: &IqChunk) -> IqChunk {
|
// None if not emited
|
||||||
let mut chunk_out = IqChunk::new();
|
pub fn process_sample(&mut self, sample: IqSample) -> Option<IqSample> {
|
||||||
|
self.history.push(sample);
|
||||||
|
|
||||||
for iq in chunk.samples.iter() {
|
self.decimation_index += 1;
|
||||||
self.history.push(*iq);
|
|
||||||
|
|
||||||
// Decimation
|
// Decimation
|
||||||
if self.decimation_index.is_multiple_of(self.decimation_factor) {
|
if self.decimation_index.is_multiple_of(self.decimation_factor) {
|
||||||
let mut y_n = IqSample::default();
|
let mut y_n = IqSample::default();
|
||||||
for k in 0..N {
|
for k in 0..N {
|
||||||
if let Some(sample) = self.history.read_at(k) {
|
if let Some(s) = self.history.read_at(k) {
|
||||||
y_n += *sample * self.taps[k];
|
y_n += *s * self.taps[k];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some(y_n)
|
||||||
chunk_out.samples.push(y_n);
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
self.decimation_index += 1;
|
|
||||||
}
|
|
||||||
chunk_out
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, E, const N: usize> Iterator for Fir<I, N>
|
impl<I, E, const N: usize> Iterator for Fir<I, N>
|
||||||
where
|
where
|
||||||
I: Iterator<Item = Result<IqChunk, E>>,
|
I: Iterator<Item = Result<IqSample, E>>,
|
||||||
{
|
{
|
||||||
type Item = Result<IqChunk, E>;
|
type Item = Result<IqSample, E>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
loop {
|
||||||
match self.inner.next()? {
|
match self.inner.next()? {
|
||||||
Ok(chunk) => Some(Ok(self.process_chunk(&chunk))),
|
Ok(sample) => {
|
||||||
Err(e) => Some(Err(e)),
|
if let Some(y_n) = self.process_sample(sample) {
|
||||||
|
return Some(Ok(y_n));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => return Some(Err(e)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use std::{os::unix::process, slice::Chunks};
|
use crate::iq_reader::IqSample;
|
||||||
|
|
||||||
use crate::iq_reader::{IqChunk, IqSample};
|
pub type Sample = f32;
|
||||||
|
|
||||||
pub struct FmDemod<I> {
|
pub struct FmDemod<I> {
|
||||||
pub inner: I,
|
pub inner: I,
|
||||||
@ -15,24 +15,22 @@ impl<I> FmDemod<I> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_chunk(&mut self, chunk: &mut IqChunk) {
|
pub fn process_sample(&mut self, sample: IqSample) -> Sample {
|
||||||
// TODO: FM Demodulation
|
let phase_diff = sample * self.prev_sample.conj();
|
||||||
todo!();
|
self.prev_sample = sample;
|
||||||
|
phase_diff.arg()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, E> Iterator for FmDemod<I>
|
impl<I, E> Iterator for FmDemod<I>
|
||||||
where
|
where
|
||||||
I: Iterator<Item = Result<IqChunk, E>>,
|
I: Iterator<Item = Result<IqSample, E>>,
|
||||||
{
|
{
|
||||||
type Item = Result<IqChunk, E>;
|
type Item = Result<Sample, E>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
match self.inner.next()? {
|
match self.inner.next()? {
|
||||||
Ok(mut chunk) => {
|
Ok(sample) => Some(Ok(self.process_sample(sample))),
|
||||||
self.process_chunk(&mut chunk);
|
|
||||||
Some(Ok(chunk))
|
|
||||||
}
|
|
||||||
Err(e) => Some(Err(e)),
|
Err(e) => Some(Err(e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,18 +6,18 @@ use std::io::{BufReader, ErrorKind, Read};
|
|||||||
pub type IqSample = Complex<f32>;
|
pub type IqSample = Complex<f32>;
|
||||||
|
|
||||||
// Data chunk
|
// Data chunk
|
||||||
#[derive(Debug)]
|
// #[derive(Debug)]
|
||||||
pub struct IqChunk {
|
// pub struct IqChunk {
|
||||||
pub samples: Vec<IqSample>,
|
// pub samples: Vec<IqSample>,
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
impl IqChunk {
|
// impl IqChunk {
|
||||||
pub fn new() -> Self {
|
// pub fn new() -> Self {
|
||||||
IqChunk {
|
// IqChunk {
|
||||||
samples: Vec::<IqSample>::new(),
|
// samples: Vec::<IqSample>::new(),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub struct FileSource {
|
pub struct FileSource {
|
||||||
// Buffer
|
// Buffer
|
||||||
@ -26,6 +26,8 @@ pub struct FileSource {
|
|||||||
pub raw_buffer: Vec<u8>,
|
pub raw_buffer: Vec<u8>,
|
||||||
// Size of a sample chunk
|
// Size of a sample chunk
|
||||||
pub chunk_samples_size: usize,
|
pub chunk_samples_size: usize,
|
||||||
|
// raw_buffer position (in samples)
|
||||||
|
pub cursor: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileSource {
|
impl FileSource {
|
||||||
@ -42,15 +44,17 @@ impl FileSource {
|
|||||||
reader,
|
reader,
|
||||||
raw_buffer: raw_reader,
|
raw_buffer: raw_reader,
|
||||||
chunk_samples_size,
|
chunk_samples_size,
|
||||||
|
cursor: chunk_samples_size,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for FileSource {
|
impl Iterator for FileSource {
|
||||||
type Item = Result<IqChunk, Box<dyn Error>>;
|
type Item = Result<IqSample, Box<dyn Error>>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
// Buffer read
|
if self.cursor >= self.chunk_samples_size {
|
||||||
|
// Buffer read chunk
|
||||||
match self.reader.read_exact(&mut self.raw_buffer) {
|
match self.reader.read_exact(&mut self.raw_buffer) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
|
|
||||||
@ -63,19 +67,15 @@ impl Iterator for FileSource {
|
|||||||
return Some(Err(e.into()));
|
return Some(Err(e.into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.cursor = 0;
|
||||||
// Output samples
|
|
||||||
let mut samples = Vec::with_capacity(self.chunk_samples_size);
|
|
||||||
|
|
||||||
// Buffer read
|
|
||||||
for iq in self.raw_buffer.chunks(2) {
|
|
||||||
let i = iq[0] as i8;
|
|
||||||
let q = iq[1] as i8;
|
|
||||||
let i_f32 = (i as f32) / 128.0;
|
|
||||||
let q_f32 = (q as f32) / 128.0;
|
|
||||||
samples.push(Complex::new(i_f32, q_f32));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Ok(IqChunk { samples }))
|
// Buffer read sample
|
||||||
|
let i = (self.raw_buffer[self.cursor * 2] as i8) as f32 / 128.0;
|
||||||
|
let q = (self.raw_buffer[self.cursor * 2 + 1] as i8) as f32 / 128.0;
|
||||||
|
|
||||||
|
self.cursor += 1;
|
||||||
|
|
||||||
|
Some(Ok(IqSample::new(i, q)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,11 +17,12 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
|
|
||||||
let pipeline = source
|
let pipeline = source
|
||||||
.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();
|
||||||
|
|
||||||
for chunk_r in pipeline {
|
for phase_r in pipeline {
|
||||||
let chunk = chunk_r?;
|
let phase = phase_r?;
|
||||||
println!("size : {}", chunk.samples.len());
|
// println!("phase : {}", phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
use crate::agc::Agc;
|
use crate::agc::Agc;
|
||||||
use crate::fir::Fir;
|
use crate::fir::Fir;
|
||||||
use crate::iq_reader::IqChunk;
|
use crate::fm_demod::FmDemod;
|
||||||
|
use crate::iq_reader::IqSample;
|
||||||
|
|
||||||
pub trait DspPipelineExt<E>: Iterator<Item = Result<IqChunk, 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> {
|
||||||
Agc::new(self, sample_rate, target_power, min_gain, max_gain)
|
Agc::new(self, sample_rate, target_power, min_gain, max_gain)
|
||||||
}
|
}
|
||||||
@ -10,6 +11,10 @@ pub trait DspPipelineExt<E>: Iterator<Item = Result<IqChunk, E>> + Sized {
|
|||||||
fn fir<const N: usize>(self, taps: [f32; N], decimation_factor: usize) -> Fir<Self, N> {
|
fn fir<const N: usize>(self, taps: [f32; N], decimation_factor: usize) -> Fir<Self, N> {
|
||||||
Fir::new(self, taps, decimation_factor)
|
Fir::new(self, taps, decimation_factor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn demodulate(self) -> FmDemod<Self> {
|
||||||
|
FmDemod::new(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, E> DspPipelineExt<E> for I where I: Iterator<Item = Result<IqChunk, E>> {}
|
impl<I, E> DspPipelineExt<E> for I where I: Iterator<Item = Result<IqSample, E>> {}
|
||||||
|
|||||||
Reference in New Issue
Block a user