Skip to main content

nanonis_rs/client/oscilloscope/
types.rs

1use crate::error::NanonisError;
2
3// ==================== Oscilloscope Types ====================
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct OscilloscopeIndex(pub i32);
7
8impl From<OscilloscopeIndex> for i32 {
9    fn from(osci: OscilloscopeIndex) -> Self {
10        osci.0
11    }
12}
13
14impl From<i32> for OscilloscopeIndex {
15    fn from(index: i32) -> Self {
16        OscilloscopeIndex(index)
17    }
18}
19
20impl From<usize> for OscilloscopeIndex {
21    fn from(index: usize) -> Self {
22        OscilloscopeIndex(index as i32)
23    }
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum TriggerMode {
28    Immediate = 0,
29    Level = 1,
30    Digital = 2,
31}
32
33impl From<TriggerMode> for u16 {
34    fn from(mode: TriggerMode) -> Self {
35        mode as u16
36    }
37}
38
39impl From<u16> for TriggerMode {
40    fn from(value: u16) -> Self {
41        match value {
42            0 => TriggerMode::Immediate,
43            1 => TriggerMode::Level,
44            2 => TriggerMode::Digital,
45            _ => TriggerMode::Immediate,
46        }
47    }
48}
49
50impl From<i32> for TriggerMode {
51    fn from(value: i32) -> Self {
52        TriggerMode::from(value as u16)
53    }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum TriggerSlope {
58    Falling = 0,
59    Rising = 1,
60}
61
62impl From<TriggerSlope> for u16 {
63    fn from(slope: TriggerSlope) -> Self {
64        slope as u16
65    }
66}
67
68impl TryFrom<u16> for TriggerSlope {
69    type Error = NanonisError;
70
71    fn try_from(value: u16) -> Result<Self, Self::Error> {
72        match value {
73            0 => Ok(TriggerSlope::Falling),
74            1 => Ok(TriggerSlope::Rising),
75            _ => Err(NanonisError::Protocol(format!(
76                "Invalid trigger slope: {}",
77                value
78            ))),
79        }
80    }
81}
82
83#[derive(Debug, Clone, Copy)]
84pub struct TriggerLevel(pub f64);
85
86impl From<TriggerLevel> for f64 {
87    fn from(level: TriggerLevel) -> Self {
88        level.0
89    }
90}
91
92impl From<f64> for TriggerLevel {
93    fn from(level: f64) -> Self {
94        TriggerLevel(level)
95    }
96}
97
98impl From<f32> for TriggerLevel {
99    fn from(level: f32) -> Self {
100        TriggerLevel(level as f64)
101    }
102}
103
104#[derive(Debug, Clone, Copy)]
105pub struct SampleCount(pub i32);
106
107impl SampleCount {
108    pub fn new(count: i32) -> Self {
109        Self(count)
110    }
111}
112
113impl From<SampleCount> for i32 {
114    fn from(samples: SampleCount) -> Self {
115        samples.0
116    }
117}
118
119impl From<i32> for SampleCount {
120    fn from(count: i32) -> Self {
121        SampleCount(count)
122    }
123}
124
125impl From<u32> for SampleCount {
126    fn from(count: u32) -> Self {
127        SampleCount(count as i32)
128    }
129}
130
131impl From<usize> for SampleCount {
132    fn from(count: usize) -> Self {
133        SampleCount(count as i32)
134    }
135}
136
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138pub enum OsciTriggerMode {
139    Immediate = 0,
140    Level = 1,
141    Auto = 2,
142}
143
144impl From<OsciTriggerMode> for u16 {
145    fn from(mode: OsciTriggerMode) -> Self {
146        mode as u16
147    }
148}
149
150impl TryFrom<u16> for OsciTriggerMode {
151    type Error = NanonisError;
152
153    fn try_from(value: u16) -> Result<Self, Self::Error> {
154        match value {
155            0 => Ok(OsciTriggerMode::Immediate),
156            1 => Ok(OsciTriggerMode::Level),
157            2 => Ok(OsciTriggerMode::Auto),
158            _ => Err(NanonisError::Protocol(format!(
159                "Invalid oscilloscope trigger mode: {}",
160                value
161            ))),
162        }
163    }
164}
165
166#[derive(Debug, Clone, Copy, PartialEq, Eq)]
167pub enum OversamplingIndex {
168    Samples50 = 0,
169    Samples20 = 1,
170    Samples10 = 2,
171    Samples5 = 3,
172    Samples2 = 4,
173    Samples1 = 5,
174}
175
176impl From<OversamplingIndex> for u16 {
177    fn from(index: OversamplingIndex) -> Self {
178        index as u16
179    }
180}
181
182impl TryFrom<u16> for OversamplingIndex {
183    type Error = NanonisError;
184
185    fn try_from(value: u16) -> Result<Self, Self::Error> {
186        match value {
187            0 => Ok(OversamplingIndex::Samples50),
188            1 => Ok(OversamplingIndex::Samples20),
189            2 => Ok(OversamplingIndex::Samples10),
190            3 => Ok(OversamplingIndex::Samples5),
191            4 => Ok(OversamplingIndex::Samples2),
192            5 => Ok(OversamplingIndex::Samples1),
193            _ => Err(NanonisError::Protocol(format!(
194                "Invalid oversampling index: {}",
195                value
196            ))),
197        }
198    }
199}
200
201#[derive(Debug, Clone, Copy, PartialEq, Eq)]
202pub struct TimebaseIndex(pub i32);
203
204impl From<TimebaseIndex> for i32 {
205    fn from(index: TimebaseIndex) -> Self {
206        index.0
207    }
208}
209
210impl From<TimebaseIndex> for u16 {
211    fn from(index: TimebaseIndex) -> Self {
212        index.0 as u16
213    }
214}
215
216impl From<i32> for TimebaseIndex {
217    fn from(value: i32) -> Self {
218        TimebaseIndex(value)
219    }
220}
221
222impl From<u16> for TimebaseIndex {
223    fn from(value: u16) -> Self {
224        TimebaseIndex(value as i32)
225    }
226}
227
228#[derive(Debug, Clone, Copy, PartialEq, Eq)]
229pub enum DataToGet {
230    Current,
231    NextTrigger,
232    Wait2Triggers,
233}
234
235#[derive(Debug, Clone, Copy)]
236pub struct TriggerConfig {
237    pub mode: OsciTriggerMode,
238    pub slope: TriggerSlope,
239    pub level: f64,
240    pub hysteresis: f64,
241}
242
243impl TriggerConfig {
244    pub fn new(mode: OsciTriggerMode, slope: TriggerSlope, level: f64, hysteresis: f64) -> Self {
245        Self {
246            mode,
247            slope,
248            level,
249            hysteresis,
250        }
251    }
252
253    pub fn immediate() -> Self {
254        Self {
255            mode: OsciTriggerMode::Immediate,
256            slope: TriggerSlope::Rising,
257            level: 0.0,
258            hysteresis: 0.0,
259        }
260    }
261
262    pub fn level_trigger(level: f64, slope: TriggerSlope) -> Self {
263        Self {
264            mode: OsciTriggerMode::Level,
265            slope,
266            level,
267            hysteresis: 0.1,
268        }
269    }
270
271    pub fn auto_trigger() -> Self {
272        Self {
273            mode: OsciTriggerMode::Auto,
274            slope: TriggerSlope::Rising,
275            level: 0.0,
276            hysteresis: 0.1,
277        }
278    }
279}
280
281#[derive(Debug, Clone)]
282pub struct SignalStats {
283    pub mean: f64,
284    pub std_dev: f64,
285    pub relative_std: f64,
286    pub window_size: usize,
287    pub stability_method: String,
288}
289
290#[derive(Debug, Clone)]
291pub struct OsciData {
292    pub t0: f64,
293    pub dt: f64,
294    pub size: i32,
295    pub data: Vec<f64>,
296    pub signal_stats: Option<SignalStats>,
297    pub is_stable: bool,
298    pub fallback_value: Option<f64>,
299}
300
301impl OsciData {
302    pub fn new(t0: f64, dt: f64, size: i32, data: Vec<f64>) -> Self {
303        Self {
304            t0,
305            dt,
306            size,
307            data,
308            signal_stats: None,
309            is_stable: true,
310            fallback_value: None,
311        }
312    }
313
314    pub fn new_with_stats(t0: f64, dt: f64, size: i32, data: Vec<f64>, stats: SignalStats) -> Self {
315        Self {
316            t0,
317            dt,
318            size,
319            data,
320            signal_stats: Some(stats),
321            is_stable: true,
322            fallback_value: None,
323        }
324    }
325
326    pub fn new_stable(t0: f64, dt: f64, size: i32, data: Vec<f64>) -> Self {
327        Self {
328            t0,
329            dt,
330            size,
331            data,
332            signal_stats: None,
333            is_stable: true,
334            fallback_value: None,
335        }
336    }
337
338    pub fn new_unstable_with_fallback(
339        t0: f64,
340        dt: f64,
341        size: i32,
342        data: Vec<f64>,
343        fallback: f64,
344    ) -> Self {
345        Self {
346            t0,
347            dt,
348            size,
349            data,
350            signal_stats: None,
351            is_stable: false,
352            fallback_value: Some(fallback),
353        }
354    }
355
356    pub fn values(&self) -> &[f64] {
357        &self.data
358    }
359
360    pub fn time_series(&self) -> Vec<(f64, f64)> {
361        self.data
362            .iter()
363            .enumerate()
364            .map(|(i, &value)| (self.t0 + i as f64 * self.dt, value))
365            .collect()
366    }
367
368    pub fn stats(&self) -> Option<&SignalStats> {
369        self.signal_stats.as_ref()
370    }
371
372    pub fn is_stable(&self) -> bool {
373        self.signal_stats.is_some()
374    }
375
376    pub fn duration(&self) -> f64 {
377        (self.size - 1) as f64 * self.dt
378    }
379
380    pub fn sample_rate(&self) -> f64 {
381        if self.dt > 0.0 {
382            1.0 / self.dt
383        } else {
384            0.0
385        }
386    }
387
388    pub fn time_points(&self) -> Vec<f64> {
389        (0..self.size)
390            .map(|i| self.t0 + i as f64 * self.dt)
391            .collect()
392    }
393}