From 090fcfa2f5a19c8e3d24e7f38d08470cc835a1a3 Mon Sep 17 00:00:00 2001 From: jess Date: Thu, 2 Apr 2026 23:09:31 -0700 Subject: [PATCH] 1 desktop: add LSV point density config, remove auto/manual toggle --- cue/src/app.rs | 130 ++++++++++++++++++++++++++++++-------------- cue/src/protocol.rs | 3 +- 2 files changed, 91 insertions(+), 42 deletions(-) diff --git a/cue/src/app.rs b/cue/src/app.rs index a12a7fc..760c32c 100644 --- a/cue/src/app.rs +++ b/cue/src/app.rs @@ -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, - 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); - } + 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::().unwrap_or(0.0); let ve = self.lsv_stop_v.parse::().unwrap_or(500.0); let sr = self.lsv_scan_rate.parse::().unwrap_or(50.0); + let density = self.lsv_density.parse::().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,31 +1572,47 @@ impl App { .align_y(iced::Alignment::End) .into(), - Tab::Lsv => row![ - column![ - text("Start mV").size(12), - text_input("0", &self.lsv_start_v).on_input(Message::LsvStartVChanged).width(80), - ].spacing(2), - column![ - text("Stop mV").size(12), - text_input("500", &self.lsv_stop_v).on_input(Message::LsvStopVChanged).width(80), - ].spacing(2), - column![ - text("Scan mV/s").size(12), - text_input("50", &self.lsv_scan_rate).on_input(Message::LsvScanRateChanged).width(80), - ].spacing(2), - column![ - text("RTIA").size(12), - pick_list(LpRtia::ALL, Some(self.lsv_rtia), Message::LsvRtiaSelected).width(Length::Shrink), - ].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), - ] + Tab::Lsv => { + let vs = self.lsv_start_v.parse::().unwrap_or(0.0); + let ve = self.lsv_stop_v.parse::().unwrap_or(500.0); + let sr = self.lsv_scan_rate.parse::().unwrap_or(50.0); + let d = self.lsv_density.parse::().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), + ].spacing(2), + column![ + text("Stop mV").size(12), + text_input("500", &self.lsv_stop_v).on_input(Message::LsvStopVChanged).width(80), + ].spacing(2), + column![ + text("Scan mV/s").size(12), + text_input("50", &self.lsv_scan_rate).on_input(Message::LsvScanRateChanged).width(80), + ].spacing(2), + column![ + 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), + ] + } .spacing(10) .align_y(iced::Alignment::End) .into(), diff --git a/cue/src/protocol.rs b/cue/src/protocol.rs index bfb8363..1aa589e 100644 --- a/cue/src/protocol.rs +++ b/cue/src/protocol.rs @@ -465,12 +465,13 @@ pub fn build_sysex_get_config() -> Vec { 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 { +pub fn build_sysex_start_lsv(v_start: f32, v_stop: f32, scan_rate: f32, lp_rtia: LpRtia, num_points: u16) -> Vec { 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 }