1 desktop: add LSV point density config, remove auto/manual toggle

This commit is contained in:
jess 2026-04-02 23:09:31 -07:00
parent a21b014d89
commit 090fcfa2f5
2 changed files with 91 additions and 42 deletions

View File

@ -17,6 +17,25 @@ use crate::protocol::{
use crate::storage::{self, Session, Storage};
use crate::udp::UdpEvent;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LsvDensityMode {
PtsPerMv,
PtsPerSec,
}
impl LsvDensityMode {
pub const ALL: &[Self] = &[Self::PtsPerMv, Self::PtsPerSec];
}
impl std::fmt::Display for LsvDensityMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::PtsPerMv => f.write_str("pts/mV"),
Self::PtsPerSec => f.write_str("pts/s"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Tab {
Eis,
@ -71,6 +90,8 @@ pub enum Message {
LsvStopVChanged(String),
LsvScanRateChanged(String),
LsvRtiaSelected(LpRtia),
LsvDensityModeSelected(LsvDensityMode),
LsvDensityChanged(String),
StartLsv,
/* Amperometry */
AmpVholdChanged(String),
@ -133,13 +154,22 @@ pub enum Message {
BrowseBack,
ExportSession(i64),
ImportSession,
/* LSV analysis */
LsvToggleManual,
/* Misc */
Reconnect,
UdpAddrChanged(String),
}
fn lsv_calc_points(v_start: f32, v_stop: f32, scan_rate: f32, density: f32, mode: LsvDensityMode) -> u16 {
let range = (v_stop - v_start).abs();
let raw = match mode {
LsvDensityMode::PtsPerMv => range * density,
LsvDensityMode::PtsPerSec => {
if scan_rate.abs() < 0.001 { 2.0 } else { (range / scan_rate.abs()) * density }
}
};
(raw as u16).clamp(2, 500)
}
pub struct App {
tab: Tab,
status: String,
@ -181,8 +211,9 @@ pub struct App {
lsv_stop_v: String,
lsv_scan_rate: String,
lsv_rtia: LpRtia,
lsv_density_mode: LsvDensityMode,
lsv_density: String,
lsv_peaks: Vec<crate::lsv_analysis::LsvPeak>,
lsv_manual_peaks: bool,
lsv_data: text_editor::Content,
/* Amp */
@ -426,8 +457,9 @@ impl App {
lsv_stop_v: "500".into(),
lsv_scan_rate: "50".into(),
lsv_rtia: LpRtia::R10K,
lsv_density_mode: LsvDensityMode::PtsPerMv,
lsv_density: "1".into(),
lsv_peaks: Vec::new(),
lsv_manual_peaks: false,
lsv_data: text_editor::Content::with_text(&fmt_lsv(&[])),
amp_points: Vec::new(),
@ -659,9 +691,7 @@ impl App {
if let Some(sid) = self.current_session {
self.save_lsv(sid);
}
if !self.lsv_manual_peaks {
self.lsv_peaks = crate::lsv_analysis::detect_peaks(&self.lsv_points);
}
let mut st = format!("LSV complete: {} points", self.lsv_points.len());
if let (Some(s), Some(o)) = (self.ph_slope, self.ph_offset) {
if let Some(peak) = crate::lsv_analysis::detect_qhq_peak(&self.lsv_points) {
@ -850,20 +880,22 @@ impl App {
Message::LsvStopVChanged(s) => self.lsv_stop_v = s,
Message::LsvScanRateChanged(s) => self.lsv_scan_rate = s,
Message::LsvRtiaSelected(r) => self.lsv_rtia = r,
Message::LsvDensityModeSelected(m) => {
self.lsv_density_mode = m;
self.lsv_density = match m {
LsvDensityMode::PtsPerMv => "1".into(),
LsvDensityMode::PtsPerSec => "100".into(),
};
}
Message::LsvDensityChanged(s) => self.lsv_density = s,
Message::StartLsv => {
let vs = self.lsv_start_v.parse::<f32>().unwrap_or(0.0);
let ve = self.lsv_stop_v.parse::<f32>().unwrap_or(500.0);
let sr = self.lsv_scan_rate.parse::<f32>().unwrap_or(50.0);
let density = self.lsv_density.parse::<f32>().unwrap_or(1.0);
let n = lsv_calc_points(vs, ve, sr, density, self.lsv_density_mode);
self.send_cmd(&protocol::build_sysex_get_temp());
self.send_cmd(&protocol::build_sysex_start_lsv(vs, ve, sr, self.lsv_rtia));
}
Message::LsvToggleManual => {
self.lsv_manual_peaks = !self.lsv_manual_peaks;
if self.lsv_manual_peaks {
self.lsv_peaks.clear();
} else {
self.lsv_peaks = crate::lsv_analysis::detect_peaks(&self.lsv_points);
}
self.send_cmd(&protocol::build_sysex_start_lsv(vs, ve, sr, self.lsv_rtia, n));
}
/* Amp */
Message::AmpVholdChanged(s) => self.amp_v_hold = s,
@ -1540,7 +1572,13 @@ impl App {
.align_y(iced::Alignment::End)
.into(),
Tab::Lsv => row![
Tab::Lsv => {
let vs = self.lsv_start_v.parse::<f32>().unwrap_or(0.0);
let ve = self.lsv_stop_v.parse::<f32>().unwrap_or(500.0);
let sr = self.lsv_scan_rate.parse::<f32>().unwrap_or(50.0);
let d = self.lsv_density.parse::<f32>().unwrap_or(1.0);
let n = lsv_calc_points(vs, ve, sr, d, self.lsv_density_mode);
row![
column![
text("Start mV").size(12),
text_input("0", &self.lsv_start_v).on_input(Message::LsvStartVChanged).width(80),
@ -1557,14 +1595,24 @@ impl App {
text("RTIA").size(12),
pick_list(LpRtia::ALL, Some(self.lsv_rtia), Message::LsvRtiaSelected).width(Length::Shrink),
].spacing(2),
column![
text("Density").size(12),
pick_list(LsvDensityMode::ALL, Some(self.lsv_density_mode), Message::LsvDensityModeSelected).width(Length::Shrink),
].spacing(2),
column![
text("Value").size(12),
text_input("1", &self.lsv_density).on_input(Message::LsvDensityChanged).width(60),
].spacing(2),
column![
text("Points").size(12),
text(format!("{}", n)).size(13),
].spacing(2),
button(text("Start LSV").size(13))
.style(style_action())
.padding([6, 16])
.on_press(Message::StartLsv),
button(text(if self.lsv_manual_peaks { "Manual" } else { "Auto" }).size(13))
.padding([6, 12])
.on_press(Message::LsvToggleManual),
]
}
.spacing(10)
.align_y(iced::Alignment::End)
.into(),

View File

@ -465,12 +465,13 @@ pub fn build_sysex_get_config() -> Vec<u8> {
vec![0xF0, SYSEX_MFR, CMD_GET_CONFIG, 0xF7]
}
pub fn build_sysex_start_lsv(v_start: f32, v_stop: f32, scan_rate: f32, lp_rtia: LpRtia) -> Vec<u8> {
pub fn build_sysex_start_lsv(v_start: f32, v_stop: f32, scan_rate: f32, lp_rtia: LpRtia, num_points: u16) -> Vec<u8> {
let mut sx = vec![0xF0, SYSEX_MFR, CMD_START_LSV];
sx.extend_from_slice(&encode_float(v_start));
sx.extend_from_slice(&encode_float(v_stop));
sx.extend_from_slice(&encode_float(scan_rate));
sx.push(lp_rtia.as_byte());
sx.extend_from_slice(&encode_u16(num_points));
sx.push(0xF7);
sx
}