2 iOS: add LSV point density config, remove auto/manual toggle
This commit is contained in:
parent
090fcfa2f5
commit
8de67ca66e
|
|
@ -1,6 +1,12 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import Observation
|
import Observation
|
||||||
|
|
||||||
|
enum LsvDensityMode: String, CaseIterable, Identifiable {
|
||||||
|
case ptsPerMv = "pts/mV"
|
||||||
|
case ptsPerSec = "pts/s"
|
||||||
|
var id: String { rawValue }
|
||||||
|
}
|
||||||
|
|
||||||
enum Tab: String, CaseIterable, Identifiable {
|
enum Tab: String, CaseIterable, Identifiable {
|
||||||
case eis = "EIS"
|
case eis = "EIS"
|
||||||
case lsv = "LSV"
|
case lsv = "LSV"
|
||||||
|
|
@ -38,12 +44,13 @@ final class AppState {
|
||||||
// LSV
|
// LSV
|
||||||
var lsvPoints: [LsvPoint] = []
|
var lsvPoints: [LsvPoint] = []
|
||||||
var lsvPeaks: [LsvPeak] = []
|
var lsvPeaks: [LsvPeak] = []
|
||||||
var lsvManualPeaks: Bool = false
|
|
||||||
var lsvTotal: UInt16 = 0
|
var lsvTotal: UInt16 = 0
|
||||||
var lsvStartV: String = "0"
|
var lsvStartV: String = "0"
|
||||||
var lsvStopV: String = "500"
|
var lsvStopV: String = "500"
|
||||||
var lsvScanRate: String = "50"
|
var lsvScanRate: String = "50"
|
||||||
var lsvRtia: LpRtia = .r10K
|
var lsvRtia: LpRtia = .r10K
|
||||||
|
var lsvDensityMode: LsvDensityMode = .ptsPerMv
|
||||||
|
var lsvDensity: String = "1"
|
||||||
|
|
||||||
// Amperometry
|
// Amperometry
|
||||||
var ampPoints: [AmpPoint] = []
|
var ampPoints: [AmpPoint] = []
|
||||||
|
|
@ -171,9 +178,7 @@ final class AppState {
|
||||||
|
|
||||||
case .lsvEnd:
|
case .lsvEnd:
|
||||||
saveLsv()
|
saveLsv()
|
||||||
if !lsvManualPeaks {
|
|
||||||
lsvPeaks = detectLsvPeaks(lsvPoints)
|
lsvPeaks = detectLsvPeaks(lsvPoints)
|
||||||
}
|
|
||||||
var st = "LSV complete: \(lsvPoints.count) points"
|
var st = "LSV complete: \(lsvPoints.count) points"
|
||||||
if let s = phSlope, let o = phOffset, abs(s) > 1e-6 {
|
if let s = phSlope, let o = phOffset, abs(s) > 1e-6 {
|
||||||
if let peak = detectQhqPeak(lsvPoints) {
|
if let peak = detectQhqPeak(lsvPoints) {
|
||||||
|
|
@ -300,13 +305,30 @@ final class AppState {
|
||||||
send(buildSysexStartSweep())
|
send(buildSysexStartSweep())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lsvCalcPoints() -> UInt16 {
|
||||||
|
let vs = Float(lsvStartV) ?? 0
|
||||||
|
let ve = Float(lsvStopV) ?? 500
|
||||||
|
let sr = Float(lsvScanRate) ?? 50
|
||||||
|
let d = Float(lsvDensity) ?? 1
|
||||||
|
let range = abs(ve - vs)
|
||||||
|
let raw: Float
|
||||||
|
switch lsvDensityMode {
|
||||||
|
case .ptsPerMv:
|
||||||
|
raw = range * d
|
||||||
|
case .ptsPerSec:
|
||||||
|
raw = abs(sr) < 0.001 ? 2 : (range / abs(sr)) * d
|
||||||
|
}
|
||||||
|
return max(2, min(500, UInt16(raw)))
|
||||||
|
}
|
||||||
|
|
||||||
func startLSV() {
|
func startLSV() {
|
||||||
lsvPoints.removeAll()
|
lsvPoints.removeAll()
|
||||||
let vs = Float(lsvStartV) ?? 0
|
let vs = Float(lsvStartV) ?? 0
|
||||||
let ve = Float(lsvStopV) ?? 500
|
let ve = Float(lsvStopV) ?? 500
|
||||||
let sr = Float(lsvScanRate) ?? 50
|
let sr = Float(lsvScanRate) ?? 50
|
||||||
|
let n = lsvCalcPoints()
|
||||||
send(buildSysexGetTemp())
|
send(buildSysexGetTemp())
|
||||||
send(buildSysexStartLsv(vStart: vs, vStop: ve, scanRate: sr, lpRtia: lsvRtia))
|
send(buildSysexStartLsv(vStart: vs, vStop: ve, scanRate: sr, lpRtia: lsvRtia, numPoints: n))
|
||||||
}
|
}
|
||||||
|
|
||||||
func startAmp() {
|
func startAmp() {
|
||||||
|
|
|
||||||
|
|
@ -307,12 +307,13 @@ func buildSysexGetConfig() -> [UInt8] {
|
||||||
[0xF0, sysexMfr, CMD_GET_CONFIG, 0xF7]
|
[0xF0, sysexMfr, CMD_GET_CONFIG, 0xF7]
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildSysexStartLsv(vStart: Float, vStop: Float, scanRate: Float, lpRtia: LpRtia) -> [UInt8] {
|
func buildSysexStartLsv(vStart: Float, vStop: Float, scanRate: Float, lpRtia: LpRtia, numPoints: UInt16) -> [UInt8] {
|
||||||
var sx: [UInt8] = [0xF0, sysexMfr, CMD_START_LSV]
|
var sx: [UInt8] = [0xF0, sysexMfr, CMD_START_LSV]
|
||||||
sx.append(contentsOf: encodeFloat(vStart))
|
sx.append(contentsOf: encodeFloat(vStart))
|
||||||
sx.append(contentsOf: encodeFloat(vStop))
|
sx.append(contentsOf: encodeFloat(vStop))
|
||||||
sx.append(contentsOf: encodeFloat(scanRate))
|
sx.append(contentsOf: encodeFloat(scanRate))
|
||||||
sx.append(lpRtia.rawValue)
|
sx.append(lpRtia.rawValue)
|
||||||
|
sx.append(contentsOf: encodeU16(numPoints))
|
||||||
sx.append(0xF7)
|
sx.append(0xF7)
|
||||||
return sx
|
return sx
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,18 +41,17 @@ struct LSVView: View {
|
||||||
LabeledPicker("RTIA", selection: $state.lsvRtia, items: LpRtia.allCases) { $0.label }
|
LabeledPicker("RTIA", selection: $state.lsvRtia, items: LpRtia.allCases) { $0.label }
|
||||||
.frame(width: 120)
|
.frame(width: 120)
|
||||||
|
|
||||||
|
LabeledPicker("Density", selection: $state.lsvDensityMode, items: LsvDensityMode.allCases) { $0.rawValue }
|
||||||
|
.frame(width: 100)
|
||||||
|
|
||||||
|
LabeledField("Value", text: $state.lsvDensity, width: 60)
|
||||||
|
|
||||||
|
Text("\(state.lsvCalcPoints()) pts")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
|
||||||
Button("Start LSV") { state.startLSV() }
|
Button("Start LSV") { state.startLSV() }
|
||||||
.buttonStyle(ActionButtonStyle(color: .green))
|
.buttonStyle(ActionButtonStyle(color: .green))
|
||||||
|
|
||||||
Button(state.lsvManualPeaks ? "Manual" : "Auto") {
|
|
||||||
state.lsvManualPeaks.toggle()
|
|
||||||
if state.lsvManualPeaks {
|
|
||||||
state.lsvPeaks.removeAll()
|
|
||||||
} else {
|
|
||||||
state.lsvPeaks = detectLsvPeaks(state.lsvPoints)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.font(.caption)
|
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
.padding(.vertical, 8)
|
.padding(.vertical, 8)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue