add cl_factor UI and state to iOS cue

This commit is contained in:
jess 2026-04-02 18:30:31 -07:00
parent e8ce7eb98c
commit 0cfeb287e6
4 changed files with 56 additions and 0 deletions

View File

@ -93,6 +93,8 @@ final class AppState {
var calTempC: String = "40" var calTempC: String = "40"
var calCellConstant: Double? = nil var calCellConstant: Double? = nil
var calRs: Double? = nil var calRs: Double? = nil
var clFactor: Double? = nil
var clCalKnownPpm: String = "5"
// Clean // Clean
var cleanV: String = "1200" var cleanV: String = "1200"
@ -251,6 +253,10 @@ final class AppState {
case .cellK(let k): case .cellK(let k):
calCellConstant = Double(k) calCellConstant = Double(k)
status = String(format: "Device cell constant: %.4f cm\u{207B}\u{00B9}", k) status = String(format: "Device cell constant: %.4f cm\u{207B}\u{00B9}", k)
case .clFactor(let f):
clFactor = Double(f)
status = String(format: "Device Cl factor: %.6f", f)
} }
} }

View File

@ -95,6 +95,7 @@ final class UDPManager: @unchecked Sendable {
send(buildSysexGetTemp()) send(buildSysexGetTemp())
send(buildSysexGetConfig()) send(buildSysexGetConfig())
send(buildSysexGetCellK()) send(buildSysexGetCellK())
send(buildSysexGetClFactor())
startTimers() startTimers()
receiveLoop() receiveLoop()

View File

@ -14,6 +14,7 @@ struct CalibrateView: View {
inputSection inputSection
resultsSection resultsSection
cellConstantSection cellConstantSection
chlorineCalSection
} }
.navigationTitle("Calibrate") .navigationTitle("Calibrate")
} }
@ -115,6 +116,48 @@ struct CalibrateView: View {
} }
} }
// MARK: - Chlorine calibration
private var chlorineCalSection: some View {
Section("Chlorine Calibration") {
if let f = state.clFactor {
Text(String(format: "Cl factor: %.6f ppm/\u{00B5}A", f))
}
if let r = state.clResult {
Text(String(format: "Last free Cl peak: %.3f \u{00B5}A", r.iFreeUa))
}
HStack {
Text("Known Cl ppm")
Spacer()
TextField("ppm", text: $state.clCalKnownPpm)
.multilineTextAlignment(.trailing)
.frame(width: 80)
#if os(iOS)
.keyboardType(.decimalPad)
#endif
}
Button("Set Cl Factor") {
guard let r = state.clResult else {
state.status = "No chlorine measurement"
return
}
let knownPpm = Double(state.clCalKnownPpm) ?? 0
let peak = abs(Double(r.iFreeUa))
guard peak > 0 else {
state.status = "Peak current is zero"
return
}
let factor = knownPpm / peak
state.clFactor = factor
state.send(buildSysexSetClFactor(Float(factor)))
state.status = String(format: "Cl factor: %.6f ppm/\u{00B5}A", factor)
}
.disabled(state.clResult == nil)
}
}
// MARK: - Calculations // MARK: - Calculations
private func saltGrams(volumeGal: Double, ppm: Double) -> Double { private func saltGrams(volumeGal: Double, ppm: Double) -> Double {

View File

@ -68,6 +68,12 @@ struct ChlorineView: View {
Text(String(format: "Combined: %.3f uA", r.iTotalUa - r.iFreeUa)) Text(String(format: "Combined: %.3f uA", r.iTotalUa - r.iFreeUa))
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
if let f = state.clFactor {
let ppm = f * Double(abs(r.iFreeUa))
Text(String(format: "Free Cl: %.2f ppm", ppm))
.foregroundStyle(.cyan)
}
if let (_, refR) = state.clRef { if let (_, refR) = state.clRef {
Divider().frame(height: 16) Divider().frame(height: 16)
Text(String(format: "vs Ref: dFree=%.3f dTotal=%.3f", Text(String(format: "vs Ref: dFree=%.3f dTotal=%.3f",