Skip to content

Commit c1f9f01

Browse files
committed
Expose logic port pins, combine measurements, move port detection function to lib
1 parent a062817 commit c1f9f01

File tree

5 files changed

+108
-24
lines changed

5 files changed

+108
-24
lines changed

cli/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ authors = ["Henk Oordt <hd@oordt.dev>"]
99
[dependencies]
1010
anyhow = { version = "1.0.60", features = ["backtrace"] }
1111
ctrlc = "3.2.2"
12-
serialport = "4.2.0"
1312
tracing = "0.1.36"
1413
tracing-subscriber = "0.3.15"
1514
clap = { version = "3.2.20", features = ["derive", "env"] }

cli/src/main.rs

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ use anyhow::Result;
22
use clap::Parser;
33
use ppk2::{
44
types::{DevicePower, MeasurementMode, SourceVoltage},
5-
Error, Ppk2,
5+
Ppk2, try_find_ppk2_port,
66
};
7-
use serialport::SerialPortType::UsbPort;
7+
88
use std::{
9-
collections::VecDeque,
109
sync::mpsc::RecvTimeoutError,
1110
time::{Duration, Instant},
1211
};
13-
use tracing::{debug, error, info, warn, Level};
12+
use tracing::{debug, error, info, Level};
1413
use tracing_subscriber::FmtSubscriber;
1514

1615
#[derive(Parser)]
@@ -113,15 +112,3 @@ fn main() -> Result<()> {
113112
info!("Goodbye!");
114113
r
115114
}
116-
117-
/// Try to find the serial port the PPK2 is connected to.
118-
fn try_find_ppk2_port() -> Result<String> {
119-
Ok(serialport::available_ports()?
120-
.into_iter()
121-
.find(|p| match &p.port_type {
122-
UsbPort(usb) => usb.vid == 0x1915 && usb.pid == 0xc00a,
123-
_ => false,
124-
})
125-
.ok_or(Error::Ppk2NotFound)?
126-
.port_name)
127-
}

ppk2/src/lib.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![doc = include_str!("../../README.md")]
22
#![deny(missing_docs)]
33

4-
use measurement::{Measurement, MeasurementAccumulator};
4+
use measurement::{Measurement, MeasurementAccumulator, MeasurementIterExt};
55
use serialport::{ClearBuffer::Input, FlowControl, SerialPort};
66
use std::str::Utf8Error;
77
use std::sync::mpsc::{self, Receiver, SendError, TryRecvError};
@@ -155,9 +155,8 @@ impl Ppk2 {
155155
missed += accumulator.feed_into(&buf[..n], &mut measurement_buf);
156156
let len = measurement_buf.len();
157157
if len >= SPS_MAX / sps {
158-
let sum: f32 = measurement_buf.drain(..).map(|m| m.micro_amps).sum();
159-
let avg = sum / (len - missed) as f32;
160-
meas_tx.send(Measurement { micro_amps: avg })?;
158+
let measurement = measurement_buf.drain(..).combine(missed);
159+
meas_tx.send(measurement)?;
161160
missed = 0;
162161
}
163162
}
@@ -198,3 +197,17 @@ impl Ppk2 {
198197
Ok(())
199198
}
200199
}
200+
201+
/// Try to find the serial port the PPK2 is connected to.
202+
pub fn try_find_ppk2_port() -> Result<String> {
203+
use serialport::SerialPortType::UsbPort;
204+
205+
Ok(serialport::available_ports()?
206+
.into_iter()
207+
.find(|p| match &p.port_type {
208+
UsbPort(usb) => usb.vid == 0x1915 && usb.pid == 0xc00a,
209+
_ => false,
210+
})
211+
.ok_or(Error::Ppk2NotFound)?
212+
.port_name)
213+
}

ppk2/src/measurement.rs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use std::collections::VecDeque;
44

5-
use crate::types::Metadata;
5+
use crate::types::{Metadata, LogicPortPins};
66

77
const ADC_MULTIPLIER: f32 = 1.8 / 163840.;
88
const SPIKE_FILTER_ALPHA: f32 = 0.18;
@@ -14,6 +14,8 @@ const SPIKE_FILTER_SAMPLES: isize = 3;
1414
pub struct Measurement {
1515
/// The measured current in mA.
1616
pub micro_amps: f32,
17+
/// Logic port bits
18+
pub pins: LogicPortPins,
1719
}
1820

1921
struct AccumulatorState {
@@ -84,7 +86,7 @@ impl MeasurementAccumulator {
8486
}
8587

8688
let adc_result = get_adc(raw) * 4;
87-
let _bits = get_logic(raw);
89+
let bits = get_logic(raw).into();
8890
let micro_amps = get_adc_result(
8991
&self.metadata,
9092
&mut self.state,
@@ -95,7 +97,7 @@ impl MeasurementAccumulator {
9597
self.state.expected_counter.replace(counter);
9698
}
9799

98-
buf.push_back(Measurement { micro_amps })
100+
buf.push_back(Measurement { micro_amps, pins: bits })
99101
}
100102
self.buf.drain(..end);
101103
samples_missed
@@ -160,6 +162,43 @@ fn get_adc_result(
160162
adc
161163
}
162164

165+
/// Extension trait for VecDeque<Measurement>
166+
pub trait MeasurementIterExt {
167+
/// Combine items into a single [Measurement]. T
168+
fn combine(self, missed: usize) -> Measurement;
169+
}
170+
171+
impl<I: Iterator<Item = Measurement>> MeasurementIterExt for I {
172+
fn combine(self, missed: usize) -> Measurement {
173+
let mut pin_high_count = [0usize; 8];
174+
let mut count = 0;
175+
let mut sum = 0f32;
176+
self.for_each(|m| {
177+
count += 1;
178+
sum += m.micro_amps;
179+
m.pins
180+
.inner()
181+
.iter()
182+
.enumerate()
183+
.filter(|(_, &p)| p)
184+
.for_each(|(i, _)| pin_high_count[i] += 1);
185+
});
186+
187+
let mut pins = [false; 8];
188+
pin_high_count
189+
.into_iter()
190+
.enumerate()
191+
.filter(|(_, p)| *p > count / 2)
192+
.for_each(|(i, _)| pins[i] = true);
193+
let avg = sum / (count - missed) as f32;
194+
195+
Measurement {
196+
micro_amps: avg,
197+
pins: pins.into(),
198+
}
199+
}
200+
}
201+
163202
const fn generate_mask(bits: u32, pos: u32) -> u32 {
164203
(2u32.pow(bits as u32) - 1) << pos
165204
}

ppk2/src/types.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,52 @@ impl FromStr for DevicePower {
136136
}
137137
}
138138

139+
/// Logic port state
140+
#[derive(Debug, Clone, Copy)]
141+
pub struct LogicPortPins {
142+
pins: [bool; 8],
143+
}
144+
145+
impl LogicPortPins {
146+
/// Check whether a pin level is high
147+
pub fn pin_is_high(&self, pin: usize) -> bool {
148+
assert!(pin < 8);
149+
self.pins[pin]
150+
}
151+
152+
/// Check whether a pin level is low
153+
pub fn pin_is_low(&self, pin: usize) -> bool {
154+
!self.pin_is_high(pin)
155+
}
156+
157+
/// Get a reference to the internal pin array
158+
pub fn inner(&self) -> &[bool; 8] {
159+
&self.pins
160+
}
161+
}
162+
163+
impl From<[bool; 8]> for LogicPortPins {
164+
fn from(pins: [bool; 8]) -> Self {
165+
Self { pins }
166+
}
167+
}
168+
169+
impl From<u8> for LogicPortPins {
170+
fn from(inner: u8) -> Self {
171+
let mut pins = [false; 8];
172+
for (i, pin) in pins.iter_mut().enumerate() {
173+
*pin = inner & 1 << i != 0
174+
}
175+
pins.into()
176+
}
177+
}
178+
179+
impl From<u32> for LogicPortPins {
180+
fn from(v: u32) -> Self {
181+
(v as u8).into()
182+
}
183+
}
184+
139185
#[derive(Default, Debug, Clone, PartialEq)]
140186
/// parsed device metadata
141187
pub struct Metadata {

0 commit comments

Comments
 (0)