@@ -81,6 +81,8 @@ final class LoopDataManager {
8181
8282 glucoseStore = GlucoseStore ( healthStore: healthStore, cacheStore: cacheStore, cacheLength: . hours( 24 ) )
8383
84+ retrospectiveCorrection = settings. enabledRetrospectiveCorrectionAlgorithm
85+
8486 overrideHistory. delegate = self
8587 cacheStore. delegate = self
8688
@@ -218,6 +220,9 @@ final class LoopDataManager {
218220 }
219221 }
220222
223+ // Confined to dataAccessQueue
224+ private var retrospectiveCorrection : RetrospectiveCorrection
225+
221226 // MARK: - Background task management
222227
223228 private var backgroundTask : UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
@@ -647,7 +652,7 @@ extension LoopDataManager {
647652 throw LoopError . missingDataError ( . glucose)
648653 }
649654
650- let retrospectiveStart = lastGlucoseDate. addingTimeInterval ( - settings . retrospectiveCorrectionIntegrationInterval )
655+ let retrospectiveStart = lastGlucoseDate. addingTimeInterval ( - retrospectiveCorrection . retrospectionInterval )
651656
652657 let earliestEffectDate = Date ( timeIntervalSinceNow: . hours( - 24 ) )
653658 let nextEffectDate = insulinCounteractionEffects. last? . endDate ?? earliestEffectDate
@@ -827,39 +832,38 @@ extension LoopDataManager {
827832 return prediction
828833 }
829834
830- /// Generates an effect based on how large the discrepancy is between the current glucose and its predicted value.
835+ /// Generates a correction effect based on how large the discrepancy is between the current glucose and its model predicted value.
831836 ///
832- /// - Parameter effectDuration: The length of time to extend the effect
833837 /// - Throws: LoopError.missingDataError
834- private func updateRetrospectiveGlucoseEffect( effectDuration : TimeInterval = TimeInterval ( minutes : 60 ) ) throws {
838+ private func updateRetrospectiveGlucoseEffect( ) throws {
835839 dispatchPrecondition ( condition: . onQueue( dataAccessQueue) )
836840
841+ // Get carb effects, otherwise clear effect and throw error
837842 guard let carbEffects = self . carbEffect else {
838843 retrospectiveGlucoseDiscrepancies = nil
839844 retrospectiveGlucoseEffect = [ ]
840845 throw LoopError . missingDataError ( . carbEffect)
841846 }
842847
843- retrospectiveGlucoseDiscrepancies = insulinCounteractionEffects. subtracting ( carbEffects, withUniformInterval: carbStore. delta)
844-
845- // Our last change should be recent, otherwise clear the effects
846- guard let discrepancy = retrospectiveGlucoseDiscrepanciesSummed? . last,
847- Date ( ) . timeIntervalSince ( discrepancy. endDate) <= settings. recencyInterval
848- else {
849- retrospectiveGlucoseEffect = [ ]
850- return
851- }
852-
848+ // Get most recent glucose, otherwise clear effect and throw error
853849 guard let glucose = self . glucoseStore. latestGlucose else {
854850 retrospectiveGlucoseEffect = [ ]
855851 throw LoopError . missingDataError ( . glucose)
856852 }
857853
858- let unit = HKUnit . milligramsPerDeciliter
859- let discrepancyTime = max ( discrepancy. endDate. timeIntervalSince ( discrepancy. startDate) , settings. retrospectiveCorrectionGroupingInterval)
860- let velocity = HKQuantity ( unit: unit. unitDivided ( by: . second( ) ) , doubleValue: discrepancy. quantity. doubleValue ( for: unit) / discrepancyTime)
854+ // Get timeline of glucose discrepancies
855+ retrospectiveGlucoseDiscrepancies = insulinCounteractionEffects. subtracting ( carbEffects, withUniformInterval: carbStore. delta)
861856
862- retrospectiveGlucoseEffect = glucose. decayEffect ( atRate: velocity, for: effectDuration)
857+ // Calculate retrospective correction
858+ retrospectiveGlucoseEffect = retrospectiveCorrection. computeEffect (
859+ startingAt: glucose,
860+ retrospectiveGlucoseDiscrepanciesSummed: retrospectiveGlucoseDiscrepanciesSummed,
861+ recencyInterval: settings. recencyInterval,
862+ insulinSensitivitySchedule: insulinSensitivitySchedule,
863+ basalRateSchedule: basalRateSchedule,
864+ glucoseCorrectionRangeSchedule: settings. glucoseTargetRangeSchedule,
865+ retrospectiveCorrectionGroupingInterval: settings. retrospectiveCorrectionGroupingInterval
866+ )
863867 }
864868
865869 /// Runs the glucose prediction on the latest effect data.
@@ -1026,6 +1030,9 @@ protocol LoopState {
10261030 /// The difference in predicted vs actual glucose over a recent period
10271031 var retrospectiveGlucoseDiscrepancies : [ GlucoseChange ] ? { get }
10281032
1033+ /// The total corrective glucose effect from retrospective correction
1034+ var totalRetrospectiveCorrection : HKQuantity ? { get }
1035+
10291036 /// Calculates a new prediction from the current data using the specified effect inputs
10301037 ///
10311038 /// This method is intended for visualization purposes only, not dosing calculation. No validation of input data is done.
@@ -1087,6 +1094,11 @@ extension LoopDataManager {
10871094 return loopDataManager. retrospectiveGlucoseDiscrepanciesSummed
10881095 }
10891096
1097+ var totalRetrospectiveCorrection : HKQuantity ? {
1098+ dispatchPrecondition ( condition: . onQueue( loopDataManager. dataAccessQueue) )
1099+ return loopDataManager. retrospectiveCorrection. totalGlucoseCorrectionEffect
1100+ }
1101+
10901102 func predictGlucose( using inputs: PredictionInputEffect ) throws -> [ GlucoseValue ] {
10911103 return try loopDataManager. predictGlucose ( using: inputs)
10921104 }
@@ -1158,7 +1170,7 @@ extension LoopDataManager {
11581170
11591171 " retrospectiveGlucoseDiscrepancies: [ " ,
11601172 " * GlucoseEffect(start, mg/dL) " ,
1161- ( manager . retrospectiveGlucoseDiscrepancies ?? [ ] ) . reduce ( into: " " , { ( entries, entry) in
1173+ ( state . retrospectiveGlucoseDiscrepancies ?? [ ] ) . reduce ( into: " " , { ( entries, entry) in
11621174 entries. append ( " * \( entry. startDate) , \( entry. quantity. doubleValue ( for: . milligramsPerDeciliter) ) \n " )
11631175 } ) ,
11641176 " ] " ,
@@ -1182,6 +1194,8 @@ extension LoopDataManager {
11821194 " " ,
11831195 " cacheStore: \( String ( reflecting: self . glucoseStore. cacheStore) ) " ,
11841196 " " ,
1197+ String ( reflecting: self . retrospectiveCorrection) ,
1198+ " " ,
11851199 ]
11861200
11871201 self . glucoseStore. generateDiagnosticReport { ( report) in
@@ -1206,7 +1220,7 @@ extension LoopDataManager {
12061220
12071221
12081222extension Notification . Name {
1209- static let LoopDataUpdated = Notification . Name ( rawValue: " com.loudnate.Naterade.notification.LoopDataUpdated " )
1223+ static let LoopDataUpdated = Notification . Name ( rawValue: " com.loudnate.Naterade.notification.LoopDataUpdated " )
12101224
12111225 static let LoopRunning = Notification . Name ( rawValue: " com.loudnate.Naterade.notification.LoopRunning " )
12121226}
0 commit comments