use serde::{Deserialize, Serialize};
pub const PRE_TRIGGER_CYCLES: usize = 10;
pub const POST_TRIGGER_CYCLES: usize = 50;
pub const SAMPLES_PER_CYCLE: usize = 160;
pub const PRE_TRIGGER_SAMPLES: usize = PRE_TRIGGER_CYCLES * SAMPLES_PER_CYCLE; pub const POST_TRIGGER_SAMPLES: usize = POST_TRIGGER_CYCLES * SAMPLES_PER_CYCLE; pub const TOTAL_SAMPLES: usize = PRE_TRIGGER_SAMPLES + POST_TRIGGER_SAMPLES;
pub const MAX_CHANNELS: usize = 8;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TriggerSource {
Manual,
Dip(u8), Swell(u8), Interruption(u8), Rvc(u8), Alarm(u8), }
impl TriggerSource {
pub fn as_str(&self) -> &'static str {
match self {
TriggerSource::Manual => "MANUAL",
TriggerSource::Dip(_) => "DIP",
TriggerSource::Swell(_) => "SWELL",
TriggerSource::Interruption(_) => "INTERRUPTION",
TriggerSource::Rvc(_) => "RVC",
TriggerSource::Alarm(_) => "ALARM",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum OscillographyState {
Idle,
Armed,
Capturing,
Ready,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct OscillographyHeader {
pub id: heapless::String<32>,
pub trigger_source: TriggerSource,
pub timestamp_ns: u64,
pub phase_mode: u8, pub sample_rate_hz: u32,
pub num_channels: u8,
pub total_samples: u32,
}
pub struct ChannelBuffer {
pub pre_trigger: [f32; PRE_TRIGGER_SAMPLES],
pub post_trigger: [f32; POST_TRIGGER_SAMPLES],
pub pre_write_ptr: usize,
pub post_write_ptr: usize,
}
impl Default for ChannelBuffer {
fn default() -> Self {
Self {
pre_trigger: [0.0; PRE_TRIGGER_SAMPLES],
post_trigger: [0.0; POST_TRIGGER_SAMPLES],
pre_write_ptr: 0,
post_write_ptr: 0,
}
}
}
impl ChannelBuffer {
pub fn reset(&mut self) {
self.pre_write_ptr = 0;
self.post_write_ptr = 0;
self.pre_trigger.fill(0.0);
self.post_trigger.fill(0.0);
}
#[inline(always)]
pub fn feed_pre(&mut self, val: f32) {
self.pre_trigger[self.pre_write_ptr] = val;
self.pre_write_ptr = (self.pre_write_ptr + 1) % PRE_TRIGGER_SAMPLES;
}
#[inline(always)]
pub fn feed_post(&mut self, val: f32) -> bool {
if self.post_write_ptr < POST_TRIGGER_SAMPLES {
self.post_trigger[self.post_write_ptr] = val;
self.post_write_ptr += 1;
self.post_write_ptr == POST_TRIGGER_SAMPLES
} else {
true
}
}
pub fn read_all(&self, dest: &mut [f32]) {
let mut idx = 0;
for i in 0..PRE_TRIGGER_SAMPLES {
let src_idx = (self.pre_write_ptr + i) % PRE_TRIGGER_SAMPLES;
dest[idx] = self.pre_trigger[src_idx];
idx += 1;
}
let post_limit = self.post_write_ptr.min(POST_TRIGGER_SAMPLES);
for i in 0..post_limit {
dest[idx] = self.post_trigger[i];
idx += 1;
}
if idx < dest.len() {
let last_val = if idx > 0 { dest[idx - 1] } else { 0.0 };
dest[idx..].fill(last_val);
}
}
}
pub struct OscillographyManager {
pub channels: [ChannelBuffer; MAX_CHANNELS],
pub state: OscillographyState,
pub trigger_source: Option<TriggerSource>,
pub trigger_timestamp_ns: u64,
pub phase_mode: u8,
pub active_channels: u8,
}
impl Default for OscillographyManager {
fn default() -> Self {
Self {
channels: [
ChannelBuffer::default(),
ChannelBuffer::default(),
ChannelBuffer::default(),
ChannelBuffer::default(),
ChannelBuffer::default(),
ChannelBuffer::default(),
ChannelBuffer::default(),
ChannelBuffer::default(),
],
state: OscillographyState::Idle,
trigger_source: None,
trigger_timestamp_ns: 0,
phase_mode: 0,
active_channels: 8,
}
}
}
impl OscillographyManager {
pub fn new() -> Self {
Self::default()
}
pub fn arm(&mut self, phase_mode: u8, num_channels: u8) {
self.state = OscillographyState::Armed;
self.trigger_source = None;
self.trigger_timestamp_ns = 0;
self.phase_mode = phase_mode;
self.active_channels = num_channels;
for ch in &mut self.channels {
ch.reset();
}
}
pub fn force_trigger(&mut self, source: TriggerSource, now_ns: u64) {
if self.state == OscillographyState::Armed {
self.state = OscillographyState::Capturing;
self.trigger_source = Some(source);
self.trigger_timestamp_ns = now_ns;
for ch in &mut self.channels {
ch.post_write_ptr = 0;
}
}
}
#[inline(always)]
pub fn feed_sample(&mut self, samples: &[f32; MAX_CHANNELS], _now_ns: u64) -> bool {
match self.state {
OscillographyState::Armed => {
for (i, ch) in self
.channels
.iter_mut()
.enumerate()
.take(self.active_channels as usize)
{
ch.feed_pre(samples[i]);
}
false
}
OscillographyState::Capturing => {
let mut completed = true;
for (i, ch) in self
.channels
.iter_mut()
.enumerate()
.take(self.active_channels as usize)
{
let ch_done = ch.feed_post(samples[i]);
completed = completed && ch_done;
}
if completed {
self.state = OscillographyState::Ready;
true
} else {
false
}
}
_ => false,
}
}
}