use crate::core::Block;
use num_complex::Complex32;
use numpy::{IntoPyArray, PyArray1, PyReadonlyArray1};
use pyo3::prelude::*;
#[pyclass(name = "CwEnvelopeDemod")]
pub struct PyCwEnvelopeDemod(crate::demodulate::CwEnvelopeDemod);
#[pymethods]
impl PyCwEnvelopeDemod {
#[new]
fn new(sample_rate: f32, tone_hz: f32, env_bw_hz: f32) -> Self {
Self(crate::demodulate::CwEnvelopeDemod::new(
sample_rate,
tone_hz,
env_bw_hz,
))
}
fn set_gain(&mut self, g: f32) {
self.0.set_gain(g);
}
fn process<'py>(
&mut self,
py: Python<'py>,
iq: PyReadonlyArray1<'py, Complex32>,
) -> PyResult<Bound<'py, PyArray1<f32>>> {
let input = iq.as_slice()?;
let mut out = vec![0.0f32; input.len()];
self.0.process(input, &mut out);
Ok(out.into_pyarray(py))
}
}
#[pyclass(name = "AmEnvelopeDemod")]
pub struct PyAmEnvelopeDemod(crate::demodulate::AmEnvelopeDemod);
#[pymethods]
impl PyAmEnvelopeDemod {
#[new]
#[pyo3(signature = (fs, audio_bw_hz, abs_approx = false))]
fn new(fs: f32, audio_bw_hz: f32, abs_approx: bool) -> Self {
let inner = crate::demodulate::AmEnvelopeDemod::new(fs, audio_bw_hz);
if abs_approx {
Self(inner.with_abs_approx(0.9482, 0.3920))
} else {
Self(inner)
}
}
fn process<'py>(
&mut self,
py: Python<'py>,
iq: PyReadonlyArray1<'py, Complex32>,
) -> PyResult<Bound<'py, PyArray1<f32>>> {
let input = iq.as_slice()?;
let mut out = vec![0.0f32; input.len()];
self.0.process(input, &mut out);
Ok(out.into_pyarray(py))
}
}
#[pyclass(name = "SsbProductDemod")]
pub struct PySsbProductDemod(crate::demodulate::SsbProductDemod);
#[pymethods]
impl PySsbProductDemod {
#[new]
fn new(fs: f32, bfo_hz: f32, audio_bw_hz: f32) -> Self {
Self(crate::demodulate::SsbProductDemod::new(
fs,
bfo_hz,
audio_bw_hz,
))
}
fn process<'py>(
&mut self,
py: Python<'py>,
iq: PyReadonlyArray1<'py, Complex32>,
) -> PyResult<Bound<'py, PyArray1<f32>>> {
let input = iq.as_slice()?;
let mut out = vec![0.0f32; input.len()];
self.0.process(input, &mut out);
Ok(out.into_pyarray(py))
}
}
#[pyclass(name = "FmQuadratureDemod")]
pub struct PyFmQuadratureDemod(crate::demodulate::FmQuadratureDemod);
#[pymethods]
impl PyFmQuadratureDemod {
#[new]
fn new(fs: f32, dev_hz: f32, audio_bw_hz: f32) -> Self {
Self(crate::demodulate::FmQuadratureDemod::new(
fs,
dev_hz,
audio_bw_hz,
))
}
fn process<'py>(
&mut self,
py: Python<'py>,
iq: PyReadonlyArray1<'py, Complex32>,
) -> PyResult<Bound<'py, PyArray1<f32>>> {
let input = iq.as_slice()?;
let mut out = vec![0.0f32; input.len()];
self.0.process(input, &mut out);
Ok(out.into_pyarray(py))
}
}
#[pyclass(name = "PmQuadratureDemod")]
pub struct PyPmQuadratureDemod(crate::demodulate::PmQuadratureDemod);
#[pymethods]
impl PyPmQuadratureDemod {
#[new]
fn new(fs: f32, k: f32, audio_bw_hz: f32) -> Self {
Self(crate::demodulate::PmQuadratureDemod::new(
fs,
k,
audio_bw_hz,
))
}
fn process<'py>(
&mut self,
py: Python<'py>,
iq: PyReadonlyArray1<'py, Complex32>,
) -> PyResult<Bound<'py, PyArray1<f32>>> {
let input = iq.as_slice()?;
let mut out = vec![0.0f32; input.len()];
self.0.process(input, &mut out);
Ok(out.into_pyarray(py))
}
}
#[pyclass(name = "BpskDemod")]
pub struct PyBpskDemod {
demod: crate::demodulate::BpskDemod,
decider: crate::demodulate::BpskDecider,
}
#[pymethods]
impl PyBpskDemod {
#[new]
fn new(gain: f32) -> Self {
Self {
demod: crate::demodulate::BpskDemod::new(gain),
decider: crate::demodulate::BpskDecider::new(),
}
}
fn set_gain(&mut self, g: f32) {
self.demod.set_gain(g);
}
fn process<'py>(
&mut self,
py: Python<'py>,
iq: PyReadonlyArray1<'py, Complex32>,
) -> PyResult<Bound<'py, PyArray1<u8>>> {
let input = iq.as_slice()?;
let n = input.len();
let mut soft = vec![Complex32::new(0.0, 0.0); n];
let mut bits = vec![0u8; n];
self.demod.process(input, &mut soft);
self.decider.process(&soft, &mut bits);
Ok(bits.into_pyarray(py))
}
}
#[pyclass(name = "QpskDemod")]
pub struct PyQpskDemod {
demod: crate::demodulate::QpskDemod,
decider: crate::demodulate::QpskDecider,
}
#[pymethods]
impl PyQpskDemod {
#[new]
fn new(gain: f32) -> Self {
Self {
demod: crate::demodulate::QpskDemod::new(gain),
decider: crate::demodulate::QpskDecider::new(),
}
}
fn set_gain(&mut self, g: f32) {
self.demod.set_gain(g);
}
fn process<'py>(
&mut self,
py: Python<'py>,
iq: PyReadonlyArray1<'py, Complex32>,
) -> PyResult<Bound<'py, PyArray1<u8>>> {
let input = iq.as_slice()?;
let n = input.len();
let mut soft = vec![Complex32::new(0.0, 0.0); n];
let mut bits = vec![0u8; n * 2];
self.demod.process(input, &mut soft);
self.decider.process(&soft, &mut bits);
Ok(bits.into_pyarray(py))
}
}
enum QamDeciderInner {
Qam16(crate::demodulate::Qam16Decider),
Qam64(crate::demodulate::Qam64Decider),
Qam256(crate::demodulate::Qam256Decider),
}
impl QamDeciderInner {
fn bits(&self) -> usize {
match self {
Self::Qam16(_) => 4,
Self::Qam64(_) => 6,
Self::Qam256(_) => 8,
}
}
fn process(&mut self, input: &[Complex32], output: &mut [u8]) {
match self {
Self::Qam16(d) => {
d.process(input, output);
}
Self::Qam64(d) => {
d.process(input, output);
}
Self::Qam256(d) => {
d.process(input, output);
}
}
}
}
#[pyclass(name = "QamDemod")]
pub struct PyQamDemod {
demod: crate::demodulate::QamDemod,
decider: QamDeciderInner,
}
#[pymethods]
impl PyQamDemod {
#[new]
fn new(order: u32, gain: f32) -> PyResult<Self> {
let decider = match order {
16 => QamDeciderInner::Qam16(crate::demodulate::Qam16Decider::new()),
64 => QamDeciderInner::Qam64(crate::demodulate::Qam64Decider::new()),
256 => QamDeciderInner::Qam256(crate::demodulate::Qam256Decider::new()),
_ => {
return Err(pyo3::exceptions::PyValueError::new_err(
"QamDemod: order must be 16, 64, or 256",
));
}
};
Ok(Self {
demod: crate::demodulate::QamDemod::new(gain),
decider,
})
}
fn set_gain(&mut self, g: f32) {
self.demod.set_gain(g);
}
fn process<'py>(
&mut self,
py: Python<'py>,
iq: PyReadonlyArray1<'py, Complex32>,
) -> PyResult<Bound<'py, PyArray1<u8>>> {
let input = iq.as_slice()?;
let n = input.len();
let bits_per_sym = self.decider.bits();
let mut soft = vec![Complex32::new(0.0, 0.0); n];
let mut bits = vec![0u8; n * bits_per_sym];
self.demod.process(input, &mut soft);
self.decider.process(&soft, &mut bits);
Ok(bits.into_pyarray(py))
}
}