Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions DoseMathTests/DoseMathTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,22 @@ class RecommendBolusTests: XCTestCase {
XCTAssertEqualWithAccuracy(0, dose.amount, accuracy: 1e-13)
}

func testStartLowEndJustAboveRange() {
let glucose = loadGlucoseValueFixture("recommended_temp_start_low_end_just_above_range")

let dose = glucose.recommendedBolus(
to: glucoseTargetRange,
at: glucose.first!.startDate,
suspendThreshold: HKQuantity(unit: .milligramsPerDeciliter(), doubleValue: 0),
sensitivity: insulinSensitivitySchedule,
model: ExponentialInsulinModel(actionDuration: 21600.0, peakActivityTime: 4500.0),
pendingInsulin: 0,
maxBolus: maxBolus
)

XCTAssertEqual(0.275, dose.amount)
}

func testHighAndRising() {
let glucose = loadGlucoseValueFixture("recommend_temp_basal_high_and_rising")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[
{"date": "2017-09-17T10:38:21", "amount": 57},
{"date": "2017-09-17T10:40:00", "amount": 57.6448},
{"date": "2017-09-17T10:45:00", "amount": 59.7488},
{"date": "2017-09-17T10:50:00", "amount": 61.8207},
{"date": "2017-09-17T10:55:00", "amount": 63.8623},
{"date": "2017-09-17T11:00:00", "amount": 65.8754},
{"date": "2017-09-17T11:05:00", "amount": 67.8615},
{"date": "2017-09-17T11:10:00", "amount": 69.8222},
{"date": "2017-09-17T11:15:00", "amount": 71.759},
{"date": "2017-09-17T11:20:00", "amount": 73.6728},
{"date": "2017-09-17T11:25:00", "amount": 75.5648},
{"date": "2017-09-17T11:30:00", "amount": 77.436},
{"date": "2017-09-17T11:35:00", "amount": 79.2873},
{"date": "2017-09-17T11:40:00", "amount": 81.1198},
{"date": "2017-09-17T11:45:00", "amount": 82.9344},
{"date": "2017-09-17T11:50:00", "amount": 84.7321},
{"date": "2017-09-17T11:55:00", "amount": 86.5139},
{"date": "2017-09-17T12:00:00", "amount": 88.281},
{"date": "2017-09-17T12:05:00", "amount": 90.0348},
{"date": "2017-09-17T12:10:00", "amount": 91.7764},
{"date": "2017-09-17T12:15:00", "amount": 93.507},
{"date": "2017-09-17T12:20:00", "amount": 95.2275},
{"date": "2017-09-17T12:25:00", "amount": 96.9392},
{"date": "2017-09-17T12:30:00", "amount": 98.6428},
{"date": "2017-09-17T12:35:00", "amount": 100.339},
{"date": "2017-09-17T12:40:00", "amount": 102.03},
{"date": "2017-09-17T12:45:00", "amount": 103.715},
{"date": "2017-09-17T12:50:00", "amount": 105.395},
{"date": "2017-09-17T12:55:00", "amount": 107.072},
{"date": "2017-09-17T13:00:00", "amount": 108.746},
{"date": "2017-09-17T13:05:00", "amount": 110.417},
{"date": "2017-09-17T13:10:00", "amount": 112.086},
{"date": "2017-09-17T13:15:00", "amount": 113.753},
{"date": "2017-09-17T13:20:00", "amount": 115.42},
{"date": "2017-09-17T13:25:00", "amount": 117.087},
{"date": "2017-09-17T13:30:00", "amount": 118.754},
{"date": "2017-09-17T13:35:00", "amount": 120.42},
{"date": "2017-09-17T13:40:00", "amount": 121.914},
{"date": "2017-09-17T13:45:00", "amount": 121.914},
{"date": "2017-09-17T13:50:00", "amount": 121.914},
{"date": "2017-09-17T16:38:21", "amount": 121.914}
]
10 changes: 7 additions & 3 deletions Loop.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
435CB6291F37B01300C320C7 /* InsulinModelSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6281F37B01300C320C7 /* InsulinModelSettings.swift */; };
436961911F19D11E00447E89 /* ChartPointsContextFillLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4369618F1F19C86400447E89 /* ChartPointsContextFillLayer.swift */; };
436A0DA51D236A2A00104B24 /* LoopError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 436A0DA41D236A2A00104B24 /* LoopError.swift */; };
436D9BF81F6F4EA100CFA75F /* recommended_temp_start_low_end_just_above_range.json in Resources */ = {isa = PBXBuildFile; fileRef = 436D9BF71F6F4EA100CFA75F /* recommended_temp_start_low_end_just_above_range.json */; };
436FACEE1D0BA636004E2427 /* InsulinDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 436FACED1D0BA636004E2427 /* InsulinDataSource.swift */; };
437272DF1F09E41200A3DA02 /* WCSession+Swift4.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437272DE1F09E41200A3DA02 /* WCSession+Swift4.swift */; };
437272E01F09E41600A3DA02 /* WCSession+Swift4.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437272DE1F09E41200A3DA02 /* WCSession+Swift4.swift */; };
Expand Down Expand Up @@ -428,6 +429,7 @@
43649A621C7A347F00523D7F /* CollectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionType.swift; sourceTree = "<group>"; };
4369618F1F19C86400447E89 /* ChartPointsContextFillLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartPointsContextFillLayer.swift; sourceTree = "<group>"; };
436A0DA41D236A2A00104B24 /* LoopError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoopError.swift; sourceTree = "<group>"; };
436D9BF71F6F4EA100CFA75F /* recommended_temp_start_low_end_just_above_range.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = recommended_temp_start_low_end_just_above_range.json; sourceTree = "<group>"; };
436FACED1D0BA636004E2427 /* InsulinDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InsulinDataSource.swift; sourceTree = "<group>"; };
437272DE1F09E41200A3DA02 /* WCSession+Swift4.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WCSession+Swift4.swift"; sourceTree = "<group>"; };
43776F8C1B8022E90074EA36 /* Loop.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Loop.app; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -858,8 +860,10 @@
43E2D8E01D20C0CB004DA55F /* Fixtures */ = {
isa = PBXGroup;
children = (
C10B28451EA9BA5E006EA1FC /* far_future_high_bg_forecast.json */,
43E2D8E11D20C0DB004DA55F /* read_selected_basal_profile.json */,
43E2D8E21D20C0DB004DA55F /* recommend_temp_basal_correct_low_at_min.json */,
C1C6591B1E1B1FDA0025CC58 /* recommend_temp_basal_dropping_then_rising.json */,
43E2D8E31D20C0DB004DA55F /* recommend_temp_basal_flat_and_high.json */,
43E2D8E41D20C0DB004DA55F /* recommend_temp_basal_high_and_falling.json */,
43E2D8E51D20C0DB004DA55F /* recommend_temp_basal_high_and_rising.json */,
Expand All @@ -869,10 +873,9 @@
43E2D8E91D20C0DB004DA55F /* recommend_temp_basal_start_high_end_low.json */,
43E2D8EA1D20C0DB004DA55F /* recommend_temp_basal_start_low_end_high.json */,
43E2D8EB1D20C0DB004DA55F /* recommend_temp_basal_start_low_end_in_range.json */,
C12F21A61DFA79CB00748193 /* recommend_temp_basal_very_low_end_in_range.json */,
C17824A21E19EAB600D9D25C /* recommend_temp_basal_start_very_low_end_high.json */,
C1C6591B1E1B1FDA0025CC58 /* recommend_temp_basal_dropping_then_rising.json */,
C10B28451EA9BA5E006EA1FC /* far_future_high_bg_forecast.json */,
C12F21A61DFA79CB00748193 /* recommend_temp_basal_very_low_end_in_range.json */,
436D9BF71F6F4EA100CFA75F /* recommended_temp_start_low_end_just_above_range.json */,
);
path = Fixtures;
sourceTree = "<group>";
Expand Down Expand Up @@ -1420,6 +1423,7 @@
C17824A31E19EAB600D9D25C /* recommend_temp_basal_start_very_low_end_high.json in Resources */,
43E2D8F41D20C0DB004DA55F /* recommend_temp_basal_start_high_end_low.json in Resources */,
43E2D8EF1D20C0DB004DA55F /* recommend_temp_basal_high_and_falling.json in Resources */,
436D9BF81F6F4EA100CFA75F /* recommended_temp_start_low_end_just_above_range.json in Resources */,
43E2D8ED1D20C0DB004DA55F /* recommend_temp_basal_correct_low_at_min.json in Resources */,
43E2D8F01D20C0DB004DA55F /* recommend_temp_basal_high_and_rising.json in Resources */,
C12F21A71DFA79CB00748193 /* recommend_temp_basal_very_low_end_in_range.json in Resources */,
Expand Down
3 changes: 2 additions & 1 deletion Loop/Managers/DoseMath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,8 @@ extension Collection where Iterator.Element == GlucoseValue {
let targetValue = targetGlucoseValue(
percentEffectDuration: time / model.effectDuration,
minValue: suspendThresholdValue,
maxValue: correctionRange.value(at: prediction.startDate).averageValue)
maxValue: correctionRange.value(at: prediction.startDate).averageValue
)

// Compute the dose required to bring this prediction to target:
// dose = (Glucose Δ) / (% effect × sensitivity)
Expand Down
15 changes: 14 additions & 1 deletion Loop/Managers/LoopDataManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,10 @@ final class LoopDataManager {
fileprivate func predictGlucose(using inputs: PredictionInputEffect) throws -> [GlucoseValue] {
dispatchPrecondition(condition: .onQueue(dataAccessQueue))

guard let model = insulinModelSettings?.model else {
throw LoopError.configurationError("Check settings")
}

guard let glucose = self.glucoseStore.latestGlucose else {
throw LoopError.missingDataError(details: "Cannot predict glucose due to missing input data", recovery: "Check your CGM data source")
}
Expand All @@ -661,7 +665,16 @@ final class LoopDataManager {
effects.append(self.retrospectiveGlucoseEffect)
}

return LoopMath.predictGlucose(glucose, momentum: momentum, effects: effects)
var prediction = LoopMath.predictGlucose(glucose, momentum: momentum, effects: effects)

// Dosing requires prediction entries at as long as the insulin model duration.
// If our prediciton is shorter than that, then extend it here.
let finalDate = glucose.startDate.addingTimeInterval(model.effectDuration)
if let last = prediction.last, last.startDate < finalDate {
prediction.append(PredictedGlucoseValue(startDate: finalDate, quantity: last.quantity))
}

return prediction
}

// MARK: - Calculation state
Expand Down