| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- //
- // WalshInsulinModel.swift
- // InsulinKit
- //
- // Created by Pete Schwamb on 7/30/17.
- // Copyright © 2017 LoopKit Authors. All rights reserved.
- //
- import Foundation
- public struct WalshInsulinModel {
- public let actionDuration: TimeInterval
- public let delay: TimeInterval
- public init(actionDuration: TimeInterval, delay: TimeInterval = 600) {
- self.actionDuration = actionDuration
- self.delay = delay
- }
- }
- extension WalshInsulinModel: InsulinModel {
- public var effectDuration: TimeInterval {
- return self.actionDuration + self.delay
- }
-
- /// Returns the percentage of total insulin effect remaining at a specified interval after delivery;
- /// also known as Insulin On Board (IOB).
- ///
- /// These are 4th-order polynomial fits of John Walsh's IOB curve plots, and they first appeared in GlucoDyn.
- ///
- /// See: https:github.com/kenstack/GlucoDyn
- ///
- /// - Parameter time: The interval after insulin delivery
- /// - Returns: The percentage of total insulin effect remaining
- public func percentEffectRemaining(at time: TimeInterval) -> Double {
- let timeAfterDelay = time - delay
- switch timeAfterDelay {
- case let t where t <= 0:
- return 1
- case let t where t >= actionDuration:
- return 0
- default:
- // We only have Walsh models for a few discrete action durations, so we scale other action durations appropriately to the nearest one.
- let nearestModeledDuration: TimeInterval
-
- switch actionDuration {
- case let x where x < TimeInterval(hours: 3):
- nearestModeledDuration = TimeInterval(hours: 3)
- case let x where x > TimeInterval(hours: 6):
- nearestModeledDuration = TimeInterval(hours: 6)
- default:
- nearestModeledDuration = TimeInterval(hours: round(actionDuration.hours))
- }
-
- let minutes = timeAfterDelay.minutes * nearestModeledDuration / actionDuration
-
- switch nearestModeledDuration {
- case TimeInterval(hours: 3):
- return -3.2030e-9 * pow(minutes, 4) + 1.354e-6 * pow(minutes, 3) - 1.759e-4 * pow(minutes, 2) + 9.255e-4 * minutes + 0.99951
- case TimeInterval(hours: 4):
- return -3.310e-10 * pow(minutes, 4) + 2.530e-7 * pow(minutes, 3) - 5.510e-5 * pow(minutes, 2) - 9.086e-4 * minutes + 0.99950
- case TimeInterval(hours: 5):
- return -2.950e-10 * pow(minutes, 4) + 2.320e-7 * pow(minutes, 3) - 5.550e-5 * pow(minutes, 2) + 4.490e-4 * minutes + 0.99300
- case TimeInterval(hours: 6):
- return -1.493e-10 * pow(minutes, 4) + 1.413e-7 * pow(minutes, 3) - 4.095e-5 * pow(minutes, 2) + 6.365e-4 * minutes + 0.99700
- default:
- assertionFailure()
- return 0
- }
- }
- }
- }
- extension WalshInsulinModel: CustomDebugStringConvertible {
- public var debugDescription: String {
- return "WalshInsulinModel(actionDuration: \(actionDuration), delay: \(delay))"
- }
- }
- extension WalshInsulinModel: Equatable {
- public static func ==(lhs: WalshInsulinModel, rhs: WalshInsulinModel) -> Bool {
- return abs(lhs.actionDuration - rhs.actionDuration) < .ulpOfOne
- }
- }
- #if swift(>=4)
- extension WalshInsulinModel: Codable {
- enum CodingKeys: String, CodingKey {
- case actionDuration
- case delay
- }
-
- public init(from decoder: Decoder) throws {
- let container = try decoder.container(keyedBy: CodingKeys.self)
- let actionDuration: Double = try container.decode(Double.self, forKey: .actionDuration)
- let delay: Double = try container.decode(TimeInterval.self, forKey: .delay)
-
- self.init(actionDuration: actionDuration, delay: delay)
- }
-
- public func encode(to encoder: Encoder) throws {
- var container = encoder.container(keyedBy: CodingKeys.self)
- try container.encode(actionDuration, forKey: .actionDuration)
- try container.encode(delay, forKey: .delay)
- }
- }
- #endif
- extension WalshInsulinModel: RawRepresentable {
- public typealias RawValue = [String: Any]
- public init?(rawValue: RawValue) {
- guard let duration = rawValue["actionDuration"] as? TimeInterval else {
- return nil
- }
- self.init(actionDuration: duration)
- }
- public var rawValue: [String : Any] {
- return ["actionDuration": self.actionDuration]
- }
- }
|