lasprs 0.14.3

Library for Acoustic Signal Processing (Rust edition, with optional Python bindings via pyo3)
Documentation
//! Example demonstrating recording data to a measurement file and then
//! reading it back to calculate the auto power of the first channel.
//!
//! This example shows how to:
//! 1. Set up a StreamMgr and configure input channels
//! 2. Record data to an HDF5 measurement file
//! 3. Open the measurement file after recording
//! 4. Calculate and print the auto power spectrum of the first channel

use anyhow::Result;
use lasprs::{Flt, FreqWeighting, Overlap, WindowType, daq::*, measurement::*, ps::*};
use std::{path::PathBuf, thread, time::Duration};

fn main() -> Result<()> {
    println!("LaspRS Recording and Analysis Example");
    println!("====================================");

    // Step 1: Set up the StreamMgr and configure recording
    let mut stream_mgr = StreamMgr::new_with_devices();

    // Get available devices
    let devices = stream_mgr.getDeviceInfo();
    if devices.is_empty() {
        anyhow::bail!("No audio devices available for recording");
    }

    println!("Available devices:");
    for (i, device) in devices.iter().enumerate() {
        println!("  {}: {}", i, device.device_name);
    }

    // Use the first available device and create a configuration
    let device = &devices[0];
    let mut config = DaqConfig::newFromDeviceInfo(device);

    // Enable the first input channel
    let mut in_channels = config.inchannel_config.clone();
    if !in_channels.is_empty() {
        in_channels[0].enabled = true;
        in_channels[0].name = "Channel 1".to_string();
        in_channels[0].sensitivity = 1.0; // Set sensitivity as needed
        config.inchannel_config = in_channels;
    } else {
        anyhow::bail!("No input channels available on the selected device");
    }

    println!("Using device: {}", device.device_name);
    println!("Sample rate: {} Hz", config.sampleRate(device));

    // Step 2: Configure recording settings
    let filename = "example_recording.h5";
    let recording_duration = Duration::from_secs(3); // Record for 3 seconds

    // Set up CPS settings for power spectrum computation
    let cps_settings = CPSSettings {
        nfft: 2048, // FFT size
        overlap: Overlap::NoOverlap {},
        window: WindowType::Hann,
        istart: None,
        istop: None,
    };

    let record_settings = RecordSettings::new(
        filename,
        false,
        Some(recording_duration),
        None, // No start delay
        Some(cps_settings),
        None,
        false,
        None,
        vec![],
    )?;

    println!("Recording settings:");
    println!("  Filename: {}", filename);
    println!("  Duration: {:?}", recording_duration);
    println!("  FFT size: {}", cps_settings.nfft);

    // Step 3: Start the input stream and begin recording
    println!("\nStarting input stream...");
    stream_mgr.startStream(StreamType::Input, &config)?;

    println!("Starting recording...");
    let mut recording = Recording::new(record_settings, &mut stream_mgr)?;

    // Monitor recording progress
    loop {
        match recording.status() {
            RecordStatus::Idle {} => {
                println!("Status: Idle");
            }
            RecordStatus::Waiting {} => {
                println!("Status: Waiting for start delay...");
            }
            RecordStatus::Recording {
                recorded,
                pct_done,
                clipped,
            } => {
                println!(
                    "Status: Recording... {:.1}% complete ({:.2}s recorded)",
                    pct_done * 100.0,
                    recorded
                );
                if clipped {
                    println!("  WARNING: Signal clipping detected!");
                }
            }
            RecordStatus::Finished { finishedrecording } => {
                println!("Recording finished!");
                if finishedrecording.clipped {
                    println!("  WARNING: Recording contained clipped samples!");
                }
                if let Some(error) = &finishedrecording.error {
                    println!("  ERROR: {}", error);
                    anyhow::bail!("Recording failed: {}", error);
                }
                break;
            }
        }

        thread::sleep(Duration::from_millis(100));
    }

    // Stop the stream
    println!("Stopping input stream...");
    stream_mgr.stopStream(StreamType::Input)?;

    // Step 4: Open the measurement file and analyze the data
    println!("\nAnalyzing recorded data...");

    let filepath = PathBuf::from(filename);
    if !filepath.exists() {
        anyhow::bail!("Recording file '{}' does not exist", filename);
    }

    // Open the measurement file
    let measurement_arc = Measurement::from_file(&filepath)?;

    // Access measurement data through the Arc<RwLock<_>>
    let (nchannels, samplerate, duration);
    let channel_names;
    let cps_result;
    {
        let mut measurement = measurement_arc.write();

        println!("Measurement file opened successfully:");
        nchannels = measurement.nchannels();
        samplerate = measurement.samplerate();
        duration = measurement.get_duration();

        println!("  Number of channels: {}", nchannels);
        println!("  Sample rate: {} Hz", samplerate);
        println!("  Duration: {:.2} seconds", duration);

        channel_names = measurement.channel_names();
        println!("  Channel names: {:?}", channel_names);

        // Step 5: Calculate auto power spectrum for the first channel
        println!("\nCalculating auto power spectrum for first channel...");

        // Get cross-power spectra (CPS) - this includes auto power spectra on the diagonal
        cps_result = measurement.CPS(
            &cps_settings,
            FreqWeighting::Z, // No frequency weighting
            Some(&[0]),       // Only first channel
        )?;
    }

    // Extract auto power spectrum for first channel (diagonal element [0,0])
    let auto_power = cps_result.ap(0);

    println!("Auto power spectrum calculated!");
    println!("  Number of frequency bins: {}", auto_power.len());

    // Calculate some statistics
    let total_power: Flt = auto_power.iter().sum();

    let max_power = auto_power.iter().fold(0.0, |acc, x| Flt::max(acc, *x));

    let avg_power = total_power / auto_power.len() as f64;

    println!("Power spectrum statistics:");
    println!("  Total power: {:.6e}", total_power);
    println!("  Average power per bin: {:.6e}", avg_power);
    println!("  Maximum power in any bin: {:.6e}", max_power);

    // Clean up - remove the temporary recording file
    println!("\nCleaning up...");
    if filepath.exists() {
        std::fs::remove_file(&filepath)?;
        println!("Removed temporary recording file: {}", filename);
    }

    println!("\nExample completed successfully!");

    Ok(())
}