brainvision 0.0.1

Rust library and TUI for Brain Products BrainVision RDA EEG streams over TCP/IP
Documentation
//! Recording/export helpers.

use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::Path;

use crate::device::Scan;
use crate::error::BrainVisionError;
use crate::types::{HeaderInfo, Marker};

/// Write scans to CSV (`sample_index,ch1,ch2,...`).
pub fn write_scans_csv<P: AsRef<Path>>(path: P, scans: &[Scan]) -> Result<(), BrainVisionError> {
    let mut w =
        BufWriter::new(File::create(path).map_err(|e| BrainVisionError::Io(e.to_string()))?);
    if let Some(first) = scans.first() {
        write!(&mut w, "sample").map_err(|e| BrainVisionError::Io(e.to_string()))?;
        for i in 0..first.data.len() {
            write!(&mut w, ",ch{}", i + 1).map_err(|e| BrainVisionError::Io(e.to_string()))?;
        }
        writeln!(&mut w).map_err(|e| BrainVisionError::Io(e.to_string()))?;
    }
    for (i, s) in scans.iter().enumerate() {
        write!(&mut w, "{}", i).map_err(|e| BrainVisionError::Io(e.to_string()))?;
        for v in &s.data {
            write!(&mut w, ",{}", v).map_err(|e| BrainVisionError::Io(e.to_string()))?;
        }
        writeln!(&mut w).map_err(|e| BrainVisionError::Io(e.to_string()))?;
    }
    Ok(())
}

/// Write markers to CSV.
pub fn write_markers_csv<P: AsRef<Path>>(
    path: P,
    markers: &[Marker],
) -> Result<(), BrainVisionError> {
    let mut w =
        BufWriter::new(File::create(path).map_err(|e| BrainVisionError::Io(e.to_string()))?);
    writeln!(&mut w, "position,points,channel,type,description")
        .map_err(|e| BrainVisionError::Io(e.to_string()))?;
    for m in markers {
        writeln!(
            &mut w,
            "{},{},{},{},{}",
            m.position, m.points, m.channel, m.kind, m.description
        )
        .map_err(|e| BrainVisionError::Io(e.to_string()))?;
    }
    Ok(())
}

/// Write a simple BrainVision-like triplet:
/// - `<prefix>.vhdr` metadata
/// - `<prefix>.eeg.csv` scan values
/// - `<prefix>.vmrk.csv` markers
///
/// This is an interoperability helper, not a full binary BrainVision writer.
pub fn write_brainvision_triplet<P: AsRef<Path>>(
    prefix: P,
    header: &HeaderInfo,
    scans: &[Scan],
    markers: &[Marker],
) -> Result<(), BrainVisionError> {
    let prefix = prefix.as_ref();
    let stem = prefix.to_string_lossy();
    let vhdr = format!("{}.vhdr", stem);
    let eeg = format!("{}.eeg.csv", stem);
    let vmrk = format!("{}.vmrk.csv", stem);

    let mut w =
        BufWriter::new(File::create(vhdr).map_err(|e| BrainVisionError::Io(e.to_string()))?);
    writeln!(&mut w, "Brain Vision Data Exchange Header File Version 1.0")
        .map_err(|e| BrainVisionError::Io(e.to_string()))?;
    writeln!(&mut w, "[Common Infos]").map_err(|e| BrainVisionError::Io(e.to_string()))?;
    writeln!(&mut w, "DataFile={}", eeg).map_err(|e| BrainVisionError::Io(e.to_string()))?;
    writeln!(&mut w, "MarkerFile={}", vmrk).map_err(|e| BrainVisionError::Io(e.to_string()))?;
    writeln!(&mut w, "NumberOfChannels={}", header.channel_count)
        .map_err(|e| BrainVisionError::Io(e.to_string()))?;
    writeln!(&mut w, "SamplingInterval={}", header.sampling_interval_us)
        .map_err(|e| BrainVisionError::Io(e.to_string()))?;

    write_scans_csv(eeg, scans)?;
    write_markers_csv(vmrk, markers)?;
    Ok(())
}