Skip to main content

pico_common/
range.rs

1use enum_iterator::IntoEnumIterator;
2use num_derive::*;
3use std::fmt;
4
5/// Pico channel ranges
6#[allow(non_camel_case_types)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8#[derive(
9    Debug, Clone, Copy, FromPrimitive, ToPrimitive, PartialEq, Eq, PartialOrd, Ord, IntoEnumIterator,
10)]
11pub enum PicoRange {
12    X1_PROBE_5MV = -1,
13    X1_PROBE_10MV = 0,
14    X1_PROBE_20MV = 1,
15    X1_PROBE_50MV = 2,
16    X1_PROBE_100MV = 3,
17    X1_PROBE_200MV = 4,
18    X1_PROBE_500MV = 5,
19    X1_PROBE_1V = 6,
20    X1_PROBE_2V = 7,
21    X1_PROBE_5V = 8,
22    X1_PROBE_10V = 9,
23    X1_PROBE_20V = 10,
24    X1_PROBE_50V = 11,
25    X1_PROBE_100V = 12,
26    X1_PROBE_200V = 13,
27
28    X10_PROBE_50MV = 31,
29    X10_PROBE_100MV = 32,
30    X10_PROBE_200MV = 33,
31    X10_PROBE_500MV = 34,
32    X10_PROBE_1V = 35,
33    X10_PROBE_2V = 36,
34    X10_PROBE_5V = 37,
35    X10_PROBE_10V = 38,
36    X10_PROBE_20V = 39,
37    X10_PROBE_50V = 40,
38    X10_PROBE_100V = 41,
39    X10_PROBE_200V = 42,
40    X10_PROBE_500V = 43,
41
42    PS4000A_RESISTANCE_315K = 512,
43    PS4000A_RESISTANCE_1100K = 513,
44    PS4000A_RESISTANCE_10M = 514,
45    PS4000A_RESISTANCE_ADCV_FLAG = 268_435_456,
46
47    CONNECT_PROBE_OFF = 1024,
48    CURRENT_CLAMP_200A_2kA_1A = 4000,
49    CURRENT_CLAMP_200A_2kA_2A = 4001,
50    CURRENT_CLAMP_200A_2kA_5A = 4002,
51    CURRENT_CLAMP_200A_2kA_10A = 4003,
52    CURRENT_CLAMP_200A_2kA_20A = 4004,
53    CURRENT_CLAMP_200A_2kA_50A = 4005,
54    CURRENT_CLAMP_200A_2kA_100A = 4006,
55    CURRENT_CLAMP_200A_2kA_200A = 4007,
56    CURRENT_CLAMP_200A_2kA_500A = 4008,
57    CURRENT_CLAMP_200A_2kA_1000A = 4009,
58    CURRENT_CLAMP_200A_2kA_2000A = 4010,
59    CURRENT_CLAMP_40A_100mA = 5000,
60    CURRENT_CLAMP_40A_200mA = 5001,
61    CURRENT_CLAMP_40A_500mA = 5002,
62    CURRENT_CLAMP_40A_1A = 5003,
63    CURRENT_CLAMP_40A_2A = 5004,
64    CURRENT_CLAMP_40A_5A = 5005,
65    CURRENT_CLAMP_40A_10A = 5006,
66    CURRENT_CLAMP_40A_20A = 5007,
67    CURRENT_CLAMP_40A_40A = 5008,
68
69    _1KV_2_5V = 6003,
70    _1KV_5V = 6004,
71    _1KV_12_5V = 6005,
72    _1KV_25V = 6006,
73    _1KV_50V = 6007,
74    _1KV_125V = 6008,
75    _1KV_250V = 6009,
76    _1KV_500V = 6010,
77    _1KV_1000V = 6011,
78
79    CURRENT_CLAMP_2000ARMS_10A = 6500,
80    CURRENT_CLAMP_2000ARMS_20A = 6501,
81    CURRENT_CLAMP_2000ARMS_50A = 6502,
82    CURRENT_CLAMP_2000ARMS_100A = 6503,
83    CURRENT_CLAMP_2000ARMS_200A = 6504,
84    CURRENT_CLAMP_2000ARMS_500A = 6505,
85    CURRENT_CLAMP_2000ARMS_1000A = 6506,
86    CURRENT_CLAMP_2000ARMS_2000A = 6507,
87    CURRENT_CLAMP_2000ARMS_5000A = 6508,
88
89    RESISTANCE_LEAD_NEG5_TO_20OHM = 7000,
90    RESISTANCE_LEAD_NEG50_TO_200OHM = 7001,
91    RESISTANCE_LEAD_NEG500_TO_2KOHM = 7002,
92    RESISTANCE_LEAD_NEG5K_TO_20KOHM = 7003,
93    RESISTANCE_LEAD_NEG50K_TO_LEAD_200KOHM = 7004,
94    RESISTANCE_LEAD_NEG500K_TO_LEAD_2MOHM = 7005,
95    RESISTANCE_LEAD_DIODE_TEST = 7006,
96
97    HT_NEG3_TO_5KV = 8950,
98    HT_NEG3_TO_10KV = 8951,
99    HT_NEG5_TO_20KV = 8952,
100    HT_NEG5_TO_50KV = 8953,
101    HT_NEG5_TO_100KV = 8954,
102    HT_NEG3_TO_5KV_INVERTED = 8955,
103    HT_NEG3_TO_10KV_INVERTED = 8956,
104    HT_NEG5_TO_20KV_INVERTED = 8957,
105    HT_NEG5_TO_50KV_INVERTED = 8958,
106    HT_NEG5_TO_100KV_INVERTED = 8959,
107
108    TEMPERATURE_NEG50_TO_150DEGC = 9000,
109
110    PRESSURE_SENSOR_NEG100000_TO_150000_PASCALS = 9100,
111    PRESSURE_SENSOR_NEG100000_TO_400000_PASCALS = 9101,
112    PRESSURE_SENSOR_NEG200000_TO_800000_PASCALS = 9102,
113    PRESSURE_SENSOR_NEG400000_TO_1600000_PASCALS = 9103,
114    PRESSURE_SENSOR_NEG400000_TO_3400000_PASCALS = 9104,
115    PRESSURE_SENSOR_NEG150000_TO_1350000_PASCALS = 9105,
116
117    CURRENT_CLAMP_100A_2_5A = 10000,
118    CURRENT_CLAMP_100A_5A = 10001,
119    CURRENT_CLAMP_100A_10A = 10002,
120    CURRENT_CLAMP_100A_25A = 10003,
121    CURRENT_CLAMP_100A_50A = 10004,
122    CURRENT_CLAMP_100A_100A = 10005,
123
124    CURRENT_CLAMP_60A_2A = 10500,
125    CURRENT_CLAMP_60A_5A = 10501,
126    CURRENT_CLAMP_60A_10A = 10502,
127    CURRENT_CLAMP_60A_20A = 10503,
128    CURRENT_CLAMP_60A_50A = 10504,
129    CURRENT_CLAMP_60A_60A = 10505,
130
131    OPTICAL_SENSOR_10V = 10550,
132
133    CURRENT_CLAMP_60A_V2_0_5A = 10600,
134    CURRENT_CLAMP_60A_V2_1A = 10601,
135    CURRENT_CLAMP_60A_V2_2A = 10602,
136    CURRENT_CLAMP_60A_V2_5A = 10603,
137    CURRENT_CLAMP_60A_V2_10A = 10604,
138    CURRENT_CLAMP_60A_V2_20A = 10605,
139    CURRENT_CLAMP_60A_V2_50A = 10606,
140    CURRENT_CLAMP_60A_V2_60A = 10607,
141}
142
143/// Strips whitespace and '±' so that ranges can be compared to user input
144fn normalize(input: &str) -> String {
145    input.replace([' ', '±'], "").to_uppercase()
146}
147
148/// Strips a trailing probe attenuation suffix, eg. `200MV(X10)` becomes `200MV`
149fn strip_probe_suffix(input: &str) -> &str {
150    ["(X1)", "(X10)", "(X100)"]
151        .iter()
152        .find_map(|suffix| input.strip_suffix(suffix))
153        .unwrap_or(input)
154}
155
156impl PicoRange {
157    pub fn parse(input: &str, valid_ranges: Option<&[Self]>) -> Option<Self> {
158        let input = normalize(input);
159        let all_ranges = PicoRange::into_enum_iter().collect::<Vec<Self>>();
160        let valid_ranges = valid_ranges.unwrap_or(&all_ranges);
161
162        // Prefer an exact match, including any probe attenuation suffix
163        if let Some(range) = valid_ranges
164            .iter()
165            .find(|range| normalize(&range.to_string()) == input)
166        {
167            return Some(*range);
168        }
169
170        // Otherwise, fall back to matching without the probe attenuation suffix
171        valid_ranges
172            .iter()
173            .find(|range| strip_probe_suffix(&normalize(&range.to_string())) == input)
174            .copied()
175    }
176
177    pub fn from_probe_and_nano_volts(probe: u32, nano_volts: i64) -> Option<Self> {
178        match (probe, nano_volts) {
179            (1, 5_000_000) => Some(PicoRange::X1_PROBE_5MV),
180            (1, 10_000_000) => Some(PicoRange::X1_PROBE_10MV),
181            (1, 20_000_000) => Some(PicoRange::X1_PROBE_20MV),
182            (1, 50_000_000) => Some(PicoRange::X1_PROBE_50MV),
183            (1, 100_000_000) => Some(PicoRange::X1_PROBE_100MV),
184            (1, 200_000_000) => Some(PicoRange::X1_PROBE_200MV),
185            (1, 500_000_000) => Some(PicoRange::X1_PROBE_500MV),
186            (1, 1_000_000_000) => Some(PicoRange::X1_PROBE_1V),
187            (1, 2_000_000_000) => Some(PicoRange::X1_PROBE_2V),
188            (1, 5_000_000_000) => Some(PicoRange::X1_PROBE_5V),
189            (1, 10_000_000_000) => Some(PicoRange::X1_PROBE_10V),
190            (1, 20_000_000_000) => Some(PicoRange::X1_PROBE_20V),
191            (1, 50_000_000_000) => Some(PicoRange::X1_PROBE_50V),
192            (1, 100_000_000_000) => Some(PicoRange::X1_PROBE_100V),
193            (1, 200_000_000_000) => Some(PicoRange::X1_PROBE_200V),
194            (10, 50_000_000) => Some(PicoRange::X10_PROBE_50MV),
195            (10, 100_000_000) => Some(PicoRange::X10_PROBE_100MV),
196            (10, 200_000_000) => Some(PicoRange::X10_PROBE_200MV),
197            (10, 500_000_000) => Some(PicoRange::X10_PROBE_500MV),
198            (10, 1_000_000_000) => Some(PicoRange::X10_PROBE_1V),
199            (10, 2_000_000_000) => Some(PicoRange::X10_PROBE_2V),
200            (10, 5_000_000_000) => Some(PicoRange::X10_PROBE_5V),
201            (10, 10_000_000_000) => Some(PicoRange::X10_PROBE_10V),
202            (10, 20_000_000_000) => Some(PicoRange::X10_PROBE_20V),
203            (10, 50_000_000_000) => Some(PicoRange::X10_PROBE_50V),
204            (10, 100_000_000_000) => Some(PicoRange::X10_PROBE_100V),
205            (10, 200_000_000_000) => Some(PicoRange::X10_PROBE_200V),
206            (10, 500_000_000_000) => Some(PicoRange::X10_PROBE_500V),
207            _ => None,
208        }
209    }
210
211    pub fn to_nano_volts(&self) -> i64 {
212        match self {
213            PicoRange::X1_PROBE_5MV => 5_000_000,
214            PicoRange::X1_PROBE_10MV => 10_000_000,
215            PicoRange::X1_PROBE_20MV => 20_000_000,
216            PicoRange::X1_PROBE_50MV => 50_000_000,
217            PicoRange::X1_PROBE_100MV => 100_000_000,
218            PicoRange::X1_PROBE_200MV => 200_000_000,
219            PicoRange::X1_PROBE_500MV => 500_000_000,
220            PicoRange::X1_PROBE_1V => 1_000_000_000,
221            PicoRange::X1_PROBE_2V => 2_000_000_000,
222            PicoRange::X1_PROBE_5V => 5_000_000_000,
223            PicoRange::X1_PROBE_10V => 10_000_000_000,
224            PicoRange::X1_PROBE_20V => 20_000_000_000,
225            PicoRange::X1_PROBE_50V => 50_000_000_000,
226            PicoRange::X1_PROBE_100V => 100_000_000_000,
227            PicoRange::X1_PROBE_200V => 200_000_000_000,
228            PicoRange::X10_PROBE_50MV => 50_000_000,
229            PicoRange::X10_PROBE_100MV => 100_000_000,
230            PicoRange::X10_PROBE_200MV => 200_000_000,
231            PicoRange::X10_PROBE_500MV => 500_000_000,
232            PicoRange::X10_PROBE_1V => 1_000_000_000,
233            PicoRange::X10_PROBE_2V => 2_000_000_000,
234            PicoRange::X10_PROBE_5V => 5_000_000_000,
235            PicoRange::X10_PROBE_10V => 10_000_000_000,
236            PicoRange::X10_PROBE_20V => 20_000_000_000,
237            PicoRange::X10_PROBE_50V => 50_000_000_000,
238            PicoRange::X10_PROBE_100V => 100_000_000_000,
239            PicoRange::X10_PROBE_200V => 200_000_000_000,
240            PicoRange::X10_PROBE_500V => 500_000_000_000,
241            _ => panic!("Does not support conversion from {} to nano volts", self),
242        }
243    }
244
245    pub fn to_probe_range(&self) -> u32 {
246        match self {
247            PicoRange::X1_PROBE_5MV
248            | PicoRange::X1_PROBE_10MV
249            | PicoRange::X1_PROBE_20MV
250            | PicoRange::X1_PROBE_50MV
251            | PicoRange::X1_PROBE_100MV
252            | PicoRange::X1_PROBE_200MV
253            | PicoRange::X1_PROBE_500MV
254            | PicoRange::X1_PROBE_1V
255            | PicoRange::X1_PROBE_2V
256            | PicoRange::X1_PROBE_5V
257            | PicoRange::X1_PROBE_10V
258            | PicoRange::X1_PROBE_20V
259            | PicoRange::X1_PROBE_50V
260            | PicoRange::X1_PROBE_100V
261            | PicoRange::X1_PROBE_200V => 1,
262
263            PicoRange::X10_PROBE_50MV
264            | PicoRange::X10_PROBE_100MV
265            | PicoRange::X10_PROBE_200MV
266            | PicoRange::X10_PROBE_500MV
267            | PicoRange::X10_PROBE_1V
268            | PicoRange::X10_PROBE_2V
269            | PicoRange::X10_PROBE_5V
270            | PicoRange::X10_PROBE_10V
271            | PicoRange::X10_PROBE_20V
272            | PicoRange::X10_PROBE_50V
273            | PicoRange::X10_PROBE_100V
274            | PicoRange::X10_PROBE_200V
275            | PicoRange::X10_PROBE_500V => 10,
276            _ => panic!("Does not support conversion from {} to nano volts", self),
277        }
278    }
279}
280
281impl fmt::Display for PicoRange {
282    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283        match self {
284            PicoRange::X1_PROBE_5MV => write!(f, "±5 mV"),
285            PicoRange::X1_PROBE_10MV => write!(f, "±10 mV"),
286            PicoRange::X1_PROBE_20MV => write!(f, "±20 mV"),
287            PicoRange::X1_PROBE_50MV => write!(f, "±50 mV"),
288            PicoRange::X1_PROBE_100MV => write!(f, "±100 mV"),
289            PicoRange::X1_PROBE_200MV => write!(f, "±200 mV"),
290            PicoRange::X1_PROBE_500MV => write!(f, "±500 mV"),
291            PicoRange::X1_PROBE_1V => write!(f, "±1 V"),
292            PicoRange::X1_PROBE_2V => write!(f, "±2 V"),
293            PicoRange::X1_PROBE_5V => write!(f, "±5 V"),
294            PicoRange::X1_PROBE_10V => write!(f, "±10 V"),
295            PicoRange::X1_PROBE_20V => write!(f, "±20 V"),
296            PicoRange::X1_PROBE_50V => write!(f, "±50 V"),
297            PicoRange::X1_PROBE_100V => write!(f, "±100 V"),
298            PicoRange::X1_PROBE_200V => write!(f, "±200 V"),
299            PicoRange::X10_PROBE_50MV => write!(f, "±50 mV (x10)"),
300            PicoRange::X10_PROBE_100MV => write!(f, "±100 mV (x10)"),
301            PicoRange::X10_PROBE_200MV => write!(f, "±200 mV (x10)"),
302            PicoRange::X10_PROBE_500MV => write!(f, "±500 mV (x10)"),
303            PicoRange::X10_PROBE_1V => write!(f, "±1 V (x10)"),
304            PicoRange::X10_PROBE_2V => write!(f, "±2 V (x10)"),
305            PicoRange::X10_PROBE_5V => write!(f, "±5 V (x10)"),
306            PicoRange::X10_PROBE_10V => write!(f, "±10 V (x10)"),
307            PicoRange::X10_PROBE_20V => write!(f, "±20 V (x10)"),
308            PicoRange::X10_PROBE_50V => write!(f, "±50 V (x10)"),
309            PicoRange::X10_PROBE_100V => write!(f, "±100 V (x10)"),
310            PicoRange::X10_PROBE_200V => write!(f, "±200 V (x10)"),
311            PicoRange::X10_PROBE_500V => write!(f, "±500 V (x10)"),
312            PicoRange::CURRENT_CLAMP_200A_2kA_1A => write!(f, "±1 A"),
313            PicoRange::CURRENT_CLAMP_200A_2kA_2A => write!(f, "±2 A"),
314            PicoRange::CURRENT_CLAMP_200A_2kA_5A => write!(f, "±5 A"),
315            PicoRange::CURRENT_CLAMP_200A_2kA_10A => write!(f, "±10 A"),
316            PicoRange::CURRENT_CLAMP_200A_2kA_20A => write!(f, "±20 A"),
317            PicoRange::CURRENT_CLAMP_200A_2kA_50A => write!(f, "±50 A"),
318            PicoRange::CURRENT_CLAMP_200A_2kA_100A => write!(f, "±100 A"),
319            PicoRange::CURRENT_CLAMP_200A_2kA_200A => write!(f, "±200 A"),
320            PicoRange::CURRENT_CLAMP_200A_2kA_500A => write!(f, "±500 A"),
321            PicoRange::CURRENT_CLAMP_200A_2kA_1000A => write!(f, "±1000 A"),
322            PicoRange::CURRENT_CLAMP_200A_2kA_2000A => write!(f, "±2000 A"),
323            PicoRange::CURRENT_CLAMP_40A_100mA => write!(f, "±100 mA"),
324            PicoRange::CURRENT_CLAMP_40A_200mA => write!(f, "±200 mA"),
325            PicoRange::CURRENT_CLAMP_40A_500mA => write!(f, "±500 mA"),
326            PicoRange::CURRENT_CLAMP_40A_1A => write!(f, "±1 A"),
327            PicoRange::CURRENT_CLAMP_40A_2A => write!(f, "±2 A"),
328            PicoRange::CURRENT_CLAMP_40A_5A => write!(f, "±5 A"),
329            PicoRange::CURRENT_CLAMP_40A_10A => write!(f, "±10 A"),
330            PicoRange::CURRENT_CLAMP_40A_20A => write!(f, "±20 A"),
331            PicoRange::CURRENT_CLAMP_40A_40A => write!(f, "±40 A"),
332            PicoRange::_1KV_2_5V => write!(f, "±2.5 V"),
333            PicoRange::_1KV_5V => write!(f, "±5 V"),
334            PicoRange::_1KV_12_5V => write!(f, "±12.5 V"),
335            PicoRange::_1KV_25V => write!(f, "±25 V"),
336            PicoRange::_1KV_50V => write!(f, "±50 V"),
337            PicoRange::_1KV_125V => write!(f, "±125 V"),
338            PicoRange::_1KV_250V => write!(f, "±250 V"),
339            PicoRange::_1KV_500V => write!(f, "±500 V"),
340            PicoRange::_1KV_1000V => write!(f, "±1 kV"),
341            PicoRange::CURRENT_CLAMP_2000ARMS_10A => write!(f, "±10 A"),
342            PicoRange::CURRENT_CLAMP_2000ARMS_20A => write!(f, "±20 A"),
343            PicoRange::CURRENT_CLAMP_2000ARMS_50A => write!(f, "±50 A"),
344            PicoRange::CURRENT_CLAMP_2000ARMS_100A => write!(f, "±100 A"),
345            PicoRange::CURRENT_CLAMP_2000ARMS_200A => write!(f, "±200 A"),
346            PicoRange::CURRENT_CLAMP_2000ARMS_500A => write!(f, "±500 A"),
347            PicoRange::CURRENT_CLAMP_2000ARMS_1000A => write!(f, "±1000 A"),
348            PicoRange::CURRENT_CLAMP_2000ARMS_2000A => write!(f, "±2000 A"),
349            PicoRange::CURRENT_CLAMP_2000ARMS_5000A => write!(f, "±5000 A"),
350            PicoRange::CURRENT_CLAMP_60A_2A => write!(f, "±2 A"),
351            PicoRange::CURRENT_CLAMP_60A_5A => write!(f, "±5 A"),
352            PicoRange::CURRENT_CLAMP_60A_10A => write!(f, "±10 A"),
353            PicoRange::CURRENT_CLAMP_60A_20A => write!(f, "±20 A"),
354            PicoRange::CURRENT_CLAMP_60A_50A => write!(f, "±50 A"),
355            PicoRange::CURRENT_CLAMP_60A_60A => write!(f, "±60 A"),
356            rest => write!(f, "{:?}", rest),
357        }
358    }
359}
360
361impl From<PicoRange> for u32 {
362    fn from(value: PicoRange) -> Self {
363        num_traits::ToPrimitive::to_u32(&value).expect("Non-valid range")
364    }
365}
366
367impl From<PicoRange> for i32 {
368    fn from(value: PicoRange) -> Self {
369        num_traits::ToPrimitive::to_i32(&value).expect("Non-valid range")
370    }
371}
372
373impl From<PicoRange> for i16 {
374    fn from(value: PicoRange) -> Self {
375        num_traits::ToPrimitive::to_i16(&value).expect("Non-valid range")
376    }
377}
378
379impl From<i32> for PicoRange {
380    fn from(value: i32) -> Self {
381        num_traits::FromPrimitive::from_i32(value)
382            .unwrap_or_else(|| panic!("Invalid PicoRange range '{}'", value))
383    }
384}
385
386impl From<u32> for PicoRange {
387    fn from(value: u32) -> Self {
388        num_traits::FromPrimitive::from_u32(value)
389            .unwrap_or_else(|| panic!("Invalid PicoRange range '{}'", value))
390    }
391}
392
393/// A simple struct containing the long and short unit strings
394pub struct UnitStrings {
395    pub long: String,
396    pub short: String,
397}
398
399impl UnitStrings {
400    pub fn new(long: &str, short: &str) -> Self {
401        UnitStrings {
402            long: long.to_string(),
403            short: short.to_string(),
404        }
405    }
406}
407
408impl PicoRange {
409    /// Get the expected units for this channel range
410    pub fn get_units(self) -> UnitStrings {
411        match self {
412            PicoRange::PS4000A_RESISTANCE_315K
413            | PicoRange::PS4000A_RESISTANCE_1100K
414            | PicoRange::PS4000A_RESISTANCE_10M
415            | PicoRange::RESISTANCE_LEAD_NEG5_TO_20OHM
416            | PicoRange::RESISTANCE_LEAD_NEG50_TO_200OHM
417            | PicoRange::RESISTANCE_LEAD_NEG500_TO_2KOHM
418            | PicoRange::RESISTANCE_LEAD_NEG5K_TO_20KOHM
419            | PicoRange::RESISTANCE_LEAD_NEG50K_TO_LEAD_200KOHM
420            | PicoRange::RESISTANCE_LEAD_NEG500K_TO_LEAD_2MOHM => UnitStrings::new("Ohms", "Ω"),
421            PicoRange::CURRENT_CLAMP_200A_2kA_1A
422            | PicoRange::CURRENT_CLAMP_200A_2kA_2A
423            | PicoRange::CURRENT_CLAMP_200A_2kA_5A
424            | PicoRange::CURRENT_CLAMP_200A_2kA_10A
425            | PicoRange::CURRENT_CLAMP_200A_2kA_20A
426            | PicoRange::CURRENT_CLAMP_200A_2kA_50A
427            | PicoRange::CURRENT_CLAMP_200A_2kA_100A
428            | PicoRange::CURRENT_CLAMP_200A_2kA_200A
429            | PicoRange::CURRENT_CLAMP_200A_2kA_500A
430            | PicoRange::CURRENT_CLAMP_200A_2kA_1000A
431            | PicoRange::CURRENT_CLAMP_200A_2kA_2000A
432            | PicoRange::CURRENT_CLAMP_40A_100mA
433            | PicoRange::CURRENT_CLAMP_40A_200mA
434            | PicoRange::CURRENT_CLAMP_40A_500mA
435            | PicoRange::CURRENT_CLAMP_40A_1A
436            | PicoRange::CURRENT_CLAMP_40A_2A
437            | PicoRange::CURRENT_CLAMP_40A_5A
438            | PicoRange::CURRENT_CLAMP_40A_10A
439            | PicoRange::CURRENT_CLAMP_40A_20A
440            | PicoRange::CURRENT_CLAMP_40A_40A
441            | PicoRange::CURRENT_CLAMP_2000ARMS_20A
442            | PicoRange::CURRENT_CLAMP_2000ARMS_10A
443            | PicoRange::CURRENT_CLAMP_2000ARMS_50A
444            | PicoRange::CURRENT_CLAMP_2000ARMS_100A
445            | PicoRange::CURRENT_CLAMP_2000ARMS_200A
446            | PicoRange::CURRENT_CLAMP_2000ARMS_500A
447            | PicoRange::CURRENT_CLAMP_2000ARMS_1000A
448            | PicoRange::CURRENT_CLAMP_2000ARMS_2000A
449            | PicoRange::CURRENT_CLAMP_2000ARMS_5000A
450            | PicoRange::CURRENT_CLAMP_100A_2_5A
451            | PicoRange::CURRENT_CLAMP_100A_5A
452            | PicoRange::CURRENT_CLAMP_100A_10A
453            | PicoRange::CURRENT_CLAMP_100A_25A
454            | PicoRange::CURRENT_CLAMP_100A_50A
455            | PicoRange::CURRENT_CLAMP_100A_100A
456            | PicoRange::CURRENT_CLAMP_60A_2A
457            | PicoRange::CURRENT_CLAMP_60A_5A
458            | PicoRange::CURRENT_CLAMP_60A_10A
459            | PicoRange::CURRENT_CLAMP_60A_20A
460            | PicoRange::CURRENT_CLAMP_60A_50A
461            | PicoRange::CURRENT_CLAMP_60A_60A => UnitStrings::new("Amps", "A"),
462            _ => UnitStrings::new("Volts", "V"),
463        }
464    }
465
466    /// Get the maximum scaled value for this range
467    pub fn get_max_scaled_value(self) -> f64 {
468        match self {
469            PicoRange::X1_PROBE_10MV => 0.01,
470            PicoRange::X1_PROBE_20MV => 0.02,
471            PicoRange::X1_PROBE_50MV => 0.05,
472            PicoRange::X1_PROBE_100MV => 0.1,
473            PicoRange::X1_PROBE_200MV => 0.2,
474            PicoRange::X1_PROBE_500MV => 0.5,
475            PicoRange::X1_PROBE_1V => 1.0,
476            PicoRange::X1_PROBE_2V => 2.0,
477            PicoRange::X1_PROBE_5V => 5.0,
478            PicoRange::X1_PROBE_10V => 10.0,
479            PicoRange::X1_PROBE_20V => 20.0,
480            PicoRange::X1_PROBE_50V => 50.0,
481            PicoRange::X1_PROBE_100V => 100.0,
482            PicoRange::X1_PROBE_200V => 200.0,
483            PicoRange::X10_PROBE_100MV => 0.1,
484            PicoRange::X10_PROBE_200MV => 0.2,
485            PicoRange::X10_PROBE_500MV => 0.5,
486            PicoRange::X10_PROBE_1V => 1.0,
487            PicoRange::X10_PROBE_2V => 2.0,
488            PicoRange::X10_PROBE_5V => 5.0,
489            PicoRange::X10_PROBE_10V => 10.0,
490            PicoRange::X10_PROBE_20V => 20.0,
491            PicoRange::X10_PROBE_50V => 50.0,
492            PicoRange::X10_PROBE_100V => 100.0,
493            PicoRange::X10_PROBE_200V => 200.0,
494            PicoRange::X10_PROBE_500V => 500.0,
495            PicoRange::PS4000A_RESISTANCE_315K => 315_000.0,
496            PicoRange::PS4000A_RESISTANCE_1100K => 1_100_000.0,
497            PicoRange::PS4000A_RESISTANCE_10M => 10_000_000.0,
498            PicoRange::CURRENT_CLAMP_200A_2kA_1A => 1.0,
499            PicoRange::CURRENT_CLAMP_200A_2kA_2A => 2.0,
500            PicoRange::CURRENT_CLAMP_200A_2kA_5A => 5.0,
501            PicoRange::CURRENT_CLAMP_200A_2kA_10A => 10.0,
502            PicoRange::CURRENT_CLAMP_200A_2kA_20A => 20.0,
503            PicoRange::CURRENT_CLAMP_200A_2kA_50A => 50.0,
504            PicoRange::CURRENT_CLAMP_200A_2kA_100A => 100.0,
505            PicoRange::CURRENT_CLAMP_200A_2kA_200A => 200.0,
506            PicoRange::CURRENT_CLAMP_200A_2kA_500A => 500.0,
507            PicoRange::CURRENT_CLAMP_200A_2kA_1000A => 1_000.0,
508            PicoRange::CURRENT_CLAMP_200A_2kA_2000A => 2_000.0,
509            PicoRange::CURRENT_CLAMP_40A_100mA => 0.1,
510            PicoRange::CURRENT_CLAMP_40A_200mA => 0.2,
511            PicoRange::CURRENT_CLAMP_40A_500mA => 0.5,
512            PicoRange::CURRENT_CLAMP_40A_1A => 1.0,
513            PicoRange::CURRENT_CLAMP_40A_2A => 2.0,
514            PicoRange::CURRENT_CLAMP_40A_5A => 5.0,
515            PicoRange::CURRENT_CLAMP_40A_10A => 10.0,
516            PicoRange::CURRENT_CLAMP_40A_20A => 20.0,
517            PicoRange::CURRENT_CLAMP_40A_40A => 40.0,
518            PicoRange::_1KV_2_5V => 2.5,
519            PicoRange::_1KV_5V => 5.0,
520            PicoRange::_1KV_12_5V => 12.5,
521            PicoRange::_1KV_25V => 25.0,
522            PicoRange::_1KV_50V => 50.0,
523            PicoRange::_1KV_125V => 125.0,
524            PicoRange::_1KV_250V => 250.0,
525            PicoRange::_1KV_500V => 500.0,
526            PicoRange::_1KV_1000V => 1_000.0,
527            PicoRange::CURRENT_CLAMP_2000ARMS_10A => 10.0,
528            PicoRange::CURRENT_CLAMP_2000ARMS_20A => 20.0,
529            PicoRange::CURRENT_CLAMP_2000ARMS_50A => 50.0,
530            PicoRange::CURRENT_CLAMP_2000ARMS_100A => 100.0,
531            PicoRange::CURRENT_CLAMP_2000ARMS_200A => 200.0,
532            PicoRange::CURRENT_CLAMP_2000ARMS_500A => 500.0,
533            PicoRange::CURRENT_CLAMP_2000ARMS_1000A => 1_000.0,
534            PicoRange::CURRENT_CLAMP_2000ARMS_2000A => 2_000.0,
535            PicoRange::CURRENT_CLAMP_2000ARMS_5000A => 5_000.0,
536            PicoRange::RESISTANCE_LEAD_NEG5_TO_20OHM => 20.0,
537            PicoRange::RESISTANCE_LEAD_NEG50_TO_200OHM => 200.0,
538            PicoRange::RESISTANCE_LEAD_NEG500_TO_2KOHM => 2_000.0,
539            PicoRange::RESISTANCE_LEAD_NEG5K_TO_20KOHM => 20_000.0,
540            PicoRange::RESISTANCE_LEAD_NEG50K_TO_LEAD_200KOHM => 200_000.0,
541            PicoRange::RESISTANCE_LEAD_NEG500K_TO_LEAD_2MOHM => 2_000_000.0,
542            // PicoRange::RESISTANCE_LEAD_DIODE_TEST => 7006,
543            PicoRange::HT_NEG3_TO_5KV => 5_000.0,
544            PicoRange::HT_NEG3_TO_10KV => 10_000.0,
545            PicoRange::HT_NEG5_TO_20KV => 20_000.0,
546            PicoRange::HT_NEG5_TO_50KV => 50_000.0,
547            PicoRange::HT_NEG5_TO_100KV => 100_000.0,
548            PicoRange::HT_NEG3_TO_5KV_INVERTED => -5_000.0,
549            PicoRange::HT_NEG3_TO_10KV_INVERTED => -10_000.0,
550            PicoRange::HT_NEG5_TO_20KV_INVERTED => -20_000.0,
551            PicoRange::HT_NEG5_TO_50KV_INVERTED => -50_000.0,
552            PicoRange::HT_NEG5_TO_100KV_INVERTED => -100_000.0,
553            PicoRange::TEMPERATURE_NEG50_TO_150DEGC => 150.0,
554            // PicoRange::PRESSURE_SENSOR_NEG100000_TO_150000_PASCALS => 150_000.0,
555            // PicoRange::PRESSURE_SENSOR_NEG100000_TO_400000_PASCALS => 400_000.0,
556            // PicoRange::PRESSURE_SENSOR_NEG200000_TO_800000_PASCALS => 800_000.0,
557            // PicoRange::PRESSURE_SENSOR_NEG400000_TO_1600000_PASCALS => 1_600_000.0,
558            // PicoRange::PRESSURE_SENSOR_NEG400000_TO_3400000_PASCALS => 3_400_000.0,
559            // PicoRange::PRESSURE_SENSOR_NEG150000_TO_1350000_PASCALS => ?,
560            PicoRange::CURRENT_CLAMP_100A_2_5A => 2.5,
561            PicoRange::CURRENT_CLAMP_100A_5A => 5.0,
562            PicoRange::CURRENT_CLAMP_100A_10A => 10.0,
563            PicoRange::CURRENT_CLAMP_100A_25A => 25.0,
564            PicoRange::CURRENT_CLAMP_100A_50A => 50.0,
565            PicoRange::CURRENT_CLAMP_100A_100A => 100.0,
566            PicoRange::CURRENT_CLAMP_60A_2A => 2.0,
567            PicoRange::CURRENT_CLAMP_60A_5A => 5.0,
568            PicoRange::CURRENT_CLAMP_60A_10A => 10.0,
569            PicoRange::CURRENT_CLAMP_60A_20A => 20.0,
570            PicoRange::CURRENT_CLAMP_60A_50A => 50.0,
571            PicoRange::CURRENT_CLAMP_60A_60A => 60.0,
572            oops => panic!("Scaling for range '{:?}' not defined", oops),
573        }
574    }
575}
576
577#[cfg(test)]
578mod range_tests {
579    use super::*;
580
581    const X10_RANGES: &[PicoRange] = &[
582        PicoRange::X10_PROBE_100MV,
583        PicoRange::X10_PROBE_200MV,
584        PicoRange::X10_PROBE_500MV,
585        PicoRange::X10_PROBE_1V,
586        PicoRange::X10_PROBE_2V,
587        PicoRange::X10_PROBE_5V,
588    ];
589
590    #[test]
591    fn channel_parse() {
592        assert_eq!(
593            PicoRange::parse("200mv", None),
594            Some(PicoRange::X1_PROBE_200MV)
595        );
596        assert_eq!(
597            PicoRange::parse(" 20 v ", None),
598            Some(PicoRange::X1_PROBE_20V)
599        );
600        assert_eq!(
601            PicoRange::parse("±20V", None),
602            Some(PicoRange::X1_PROBE_20V)
603        );
604        assert_eq!(PicoRange::parse("20 Gigavolts", None), None);
605    }
606
607    #[test]
608    fn channel_parse_with_probe_suffix() {
609        // The probe attenuation suffix is matched when it's supplied
610        assert_eq!(
611            PicoRange::parse("200 mv (x10)", None),
612            Some(PicoRange::X10_PROBE_200MV)
613        );
614        assert_eq!(
615            PicoRange::parse("±200mV(x10)", Some(X10_RANGES)),
616            Some(PicoRange::X10_PROBE_200MV)
617        );
618    }
619
620    #[test]
621    fn channel_parse_falls_back_to_ignoring_probe_suffix() {
622        // Without the suffix, the x1 range is an exact match so it wins
623        assert_eq!(
624            PicoRange::parse("200 mv", None),
625            Some(PicoRange::X1_PROBE_200MV)
626        );
627
628        // ...but when only x10 ranges are valid, we fall back to those
629        assert_eq!(
630            PicoRange::parse("200 mv", Some(X10_RANGES)),
631            Some(PicoRange::X10_PROBE_200MV)
632        );
633
634        // The fallback still only matches valid ranges
635        assert_eq!(PicoRange::parse("50 mv", Some(X10_RANGES)), None);
636    }
637}