orion-sdr 0.0.34

DSP/SDR block library targeting HF-to-UHF, satellites, and Python bindings. Roadmap inside.
Documentation
// Copyright (c) 2025-2026 G & R Associates LLC
// SPDX-License-Identifier: MIT OR Apache-2.0

use super::FirLowpass;
use crate::core::{Block, WorkReport};
use num_complex::Complex32 as C32;

/// FIR-based decimator for complex IQ.
#[derive(Debug, Clone)]
pub struct FirDecimator {
    fs: f32,
    m: usize,
    lp_i: FirLowpass,
    lp_q: FirLowpass,
    // scratch
    ri: Vec<f32>,
    rq: Vec<f32>,
    yi: Vec<f32>,
    yq: Vec<f32>,
}

impl FirDecimator {
    /// `cutoff_hz` & `trans_hz` are at the input rate `fs`.
    pub fn new(fs: f32, m: usize, cutoff_hz: f32, trans_hz: f32) -> Self {
        let lp_i = FirLowpass::design(fs, cutoff_hz, trans_hz);
        let lp_q = FirLowpass::design(fs, cutoff_hz, trans_hz);
        Self {
            fs,
            m: m.max(1),
            lp_i,
            lp_q,
            ri: Vec::new(),
            rq: Vec::new(),
            yi: Vec::new(),
            yq: Vec::new(),
        }
    }
}

impl Block for FirDecimator {
    type In = C32;
    type Out = C32;

    fn process(&mut self, input: &[Self::In], output: &mut [Self::Out]) -> WorkReport {
        let n = input.len();
        if self.ri.len() < n {
            self.ri.resize(n, 0.0);
            self.rq.resize(n, 0.0);
        }
        if self.yi.len() < n {
            self.yi.resize(n, 0.0);
            self.yq.resize(n, 0.0);
        }

        // split I/Q
        for (k, s) in input.iter().enumerate().take(n) {
            self.ri[k] = s.re;
            self.rq[k] = s.im;
        }
        // filter at input rate
        self.lp_i.process(&self.ri[..n], &mut self.yi[..n]);
        self.lp_q.process(&self.rq[..n], &mut self.yq[..n]);

        // decimate by M
        let m = self.m;
        let n_out = n.div_ceil(m);
        let n_write = n_out.min(output.len());
        for (j, out) in output.iter_mut().enumerate().take(n_write) {
            let k = j * m;
            *out = C32::new(self.yi[k], self.yq[k]);
        }
        WorkReport {
            in_read: n,
            out_written: n_write,
        }
    }
}