diff --git a/cue/src/app.rs b/cue/src/app.rs index 4a5ffba..10c455d 100644 --- a/cue/src/app.rs +++ b/cue/src/app.rs @@ -124,6 +124,8 @@ pub enum Message { BrowseLoadAsReference(i64), BrowseDeleteMeasurement(i64), BrowseBack, + /* LSV analysis */ + LsvToggleManual, /* Misc */ Reconnect, UdpAddrChanged(String), @@ -170,6 +172,8 @@ pub struct App { lsv_stop_v: String, lsv_scan_rate: String, lsv_rtia: LpRtia, + lsv_peaks: Vec, + lsv_manual_peaks: bool, lsv_data: text_editor::Content, /* Amp */ @@ -407,6 +411,8 @@ impl App { lsv_stop_v: "500".into(), lsv_scan_rate: "50".into(), lsv_rtia: LpRtia::R10K, + lsv_peaks: Vec::new(), + lsv_manual_peaks: false, lsv_data: text_editor::Content::with_text(&fmt_lsv(&[])), amp_points: Vec::new(), @@ -630,6 +636,9 @@ 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.status = format!("LSV complete: {} points", self.lsv_points.len()); } EisMessage::AmpStart { v_hold } => { @@ -807,6 +816,14 @@ impl App { 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); + } + } /* Amp */ Message::AmpVholdChanged(s) => self.amp_v_hold = s, Message::AmpIntervalChanged(s) => self.amp_interval = s, @@ -1404,6 +1421,9 @@ impl App { .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) @@ -1515,11 +1535,27 @@ impl App { .height(Length::Fill); row![bode, nyquist].spacing(10).height(Length::Fill).into() } - Tab::Lsv => canvas(crate::plot::VoltammogramPlot { - points: &self.lsv_points, - reference: self.lsv_ref.as_deref(), - }) - .width(Length::Fill).height(Length::Fill).into(), + Tab::Lsv => { + let plot = canvas(crate::plot::VoltammogramPlot { + points: &self.lsv_points, + reference: self.lsv_ref.as_deref(), + peaks: &self.lsv_peaks, + }) + .width(Length::Fill).height(Length::Fill); + if self.lsv_peaks.is_empty() { + plot.into() + } else { + let info: Vec = self.lsv_peaks.iter().map(|p| { + use crate::lsv_analysis::PeakKind; + match p.kind { + PeakKind::FreeCl => format!("Free: {:.0}mV {:.2}uA", p.v_mv, p.i_ua), + PeakKind::TotalCl => format!("Total: {:.0}mV {:.2}uA", p.v_mv, p.i_ua), + PeakKind::Crossover => format!("X-over: {:.0}mV", p.v_mv), + } + }).collect(); + column![text(info.join(" | ")).size(14), plot].spacing(4).height(Length::Fill).into() + } + } Tab::Amp => canvas(crate::plot::AmperogramPlot { points: &self.amp_points, reference: self.amp_ref.as_deref(),