Skip to main content

sc_neurocore_engine/bindings/
adc_to_spike.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Commercial license available
3// © Concepts 1996–2026 Miroslav Šotek. All rights reserved.
4// © Code 2020–2026 Miroslav Šotek. All rights reserved.
5// ORCID: 0009-0009-3560-0851
6// Contact: www.anulum.li | protoscience@anulum.li
7// SC-NeuroCore — ADC-to-spike PyO3 binding
8
9//! Python binding for decimating ADC samples into exact integer rate codes.
10
11use numpy::{IntoPyArray, PyReadonlyArray1};
12use pyo3::exceptions::PyValueError;
13use pyo3::prelude::*;
14use pyo3::types::PyDict;
15
16use crate::adc_to_spike;
17
18/// Register the ADC-to-spike window encoder with the extension module.
19pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
20    module.add_function(wrap_pyfunction!(py_adc_to_spike_windows, module)?)?;
21    Ok(())
22}
23
24/// Encode raw ADC samples into per-window spike rate codes.
25///
26/// Parity contract with `sc_neurocore.sensors.adc_to_spike_kernel`: this Rust
27/// path and the Julia, Go, Mojo and Python backends return bit-identical arrays
28/// because the per-window quantise/average/rate-code arithmetic is exact integer.
29///
30/// `signed_input` is `0` for offset-binary or `1` for two's-complement ADC
31/// samples. Returns a dict with `window_values_q` (int32), `spike_counts` (int32)
32/// and `polarities` (bool), each of length `samples.len() / decimation`.
33#[pyfunction]
34#[pyo3(signature = (
35    samples, adc_width, q_int, q_frac, decimation, signed_input, threshold_q,
36))]
37#[allow(clippy::too_many_arguments)]
38fn py_adc_to_spike_windows<'py>(
39    py: Python<'py>,
40    samples: PyReadonlyArray1<'py, i64>,
41    adc_width: u32,
42    q_int: u32,
43    q_frac: u32,
44    decimation: u32,
45    signed_input: i64,
46    threshold_q: i64,
47) -> PyResult<Py<PyAny>> {
48    let result = adc_to_spike::adc_to_spike_windows(
49        samples.as_slice()?,
50        adc_width,
51        q_int,
52        q_frac,
53        decimation,
54        signed_input != 0,
55        threshold_q,
56    )
57    .map_err(|e| PyValueError::new_err(e.to_string()))?;
58    let d = PyDict::new(py);
59    d.set_item("window_values_q", result.window_values_q.into_pyarray(py))?;
60    d.set_item("spike_counts", result.spike_counts.into_pyarray(py))?;
61    d.set_item("polarities", result.polarities.into_pyarray(py))?;
62    Ok(d.into_any().unbind())
63}