1#![cfg_attr(feature = "simd", feature(portable_simd))]
3#![cfg_attr(
5 all(
6 feature = "simd",
7 any(target_arch = "riscv32", target_arch = "riscv64")
8 ),
9 feature(stdarch_riscv_feature_detection)
10)]
11#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
12
13pub use rustradio_macros;
114
115pub mod add;
117pub mod add_const;
118pub mod au;
119pub mod binary_slicer;
120pub mod burst_tagger;
121pub mod cma;
122pub mod complex_to_mag2;
123pub mod constant_source;
124pub mod convert;
125pub mod correlate_access_code;
126pub mod debug_sink;
127pub mod delay;
128pub mod descrambler;
129pub mod fft;
130pub mod fft_filter;
131pub mod fft_stream;
132pub mod file_sink;
133pub mod file_source;
134pub mod fir;
135pub mod hasher;
136pub mod hdlc_deframer;
137pub mod hilbert;
138pub mod iir_filter;
139pub mod il2p_deframer;
140pub mod multiply_const;
141pub mod nrzi;
142pub mod null_sink;
143pub mod pdu_writer;
144pub mod quadrature_demod;
145pub mod rational_resampler;
146pub mod rtlsdr_decode;
147pub mod sigmf;
148pub mod signal_source;
149pub mod single_pole_iir_filter;
150pub mod skip;
151pub mod stream_to_pdu;
152pub mod symbol_sync;
153pub mod tcp_source;
154pub mod tee;
155pub mod to_text;
156pub mod vec_to_stream;
157pub mod vector_sink;
158pub mod vector_source;
159pub mod wpcr;
160pub mod xor;
161pub mod xor_const;
162pub mod zero_crossing;
163
164#[cfg(feature = "audio")]
165pub mod audio_sink;
166
167#[cfg(feature = "rtlsdr")]
168pub mod rtlsdr_source;
169
170#[cfg(feature = "soapysdr")]
171pub mod soapysdr_sink;
172
173#[cfg(feature = "soapysdr")]
174pub mod soapysdr_source;
175
176pub mod block;
177pub mod blocks;
178pub mod circular_buffer;
179pub mod graph;
180pub mod mtgraph;
181pub mod stream;
182pub mod window;
183
184#[cfg(feature = "async")]
185pub mod agraph;
186
187pub type Float = f32;
189
190pub type Complex = num_complex::Complex<Float>;
192
193#[derive(thiserror::Error, Debug)]
195#[non_exhaustive]
196pub enum Error {
197 #[error("IO Error on {path:?}: {source:?}")]
199 FileIo {
200 #[source]
201 source: std::io::Error,
202 path: std::path::PathBuf,
203 },
204
205 #[error("DeviceError: {msg:?}: {source:?}")]
207 DeviceError {
208 #[source]
209 source: Box<dyn std::error::Error + Send + Sync>,
210 msg: Option<String>,
211 },
212
213 #[error("IO Error: {0}")]
215 Io(#[from] std::io::Error),
216
217 #[error("An error occurred: {0}")]
219 Plain(String),
220
221 #[error("{msg:?}: {source:?}")]
223 Other {
224 #[source]
225 source: Box<dyn std::error::Error + Send + Sync>,
226 msg: Option<String>,
227 },
228}
229
230impl Error {
231 #[must_use]
233 pub fn msg<S: Into<String>>(msg: S) -> Self {
234 Self::Plain(msg.into())
235 }
236
237 #[must_use]
239 pub fn file_io<P: Into<std::path::PathBuf>>(source: std::io::Error, path: P) -> Self {
240 Self::FileIo {
241 path: path.into(),
242 source,
243 }
244 }
245
246 #[must_use]
250 pub fn wrap<S: Into<String>>(
251 source: impl std::error::Error + Send + Sync + 'static,
252 msg: S,
253 ) -> Self {
254 let msg = msg.into();
255 Self::Other {
256 source: Box::new(source),
257 msg: if msg.is_empty() { None } else { Some(msg) },
258 }
259 }
260
261 #[must_use]
265 pub fn device<S: Into<String>>(
266 source: impl std::error::Error + Send + Sync + 'static,
267 msg: S,
268 ) -> Self {
269 let msg = msg.into();
270 Self::DeviceError {
271 source: Box::new(source),
272 msg: if msg.is_empty() { None } else { Some(msg) },
273 }
274 }
275}
276
277#[macro_export]
278macro_rules! error_from {
279 ($ctx:literal, $($err_ty:ty),* $(,)?) => {
280 $(
281 impl From<$err_ty> for Error {
282 fn from(e: $err_ty) -> Self {
283 let s = if $ctx.is_empty() {
284 format!("{}", std::any::type_name::<$err_ty>())
285 } else {
286 format!("{} in {}", std::any::type_name::<$err_ty>(), $ctx)
287 };
288 Error::wrap(e, s)
289 }
290 }
291 )*
292 };
293}
294
295#[macro_export]
296macro_rules! blockchain {
297 ($g:expr, $prev:ident, $($cons:expr),* $(,)?) => {{
298 $(
299 let (block, $prev) = $cons;
300 $g.add(Box::new(block));
301 )*
302 $prev
303 }};
304}
305
306error_from!(
307 "", std::sync::mpsc::RecvError,
309 std::sync::mpsc::TryRecvError,
310 std::string::FromUtf8Error,
311 std::array::TryFromSliceError,
312 std::num::TryFromIntError,
313);
314
315pub type Result<T> = std::result::Result<T, Error>;
316
317#[derive(Debug)]
319pub struct Repeat {
320 repeater: Repeater,
321 count: u64,
322}
323
324impl Repeat {
325 #[must_use]
327 pub fn finite(n: u64) -> Self {
328 Self {
329 repeater: Repeater::Finite(n),
330 count: 0,
331 }
332 }
333
334 #[must_use]
336 pub fn infinite() -> Self {
337 Self {
338 repeater: Repeater::Infinite,
339 count: 0,
340 }
341 }
342
343 #[must_use]
345 pub fn again(&mut self) -> bool {
346 self.count += 1;
347 match self.repeater {
348 Repeater::Finite(n) => {
349 self.repeater = Repeater::Finite(n - 1);
350 n > 1
351 }
352 Repeater::Infinite => true,
353 }
354 }
355
356 #[must_use]
358 pub fn done(&self) -> bool {
359 match self.repeater {
360 Repeater::Finite(n) => n == 0,
361 Repeater::Infinite => false,
362 }
363 }
364
365 #[must_use]
367 pub fn count(&self) -> u64 {
368 self.count
369 }
370}
371
372#[derive(Debug)]
373enum Repeater {
374 Finite(u64),
375 Infinite,
376}
377
378pub struct Feature {
379 name: String,
380 build: bool,
381 detected: bool,
382}
383
384impl Feature {
385 #[must_use]
386 fn new<S: Into<String>>(name: S, build: bool, detected: bool) -> Self {
387 Self {
388 name: name.into(),
389 build,
390 detected,
391 }
392 }
393}
394
395#[must_use]
396pub fn environment_str(features: &[Feature]) -> String {
397 let mut s = "Feature Build Detected\n".to_string();
398 for feature in features {
399 s += &format!(
400 "{:10} {:-5} {:-5}\n",
401 feature.name, feature.build, feature.detected
402 );
403 }
404 s
405}
406
407pub fn check_environment() -> Result<Vec<Feature>> {
408 #[allow(unused_mut)]
409 let mut assumptions: Vec<Feature> = Vec::new();
410 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
411 {
412 assumptions.push(Feature::new(
413 "FMA",
414 cfg!(target_feature = "fma"),
415 is_x86_feature_detected!("fma"),
416 ));
417 assumptions.push(Feature::new(
418 "SSE",
419 cfg!(target_feature = "sse"),
420 is_x86_feature_detected!("sse"),
421 ));
422 assumptions.push(Feature::new(
423 "SSE3",
424 cfg!(target_feature = "sse3"),
425 is_x86_feature_detected!("sse3"),
426 ));
427 assumptions.push(Feature::new(
428 "AVX",
429 cfg!(target_feature = "avx"),
430 is_x86_feature_detected!("avx"),
431 ));
432 assumptions.push(Feature::new(
433 "AVX2",
434 cfg!(target_feature = "avx2"),
435 is_x86_feature_detected!("avx2"),
436 ));
437 }
438
439 #[cfg(all(
445 feature = "simd",
446 any(target_arch = "riscv32", target_arch = "riscv64")
447 ))]
448 {
449 assumptions.push(Feature::new(
450 "Vector",
451 cfg!(target_feature = "v"),
452 std::arch::is_riscv_feature_detected!("v"),
453 ));
454 }
455
456 let errs: Vec<_> = assumptions
457 .iter()
458 .filter_map(|f| {
459 if f.build && !f.detected {
460 Some(format!(
461 "Feature {} assumed by build flags but not detected",
462 f.name
463 ))
464 } else {
465 None
466 }
467 })
468 .collect();
469 if errs.is_empty() {
470 Ok(assumptions)
471 } else {
472 Err(Error::msg(format!("{errs:?}")))
473 }
474}
475
476pub trait Sample: Copy + Default + Send + Sync + 'static {
478 type Type;
480
481 #[must_use]
483 fn size() -> usize;
484
485 fn parse(data: &[u8]) -> Result<Self::Type>;
487
488 #[must_use]
490 fn serialize(&self) -> Vec<u8>;
491}
492
493impl Sample for Complex {
494 type Type = Complex;
495 fn size() -> usize {
496 std::mem::size_of::<Self>()
497 }
498 fn parse(data: &[u8]) -> Result<Self::Type> {
499 if data.len() != Self::size() {
500 panic!("TODO: Complex is wrong size");
501 }
502 let i = Float::from_le_bytes(data[0..Self::size() / 2].try_into()?);
503 let q = Float::from_le_bytes(data[Self::size() / 2..].try_into()?);
504 Ok(Complex::new(i, q))
505 }
506 fn serialize(&self) -> Vec<u8> {
507 let mut ret = Vec::new();
508 ret.extend(Float::to_le_bytes(self.re));
509 ret.extend(Float::to_le_bytes(self.im));
510 ret
511 }
512}
513
514impl Sample for Float {
515 type Type = Float;
516 fn size() -> usize {
517 std::mem::size_of::<Self>()
518 }
519 fn parse(data: &[u8]) -> Result<Self::Type> {
520 if data.len() != Self::size() {
521 panic!("TODO: Float is wrong size");
522 }
523 Ok(Float::from_le_bytes(data[0..Self::size()].try_into()?))
524 }
525 fn serialize(&self) -> Vec<u8> {
526 Float::to_le_bytes(*self).to_vec()
527 }
528}
529
530impl Sample for u8 {
531 type Type = u8;
532 fn size() -> usize {
533 std::mem::size_of::<Self>()
534 }
535 fn parse(data: &[u8]) -> Result<Self::Type> {
536 if data.len() != Self::size() {
537 panic!("TODO: u8 is wrong size");
538 }
539 Ok(data[0])
540 }
541 fn serialize(&self) -> Vec<u8> {
542 vec![*self]
543 }
544}
545
546impl Sample for u32 {
547 type Type = u32;
548 fn size() -> usize {
549 4
550 }
551 fn parse(data: &[u8]) -> Result<Self::Type> {
552 if data.len() != Self::size() {
553 panic!("TODO: Float is wrong size");
554 }
555 Ok(u32::from_le_bytes(data[0..Self::size()].try_into()?))
556 }
557 fn serialize(&self) -> Vec<u8> {
558 u32::to_le_bytes(*self).to_vec()
559 }
560}
561
562impl Sample for i32 {
563 type Type = i32;
564 fn size() -> usize {
565 std::mem::size_of::<Self>()
566 }
567 fn parse(data: &[u8]) -> Result<Self::Type> {
568 if data.len() != Self::size() {
569 panic!("TODO: Float is wrong size");
570 }
571 Ok(i32::from_le_bytes(data[0..Self::size()].try_into()?))
572 }
573 fn serialize(&self) -> Vec<u8> {
574 i32::to_le_bytes(*self).to_vec()
575 }
576}
577
578#[allow(clippy::len_without_is_empty)]
580pub trait Len {
581 #[must_use]
583 fn len(&self) -> usize;
584}
585impl<T> Len for Vec<T> {
586 fn len(&self) -> usize {
587 self.len()
588 }
589}
590
591#[cfg(test)]
592#[cfg_attr(coverage_nightly, coverage(off))]
593pub mod tests {
594 use super::*;
596
597 pub fn assert_almost_equal_complex(left: &[Complex], right: &[Complex]) {
601 assert_eq!(
602 left.len(),
603 right.len(),
604 "\nleft: {left:?}\nright: {right:?}",
605 );
606 for i in 0..left.len() {
607 let dist = (left[i] - right[i]).norm_sqr().sqrt();
608 if dist > 0.001 {
609 assert_eq!(
610 left[i], right[i],
611 "\nElement {i}:\nleft: {left:?}\nright: {right:?}",
612 );
613 }
614 }
615 }
616
617 pub fn assert_almost_equal_float(left: &[Float], right: &[Float]) {
621 assert_eq!(
622 left.len(),
623 right.len(),
624 "\nleft: {left:?}\nright: {right:?}",
625 );
626 for i in 0..left.len() {
627 let dist = (left[i] - right[i]).sqrt();
628 if dist > 0.001 {
629 assert_eq!(left[i], right[i], "\nleft: {left:?}\nright: {right:?}");
630 }
631 }
632 }
633
634 #[test]
635 fn check_env() -> Result<()> {
636 assert!(!environment_str(&check_environment()?).is_empty());
637 Ok(())
638 }
639
640 #[test]
641 fn error_wrap() {
642 use std::error::Error as SysError;
643 let e = Error::msg("foo");
644 assert!(matches![e, Error::Plain(_)]);
645 let _e2: &dyn std::error::Error = &e;
646 let e_str = e.to_string();
647 assert_eq!(e_str, "An error occurred: foo");
648 let e3 = Error::wrap(e, "foo");
649 assert!(matches![e3, Error::Other { source: _, msg: _ }]);
650 let e4 = e3.source().unwrap();
651 assert_eq!(e_str, e4.to_string());
652 let e5 = e4.downcast_ref::<Error>().unwrap();
653 assert!(matches![e5, Error::Plain(_)]);
654 }
655}