| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- import Foundation
- import LibreTransmitter
- import Swinject
- struct Calibration: JSON, Hashable, Identifiable {
- let x: Double
- let y: Double
- var date = Date()
- static let zero = Calibration(x: 0, y: 0)
- var id = UUID()
- }
- protocol CalibrationService {
- var slope: Double { get }
- var intercept: Double { get }
- var calibrations: [Calibration] { get }
- func addCalibration(_ calibration: Calibration)
- func removeCalibration(_ calibration: Calibration)
- func removeAllCalibrations()
- func removeLast()
- func calibrate(value: Double) -> Double
- }
- final class BaseCalibrationService: CalibrationService, Injectable {
- private enum Config {
- static let minSlope = 0.8
- static let maxSlope = 1.25
- static let minIntercept = -100.0
- static let maxIntercept = 100.0
- static let maxValue = 500.0
- static let minValue = 0.0
- }
- @Injected() var storage: FileStorage!
- @Injected() var notificationCenter: NotificationCenter!
- private var lifetime = Lifetime()
- private(set) var calibrations: [Calibration] = [] {
- didSet {
- storage.save(calibrations, as: OpenAPS.FreeAPS.calibrations)
- }
- }
- init(resolver: Resolver) {
- injectServices(resolver)
- calibrations = storage.retrieve(OpenAPS.FreeAPS.calibrations, as: [Calibration].self) ?? []
- subscribe()
- }
- private func subscribe() {
- notificationCenter.publisher(for: .newSensorDetected)
- .sink { [weak self] _ in
- self?.removeAllCalibrations()
- }
- .store(in: &lifetime)
- }
- var slope: Double {
- guard calibrations.count >= 2 else {
- return 1
- }
- let xs = calibrations.map(\.x)
- let ys = calibrations.map(\.y)
- let sum1 = average(multiply(xs, ys)) - average(xs) * average(ys)
- let sum2 = average(multiply(xs, xs)) - pow(average(xs), 2)
- let slope = sum1 / sum2
- return min(max(slope, Config.minSlope), Config.maxSlope)
- }
- var intercept: Double {
- guard calibrations.count >= 1 else {
- return 0
- }
- let xs = calibrations.map(\.x)
- let ys = calibrations.map(\.y)
- let intercept = average(ys) - slope * average(xs)
- return min(max(intercept, Config.minIntercept), Config.maxIntercept)
- }
- func calibrate(value: Double) -> Double {
- linearRegression(value)
- }
- func addCalibration(_ calibration: Calibration) {
- calibrations.append(calibration)
- }
- func removeCalibration(_ calibration: Calibration) {
- calibrations.removeAll { $0 == calibration }
- }
- func removeAllCalibrations() {
- calibrations.removeAll()
- }
- func removeLast() {
- calibrations.removeLast()
- }
- private func average(_ input: [Double]) -> Double {
- input.reduce(0, +) / Double(input.count)
- }
- private func multiply(_ a: [Double], _ b: [Double]) -> [Double] {
- zip(a, b).map(*)
- }
- private func linearRegression(_ x: Double) -> Double {
- (intercept + slope * x).clamped(Config.minValue ... Config.maxValue)
- }
- }
|