use pyo3::prelude::*;
use numpy::{PyReadonlyArray1, PyArray1, IntoPyArray};
use num_complex::Complex32;
use crate::core::Block;
#[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))
}
}