use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WaveformSnippet {
pub primary_channel: usize,
pub center_sample: u64,
pub subsample_offset: f32,
pub channel_ids: Vec<usize>,
pub num_samples: usize,
pub waveform: Vec<f32>,
}
impl WaveformSnippet {
pub fn num_channels(&self) -> usize {
self.channel_ids.len()
}
pub fn primary_trace(&self) -> &[f32] {
let row = self.channel_ids.iter().position(|&c| c == self.primary_channel).unwrap_or(0);
self.waveform.get(row * self.num_samples..(row + 1) * self.num_samples).unwrap_or(&[])
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SnippetBatch {
pub data: Vec<f32>,
pub num_spikes: usize,
pub num_channels: usize,
pub num_samples: usize,
pub peak_index: usize,
pub primary_channels: Vec<usize>,
pub center_samples: Vec<u64>,
pub subsample_offsets: Vec<f32>,
pub channel_ids: Vec<usize>,
}
impl SnippetBatch {
pub fn new(num_channels: usize, num_samples: usize, peak_index: usize) -> Self {
Self {
data: Vec::new(),
num_spikes: 0,
num_channels,
num_samples,
peak_index,
primary_channels: Vec::new(),
center_samples: Vec::new(),
subsample_offsets: Vec::new(),
channel_ids: Vec::new(),
}
}
#[allow(clippy::too_many_arguments)]
pub fn from_raw_parts(
data: Vec<f32>,
num_spikes: usize,
num_channels: usize,
num_samples: usize,
peak_index: usize,
primary_channels: Vec<usize>,
center_samples: Vec<u64>,
subsample_offsets: Vec<f32>,
channel_ids: Vec<usize>,
) -> Self {
assert!(peak_index < num_samples.max(1), "peak index outside the snippet");
assert_eq!(data.len(), num_spikes * num_channels * num_samples);
assert_eq!(primary_channels.len(), num_spikes);
assert_eq!(center_samples.len(), num_spikes);
assert_eq!(subsample_offsets.len(), num_spikes);
assert_eq!(channel_ids.len(), num_spikes * num_channels);
Self {
data,
num_spikes,
num_channels,
num_samples,
peak_index,
primary_channels,
center_samples,
subsample_offsets,
channel_ids,
}
}
pub fn shape(&self) -> [usize; 3] {
[self.num_spikes, self.num_channels, self.num_samples]
}
pub fn as_flat_slice(&self) -> &[f32] {
&self.data
}
pub fn as_flat_slice_mut(&mut self) -> &mut [f32] {
&mut self.data
}
pub fn snippet_slice(&self, spike_idx: usize) -> &[f32] {
let stride = self.num_channels * self.num_samples;
let start = spike_idx * stride;
&self.data[start..start + stride]
}
pub fn channel_slice(&self, spike_idx: usize, local_ch: usize) -> &[f32] {
let start = (spike_idx * self.num_channels + local_ch) * self.num_samples;
&self.data[start..start + self.num_samples]
}
pub fn spike_channel_ids(&self, spike_idx: usize) -> &[usize] {
let start = spike_idx * self.num_channels;
&self.channel_ids[start..start + self.num_channels]
}
pub fn from_snippets(snippets: &[WaveformSnippet], peak_index: usize) -> Option<Self> {
if snippets.is_empty() {
return None;
}
let num_channels = snippets[0].num_channels();
let num_samples = snippets[0].num_samples;
let stride = num_channels * num_samples;
let num_spikes = snippets.len();
let mut data = Vec::with_capacity(num_spikes * stride);
let mut primary_channels = Vec::with_capacity(num_spikes);
let mut center_samples = Vec::with_capacity(num_spikes);
let mut subsample_offsets = Vec::with_capacity(num_spikes);
let mut channel_ids = Vec::with_capacity(num_spikes * num_channels);
for s in snippets {
if s.waveform.len() != stride || s.channel_ids.len() != num_channels {
continue;
}
data.extend_from_slice(&s.waveform);
primary_channels.push(s.primary_channel);
center_samples.push(s.center_sample);
subsample_offsets.push(s.subsample_offset);
channel_ids.extend_from_slice(&s.channel_ids);
}
let actual_spikes = primary_channels.len();
Some(Self {
data,
num_spikes: actual_spikes,
num_channels,
num_samples,
peak_index,
primary_channels,
center_samples,
subsample_offsets,
channel_ids,
})
}
pub fn to_snippets(&self) -> Vec<WaveformSnippet> {
let mut out = Vec::with_capacity(self.num_spikes);
for i in 0..self.num_spikes {
out.push(WaveformSnippet {
primary_channel: self.primary_channels[i],
center_sample: self.center_samples[i],
subsample_offset: self.subsample_offsets[i],
channel_ids: self.spike_channel_ids(i).to_vec(),
num_samples: self.num_samples,
waveform: self.snippet_slice(i).to_vec(),
});
}
out
}
}