Code cleanup with more use statements. Better Python wrapper code.
This commit is contained in:
parent
b15e81409e
commit
22a9c9f850
@ -88,3 +88,4 @@ record = ["dep:hdf5-sys", "dep:hdf5", "dep:chrono", "dep:uuid"]
|
|||||||
f64 = []
|
f64 = []
|
||||||
f32 = []
|
f32 = []
|
||||||
python-bindings = ["dep:pyo3", "dep:numpy"]
|
python-bindings = ["dep:pyo3", "dep:numpy"]
|
||||||
|
extension-module = []
|
||||||
|
@ -1 +1 @@
|
|||||||
|
from ._lasprs import *
|
||||||
|
@ -51,12 +51,12 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
sleep(100);
|
sleep(100);
|
||||||
match smgr.getStatus(StreamType::Output) {
|
match smgr.getStatus(StreamType::Output) {
|
||||||
StreamStatus::NotRunning => {
|
StreamStatus::NotRunning{} => {
|
||||||
println!("Stream is not running?");
|
println!("Stream is not running?");
|
||||||
break 'infy;
|
break 'infy;
|
||||||
}
|
}
|
||||||
StreamStatus::Running => {}
|
StreamStatus::Running{} => {}
|
||||||
StreamStatus::Error(e) => {
|
StreamStatus::Error{e} => {
|
||||||
println!("Stream error: {}", e);
|
println!("Stream error: {}", e);
|
||||||
break 'infy;
|
break 'infy;
|
||||||
}
|
}
|
||||||
|
@ -24,19 +24,21 @@ cfg_if::cfg_if! {
|
|||||||
if #[cfg(feature = "python-bindings")] {
|
if #[cfg(feature = "python-bindings")] {
|
||||||
pub use numpy::ndarray::{ArrayD, ArrayViewD, ArrayViewMutD};
|
pub use numpy::ndarray::{ArrayD, ArrayViewD, ArrayViewMutD};
|
||||||
pub use numpy::ndarray::prelude::*;
|
pub use numpy::ndarray::prelude::*;
|
||||||
pub use numpy::{IntoPyArray, PyArray1, PyArrayDyn, PyArrayLike1, PyReadonlyArrayDyn};
|
pub use numpy::{IntoPyArray,PyArray, PyArray1, PyArrayDyn, PyArrayLike1, PyReadonlyArrayDyn};
|
||||||
pub use pyo3::exceptions::PyValueError;
|
|
||||||
pub use pyo3::prelude::*;
|
pub use pyo3::prelude::*;
|
||||||
|
pub use pyo3::exceptions::PyValueError;
|
||||||
pub use pyo3::{pymodule, types::PyModule, PyResult};
|
pub use pyo3::{pymodule, types::PyModule, PyResult};
|
||||||
|
pub use pyo3::anyhow::*;
|
||||||
|
pub use pyo3;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
pub use ndarray::prelude::*;
|
pub use ndarray::prelude::*;
|
||||||
pub use ndarray::{Array1, Array2, ArrayView1};
|
pub use ndarray::{Array1, Array2, ArrayView1};
|
||||||
} }
|
} }
|
||||||
|
|
||||||
// pub use num::complex::i;
|
use ndarray::OwnedRepr;
|
||||||
use num::complex::*;
|
use num::complex::Complex;
|
||||||
|
|
||||||
|
|
||||||
/// View into 1D array of floats
|
/// View into 1D array of floats
|
||||||
pub type VdView<'a> = ArrayView1<'a, Flt>;
|
pub type VdView<'a> = ArrayView1<'a, Flt>;
|
||||||
@ -66,3 +68,17 @@ pub type Ccol = Array1<Cflt>;
|
|||||||
pub type Dmat = Array2<Flt>;
|
pub type Dmat = Array2<Flt>;
|
||||||
/// 2D array of complex floats
|
/// 2D array of complex floats
|
||||||
pub type Cmat = Array2<Cflt>;
|
pub type Cmat = Array2<Cflt>;
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(feature = "python-bindings")] {
|
||||||
|
|
||||||
|
/// 1D array of T as returned from Rust to Numpy
|
||||||
|
pub type PyArr1<'py, T> = Bound<'py, PyArray<T, ndarray::Dim<[usize; 1]>>>;
|
||||||
|
|
||||||
|
/// 1D array Floats returned from Rust to Numpy
|
||||||
|
pub type PyArr1Flt<'py> = PyArr1<'py, Flt>;
|
||||||
|
|
||||||
|
/// 1D array of Complex returned from Rust to Numpy
|
||||||
|
pub type PyArr1Cflt<'py> = PyArr1<'py, Cflt>;
|
||||||
|
|
||||||
|
}}
|
@ -1,7 +1,7 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
use super::Stream;
|
use super::Stream;
|
||||||
use super::StreamMetaData;
|
use super::StreamMetaData;
|
||||||
use crate::daq::streamdata::*;
|
use crate::daq::{streamdata::*, StreamApiDescr};
|
||||||
use crate::config::{self, *};
|
use crate::config::{self, *};
|
||||||
use crate::daq::{self, *};
|
use crate::daq::{self, *};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
@ -147,7 +147,7 @@ impl CpalApi {
|
|||||||
};
|
};
|
||||||
let prefSampleRate = *avSampleRates.last().unwrap_or(&48000.);
|
let prefSampleRate = *avSampleRates.last().unwrap_or(&48000.);
|
||||||
devs.push(DeviceInfo {
|
devs.push(DeviceInfo {
|
||||||
api: super::StreamApiDescr::Cpal,
|
api: StreamApiDescr::Cpal,
|
||||||
device_name: dev.name()?,
|
device_name: dev.name()?,
|
||||||
avDataTypes: dtypes,
|
avDataTypes: dtypes,
|
||||||
prefDataType,
|
prefDataType,
|
||||||
@ -186,7 +186,7 @@ impl CpalApi {
|
|||||||
if let Some(sender) = &send_ch {
|
if let Some(sender) = &send_ch {
|
||||||
sender.send(RawStreamData::StreamError(serr)).unwrap();
|
sender.send(RawStreamData::StreamError(serr)).unwrap();
|
||||||
}
|
}
|
||||||
status.store(StreamStatus::Error(serr));
|
status.store(StreamStatus::Error{e: serr});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,7 +251,7 @@ impl CpalApi {
|
|||||||
en_inchannels: Vec<usize>,
|
en_inchannels: Vec<usize>,
|
||||||
framesPerBlock: usize,
|
framesPerBlock: usize,
|
||||||
) -> Result<(cpal::Stream, Arc<AtomicCell<StreamStatus>>)> {
|
) -> Result<(cpal::Stream, Arc<AtomicCell<StreamStatus>>)> {
|
||||||
let status = Arc::new(AtomicCell::new(StreamStatus::NotRunning));
|
let status = Arc::new(AtomicCell::new(StreamStatus::NotRunning{}));
|
||||||
|
|
||||||
let errfcn = CpalApi::create_errfcn(Some(sender.clone()), status.clone());
|
let errfcn = CpalApi::create_errfcn(Some(sender.clone()), status.clone());
|
||||||
|
|
||||||
@ -301,7 +301,7 @@ impl CpalApi {
|
|||||||
|
|
||||||
let mut setToEquilibrium = || data.iter_mut().for_each(|v| *v = Sample::EQUILIBRIUM);
|
let mut setToEquilibrium = || data.iter_mut().for_each(|v| *v = Sample::EQUILIBRIUM);
|
||||||
match status {
|
match status {
|
||||||
StreamStatus::NotRunning | StreamStatus::Error(_) => {
|
StreamStatus::NotRunning{} | StreamStatus::Error{..} => {
|
||||||
setToEquilibrium();
|
setToEquilibrium();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -312,7 +312,7 @@ impl CpalApi {
|
|||||||
// Obtain new samples from the generator
|
// Obtain new samples from the generator
|
||||||
for dat in receiver.try_iter() {
|
for dat in receiver.try_iter() {
|
||||||
let slice = dat.getRef::<T>();
|
let slice = dat.getRef::<T>();
|
||||||
if let StreamStatus::Running = status {
|
if let StreamStatus::Running{} = status {
|
||||||
q.extend(slice);
|
q.extend(slice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -330,7 +330,7 @@ impl CpalApi {
|
|||||||
setToEquilibrium();
|
setToEquilibrium();
|
||||||
} else {
|
} else {
|
||||||
// Output buffer underrun
|
// Output buffer underrun
|
||||||
streamstatus.store(StreamStatus::Error(StreamError::OutputUnderrunError));
|
streamstatus.store(StreamStatus::Error{e:StreamError::OutputUnderrunError});
|
||||||
setToEquilibrium();
|
setToEquilibrium();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -345,7 +345,7 @@ impl CpalApi {
|
|||||||
) -> Result<(cpal::Stream, Arc<AtomicCell<StreamStatus>>)> {
|
) -> Result<(cpal::Stream, Arc<AtomicCell<StreamStatus>>)> {
|
||||||
// let tot_ch = config.channels as usize;
|
// let tot_ch = config.channels as usize;
|
||||||
|
|
||||||
let status = Arc::new(AtomicCell::new(StreamStatus::NotRunning));
|
let status = Arc::new(AtomicCell::new(StreamStatus::NotRunning{}));
|
||||||
|
|
||||||
let err_cb = CpalApi::create_errfcn(None, status.clone());
|
let err_cb = CpalApi::create_errfcn(None, status.clone());
|
||||||
macro_rules! build_stream{
|
macro_rules! build_stream{
|
||||||
@ -472,7 +472,7 @@ impl CpalApi {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
stream.play()?;
|
stream.play()?;
|
||||||
status.store(StreamStatus::Running);
|
status.store(StreamStatus::Running{});
|
||||||
|
|
||||||
return Ok(Box::new(CpalStream {
|
return Ok(Box::new(CpalStream {
|
||||||
stream,
|
stream,
|
||||||
@ -514,7 +514,7 @@ impl CpalApi {
|
|||||||
framesPerBlock,
|
framesPerBlock,
|
||||||
)?;
|
)?;
|
||||||
stream.play()?;
|
stream.play()?;
|
||||||
status.store(StreamStatus::Running);
|
status.store(StreamStatus::Running{});
|
||||||
|
|
||||||
// Daq: default channel config
|
// Daq: default channel config
|
||||||
let daqchannels = Vec::from_iter(
|
let daqchannels = Vec::from_iter(
|
||||||
@ -574,7 +574,7 @@ impl CpalApi {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
stream.play()?;
|
stream.play()?;
|
||||||
status.store(StreamStatus::Running);
|
status.store(StreamStatus::Running{});
|
||||||
|
|
||||||
// Daq: default channel config
|
// Daq: default channel config
|
||||||
let daqchannels =
|
let daqchannels =
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
/// Daq apis that are optionally compiled in. Examples:
|
/// Daq apis that are optionally compiled in. Examples:
|
||||||
///
|
///
|
||||||
/// - CPAL (Cross-Platform Audio Library)
|
/// - CPAL (Cross-Platform Audio Library)
|
||||||
/// - ...
|
/// - ...
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::sync::Arc;
|
||||||
use strum::EnumMessage;
|
use strum::EnumMessage;
|
||||||
use strum_macros;
|
use strum_macros;
|
||||||
use std::sync::Arc;
|
use crate::config::*;
|
||||||
|
|
||||||
use super::{streamstatus::StreamStatus, streamdata::StreamMetaData};
|
use super::{streamdata::StreamMetaData, streamstatus::StreamStatus};
|
||||||
|
|
||||||
#[cfg(feature = "cpal-api")]
|
#[cfg(feature = "cpal-api")]
|
||||||
pub mod api_cpal;
|
pub mod api_cpal;
|
||||||
@ -26,9 +27,12 @@ pub trait Stream {
|
|||||||
/// Number of output channels in stream
|
/// Number of output channels in stream
|
||||||
fn noutchannels(&self) -> usize;
|
fn noutchannels(&self) -> usize;
|
||||||
|
|
||||||
|
/// Obtain stream status
|
||||||
fn status(&self) -> StreamStatus;
|
fn status(&self) -> StreamStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stream API descriptor: type and corresponding text
|
||||||
|
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||||
#[derive(strum_macros::EnumMessage, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(strum_macros::EnumMessage, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum StreamApiDescr {
|
pub enum StreamApiDescr {
|
||||||
@ -38,4 +42,4 @@ pub enum StreamApiDescr {
|
|||||||
/// PulseAudio api
|
/// PulseAudio api
|
||||||
#[strum(message = "pulse", detailed_message = "Pulseaudio")]
|
#[strum(message = "pulse", detailed_message = "Pulseaudio")]
|
||||||
Pulse = 1,
|
Pulse = 1,
|
||||||
}
|
}
|
@ -1,9 +1,6 @@
|
|||||||
use std::{ops::Index, path::PathBuf};
|
use std::{ops::Index, path::PathBuf};
|
||||||
|
|
||||||
use super::api::StreamApiDescr;
|
use super::*;
|
||||||
use super::datatype::DataType;
|
|
||||||
use super::deviceinfo::DeviceInfo;
|
|
||||||
use super::qty::Qty;
|
|
||||||
use crate::config::*;
|
use crate::config::*;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -3,10 +3,12 @@
|
|||||||
use strum::EnumMessage;
|
use strum::EnumMessage;
|
||||||
use strum_macros;
|
use strum_macros;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
use crate::config::*;
|
||||||
|
|
||||||
/// Data type description for samples coming from a stream
|
/// Data type description for samples coming from a stream
|
||||||
#[derive(strum_macros::EnumMessage, PartialEq, Copy, Debug, Clone, Serialize, Deserialize)]
|
#[derive(strum_macros::EnumMessage, PartialEq, Copy, Debug, Clone, Serialize, Deserialize)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||||
pub enum DataType {
|
pub enum DataType {
|
||||||
/// 32-bit floats
|
/// 32-bit floats
|
||||||
#[strum(message = "F32", detailed_message = "32-bits floating points")]
|
#[strum(message = "F32", detailed_message = "32-bits floating points")]
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
//! Data acquisition model. Provides abstract layers around DAQ devices.
|
//! Data acquisition model. Provides abstract layers around DAQ devices.
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use super::api::StreamApiDescr;
|
use super::StreamApiDescr;
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::config::*;
|
use crate::config::*;
|
||||||
|
|
||||||
/// Device info structure. Gives all information regarding a device, i.e. the number of input and
|
/// Device info structure. Gives all information regarding a device, i.e. the number of input and
|
||||||
/// output channels, its name and available sample rates and types.
|
/// output channels, its name and available sample rates and types.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
#[cfg_attr(feature = "python-bindings", pyclass(get_all))]
|
||||||
|
|
||||||
pub struct DeviceInfo {
|
pub struct DeviceInfo {
|
||||||
/// The api in use for this device
|
/// The api in use for this device
|
||||||
pub api: StreamApiDescr,
|
pub api: StreamApiDescr,
|
||||||
@ -17,9 +19,11 @@ pub struct DeviceInfo {
|
|||||||
pub device_name: String,
|
pub device_name: String,
|
||||||
|
|
||||||
/// Available data types for the sample
|
/// Available data types for the sample
|
||||||
|
// #[pyo3(get)]
|
||||||
pub avDataTypes: Vec<DataType>,
|
pub avDataTypes: Vec<DataType>,
|
||||||
|
|
||||||
/// Preferred data type for device
|
/// Preferred data type for device
|
||||||
|
// #[pyo3(get)]
|
||||||
pub prefDataType: DataType,
|
pub prefDataType: DataType,
|
||||||
|
|
||||||
/// Available frames per block
|
/// Available frames per block
|
||||||
@ -65,5 +69,12 @@ pub struct DeviceInfo {
|
|||||||
/// devices, this is typically a 'number' between +/- full scale. For some
|
/// devices, this is typically a 'number' between +/- full scale. For some
|
||||||
/// devices however, the output quantity corresponds to a physical signal,
|
/// devices however, the output quantity corresponds to a physical signal,
|
||||||
/// such a Volts.
|
/// such a Volts.
|
||||||
|
// #[pyo3(get)]
|
||||||
pub physicalIOQty: Qty,
|
pub physicalIOQty: Qty,
|
||||||
}
|
}
|
||||||
|
#[cfg_attr(feature = "python-bindings", pymethods)]
|
||||||
|
impl DeviceInfo {
|
||||||
|
fn __repr__(&self) -> String {
|
||||||
|
format!("{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -22,9 +22,10 @@ pub use datatype::DataType;
|
|||||||
pub use deviceinfo::DeviceInfo;
|
pub use deviceinfo::DeviceInfo;
|
||||||
pub use qty::Qty;
|
pub use qty::Qty;
|
||||||
pub use streamhandler::StreamHandler;
|
pub use streamhandler::StreamHandler;
|
||||||
pub use streammgr::StreamMgr;
|
pub use streammgr::*;
|
||||||
pub use streammsg::InStreamMsg;
|
pub use streammsg::InStreamMsg;
|
||||||
pub use streamstatus::StreamStatus;
|
pub use streamstatus::StreamStatus;
|
||||||
|
use api::*;
|
||||||
|
|
||||||
#[cfg(feature = "record")]
|
#[cfg(feature = "record")]
|
||||||
pub use record::*;
|
pub use record::*;
|
||||||
@ -46,8 +47,26 @@ pub enum StreamType {
|
|||||||
Duplex,
|
Duplex,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "python-bindings")]
|
||||||
|
/// Add Python classes from stream manager
|
||||||
|
pub fn add_py_classses(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
|
|
||||||
|
m.add_class::<DeviceInfo>()?;
|
||||||
|
m.add_class::<StreamMgr>()?;
|
||||||
|
m.add_class::<StreamApiDescr>()?;
|
||||||
|
m.add_class::<DataType>()?;
|
||||||
|
m.add_class::<Qty>()?;
|
||||||
|
m.add_class::<StreamType>()?;
|
||||||
|
m.add_class::<StreamError>()?;
|
||||||
|
m.add_class::<StreamStatus>()?;
|
||||||
|
m.add_class::<StreamError>()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Errors that happen in a stream
|
/// Errors that happen in a stream
|
||||||
#[derive(strum_macros::EnumMessage, Debug, Clone, Display, Copy)]
|
#[derive(strum_macros::EnumMessage, Debug, Clone, Display, Copy)]
|
||||||
|
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||||
pub enum StreamError {
|
pub enum StreamError {
|
||||||
/// Input overrun
|
/// Input overrun
|
||||||
#[strum(
|
#[strum(
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
//! Physical quantities that are input / output of a daq device. Provides an enumeration for these.
|
//! Physical quantities that are input / output of a daq device. Provides an enumeration for these.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
use crate::config::*;
|
||||||
use strum::EnumMessage;
|
use strum::EnumMessage;
|
||||||
use strum_macros;
|
use strum_macros;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
/// Physical quantities that are I/O of a Daq device.
|
/// Physical quantities that are I/O of a Daq device.
|
||||||
#[derive(PartialEq, Serialize, Deserialize, strum_macros::EnumMessage, Debug, Clone, Copy)]
|
#[derive(PartialEq, Serialize, Deserialize, strum_macros::EnumMessage, Debug, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum Qty {
|
pub enum Qty {
|
||||||
/// Number
|
/// Number
|
||||||
|
@ -5,7 +5,6 @@ use clap::builder::OsStr;
|
|||||||
use crossbeam::atomic::AtomicCell;
|
use crossbeam::atomic::AtomicCell;
|
||||||
use hdf5::types::{VarLenArray, VarLenUnicode};
|
use hdf5::types::{VarLenArray, VarLenUnicode};
|
||||||
use hdf5::{dataset, datatype, Dataset, File, H5Type};
|
use hdf5::{dataset, datatype, Dataset, File, H5Type};
|
||||||
use ndarray::ArrayView2;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering::SeqCst};
|
use std::sync::atomic::{AtomicBool, Ordering::SeqCst};
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
use crate::siggen::*;
|
use crate::siggen::*;
|
||||||
use super::streammgr::SharedInQueue;
|
use super::streammgr::SharedInQueue;
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(feature = "python-bindings")] {
|
|
||||||
use pyo3::exceptions::PyValueError;
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use pyo3::{pymodule, pyclass, types::PyModule, PyResult};
|
|
||||||
} else {} }
|
|
||||||
|
|
||||||
|
|
||||||
/// Commands that can be sent to a running stream
|
/// Commands that can be sent to a running stream
|
||||||
@ -13,9 +7,6 @@ pub enum StreamCommand {
|
|||||||
/// Add a new queue to a running INPUT stream
|
/// Add a new queue to a running INPUT stream
|
||||||
AddInQueue(SharedInQueue),
|
AddInQueue(SharedInQueue),
|
||||||
|
|
||||||
/// Remove a queue to a running INPUT stream
|
|
||||||
RemoveInQueue(SharedInQueue),
|
|
||||||
|
|
||||||
/// New signal generator config to be used in OUTPUT stream
|
/// New signal generator config to be used in OUTPUT stream
|
||||||
NewSiggen(Siggen),
|
NewSiggen(Siggen),
|
||||||
|
|
||||||
|
@ -12,12 +12,6 @@ use std::u128::MAX;
|
|||||||
use strum_macros::Display;
|
use strum_macros::Display;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(feature = "python-bindings")] {
|
|
||||||
use pyo3::exceptions::PyValueError;
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use pyo3::{pymodule, pyclass, types::PyModule, PyResult};
|
|
||||||
} else {} }
|
|
||||||
|
|
||||||
/// Raw stream data coming from a stream.
|
/// Raw stream data coming from a stream.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Data acquisition model. Provides abstract layers around DAQ devices.
|
//! Data acquisition model. Provides abstract layers around DAQ devices.
|
||||||
use super::api::*;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use super::config::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
config::*,
|
config::*,
|
||||||
siggen::{self, Siggen},
|
siggen::{self, Siggen},
|
||||||
@ -15,19 +15,14 @@ use crossbeam::{
|
|||||||
};
|
};
|
||||||
use std::sync::{atomic::AtomicBool, Arc, Mutex};
|
use std::sync::{atomic::AtomicBool, Arc, Mutex};
|
||||||
use std::thread::{JoinHandle, Thread};
|
use std::thread::{JoinHandle, Thread};
|
||||||
use streamcmd::StreamCommand;
|
|
||||||
use streamdata::*;
|
use streamdata::*;
|
||||||
|
use streamcmd::StreamCommand;
|
||||||
use streammsg::*;
|
use streammsg::*;
|
||||||
|
use api::StreamApiDescr;
|
||||||
|
|
||||||
|
|
||||||
#[cfg(feature = "cpal-api")]
|
#[cfg(feature = "cpal-api")]
|
||||||
use super::api::api_cpal::CpalApi;
|
use super::api::{api_cpal::CpalApi, Stream};
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(feature = "python-bindings")] {
|
|
||||||
use pyo3::exceptions::PyValueError;
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use pyo3::{pymodule, types::PyModule, PyResult};
|
|
||||||
} else {} }
|
|
||||||
|
|
||||||
/// Store a queue in a shared pointer, to share sending
|
/// Store a queue in a shared pointer, to share sending
|
||||||
/// and receiving part of the queue.
|
/// and receiving part of the queue.
|
||||||
@ -45,9 +40,9 @@ struct StreamInfo<T> {
|
|||||||
/// Keep track of whether the stream has been created. To ensure singleton behaviour.
|
/// Keep track of whether the stream has been created. To ensure singleton behaviour.
|
||||||
static smgr_created: AtomicBool = AtomicBool::new(false);
|
static smgr_created: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
#[cfg_attr(feature = "python-bindings", pyclass(unsendable))]
|
|
||||||
/// Configure and manage input / output streams.
|
/// Configure and manage input / output streams.
|
||||||
///
|
///
|
||||||
|
#[cfg_attr(feature = "python-bindings", pyclass(unsendable))]
|
||||||
pub struct StreamMgr {
|
pub struct StreamMgr {
|
||||||
// List of available devices
|
// List of available devices
|
||||||
devs: Vec<DeviceInfo>,
|
devs: Vec<DeviceInfo>,
|
||||||
@ -82,13 +77,23 @@ impl StreamMgr {
|
|||||||
StreamMgr::new()
|
StreamMgr::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[pyo3(name = "unit")]
|
#[pyo3(name = "startDefaultInputStream")]
|
||||||
// #[staticmethod]
|
fn startDefaultInputStream_py(&mut self) -> PyResult<()> {
|
||||||
// /// See: [Biquad::unit()]
|
Ok(self.startDefaultInputStream()?)
|
||||||
// pub fn unit_py() -> Biquad {
|
}
|
||||||
// Biquad::unit()
|
#[pyo3(name = "startDefaultOutputStream")]
|
||||||
// }
|
fn startDefaultOutputStream_py(&mut self) -> PyResult<()> {
|
||||||
// #[pyo3(name = "firstOrderHighPass")]
|
Ok(self.startDefaultOutputStream()?)
|
||||||
|
}
|
||||||
|
#[pyo3(name = "getDeviceInfo")]
|
||||||
|
fn getDeviceInfo_py(&mut self) -> PyResult<Vec<DeviceInfo>> {
|
||||||
|
Ok(self.getDeviceInfo())
|
||||||
|
}
|
||||||
|
#[pyo3(name = "getStatus")]
|
||||||
|
fn getStatus_py(&self, st: StreamType) -> StreamStatus {
|
||||||
|
self.getStatus(st)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
impl Default for StreamMgr {
|
impl Default for StreamMgr {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@ -130,14 +135,14 @@ impl StreamMgr {
|
|||||||
if let Some(s) = &self.input_stream {
|
if let Some(s) = &self.input_stream {
|
||||||
s.stream.status()
|
s.stream.status()
|
||||||
} else {
|
} else {
|
||||||
StreamStatus::NotRunning
|
StreamStatus::NotRunning{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StreamType::Output => {
|
StreamType::Output => {
|
||||||
if let Some(s) = &self.output_stream {
|
if let Some(s) = &self.output_stream {
|
||||||
s.stream.status()
|
s.stream.status()
|
||||||
} else {
|
} else {
|
||||||
StreamStatus::NotRunning
|
StreamStatus::NotRunning{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,8 +176,8 @@ impl StreamMgr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain a list of devices that are available for each available API
|
/// Obtain a list of devices that are available for each available API
|
||||||
pub fn getDeviceInfo(&mut self) -> &Vec<DeviceInfo> {
|
pub fn getDeviceInfo(&mut self) -> Vec<DeviceInfo> {
|
||||||
&self.devs
|
self.devs.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scanDeviceInfo(&self) -> Vec<DeviceInfo> {
|
fn scanDeviceInfo(&self) -> Vec<DeviceInfo> {
|
||||||
@ -227,14 +232,9 @@ impl StreamMgr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove queue from list
|
|
||||||
StreamCommand::RemoveInQueue(queue) => {
|
|
||||||
iqueues.retain(|q| !q.same_channel(&queue))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop this thread. Returns the queue
|
// Stop this thread. Returns the queue
|
||||||
StreamCommand::StopThread => {
|
StreamCommand::StopThread => {
|
||||||
sendMsgToAllQueues(&mut iqueues, InStreamMsg::StreamStopped);
|
sendMsgToAllQueuesRemoveUnused(&mut iqueues, InStreamMsg::StreamStopped);
|
||||||
break 'infy;
|
break 'infy;
|
||||||
}
|
}
|
||||||
StreamCommand::NewSiggen(_) => {
|
StreamCommand::NewSiggen(_) => {
|
||||||
@ -249,7 +249,7 @@ impl StreamMgr {
|
|||||||
let streamdata = Arc::new(streamdata);
|
let streamdata = Arc::new(streamdata);
|
||||||
|
|
||||||
let msg = InStreamMsg::StreamData(streamdata);
|
let msg = InStreamMsg::StreamData(streamdata);
|
||||||
sendMsgToAllQueues(&mut iqueues, msg);
|
sendMsgToAllQueuesRemoveUnused(&mut iqueues, msg);
|
||||||
ctr += 1;
|
ctr += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -295,11 +295,6 @@ impl StreamMgr {
|
|||||||
panic!("Invalid message send to output thread: AddInQueue");
|
panic!("Invalid message send to output thread: AddInQueue");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove queue from list
|
|
||||||
StreamCommand::RemoveInQueue(_) => {
|
|
||||||
panic!("Invalid message send to output thread: RemoveInQueue");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop this thread. Returns the queue
|
// Stop this thread. Returns the queue
|
||||||
StreamCommand::StopThread => {
|
StreamCommand::StopThread => {
|
||||||
break 'infy;
|
break 'infy;
|
||||||
@ -422,7 +417,7 @@ impl StreamMgr {
|
|||||||
|
|
||||||
let meta = stream.metadata();
|
let meta = stream.metadata();
|
||||||
|
|
||||||
sendMsgToAllQueues(iqueues, InStreamMsg::StreamStarted(meta.clone()));
|
sendMsgToAllQueuesRemoveUnused(iqueues, InStreamMsg::StreamStarted(meta.clone()));
|
||||||
|
|
||||||
let (threadhandle, commtx) = self.startInputStreamThread(meta, rx);
|
let (threadhandle, commtx) = self.startInputStreamThread(meta, rx);
|
||||||
|
|
||||||
@ -453,7 +448,7 @@ impl StreamMgr {
|
|||||||
|
|
||||||
let iqueues = self.instreamqueues.as_mut().unwrap();
|
let iqueues = self.instreamqueues.as_mut().unwrap();
|
||||||
let meta = stream.metadata();
|
let meta = stream.metadata();
|
||||||
sendMsgToAllQueues(iqueues, InStreamMsg::StreamStarted(meta.clone()));
|
sendMsgToAllQueuesRemoveUnused(iqueues, InStreamMsg::StreamStarted(meta.clone()));
|
||||||
|
|
||||||
let (threadhandle, commtx) = self.startInputStreamThread(meta, rx);
|
let (threadhandle, commtx) = self.startInputStreamThread(meta, rx);
|
||||||
|
|
||||||
@ -582,7 +577,7 @@ impl Drop for StreamMgr {
|
|||||||
|
|
||||||
// Send to all queues, remove queues that are disconnected when found out
|
// Send to all queues, remove queues that are disconnected when found out
|
||||||
// on the way.
|
// on the way.
|
||||||
fn sendMsgToAllQueues(iqueues: &mut InQueues, msg: InStreamMsg) {
|
fn sendMsgToAllQueuesRemoveUnused(iqueues: &mut InQueues, msg: InStreamMsg) {
|
||||||
// Loop over queues. Remove queues that error when we try to send
|
// Loop over queues. Remove queues that error when we try to send
|
||||||
// to them
|
// to them
|
||||||
iqueues.retain(|q| match q.try_send(msg.clone()) {
|
iqueues.retain(|q| match q.try_send(msg.clone()) {
|
||||||
|
@ -2,24 +2,22 @@
|
|||||||
use strum_macros::Display;
|
use strum_macros::Display;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(feature = "python-bindings")] {
|
|
||||||
use pyo3::exceptions::PyValueError;
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
use pyo3::{pymodule, pyclass, types::PyModule, PyResult};
|
|
||||||
} else {} }
|
|
||||||
|
|
||||||
/// Gives the stream status of a stream, either input / output or duplex.
|
/// Gives the stream status of a (possible) stream, either input / output or duplex.
|
||||||
#[derive(strum_macros::EnumMessage, Debug, Clone, Copy, Display)]
|
#[derive(strum_macros::EnumMessage, Debug, Clone, Copy, Display)]
|
||||||
|
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||||
pub enum StreamStatus {
|
pub enum StreamStatus {
|
||||||
/// Stream is not running
|
/// Stream is not running
|
||||||
#[strum(message = "NotRunning", detailed_message = "Stream is not running")]
|
#[strum(message = "NotRunning", detailed_message = "Stream is not running")]
|
||||||
NotRunning,
|
NotRunning{},
|
||||||
/// Stream is running properly
|
/// Stream is running properly
|
||||||
#[strum(message = "Running", detailed_message = "Stream is running")]
|
#[strum(message = "Running", detailed_message = "Stream is running")]
|
||||||
Running,
|
Running{},
|
||||||
|
|
||||||
/// An error occured in the stream.
|
/// An error occured in the stream.
|
||||||
#[strum(message = "Error", detailed_message = "An error occured with the stream")]
|
#[strum(message = "Error", detailed_message = "An error occured with the stream")]
|
||||||
Error(StreamError)
|
Error{
|
||||||
|
/// In case the stream has an error: e is the field name
|
||||||
|
e: StreamError
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use ndarray::prelude::*;
|
use crate::config::*;
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
use num::Complex;
|
use num::Complex;
|
||||||
|
|
||||||
@ -21,13 +21,12 @@ pub struct Biquad {
|
|||||||
a1: Flt,
|
a1: Flt,
|
||||||
a2: Flt,
|
a2: Flt,
|
||||||
}
|
}
|
||||||
#[cfg(feature = "python-bindings")]
|
|
||||||
#[cfg_attr(feature = "python-bindings", pymethods)]
|
#[cfg_attr(feature = "python-bindings", pymethods)]
|
||||||
impl Biquad {
|
impl Biquad {
|
||||||
#[new]
|
#[new]
|
||||||
/// Create new biquad filter. See [Biquad::new()]
|
/// Create new biquad filter. See [Biquad::new()]
|
||||||
///
|
///
|
||||||
pub fn new_py<'py>(coefs: PyReadonlyArrayDyn<Flt>) -> PyResult<Self> {
|
pub fn new_py<'py>(coefs: PyArrayLike1<Flt>) -> PyResult<Self> {
|
||||||
Ok(Biquad::new(coefs.as_slice()?)?)
|
Ok(Biquad::new(coefs.as_slice()?)?)
|
||||||
}
|
}
|
||||||
#[pyo3(name = "unit")]
|
#[pyo3(name = "unit")]
|
||||||
@ -48,8 +47,8 @@ impl Biquad {
|
|||||||
&mut self,
|
&mut self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
input: PyArrayLike1<Flt>,
|
input: PyArrayLike1<Flt>,
|
||||||
) -> PyResult<&'py PyArray1<Flt>> {
|
) -> PyResult<PyArr1Flt<'py>> {
|
||||||
Ok(self.filter(input.as_slice()?).into_pyarray(py))
|
Ok(PyArray1::from_vec_bound(py, self.filter(input.as_slice()?)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Biquad {
|
impl Biquad {
|
||||||
@ -112,6 +111,8 @@ impl Biquad {
|
|||||||
|
|
||||||
Ok(Biquad::new(&coefs).unwrap())
|
Ok(Biquad::new(&coefs).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Filter input signal, output by overwriting input slice.
|
||||||
pub fn filter_inout(&mut self, inout: &mut [Flt]) {
|
pub fn filter_inout(&mut self, inout: &mut [Flt]) {
|
||||||
for sample in inout.iter_mut() {
|
for sample in inout.iter_mut() {
|
||||||
let w0 = *sample - self.a1 * self.w1 - self.a2 * self.w2;
|
let w0 = *sample - self.a1 * self.w1 - self.a2 * self.w2;
|
||||||
@ -123,6 +124,7 @@ impl Biquad {
|
|||||||
// println!("{:?}", inout);
|
// println!("{:?}", inout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Filter for Biquad {
|
impl Filter for Biquad {
|
||||||
fn filter(&mut self, input: &[Flt]) -> Vec<Flt> {
|
fn filter(&mut self, input: &[Flt]) -> Vec<Flt> {
|
||||||
let mut out = input.to_vec();
|
let mut out = input.to_vec();
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use super::biquad::Biquad;
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||||
|
|
||||||
|
|
||||||
/// Series of biquads that filter sequentially on an input signal
|
/// Series of biquads that filter sequentially on an input signal
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// See (tests)
|
/// See (tests)
|
||||||
///
|
///
|
||||||
use super::*;
|
|
||||||
use super::biquad::Biquad;
|
|
||||||
use anyhow::{bail, Result};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
#[cfg_attr(feature = "python-bindings", pyclass)]
|
|
||||||
|
|
||||||
pub struct SeriesBiquad {
|
pub struct SeriesBiquad {
|
||||||
biqs: Vec<Biquad>,
|
biqs: Vec<Biquad>,
|
||||||
}
|
}
|
||||||
@ -19,6 +22,8 @@ pub struct SeriesBiquad {
|
|||||||
#[cfg(feature = "python-bindings")]
|
#[cfg(feature = "python-bindings")]
|
||||||
#[cfg_attr(feature = "python-bindings", pymethods)]
|
#[cfg_attr(feature = "python-bindings", pymethods)]
|
||||||
impl SeriesBiquad {
|
impl SeriesBiquad {
|
||||||
|
|
||||||
|
|
||||||
#[new]
|
#[new]
|
||||||
/// Create new series filter set. See [SeriesBiquad::new()]
|
/// Create new series filter set. See [SeriesBiquad::new()]
|
||||||
///
|
///
|
||||||
@ -37,8 +42,9 @@ impl SeriesBiquad {
|
|||||||
&mut self,
|
&mut self,
|
||||||
py: Python<'py>,
|
py: Python<'py>,
|
||||||
input: PyArrayLike1<Flt>,
|
input: PyArrayLike1<Flt>,
|
||||||
) -> PyResult<&'py PyArray1<Flt>> {
|
) -> Result<PyArr1Flt<'py>> {
|
||||||
Ok(self.filter(input.as_slice()?).into_pyarray(py))
|
let res = self.filter(input.as_slice()?);
|
||||||
|
Ok(PyArray1::from_vec_bound(py, res))
|
||||||
}
|
}
|
||||||
#[pyo3(name = "reset")]
|
#[pyo3(name = "reset")]
|
||||||
/// See: [SeriesBiquad::reset()]
|
/// See: [SeriesBiquad::reset()]
|
||||||
|
22
src/lib.rs
22
src/lib.rs
@ -20,28 +20,22 @@ pub mod daq;
|
|||||||
pub mod siggen;
|
pub mod siggen;
|
||||||
|
|
||||||
use filter::*;
|
use filter::*;
|
||||||
|
use daq::*;
|
||||||
|
|
||||||
|
|
||||||
/// A Python module implemented in Rust.
|
/// A Python module implemented in Rust.
|
||||||
#[cfg(feature = "python-bindings")]
|
#[cfg(feature = "python-bindings")]
|
||||||
#[pymodule]
|
#[pymodule]
|
||||||
#[pyo3(name="_lasprs")]
|
#[pyo3(name="_lasprs")]
|
||||||
fn lasprs(py: Python, m: &PyModule) -> PyResult<()> {
|
fn lasprs(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
|
|
||||||
|
|
||||||
pyo3_add_submodule_filter(py, m)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add filter submodule to extension
|
|
||||||
#[cfg(feature = "python-bindings")]
|
|
||||||
fn pyo3_add_submodule_filter(py: Python, m: &PyModule) -> PyResult<()> {
|
|
||||||
// Add filter submodule
|
// Add filter submodule
|
||||||
let filter_module = PyModule::new(py, "filter")?;
|
m.add_class::<filter::Biquad>()?;
|
||||||
filter_module.add_class::<filter::Biquad>()?;
|
m.add_class::<filter::SeriesBiquad>()?;
|
||||||
filter_module.add_class::<filter::SeriesBiquad>()?;
|
m.add_class::<filter::BiquadBank>()?;
|
||||||
filter_module.add_class::<filter::BiquadBank>()?;
|
|
||||||
m.add_submodule(filter_module)?;
|
daq::add_py_classses(m)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,9 +23,6 @@ use std::fmt::Debug;
|
|||||||
use std::iter::ExactSizeIterator;
|
use std::iter::ExactSizeIterator;
|
||||||
use std::slice::IterMut;
|
use std::slice::IterMut;
|
||||||
|
|
||||||
#[cfg(feature = "python-bindings")]
|
|
||||||
use pyo3::prelude::*;
|
|
||||||
|
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use rand::rngs::ThreadRng;
|
use rand::rngs::ThreadRng;
|
||||||
use rand_distr::StandardNormal;
|
use rand_distr::StandardNormal;
|
||||||
|
Loading…
Reference in New Issue
Block a user