1use num_enum::{IntoPrimitive, TryFromPrimitive};
2use std::fmt;
3use strum_macros::Display;
4use uom::si::electric_current::{ampere, microampere};
5use uom::si::electric_potential::{microvolt, millivolt, volt};
6use uom::si::f64::{ElectricCurrent, ElectricPotential, Frequency, Power, ThermodynamicTemperature};
7use uom::si::frequency::hertz;
8use uom::si::power::watt;
9use uom::si::thermodynamic_temperature::degree_celsius;
10use zerocopy::byteorder::little_endian::{I16, I32, U16};
11use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
12
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Display, Default, TryFromPrimitive, IntoPrimitive)]
17#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
18#[cfg_attr(feature = "python", pyo3::pyclass(eq, skip_from_py_object, name = "SampleRate"))]
19#[repr(u8)]
20pub enum SampleRate {
21 #[default]
22 #[strum(to_string = "2 SPS")]
23 Sps2 = 0,
24 #[strum(to_string = "10 SPS")]
25 Sps10 = 1,
26 #[strum(to_string = "50 SPS")]
27 Sps50 = 2,
28 #[strum(to_string = "1 kSPS")]
29 Sps1000 = 3,
30 #[strum(to_string = "10 kSPS")]
31 Sps10000 = 4,
32}
33
34impl SampleRate {
35 pub fn frequency(self) -> Frequency {
37 Frequency::new::<hertz>(match self {
38 Self::Sps2 => 2.0,
39 Self::Sps10 => 10.0,
40 Self::Sps50 => 50.0,
41 Self::Sps1000 => 1_000.0,
42 Self::Sps10000 => 10_000.0,
43 })
44 }
45}
46
47#[cfg(feature = "python")]
48#[pyo3::pymethods]
49impl SampleRate {
50 #[getter]
51 fn hz(&self) -> f64 {
52 self.frequency().get::<hertz>()
53 }
54
55 #[getter]
56 fn name(&self) -> String {
57 self.to_string()
58 }
59
60 fn __repr__(&self) -> String {
61 format!("SampleRate({})", self)
62 }
63
64 fn __str__(&self) -> String {
65 self.to_string()
66 }
67}
68
69#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
70#[repr(C)]
71pub struct AdcDataRaw {
72 pub vbus_uv: I32, pub ibus_ua: I32, pub vbus_avg_uv: I32, pub ibus_avg_ua: I32, pub vbus_ori_avg_raw: I32, pub ibus_ori_avg_raw: I32, pub temp_raw: I16,
80 pub vcc1_tenth_mv: U16, pub vcc2_raw: U16, pub vdp_mv: U16, pub vdm_mv: U16, pub internal_vdd_raw: U16, pub rate_raw: u8, pub reserved: u8, pub vcc2_avg_raw: U16, pub vdp_avg_mv: U16, pub vdm_avg_mv: U16, }
91
92#[derive(Debug, Clone, Copy, PartialEq)]
93#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
94#[cfg_attr(feature = "python", pyo3::pyclass(skip_from_py_object, name = "AdcData"))]
95pub struct AdcDataSimple {
96 pub vbus: ElectricPotential,
98 pub ibus: ElectricCurrent,
102 pub power: Power,
106
107 pub vbus_average: ElectricPotential,
109 pub ibus_average: ElectricCurrent,
110 pub vbus_uncalibrated_average_raw: i32,
112 pub ibus_uncalibrated_average_raw: i32,
114
115 pub temperature: ThermodynamicTemperature,
117
118 pub vdp: ElectricPotential,
120 pub vdm: ElectricPotential,
121 pub vdp_average: ElectricPotential,
122 pub vdm_average: ElectricPotential,
123
124 pub cc1: ElectricPotential,
126 pub cc2: ElectricPotential,
127 pub cc2_average: ElectricPotential,
128
129 pub internal_vdd: ElectricPotential,
131
132 pub sample_rate: Option<SampleRate>,
134 pub sample_rate_raw: u8,
136 pub vendor_flags: u8,
138}
139
140impl From<AdcDataRaw> for AdcDataSimple {
141 fn from(raw: AdcDataRaw) -> Self {
142 let vbus = ElectricPotential::new::<microvolt>(f64::from(raw.vbus_uv.get()));
143 let ibus = ElectricCurrent::new::<microampere>(f64::from(raw.ibus_ua.get()));
144
145 let sample_rate = SampleRate::try_from(raw.rate_raw).ok();
146
147 AdcDataSimple {
148 vbus,
149 ibus,
150 power: vbus * ibus,
151 vbus_average: ElectricPotential::new::<microvolt>(f64::from(raw.vbus_avg_uv.get())),
152 ibus_average: ElectricCurrent::new::<microampere>(f64::from(raw.ibus_avg_ua.get())),
153 vbus_uncalibrated_average_raw: raw.vbus_ori_avg_raw.get(),
154 ibus_uncalibrated_average_raw: raw.ibus_ori_avg_raw.get(),
155 temperature: ThermodynamicTemperature::new::<degree_celsius>(f64::from(raw.temp_raw.get()) / 128.0),
156 vdp: ElectricPotential::new::<millivolt>(f64::from(raw.vdp_mv.get()) / 10.0),
157 vdm: ElectricPotential::new::<millivolt>(f64::from(raw.vdm_mv.get()) / 10.0),
158 vdp_average: ElectricPotential::new::<millivolt>(f64::from(raw.vdp_avg_mv.get())),
159 vdm_average: ElectricPotential::new::<millivolt>(f64::from(raw.vdm_avg_mv.get())),
160 cc1: ElectricPotential::new::<millivolt>(f64::from(raw.vcc1_tenth_mv.get()) / 10.0),
161 cc2: ElectricPotential::new::<millivolt>(f64::from(raw.vcc2_raw.get()) / 10.0),
162 cc2_average: ElectricPotential::new::<millivolt>(f64::from(raw.vcc2_avg_raw.get())),
163 internal_vdd: ElectricPotential::new::<millivolt>(f64::from(raw.internal_vdd_raw.get()) / 10.0),
164 sample_rate,
165 sample_rate_raw: raw.rate_raw,
166 vendor_flags: raw.reserved,
167 }
168 }
169}
170
171impl From<AdcDataSimple> for AdcDataRaw {
172 fn from(data: AdcDataSimple) -> Self {
173 AdcDataRaw {
174 vbus_uv: I32::new(data.vbus.get::<microvolt>().round() as i32),
175 ibus_ua: I32::new(data.ibus.get::<microampere>().round() as i32),
176 vbus_avg_uv: I32::new(data.vbus_average.get::<microvolt>().round() as i32),
177 ibus_avg_ua: I32::new(data.ibus_average.get::<microampere>().round() as i32),
178 vbus_ori_avg_raw: I32::new(data.vbus_uncalibrated_average_raw),
179 ibus_ori_avg_raw: I32::new(data.ibus_uncalibrated_average_raw),
180 temp_raw: I16::new((data.temperature.get::<degree_celsius>() * 128.0).round() as i16),
182 vcc1_tenth_mv: U16::new((data.cc1.get::<millivolt>() * 10.0).round() as u16),
183 vcc2_raw: U16::new((data.cc2.get::<millivolt>() * 10.0).round() as u16),
184 vdp_mv: U16::new((data.vdp.get::<millivolt>() * 10.0).round() as u16),
185 vdm_mv: U16::new((data.vdm.get::<millivolt>() * 10.0).round() as u16),
186 internal_vdd_raw: U16::new((data.internal_vdd.get::<millivolt>() * 10.0).round() as u16),
187 rate_raw: data.sample_rate.map_or(data.sample_rate_raw, u8::from),
188 reserved: data.vendor_flags,
189 vcc2_avg_raw: U16::new(data.cc2_average.get::<millivolt>().round() as u16),
191 vdp_avg_mv: U16::new(data.vdp_average.get::<millivolt>().round() as u16),
192 vdm_avg_mv: U16::new(data.vdm_average.get::<millivolt>().round() as u16),
193 }
194 }
195}
196
197impl AdcDataSimple {
198 pub fn current_abs(&self) -> ElectricCurrent {
200 self.ibus.abs()
201 }
202
203 pub fn power_abs(&self) -> Power {
205 self.power.abs()
206 }
207}
208
209impl fmt::Display for AdcDataSimple {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 write!(
212 f,
213 "VBUS: {:.3} V, IBUS: {:.3} A, Power: {:.3} W, Temp: {:.1} °C, Rate: {}",
214 self.vbus.get::<volt>(),
215 self.ibus.get::<ampere>(),
216 self.power.get::<watt>(),
217 self.temperature.get::<degree_celsius>(),
218 self.sample_rate.map_or_else(
219 || format!("Unknown ({})", self.sample_rate_raw),
220 |rate| rate.to_string()
221 )
222 )
223 }
224}
225
226#[cfg(feature = "python")]
227#[pyo3::pymethods]
228impl AdcDataSimple {
229 #[getter]
230 fn vbus_v(&self) -> f64 {
231 self.vbus.get::<volt>()
232 }
233 #[getter]
234 fn ibus_a(&self) -> f64 {
235 self.ibus.get::<ampere>()
236 }
237 #[getter]
238 fn power_w(&self) -> f64 {
239 self.power.get::<watt>()
240 }
241 #[getter]
242 fn vbus_avg_v(&self) -> f64 {
243 self.vbus_average.get::<volt>()
244 }
245 #[getter]
246 fn ibus_avg_a(&self) -> f64 {
247 self.ibus_average.get::<ampere>()
248 }
249 #[getter]
250 fn temp_c(&self) -> f64 {
251 self.temperature.get::<degree_celsius>()
252 }
253 #[getter]
254 fn vdp_v(&self) -> f64 {
255 self.vdp.get::<volt>()
256 }
257 #[getter]
258 fn vdm_v(&self) -> f64 {
259 self.vdm.get::<volt>()
260 }
261 #[getter]
262 fn vdp_avg_v(&self) -> f64 {
263 self.vdp_average.get::<volt>()
264 }
265 #[getter]
266 fn vdm_avg_v(&self) -> f64 {
267 self.vdm_average.get::<volt>()
268 }
269 #[getter]
270 fn cc1_v(&self) -> f64 {
271 self.cc1.get::<volt>()
272 }
273 #[getter]
274 fn cc2_v(&self) -> f64 {
275 self.cc2.get::<volt>()
276 }
277 #[getter]
278 fn cc2_avg_v(&self) -> f64 {
279 self.cc2_average.get::<volt>()
280 }
281 #[getter]
282 fn internal_vdd_v(&self) -> f64 {
283 self.internal_vdd.get::<volt>()
284 }
285 #[getter]
286 fn sample_rate(&self) -> Option<SampleRate> {
287 self.sample_rate
288 }
289 #[getter]
290 fn sample_rate_raw(&self) -> u8 {
291 self.sample_rate_raw
292 }
293 #[getter]
294 fn vendor_flags(&self) -> u8 {
295 self.vendor_flags
296 }
297 #[getter]
298 fn vbus_uncalibrated_average_raw(&self) -> i32 {
299 self.vbus_uncalibrated_average_raw
300 }
301 #[getter]
302 fn ibus_uncalibrated_average_raw(&self) -> i32 {
303 self.ibus_uncalibrated_average_raw
304 }
305
306 fn __repr__(&self) -> String {
307 format!(
308 "AdcData(vbus={:.3}V, ibus={:.3}A, power={:.3}W, temp={:.1}°C)",
309 self.vbus.get::<volt>(),
310 self.ibus.get::<ampere>(),
311 self.power.get::<watt>(),
312 self.temperature.get::<degree_celsius>()
313 )
314 }
315
316 fn __str__(&self) -> String {
317 self.__repr__()
318 }
319}