fleascope_rs/
trigger_config.rs

1use crate::{flea_scope::CaptureConfigError, FleaProbe};
2
3pub trait TriggerConfig {
4    fn into_trigger_fields(self) -> StringifiedTriggerConfig;
5}
6
7pub struct StringifiedTriggerConfig {
8    trigger_fields: String,
9}
10
11impl StringifiedTriggerConfig {
12    pub fn into_string(self) -> String {
13        self.trigger_fields
14    }
15}
16
17#[derive(Debug, Clone, Copy, PartialEq)]
18pub enum BitState {
19    High,
20    Low,
21    DontCare,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq)]
25pub enum DigitalTriggerBehavior {
26    Auto,
27    While,
28    Start,
29    Stop,
30}
31
32impl DigitalTriggerBehavior {
33    pub fn as_str(&self) -> &'static str {
34        match self {
35            DigitalTriggerBehavior::Auto => "~",
36            DigitalTriggerBehavior::While => "",
37            DigitalTriggerBehavior::Start => "+",
38            DigitalTriggerBehavior::Stop => "-",
39        }
40    }
41}
42
43#[derive(Debug, Clone, Copy, PartialEq)]
44pub enum AnalogTriggerBehavior {
45    Auto,
46    Level,
47    Rising,
48    Falling,
49}
50
51impl AnalogTriggerBehavior {
52    pub fn as_str(&self) -> &'static str {
53        match self {
54            AnalogTriggerBehavior::Auto => "~",
55            AnalogTriggerBehavior::Level => "",
56            AnalogTriggerBehavior::Rising => "+",
57            AnalogTriggerBehavior::Falling => "-",
58        }
59    }
60}
61
62#[derive(Debug)]
63pub struct BitTriggerBuilder {
64    bit_states: [BitState; 9],
65}
66
67impl BitTriggerBuilder {
68    pub fn new() -> Self {
69        Self {
70            bit_states: [BitState::DontCare; 9],
71        }
72    }
73
74    pub fn set_bit(mut self, bit: usize, state: BitState) -> Self {
75        if bit >= self.bit_states.len() {
76            panic!(
77                "Bit index {} out of range, must be between 0 and {}",
78                bit,
79                self.bit_states.len() - 1
80            );
81        }
82        self.bit_states[bit] = state;
83        self
84    }
85
86    pub fn bit0(self, state: BitState) -> Self {
87        self.set_bit(0, state)
88    }
89
90    pub fn bit1(self, state: BitState) -> Self {
91        self.set_bit(1, state)
92    }
93
94    pub fn bit2(self, state: BitState) -> Self {
95        self.set_bit(2, state)
96    }
97
98    pub fn bit3(self, state: BitState) -> Self {
99        self.set_bit(3, state)
100    }
101
102    pub fn bit4(self, state: BitState) -> Self {
103        self.set_bit(4, state)
104    }
105
106    pub fn bit5(self, state: BitState) -> Self {
107        self.set_bit(5, state)
108    }
109
110    pub fn bit6(self, state: BitState) -> Self {
111        self.set_bit(6, state)
112    }
113
114    pub fn bit7(self, state: BitState) -> Self {
115        self.set_bit(7, state)
116    }
117
118    pub fn bit8(self, state: BitState) -> Self {
119        self.set_bit(8, state)
120    }
121
122    pub fn is_matching(self) -> DigitalTrigger {
123        DigitalTrigger::new(self.bit_states, DigitalTriggerBehavior::While)
124    }
125
126    pub fn starts_matching(self) -> DigitalTrigger {
127        DigitalTrigger::new(self.bit_states, DigitalTriggerBehavior::Start)
128    }
129
130    pub fn stops_matching(self) -> DigitalTrigger {
131        DigitalTrigger::new(self.bit_states, DigitalTriggerBehavior::Stop)
132    }
133
134    /// Same as is_matching, but will also trigger when the bits did not match within 100ms.
135    pub fn auto(self) -> DigitalTrigger {
136        DigitalTrigger::new(self.bit_states, DigitalTriggerBehavior::Auto)
137    }
138}
139
140impl Default for BitTriggerBuilder {
141    fn default() -> Self {
142        Self::new()
143    }
144}
145
146#[derive(Debug, Clone)]
147pub struct DigitalTrigger {
148    pub bit_states: [BitState; 9],
149    pub behavior: DigitalTriggerBehavior,
150}
151
152impl DigitalTrigger {
153    pub fn new(bit_states: [BitState; 9], behavior: DigitalTriggerBehavior) -> Self {
154        Self {
155            bit_states,
156            behavior,
157        }
158    }
159
160    pub fn start_capturing_when() -> BitTriggerBuilder {
161        BitTriggerBuilder::new()
162    }
163}
164
165impl TriggerConfig for DigitalTrigger {
166    fn into_trigger_fields(self) -> StringifiedTriggerConfig {
167        let mut relevant_bits = 0u32;
168        for (i, state) in self.bit_states.iter().enumerate() {
169            if *state != BitState::DontCare {
170                relevant_bits |= 1 << i;
171            }
172        }
173
174        let mut active_bits = 0u32;
175        for (i, state) in self.bit_states.iter().enumerate() {
176            if *state == BitState::High {
177                active_bits |= 1 << i;
178            }
179        }
180
181        let trigger_behavior_flag = self.behavior.as_str();
182        StringifiedTriggerConfig {
183            trigger_fields: format!(
184                "{}0x{:02x} 0x{:02x}",
185                trigger_behavior_flag, active_bits, relevant_bits
186            ),
187        }
188    }
189}
190
191#[derive(Debug, Clone)]
192pub struct AnalogTriggerBuilder {
193    pub volts: f64,
194    pub behavior: AnalogTriggerBehavior,
195}
196
197impl AnalogTriggerBuilder {
198    pub fn rising_edge(mut self) -> AnalogTriggerBuilder {
199        self.behavior = AnalogTriggerBehavior::Rising;
200        self
201    }
202
203    pub fn falling_edge(mut self) -> AnalogTriggerBuilder {
204        self.behavior = AnalogTriggerBehavior::Falling;
205        self
206    }
207
208    pub fn level(mut self) -> AnalogTriggerBuilder {
209        self.behavior = AnalogTriggerBehavior::Level;
210        self
211    }
212
213    /// Same as level, but will also trigger when the voltage did not match within 100ms.
214    pub fn auto(mut self) -> AnalogTriggerBuilder {
215        self.behavior = AnalogTriggerBehavior::Auto;
216        self
217    }
218
219    pub fn into_trigger(self, flea_probe: &FleaProbe) -> Result<AnalogTrigger, CaptureConfigError> {
220        let raw_level = (flea_probe.voltage_to_raw(self.volts) / 4.0 + 0.5) as i16;
221
222        if !(-1023..=1023).contains(&raw_level) {
223            return Err(CaptureConfigError::VoltageOutOfRange);
224        }
225        Ok(AnalogTrigger::new(raw_level, self.behavior))
226    }
227}
228
229#[derive(Debug, Clone)]
230pub struct AnalogTrigger {
231    pub level: i16,
232    pub behavior: AnalogTriggerBehavior,
233}
234
235impl AnalogTrigger {
236    pub fn new(raw_value: i16, behavior: AnalogTriggerBehavior) -> Self {
237        Self {
238            level: raw_value,
239            behavior,
240        }
241    }
242
243    pub fn start_capturing_when(volts: f64) -> AnalogTriggerBuilder {
244        AnalogTriggerBuilder {
245            volts,
246            behavior: AnalogTriggerBehavior::Auto,
247        }
248    }
249}
250
251impl TriggerConfig for AnalogTrigger {
252    fn into_trigger_fields(self) -> StringifiedTriggerConfig {
253        let trigger_behavior_flag = self.behavior.as_str();
254
255        StringifiedTriggerConfig {
256            trigger_fields: format!("{}{} 0", trigger_behavior_flag, self.level),
257        }
258    }
259}
260
261/// A unified trigger type that can represent both analog and digital triggers.
262/// This allows treating all triggers uniformly in the API.
263#[derive(Debug, Clone)]
264pub enum Trigger {
265    Analog(AnalogTrigger),
266    Digital(DigitalTrigger),
267}
268
269impl From<AnalogTrigger> for Trigger {
270    fn from(trigger: AnalogTrigger) -> Self {
271        Self::Analog(trigger)
272    }
273}
274
275impl From<DigitalTrigger> for Trigger {
276    fn from(trigger: DigitalTrigger) -> Self {
277        Self::Digital(trigger)
278    }
279}