From 09036ee87195d49f4e65cc6f3c5cf53c05d066bc Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sun, 1 Nov 2026 00:52:07 -0500 Subject: [PATCH 1/3] Use LoopKit's value types and bridge to LoopAlgorithm LoopKit no longer depends on LoopAlgorithm, and LoopAlgorithm takes plain numbers in fixed units (LoopKit/LoopAlgorithm#17). Loop is now the one place that converts between the two. - AlgorithmBridge.swift: StoredGlucoseSample and StoredCarbEntry conform to the algorithm's protocols; LoopKit insulin models are wrapped for the algorithm; schedules convert to mg/dL; algorithm results convert back to LoopKit values for storage, display and the watch. - LoopKitTypeAliases.swift makes the unqualified value-type names mean LoopKit's in each target; algorithm types are written LoopAlgorithm.X. - LoopAlgorithm is linked once, in LoopCore; the widget, watch app and LoopCore's own code no longer import it. - LoopCore gets the two constants the UI used from LoopAlgorithm. - Name the new slow retrospective correction experiment in Current Setup. - Tests: an end-to-end test that mmol/L settings dose the same as mg/dL, and the dose annotation, IOB and override tests moved from LoopKit. --- Common/Extensions/GlucoseRangeSchedule.swift | 1 - Common/Extensions/HKUnit.swift | 2 +- Common/Extensions/NumberFormatter.swift | 2 +- Common/Extensions/SampleValue.swift | 1 - Common/LoopKitTypeAliases.swift | 34 + Common/Models/StatusExtensionContext.swift | 2 - Common/Models/WatchHistoricalGlucose.swift | 1 - .../GlucoseLiveActivityConfiguration.swift | 1 - .../Timeline/StatusWidgetTimelimeEntry.swift | 3 +- .../StatusWidgetTimelineProvider.swift | 5 +- Loop.xcodeproj/project.pbxproj | 114 + Loop/Extensions/BasalDeliveryState.swift | 1 - Loop/Extensions/BasalRelativeDose.swift | 1 + .../CarbStore+SimulatedCoreData.swift | 1 - Loop/Extensions/CollectionType+Loop.swift | 3 +- .../DoseStore+SimulatedCoreData.swift | 1 - ...osingDecisionStore+SimulatedCoreData.swift | 1 - .../Environment+SettingsManager.swift | 1 - .../GlucoseStore+SimulatedCoreData.swift | 1 - .../SettingsStore+SimulatedCoreData.swift | 1 - Loop/Extensions/TempBasalRecommendation.swift | 1 - Loop/Extensions/UserDefaults+Loop.swift | 1 - .../Managers/Alerts/GlucoseAlertManager.swift | 1 - Loop/Managers/AnalyticsServicesManager.swift | 1 - Loop/Managers/CGMStalenessMonitor.swift | 9 +- Loop/Managers/DeviceDataManager.swift | 4 +- Loop/Managers/DoseEnactor.swift | 1 - Loop/Managers/ExtensionDataManager.swift | 3 +- .../Live Activity/ChartAxisGenerator.swift | 2 +- .../Live Activity/LiveActivityManager.swift | 1 - .../LiveActivityManagerProxy.swift | 1 - Loop/Managers/LoopAppManager.swift | 5 +- .../LoopDataManager+CarbAbsorption.swift | 17 +- Loop/Managers/LoopDataManager.swift | 102 +- .../MealDetectionManager.swift | 42 +- Loop/Managers/RemoteDataServicesManager.swift | 1 - Loop/Managers/SettingsManager.swift | 1 - Loop/Managers/StatusChartsManager.swift | 1 - .../Store Protocols/DoseStoreProtocol.swift | 1 - .../DosingDecisionStoreProtocol.swift | 1 - .../GlucoseStoreProtocol.swift | 1 - Loop/Managers/TemporaryPresetsManager.swift | 1 - Loop/Managers/WatchDataManager.swift | 5 +- Loop/Models/AlgorithmBridge.swift | 176 ++ Loop/Models/AutomationHistoryEntry.swift | 2 +- Loop/Models/BolusDosingDecision.swift | 1 - Loop/Models/CarbEntryChange.swift | 1 - Loop/Models/CrashRecoveryManager.swift | 1 - Loop/Models/Deeplink.swift | 2 +- Loop/Models/GlucoseDisplay.swift | 1 - Loop/Models/GlucoseEffectVelocity.swift | 1 - Loop/Models/GlucoseStatistics.swift | 1 - Loop/Models/InsulinDeliveryHistory.swift | 1 - Loop/Models/InsulinDeliveryLogEvent.swift | 1 - Loop/Models/LoopConstants.swift | 1 - Loop/Models/ManualBolusRecommendation.swift | 1 - Loop/Models/NetBasal.swift | 1 - Loop/Models/SimpleBolusCalculator.swift | 3 +- Loop/Models/SimpleInsulinDose.swift | 6 +- Loop/Models/StoredDataAlgorithmInput.swift | 12 +- Loop/Models/WatchContext+LoopKit.swift | 1 - .../CarbAbsorptionViewController.swift | 10 +- .../GlucoseThresholdTableViewController.swift | 1 - .../PredictionTableViewController.swift | 10 +- .../StatusTableViewController.swift | 3 +- Loop/View Models/BolusEntryViewModel.swift | 8 +- Loop/View Models/CarbEntryViewModel.swift | 1 - .../FavoriteFoodAddEditViewModel.swift | 1 - .../FavoriteFoodInsightsViewModel.swift | 2 +- Loop/View Models/FavoriteFoodsViewModel.swift | 1 - .../ManualEntryDoseViewModel.swift | 6 +- Loop/View Models/SettingsViewModel.swift | 1 - Loop/View Models/SimpleBolusViewModel.swift | 1 - Loop/View Models/StatisticsViewModel.swift | 1 - Loop/Views/BolusEntryView.swift | 1 - Loop/Views/BolusProgressTableViewCell.swift | 1 - Loop/Views/Charts/CarbEffectChartView.swift | 1 - Loop/Views/Charts/DoseChartView.swift | 4 +- Loop/Views/Charts/GlucoseCarbChartView.swift | 1 - Loop/Views/Charts/IOBChartView.swift | 1 - .../Charts/PredictedGlucoseChartView.swift | 1 - Loop/Views/CurrentSetupView.swift | 4 +- .../FavoriteFoodInsightsChartsView.swift | 1 - .../FavoriteFoodInsightsView.swift | 1 - Loop/Views/GlucoseAlertSettingsView.swift | 1 - .../InsulinDeliveryEventDetailsView.swift | 1 - .../InsulinDeliveryLog.swift | 1 - .../InsulinDeliveryLogEventRow.swift | 1 - .../InsulinDeliveryLogViewModel.swift | 1 - .../InsulinDeliveryOverview.swift | 1 - Loop/Views/ManualEntryDoseView.swift | 1 - Loop/Views/ManualGlucoseEntryRow.swift | 1 - .../Presets/Components/PresetDetentView.swift | 1 - Loop/Views/Presets/EditPresetRangeView.swift | 1 - .../PresetPerformanceHistoryView.swift | 1 - Loop/Views/Presets/PresetsHistoryView.swift | 1 - .../PresetsPerformanceHistoryViewModel.swift | 1 - Loop/Views/Presets/PresetsView.swift | 1 - .../TherapySettingsExampleView.swift | 1 - .../Training/PresetsTrainingContent.swift | 1 - Loop/Views/SimpleBolusView.swift | 1 - Loop/Views/Statistics/AGPChartView.swift | 1 - .../Statistics/SetupComparisonCharts.swift | 1 - Loop/Views/Statistics/StatisticsView.swift | 1 - Loop/Views/Statistics/TimeInRangeBar.swift | 2 +- Loop/Views/StatusTableView.swift | 1 - LoopCore/LoopCoreConstants.swift | 6 + LoopCore/LoopSettings.swift | 1 - LoopCore/Models/WatchContext.swift | 1 - LoopCore/Models/WatchPredictedGlucose.swift | 1 - LoopCore/NSUserDefaults.swift | 1 - LoopTests/Fixtures/insulin_math/basal.json | 44 + .../iob_from_doses_exponential_output.json | 642 ++++++ .../insulin_math/iob_from_doses_output.json | 1 + .../iob_from_multiple_curves_output.json | 1832 +++++++++++++++++ .../iob_from_reservoir_output.json | 557 +++++ .../normalize_edge_case_doses_input.json | 16 + ...rmalize_edge_case_doses_mutable_input.json | 18 + ...malize_edge_case_doses_mutable_output.json | 42 + .../normalize_edge_case_doses_output.json | 37 + .../insulin_math/normalized_doses.json | 171 ++ .../normalized_reservoir_history_output.json | 434 ++++ ..._history_with_rewind_and_prime_output.json | 434 ++++ .../Fixtures/insulin_math/suspend_dose.json | 9 + .../insulin_math/suspend_dose_reconciled.json | 9 + .../suspend_dose_reconciled_normalized.json | 9 + ...uspend_dose_reconciled_normalized_iob.json | 1 + .../Managers/CGMStalenessMonitorTests.swift | 1 - .../Managers/DeviceDataManagerTests.swift | 1 - LoopTests/Managers/DoseEnactorTests.swift | 1 - .../Managers/GlucoseAlertManagerTests.swift | 1 - LoopTests/Managers/LoopDataManagerTests.swift | 62 +- .../Managers/MealDetectionManagerTests.swift | 38 +- LoopTests/Mock Stores/MockDoseStore.swift | 1 - LoopTests/Mock Stores/MockGlucoseStore.swift | 1 - LoopTests/Mocks/LoopControlMock.swift | 1 - LoopTests/Mocks/MockDeliveryDelegate.swift | 1 - LoopTests/Mocks/MockSettingsProvider.swift | 1 - .../Models/DoseEntryInsulinMathTests.swift | 510 +++++ LoopTests/Models/GlucoseStatisticsTests.swift | 1 - .../Models/InsulinDeliveryHistoryTests.swift | 6 +- LoopTests/Models/SetBolusUserInfoTests.swift | 1 - LoopTests/Models/SetupChangeTests.swift | 1 - LoopTests/Models/SetupComparisonTests.swift | 4 +- .../Models/SimpleBolusCalculatorTests.swift | 1 - .../Models/TempBasalRecommendationTests.swift | 1 - ...poraryScheduleOverrideAlgorithmTests.swift | 139 ++ .../Models/WatchHistoricalGlucoseTest.swift | 1 - .../ViewModels/BolusEntryViewModelTests.swift | 61 +- .../CGMStatusHUDViewModelTests.swift | 1 - .../ManualEntryDoseViewModelTests.swift | 3 +- .../SimpleBolusViewModelTests.swift | 1 - LoopUI/ViewModel/CGMStatusHUDViewModel.swift | 1 - LoopUI/Views/CGMStatusHUDView.swift | 1 - LoopUI/Views/GlucoseHUDView.swift | 1 - .../ComplicationController.swift | 8 +- WatchApp Extension/ExtensionDelegate.swift | 1 - .../Extensions/CLKComplicationTemplate.swift | 1 - ...EnvironmentValues+GlucoseDisplayUnit.swift | 2 +- .../Extensions/GlucoseCondition.swift | 2 +- .../Extensions/WatchContext+WatchApp.swift | 1 - .../Managers/ComplicationChartManager.swift | 1 - .../Managers/LoopDataManager.swift | 3 +- .../Models/GlucoseChartData.swift | 1 - .../Models/GlucoseChartScaler.swift | 1 - .../Scenes/GlucoseChartValueHashable.swift | 1 - .../CarbAndBolusFlowViewModel.swift | 1 - WatchApp Extension/Views/CarbList.swift | 4 +- WatchApp Extension/Views/ChartPageView.swift | 1 - .../Views/PresetWatchCard.swift | 1 - 170 files changed, 5468 insertions(+), 348 deletions(-) create mode 100644 Common/LoopKitTypeAliases.swift create mode 100644 Loop/Models/AlgorithmBridge.swift create mode 100644 LoopTests/Fixtures/insulin_math/basal.json create mode 100644 LoopTests/Fixtures/insulin_math/iob_from_doses_exponential_output.json create mode 100644 LoopTests/Fixtures/insulin_math/iob_from_doses_output.json create mode 100644 LoopTests/Fixtures/insulin_math/iob_from_multiple_curves_output.json create mode 100644 LoopTests/Fixtures/insulin_math/iob_from_reservoir_output.json create mode 100644 LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_input.json create mode 100644 LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_mutable_input.json create mode 100644 LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_mutable_output.json create mode 100644 LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_output.json create mode 100644 LoopTests/Fixtures/insulin_math/normalized_doses.json create mode 100644 LoopTests/Fixtures/insulin_math/normalized_reservoir_history_output.json create mode 100644 LoopTests/Fixtures/insulin_math/reservoir_history_with_rewind_and_prime_output.json create mode 100644 LoopTests/Fixtures/insulin_math/suspend_dose.json create mode 100644 LoopTests/Fixtures/insulin_math/suspend_dose_reconciled.json create mode 100644 LoopTests/Fixtures/insulin_math/suspend_dose_reconciled_normalized.json create mode 100644 LoopTests/Fixtures/insulin_math/suspend_dose_reconciled_normalized_iob.json create mode 100644 LoopTests/Models/DoseEntryInsulinMathTests.swift create mode 100644 LoopTests/Models/TemporaryScheduleOverrideAlgorithmTests.swift diff --git a/Common/Extensions/GlucoseRangeSchedule.swift b/Common/Extensions/GlucoseRangeSchedule.swift index a7bfef414f..4a1d04c3f3 100644 --- a/Common/Extensions/GlucoseRangeSchedule.swift +++ b/Common/Extensions/GlucoseRangeSchedule.swift @@ -6,7 +6,6 @@ // import LoopKit -import LoopAlgorithm extension GlucoseRangeSchedule { diff --git a/Common/Extensions/HKUnit.swift b/Common/Extensions/HKUnit.swift index 69cf8fb40c..004c1164a4 100644 --- a/Common/Extensions/HKUnit.swift +++ b/Common/Extensions/HKUnit.swift @@ -6,7 +6,7 @@ // Copyright © 2016 LoopKit Authors. All rights reserved. // -import LoopAlgorithm +import LoopKit import LoopCore // Code in this extension is duplicated from: diff --git a/Common/Extensions/NumberFormatter.swift b/Common/Extensions/NumberFormatter.swift index c0f8c3addb..0b7cd834b8 100644 --- a/Common/Extensions/NumberFormatter.swift +++ b/Common/Extensions/NumberFormatter.swift @@ -7,7 +7,7 @@ // import Foundation -import LoopAlgorithm +import LoopKit extension NumberFormatter { diff --git a/Common/Extensions/SampleValue.swift b/Common/Extensions/SampleValue.swift index 40d9faab4d..d8c0e87699 100644 --- a/Common/Extensions/SampleValue.swift +++ b/Common/Extensions/SampleValue.swift @@ -6,7 +6,6 @@ // import LoopKit -import LoopAlgorithm extension SampleValue { /// A glucose value entered by hand, such as a fingerstick or calibration. diff --git a/Common/LoopKitTypeAliases.swift b/Common/LoopKitTypeAliases.swift new file mode 100644 index 0000000000..3bc59914a6 --- /dev/null +++ b/Common/LoopKitTypeAliases.swift @@ -0,0 +1,34 @@ +// +// LoopKitTypeAliases.swift +// Loop +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import LoopKit + +// LoopKit and LoopAlgorithm define types with the same names. These make the unqualified names +// mean LoopKit's; algorithm types are written LoopAlgorithm.Name. +typealias AbsoluteScheduleValue = LoopKit.AbsoluteScheduleValue +typealias AutomaticDoseRecommendation = LoopKit.AutomaticDoseRecommendation +typealias BolusRecommendationNotice = LoopKit.BolusRecommendationNotice +typealias CarbEntry = LoopKit.CarbEntry +typealias CarbValue = LoopKit.CarbValue +typealias DoseUnit = LoopKit.DoseUnit +typealias ExponentialInsulinModel = LoopKit.ExponentialInsulinModel +typealias ExponentialInsulinModelPreset = LoopKit.ExponentialInsulinModelPreset +typealias GlucoseChange = LoopKit.GlucoseChange +typealias GlucoseCondition = LoopKit.GlucoseCondition +typealias GlucoseEffect = LoopKit.GlucoseEffect +typealias GlucoseEffectVelocity = LoopKit.GlucoseEffectVelocity +typealias GlucoseRangeTimeline = LoopKit.GlucoseRangeTimeline +typealias GlucoseSampleValue = LoopKit.GlucoseSampleValue +typealias GlucoseValue = LoopKit.GlucoseValue +typealias InsulinModel = LoopKit.InsulinModel +typealias InsulinValue = LoopKit.InsulinValue +typealias ManualBolusRecommendation = LoopKit.ManualBolusRecommendation +typealias PredictedGlucoseValue = LoopKit.PredictedGlucoseValue +typealias SampleValue = LoopKit.SampleValue +typealias SimpleGlucoseValue = LoopKit.SimpleGlucoseValue +typealias TempBasalRecommendation = LoopKit.TempBasalRecommendation +typealias TimelineValue = LoopKit.TimelineValue diff --git a/Common/Models/StatusExtensionContext.swift b/Common/Models/StatusExtensionContext.swift index 114f2a416f..dc216bd731 100644 --- a/Common/Models/StatusExtensionContext.swift +++ b/Common/Models/StatusExtensionContext.swift @@ -8,10 +8,8 @@ // This class allows Loop to pass context data to the Loop Status Extension. import Foundation -import LoopAlgorithm import LoopKit import LoopKitUI -import LoopAlgorithm struct NetBasalContext { diff --git a/Common/Models/WatchHistoricalGlucose.swift b/Common/Models/WatchHistoricalGlucose.swift index 71209d428d..d360ec6f26 100644 --- a/Common/Models/WatchHistoricalGlucose.swift +++ b/Common/Models/WatchHistoricalGlucose.swift @@ -9,7 +9,6 @@ import Foundation import HealthKit import LoopKit -import LoopAlgorithm struct WatchHistoricalGlucose { let samples: [StoredGlucoseSample] diff --git a/Loop Widget Extension/Live Activity/GlucoseLiveActivityConfiguration.swift b/Loop Widget Extension/Live Activity/GlucoseLiveActivityConfiguration.swift index 314d0de950..071c16d6ad 100644 --- a/Loop Widget Extension/Live Activity/GlucoseLiveActivityConfiguration.swift +++ b/Loop Widget Extension/Live Activity/GlucoseLiveActivityConfiguration.swift @@ -9,7 +9,6 @@ import ActivityKit import Charts import HealthKit -import LoopAlgorithm import LoopCore import LoopKit import SwiftUI diff --git a/Loop Widget Extension/Timeline/StatusWidgetTimelimeEntry.swift b/Loop Widget Extension/Timeline/StatusWidgetTimelimeEntry.swift index 4fe09ba12f..657eecb28d 100644 --- a/Loop Widget Extension/Timeline/StatusWidgetTimelimeEntry.swift +++ b/Loop Widget Extension/Timeline/StatusWidgetTimelimeEntry.swift @@ -9,7 +9,6 @@ import LoopCore import LoopKit import WidgetKit -import LoopAlgorithm struct StatusWidgetTimelimeEntry: TimelineEntry { @@ -56,6 +55,6 @@ struct StatusWidgetTimelimeEntry: TimelineEntry { } let glucoseAge = date - glucoseDate - return glucoseAge >= LoopAlgorithm.inputDataRecencyInterval + return glucoseAge >= LoopCoreConstants.inputDataRecencyInterval } } diff --git a/Loop Widget Extension/Timeline/StatusWidgetTimelineProvider.swift b/Loop Widget Extension/Timeline/StatusWidgetTimelineProvider.swift index bfc46aeaa0..cb0c7c5ce0 100644 --- a/Loop Widget Extension/Timeline/StatusWidgetTimelineProvider.swift +++ b/Loop Widget Extension/Timeline/StatusWidgetTimelineProvider.swift @@ -11,7 +11,6 @@ import LoopCore import LoopKit import OSLog import WidgetKit -import LoopAlgorithm class StatusWidgetTimelineProvider: TimelineProvider { lazy var defaults = UserDefaults.appGroup @@ -74,7 +73,7 @@ class StatusWidgetTimelineProvider: TimelineProvider { // Date glucose staleness changes if let lastBGTime = newEntry.currentGlucose?.startDate { - let staleBgRefreshTime = lastBGTime.addingTimeInterval(LoopAlgorithm.inputDataRecencyInterval+1) + let staleBgRefreshTime = lastBGTime.addingTimeInterval(LoopCoreConstants.inputDataRecencyInterval+1) datesToRefreshWidget.append(staleBgRefreshTime) } @@ -97,7 +96,7 @@ class StatusWidgetTimelineProvider: TimelineProvider { func update(completion: @escaping (StatusWidgetTimelimeEntry) -> Void) { - let startDate = Date(timeIntervalSinceNow: -LoopAlgorithm.inputDataRecencyInterval) + let startDate = Date(timeIntervalSinceNow: -LoopCoreConstants.inputDataRecencyInterval) Task { diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj index 03ce9afa1d..b0b7b65d80 100644 --- a/Loop.xcodeproj/project.pbxproj +++ b/Loop.xcodeproj/project.pbxproj @@ -717,6 +717,32 @@ FEEDB0000000000000000004 /* Statistics/TimeInRangeBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEDF0000000000000000004 /* Statistics/TimeInRangeBar.swift */; }; 30E4A68B8C914C5E4B245555 /* InsulinDeliveryHistoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F69CE2EE3714858772FE7A26 /* InsulinDeliveryHistoryTests.swift */; }; FEEDB0000000000000000005 /* GlucoseStatisticsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEDF0000000000000000005 /* GlucoseStatisticsTests.swift */; }; + 17EA66859884C0208DED47CF /* LoopKitTypeAliases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FAEFD820F50BFF211AC6D9 /* LoopKitTypeAliases.swift */; }; + 1C99EE4A3BB181BB23DEF066 /* LoopAlgorithm in Frameworks */ = {isa = PBXBuildFile; productRef = C42F6B8A2B1B9E0142078BF9 /* LoopAlgorithm */; }; + 82430C768078D9BEDFDECA6C /* DoseEntryInsulinMathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0A246E78F6985AB1207647 /* DoseEntryInsulinMathTests.swift */; }; + BD37CA2F10445DB600A35199 /* TemporaryScheduleOverrideAlgorithmTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C372374D3D3D9C5F8E3AD93 /* TemporaryScheduleOverrideAlgorithmTests.swift */; }; + 0D48B6D1CF1EA2B542229625 /* basal.json in Resources */ = {isa = PBXBuildFile; fileRef = 00B649A44AFB50BC4B9A70F6 /* basal.json */; }; + 0131FD2953DE7C998EE3F663 /* iob_from_doses_exponential_output.json in Resources */ = {isa = PBXBuildFile; fileRef = B3DDD98615BAC7131714F89B /* iob_from_doses_exponential_output.json */; }; + C09625393B9711F25BAD3D52 /* iob_from_doses_output.json in Resources */ = {isa = PBXBuildFile; fileRef = 5B3F3FD4EDF5B0DE5F25489A /* iob_from_doses_output.json */; }; + 59D3D2A08D733F67B0F0935E /* iob_from_multiple_curves_output.json in Resources */ = {isa = PBXBuildFile; fileRef = DD57D2F830A1249A6C5622C0 /* iob_from_multiple_curves_output.json */; }; + 3327886285617123471C28DA /* iob_from_reservoir_output.json in Resources */ = {isa = PBXBuildFile; fileRef = C8FF391DE1198B1832CE4313 /* iob_from_reservoir_output.json */; }; + 5EF5D4007DBE6486653E11EF /* normalize_edge_case_doses_input.json in Resources */ = {isa = PBXBuildFile; fileRef = 2DB5AB3635FA0041E6102578 /* normalize_edge_case_doses_input.json */; }; + 3948ACE22554E6743AF410D9 /* normalize_edge_case_doses_mutable_input.json in Resources */ = {isa = PBXBuildFile; fileRef = 2776E774AAD3E4D7CFE1FA6E /* normalize_edge_case_doses_mutable_input.json */; }; + 1C21A7E243CB4F2833F77511 /* normalize_edge_case_doses_mutable_output.json in Resources */ = {isa = PBXBuildFile; fileRef = 1FC4107E6003069B6CE7057A /* normalize_edge_case_doses_mutable_output.json */; }; + 9AC8982609B95285B3F1BB52 /* normalize_edge_case_doses_output.json in Resources */ = {isa = PBXBuildFile; fileRef = 19B187891AF73C340EE9854A /* normalize_edge_case_doses_output.json */; }; + B8668893E285EC0E23BACEFA /* normalized_doses.json in Resources */ = {isa = PBXBuildFile; fileRef = 9C2AC83FE854B422A6BCC7A8 /* normalized_doses.json */; }; + 89E3C86E262B61F3D940E805 /* normalized_reservoir_history_output.json in Resources */ = {isa = PBXBuildFile; fileRef = 69A018B00CD62B1E5349701F /* normalized_reservoir_history_output.json */; }; + 8CA0807CED5865CF795FEA1C /* reservoir_history_with_rewind_and_prime_output.json in Resources */ = {isa = PBXBuildFile; fileRef = A69826F94E3337F0DE9F46C1 /* reservoir_history_with_rewind_and_prime_output.json */; }; + 3B8D727402F48A542A6B3F86 /* suspend_dose.json in Resources */ = {isa = PBXBuildFile; fileRef = 9BE0AA50E7B42B8FBAFA8304 /* suspend_dose.json */; }; + 5530CD403A0857E55CFDD24C /* suspend_dose_reconciled.json in Resources */ = {isa = PBXBuildFile; fileRef = 1EBEB8F96DA092DAD8140367 /* suspend_dose_reconciled.json */; }; + D3311BF2CE4A9689C7FC14C4 /* suspend_dose_reconciled_normalized.json in Resources */ = {isa = PBXBuildFile; fileRef = B78CA313148D4CF2EDBBC165 /* suspend_dose_reconciled_normalized.json */; }; + 0491B8B99FD555646B83E455 /* suspend_dose_reconciled_normalized_iob.json in Resources */ = {isa = PBXBuildFile; fileRef = F57C6E57E2318FE5A3353E85 /* suspend_dose_reconciled_normalized_iob.json */; }; + 5E77F6516D69B22A57EB850A /* LoopAlgorithm in Frameworks */ = {isa = PBXBuildFile; productRef = 761F6A285BE2833DF06F7E55 /* LoopAlgorithm */; }; + 613BF2B78104AB9694590C65 /* AlgorithmBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36421A4B9676EF68CDDC4ECD /* AlgorithmBridge.swift */; }; + 767B36174D190F3191F2EA73 /* LoopKitTypeAliases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FAEFD820F50BFF211AC6D9 /* LoopKitTypeAliases.swift */; }; + 8DC1CE32F39301122B550325 /* LoopKitTypeAliases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FAEFD820F50BFF211AC6D9 /* LoopKitTypeAliases.swift */; }; + ABD9218EB98C9216FBB04382 /* LoopKitTypeAliases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FAEFD820F50BFF211AC6D9 /* LoopKitTypeAliases.swift */; }; + AF79DFAB0F55143D66022668 /* LoopKitTypeAliases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FAEFD820F50BFF211AC6D9 /* LoopKitTypeAliases.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -1584,6 +1610,26 @@ FEEDF0000000000000000004 /* Statistics/TimeInRangeBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Statistics/TimeInRangeBar.swift; sourceTree = ""; }; F69CE2EE3714858772FE7A26 /* InsulinDeliveryHistoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsulinDeliveryHistoryTests.swift; sourceTree = ""; }; FEEDF0000000000000000005 /* GlucoseStatisticsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseStatisticsTests.swift; sourceTree = ""; }; + 35FAEFD820F50BFF211AC6D9 /* LoopKitTypeAliases.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LoopKitTypeAliases.swift; sourceTree = ""; }; + 36421A4B9676EF68CDDC4ECD /* AlgorithmBridge.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AlgorithmBridge.swift; sourceTree = ""; }; + 0E0A246E78F6985AB1207647 /* DoseEntryInsulinMathTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoseEntryInsulinMathTests.swift; sourceTree = ""; }; + 6C372374D3D3D9C5F8E3AD93 /* TemporaryScheduleOverrideAlgorithmTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemporaryScheduleOverrideAlgorithmTests.swift; sourceTree = ""; }; + 00B649A44AFB50BC4B9A70F6 /* basal.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = basal.json; sourceTree = ""; }; + B3DDD98615BAC7131714F89B /* iob_from_doses_exponential_output.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = iob_from_doses_exponential_output.json; sourceTree = ""; }; + 5B3F3FD4EDF5B0DE5F25489A /* iob_from_doses_output.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = iob_from_doses_output.json; sourceTree = ""; }; + DD57D2F830A1249A6C5622C0 /* iob_from_multiple_curves_output.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = iob_from_multiple_curves_output.json; sourceTree = ""; }; + C8FF391DE1198B1832CE4313 /* iob_from_reservoir_output.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = iob_from_reservoir_output.json; sourceTree = ""; }; + 2DB5AB3635FA0041E6102578 /* normalize_edge_case_doses_input.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = normalize_edge_case_doses_input.json; sourceTree = ""; }; + 2776E774AAD3E4D7CFE1FA6E /* normalize_edge_case_doses_mutable_input.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = normalize_edge_case_doses_mutable_input.json; sourceTree = ""; }; + 1FC4107E6003069B6CE7057A /* normalize_edge_case_doses_mutable_output.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = normalize_edge_case_doses_mutable_output.json; sourceTree = ""; }; + 19B187891AF73C340EE9854A /* normalize_edge_case_doses_output.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = normalize_edge_case_doses_output.json; sourceTree = ""; }; + 9C2AC83FE854B422A6BCC7A8 /* normalized_doses.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = normalized_doses.json; sourceTree = ""; }; + 69A018B00CD62B1E5349701F /* normalized_reservoir_history_output.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = normalized_reservoir_history_output.json; sourceTree = ""; }; + A69826F94E3337F0DE9F46C1 /* reservoir_history_with_rewind_and_prime_output.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = reservoir_history_with_rewind_and_prime_output.json; sourceTree = ""; }; + 9BE0AA50E7B42B8FBAFA8304 /* suspend_dose.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = suspend_dose.json; sourceTree = ""; }; + 1EBEB8F96DA092DAD8140367 /* suspend_dose_reconciled.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = suspend_dose_reconciled.json; sourceTree = ""; }; + B78CA313148D4CF2EDBBC165 /* suspend_dose_reconciled_normalized.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = suspend_dose_reconciled_normalized.json; sourceTree = ""; }; + F57C6E57E2318FE5A3353E85 /* suspend_dose_reconciled_normalized_iob.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = suspend_dose_reconciled_normalized_iob.json; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1637,6 +1683,7 @@ files = ( 43D9002D21EB225D00AF44BF /* HealthKit.framework in Frameworks */, C1BE2ADD2FCDE9F700D5A369 /* LoopKit.framework in Frameworks */, + 1C99EE4A3BB181BB23DEF066 /* LoopAlgorithm in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1645,6 +1692,7 @@ buildActionMask = 2147483647; files = ( C1275DDE2E81FD470013B99D /* LoopKit.framework in Frameworks */, + 5E77F6516D69B22A57EB850A /* LoopAlgorithm in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1856,6 +1904,7 @@ C16F511A2B89363A00EFD7A1 /* SimpleInsulinDose.swift */, C152B9F42C9C7D4A00ACBC06 /* AutomationHistoryEntry.swift */, 84213C882D94941000642E78 /* InsulinDeliveryLogEvent.swift */, + 36421A4B9676EF68CDDC4ECD /* AlgorithmBridge.swift */, ); path = Models; sourceTree = ""; @@ -2363,6 +2412,7 @@ 43785E9B2120E7060057DED1 /* Intents.intentdefinition */, 89E267FB2292456700A3F2AF /* FeatureFlags.swift */, 7D9BEEF52335CF8D005DCFD6 /* Localizable.strings */, + 35FAEFD820F50BFF211AC6D9 /* LoopKitTypeAliases.swift */, ); path = Common; sourceTree = ""; @@ -2807,6 +2857,8 @@ A9BD28E6272226B40071DF15 /* TestLocalizedError.swift */, A9DFAFB424F048A000950D1E /* WatchHistoricalCarbsTests.swift */, A9C1719625366F780053BCBD /* WatchHistoricalGlucoseTest.swift */, + 0E0A246E78F6985AB1207647 /* DoseEntryInsulinMathTests.swift */, + 6C372374D3D3D9C5F8E3AD93 /* TemporaryScheduleOverrideAlgorithmTests.swift */, ); path = Models; sourceTree = ""; @@ -2971,10 +3023,34 @@ E9C58A7924DB529A00487A17 /* dynamic_glucose_effect_partially_observed.json */, E9C58A7B24DB529A00487A17 /* insulin_effect.json */, E9C58A7724DB529A00487A17 /* momentum_effect_bouncing.json */, + 6783361D7C37BAC435B5F770 /* insulin_math */, ); path = Fixtures; sourceTree = ""; }; + 6783361D7C37BAC435B5F770 /* insulin_math */ = { + isa = PBXGroup; + children = ( + 00B649A44AFB50BC4B9A70F6 /* basal.json */, + B3DDD98615BAC7131714F89B /* iob_from_doses_exponential_output.json */, + 5B3F3FD4EDF5B0DE5F25489A /* iob_from_doses_output.json */, + DD57D2F830A1249A6C5622C0 /* iob_from_multiple_curves_output.json */, + C8FF391DE1198B1832CE4313 /* iob_from_reservoir_output.json */, + 2DB5AB3635FA0041E6102578 /* normalize_edge_case_doses_input.json */, + 2776E774AAD3E4D7CFE1FA6E /* normalize_edge_case_doses_mutable_input.json */, + 1FC4107E6003069B6CE7057A /* normalize_edge_case_doses_mutable_output.json */, + 19B187891AF73C340EE9854A /* normalize_edge_case_doses_output.json */, + 9C2AC83FE854B422A6BCC7A8 /* normalized_doses.json */, + 69A018B00CD62B1E5349701F /* normalized_reservoir_history_output.json */, + A69826F94E3337F0DE9F46C1 /* reservoir_history_with_rewind_and_prime_output.json */, + 9BE0AA50E7B42B8FBAFA8304 /* suspend_dose.json */, + 1EBEB8F96DA092DAD8140367 /* suspend_dose_reconciled.json */, + B78CA313148D4CF2EDBBC165 /* suspend_dose_reconciled_normalized.json */, + F57C6E57E2318FE5A3353E85 /* suspend_dose_reconciled_normalized_iob.json */, + ); + path = insulin_math; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -3092,6 +3168,9 @@ dependencies = ( ); name = "LoopCore-watchOS"; + packageProductDependencies = ( + C42F6B8A2B1B9E0142078BF9 /* LoopAlgorithm */, + ); productName = LoopCore; productReference = 43D9002A21EB209400AF44BF /* LoopCore.framework */; productType = "com.apple.product-type.framework"; @@ -3110,6 +3189,9 @@ dependencies = ( ); name = LoopCore; + packageProductDependencies = ( + 761F6A285BE2833DF06F7E55 /* LoopAlgorithm */, + ); productName = LoopCore; productReference = 43D9FFCF21EAE05D00AF44BF /* LoopCore.framework */; productType = "com.apple.product-type.framework"; @@ -3436,6 +3518,22 @@ E9C58A7D24DB529A00487A17 /* basal_profile.json in Resources */, E9C58A7E24DB529A00487A17 /* dynamic_glucose_effect_partially_observed.json in Resources */, E9C58A7C24DB529A00487A17 /* momentum_effect_bouncing.json in Resources */, + 0D48B6D1CF1EA2B542229625 /* basal.json in Resources */, + 0131FD2953DE7C998EE3F663 /* iob_from_doses_exponential_output.json in Resources */, + C09625393B9711F25BAD3D52 /* iob_from_doses_output.json in Resources */, + 59D3D2A08D733F67B0F0935E /* iob_from_multiple_curves_output.json in Resources */, + 3327886285617123471C28DA /* iob_from_reservoir_output.json in Resources */, + 5EF5D4007DBE6486653E11EF /* normalize_edge_case_doses_input.json in Resources */, + 3948ACE22554E6743AF410D9 /* normalize_edge_case_doses_mutable_input.json in Resources */, + 1C21A7E243CB4F2833F77511 /* normalize_edge_case_doses_mutable_output.json in Resources */, + 9AC8982609B95285B3F1BB52 /* normalize_edge_case_doses_output.json in Resources */, + B8668893E285EC0E23BACEFA /* normalized_doses.json in Resources */, + 89E3C86E262B61F3D940E805 /* normalized_reservoir_history_output.json in Resources */, + 8CA0807CED5865CF795FEA1C /* reservoir_history_with_rewind_and_prime_output.json in Resources */, + 3B8D727402F48A542A6B3F86 /* suspend_dose.json in Resources */, + 5530CD403A0857E55CFDD24C /* suspend_dose_reconciled.json in Resources */, + D3311BF2CE4A9689C7FC14C4 /* suspend_dose_reconciled_normalized.json in Resources */, + 0491B8B99FD555646B83E455 /* suspend_dose_reconciled_normalized_iob.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3619,6 +3717,7 @@ 84AA81DD2A4A2999000B658B /* StatusWidgetTimelineProvider.swift in Sources */, 84D2879F2AC756C8007ED283 /* ContentMargin.swift in Sources */, 8496F7312B5711C4003E672C /* ContentMargin.swift in Sources */, + ABD9218EB98C9216FBB04382 /* LoopKitTypeAliases.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3893,6 +3992,8 @@ 43C2FAE11EB656A500364AFF /* GlucoseEffectVelocity.swift in Sources */, 14C970862C5C358C00E8A01B /* FavoriteFoodInsightsChartsView.swift in Sources */, 5021CA8C264146C9DFF33E9D /* PluginPickerView.swift in Sources */, + 17EA66859884C0208DED47CF /* LoopKitTypeAliases.swift in Sources */, + 613BF2B78104AB9694590C65 /* AlgorithmBridge.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3993,6 +4094,7 @@ 894F6DD3243BCBDB00CCE676 /* Environment+SizeClass.swift in Sources */, C17D52042E7F0578001D2AD2 /* LabelValueRow.swift in Sources */, 895788B2242E69A2002CB114 /* CircularAccessoryButtonStyle.swift in Sources */, + 8DC1CE32F39301122B550325 /* LoopKitTypeAliases.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4129,6 +4231,9 @@ E93E86B224DDE21D00FF40C8 /* MockCarbStore.swift in Sources */, C1DA43592B1A784900CBD33F /* MockDeliveryDelegate.swift in Sources */, C18859A02AF1612B0010F21F /* PersistenceController.swift in Sources */, + 82430C768078D9BEDFDECA6C /* DoseEntryInsulinMathTests.swift in Sources */, + BD37CA2F10445DB600A35199 /* TemporaryScheduleOverrideAlgorithmTests.swift in Sources */, + AF79DFAB0F55143D66022668 /* LoopKitTypeAliases.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4173,6 +4278,7 @@ B4E96D57248A7B0F002DABAD /* StatusHighlightHUDView.swift in Sources */, B42C951424A3C76000857C73 /* CGMStatusHUDViewModel.swift in Sources */, 4F75289E1DFE1F6000C322D6 /* LoopCompletionHUDView.swift in Sources */, + 767B36174D190F3191F2EA73 /* LoopKitTypeAliases.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -5510,6 +5616,14 @@ package = C1CCF10B2858F4F70035389C /* XCRemoteSwiftPackageReference "SwiftCharts" */; productName = SwiftCharts; }; + 761F6A285BE2833DF06F7E55 /* LoopAlgorithm */ = { + isa = XCSwiftPackageProductDependency; + productName = LoopAlgorithm; + }; + C42F6B8A2B1B9E0142078BF9 /* LoopAlgorithm */ = { + isa = XCSwiftPackageProductDependency; + productName = LoopAlgorithm; + }; /* End XCSwiftPackageProductDependency section */ /* Begin XCVersionGroup section */ diff --git a/Loop/Extensions/BasalDeliveryState.swift b/Loop/Extensions/BasalDeliveryState.swift index 32cf77c930..6b53f06e2b 100644 --- a/Loop/Extensions/BasalDeliveryState.swift +++ b/Loop/Extensions/BasalDeliveryState.swift @@ -8,7 +8,6 @@ import LoopKit import LoopCore -import LoopAlgorithm extension PumpManagerStatus.BasalDeliveryState { func getNetBasal(basalSchedule: BasalRateSchedule, maximumBasalRatePerHour: Double?) -> NetBasal? { diff --git a/Loop/Extensions/BasalRelativeDose.swift b/Loop/Extensions/BasalRelativeDose.swift index d78d5ad967..41497fdd38 100644 --- a/Loop/Extensions/BasalRelativeDose.swift +++ b/Loop/Extensions/BasalRelativeDose.swift @@ -7,6 +7,7 @@ // import Foundation +import LoopKit import LoopAlgorithm public extension Array where Element == BasalRelativeDose { diff --git a/Loop/Extensions/CarbStore+SimulatedCoreData.swift b/Loop/Extensions/CarbStore+SimulatedCoreData.swift index 71cca3c2fc..6640dc07f3 100644 --- a/Loop/Extensions/CarbStore+SimulatedCoreData.swift +++ b/Loop/Extensions/CarbStore+SimulatedCoreData.swift @@ -7,7 +7,6 @@ // import Foundation -import LoopAlgorithm import LoopKit // MARK: - Simulated Core Data diff --git a/Loop/Extensions/CollectionType+Loop.swift b/Loop/Extensions/CollectionType+Loop.swift index 8740bdb453..7aedadc6b0 100644 --- a/Loop/Extensions/CollectionType+Loop.swift +++ b/Loop/Extensions/CollectionType+Loop.swift @@ -8,10 +8,9 @@ import Foundation import LoopKit -import LoopAlgorithm -public extension Sequence where Element: TimelineValue { +public extension Sequence where Element: LoopKit.TimelineValue { /// Returns the closest element index in the sorted sequence prior to the specified date /// /// - parameter date: The date to use in the search diff --git a/Loop/Extensions/DoseStore+SimulatedCoreData.swift b/Loop/Extensions/DoseStore+SimulatedCoreData.swift index 185bea71b9..0ca98d22f9 100644 --- a/Loop/Extensions/DoseStore+SimulatedCoreData.swift +++ b/Loop/Extensions/DoseStore+SimulatedCoreData.swift @@ -7,7 +7,6 @@ // import Foundation -import LoopAlgorithm import LoopKit // MARK: - Simulated Core Data diff --git a/Loop/Extensions/DosingDecisionStore+SimulatedCoreData.swift b/Loop/Extensions/DosingDecisionStore+SimulatedCoreData.swift index 83072b4701..f9ba288d06 100644 --- a/Loop/Extensions/DosingDecisionStore+SimulatedCoreData.swift +++ b/Loop/Extensions/DosingDecisionStore+SimulatedCoreData.swift @@ -9,7 +9,6 @@ import Foundation import HealthKit import LoopKit -import LoopAlgorithm // MARK: - Simulated Core Data diff --git a/Loop/Extensions/Environment+SettingsManager.swift b/Loop/Extensions/Environment+SettingsManager.swift index 717726f025..a52626794e 100644 --- a/Loop/Extensions/Environment+SettingsManager.swift +++ b/Loop/Extensions/Environment+SettingsManager.swift @@ -8,7 +8,6 @@ import SwiftUI import LoopKit -import LoopAlgorithm @MainActor diff --git a/Loop/Extensions/GlucoseStore+SimulatedCoreData.swift b/Loop/Extensions/GlucoseStore+SimulatedCoreData.swift index 99cc96c19e..e579f4d928 100644 --- a/Loop/Extensions/GlucoseStore+SimulatedCoreData.swift +++ b/Loop/Extensions/GlucoseStore+SimulatedCoreData.swift @@ -7,7 +7,6 @@ // import Foundation -import LoopAlgorithm import LoopKit // MARK: - Simulated Core Data diff --git a/Loop/Extensions/SettingsStore+SimulatedCoreData.swift b/Loop/Extensions/SettingsStore+SimulatedCoreData.swift index 4e430c0834..8fff07dbaf 100644 --- a/Loop/Extensions/SettingsStore+SimulatedCoreData.swift +++ b/Loop/Extensions/SettingsStore+SimulatedCoreData.swift @@ -9,7 +9,6 @@ import Foundation import HealthKit import LoopKit -import LoopAlgorithm // MARK: - Simulated Core Data diff --git a/Loop/Extensions/TempBasalRecommendation.swift b/Loop/Extensions/TempBasalRecommendation.swift index 0d7103b7f6..f5cae224c3 100644 --- a/Loop/Extensions/TempBasalRecommendation.swift +++ b/Loop/Extensions/TempBasalRecommendation.swift @@ -8,7 +8,6 @@ import Foundation import LoopKit -import LoopAlgorithm extension TempBasalRecommendation { /// Equates the recommended rate with another rate diff --git a/Loop/Extensions/UserDefaults+Loop.swift b/Loop/Extensions/UserDefaults+Loop.swift index 58e44a9b84..ec07d031ef 100644 --- a/Loop/Extensions/UserDefaults+Loop.swift +++ b/Loop/Extensions/UserDefaults+Loop.swift @@ -7,7 +7,6 @@ import Foundation import LoopKit -import LoopAlgorithm extension UserDefaults { diff --git a/Loop/Managers/Alerts/GlucoseAlertManager.swift b/Loop/Managers/Alerts/GlucoseAlertManager.swift index 01109738c2..be6dd09f95 100644 --- a/Loop/Managers/Alerts/GlucoseAlertManager.swift +++ b/Loop/Managers/Alerts/GlucoseAlertManager.swift @@ -11,7 +11,6 @@ import Combine import Foundation -import LoopAlgorithm import LoopKit import LoopKitUI import os.log diff --git a/Loop/Managers/AnalyticsServicesManager.swift b/Loop/Managers/AnalyticsServicesManager.swift index bfa9392e67..627aa167c7 100644 --- a/Loop/Managers/AnalyticsServicesManager.swift +++ b/Loop/Managers/AnalyticsServicesManager.swift @@ -9,7 +9,6 @@ import Foundation import LoopKit import LoopCore -import LoopAlgorithm final class AnalyticsServicesManager { diff --git a/Loop/Managers/CGMStalenessMonitor.swift b/Loop/Managers/CGMStalenessMonitor.swift index 25c0365e1e..522f8fb5cc 100644 --- a/Loop/Managers/CGMStalenessMonitor.swift +++ b/Loop/Managers/CGMStalenessMonitor.swift @@ -9,7 +9,6 @@ import Foundation import LoopKit import LoopCore -import LoopAlgorithm protocol CGMStalenessMonitorDelegate: AnyObject { func getLatestCGMGlucose(since: Date) async throws -> StoredGlucoseSample? @@ -38,9 +37,9 @@ class CGMStalenessMonitor { let mostRecentGlucose = samples.map { $0.date }.max()! let cgmDataAge = -mostRecentGlucose.timeIntervalSinceNow - if cgmDataAge < LoopAlgorithm.inputDataRecencyInterval { + if cgmDataAge < LoopCoreConstants.inputDataRecencyInterval { self.cgmDataIsStale = false - self.updateCGMStalenessTimer(expiration: mostRecentGlucose.addingTimeInterval(LoopAlgorithm.inputDataRecencyInterval)) + self.updateCGMStalenessTimer(expiration: mostRecentGlucose.addingTimeInterval(LoopCoreConstants.inputDataRecencyInterval)) } else { self.cgmDataIsStale = true } @@ -60,11 +59,11 @@ class CGMStalenessMonitor { func checkCGMStaleness() async { do { - let sample = try await delegate?.getLatestCGMGlucose(since: Date(timeIntervalSinceNow: -LoopAlgorithm.inputDataRecencyInterval)) + let sample = try await delegate?.getLatestCGMGlucose(since: Date(timeIntervalSinceNow: -LoopCoreConstants.inputDataRecencyInterval)) self.log.debug("Fetched latest CGM Glucose for checkCGMStaleness: %{public}@", String(describing: sample)) if let sample = sample { self.cgmDataIsStale = false - self.updateCGMStalenessTimer(expiration: sample.startDate.addingTimeInterval(LoopAlgorithm.inputDataRecencyInterval + CGMStalenessMonitor.cgmStalenessTimerTolerance)) + self.updateCGMStalenessTimer(expiration: sample.startDate.addingTimeInterval(LoopCoreConstants.inputDataRecencyInterval + CGMStalenessMonitor.cgmStalenessTimerTolerance)) } else { self.cgmDataIsStale = true } diff --git a/Loop/Managers/DeviceDataManager.swift b/Loop/Managers/DeviceDataManager.swift index 236c40a22d..a9df54061f 100644 --- a/Loop/Managers/DeviceDataManager.swift +++ b/Loop/Managers/DeviceDataManager.swift @@ -13,7 +13,7 @@ import LoopCore import LoopTestingKit import UserNotifications import Combine -import LoopAlgorithm +import LoopKit public enum DemoError: LocalizedError { case CommsError @@ -669,7 +669,7 @@ final class DeviceDataManager { var isGlucoseValueStale: Bool { guard let latestGlucoseDataDate = glucoseStore.latestGlucose?.startDate else { return true } - return Date().timeIntervalSince(latestGlucoseDataDate) > LoopAlgorithm.inputDataRecencyInterval + return Date().timeIntervalSince(latestGlucoseDataDate) > LoopCoreConstants.inputDataRecencyInterval } } diff --git a/Loop/Managers/DoseEnactor.swift b/Loop/Managers/DoseEnactor.swift index f9df3ebdff..f7ff5b89eb 100644 --- a/Loop/Managers/DoseEnactor.swift +++ b/Loop/Managers/DoseEnactor.swift @@ -8,7 +8,6 @@ import Foundation import LoopKit -import LoopAlgorithm class DoseEnactor { diff --git a/Loop/Managers/ExtensionDataManager.swift b/Loop/Managers/ExtensionDataManager.swift index 32bc0b90c4..915da7554b 100644 --- a/Loop/Managers/ExtensionDataManager.swift +++ b/Loop/Managers/ExtensionDataManager.swift @@ -6,7 +6,6 @@ // Copyright © 2016 Nathan Racklyeft. All rights reserved. // -import LoopAlgorithm import UIKit import LoopKit import LoopCore @@ -149,7 +148,7 @@ final class ExtensionDataManager { let first = predictedGlucose[predictedGlucose.startIndex] let second = predictedGlucose[predictedGlucose.startIndex.advanced(by: 1)] context.predictedGlucose = PredictedGlucoseContext( - values: predictedGlucose.map { $0.quantity.doubleValue(for: glucoseUnit) }, + values: predictedGlucose.map { $0.loopKit.quantity.doubleValue(for: glucoseUnit) }, unit: glucoseUnit, startDate: first.startDate, interval: second.startDate.timeIntervalSince(first.startDate)) diff --git a/Loop/Managers/Live Activity/ChartAxisGenerator.swift b/Loop/Managers/Live Activity/ChartAxisGenerator.swift index 536d10daa7..b2db2973e2 100644 --- a/Loop/Managers/Live Activity/ChartAxisGenerator.swift +++ b/Loop/Managers/Live Activity/ChartAxisGenerator.swift @@ -8,7 +8,7 @@ import Foundation import HealthKit -import LoopAlgorithm +import LoopKit import SwiftCharts import UIKit diff --git a/Loop/Managers/Live Activity/LiveActivityManager.swift b/Loop/Managers/Live Activity/LiveActivityManager.swift index d8653b1c40..3d1073a100 100644 --- a/Loop/Managers/Live Activity/LiveActivityManager.swift +++ b/Loop/Managers/Live Activity/LiveActivityManager.swift @@ -9,7 +9,6 @@ import LoopKitUI import LoopKit import LoopCore -import LoopAlgorithm import Foundation import HealthKit import ActivityKit diff --git a/Loop/Managers/Live Activity/LiveActivityManagerProxy.swift b/Loop/Managers/Live Activity/LiveActivityManagerProxy.swift index ffea337d21..92d0899b8a 100644 --- a/Loop/Managers/Live Activity/LiveActivityManagerProxy.swift +++ b/Loop/Managers/Live Activity/LiveActivityManagerProxy.swift @@ -7,7 +7,6 @@ // import LoopKit -import LoopAlgorithm protocol LiveActivityManagerProxy { /// Update the live activity with current override and glucose target information. diff --git a/Loop/Managers/LoopAppManager.swift b/Loop/Managers/LoopAppManager.swift index 57d121a979..10d4fcfc12 100644 --- a/Loop/Managers/LoopAppManager.swift +++ b/Loop/Managers/LoopAppManager.swift @@ -17,7 +17,6 @@ import MockKit import HealthKit import WidgetKit import LoopCore -import LoopAlgorithm import SwiftUI #if targetEnvironment(simulator) @@ -244,7 +243,7 @@ class LoopAppManager: NSObject { healthStore: healthStore, observeHealthKitSamplesFromOtherApps: FeatureFlags.observeHealthKitCarbSamplesFromOtherApps, // At some point we should let the user decide which apps they would like to import from. type: HealthKitSampleStore.carbType, - observationStart: Date().addingTimeInterval(-CarbMath.maximumAbsorptionTimeInterval) + observationStart: Date().addingTimeInterval(-LoopCoreConstants.maximumCarbAbsorptionTimeInterval) ) temporaryPresetsManager = TemporaryPresetsManager(settingsProvider: settingsManager, alertIssuer: alertManager) @@ -264,7 +263,7 @@ class LoopAppManager: NSObject { healthStore: healthStore, observeHealthKitSamplesFromOtherApps: FeatureFlags.observeHealthKitDoseSamplesFromOtherApps, type: HealthKitSampleStore.insulinQuantityType, - observationStart: Date().addingTimeInterval(-CarbMath.maximumAbsorptionTimeInterval) + observationStart: Date().addingTimeInterval(-LoopCoreConstants.maximumCarbAbsorptionTimeInterval) ) self.doseStore = await DoseStore( diff --git a/Loop/Managers/LoopDataManager+CarbAbsorption.swift b/Loop/Managers/LoopDataManager+CarbAbsorption.swift index cb1e49a977..3aee4d581a 100644 --- a/Loop/Managers/LoopDataManager+CarbAbsorption.swift +++ b/Loop/Managers/LoopDataManager+CarbAbsorption.swift @@ -21,7 +21,7 @@ extension LoopDataManager { func dynamicCarbsOnBoard(from start: Date? = nil, to end: Date? = nil) async -> [CarbValue] { if let effects = displayState.output?.effects { - return effects.carbStatus.dynamicCarbsOnBoard(from: start, to: end, absorptionModel: carbAbsorptionModel.model) + return effects.carbStatus.dynamicCarbsOnBoard(from: start, to: end, absorptionModel: carbAbsorptionModel.model).map(\.loopKit) } else { return [] } @@ -65,10 +65,11 @@ extension LoopDataManager { guard !carbRatio.isEmpty else { throw LoopError.configurationError(.carbRatioSchedule) } - let carbRatioWithOverrides = overrides.applyCarbRatio(over: carbRatio) + let carbRatioWithOverrides = overrides.applyCarbRatio(over: carbRatio).algorithmSchedule() + let algorithmSensitivity = sensitivityWithOverrides.algorithmMgdlSchedule() // Overlay basal history on basal doses, splitting doses to get amount delivered relative to basal - let annotatedDoses = doses.annotated(with: basalWithOverrides) + let annotatedDoses = doses.annotated(with: basalWithOverrides.algorithmSchedule()) // Use the standard mid-absorption ISF model — matching LoopAlgorithm's // prediction path (useMidAbsorptionISF) — so this review's carb @@ -77,7 +78,7 @@ extension LoopDataManager { // disagreed, an ISF-changing override made the carb screen and the // home screen show different COB. let insulinEffects = annotatedDoses.glucoseEffectsMidAbsorptionISF( - insulinSensitivityHistory: sensitivityWithOverrides, + insulinSensitivityHistoryMgdlPerUnit: algorithmSensitivity, from: start.addingTimeInterval(-CarbMath.maximumAbsorptionTimeInterval).dateFlooredToTimeInterval(GlucoseMath.defaultDelta), to: nil) @@ -88,22 +89,22 @@ extension LoopDataManager { let carbStatus = carbEntries.map( to: insulinCounteractionEffects, carbRatio: carbRatioWithOverrides, - insulinSensitivity: sensitivityWithOverrides + insulinSensitivityMgdlPerUnit: algorithmSensitivity ) let carbEffects = carbStatus.dynamicGlucoseEffects( from: start, to: end.addingTimeInterval(InsulinMath.defaultInsulinActivityDuration), carbRatios: carbRatioWithOverrides, - insulinSensitivities: sensitivityWithOverrides, + insulinSensitivitiesMgdlPerUnit: algorithmSensitivity, absorptionModel: CarbAbsorptionModel.piecewiseLinear.model ) return CarbAbsorptionReview( carbEntries: carbEntries, carbStatuses: carbStatus, - effectsVelocities: insulinCounteractionEffects, - carbEffects: carbEffects + effectsVelocities: insulinCounteractionEffects.map(\.loopKit), + carbEffects: carbEffects.map(\.loopKit) ) } } diff --git a/Loop/Managers/LoopDataManager.swift b/Loop/Managers/LoopDataManager.swift index 45c48734a0..f8f4d4bc39 100644 --- a/Loop/Managers/LoopDataManager.swift +++ b/Loop/Managers/LoopDataManager.swift @@ -9,10 +9,10 @@ import Foundation import Combine import LoopKit +import LoopAlgorithm import LoopKitUI import LoopCore import WidgetKit -import LoopAlgorithm struct AlgorithmDisplayState { @@ -99,11 +99,11 @@ final class LoopDataManager: ObservableObject { // Display state convenience accessors var predictedGlucose: [PredictedGlucoseValue]? { - displayState.output?.predictedGlucose + displayState.output?.predictedGlucose.map(\.loopKit) } var tempBasalRecommendation: TempBasalRecommendation? { - displayState.output?.recommendation?.automatic?.basalAdjustment + displayState.output?.recommendation?.automatic?.basalAdjustment.loopKit } var automaticBolusRecommendation: Double? { @@ -111,7 +111,7 @@ final class LoopDataManager: ObservableObject { } var automaticRecommendation: AutomaticDoseRecommendation? { - displayState.output?.recommendation?.automatic + displayState.output?.recommendation?.automatic?.loopKit } @Published private(set) var lastLoopCompleted: Date? @@ -445,7 +445,7 @@ final class LoopDataManager: ObservableObject { start: baseTime, duration: recommendationInsulinModel.effectDuration ) - let neededSensitivityTimeline = LoopAlgorithm.timelineIntervalForSensitivity( + let neededSensitivityTimeline = Algorithm.timelineIntervalForSensitivity( doses: dosesWithModel, glucoseHistoryStart: glucose.first?.startDate ?? baseTime, recommendationEffectInterval: recommendationEffectInterval @@ -546,17 +546,17 @@ final class LoopDataManager: ObservableObject { doses: dosesWithModel, carbEntries: carbEntries, predictionStart: baseTime, - basal: basalWithOverrides, - sensitivity: sensitivityWithOverrides, - carbRatio: carbRatioWithOverrides, - target: target, - suspendThreshold: suspendThreshold, + basal: basalWithOverrides.algorithmSchedule(), + sensitivityMgdlPerUnit: sensitivityWithOverrides.algorithmMgdlSchedule(), + carbRatio: carbRatioWithOverrides.algorithmSchedule(), + targetMgdl: target.algorithmMgdlRanges(), + suspendThresholdMgdl: suspendThreshold.doubleValue(for: .milligramsPerDeciliter), maxBolus: maxBolus, maxBasalRate: maxBasalRate, useIntegralRetrospectiveCorrection: false, includePositiveVelocityAndRC: true, carbAbsorptionModel: carbAbsorptionModel, - recommendationInsulinModel: recommendationInsulinModel, + recommendationInsulinModel: AlgorithmInsulinModel(recommendationInsulinModel), recommendationType: .manualBolus, automaticBolusApplicationFactor: nil, experiments: experiments, @@ -633,7 +633,7 @@ final class LoopDataManager: ObservableObject { } private nonisolated func runAlgorithm(input: StoredDataAlgorithmInput) async -> AlgorithmOutput { - LoopAlgorithm.run(input: input) + Algorithm.run(input: input) } /// Cancel the active temp basal if it was automatically issued @@ -702,19 +702,19 @@ final class LoopDataManager: ObservableObject { throw LoopError.missingDataError(.glucose) } - guard loopBaseTime.timeIntervalSince(latestGlucose.startDate) <= LoopAlgorithm.inputDataRecencyInterval else { + guard loopBaseTime.timeIntervalSince(latestGlucose.startDate) <= LoopCoreConstants.inputDataRecencyInterval else { throw LoopError.glucoseTooOld(date: latestGlucose.startDate) } - guard latestGlucose.startDate.timeIntervalSince(loopBaseTime) <= LoopAlgorithm.inputDataRecencyInterval else { + guard latestGlucose.startDate.timeIntervalSince(loopBaseTime) <= LoopCoreConstants.inputDataRecencyInterval else { throw LoopError.invalidFutureGlucose(date: latestGlucose.startDate) } - guard loopBaseTime.timeIntervalSince(doseStore.lastAddedPumpData) <= LoopAlgorithm.inputDataRecencyInterval else { + guard loopBaseTime.timeIntervalSince(doseStore.lastAddedPumpData) <= LoopCoreConstants.inputDataRecencyInterval else { throw LoopError.pumpDataTooOld(date: doseStore.lastAddedPumpData) } - var output = LoopAlgorithm.run(input: input) + var output = Algorithm.run(input: input) dosingDecision.algorithmExperiments = algorithmExperiments(for: dosingStrategy, guardAndRunway: output.guardAndRunway) switch output.recommendationResult { @@ -738,7 +738,7 @@ final class LoopDataManager: ObservableObject { let scheduledBasalRate = input.basal.closestPrior(to: loopBaseTime)!.value let activeOverride = temporaryPresetsManager.presetHistory.activeOverride(at: loopBaseTime) - let basalAdjustment = basal.adjustForCurrentDelivery( + let basalAdjustment = basal.loopKit.adjustForCurrentDelivery( at: loopBaseTime, neutralBasalRate: scheduledBasalRate, currentTempBasal: deliveryDelegate.basalDeliveryState?.currentTempBasal, @@ -747,7 +747,7 @@ final class LoopDataManager: ObservableObject { ) if let basalAdjustment { - recommendationToEnact.basalAdjustment = basalAdjustment + recommendationToEnact.basalAdjustment = basalAdjustment.algorithm } output.recommendationResult = .success(.init(automatic: recommendationToEnact)) @@ -833,7 +833,7 @@ final class LoopDataManager: ObservableObject { input.includePositiveVelocityAndRC = usePositiveMomentumAndRCForManualBoluses input.recommendationType = .manualBolus - let output = LoopAlgorithm.run(input: input) + let output = Algorithm.run(input: input) switch output.recommendationResult { @@ -842,7 +842,7 @@ final class LoopDataManager: ObservableObject { if let roundedAmount = deliveryDelegate?.roundBolusVolume(units: manualBolusRecommendation.amount) { manualBolusRecommendation.amount = roundedAmount } - return manualBolusRecommendation + return manualBolusRecommendation.loopKit case .failure(let error): throw error } @@ -856,7 +856,7 @@ final class LoopDataManager: ObservableObject { let now = data.predictionStart let midnight = Calendar.current.startOfDay(for: now) - let history = InsulinDeliveryHistory(doses: data.doses, scheduledBasal: data.basal, coverageStart: midnight, end: now) + let history = InsulinDeliveryHistory(doses: data.doses, scheduledBasal: data.basal.loopKitSchedule(), coverageStart: midnight, end: now) return InsulinValue( startDate: midnight, @@ -889,7 +889,7 @@ final class LoopDataManager: ObservableObject { } var iobValues: [InsulinValue] { - dosesRelativeToBasal.insulinOnBoardTimeline() + dosesRelativeToBasal.insulinOnBoardTimeline().map(\.loopKit) } var dosesRelativeToBasal: [BasalRelativeDose] { @@ -898,10 +898,10 @@ final class LoopDataManager: ObservableObject { func updateRemoteRecommendation(force: Bool = false) async { if lastManualBolusRecommendation == nil { - lastManualBolusRecommendation = displayState.output?.recommendation?.manual + lastManualBolusRecommendation = displayState.output?.recommendation?.manual?.loopKit } - let recommendationChanged = lastManualBolusRecommendation != displayState.output?.recommendation?.manual + let recommendationChanged = lastManualBolusRecommendation != displayState.output?.recommendation?.manual?.loopKit // DIY: post-dose "updateRemoteRecommendation" decisions are also Nightscout's // Loop pill + forecast source (NightscoutService pairs them with the cached "loop" @@ -911,19 +911,19 @@ final class LoopDataManager: ObservableObject { return } - lastManualBolusRecommendation = displayState.output?.recommendation?.manual + lastManualBolusRecommendation = displayState.output?.recommendation?.manual?.loopKit if let output = displayState.output { var dosingDecision = StoredDosingDecision(date: now, reason: "updateRemoteRecommendation") dosingDecision.algorithmExperiments = algorithmExperiments(for: currentDosingStrategy, guardAndRunway: output.guardAndRunway) - dosingDecision.predictedGlucose = output.predictedGlucose + dosingDecision.predictedGlucose = output.predictedGlucose.map(\.loopKit) dosingDecision.insulinOnBoard = displayState.activeInsulin dosingDecision.carbsOnBoard = displayState.activeCarbs switch output.recommendationResult { case .success(let recommendation): - dosingDecision.automaticDoseRecommendation = recommendation.automatic + dosingDecision.automaticDoseRecommendation = recommendation.automatic?.loopKit if let recommendationDate = displayState.input?.predictionStart, let manualRec = recommendation.manual { - dosingDecision.manualBolusRecommendation = ManualBolusRecommendationWithDate(recommendation: manualRec, date: recommendationDate) + dosingDecision.manualBolusRecommendation = ManualBolusRecommendationWithDate(recommendation: manualRec.loopKit, date: recommendationDate) } case .failure(let error): if let loopError = error as? LoopError { @@ -980,7 +980,7 @@ final class LoopDataManager: ObservableObject { private var glucoseValueStaleDate: Date? { guard let latestGlucoseDataDate = glucoseStore.latestGlucose?.startDate else { return nil } - return latestGlucoseDataDate.addingTimeInterval(LoopAlgorithm.inputDataRecencyInterval) + return latestGlucoseDataDate.addingTimeInterval(LoopCoreConstants.inputDataRecencyInterval) } } @@ -1174,8 +1174,8 @@ extension LoopDataManager { // Calculate predicted glucose effect of suspending insulin delivery return suspendDoses.glucoseEffects( - insulinSensitivityHistory: sensitivity - ).filterDateRange(startSuspend, endSuspend) + insulinSensitivityHistoryMgdlPerUnit: sensitivity.algorithmMgdlSchedule() + ).filterDateRange(startSuspend, endSuspend).map(\.loopKit) } func computeSimpleBolusRecommendation(at date: Date, mealCarbs: LoopQuantity?, manualGlucose: LoopQuantity?) -> BolusDosingDecision? { @@ -1325,14 +1325,14 @@ extension StoredDataAlgorithmInput { return rval } - func predictGlucose(effectsOptions: AlgorithmEffectsOptions = .all) throws -> [PredictedGlucoseValue] { - let prediction = LoopAlgorithm.generatePrediction( + func predictGlucose(effectsOptions: AlgorithmEffectsOptions = .all) throws -> [LoopAlgorithm.PredictedGlucoseValue] { + let prediction = Algorithm.generatePrediction( start: predictionStart, glucoseHistory: glucoseHistory, doses: doses, carbEntries: carbEntries, basal: basal, - sensitivity: sensitivity, + sensitivityMgdlPerUnit: sensitivityMgdlPerUnit, carbRatio: carbRatio, algorithmEffectsOptions: effectsOptions, useIntegralRetrospectiveCorrection: self.useIntegralRetrospectiveCorrection, @@ -1564,7 +1564,7 @@ extension LoopDataManager: BolusEntryViewModelDelegate { let prediction = try input.predictGlucose() - return (historicGlucose: input.glucoseHistory, predictedGlucose: prediction) + return (historicGlucose: input.glucoseHistory, predictedGlucose: prediction.map(\.loopKit)) } } @@ -1638,14 +1638,14 @@ extension LoopDataManager: FavoriteFoodInsightsViewModelDelegate { // binary-search filterDateRange. let annotatedDoses = doses .map({ $0.simpleDose(with: insulinModel(for: $0.insulinType)) }) - .annotated(with: basalWithOverrides) + .annotated(with: basalWithOverrides.algorithmSchedule()) .sorted { $0.startDate < $1.startDate } // Standard mid-absorption ISF model, matching LoopAlgorithm's prediction // path — keeps this review's carb absorption consistent with the // algorithm (dose-time `glucoseEffects` is legacy backwards-compat). let insulinEffects = annotatedDoses.glucoseEffectsMidAbsorptionISF( - insulinSensitivityHistory: sensitivityWithOverrides, + insulinSensitivityHistoryMgdlPerUnit: sensitivityWithOverrides.algorithmMgdlSchedule(), from: start.addingTimeInterval(-CarbMath.maximumAbsorptionTimeInterval).dateFlooredToTimeInterval(GlucoseMath.defaultDelta), to: nil) @@ -1655,27 +1655,27 @@ extension LoopDataManager: FavoriteFoodInsightsViewModelDelegate { // Carb Effects let carbStatus = carbEntries.map( to: insulinCounteractionEffects, - carbRatio: carbRatioWithOverrides, - insulinSensitivity: sensitivityWithOverrides + carbRatio: carbRatioWithOverrides.algorithmSchedule(), + insulinSensitivityMgdlPerUnit: sensitivityWithOverrides.algorithmMgdlSchedule() ) let carbEffects = carbStatus.dynamicGlucoseEffects( from: start, to: end.addingTimeInterval(InsulinMath.defaultInsulinActivityDuration), - carbRatios: carbRatioWithOverrides, - insulinSensitivities: sensitivityWithOverrides, + carbRatios: carbRatioWithOverrides.algorithmSchedule(), + insulinSensitivitiesMgdlPerUnit: sensitivityWithOverrides.algorithmMgdlSchedule(), absorptionModel: CarbAbsorptionModel.piecewiseLinear.model ) let carbAbsorptionReview = CarbAbsorptionReview( carbEntries: carbEntries, carbStatuses: carbStatus, - effectsVelocities: insulinCounteractionEffects, - carbEffects: carbEffects + effectsVelocities: insulinCounteractionEffects.map(\.loopKit), + carbEffects: carbEffects.map(\.loopKit) ) let trimmedDoses = annotatedDoses.filterDateRange(start, end) - let trimmedIOBValues = annotatedDoses.insulinOnBoardTimeline().filterDateRange(start, end) + let trimmedIOBValues = annotatedDoses.insulinOnBoardTimeline().filterDateRange(start, end).map(\.loopKit) let historicalChartsData = HistoricalChartsData( glucoseValues: glucose, @@ -1728,7 +1728,7 @@ extension StoredDosingDecision { self.historicalGlucose = input.glucoseHistory.map { HistoricalGlucoseValue(startDate: $0.startDate, quantity: $0.quantity) } switch output.recommendationResult { case .success(let recommendation): - self.automaticDoseRecommendation = recommendation.automatic + self.automaticDoseRecommendation = recommendation.automatic?.loopKit case .failure(let error): self.appendError(error as? LoopError ?? .unknownError(error)) } @@ -1738,7 +1738,7 @@ extension StoredDosingDecision { if let activeCarbs = output.activeCarbs { self.carbsOnBoard = CarbValue(startDate: input.predictionStart, value: activeCarbs) } - self.predictedGlucose = output.predictedGlucose + self.predictedGlucose = output.predictedGlucose.map(\.loopKit) } } @@ -1779,28 +1779,28 @@ extension LoopDataManager: DiagnosticReportGenerator { "insulinCounteractionEffects: [", "* GlucoseEffectVelocity(start, end, mg/dL/min)", (algoOutput?.effects.insulinCounteraction ?? []).reduce(into: "", { (entries, entry) in - entries.append("* \(entry.startDate), \(entry.endDate), \(entry.quantity.doubleValue(for: GlucoseEffectVelocity.unit))\n") + entries.append("* \(entry.startDate), \(entry.endDate), \(entry.mgdlPerSecond * 60)\n") }), "]", "insulinEffect: [", "* GlucoseEffect(start, mg/dL)", (algoOutput?.effects.insulin ?? []).reduce(into: "", { (entries, entry) in - entries.append("* \(entry.startDate), \(entry.quantity.doubleValue(for: .milligramsPerDeciliter))\n") + entries.append("* \(entry.startDate), \(entry.mgdl)\n") }), "]", "carbEffect: [", "* GlucoseEffect(start, mg/dL)", (algoOutput?.effects.carbs ?? []).reduce(into: "", { (entries, entry) in - entries.append("* \(entry.startDate), \(entry.quantity.doubleValue(for: .milligramsPerDeciliter))\n") + entries.append("* \(entry.startDate), \(entry.mgdl)\n") }), "]", "predictedGlucose: [", "* PredictedGlucoseValue(start, mg/dL)", (algoOutput?.predictedGlucose ?? []).reduce(into: "", { (entries, entry) in - entries.append("* \(entry.startDate), \(entry.quantity.doubleValue(for: .milligramsPerDeciliter))\n") + entries.append("* \(entry.startDate), \(entry.mgdl)\n") }), "]", @@ -1809,7 +1809,7 @@ extension LoopDataManager: DiagnosticReportGenerator { "retrospectiveCorrection: [", "* GlucoseEffect(start, mg/dL)", (algoOutput?.effects.retrospectiveCorrection ?? []).reduce(into: "", { (entries, entry) in - entries.append("* \(entry.startDate), \(entry.quantity.doubleValue(for: .milligramsPerDeciliter))\n") + entries.append("* \(entry.startDate), \(entry.mgdl)\n") }), "]", diff --git a/Loop/Managers/Missed Meal Detection/MealDetectionManager.swift b/Loop/Managers/Missed Meal Detection/MealDetectionManager.swift index 32b2dd37ce..a33a7d6c44 100644 --- a/Loop/Managers/Missed Meal Detection/MealDetectionManager.swift +++ b/Loop/Managers/Missed Meal Detection/MealDetectionManager.swift @@ -10,8 +10,8 @@ import Foundation import OSLog import LoopCore import LoopKit -import Combine import LoopAlgorithm +import Combine enum MissedMealStatus: Equatable { case hasMissedMeal(startTime: Date, carbAmount: Double) @@ -104,9 +104,9 @@ class MealDetectionManager { // MARK: Meal Detection func hasMissedMeal( at date: Date, - glucoseSamples: [some GlucoseSampleValue], - insulinCounteractionEffects: [GlucoseEffectVelocity], - carbEffects: [GlucoseEffect], + glucoseSamples: [some LoopAlgorithm.GlucoseSampleValue], + insulinCounteractionEffects: [LoopAlgorithm.GlucoseEffectVelocity], + carbEffects: [LoopAlgorithm.GlucoseEffect], sensitivitySchedule: InsulinSensitivitySchedule, carbRatioSchedule: CarbRatioSchedule ) -> MissedMealStatus @@ -132,11 +132,11 @@ class MealDetectionManager { var effectValueCache: [Date: Double] = [:] /// Carb effects are cumulative, so we have to subtract the previous effect value - var previousEffectValue: Double = filteredCarbEffects.first?.quantity.doubleValue(for: unit) ?? 0 + var previousEffectValue: Double = filteredCarbEffects.first?.mgdl ?? 0 /// Counteraction effects only take insulin into account, so we need to account for the carb effects when computing the unexpected deviations for effect in filteredCarbEffects { - let value = effect.quantity.doubleValue(for: unit) + let value = effect.mgdl /// We do `-1 * (value - previousEffectValue)` because this will compute the carb _counteraction_ effect effectValueCache[effect.startDate] = (effectValueCache[effect.startDate] ?? 0) + -1 * (value - previousEffectValue) previousEffectValue = value @@ -150,16 +150,16 @@ class MealDetectionManager { let end = min($0.endDate, now) guard let effect = $0.effect(from: start, to: end) else { - let item: GlucoseEffect? = nil // FIXME: we get a compiler error if we try to return `nil` directly + let item: LoopAlgorithm.GlucoseEffect? = nil // FIXME: we get a compiler error if we try to return `nil` directly return item } - return GlucoseEffect(startDate: effect.endDate.dateCeiledToTimeInterval(delta), - quantity: effect.quantity) + return LoopAlgorithm.GlucoseEffect(startDate: effect.endDate.dateCeiledToTimeInterval(delta), + mgdl: effect.mgdl) } for effect in processedICE { - let value = effect.quantity.doubleValue(for: unit) + let value = effect.mgdl effectValueCache[effect.startDate] = (effectValueCache[effect.startDate] ?? 0) + value } @@ -278,9 +278,9 @@ class MealDetectionManager { /// - carbEffects: the effects of any active carb entries. Must include effects from `currentDate() - MissedMealSettings.maxRecency` until `currentDate()`. func generateMissedMealNotificationIfNeeded( at date: Date, - glucoseSamples: [some GlucoseSampleValue], - insulinCounteractionEffects: [GlucoseEffectVelocity], - carbEffects: [GlucoseEffect], + glucoseSamples: [some LoopAlgorithm.GlucoseSampleValue], + insulinCounteractionEffects: [LoopAlgorithm.GlucoseEffectVelocity], + carbEffects: [LoopAlgorithm.GlucoseEffect], sensitivitySchedule: InsulinSensitivitySchedule, carbRatioSchedule: CarbRatioSchedule, maxBolus: Double @@ -370,7 +370,7 @@ class MealDetectionManager { } } -fileprivate extension BidirectionalCollection where Element: GlucoseSampleValue, Index == Int { +fileprivate extension BidirectionalCollection where Element: LoopAlgorithm.GlucoseSampleValue, Index == Int { /// Returns whether there are any user-entered or calibration points /// Runtime: O(n) func containsUserEntered() -> Bool { @@ -387,9 +387,9 @@ extension BolusStateProvider { } } -extension GlucoseEffectVelocity { +extension LoopAlgorithm.GlucoseEffectVelocity { /// The integration of the velocity span from `start` to `end` - public func effect(from start: Date, to end: Date) -> GlucoseEffect? { + public func effect(from start: Date, to end: Date) -> LoopAlgorithm.GlucoseEffect? { guard start <= end, startDate <= start, @@ -399,15 +399,7 @@ extension GlucoseEffectVelocity { } let duration = end.timeIntervalSince(start) - let velocityPerSecond = quantity.doubleValue(for: GlucoseEffectVelocity.perSecondUnit) - - return GlucoseEffect( - startDate: end, - quantity: LoopQuantity( - unit: .milligramsPerDeciliter, - doubleValue: velocityPerSecond * duration - ) - ) + return LoopAlgorithm.GlucoseEffect(startDate: end, mgdl: mgdlPerSecond * duration) } } diff --git a/Loop/Managers/RemoteDataServicesManager.swift b/Loop/Managers/RemoteDataServicesManager.swift index b28583fb66..1e3a8c6a4b 100644 --- a/Loop/Managers/RemoteDataServicesManager.swift +++ b/Loop/Managers/RemoteDataServicesManager.swift @@ -8,7 +8,6 @@ import os.log import Foundation -import LoopAlgorithm import LoopKit import UIKit diff --git a/Loop/Managers/SettingsManager.swift b/Loop/Managers/SettingsManager.swift index 466581b765..66c7aa2c9f 100644 --- a/Loop/Managers/SettingsManager.swift +++ b/Loop/Managers/SettingsManager.swift @@ -14,7 +14,6 @@ import Combine import LoopCore import LoopKitUI import os.log -import LoopAlgorithm @MainActor diff --git a/Loop/Managers/StatusChartsManager.swift b/Loop/Managers/StatusChartsManager.swift index a57b52d970..e7a9df2dfd 100644 --- a/Loop/Managers/StatusChartsManager.swift +++ b/Loop/Managers/StatusChartsManager.swift @@ -10,7 +10,6 @@ import LoopKit import LoopUI import LoopKitUI import SwiftCharts -import LoopAlgorithm class StatusChartsManager: ChartsManager { diff --git a/Loop/Managers/Store Protocols/DoseStoreProtocol.swift b/Loop/Managers/Store Protocols/DoseStoreProtocol.swift index 0d0d11d6a9..6de9aa59b2 100644 --- a/Loop/Managers/Store Protocols/DoseStoreProtocol.swift +++ b/Loop/Managers/Store Protocols/DoseStoreProtocol.swift @@ -8,7 +8,6 @@ import LoopKit import HealthKit -import LoopAlgorithm protocol DoseStoreProtocol: AnyObject { func getNormalizedDoseEntries(start: Date, end: Date?) async throws -> [DoseEntry] diff --git a/Loop/Managers/Store Protocols/DosingDecisionStoreProtocol.swift b/Loop/Managers/Store Protocols/DosingDecisionStoreProtocol.swift index 401dfb7e31..7451b237a2 100644 --- a/Loop/Managers/Store Protocols/DosingDecisionStoreProtocol.swift +++ b/Loop/Managers/Store Protocols/DosingDecisionStoreProtocol.swift @@ -6,7 +6,6 @@ // Copyright © 2020 LoopKit Authors. All rights reserved. // -import LoopAlgorithm import LoopKit struct LightDosingDecision: DosingDecision { diff --git a/Loop/Managers/Store Protocols/GlucoseStoreProtocol.swift b/Loop/Managers/Store Protocols/GlucoseStoreProtocol.swift index 915f0016f7..c1600d165b 100644 --- a/Loop/Managers/Store Protocols/GlucoseStoreProtocol.swift +++ b/Loop/Managers/Store Protocols/GlucoseStoreProtocol.swift @@ -7,7 +7,6 @@ // import LoopKit -import LoopAlgorithm protocol GlucoseStoreProtocol: AnyObject { var latestGlucose: GlucoseSampleValue? { get } diff --git a/Loop/Managers/TemporaryPresetsManager.swift b/Loop/Managers/TemporaryPresetsManager.swift index 0b71d95dc3..19729534cc 100644 --- a/Loop/Managers/TemporaryPresetsManager.swift +++ b/Loop/Managers/TemporaryPresetsManager.swift @@ -10,7 +10,6 @@ import Foundation import LoopKit import os.log import LoopCore -import LoopAlgorithm @MainActor protocol PresetActivationObserver: AnyObject { diff --git a/Loop/Managers/WatchDataManager.swift b/Loop/Managers/WatchDataManager.swift index 77d42ba3dd..fd9c270140 100644 --- a/Loop/Managers/WatchDataManager.swift +++ b/Loop/Managers/WatchDataManager.swift @@ -9,7 +9,6 @@ import HealthKit import UIKit import WatchConnectivity -import LoopAlgorithm import LoopKit import LoopCore @@ -386,11 +385,11 @@ final class WatchDataManager: NSObject { // Drop the first element in predictedGlucose because it is the current glucose let filteredPredictedGlucose = predictedGlucose.dropFirst() if filteredPredictedGlucose.count > 0 { - context.predictedGlucose = WatchPredictedGlucose(values: Array(filteredPredictedGlucose)) + context.predictedGlucose = WatchPredictedGlucose(values: filteredPredictedGlucose.map(\.loopKit)) } } - dosingDecision.predictedGlucose = algoOutput?.predictedGlucose + dosingDecision.predictedGlucose = algoOutput?.predictedGlucose.map(\.loopKit) var preMealOverride = self.temporaryPresetsManager.preMealOverride if preMealOverride?.hasFinished() == true { diff --git a/Loop/Models/AlgorithmBridge.swift b/Loop/Models/AlgorithmBridge.swift new file mode 100644 index 0000000000..db816d1b90 --- /dev/null +++ b/Loop/Models/AlgorithmBridge.swift @@ -0,0 +1,176 @@ +// +// AlgorithmBridge.swift +// Loop +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import Foundation +import LoopKit +import LoopAlgorithm +import LoopKitUI + +// LoopKit's stored values, given to LoopAlgorithm in its fixed units. + +extension StoredCarbEntry: @retroactive LoopAlgorithm.CarbEntry { + public var grams: Double { quantity.doubleValue(for: .gram) } + public var endDate: Date { startDate } +} + +extension StoredGlucoseSample: @retroactive LoopAlgorithm.GlucoseSampleValue { + public var mgdl: Double { quantity.doubleValue(for: .milligramsPerDeciliter) } + public var endDate: Date { startDate } +} + +// These types conform to both modules' TimelineValue, so the protocols' shared helpers are +// ambiguous on them; these concrete versions take precedence. +extension Array where Element == StoredGlucoseSample { + func filterDateRange(_ startDate: Date?, _ endDate: Date?) -> [StoredGlucoseSample] { + filter { (startDate == nil || $0.endDate >= startDate!) && (endDate == nil || $0.startDate <= endDate!) } + } +} + +extension Array where Element == StoredCarbEntry { + func filterDateRange(_ startDate: Date?, _ endDate: Date?) -> [StoredCarbEntry] { + filter { (startDate == nil || $0.endDate >= startDate!) && (endDate == nil || $0.startDate <= endDate!) } + } +} + +struct AlgorithmInsulinModel: LoopAlgorithm.InsulinModel { + let model: LoopKit.InsulinModel + + init(_ model: LoopKit.InsulinModel) { + self.model = model + } + + func percentEffectRemaining(at time: TimeInterval) -> Double { + model.percentEffectRemaining(at: time) + } + + var effectDuration: TimeInterval { model.effectDuration } + + var delay: TimeInterval { model.delay } +} + +extension Array { + func algorithmSchedule() -> [LoopAlgorithm.AbsoluteScheduleValue] where Element == LoopKit.AbsoluteScheduleValue { + map { LoopAlgorithm.AbsoluteScheduleValue(startDate: $0.startDate, endDate: $0.endDate, value: $0.value) } + } + + /// Glucose quantities (or sensitivities) in mg/dL + func algorithmMgdlSchedule() -> [LoopAlgorithm.AbsoluteScheduleValue] where Element == LoopKit.AbsoluteScheduleValue { + map { LoopAlgorithm.AbsoluteScheduleValue(startDate: $0.startDate, endDate: $0.endDate, value: $0.value.doubleValue(for: .milligramsPerDeciliter)) } + } + + func algorithmMgdlRanges() -> LoopAlgorithm.GlucoseRangeTimeline where Element == LoopKit.AbsoluteScheduleValue> { + map { + LoopAlgorithm.AbsoluteScheduleValue(startDate: $0.startDate, endDate: $0.endDate, + value: $0.value.lowerBound.doubleValue(for: .milligramsPerDeciliter)...$0.value.upperBound.doubleValue(for: .milligramsPerDeciliter)) + } + } +} + +// LoopAlgorithm's results, as LoopKit values for storage and charts. + +extension LoopAlgorithm.GlucoseEffect { + var loopKit: LoopKit.GlucoseEffect { + LoopKit.GlucoseEffect(startDate: startDate, quantity: LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: mgdl)) + } +} + +extension LoopAlgorithm.PredictedGlucoseValue { + var loopKit: LoopKit.PredictedGlucoseValue { + LoopKit.PredictedGlucoseValue(startDate: startDate, quantity: LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: mgdl)) + } +} + +extension LoopAlgorithm.GlucoseEffectVelocity { + var loopKit: LoopKit.GlucoseEffectVelocity { + LoopKit.GlucoseEffectVelocity(startDate: startDate, endDate: endDate, + quantity: LoopQuantity(unit: .milligramsPerDeciliterPerSecond, doubleValue: mgdlPerSecond)) + } +} + +extension LoopAlgorithm.CarbValue { + var loopKit: LoopKit.CarbValue { + LoopKit.CarbValue(startDate: startDate, endDate: endDate, value: value) + } +} + +extension LoopAlgorithm.InsulinValue { + var loopKit: LoopKit.InsulinValue { + LoopKit.InsulinValue(startDate: startDate, value: value) + } +} + +extension LoopAlgorithm.BasalRelativeDose { + var chartedDose: ChartedDose { + let type: ChartedDose.DoseType + switch self.type { + case .bolus: type = .bolus + case .basal: type = .basal + } + return ChartedDose(type: type, startDate: startDate, endDate: endDate, volume: volume, netBasalUnits: netBasalUnits) + } +} + +extension LoopAlgorithm.GlucoseChange { + var loopKit: LoopKit.GlucoseChange { + LoopKit.GlucoseChange(startDate: startDate, endDate: endDate, quantity: LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: mgdl)) + } +} + +extension LoopAlgorithm.SimpleGlucoseValue { + var loopKit: LoopKit.SimpleGlucoseValue { + LoopKit.SimpleGlucoseValue(startDate: startDate, endDate: endDate, quantity: LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: mgdl)) + } +} + +extension LoopAlgorithm.TempBasalRecommendation { + var loopKit: LoopKit.TempBasalRecommendation { + LoopKit.TempBasalRecommendation(unitsPerHour: unitsPerHour, duration: duration) + } +} + +extension LoopAlgorithm.AutomaticDoseRecommendation { + var loopKit: LoopKit.AutomaticDoseRecommendation { + LoopKit.AutomaticDoseRecommendation(basalAdjustment: basalAdjustment.loopKit, + direction: LoopKit.AutomaticDoseRecommendation.Direction(rawValue: direction.rawValue)!, + bolusUnits: bolusUnits) + } +} + +extension LoopAlgorithm.BolusRecommendationNotice { + var loopKit: LoopKit.BolusRecommendationNotice { + switch self { + case .glucoseBelowSuspendThreshold(let minGlucose): + return .glucoseBelowSuspendThreshold(minGlucose: minGlucose.loopKit) + case .currentGlucoseBelowTarget(let glucose): + return .currentGlucoseBelowTarget(glucose: glucose.loopKit) + case .predictedGlucoseBelowTarget(let minGlucose): + return .predictedGlucoseBelowTarget(minGlucose: minGlucose.loopKit) + case .predictedGlucoseInRange: + return .predictedGlucoseInRange + case .allGlucoseBelowTarget(let minGlucose): + return .allGlucoseBelowTarget(minGlucose: minGlucose.loopKit) + } + } +} + +extension LoopAlgorithm.ManualBolusRecommendation { + var loopKit: LoopKit.ManualBolusRecommendation { + LoopKit.ManualBolusRecommendation(amount: amount, notice: notice?.loopKit) + } +} + +extension Array { + func loopKitSchedule() -> [LoopKit.AbsoluteScheduleValue] where Element == LoopAlgorithm.AbsoluteScheduleValue { + map { LoopKit.AbsoluteScheduleValue(startDate: $0.startDate, endDate: $0.endDate, value: $0.value) } + } +} + +extension LoopKit.TempBasalRecommendation { + var algorithm: LoopAlgorithm.TempBasalRecommendation { + LoopAlgorithm.TempBasalRecommendation(unitsPerHour: unitsPerHour, duration: duration) + } +} diff --git a/Loop/Models/AutomationHistoryEntry.swift b/Loop/Models/AutomationHistoryEntry.swift index 4a989a67e8..55ef3cedcf 100644 --- a/Loop/Models/AutomationHistoryEntry.swift +++ b/Loop/Models/AutomationHistoryEntry.swift @@ -7,7 +7,7 @@ // import Foundation -import LoopAlgorithm +import LoopKit public struct AutomationHistoryEntry: Codable, Hashable { var startDate: Date diff --git a/Loop/Models/BolusDosingDecision.swift b/Loop/Models/BolusDosingDecision.swift index d9b8a47073..c45317720b 100644 --- a/Loop/Models/BolusDosingDecision.swift +++ b/Loop/Models/BolusDosingDecision.swift @@ -7,7 +7,6 @@ // import LoopKit -import LoopAlgorithm struct BolusDosingDecision { enum Reason: String { diff --git a/Loop/Models/CarbEntryChange.swift b/Loop/Models/CarbEntryChange.swift index a858fa4a5b..155ad34b53 100644 --- a/Loop/Models/CarbEntryChange.swift +++ b/Loop/Models/CarbEntryChange.swift @@ -6,7 +6,6 @@ // import Foundation -import LoopAlgorithm import LoopKit /// A carb entry about to be saved: a new entry, or new content for a stored one. diff --git a/Loop/Models/CrashRecoveryManager.swift b/Loop/Models/CrashRecoveryManager.swift index 5df015dac9..94d984d863 100644 --- a/Loop/Models/CrashRecoveryManager.swift +++ b/Loop/Models/CrashRecoveryManager.swift @@ -8,7 +8,6 @@ import Foundation import LoopKit -import LoopAlgorithm class CrashRecoveryManager { diff --git a/Loop/Models/Deeplink.swift b/Loop/Models/Deeplink.swift index aaede07661..c19023f04c 100644 --- a/Loop/Models/Deeplink.swift +++ b/Loop/Models/Deeplink.swift @@ -7,7 +7,7 @@ // import Foundation -import LoopAlgorithm +import LoopKit enum Deeplink: Hashable { enum Host: String, CaseIterable { diff --git a/Loop/Models/GlucoseDisplay.swift b/Loop/Models/GlucoseDisplay.swift index fe81a05b34..79720e9f1a 100644 --- a/Loop/Models/GlucoseDisplay.swift +++ b/Loop/Models/GlucoseDisplay.swift @@ -7,7 +7,6 @@ // import Foundation -import LoopAlgorithm import LoopKit struct GlucoseDisplay: GlucoseDisplayable { diff --git a/Loop/Models/GlucoseEffectVelocity.swift b/Loop/Models/GlucoseEffectVelocity.swift index 6a731611bf..a93f3335fa 100644 --- a/Loop/Models/GlucoseEffectVelocity.swift +++ b/Loop/Models/GlucoseEffectVelocity.swift @@ -6,7 +6,6 @@ // import LoopKit -import LoopAlgorithm extension GlucoseEffectVelocity: @retroactive RawRepresentable { diff --git a/Loop/Models/GlucoseStatistics.swift b/Loop/Models/GlucoseStatistics.swift index 7e832c3723..c321b6d854 100644 --- a/Loop/Models/GlucoseStatistics.swift +++ b/Loop/Models/GlucoseStatistics.swift @@ -7,7 +7,6 @@ import Foundation import LoopKit -import LoopAlgorithm /// Glucose ranges for the time-in-range breakdown, using the international /// consensus thresholds (mg/dL) shared by the AGP report: diff --git a/Loop/Models/InsulinDeliveryHistory.swift b/Loop/Models/InsulinDeliveryHistory.swift index 72603ebd81..55febcdbf1 100644 --- a/Loop/Models/InsulinDeliveryHistory.swift +++ b/Loop/Models/InsulinDeliveryHistory.swift @@ -7,7 +7,6 @@ import Foundation import LoopKit -import LoopAlgorithm struct InsulinDeliveryTotals: Equatable { var basal: Double = 0 diff --git a/Loop/Models/InsulinDeliveryLogEvent.swift b/Loop/Models/InsulinDeliveryLogEvent.swift index 9305601a9a..b7855b4d18 100644 --- a/Loop/Models/InsulinDeliveryLogEvent.swift +++ b/Loop/Models/InsulinDeliveryLogEvent.swift @@ -8,7 +8,6 @@ import Foundation import LoopKit -import LoopAlgorithm struct InsulinDeliveryLogEvent: Hashable, Identifiable { enum EventType: Hashable { diff --git a/Loop/Models/LoopConstants.swift b/Loop/Models/LoopConstants.swift index 42b38b273b..a7b484475f 100644 --- a/Loop/Models/LoopConstants.swift +++ b/Loop/Models/LoopConstants.swift @@ -7,7 +7,6 @@ // import Foundation -import LoopAlgorithm import LoopKit import UIKit diff --git a/Loop/Models/ManualBolusRecommendation.swift b/Loop/Models/ManualBolusRecommendation.swift index 4594bae7f8..879c59c53d 100644 --- a/Loop/Models/ManualBolusRecommendation.swift +++ b/Loop/Models/ManualBolusRecommendation.swift @@ -8,7 +8,6 @@ import Foundation import LoopKit -import LoopAlgorithm extension BolusRecommendationNotice { diff --git a/Loop/Models/NetBasal.swift b/Loop/Models/NetBasal.swift index 02a349a602..ff11e9e064 100644 --- a/Loop/Models/NetBasal.swift +++ b/Loop/Models/NetBasal.swift @@ -8,7 +8,6 @@ import Foundation import LoopKit -import LoopAlgorithm /// Max basal should generally be set, but in those cases where it isn't just use 3.0U/hr as a default top of scale, so we can show *something*. fileprivate let defaultMaxBasalForScale = 3.0 diff --git a/Loop/Models/SimpleBolusCalculator.swift b/Loop/Models/SimpleBolusCalculator.swift index 515aef3f5f..13562211d5 100644 --- a/Loop/Models/SimpleBolusCalculator.swift +++ b/Loop/Models/SimpleBolusCalculator.swift @@ -8,7 +8,6 @@ import Foundation import LoopCore -import LoopAlgorithm import LoopKit struct SimpleBolusCalculator { @@ -25,7 +24,7 @@ struct SimpleBolusCalculator { let sensitivity = sensitivitySchedule.quantity(at: date).doubleValue(for: .milligramsPerDeciliter) let correctionRange = correctionRangeSchedule.quantityRange(at: date) if (!correctionRange.contains(manualGlucose)) { - let correctionTarget = correctionRange.averageValue(for: .milligramsPerDeciliter) + let correctionTarget = (correctionRange.lowerBound.doubleValue(for: .milligramsPerDeciliter) + correctionRange.upperBound.doubleValue(for: .milligramsPerDeciliter)) / 2 let correctionBolus = (manualGlucose.doubleValue(for: .milligramsPerDeciliter) - correctionTarget) / sensitivity if correctionBolus >= 0 { let activeInsulin = max(0, activeInsulin.doubleValue(for: .internationalUnit)) diff --git a/Loop/Models/SimpleInsulinDose.swift b/Loop/Models/SimpleInsulinDose.swift index f0cd1048dc..c3963b47ce 100644 --- a/Loop/Models/SimpleInsulinDose.swift +++ b/Loop/Models/SimpleInsulinDose.swift @@ -19,7 +19,7 @@ struct SimpleInsulinDose: InsulinDose { var startDate: Date var endDate: Date var volume: Double - var insulinModel: InsulinModel + var insulinModel: LoopAlgorithm.InsulinModel } extension DoseEntry { @@ -36,14 +36,14 @@ extension DoseEntry { return deliveredUnits ?? programmedUnits } - func simpleDose(with model: InsulinModel) -> SimpleInsulinDose { + func simpleDose(with model: LoopKit.InsulinModel) -> SimpleInsulinDose { SimpleInsulinDose( deliveryType: deliveryType, automatic: automatic ?? false, startDate: startDate, endDate: endDate, volume: volume, - insulinModel: model + insulinModel: AlgorithmInsulinModel(model) ) } } diff --git a/Loop/Models/StoredDataAlgorithmInput.swift b/Loop/Models/StoredDataAlgorithmInput.swift index 8d8441c45a..1708a98a55 100644 --- a/Loop/Models/StoredDataAlgorithmInput.swift +++ b/Loop/Models/StoredDataAlgorithmInput.swift @@ -25,15 +25,15 @@ struct StoredDataAlgorithmInput: AlgorithmInput { var predictionStart: Date - var basal: [AbsoluteScheduleValue] + var basal: [LoopAlgorithm.AbsoluteScheduleValue] - var sensitivity: [AbsoluteScheduleValue] + var sensitivityMgdlPerUnit: [LoopAlgorithm.AbsoluteScheduleValue] - var carbRatio: [AbsoluteScheduleValue] + var carbRatio: [LoopAlgorithm.AbsoluteScheduleValue] - var target: GlucoseRangeTimeline + var targetMgdl: LoopAlgorithm.GlucoseRangeTimeline - var suspendThreshold: LoopQuantity? + var suspendThresholdMgdl: Double? var maxBolus: Double @@ -45,7 +45,7 @@ struct StoredDataAlgorithmInput: AlgorithmInput { var carbAbsorptionModel: CarbAbsorptionModel - var recommendationInsulinModel: InsulinModel + var recommendationInsulinModel: LoopAlgorithm.InsulinModel var recommendationType: DoseRecommendationType diff --git a/Loop/Models/WatchContext+LoopKit.swift b/Loop/Models/WatchContext+LoopKit.swift index 997124203d..b03f2b375d 100644 --- a/Loop/Models/WatchContext+LoopKit.swift +++ b/Loop/Models/WatchContext+LoopKit.swift @@ -8,7 +8,6 @@ import Foundation import LoopKit -import LoopAlgorithm import LoopCore extension WatchContext { diff --git a/Loop/View Controllers/CarbAbsorptionViewController.swift b/Loop/View Controllers/CarbAbsorptionViewController.swift index 3effa663a9..bcdbad11ef 100644 --- a/Loop/View Controllers/CarbAbsorptionViewController.swift +++ b/Loop/View Controllers/CarbAbsorptionViewController.swift @@ -9,10 +9,10 @@ import SwiftUI import Intents import LoopCore import LoopKit +import LoopAlgorithm import LoopKitUI import LoopUI import os.log -import LoopAlgorithm private extension RefreshContext { @@ -147,7 +147,7 @@ final class CarbAbsorptionViewController: LoopChartsTableViewController, Identif charts.updateEndDate(chartStartDate.addingTimeInterval(.hours(totalHours+1))) // When there is no data, this allows presenting current hour + 1 let midnight = Calendar.current.startOfDay(for: Date()) - let listStart = min(midnight, chartStartDate, Date(timeIntervalSinceNow: -CarbMath.maximumAbsorptionTimeInterval)) + let listStart = min(midnight, chartStartDate, Date(timeIntervalSinceNow: -LoopCoreConstants.maximumCarbAbsorptionTimeInterval)) let listEnd = Date().addingTimeInterval(LoopConstants.maxCarbEntryFutureTime) let shouldUpdateGlucose = currentContext.contains(.glucose) @@ -322,8 +322,8 @@ final class CarbAbsorptionViewController: LoopChartsTableViewController, Identif if let absorption = status.absorption { // Absorbed value - let observedProgress = Float(absorption.observedProgress.doubleValue(for: .percent)) - let observedCarbs = absorption.observed + let observedProgress = Float(absorption.observedFraction) + let observedCarbs = LoopQuantity(unit: .gram, doubleValue: absorption.observedGrams) if let observedCarbsText = carbFormatter.string(from: observedCarbs) { cell.observedValueText = String( @@ -341,7 +341,7 @@ final class CarbAbsorptionViewController: LoopChartsTableViewController, Identif } cell.observedProgress = observedProgress - cell.clampedProgress = Float(absorption.observedProgress.doubleValue(for: .percent)) + cell.clampedProgress = Float(absorption.observedFraction) cell.observedDateText = absorptionFormatter.string(from: absorption.estimatedDate.duration) // Absorbed time diff --git a/Loop/View Controllers/GlucoseThresholdTableViewController.swift b/Loop/View Controllers/GlucoseThresholdTableViewController.swift index cb6bbb7011..b26b4455d2 100644 --- a/Loop/View Controllers/GlucoseThresholdTableViewController.swift +++ b/Loop/View Controllers/GlucoseThresholdTableViewController.swift @@ -7,7 +7,6 @@ // import Foundation -import LoopAlgorithm import LoopKit import LoopKitUI import UIKit diff --git a/Loop/View Controllers/PredictionTableViewController.swift b/Loop/View Controllers/PredictionTableViewController.swift index c3ddaa7a78..134a33c863 100644 --- a/Loop/View Controllers/PredictionTableViewController.swift +++ b/Loop/View Controllers/PredictionTableViewController.swift @@ -8,11 +8,11 @@ import LoopCore import LoopKit +import LoopAlgorithm import LoopKitUI import LoopUI import UIKit import os.log -import LoopAlgorithm private extension RefreshContext { @@ -126,18 +126,18 @@ class PredictionTableViewController: LoopChartsTableViewController, Identifiable glucoseSamples = algoInput.glucoseHistory.filterDateRange(self.chartStartDate, nil) } - self.retrospectiveGlucoseDiscrepancies = algoOutput?.effects.retrospectiveGlucoseDiscrepancies - totalRetrospectiveCorrection = algoOutput?.effects.totalRetrospectiveCorrectionEffect + self.retrospectiveGlucoseDiscrepancies = algoOutput?.effects.retrospectiveGlucoseDiscrepancies.map(\.loopKit) + totalRetrospectiveCorrection = algoOutput?.effects.totalRetrospectiveCorrectionEffect.map { LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: $0) } self.guardAndRunway = algoOutput?.guardAndRunway - self.glucoseChart.setPredictedGlucoseValues(algoOutput?.predictedGlucose ?? []) + self.glucoseChart.setPredictedGlucoseValues((algoOutput?.predictedGlucose ?? []).map(\.loopKit)) do { var glucose = try algoInput?.predictGlucose(effectsOptions: self.selectedInputs.algorithmEffectOptions) ?? [] if self.selectedInputs.contains(.guardAndRunway), let guardAndRunway = self.guardAndRunway { glucose = guardAndRunway.guarded(glucose) } - self.glucoseChart.setAlternatePredictedGlucoseValues(glucose) + self.glucoseChart.setAlternatePredictedGlucoseValues(glucose.map(\.loopKit)) } catch { self.refreshContext.update(with: .status) self.glucoseChart.setAlternatePredictedGlucoseValues([]) diff --git a/Loop/View Controllers/StatusTableViewController.swift b/Loop/View Controllers/StatusTableViewController.swift index 70d95eebd3..ece45a5df3 100644 --- a/Loop/View Controllers/StatusTableViewController.swift +++ b/Loop/View Controllers/StatusTableViewController.swift @@ -18,7 +18,6 @@ import SwiftCharts import os.log import Combine import WidgetKit -import LoopAlgorithm private extension RefreshContext { static let all: Set = [.status, .glucose, .insulin, .carbs, .targets] @@ -546,7 +545,7 @@ final class StatusTableViewController: LoopChartsTableViewController { let automaticDosingEnabled = settingsManager.dosingEnabled let state = await loopManager.algorithmDisplayState - predictedGlucoseValues = state.output?.predictedGlucose ?? [] + predictedGlucoseValues = state.output?.predictedGlucose.map(\.loopKit) ?? [] /// Update the status HUDs immediately let lastLoopError: Error? diff --git a/Loop/View Models/BolusEntryViewModel.swift b/Loop/View Models/BolusEntryViewModel.swift index 18fa58e64e..064d49d45e 100644 --- a/Loop/View Models/BolusEntryViewModel.swift +++ b/Loop/View Models/BolusEntryViewModel.swift @@ -12,11 +12,11 @@ import Intents import os.log import LoopCore import LoopKit +import LoopAlgorithm import LoopKitUI import LoopUI import SwiftUI import SwiftCharts -import LoopAlgorithm @MainActor protocol BolusEntryViewModelDelegate: AnyObject { @@ -530,7 +530,7 @@ final class BolusEntryViewModel: ObservableObject { startDate: startDate, endDate: startDate, volume: enteredBolus.doubleValue(for: .internationalUnit), - insulinModel: insulinModel + insulinModel: AlgorithmInsulinModel(insulinModel) ) let (glucoseHistory, prediction) = try await delegate.generatePrediction( @@ -737,12 +737,12 @@ extension BolusEntryViewModel { var isGlucoseDataStale: Bool { guard let latestGlucoseDataDate = delegate?.mostRecentGlucoseDataDate else { return true } - return now().timeIntervalSince(latestGlucoseDataDate) > LoopAlgorithm.inputDataRecencyInterval + return now().timeIntervalSince(latestGlucoseDataDate) > LoopCoreConstants.inputDataRecencyInterval } var isPumpDataStale: Bool { guard let latestPumpDataDate = delegate?.mostRecentPumpDataDate else { return true } - return now().timeIntervalSince(latestPumpDataDate) > LoopAlgorithm.inputDataRecencyInterval + return now().timeIntervalSince(latestPumpDataDate) > LoopCoreConstants.inputDataRecencyInterval } var isManualGlucosePromptVisible: Bool { diff --git a/Loop/View Models/CarbEntryViewModel.swift b/Loop/View Models/CarbEntryViewModel.swift index 719a2daa22..7b84705527 100644 --- a/Loop/View Models/CarbEntryViewModel.swift +++ b/Loop/View Models/CarbEntryViewModel.swift @@ -10,7 +10,6 @@ import SwiftUI import LoopKit import Combine import LoopCore -import LoopAlgorithm import os.log @MainActor diff --git a/Loop/View Models/FavoriteFoodAddEditViewModel.swift b/Loop/View Models/FavoriteFoodAddEditViewModel.swift index b3f70fe3cf..1140db0df0 100644 --- a/Loop/View Models/FavoriteFoodAddEditViewModel.swift +++ b/Loop/View Models/FavoriteFoodAddEditViewModel.swift @@ -8,7 +8,6 @@ import SwiftUI import LoopKit -import LoopAlgorithm final class FavoriteFoodAddEditViewModel: ObservableObject { enum Alert: Identifiable { diff --git a/Loop/View Models/FavoriteFoodInsightsViewModel.swift b/Loop/View Models/FavoriteFoodInsightsViewModel.swift index 79337f5acf..04e87d13be 100644 --- a/Loop/View Models/FavoriteFoodInsightsViewModel.swift +++ b/Loop/View Models/FavoriteFoodInsightsViewModel.swift @@ -7,8 +7,8 @@ // import LoopKit -import LoopKitUI import LoopAlgorithm +import LoopKitUI import os.log import Combine diff --git a/Loop/View Models/FavoriteFoodsViewModel.swift b/Loop/View Models/FavoriteFoodsViewModel.swift index 4b425ccee7..6dfebb3fc9 100644 --- a/Loop/View Models/FavoriteFoodsViewModel.swift +++ b/Loop/View Models/FavoriteFoodsViewModel.swift @@ -7,7 +7,6 @@ // import SwiftUI -import LoopAlgorithm import LoopKit import Combine import os.log diff --git a/Loop/View Models/ManualEntryDoseViewModel.swift b/Loop/View Models/ManualEntryDoseViewModel.swift index c721e05814..da03bc0a5c 100644 --- a/Loop/View Models/ManualEntryDoseViewModel.swift +++ b/Loop/View Models/ManualEntryDoseViewModel.swift @@ -15,7 +15,6 @@ import LoopKit import LoopKitUI import LoopUI import SwiftUI -import LoopAlgorithm enum ManualEntryDoseViewModelError: Error { case notAuthenticated @@ -237,7 +236,7 @@ final class ManualEntryDoseViewModel: ObservableObject { startDate: selectedDoseDate, endDate: selectedDoseDate, volume: enteredBolus.doubleValue(for: .internationalUnit), - insulinModel: insulinModel + insulinModel: AlgorithmInsulinModel(insulinModel) ) self.activeInsulin = state.activeInsulin?.quantity @@ -250,7 +249,8 @@ final class ManualEntryDoseViewModel: ObservableObject { do { predictedGlucoseValues = try input .addingDose(dose: enteredBolusDose) - .predictGlucose() +.predictGlucose() + .map(\.loopKit) } catch { predictedGlucoseValues = [] } diff --git a/Loop/View Models/SettingsViewModel.swift b/Loop/View Models/SettingsViewModel.swift index 9c78a8c46c..626ae21e28 100644 --- a/Loop/View Models/SettingsViewModel.swift +++ b/Loop/View Models/SettingsViewModel.swift @@ -7,7 +7,6 @@ // import Combine -import LoopAlgorithm import LoopCore import LoopKit import LoopKitUI diff --git a/Loop/View Models/SimpleBolusViewModel.swift b/Loop/View Models/SimpleBolusViewModel.swift index 4053621700..2555bd5416 100644 --- a/Loop/View Models/SimpleBolusViewModel.swift +++ b/Loop/View Models/SimpleBolusViewModel.swift @@ -14,7 +14,6 @@ import SwiftUI import LoopCore import Intents import LocalAuthentication -import LoopAlgorithm @MainActor protocol SimpleBolusViewModelDelegate: AnyObject { diff --git a/Loop/View Models/StatisticsViewModel.swift b/Loop/View Models/StatisticsViewModel.swift index 575ffb49b4..5b386adf6c 100644 --- a/Loop/View Models/StatisticsViewModel.swift +++ b/Loop/View Models/StatisticsViewModel.swift @@ -7,7 +7,6 @@ import Foundation import LoopKit -import LoopAlgorithm @MainActor final class StatisticsViewModel: ObservableObject { diff --git a/Loop/Views/BolusEntryView.swift b/Loop/Views/BolusEntryView.swift index ef658d724b..6023a11e1c 100644 --- a/Loop/Views/BolusEntryView.swift +++ b/Loop/Views/BolusEntryView.swift @@ -7,7 +7,6 @@ // import Combine -import LoopAlgorithm import SwiftUI import LoopKit import LoopKitUI diff --git a/Loop/Views/BolusProgressTableViewCell.swift b/Loop/Views/BolusProgressTableViewCell.swift index f8d801da55..9d1d6f1071 100644 --- a/Loop/Views/BolusProgressTableViewCell.swift +++ b/Loop/Views/BolusProgressTableViewCell.swift @@ -9,7 +9,6 @@ import Foundation import LoopKit import LoopUI -import LoopAlgorithm import MKRingProgressView diff --git a/Loop/Views/Charts/CarbEffectChartView.swift b/Loop/Views/Charts/CarbEffectChartView.swift index 9a98113535..762253c6bb 100644 --- a/Loop/Views/Charts/CarbEffectChartView.swift +++ b/Loop/Views/Charts/CarbEffectChartView.swift @@ -9,7 +9,6 @@ import SwiftUI import LoopKit import LoopKitUI -import LoopAlgorithm struct CarbEffectChartView: View { let chartManager: ChartsManager diff --git a/Loop/Views/Charts/DoseChartView.swift b/Loop/Views/Charts/DoseChartView.swift index 9161436813..a76db12785 100644 --- a/Loop/Views/Charts/DoseChartView.swift +++ b/Loop/Views/Charts/DoseChartView.swift @@ -8,8 +8,8 @@ import SwiftUI import LoopKit -import LoopKitUI import LoopAlgorithm +import LoopKitUI struct DoseChartView: View { let chartManager: ChartsManager @@ -20,7 +20,7 @@ struct DoseChartView: View { var body: some View { LoopChartView(chartManager: chartManager, dateInterval: dateInterval, isInteractingWithChart: $isInteractingWithChart) { doseChart in - doseChart.doseEntries = doses + doseChart.doseEntries = doses.map(\.chartedDose) } } } diff --git a/Loop/Views/Charts/GlucoseCarbChartView.swift b/Loop/Views/Charts/GlucoseCarbChartView.swift index 70f1f5c4ad..6be5364304 100644 --- a/Loop/Views/Charts/GlucoseCarbChartView.swift +++ b/Loop/Views/Charts/GlucoseCarbChartView.swift @@ -9,7 +9,6 @@ import SwiftUI import LoopKit import LoopKitUI -import LoopAlgorithm struct GlucoseCarbChartView: View { let chartManager: ChartsManager diff --git a/Loop/Views/Charts/IOBChartView.swift b/Loop/Views/Charts/IOBChartView.swift index e164a42045..1909e4465a 100644 --- a/Loop/Views/Charts/IOBChartView.swift +++ b/Loop/Views/Charts/IOBChartView.swift @@ -9,7 +9,6 @@ import SwiftUI import LoopKit import LoopKitUI -import LoopAlgorithm struct IOBChartView: View { let chartManager: ChartsManager diff --git a/Loop/Views/Charts/PredictedGlucoseChartView.swift b/Loop/Views/Charts/PredictedGlucoseChartView.swift index e223a2c3cc..992870ece0 100644 --- a/Loop/Views/Charts/PredictedGlucoseChartView.swift +++ b/Loop/Views/Charts/PredictedGlucoseChartView.swift @@ -9,7 +9,6 @@ import SwiftUI import LoopKit import LoopKitUI -import LoopAlgorithm struct PredictedGlucoseChartView: View { let chartManager: ChartsManager diff --git a/Loop/Views/CurrentSetupView.swift b/Loop/Views/CurrentSetupView.swift index 6e1ead3add..18b737c22f 100644 --- a/Loop/Views/CurrentSetupView.swift +++ b/Loop/Views/CurrentSetupView.swift @@ -7,8 +7,8 @@ import SwiftUI import LoopKit -import LoopKitUI import LoopAlgorithm +import LoopKitUI struct CurrentSetupView: View { @StateObject private var viewModel: CurrentSetupViewModel @@ -269,6 +269,8 @@ extension ExperimentOption { return NSLocalizedString("Glucose Based Partial Application", comment: "Title of glucose based partial application experiment") case .integralRetrospectiveCorrection: return NSLocalizedString("Integral Retrospective Correction", comment: "Title of integral retrospective correction experiment") + case .slowRetrospectiveCorrection: + return NSLocalizedString("Slow Retrospective Correction", comment: "Title of slow retrospective correction experiment") } } } diff --git a/Loop/Views/Favorite Foods/FavoriteFoodInsightsChartsView.swift b/Loop/Views/Favorite Foods/FavoriteFoodInsightsChartsView.swift index 8a67b7c519..be6c9656b9 100644 --- a/Loop/Views/Favorite Foods/FavoriteFoodInsightsChartsView.swift +++ b/Loop/Views/Favorite Foods/FavoriteFoodInsightsChartsView.swift @@ -9,7 +9,6 @@ import SwiftUI import LoopKit import LoopKitUI -import LoopAlgorithm import Combine struct FavoriteFoodsInsightsChartsView: View { diff --git a/Loop/Views/Favorite Foods/FavoriteFoodInsightsView.swift b/Loop/Views/Favorite Foods/FavoriteFoodInsightsView.swift index 582a661f3a..f2403563f1 100644 --- a/Loop/Views/Favorite Foods/FavoriteFoodInsightsView.swift +++ b/Loop/Views/Favorite Foods/FavoriteFoodInsightsView.swift @@ -9,7 +9,6 @@ import SwiftUI import LoopKit import LoopKitUI -import LoopAlgorithm struct FavoriteFoodInsightsView: View { @StateObject private var viewModel: FavoriteFoodInsightsViewModel diff --git a/Loop/Views/GlucoseAlertSettingsView.swift b/Loop/Views/GlucoseAlertSettingsView.swift index 27f800355b..4a2913d7f2 100644 --- a/Loop/Views/GlucoseAlertSettingsView.swift +++ b/Loop/Views/GlucoseAlertSettingsView.swift @@ -9,7 +9,6 @@ import SwiftUI import AVFoundation -import LoopAlgorithm import LoopKit import LoopKitUI diff --git a/Loop/Views/Insulin Delivery Log/InsulinDeliveryEventDetailsView.swift b/Loop/Views/Insulin Delivery Log/InsulinDeliveryEventDetailsView.swift index 841e0f55b7..6b78459187 100644 --- a/Loop/Views/Insulin Delivery Log/InsulinDeliveryEventDetailsView.swift +++ b/Loop/Views/Insulin Delivery Log/InsulinDeliveryEventDetailsView.swift @@ -6,7 +6,6 @@ // Copyright © 2025 LoopKit Authors. All rights reserved. // -import LoopAlgorithm import LoopKit import SwiftUI diff --git a/Loop/Views/Insulin Delivery Log/InsulinDeliveryLog.swift b/Loop/Views/Insulin Delivery Log/InsulinDeliveryLog.swift index eeac82bee4..3f78360175 100644 --- a/Loop/Views/Insulin Delivery Log/InsulinDeliveryLog.swift +++ b/Loop/Views/Insulin Delivery Log/InsulinDeliveryLog.swift @@ -6,7 +6,6 @@ // Copyright © 2025 LoopKit Authors. All rights reserved. // -import LoopAlgorithm import LoopKit import LoopKitUI import SwiftUI diff --git a/Loop/Views/Insulin Delivery Log/InsulinDeliveryLogEventRow.swift b/Loop/Views/Insulin Delivery Log/InsulinDeliveryLogEventRow.swift index 17d02c99ed..4a8eef476b 100644 --- a/Loop/Views/Insulin Delivery Log/InsulinDeliveryLogEventRow.swift +++ b/Loop/Views/Insulin Delivery Log/InsulinDeliveryLogEventRow.swift @@ -6,7 +6,6 @@ // Copyright © 2025 LoopKit Authors. All rights reserved. // -import LoopAlgorithm import LoopKit import LoopKitUI import SwiftUI diff --git a/Loop/Views/Insulin Delivery Log/InsulinDeliveryLogViewModel.swift b/Loop/Views/Insulin Delivery Log/InsulinDeliveryLogViewModel.swift index 2a225f8de8..ca089e7b95 100644 --- a/Loop/Views/Insulin Delivery Log/InsulinDeliveryLogViewModel.swift +++ b/Loop/Views/Insulin Delivery Log/InsulinDeliveryLogViewModel.swift @@ -6,7 +6,6 @@ // Copyright © 2025 LoopKit Authors. All rights reserved. // -import LoopAlgorithm import LoopKit import os.log diff --git a/Loop/Views/Insulin Delivery Log/InsulinDeliveryOverview.swift b/Loop/Views/Insulin Delivery Log/InsulinDeliveryOverview.swift index 0d9a78cc77..61dd13855f 100644 --- a/Loop/Views/Insulin Delivery Log/InsulinDeliveryOverview.swift +++ b/Loop/Views/Insulin Delivery Log/InsulinDeliveryOverview.swift @@ -6,7 +6,6 @@ // Copyright © 2025 LoopKit Authors. All rights reserved. // -import LoopAlgorithm import LoopKit import SwiftUI import LoopCore diff --git a/Loop/Views/ManualEntryDoseView.swift b/Loop/Views/ManualEntryDoseView.swift index dabb704a04..443ae29eda 100644 --- a/Loop/Views/ManualEntryDoseView.swift +++ b/Loop/Views/ManualEntryDoseView.swift @@ -7,7 +7,6 @@ // import Combine -import LoopAlgorithm import SwiftUI import LoopKit import LoopKitUI diff --git a/Loop/Views/ManualGlucoseEntryRow.swift b/Loop/Views/ManualGlucoseEntryRow.swift index c3cda677fa..dd59bd09a9 100644 --- a/Loop/Views/ManualGlucoseEntryRow.swift +++ b/Loop/Views/ManualGlucoseEntryRow.swift @@ -8,7 +8,6 @@ import Foundation import SwiftUI -import LoopAlgorithm import LoopKit import LoopKitUI import Combine diff --git a/Loop/Views/Presets/Components/PresetDetentView.swift b/Loop/Views/Presets/Components/PresetDetentView.swift index 0c74ad9f03..96a8e384f2 100644 --- a/Loop/Views/Presets/Components/PresetDetentView.swift +++ b/Loop/Views/Presets/Components/PresetDetentView.swift @@ -10,7 +10,6 @@ import LoopKit import LoopKitUI import SwiftUI import LoopCore -import LoopAlgorithm struct PresetDetentView: View { diff --git a/Loop/Views/Presets/EditPresetRangeView.swift b/Loop/Views/Presets/EditPresetRangeView.swift index 14096ea9aa..3535cbe02f 100644 --- a/Loop/Views/Presets/EditPresetRangeView.swift +++ b/Loop/Views/Presets/EditPresetRangeView.swift @@ -6,7 +6,6 @@ // Copyright © 2024 LoopKit Authors. All rights reserved. // import SwiftUI -import LoopAlgorithm import LoopKit import LoopKitUI diff --git a/Loop/Views/Presets/PresetPerformanceHistoryView.swift b/Loop/Views/Presets/PresetPerformanceHistoryView.swift index c0eec2209d..fc8a0f800c 100644 --- a/Loop/Views/Presets/PresetPerformanceHistoryView.swift +++ b/Loop/Views/Presets/PresetPerformanceHistoryView.swift @@ -6,7 +6,6 @@ // Copyright © 2026 LoopKit Authors. All rights reserved. // -import LoopAlgorithm import LoopKit import LoopKitUI import SwiftUI diff --git a/Loop/Views/Presets/PresetsHistoryView.swift b/Loop/Views/Presets/PresetsHistoryView.swift index 4d55c29b41..519fa620c4 100644 --- a/Loop/Views/Presets/PresetsHistoryView.swift +++ b/Loop/Views/Presets/PresetsHistoryView.swift @@ -6,7 +6,6 @@ // Copyright © 2024 LoopKit Authors. All rights reserved. // -import LoopAlgorithm import LoopKit import LoopKitUI import SwiftUI diff --git a/Loop/Views/Presets/PresetsPerformanceHistoryViewModel.swift b/Loop/Views/Presets/PresetsPerformanceHistoryViewModel.swift index 111f6e27f2..6a00cfffd0 100644 --- a/Loop/Views/Presets/PresetsPerformanceHistoryViewModel.swift +++ b/Loop/Views/Presets/PresetsPerformanceHistoryViewModel.swift @@ -6,7 +6,6 @@ // Copyright © 2026 LoopKit Authors. All rights reserved. // -import LoopAlgorithm import LoopKit @MainActor diff --git a/Loop/Views/Presets/PresetsView.swift b/Loop/Views/Presets/PresetsView.swift index db474c6340..6a8fdcf9e5 100644 --- a/Loop/Views/Presets/PresetsView.swift +++ b/Loop/Views/Presets/PresetsView.swift @@ -6,7 +6,6 @@ // Copyright © 2024 LoopKit Authors. All rights reserved. // -import LoopAlgorithm import LoopKit import LoopKitUI import SwiftUI diff --git a/Loop/Views/Presets/Training/Components/TherapySettingsExampleView.swift b/Loop/Views/Presets/Training/Components/TherapySettingsExampleView.swift index ec4593809e..9e5cbe786e 100644 --- a/Loop/Views/Presets/Training/Components/TherapySettingsExampleView.swift +++ b/Loop/Views/Presets/Training/Components/TherapySettingsExampleView.swift @@ -6,7 +6,6 @@ // Copyright © 2024 LoopKit Authors. All rights reserved. // -import LoopAlgorithm import LoopKit import LoopKitUI import SwiftUI diff --git a/Loop/Views/Presets/Training/PresetsTrainingContent.swift b/Loop/Views/Presets/Training/PresetsTrainingContent.swift index cff2c42de2..eb1a4a13a0 100644 --- a/Loop/Views/Presets/Training/PresetsTrainingContent.swift +++ b/Loop/Views/Presets/Training/PresetsTrainingContent.swift @@ -6,7 +6,6 @@ // Copyright © 2025 LoopKit Authors. All rights reserved. // -import LoopAlgorithm import LoopKit import LoopKitUI import SwiftUI diff --git a/Loop/Views/SimpleBolusView.swift b/Loop/Views/SimpleBolusView.swift index a26032029f..736f75c278 100644 --- a/Loop/Views/SimpleBolusView.swift +++ b/Loop/Views/SimpleBolusView.swift @@ -10,7 +10,6 @@ import SwiftUI import LoopKit import LoopKitUI import LoopCore -import LoopAlgorithm struct SimpleBolusView: View { @EnvironmentObject private var displayGlucosePreference: DisplayGlucosePreference diff --git a/Loop/Views/Statistics/AGPChartView.swift b/Loop/Views/Statistics/AGPChartView.swift index 53db129c10..55bac2b930 100644 --- a/Loop/Views/Statistics/AGPChartView.swift +++ b/Loop/Views/Statistics/AGPChartView.swift @@ -7,7 +7,6 @@ import SwiftUI import LoopKit -import LoopAlgorithm import LoopUI /// The ambulatory glucose profile, styled after the IDC / captūrAGP report. diff --git a/Loop/Views/Statistics/SetupComparisonCharts.swift b/Loop/Views/Statistics/SetupComparisonCharts.swift index 3c078947d8..d6e104f0f6 100644 --- a/Loop/Views/Statistics/SetupComparisonCharts.swift +++ b/Loop/Views/Statistics/SetupComparisonCharts.swift @@ -7,7 +7,6 @@ import SwiftUI import LoopKit -import LoopAlgorithm /// A horizontal time-in-range bar for one period, lows on the left. struct TimeInRangeStripe: View { diff --git a/Loop/Views/Statistics/StatisticsView.swift b/Loop/Views/Statistics/StatisticsView.swift index 23b41652fc..1befaa5062 100644 --- a/Loop/Views/Statistics/StatisticsView.swift +++ b/Loop/Views/Statistics/StatisticsView.swift @@ -8,7 +8,6 @@ import SwiftUI import LoopKit import LoopKitUI -import LoopAlgorithm import LoopUI /// "How am I doing?" overview — an Ambulatory Glucose Profile report: summary diff --git a/Loop/Views/Statistics/TimeInRangeBar.swift b/Loop/Views/Statistics/TimeInRangeBar.swift index 29d83cbe47..171c28b041 100644 --- a/Loop/Views/Statistics/TimeInRangeBar.swift +++ b/Loop/Views/Statistics/TimeInRangeBar.swift @@ -6,7 +6,7 @@ // import SwiftUI -import LoopAlgorithm +import LoopKit import LoopKitUI import LoopUI diff --git a/Loop/Views/StatusTableView.swift b/Loop/Views/StatusTableView.swift index be4012a8f2..a09e8b9690 100644 --- a/Loop/Views/StatusTableView.swift +++ b/Loop/Views/StatusTableView.swift @@ -6,7 +6,6 @@ // Copyright © 2024 LoopKit Authors. All rights reserved. // -import LoopAlgorithm import LoopKit import LoopKitUI import SwiftUI diff --git a/LoopCore/LoopCoreConstants.swift b/LoopCore/LoopCoreConstants.swift index 6d8edfea82..0b94b11e70 100644 --- a/LoopCore/LoopCoreConstants.swift +++ b/LoopCore/LoopCoreConstants.swift @@ -15,6 +15,12 @@ public enum LoopCoreConstants { /// The amount of time in the future a glucose value should be considered valid public static let futureGlucoseDataInterval = TimeInterval(minutes: 5) + /// How old glucose and pump data can be for the algorithm to run; matches LoopAlgorithm's limit + public static let inputDataRecencyInterval = TimeInterval(minutes: 15) + + /// The longest a carb entry can take to absorb; matches LoopAlgorithm's limit + public static let maximumCarbAbsorptionTimeInterval = TimeInterval(hours: 10) + public static let defaultCarbAbsorptionTimes: DefaultAbsorptionTimes = (fast: .minutes(30), medium: .hours(3), slow: .hours(5)) /// How much historical glucose to include in a dosing decision diff --git a/LoopCore/LoopSettings.swift b/LoopCore/LoopSettings.swift index ba83a7aa55..43d1afa93a 100644 --- a/LoopCore/LoopSettings.swift +++ b/LoopCore/LoopSettings.swift @@ -7,7 +7,6 @@ import HealthKit import LoopKit -import LoopAlgorithm public extension AutomaticDosingStrategy { var title: String { diff --git a/LoopCore/Models/WatchContext.swift b/LoopCore/Models/WatchContext.swift index 9a0e37ad3b..a83fe31f63 100644 --- a/LoopCore/Models/WatchContext.swift +++ b/LoopCore/Models/WatchContext.swift @@ -8,7 +8,6 @@ import Foundation import LoopKit -import LoopAlgorithm public enum InsulinDeliveryWatchState: Int, Equatable { case neutralNoOverride diff --git a/LoopCore/Models/WatchPredictedGlucose.swift b/LoopCore/Models/WatchPredictedGlucose.swift index b17ca89c76..4f62763893 100644 --- a/LoopCore/Models/WatchPredictedGlucose.swift +++ b/LoopCore/Models/WatchPredictedGlucose.swift @@ -8,7 +8,6 @@ import Foundation import LoopKit -import LoopAlgorithm public struct WatchPredictedGlucose: Equatable { public let values: [PredictedGlucoseValue] diff --git a/LoopCore/NSUserDefaults.swift b/LoopCore/NSUserDefaults.swift index 8ed715f823..0975cefd9a 100644 --- a/LoopCore/NSUserDefaults.swift +++ b/LoopCore/NSUserDefaults.swift @@ -8,7 +8,6 @@ import Foundation import LoopKit -import LoopAlgorithm extension UserDefaults { diff --git a/LoopTests/Fixtures/insulin_math/basal.json b/LoopTests/Fixtures/insulin_math/basal.json new file mode 100644 index 0000000000..98f572dcef --- /dev/null +++ b/LoopTests/Fixtures/insulin_math/basal.json @@ -0,0 +1,44 @@ +[ + { + "i": 0, + "start": "00:00:00", + "rate": 0.9, + "minutes": 0 + }, + { + "i": 1, + "start": "04:00:00", + "rate": 0.925, + "minutes": 240 + }, + { + "i": 2, + "start": "07:00:00", + "rate": 0.85, + "minutes": 420 + }, + { + "i": 3, + "start": "10:00:00", + "rate": 0.85, + "minutes": 600 + }, + { + "i": 4, + "start": "12:00:00", + "rate": 0.75, + "minutes": 720 + }, + { + "i": 5, + "start": "15:00:00", + "rate": 0.8, + "minutes": 900 + }, + { + "i": 6, + "start": "22:00:00", + "rate": 0.9, + "minutes": 1320 + } +] \ No newline at end of file diff --git a/LoopTests/Fixtures/insulin_math/iob_from_doses_exponential_output.json b/LoopTests/Fixtures/insulin_math/iob_from_doses_exponential_output.json new file mode 100644 index 0000000000..6f86752281 --- /dev/null +++ b/LoopTests/Fixtures/insulin_math/iob_from_doses_exponential_output.json @@ -0,0 +1,642 @@ +[ + { + "date" : "2015-10-15T18:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2015-10-15T18:10:00", + "unit" : "U", + "value" : -0.018750000000000003 + }, + { + "date" : "2015-10-15T18:15:00", + "unit" : "U", + "value" : -0.020083333333333335 + }, + { + "date" : "2015-10-15T18:20:00", + "unit" : "U", + "value" : -0.08674967391190945 + }, + { + "date" : "2015-10-15T18:25:00", + "unit" : "U", + "value" : -0.1533638209815783 + }, + { + "date" : "2015-10-15T18:30:00", + "unit" : "U", + "value" : -0.21985157514541492 + }, + { + "date" : "2015-10-15T18:35:00", + "unit" : "U", + "value" : -0.28594868294984466 + }, + { + "date" : "2015-10-15T18:40:00", + "unit" : "U", + "value" : -0.35138913185798915 + }, + { + "date" : "2015-10-15T18:45:00", + "unit" : "U", + "value" : -0.41460789687197663 + }, + { + "date" : "2015-10-15T18:50:00", + "unit" : "U", + "value" : -0.41140454839697138 + }, + { + "date" : "2015-10-15T18:55:00", + "unit" : "U", + "value" : -0.40694042602624531 + }, + { + "date" : "2015-10-15T19:00:00", + "unit" : "U", + "value" : -0.40111368746478182 + }, + { + "date" : "2015-10-15T19:05:00", + "unit" : "U", + "value" : -0.45918724201570321 + }, + { + "date" : "2015-10-15T19:10:00", + "unit" : "U", + "value" : -0.51777937898327886 + }, + { + "date" : "2015-10-15T19:15:00", + "unit" : "U", + "value" : -0.57545506272901459 + }, + { + "date" : "2015-10-15T19:20:00", + "unit" : "U", + "value" : -0.67031079437724594 + }, + { + "date" : "2015-10-15T19:25:00", + "unit" : "U", + "value" : -0.65308790247022863 + }, + { + "date" : "2015-10-15T19:30:00", + "unit" : "U", + "value" : 4.5844563428505971 + }, + { + "date" : "2015-10-15T19:35:00", + "unit" : "U", + "value" : 4.8234744416481918 + }, + { + "date" : "2015-10-15T19:40:00", + "unit" : "U", + "value" : 5.0555348107831648 + }, + { + "date" : "2015-10-15T19:45:00", + "unit" : "U", + "value" : 5.2654029256034685 + }, + { + "date" : "2015-10-15T19:50:00", + "unit" : "U", + "value" : 5.4543457491633829 + }, + { + "date" : "2015-10-15T19:55:00", + "unit" : "U", + "value" : 5.5796818289595951 + }, + { + "date" : "2015-10-15T20:00:00", + "unit" : "U", + "value" : 5.7313776281043038 + }, + { + "date" : "2015-10-15T20:05:00", + "unit" : "U", + "value" : 5.866534076169672 + }, + { + "date" : "2015-10-15T20:10:00", + "unit" : "U", + "value" : 9.3515233597392591 + }, + { + "date" : "2015-10-15T20:15:00", + "unit" : "U", + "value" : 9.2639948506563528 + }, + { + "date" : "2015-10-15T20:20:00", + "unit" : "U", + "value" : 9.1250155710516587 + }, + { + "date" : "2015-10-15T20:25:00", + "unit" : "U", + "value" : 8.959613360663484 + }, + { + "date" : "2015-10-15T20:30:00", + "unit" : "U", + "value" : 8.7679394277654108 + }, + { + "date" : "2015-10-15T20:35:00", + "unit" : "U", + "value" : 8.4976351667316088 + }, + { + "date" : "2015-10-15T20:40:00", + "unit" : "U", + "value" : 8.2718009242472466 + }, + { + "date" : "2015-10-15T20:45:00", + "unit" : "U", + "value" : 7.9677397922764914 + }, + { + "date" : "2015-10-15T20:50:00", + "unit" : "U", + "value" : 7.6531423110771213 + }, + { + "date" : "2015-10-15T20:55:00", + "unit" : "U", + "value" : 7.3305121057107296 + }, + { + "date" : "2015-10-15T21:00:00", + "unit" : "U", + "value" : 7.0039882775409179 + }, + { + "date" : "2015-10-15T21:05:00", + "unit" : "U", + "value" : 6.8599232815908628 + }, + { + "date" : "2015-10-15T21:10:00", + "unit" : "U", + "value" : 6.6827268353420592 + }, + { + "date" : "2015-10-15T21:15:00", + "unit" : "U", + "value" : 6.4975323125878832 + }, + { + "date" : "2015-10-15T21:20:00", + "unit" : "U", + "value" : 6.2311950271168621 + }, + { + "date" : "2015-10-15T21:25:00", + "unit" : "U", + "value" : 5.9662610775540603 + }, + { + "date" : "2015-10-15T21:30:00", + "unit" : "U", + "value" : 5.703410306528899 + }, + { + "date" : "2015-10-15T21:35:00", + "unit" : "U", + "value" : 5.443549906835079 + }, + { + "date" : "2015-10-15T21:40:00", + "unit" : "U", + "value" : 9.1941718730890045 + }, + { + "date" : "2015-10-15T21:45:00", + "unit" : "U", + "value" : 8.9426578501594971 + }, + { + "date" : "2015-10-15T21:50:00", + "unit" : "U", + "value" : 8.6873901964495222 + }, + { + "date" : "2015-10-15T21:55:00", + "unit" : "U", + "value" : 8.419996897114423 + }, + { + "date" : "2015-10-15T22:00:00", + "unit" : "U", + "value" : 8.1495692963244348 + }, + { + "date" : "2015-10-15T22:05:00", + "unit" : "U", + "value" : 8.0510013404305631 + }, + { + "date" : "2015-10-15T22:10:00", + "unit" : "U", + "value" : 7.947479248003801 + }, + { + "date" : "2015-10-15T22:15:00", + "unit" : "U", + "value" : 7.8407880010764028 + }, + { + "date" : "2015-10-15T22:20:00", + "unit" : "U", + "value" : 7.7318648464436839 + }, + { + "date" : "2015-10-15T22:25:00", + "unit" : "U", + "value" : 7.621458400003899 + }, + { + "date" : "2015-10-15T22:30:00", + "unit" : "U", + "value" : 7.504058141533358 + }, + { + "date" : "2015-10-15T22:35:00", + "unit" : "U", + "value" : 7.2072063559501762 + }, + { + "date" : "2015-10-15T22:40:00", + "unit" : "U", + "value" : 6.9106671373789181 + }, + { + "date" : "2015-10-15T22:45:00", + "unit" : "U", + "value" : 6.6150483599313352 + }, + { + "date" : "2015-10-15T22:50:00", + "unit" : "U", + "value" : 6.3215294099560326 + }, + { + "date" : "2015-10-15T22:55:00", + "unit" : "U", + "value" : 6.0312390617306626 + }, + { + "date" : "2015-10-15T23:00:00", + "unit" : "U", + "value" : 5.7451618503011987 + }, + { + "date" : "2015-10-15T23:05:00", + "unit" : "U", + "value" : 5.4641508016258493 + }, + { + "date" : "2015-10-15T23:10:00", + "unit" : "U", + "value" : 5.1889392020133407 + }, + { + "date" : "2015-10-15T23:15:00", + "unit" : "U", + "value" : 4.9201514731443847 + }, + { + "date" : "2015-10-15T23:20:00", + "unit" : "U", + "value" : 4.6583132146527513 + }, + { + "date" : "2015-10-15T23:25:00", + "unit" : "U", + "value" : 4.4038604721990744 + }, + { + "date" : "2015-10-15T23:30:00", + "unit" : "U", + "value" : 4.157148285179713 + }, + { + "date" : "2015-10-15T23:35:00", + "unit" : "U", + "value" : 3.9184585646600665 + }, + { + "date" : "2015-10-15T23:40:00", + "unit" : "U", + "value" : 3.6880073487913116 + }, + { + "date" : "2015-10-15T23:45:00", + "unit" : "U", + "value" : 3.4659514798492381 + }, + { + "date" : "2015-10-15T23:50:00", + "unit" : "U", + "value" : 3.2523947441097145 + }, + { + "date" : "2015-10-15T23:55:00", + "unit" : "U", + "value" : 3.0473935130363934 + }, + { + "date" : "2015-10-16T00:00:00", + "unit" : "U", + "value" : 2.8509619216906583 + }, + { + "date" : "2015-10-16T00:05:00", + "unit" : "U", + "value" : 2.6630766178710337 + }, + { + "date" : "2015-10-16T00:10:00", + "unit" : "U", + "value" : 2.4836811132395682 + }, + { + "date" : "2015-10-16T00:15:00", + "unit" : "U", + "value" : 2.3126897655863949 + }, + { + "date" : "2015-10-16T00:20:00", + "unit" : "U", + "value" : 2.1499914289175757 + }, + { + "date" : "2015-10-16T00:25:00", + "unit" : "U", + "value" : 1.9954542992700548 + }, + { + "date" : "2015-10-16T00:30:00", + "unit" : "U", + "value" : 1.8489281662494723 + }, + { + "date" : "2015-10-16T00:35:00", + "unit" : "U", + "value" : 1.7102522167447076 + }, + { + "date" : "2015-10-16T00:40:00", + "unit" : "U", + "value" : 1.5792522393334008 + }, + { + "date" : "2015-10-16T00:45:00", + "unit" : "U", + "value" : 1.4557417966735475 + }, + { + "date" : "2015-10-16T00:50:00", + "unit" : "U", + "value" : 1.3395243199229128 + }, + { + "date" : "2015-10-16T00:55:00", + "unit" : "U", + "value" : 1.2303950054342776 + }, + { + "date" : "2015-10-16T01:00:00", + "unit" : "U", + "value" : 1.1281412265192725 + }, + { + "date" : "2015-10-16T01:05:00", + "unit" : "U", + "value" : 1.0325384190273319 + }, + { + "date" : "2015-10-16T01:10:00", + "unit" : "U", + "value" : 0.94335795605105055 + }, + { + "date" : "2015-10-16T01:15:00", + "unit" : "U", + "value" : 0.86037059905713076 + }, + { + "date" : "2015-10-16T01:20:00", + "unit" : "U", + "value" : 0.78335347466781946 + }, + { + "date" : "2015-10-16T01:25:00", + "unit" : "U", + "value" : 0.71208475485032652 + }, + { + "date" : "2015-10-16T01:30:00", + "unit" : "U", + "value" : 0.64634339612662106 + }, + { + "date" : "2015-10-16T01:35:00", + "unit" : "U", + "value" : 0.58591097891387478 + }, + { + "date" : "2015-10-16T01:40:00", + "unit" : "U", + "value" : 0.53031810327106477 + }, + { + "date" : "2015-10-16T01:45:00", + "unit" : "U", + "value" : 0.47887549890416919 + }, + { + "date" : "2015-10-16T01:50:00", + "unit" : "U", + "value" : 0.43136991580673895 + }, + { + "date" : "2015-10-16T01:55:00", + "unit" : "U", + "value" : 0.38760420145484975 + }, + { + "date" : "2015-10-16T02:00:00", + "unit" : "U", + "value" : 0.34738572960367692 + }, + { + "date" : "2015-10-16T02:05:00", + "unit" : "U", + "value" : 0.31052608873297527 + }, + { + "date" : "2015-10-16T02:10:00", + "unit" : "U", + "value" : 0.27684765577284548 + }, + { + "date" : "2015-10-16T02:15:00", + "unit" : "U", + "value" : 0.24617422029248465 + }, + { + "date" : "2015-10-16T02:20:00", + "unit" : "U", + "value" : 0.21811262350069069 + }, + { + "date" : "2015-10-16T02:25:00", + "unit" : "U", + "value" : 0.1922648983849887 + }, + { + "date" : "2015-10-16T02:30:00", + "unit" : "U", + "value" : 0.16852209381294375 + }, + { + "date" : "2015-10-16T02:35:00", + "unit" : "U", + "value" : 0.14678704473003373 + }, + { + "date" : "2015-10-16T02:40:00", + "unit" : "U", + "value" : 0.12696661469528853 + }, + { + "date" : "2015-10-16T02:45:00", + "unit" : "U", + "value" : 0.10897012161281953 + }, + { + "date" : "2015-10-16T02:50:00", + "unit" : "U", + "value" : 0.092714235329971514 + }, + { + "date" : "2015-10-16T02:55:00", + "unit" : "U", + "value" : 0.07811720379651968 + }, + { + "date" : "2015-10-16T03:00:00", + "unit" : "U", + "value" : 0.065102801536982405 + }, + { + "date" : "2015-10-16T03:05:00", + "unit" : "U", + "value" : 0.053597770353323318 + }, + { + "date" : "2015-10-16T03:10:00", + "unit" : "U", + "value" : 0.043530781995733972 + }, + { + "date" : "2015-10-16T03:15:00", + "unit" : "U", + "value" : 0.03482327014417011 + }, + { + "date" : "2015-10-16T03:20:00", + "unit" : "U", + "value" : 0.027389826284725402 + }, + { + "date" : "2015-10-16T03:25:00", + "unit" : "U", + "value" : 0.021146086743643433 + }, + { + "date" : "2015-10-16T03:30:00", + "unit" : "U", + "value" : 0.016012309934482737 + }, + { + "date" : "2015-10-16T03:35:00", + "unit" : "U", + "value" : 0.011921441689652637 + }, + { + "date" : "2015-10-16T03:40:00", + "unit" : "U", + "value" : 0.0088100824151718816 + }, + { + "date" : "2015-10-16T03:45:00", + "unit" : "U", + "value" : 0.0066173260136511026 + }, + { + "date" : "2015-10-16T03:50:00", + "unit" : "U", + "value" : 0.0050210961976692075 + }, + { + "date" : "2015-10-16T03:55:00", + "unit" : "U", + "value" : 0.0036823783158275642 + }, + { + "date" : "2015-10-16T04:00:00", + "unit" : "U", + "value" : 0.0025852439700395813 + }, + { + "date" : "2015-10-16T04:05:00", + "unit" : "U", + "value" : 0.0017152598603126484 + }, + { + "date" : "2015-10-16T04:10:00", + "unit" : "U", + "value" : 0.0010585498741866298 + }, + { + "date" : "2015-10-16T04:15:00", + "unit" : "U", + "value" : 0.00059794710049142111 + }, + { + "date" : "2015-10-16T04:20:00", + "unit" : "U", + "value" : 0.00029752570973717093 + }, + { + "date" : "2015-10-16T04:25:00", + "unit" : "U", + "value" : 0.0001207375635234328 + }, + { + "date" : "2015-10-16T04:30:00", + "unit" : "U", + "value" : 3.3298623365256642e-05 + }, + { + "date" : "2015-10-16T04:35:00", + "unit" : "U", + "value" : 3.0976579948276282e-06 + }, + { + "date" : "2015-10-16T04:40:00", + "unit" : "U", + "value" : 0 + } +] \ No newline at end of file diff --git a/LoopTests/Fixtures/insulin_math/iob_from_doses_output.json b/LoopTests/Fixtures/insulin_math/iob_from_doses_output.json new file mode 100644 index 0000000000..b6570231a7 --- /dev/null +++ b/LoopTests/Fixtures/insulin_math/iob_from_doses_output.json @@ -0,0 +1 @@ +[{"date": "2015-10-15T18:05:00", "value": 0.0, "unit": "U"}, {"date": "2015-10-15T18:10:00", "value": -0.018750000000000006, "unit": "U"}, {"date": "2015-10-15T18:15:00", "value": -0.085416666666666669, "unit": "U"}, {"date": "2015-10-15T18:20:00", "value": -0.15206668087682032, "unit": "U"}, {"date": "2015-10-15T18:25:00", "value": -0.21857935431982956, "unit": "U"}, {"date": "2015-10-15T18:30:00", "value": -0.28464801252543015, "unit": "U"}, {"date": "2015-10-15T18:35:00", "value": -0.35010510247148485, "unit": "U"}, {"date": "2015-10-15T18:40:00", "value": -0.41479512492210602, "unit": "U"}, {"date": "2015-10-15T18:45:00", "value": -0.41190763676098952, "unit": "U"}, {"date": "2015-10-15T18:50:00", "value": -0.40797691999141444, "unit": "U"}, {"date": "2015-10-15T18:55:00", "value": -0.40292107811937894, "unit": "U"}, {"date": "2015-10-15T19:00:00", "value": -0.39698624056940118, "unit": "U"}, {"date": "2015-10-15T19:05:00", "value": -0.45690984213971042, "unit": "U"}, {"date": "2015-10-15T19:10:00", "value": -0.5160939052014526, "unit": "U"}, {"date": "2015-10-15T19:15:00", "value": -0.5741902604296798, "unit": "U"}, {"date": "2015-10-15T19:20:00", "value": -0.66778492452731286, "unit": "U"}, {"date": "2015-10-15T19:25:00", "value": -0.43178101319080897, "unit": "U"}, {"date": "2015-10-15T19:30:00", "value": 4.8055335550120359, "unit": "U"}, {"date": "2015-10-15T19:35:00", "value": 5.0439569248931519, "unit": "U"}, {"date": "2015-10-15T19:40:00", "value": 5.2564032091288739, "unit": "U"}, {"date": "2015-10-15T19:45:00", "value": 5.4534881571023126, "unit": "U"}, {"date": "2015-10-15T19:50:00", "value": 5.5918540183876919, "unit": "U"}, {"date": "2015-10-15T19:55:00", "value": 5.7617970521213326, "unit": "U"}, {"date": "2015-10-15T20:00:00", "value": 5.9186590272588271, "unit": "U"}, {"date": "2015-10-15T20:05:00", "value": 6.0629788559331814, "unit": "U"}, {"date": "2015-10-15T20:10:00", "value": 9.3416452708111812, "unit": "U"}, {"date": "2015-10-15T20:15:00", "value": 9.2679264924395568, "unit": "U"}, {"date": "2015-10-15T20:20:00", "value": 9.1311566183775188, "unit": "U"}, {"date": "2015-10-15T20:25:00", "value": 8.9767537893386322, "unit": "U"}, {"date": "2015-10-15T20:30:00", "value": 8.8018580566191247, "unit": "U"}, {"date": "2015-10-15T20:35:00", "value": 8.552252234133352, "unit": "U"}, {"date": "2015-10-15T20:40:00", "value": 8.2844286507395548, "unit": "U"}, {"date": "2015-10-15T20:45:00", "value": 8.0040683600139655, "unit": "U"}, {"date": "2015-10-15T20:50:00", "value": 7.712867310317252, "unit": "U"}, {"date": "2015-10-15T20:55:00", "value": 7.4123606321714997, "unit": "U"}, {"date": "2015-10-15T21:00:00", "value": 7.1706722555753073, "unit": "U"}, {"date": "2015-10-15T21:05:00", "value": 7.1254496600122827, "unit": "U"}, {"date": "2015-10-15T21:10:00", "value": 6.9617393970761903, "unit": "U"}, {"date": "2015-10-15T21:15:00", "value": 6.7034202910941953, "unit": "U"}, {"date": "2015-10-15T21:20:00", "value": 6.4411294655025397, "unit": "U"}, {"date": "2015-10-15T21:25:00", "value": 6.1756930346329053, "unit": "U"}, {"date": "2015-10-15T21:30:00", "value": 5.9083425409811152, "unit": "U"}, {"date": "2015-10-15T21:35:00", "value": 5.6400399553070137, "unit": "U"}, {"date": "2015-10-15T21:40:00", "value": 9.3783661475995945, "unit": "U"}, {"date": "2015-10-15T21:45:00", "value": 9.1108542204103582, "unit": "U"}, {"date": "2015-10-15T21:50:00", "value": 8.8205731329424886, "unit": "U"}, {"date": "2015-10-15T21:55:00", "value": 8.5236544857453715, "unit": "U"}, {"date": "2015-10-15T22:00:00", "value": 8.4121628508470163, "unit": "U"}, {"date": "2015-10-15T22:05:00", "value": 8.2891516195852599, "unit": "U"}, {"date": "2015-10-15T22:10:00", "value": 8.1623939723762202, "unit": "U"}, {"date": "2015-10-15T22:15:00", "value": 8.031926498827497, "unit": "U"}, {"date": "2015-10-15T22:20:00", "value": 7.898682868513041, "unit": "U"}, {"date": "2015-10-15T22:25:00", "value": 7.7572027441484082, "unit": "U"}, {"date": "2015-10-15T22:30:00", "value": 7.4342948897702952, "unit": "U"}, {"date": "2015-10-15T22:35:00", "value": 7.1098374176912387, "unit": "U"}, {"date": "2015-10-15T22:40:00", "value": 6.7844713305118889, "unit": "U"}, {"date": "2015-10-15T22:45:00", "value": 6.4599143409690667, "unit": "U"}, {"date": "2015-10-15T22:50:00", "value": 6.1370686677850914, "unit": "U"}, {"date": "2015-10-15T22:55:00", "value": 5.8163964384449898, "unit": "U"}, {"date": "2015-10-15T23:00:00", "value": 5.4998105211368991, "unit": "U"}, {"date": "2015-10-15T23:05:00", "value": 5.1880290027561484, "unit": "U"}, {"date": "2015-10-15T23:10:00", "value": 4.8816947056251614, "unit": "U"}, {"date": "2015-10-15T23:15:00", "value": 4.5812274778780608, "unit": "U"}, {"date": "2015-10-15T23:20:00", "value": 4.2866787472586676, "unit": "U"}, {"date": "2015-10-15T23:25:00", "value": 3.9985006890305459, "unit": "U"}, {"date": "2015-10-15T23:30:00", "value": 3.7174102768933639, "unit": "U"}, {"date": "2015-10-15T23:35:00", "value": 3.4413002004784681, "unit": "U"}, {"date": "2015-10-15T23:40:00", "value": 3.1790663931988168, "unit": "U"}, {"date": "2015-10-15T23:45:00", "value": 2.9674459025121354, "unit": "U"}, {"date": "2015-10-15T23:50:00", "value": 2.7629813356602755, "unit": "U"}, {"date": "2015-10-15T23:55:00", "value": 2.5658111309990193, "unit": "U"}, {"date": "2015-10-16T00:00:00", "value": 2.3763386371089359, "unit": "U"}, {"date": "2015-10-16T00:05:00", "value": 2.1936103849532484, "unit": "U"}, {"date": "2015-10-16T00:10:00", "value": 2.0183994767110565, "unit": "U"}, {"date": "2015-10-16T00:15:00", "value": 1.8507002564177386, "unit": "U"}, {"date": "2015-10-16T00:20:00", "value": 1.6985068266336847, "unit": "U"}, {"date": "2015-10-16T00:25:00", "value": 1.5747066972951482, "unit": "U"}, {"date": "2015-10-16T00:30:00", "value": 1.4549325333370042, "unit": "U"}, {"date": "2015-10-16T00:35:00", "value": 1.3388718304255545, "unit": "U"}, {"date": "2015-10-16T00:40:00", "value": 1.2267156075864047, "unit": "U"}, {"date": "2015-10-16T00:45:00", "value": 1.1189147793984533, "unit": "U"}, {"date": "2015-10-16T00:50:00", "value": 1.0147401216299032, "unit": "U"}, {"date": "2015-10-16T00:55:00", "value": 0.9142150372798441, "unit": "U"}, {"date": "2015-10-16T01:00:00", "value": 0.81744236897927469, "unit": "U"}, {"date": "2015-10-16T01:05:00", "value": 0.72449566972835622, "unit": "U"}, {"date": "2015-10-16T01:10:00", "value": 0.63497148245908197, "unit": "U"}, {"date": "2015-10-16T01:15:00", "value": 0.54966882501852599, "unit": "U"}, {"date": "2015-10-16T01:20:00", "value": 0.47039614943892216, "unit": "U"}, {"date": "2015-10-16T01:25:00", "value": 0.3967708826026185, "unit": "U"}, {"date": "2015-10-16T01:30:00", "value": 0.32725818119704375, "unit": "U"}, {"date": "2015-10-16T01:35:00", "value": 0.26174046235113074, "unit": "U"}, {"date": "2015-10-16T01:40:00", "value": 0.20007472515215177, "unit": "U"}, {"date": "2015-10-16T01:45:00", "value": 0.14209255064571641, "unit": "U"}, {"date": "2015-10-16T01:50:00", "value": 0.095810842977274838, "unit": "U"}, {"date": "2015-10-16T01:55:00", "value": 0.080002965672173271, "unit": "U"}, {"date": "2015-10-16T02:00:00", "value": 0.065104330918007297, "unit": "U"}, {"date": "2015-10-16T02:05:00", "value": 0.051088357857381354, "unit": "U"}, {"date": "2015-10-16T02:10:00", "value": 0.036587766581119896, "unit": "U"}, {"date": "2015-10-16T02:15:00", "value": 0.024746042994965325, "unit": "U"}, {"date": "2015-10-16T02:20:00", "value": 0.015304914415581596, "unit": "U"}, {"date": "2015-10-16T02:25:00", "value": 0.0081185642627603517, "unit": "U"}, {"date": "2015-10-16T02:30:00", "value": 0.0030469902521267325, "unit": "U"}, {"date": "2015-10-16T02:35:00", "value": 0.0, "unit": "U"}, {"date": "2015-10-16T02:40:00", "value": 0.0, "unit": "U"}] \ No newline at end of file diff --git a/LoopTests/Fixtures/insulin_math/iob_from_multiple_curves_output.json b/LoopTests/Fixtures/insulin_math/iob_from_multiple_curves_output.json new file mode 100644 index 0000000000..59d6d88351 --- /dev/null +++ b/LoopTests/Fixtures/insulin_math/iob_from_multiple_curves_output.json @@ -0,0 +1,1832 @@ +[ + { + "date" : "2018-05-15T14:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T14:35:00", + "unit" : "U", + "value" : 0.068083333333333329 + }, + { + "date" : "2018-05-15T14:40:00", + "unit" : "U", + "value" : 0.22641666666666665 + }, + { + "date" : "2018-05-15T14:45:00", + "unit" : "U", + "value" : 1.2847500000000001 + }, + { + "date" : "2018-05-15T14:50:00", + "unit" : "U", + "value" : 1.4426604218252472 + }, + { + "date" : "2018-05-15T14:55:00", + "unit" : "U", + "value" : 1.6315008168845901 + }, + { + "date" : "2018-05-15T15:00:00", + "unit" : "U", + "value" : 1.856973269641518 + }, + { + "date" : "2018-05-15T15:05:00", + "unit" : "U", + "value" : 2.0051090073701832 + }, + { + "date" : "2018-05-15T15:10:00", + "unit" : "U", + "value" : 2.0541406193487384 + }, + { + "date" : "2018-05-15T15:15:00", + "unit" : "U", + "value" : 2.0944463997593505 + }, + { + "date" : "2018-05-15T15:20:00", + "unit" : "U", + "value" : 2.1265928762508812 + }, + { + "date" : "2018-05-15T15:25:00", + "unit" : "U", + "value" : 2.1516271451485611 + }, + { + "date" : "2018-05-15T15:30:00", + "unit" : "U", + "value" : 2.1704824312840056 + }, + { + "date" : "2018-05-15T15:35:00", + "unit" : "U", + "value" : 2.1839875490356584 + }, + { + "date" : "2018-05-15T15:40:00", + "unit" : "U", + "value" : 2.1928777062534417 + }, + { + "date" : "2018-05-15T15:45:00", + "unit" : "U", + "value" : 2.1978042452867692 + }, + { + "date" : "2018-05-15T15:50:00", + "unit" : "U", + "value" : 2.1993434218318555 + }, + { + "date" : "2018-05-15T15:55:00", + "unit" : "U", + "value" : 2.1657543130517265 + }, + { + "date" : "2018-05-15T16:00:00", + "unit" : "U", + "value" : 2.0869859379909013 + }, + { + "date" : "2018-05-15T16:05:00", + "unit" : "U", + "value" : 2.0061836656333947 + }, + { + "date" : "2018-05-15T16:10:00", + "unit" : "U", + "value" : 1.9238953054745647 + }, + { + "date" : "2018-05-15T16:15:00", + "unit" : "U", + "value" : 1.8408465292538017 + }, + { + "date" : "2018-05-15T16:20:00", + "unit" : "U", + "value" : 1.7576646941017673 + }, + { + "date" : "2018-05-15T16:25:00", + "unit" : "U", + "value" : 1.6748882348269276 + }, + { + "date" : "2018-05-15T16:30:00", + "unit" : "U", + "value" : 1.5929762431399233 + }, + { + "date" : "2018-05-15T16:35:00", + "unit" : "U", + "value" : 1.5123171313102044 + }, + { + "date" : "2018-05-15T16:40:00", + "unit" : "U", + "value" : 1.4332364621749809 + }, + { + "date" : "2018-05-15T16:45:00", + "unit" : "U", + "value" : 1.3560040203811585 + }, + { + "date" : "2018-05-15T16:50:00", + "unit" : "U", + "value" : 1.2808401932907929 + }, + { + "date" : "2018-05-15T16:55:00", + "unit" : "U", + "value" : 1.2079217240712818 + }, + { + "date" : "2018-05-15T17:00:00", + "unit" : "U", + "value" : 1.1373868940784675 + }, + { + "date" : "2018-05-15T17:05:00", + "unit" : "U", + "value" : 1.0693401866831296 + }, + { + "date" : "2018-05-15T17:10:00", + "unit" : "U", + "value" : 1.0038564801514345 + }, + { + "date" : "2018-05-15T17:15:00", + "unit" : "U", + "value" : 0.94098481303325587 + }, + { + "date" : "2018-05-15T17:20:00", + "unit" : "U", + "value" : 0.88075176170717628 + }, + { + "date" : "2018-05-15T17:25:00", + "unit" : "U", + "value" : 0.82316446624830775 + }, + { + "date" : "2018-05-15T17:30:00", + "unit" : "U", + "value" : 0.7682133375981065 + }, + { + "date" : "2018-05-15T17:35:00", + "unit" : "U", + "value" : 0.71587447609950972 + }, + { + "date" : "2018-05-15T17:40:00", + "unit" : "U", + "value" : 0.66611182879349462 + }, + { + "date" : "2018-05-15T17:45:00", + "unit" : "U", + "value" : 0.61887911043380595 + }, + { + "date" : "2018-05-15T17:50:00", + "unit" : "U", + "value" : 0.57412151094614827 + }, + { + "date" : "2018-05-15T17:55:00", + "unit" : "U", + "value" : 0.53177721001913325 + }, + { + "date" : "2018-05-15T18:00:00", + "unit" : "U", + "value" : 0.49177871765069903 + }, + { + "date" : "2018-05-15T18:05:00", + "unit" : "U", + "value" : 0.45405405777089097 + }, + { + "date" : "2018-05-15T18:10:00", + "unit" : "U", + "value" : 0.41852781050631305 + }, + { + "date" : "2018-05-15T18:15:00", + "unit" : "U", + "value" : 0.38512202723086875 + }, + { + "date" : "2018-05-15T18:20:00", + "unit" : "U", + "value" : 0.35375703125023233 + }, + { + "date" : "2018-05-15T18:25:00", + "unit" : "U", + "value" : 0.3243521157834296 + }, + { + "date" : "2018-05-15T18:30:00", + "unit" : "U", + "value" : 0.29682614982439526 + }, + { + "date" : "2018-05-15T18:35:00", + "unit" : "U", + "value" : 0.27109810148064761 + }, + { + "date" : "2018-05-15T18:40:00", + "unit" : "U", + "value" : 0.24708748748724252 + }, + { + "date" : "2018-05-15T18:45:00", + "unit" : "U", + "value" : 0.22471475677456665 + }, + { + "date" : "2018-05-15T18:50:00", + "unit" : "U", + "value" : 0.20390161522154615 + }, + { + "date" : "2018-05-15T18:55:00", + "unit" : "U", + "value" : 0.184571298045277 + }, + { + "date" : "2018-05-15T19:00:00", + "unit" : "U", + "value" : 0.16664879565826193 + }, + { + "date" : "2018-05-15T19:05:00", + "unit" : "U", + "value" : 0.15006103826013523 + }, + { + "date" : "2018-05-15T19:10:00", + "unit" : "U", + "value" : 0.13473704391723001 + }, + { + "date" : "2018-05-15T19:15:00", + "unit" : "U", + "value" : 0.12060803441618617 + }, + { + "date" : "2018-05-15T19:20:00", + "unit" : "U", + "value" : 0.10760752275303831 + }, + { + "date" : "2018-05-15T19:25:00", + "unit" : "U", + "value" : 0.095671375733160369 + }, + { + "date" : "2018-05-15T19:30:00", + "unit" : "U", + "value" : 0.084737854806747129 + }, + { + "date" : "2018-05-15T19:35:00", + "unit" : "U", + "value" : 0.074747637946072754 + }, + { + "date" : "2018-05-15T19:40:00", + "unit" : "U", + "value" : 0.065643825081797474 + }, + { + "date" : "2018-05-15T19:45:00", + "unit" : "U", + "value" : 0.057371929353482794 + }, + { + "date" : "2018-05-15T19:50:00", + "unit" : "U", + "value" : 0.0498798561918899 + }, + { + "date" : "2018-05-15T19:55:00", + "unit" : "U", + "value" : 0.04311787203539412 + }, + { + "date" : "2018-05-15T20:00:00", + "unit" : "U", + "value" : 0.03703856428799019 + }, + { + "date" : "2018-05-15T20:05:00", + "unit" : "U", + "value" : 0.031596793950053138 + }, + { + "date" : "2018-05-15T20:10:00", + "unit" : "U", + "value" : 0.026749642193648854 + }, + { + "date" : "2018-05-15T20:15:00", + "unit" : "U", + "value" : 0.022456352010185483 + }, + { + "date" : "2018-05-15T20:20:00", + "unit" : "U", + "value" : 0.018678265928236615 + }, + { + "date" : "2018-05-15T20:25:00", + "unit" : "U", + "value" : 0.015378760682142785 + }, + { + "date" : "2018-05-15T20:30:00", + "unit" : "U", + "value" : 0.012523179606376668 + }, + { + "date" : "2018-05-15T20:35:00", + "unit" : "U", + "value" : 0.010078763435580854 + }, + { + "date" : "2018-05-15T20:40:00", + "unit" : "U", + "value" : 0.0080145801046833092 + }, + { + "date" : "2018-05-15T20:45:00", + "unit" : "U", + "value" : 0.0063014540666978987 + }, + { + "date" : "2018-05-15T20:50:00", + "unit" : "U", + "value" : 0.0049061628994142908 + }, + { + "date" : "2018-05-15T20:55:00", + "unit" : "U", + "value" : 0.003790314555342678 + }, + { + "date" : "2018-05-15T21:00:00", + "unit" : "U", + "value" : 0.0028989697490798255 + }, + { + "date" : "2018-05-15T21:05:00", + "unit" : "U", + "value" : 0.0021858386616289212 + }, + { + "date" : "2018-05-15T21:10:00", + "unit" : "U", + "value" : 0.0016213951706374548 + }, + { + "date" : "2018-05-15T21:15:00", + "unit" : "U", + "value" : 0.0011748698803442786 + }, + { + "date" : "2018-05-15T21:20:00", + "unit" : "U", + "value" : 0.0008236024229041905 + }, + { + "date" : "2018-05-15T21:25:00", + "unit" : "U", + "value" : 0.00055393548025383656 + }, + { + "date" : "2018-05-15T21:30:00", + "unit" : "U", + "value" : 0.00035322601755862491 + }, + { + "date" : "2018-05-15T21:35:00", + "unit" : "U", + "value" : 0.00020976022448825627 + }, + { + "date" : "2018-05-15T21:40:00", + "unit" : "U", + "value" : 0.00011270366979644896 + }, + { + "date" : "2018-05-15T21:45:00", + "unit" : "U", + "value" : 5.2053208299390748e-05 + }, + { + "date" : "2018-05-15T21:50:00", + "unit" : "U", + "value" : 1.8590657644046435e-05 + }, + { + "date" : "2018-05-15T21:55:00", + "unit" : "U", + "value" : 3.8382512378587476e-06 + }, + { + "date" : "2018-05-15T22:00:00", + "unit" : "U", + "value" : 1.5864094737860769e-08 + }, + { + "date" : "2018-05-15T22:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T22:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T22:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T22:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T22:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T22:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T22:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T22:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T22:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T22:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T22:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T23:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T23:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T23:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T23:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T23:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T23:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T23:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T23:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T23:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T23:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T23:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-15T23:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T00:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T00:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T00:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T00:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T00:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T00:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T00:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T00:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T00:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T00:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T00:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T00:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T01:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T01:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T01:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T01:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T01:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T01:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T01:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T01:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T01:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T01:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T01:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T01:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T02:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T02:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T02:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T02:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T02:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T02:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T02:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T02:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T02:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T02:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T02:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T02:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T03:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T03:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T03:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T03:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T03:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T03:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T03:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T03:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T03:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T03:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T03:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T03:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T04:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T04:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T04:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T04:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T04:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T04:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T04:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T04:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T04:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T04:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T04:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T04:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T05:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T05:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T05:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T05:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T05:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T05:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T05:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T05:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T05:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T05:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T05:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T05:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T06:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T06:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T06:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T06:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T06:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T06:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T06:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T06:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T06:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T06:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T06:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T06:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T07:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T07:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T07:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T07:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T07:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T07:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T07:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T07:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T07:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T07:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T07:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T07:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T08:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T08:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T08:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T08:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T08:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T08:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T08:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T08:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T08:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T08:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T08:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T08:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T09:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T09:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T09:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T09:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T09:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T09:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T09:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T09:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T09:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T09:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T09:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T09:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T10:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T10:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T10:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T10:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T10:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T10:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T10:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T10:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T10:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T10:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T10:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T10:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T11:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T11:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T11:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T11:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T11:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T11:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T11:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T11:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T11:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T11:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T11:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T11:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T12:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T12:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T12:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T12:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T12:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T12:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T12:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T12:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T12:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T12:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T12:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T12:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T13:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T13:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T13:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T13:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T13:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T13:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T13:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T13:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T13:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T13:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T13:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T13:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T14:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T14:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T14:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T14:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T14:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T14:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T14:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T14:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T14:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T14:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T14:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T14:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T15:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T15:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T15:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T15:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T15:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T15:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T15:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T15:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T15:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T15:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T15:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T15:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T16:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T16:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T16:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T16:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T16:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T16:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T16:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T16:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T16:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T16:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T16:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T16:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T17:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T17:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T17:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T17:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T17:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T17:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T17:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T17:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T17:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T17:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T17:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T17:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T18:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T18:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T18:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T18:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T18:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T18:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T18:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T18:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T18:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T18:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T18:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T18:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T19:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T19:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T19:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T19:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T19:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T19:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T19:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T19:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T19:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T19:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T19:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T19:55:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T20:00:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T20:05:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T20:10:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T20:15:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T20:20:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T20:25:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T20:30:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T20:35:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T20:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T20:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T20:50:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2018-05-16T20:55:00", + "unit" : "U", + "value" : 0 + } +] \ No newline at end of file diff --git a/LoopTests/Fixtures/insulin_math/iob_from_reservoir_output.json b/LoopTests/Fixtures/insulin_math/iob_from_reservoir_output.json new file mode 100644 index 0000000000..00dde41e7f --- /dev/null +++ b/LoopTests/Fixtures/insulin_math/iob_from_reservoir_output.json @@ -0,0 +1,557 @@ +[ + { + "date" : "2016-01-30T15:40:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2016-01-30T15:45:00", + "unit" : "U", + "value" : 0.13488888888888889 + }, + { + "date" : "2016-01-30T15:50:00", + "unit" : "U", + "value" : 0.068222222222222226 + }, + { + "date" : "2016-01-30T15:55:00", + "unit" : "U", + "value" : 0.067514484321220022 + }, + { + "date" : "2016-01-30T16:00:00", + "unit" : "U", + "value" : 0.06678896639537546 + }, + { + "date" : "2016-01-30T16:05:00", + "unit" : "U", + "value" : -0.00074881278069904855 + }, + { + "date" : "2016-01-30T16:10:00", + "unit" : "U", + "value" : -0.018427618409960961 + }, + { + "date" : "2016-01-30T16:15:00", + "unit" : "U", + "value" : -0.010875573683178208 + }, + { + "date" : "2016-01-30T16:20:00", + "unit" : "U", + "value" : -0.0029493923329813876 + }, + { + "date" : "2016-01-30T16:25:00", + "unit" : "U", + "value" : -0.020414483345468498 + }, + { + "date" : "2016-01-30T16:30:00", + "unit" : "U", + "value" : 0.062365834815154765 + }, + { + "date" : "2016-01-30T16:35:00", + "unit" : "U", + "value" : 0.14505998900045661 + }, + { + "date" : "2016-01-30T16:40:00", + "unit" : "U", + "value" : 0.32783757546514158 + }, + { + "date" : "2016-01-30T16:45:00", + "unit" : "U", + "value" : 0.4598019301783795 + }, + { + "date" : "2016-01-30T16:50:00", + "unit" : "U", + "value" : 0.59043188942305824 + }, + { + "date" : "2016-01-30T16:55:00", + "unit" : "U", + "value" : 0.74279454295062775 + }, + { + "date" : "2016-01-30T17:00:00", + "unit" : "U", + "value" : 0.91883957574081498 + }, + { + "date" : "2016-01-30T17:05:00", + "unit" : "U", + "value" : 4.7473335690414027 + }, + { + "date" : "2016-01-30T17:10:00", + "unit" : "U", + "value" : 4.7205893142678566 + }, + { + "date" : "2016-01-30T17:15:00", + "unit" : "U", + "value" : 4.6956460306439167 + }, + { + "date" : "2016-01-30T17:20:00", + "unit" : "U", + "value" : 4.6581878823793952 + }, + { + "date" : "2016-01-30T17:25:00", + "unit" : "U", + "value" : 4.5854451394476108 + }, + { + "date" : "2016-01-30T17:30:00", + "unit" : "U", + "value" : 4.6027813624337712 + }, + { + "date" : "2016-01-30T17:35:00", + "unit" : "U", + "value" : 4.6102259682999147 + }, + { + "date" : "2016-01-30T17:40:00", + "unit" : "U", + "value" : 4.5589603458756276 + }, + { + "date" : "2016-01-30T17:45:00", + "unit" : "U", + "value" : 4.548846830954254 + }, + { + "date" : "2016-01-30T17:50:00", + "unit" : "U", + "value" : 4.4565829509093584 + }, + { + "date" : "2016-01-30T17:55:00", + "unit" : "U", + "value" : 4.3325605947143888 + }, + { + "date" : "2016-01-30T18:00:00", + "unit" : "U", + "value" : 4.2267974800496662 + }, + { + "date" : "2016-01-30T18:05:00", + "unit" : "U", + "value" : 5.4155700945160623 + }, + { + "date" : "2016-01-30T18:10:00", + "unit" : "U", + "value" : 5.2494825347097569 + }, + { + "date" : "2016-01-30T18:15:00", + "unit" : "U", + "value" : 5.0467007018788044 + }, + { + "date" : "2016-01-30T18:20:00", + "unit" : "U", + "value" : 4.9041815574952192 + }, + { + "date" : "2016-01-30T18:25:00", + "unit" : "U", + "value" : 4.7641713008881359 + }, + { + "date" : "2016-01-30T18:30:00", + "unit" : "U", + "value" : 4.6196316848788213 + }, + { + "date" : "2016-01-30T18:35:00", + "unit" : "U", + "value" : 4.4708378425452819 + }, + { + "date" : "2016-01-30T18:40:00", + "unit" : "U", + "value" : 4.292838715026746 + }, + { + "date" : "2016-01-30T18:45:00", + "unit" : "U", + "value" : 4.0878775257155624 + }, + { + "date" : "2016-01-30T18:50:00", + "unit" : "U", + "value" : 3.8554187635181547 + }, + { + "date" : "2016-01-30T18:55:00", + "unit" : "U", + "value" : 3.6217340636776383 + }, + { + "date" : "2016-01-30T19:00:00", + "unit" : "U", + "value" : 3.4624685995070617 + }, + { + "date" : "2016-01-30T19:05:00", + "unit" : "U", + "value" : 3.3038194035334003 + }, + { + "date" : "2016-01-30T19:10:00", + "unit" : "U", + "value" : 3.12108881849452 + }, + { + "date" : "2016-01-30T19:15:00", + "unit" : "U", + "value" : 2.9640737031370752 + }, + { + "date" : "2016-01-30T19:20:00", + "unit" : "U", + "value" : 2.8090169147849231 + }, + { + "date" : "2016-01-30T19:25:00", + "unit" : "U", + "value" : 2.6310439784619697 + }, + { + "date" : "2016-01-30T19:30:00", + "unit" : "U", + "value" : 2.7158656703311048 + }, + { + "date" : "2016-01-30T19:35:00", + "unit" : "U", + "value" : 2.8433020866290324 + }, + { + "date" : "2016-01-30T19:40:00", + "unit" : "U", + "value" : 3.4632207817542824 + }, + { + "date" : "2016-01-30T19:45:00", + "unit" : "U", + "value" : 3.3198722041766531 + }, + { + "date" : "2016-01-30T19:50:00", + "unit" : "U", + "value" : 3.1994073575748523 + }, + { + "date" : "2016-01-30T19:55:00", + "unit" : "U", + "value" : 3.1797643299899385 + }, + { + "date" : "2016-01-30T20:00:00", + "unit" : "U", + "value" : 3.1121937375789712 + }, + { + "date" : "2016-01-30T20:05:00", + "unit" : "U", + "value" : 3.1446751787258891 + }, + { + "date" : "2016-01-30T20:10:00", + "unit" : "U", + "value" : 3.1285196330888527 + }, + { + "date" : "2016-01-30T20:15:00", + "unit" : "U", + "value" : 3.1632801791886243 + }, + { + "date" : "2016-01-30T20:20:00", + "unit" : "U", + "value" : 4.1479809029771069 + }, + { + "date" : "2016-01-30T20:25:00", + "unit" : "U", + "value" : 3.9834822751069776 + }, + { + "date" : "2016-01-30T20:30:00", + "unit" : "U", + "value" : 3.8392406781815338 + }, + { + "date" : "2016-01-30T20:35:00", + "unit" : "U", + "value" : 3.6946591732484402 + }, + { + "date" : "2016-01-30T20:40:00", + "unit" : "U", + "value" : 3.5407257995084676 + }, + { + "date" : "2016-01-30T20:45:00", + "unit" : "U", + "value" : 3.3874261680274937 + }, + { + "date" : "2016-01-30T20:50:00", + "unit" : "U", + "value" : 3.2352531696570548 + }, + { + "date" : "2016-01-30T20:55:00", + "unit" : "U", + "value" : 3.085261005079988 + }, + { + "date" : "2016-01-30T21:00:00", + "unit" : "U", + "value" : 2.9372816673332909 + }, + { + "date" : "2016-01-30T21:05:00", + "unit" : "U", + "value" : 2.7907413629475002 + }, + { + "date" : "2016-01-30T21:10:00", + "unit" : "U", + "value" : 2.6471028260569947 + }, + { + "date" : "2016-01-30T21:15:00", + "unit" : "U", + "value" : 2.5111502668268844 + }, + { + "date" : "2016-01-30T21:20:00", + "unit" : "U", + "value" : 2.4064958525345617 + }, + { + "date" : "2016-01-30T21:25:00", + "unit" : "U", + "value" : 2.3012714622124935 + }, + { + "date" : "2016-01-30T21:30:00", + "unit" : "U", + "value" : 2.1960183464844891 + }, + { + "date" : "2016-01-30T21:35:00", + "unit" : "U", + "value" : 2.0910235309730165 + }, + { + "date" : "2016-01-30T21:40:00", + "unit" : "U", + "value" : 1.986502686195295 + }, + { + "date" : "2016-01-30T21:45:00", + "unit" : "U", + "value" : 1.8835249690318872 + }, + { + "date" : "2016-01-30T21:50:00", + "unit" : "U", + "value" : 1.7822280295871071 + }, + { + "date" : "2016-01-30T21:55:00", + "unit" : "U", + "value" : 1.6824769595711171 + }, + { + "date" : "2016-01-30T22:00:00", + "unit" : "U", + "value" : 1.5847700423010984 + }, + { + "date" : "2016-01-30T22:05:00", + "unit" : "U", + "value" : 1.4886582778513744 + }, + { + "date" : "2016-01-30T22:10:00", + "unit" : "U", + "value" : 1.3941551212776522 + }, + { + "date" : "2016-01-30T22:15:00", + "unit" : "U", + "value" : 1.303115763902519 + }, + { + "date" : "2016-01-30T22:20:00", + "unit" : "U", + "value" : 1.2241938781749204 + }, + { + "date" : "2016-01-30T22:25:00", + "unit" : "U", + "value" : 1.1463251788331443 + }, + { + "date" : "2016-01-30T22:30:00", + "unit" : "U", + "value" : 1.0695894037349465 + }, + { + "date" : "2016-01-30T22:35:00", + "unit" : "U", + "value" : 0.99471197396772404 + }, + { + "date" : "2016-01-30T22:40:00", + "unit" : "U", + "value" : 0.92189770714827757 + }, + { + "date" : "2016-01-30T22:45:00", + "unit" : "U", + "value" : 0.85127298068876722 + }, + { + "date" : "2016-01-30T22:50:00", + "unit" : "U", + "value" : 0.78291513570575266 + }, + { + "date" : "2016-01-30T22:55:00", + "unit" : "U", + "value" : 0.71669318624836187 + }, + { + "date" : "2016-01-30T23:00:00", + "unit" : "U", + "value" : 0.65247840707485749 + }, + { + "date" : "2016-01-30T23:05:00", + "unit" : "U", + "value" : 0.59016663173339567 + }, + { + "date" : "2016-01-30T23:10:00", + "unit" : "U", + "value" : 0.52993154731493231 + }, + { + "date" : "2016-01-30T23:15:00", + "unit" : "U", + "value" : 0.47241619989047379 + }, + { + "date" : "2016-01-30T23:20:00", + "unit" : "U", + "value" : 0.41760095998118058 + }, + { + "date" : "2016-01-30T23:25:00", + "unit" : "U", + "value" : 0.36531374345888057 + }, + { + "date" : "2016-01-30T23:30:00", + "unit" : "U", + "value" : 0.31573377830312777 + }, + { + "date" : "2016-01-30T23:35:00", + "unit" : "U", + "value" : 0.26879376195107563 + }, + { + "date" : "2016-01-30T23:40:00", + "unit" : "U", + "value" : 0.22326696667877355 + }, + { + "date" : "2016-01-30T23:45:00", + "unit" : "U", + "value" : 0.18282221358519934 + }, + { + "date" : "2016-01-30T23:50:00", + "unit" : "U", + "value" : 0.14905533718034986 + }, + { + "date" : "2016-01-30T23:55:00", + "unit" : "U", + "value" : 0.12297488953685366 + }, + { + "date" : "2016-01-31T00:00:00", + "unit" : "U", + "value" : 0.098520190008746233 + }, + { + "date" : "2016-01-31T00:05:00", + "unit" : "U", + "value" : 0.075947521254802516 + }, + { + "date" : "2016-01-31T00:10:00", + "unit" : "U", + "value" : 0.05589249490473374 + }, + { + "date" : "2016-01-31T00:15:00", + "unit" : "U", + "value" : 0.037949724121158643 + }, + { + "date" : "2016-01-31T00:20:00", + "unit" : "U", + "value" : 0.022714114635155692 + }, + { + "date" : "2016-01-31T00:25:00", + "unit" : "U", + "value" : 0.0096951335481437178 + }, + { + "date" : "2016-01-31T00:30:00", + "unit" : "U", + "value" : 0.0002680112283814255 + }, + { + "date" : "2016-01-31T00:35:00", + "unit" : "U", + "value" : 0.00022992226001462311 + }, + { + "date" : "2016-01-31T00:40:00", + "unit" : "U", + "value" : 7.3444943720727971e-05 + }, + { + "date" : "2016-01-31T00:45:00", + "unit" : "U", + "value" : 0 + }, + { + "date" : "2016-01-31T00:50:00", + "unit" : "U", + "value" : 0 + } +] \ No newline at end of file diff --git a/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_input.json b/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_input.json new file mode 100644 index 0000000000..5f1b6aba64 --- /dev/null +++ b/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_input.json @@ -0,0 +1,16 @@ +[ + { + "type": "TempBasal", + "start_at": "2015-07-12T23:59:59", + "end_at": "2015-07-13T07:54:00", + "amount": 2.0, + "unit": "U/hour" + }, + { + "type": "Bolus", + "start_at": "2015-07-13T11:00:00", + "end_at": "2015-07-13T11:00:00", + "amount": 3.0, + "unit": "U" + } +] \ No newline at end of file diff --git a/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_mutable_input.json b/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_mutable_input.json new file mode 100644 index 0000000000..329f46b2c5 --- /dev/null +++ b/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_mutable_input.json @@ -0,0 +1,18 @@ +[ + { + "type": "TempBasal", + "start_at": "2015-07-12T23:59:59", + "end_at": "2015-07-13T07:54:00", + "amount": 2.0, + "unit": "U/hour", + "isMutable": true + }, + { + "type": "Bolus", + "start_at": "2015-07-13T11:00:00", + "end_at": "2015-07-13T11:00:00", + "amount": 3.0, + "unit": "U", + "isMutable": true + } +] diff --git a/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_mutable_output.json b/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_mutable_output.json new file mode 100644 index 0000000000..583c312a83 --- /dev/null +++ b/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_mutable_output.json @@ -0,0 +1,42 @@ +[ + { + "type": "TempBasal", + "start_at": "2015-07-12T23:59:59", + "end_at": "2015-07-13T00:00:00", + "amount": 1.1, + "unit": "U/hour", + "isMutable": true + }, + { + "type": "TempBasal", + "start_at": "2015-07-13T00:00:00", + "end_at": "2015-07-13T04:00:00", + "amount": 1.1, + "unit": "U/hour", + "isMutable": true + }, + { + "type": "TempBasal", + "start_at": "2015-07-13T04:00:00", + "end_at": "2015-07-13T07:00:00", + "amount": 1.075, + "unit": "U/hour", + "isMutable": true + }, + { + "type": "TempBasal", + "start_at": "2015-07-13T07:00:00", + "end_at": "2015-07-13T07:54:00", + "amount": 1.15, + "unit": "U/hour", + "isMutable": true + }, + { + "type": "Bolus", + "start_at": "2015-07-13T11:00:00", + "end_at": "2015-07-13T11:00:00", + "amount": 3.0, + "unit": "U", + "isMutable": true + } +] diff --git a/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_output.json b/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_output.json new file mode 100644 index 0000000000..52f09439e4 --- /dev/null +++ b/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_output.json @@ -0,0 +1,37 @@ +[ + { + "type": "TempBasal", + "start_at": "2015-07-12T23:59:59", + "end_at": "2015-07-13T00:00:00", + "amount": 1.1, + "unit": "U/hour" + }, + { + "type": "TempBasal", + "start_at": "2015-07-13T00:00:00", + "end_at": "2015-07-13T04:00:00", + "amount": 1.1, + "unit": "U/hour" + }, + { + "type": "TempBasal", + "start_at": "2015-07-13T04:00:00", + "end_at": "2015-07-13T07:00:00", + "amount": 1.075, + "unit": "U/hour" + }, + { + "type": "TempBasal", + "start_at": "2015-07-13T07:00:00", + "end_at": "2015-07-13T07:54:00", + "amount": 1.15, + "unit": "U/hour" + }, + { + "type": "Bolus", + "start_at": "2015-07-13T11:00:00", + "end_at": "2015-07-13T11:00:00", + "amount": 3.0, + "unit": "U" + } +] \ No newline at end of file diff --git a/LoopTests/Fixtures/insulin_math/normalized_doses.json b/LoopTests/Fixtures/insulin_math/normalized_doses.json new file mode 100644 index 0000000000..127adea5dd --- /dev/null +++ b/LoopTests/Fixtures/insulin_math/normalized_doses.json @@ -0,0 +1,171 @@ +[ + { + "amount": 3.225, + "start_at": "2015-10-15T22:00:00", + "description": "TempBasal: 3.125U/hour over 30min", + "type": "TempBasal", + "unit": "U/hour", + "end_at": "2015-10-15T22:29:50", + "scheduled": 1.0, + "isMutable": true + }, + { + "amount": 3.325, + "start_at": "2015-10-15T21:59:50", + "description": "TempBasal: 3.125U/hour over 30min", + "type": "TempBasal", + "unit": "U/hour", + "end_at": "2015-10-15T22:00:00", + "scheduled": 1.0 + }, + { + "amount": 1.09999999999999998, + "start_at": "2015-10-15T21:39:49", + "description": "TempBasal: 0.9U/hour over 4min", + "type": "TempBasal", + "unit": "U/hour", + "end_at": "2015-10-15T21:43:49", + "scheduled": 1.0 + }, + { + "start_at": "2015-10-15T21:35:12", + "description": "Normal bolus: 4.0U", + "end_at": "2015-10-15T21:35:12", + "amount": 4.0, + "type": "Bolus", + "unit": "U" + }, + { + "amount": 2.075, + "start_at": "2015-10-15T21:04:34", + "description": "TempBasal: 1.875U/hour over 10min", + "type": "TempBasal", + "unit": "U/hour", + "end_at": "2015-10-15T21:14:34", + "scheduled": 1.0 + }, + { + "amount": 2.7, + "start_at": "2015-10-15T21:00:04", + "description": "TempBasal: 2.5U/hour over 4min", + "type": "TempBasal", + "unit": "U/hour", + "end_at": "2015-10-15T21:04:04", + "scheduled": 1.0 + }, + { + "amount": 0.0, + "start_at": "2015-10-15T20:39:52", + "description": "TempBasal: 0.0U/hour over 20min", + "type": "TempBasal", + "unit": "U/hour", + "end_at": "2015-10-15T20:59:52", + "scheduled": 0.8 + }, + { + "amount": 0.05, + "start_at": "2015-10-15T20:34:34", + "description": "TempBasal: 0.05U/hour over 5min", + "type": "TempBasal", + "unit": "U/hour", + "end_at": "2015-10-15T20:39:34", + "scheduled": 0.8 + }, + { + "amount": 0.95, + "start_at": "2015-10-15T20:29:51", + "description": "TempBasal: 0.75U/hour over 4min", + "type": "TempBasal", + "unit": "U/hour", + "end_at": "2015-10-15T20:33:51", + "scheduled": 1.0 + }, + { + "amount": 1.375, + "start_at": "2015-10-15T20:14:37", + "description": "TempBasal: 1.175U/hour over 5min", + "type": "TempBasal", + "unit": "U/hour", + "end_at": "2015-10-15T20:19:37", + "scheduled": 1.0 + }, + { + "amount": 2.075, + "start_at": "2015-10-15T20:09:52", + "description": "TempBasal: 1.875U/hour over 4min", + "type": "TempBasal", + "unit": "U/hour", + "end_at": "2015-10-15T20:13:52", + "scheduled": 1.0 + }, + { + "start_at": "2015-10-15T20:05:10", + "description": "Normal bolus: 3.3U", + "end_at": "2015-10-15T20:05:10", + "amount": 3.3, + "type": "Bolus", + "unit": "U" + }, + { + "amount": 3.7, + "start_at": "2015-10-15T19:54:51", + "description": "TempBasal: 3.5U/hour over 15min", + "type": "TempBasal", + "unit": "U/hour", + "end_at": "2015-10-15T20:09:51", + "scheduled": 1.0 + }, + { + "start_at": "2015-10-15T19:25:54", + "description": "Normal bolus: 5.0U", + "end_at": "2015-10-15T19:25:54", + "amount": 5.0, + "type": "Bolus", + "unit": "U" + }, + { + "amount": 3.7, + "start_at": "2015-10-15T19:24:52", + "description": "TempBasal: 3.5U/hour over 29min", + "type": "TempBasal", + "unit": "U/hour", + "end_at": "2015-10-15T19:53:52", + "scheduled": 1.0 + }, + { + "amount": 0.4, + "start_at": "2015-10-15T19:19:39", + "description": "TempBasal: 0.2U/hour over 5min", + "type": "TempBasal", + "unit": "U/hour", + "end_at": "2015-10-15T19:24:39", + "scheduled": 1.0 + }, + { + "amount": 0.2, + "start_at": "2015-10-15T19:00:07", + "description": "TempBasal: 0.0U/hour over 19min", + "type": "TempBasal", + "unit": "U/hour", + "end_at": "2015-10-15T19:19:07", + "scheduled": 1.0 + }, + { + "amount": 0.0, + "start_at": "2015-10-15T18:14:54", + "description": "TempBasal: 0.0U/hour over 30min", + "type": "TempBasal", + "unit": "U/hour", + "end_at": "2015-10-15T18:44:54", + "scheduled": 0.8 + }, + { + "amount": 0.775, + "start_at": "2015-10-15T18:09:35", + "description": "TempBasal: 0.575U/hour over 5min", + "type": "TempBasal", + "unit": "U/hour", + "end_at": "2015-10-15T18:14:35", + "scheduled": 1.0 + } +] diff --git a/LoopTests/Fixtures/insulin_math/normalized_reservoir_history_output.json b/LoopTests/Fixtures/insulin_math/normalized_reservoir_history_output.json new file mode 100644 index 0000000000..131d0cc93d --- /dev/null +++ b/LoopTests/Fixtures/insulin_math/normalized_reservoir_history_output.json @@ -0,0 +1,434 @@ +[ + { + "start_at" : "2016-01-30T20:30:43", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T20:35:41", + "type" : "TempBasal", + "amount" : 0.90604026845637575, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T20:25:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T20:30:43", + "type" : "TempBasal", + "amount" : 0.89700996677740863, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T20:20:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T20:25:42", + "type" : "TempBasal", + "amount" : 0.60000000000000009, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T20:15:41", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T20:20:42", + "type" : "TempBasal", + "amount" : 14.352159468438538, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T20:10:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T20:15:41", + "type" : "TempBasal", + "amount" : 3.0100334448160533, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T20:05:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T20:10:42", + "type" : "TempBasal", + "amount" : 2.4000000000000004, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T20:00:41", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T20:05:42", + "type" : "TempBasal", + "amount" : 2.9900332225913622, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T19:55:41", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T20:00:41", + "type" : "TempBasal", + "amount" : 1.8, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T19:50:41", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T19:55:41", + "type" : "TempBasal", + "amount" : 2.4000000000000004, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T19:45:41", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T19:50:41", + "type" : "TempBasal", + "amount" : 1.2000000000000002, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T19:40:41", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T19:45:41", + "type" : "TempBasal", + "amount" : 0.90000000000000002, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T19:35:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T19:40:41", + "type" : "TempBasal", + "amount" : 9.6321070234113702, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T19:25:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T19:35:42", + "type" : "TempBasal", + "amount" : 4.2000000000000002, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T19:20:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T19:25:42", + "type" : "TempBasal", + "amount" : 0.60000000000000009, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T19:15:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T19:20:42", + "type" : "TempBasal", + "amount" : 0.90000000000000002, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T19:10:41", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T19:15:42", + "type" : "TempBasal", + "amount" : 0.89700996677740863, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T19:05:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T19:10:41", + "type" : "TempBasal", + "amount" : 0.60200668896321063, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T19:00:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T19:05:42", + "type" : "TempBasal", + "amount" : 0.90000000000000002, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T18:55:41", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T19:00:42", + "type" : "TempBasal", + "amount" : 0.89700996677740863, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T18:50:41", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T18:55:41", + "type" : "TempBasal", + "amount" : 0, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T18:45:41", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T18:50:41", + "type" : "TempBasal", + "amount" : 0, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T18:40:43", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T18:45:41", + "type" : "TempBasal", + "amount" : 0.30201342281879195, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T18:35:41", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T18:40:43", + "type" : "TempBasal", + "amount" : 0.59602649006622521, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T18:30:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T18:35:41", + "type" : "TempBasal", + "amount" : 0.9030100334448159, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T18:25:43", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T18:30:42", + "type" : "TempBasal", + "amount" : 0.9030100334448159, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T18:20:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T18:25:43", + "type" : "TempBasal", + "amount" : 0.89700996677740863, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T18:10:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T18:15:44", + "type" : "TempBasal", + "amount" : 0, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T18:05:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T18:10:42", + "type" : "TempBasal", + "amount" : 0.30000000000000004, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T18:00:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T18:05:42", + "type" : "TempBasal", + "amount" : 16.5, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T17:55:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T18:00:42", + "type" : "TempBasal", + "amount" : 0.90000000000000002, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T17:50:44", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T17:55:42", + "type" : "TempBasal", + "amount" : 0.60402684563758391, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T17:45:44", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T17:50:44", + "type" : "TempBasal", + "amount" : 0.90000000000000002, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T17:40:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T17:45:44", + "type" : "TempBasal", + "amount" : 1.7880794701986755, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T17:35:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T17:40:42", + "type" : "TempBasal", + "amount" : 1.2000000000000002, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T17:30:41", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T17:35:42", + "type" : "TempBasal", + "amount" : 1.7940199335548173, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T17:25:43", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T17:30:41", + "type" : "TempBasal", + "amount" : 1.8120805369127515, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T17:20:44", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T17:25:43", + "type" : "TempBasal", + "amount" : 0.60200668896321063, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T17:15:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T17:20:44", + "type" : "TempBasal", + "amount" : 0.89403973509933776, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T17:10:41", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T17:15:42", + "type" : "TempBasal", + "amount" : 0.89700996677740863, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T17:05:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T17:10:41", + "type" : "TempBasal", + "amount" : 0.60200668896321063, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T17:00:45", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T17:05:42", + "type" : "TempBasal", + "amount" : 46.969696969696969, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T16:50:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T17:00:45", + "type" : "TempBasal", + "amount" : 2.9850746268656714, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T16:45:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T16:50:42", + "type" : "TempBasal", + "amount" : 2.4000000000000004, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T16:40:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T16:45:42", + "type" : "TempBasal", + "amount" : 2.4000000000000004, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T16:35:43", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T16:40:42", + "type" : "TempBasal", + "amount" : 3.0100334448160533, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T16:30:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T16:35:43", + "type" : "TempBasal", + "amount" : 1.7940199335548173, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T16:25:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T16:30:42", + "type" : "TempBasal", + "amount" : 1.8, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T16:20:41", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T16:25:42", + "type" : "TempBasal", + "amount" : 0.59800664451827246, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T16:15:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T16:20:41", + "type" : "TempBasal", + "amount" : 0.9030100334448159, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T16:10:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T16:15:42", + "type" : "TempBasal", + "amount" : 0.90000000000000002, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T16:05:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T16:10:42", + "type" : "TempBasal", + "amount" : 0.60000000000000009, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T16:00:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T16:05:42", + "type" : "TempBasal", + "amount" : 0, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T15:45:42", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T15:50:42", + "type" : "TempBasal", + "amount" : 0, + "unit" : "U/hour" + }, + { + "start_at" : "2016-01-30T15:40:49", + "scheduled" : 0.80000000000000004, + "end_at" : "2016-01-30T15:45:42", + "type" : "TempBasal", + "amount" : 2.4573378839590441, + "unit" : "U/hour" + } +] diff --git a/LoopTests/Fixtures/insulin_math/reservoir_history_with_rewind_and_prime_output.json b/LoopTests/Fixtures/insulin_math/reservoir_history_with_rewind_and_prime_output.json new file mode 100644 index 0000000000..ff5dbd2ae9 --- /dev/null +++ b/LoopTests/Fixtures/insulin_math/reservoir_history_with_rewind_and_prime_output.json @@ -0,0 +1,434 @@ +[ + { + "amount": 0.075, + "start_at": "2016-01-30T20:30:43", + "description": "Reservoir decreased 0.075U over 4.97min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T20:35:41" + }, + { + "amount": 0.075, + "start_at": "2016-01-30T20:25:42", + "description": "Reservoir decreased 0.075U over 5.02min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T20:30:43" + }, + { + "amount": 0.05, + "start_at": "2016-01-30T20:20:42", + "description": "Reservoir decreased 0.05U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T20:25:42" + }, + { + "amount": 1.2, + "start_at": "2016-01-30T20:15:41", + "description": "Reservoir decreased 1.2U over 5.02min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T20:20:42" + }, + { + "amount": 0.25, + "start_at": "2016-01-30T20:10:42", + "description": "Reservoir decreased 0.25U over 4.98min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T20:15:41" + }, + { + "amount": 0.2, + "start_at": "2016-01-30T20:05:42", + "description": "Reservoir decreased 0.2U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T20:10:42" + }, + { + "amount": 0.25, + "start_at": "2016-01-30T20:00:41", + "description": "Reservoir decreased 0.25U over 5.02min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T20:05:42" + }, + { + "amount": 0.15, + "start_at": "2016-01-30T19:55:41", + "description": "Reservoir decreased 0.15U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T20:00:41" + }, + { + "amount": 0.2, + "start_at": "2016-01-30T19:50:41", + "description": "Reservoir decreased 0.2U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T19:55:41" + }, + { + "amount": 0.1, + "start_at": "2016-01-30T19:45:41", + "description": "Reservoir decreased 0.1U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T19:50:41" + }, + { + "amount": 0.075, + "start_at": "2016-01-30T19:40:41", + "description": "Reservoir decreased 0.075U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T19:45:41" + }, + { + "amount": 0.80, + "start_at": "2016-01-30T19:35:42", + "description": "Reservoir decreased 0.80U over 4.98min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T19:40:41" + }, + { + "amount": 0.7, + "start_at": "2016-01-30T19:25:42", + "description": "Reservoir decreased 0.7U over 10.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T19:35:42" + }, + { + "amount": 0.05, + "start_at": "2016-01-30T19:20:42", + "description": "Reservoir decreased 0.05U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T19:25:42" + }, + { + "amount": 0.075, + "start_at": "2016-01-30T19:15:42", + "description": "Reservoir decreased 0.075U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T19:20:42" + }, + { + "amount": 0.075, + "start_at": "2016-01-30T19:10:41", + "description": "Reservoir decreased 0.075U over 5.02min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T19:15:42" + }, + { + "amount": 0.05, + "start_at": "2016-01-30T19:05:42", + "description": "Reservoir decreased 0.05U over 4.98min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T19:10:41" + }, + { + "amount": 0.075, + "start_at": "2016-01-30T19:00:42", + "description": "Reservoir decreased 0.075U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T19:05:42" + }, + { + "amount": 0.075, + "start_at": "2016-01-30T18:55:41", + "description": "Reservoir decreased 0.075U over 5.02min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T19:00:42" + }, + { + "amount": 0.0, + "start_at": "2016-01-30T18:50:41", + "description": "Reservoir decreased 0.0U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T18:55:41" + }, + { + "amount": 0.0, + "start_at": "2016-01-30T18:45:41", + "description": "Reservoir decreased 0.0U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T18:50:41" + }, + { + "amount": 0.025, + "start_at": "2016-01-30T18:40:43", + "description": "Reservoir decreased 0.025U over 4.97min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T18:45:41" + }, + { + "amount": 0.05, + "start_at": "2016-01-30T18:35:41", + "description": "Reservoir decreased 0.05U over 5.03min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T18:40:43" + }, + { + "amount": 0.075, + "start_at": "2016-01-30T18:30:42", + "description": "Reservoir decreased 0.075U over 4.98min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T18:35:41" + }, + { + "amount": 0.075, + "start_at": "2016-01-30T18:25:43", + "description": "Reservoir decreased 0.075U over 4.98min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T18:30:42" + }, + { + "amount": 0.075, + "start_at": "2016-01-30T18:20:42", + "description": "Reservoir decreased 0.075U over 5.02min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T18:25:43" + }, + { + "amount": 0.0, + "start_at": "2016-01-30T18:10:42", + "description": "Reservoir decreased 0.0U over 5.03min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T18:15:44" + }, + { + "amount": 0.025, + "start_at": "2016-01-30T18:05:42", + "description": "Reservoir decreased 0.025U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T18:10:42" + }, + { + "amount": 1.375, + "start_at": "2016-01-30T18:00:42", + "description": "Reservoir decreased 1.375U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T18:05:42" + }, + { + "amount": 0.075, + "start_at": "2016-01-30T17:55:42", + "description": "Reservoir decreased 0.075U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T18:00:42" + }, + { + "amount": 0.05, + "start_at": "2016-01-30T17:50:44", + "description": "Reservoir decreased 0.05U over 4.97min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T17:55:42" + }, + { + "amount": 0.075, + "start_at": "2016-01-30T17:45:44", + "description": "Reservoir decreased 0.075U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T17:50:44" + }, + { + "amount": 0.15, + "start_at": "2016-01-30T17:40:42", + "description": "Reservoir decreased 0.15U over 5.03min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T17:45:44" + }, + { + "amount": 0.1, + "start_at": "2016-01-30T17:35:42", + "description": "Reservoir decreased 0.1U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T17:40:42" + }, + { + "amount": 0.15, + "start_at": "2016-01-30T17:30:41", + "description": "Reservoir decreased 0.15U over 5.02min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T17:35:42" + }, + { + "amount": 0.15, + "start_at": "2016-01-30T17:25:43", + "description": "Reservoir decreased 0.15U over 4.97min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T17:30:41" + }, + { + "amount": 0.05, + "start_at": "2016-01-30T17:20:44", + "description": "Reservoir decreased 0.05U over 4.98min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T17:25:43" + }, + { + "amount": 0.075, + "start_at": "2016-01-30T17:15:42", + "description": "Reservoir decreased 0.075U over 5.03min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T17:20:44" + }, + { + "amount": 0.075, + "start_at": "2016-01-30T17:10:41", + "description": "Reservoir decreased 0.075U over 5.02min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T17:15:42" + }, + { + "amount": 0.05, + "start_at": "2016-01-30T17:05:42", + "description": "Reservoir decreased 0.05U over 4.98min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T17:10:41" + }, + { + "amount": 3.875, + "start_at": "2016-01-30T17:00:45", + "description": "Reservoir decreased 3.875U over 4.95min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T17:05:42" + }, + { + "amount": 0.5, + "start_at": "2016-01-30T16:50:42", + "description": "Reservoir decreased 0.5U over 10.05min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T17:00:45" + }, + { + "amount": 0.2, + "start_at": "2016-01-30T16:45:42", + "description": "Reservoir decreased 0.2U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T16:50:42" + }, + { + "amount": 0.2, + "start_at": "2016-01-30T16:40:42", + "description": "Reservoir decreased 0.2U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T16:45:42" + }, + { + "amount": 0.25, + "start_at": "2016-01-30T16:35:43", + "description": "Reservoir decreased 0.25U over 4.98min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T16:40:42" + }, + { + "amount": 0.15, + "start_at": "2016-01-30T16:30:42", + "description": "Reservoir decreased 0.15U over 5.02min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T16:35:43" + }, + { + "amount": 0.15, + "start_at": "2016-01-30T16:25:42", + "description": "Reservoir decreased 0.15U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T16:30:42" + }, + { + "amount": 0.05, + "start_at": "2016-01-30T16:20:41", + "description": "Reservoir decreased 0.05U over 5.02min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T16:25:42" + }, + { + "amount": 0.075, + "start_at": "2016-01-30T16:15:42", + "description": "Reservoir decreased 0.075U over 4.98min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T16:20:41" + }, + { + "amount": 0.075, + "start_at": "2016-01-30T16:10:42", + "description": "Reservoir decreased 0.075U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T16:15:42" + }, + { + "amount": 0.05, + "start_at": "2016-01-30T16:05:42", + "description": "Reservoir decreased 0.05U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T16:10:42" + }, + { + "amount": 0.0, + "start_at": "2016-01-30T16:00:42", + "description": "Reservoir decreased 0.0U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T16:05:42" + }, + { + "amount": 0.0, + "start_at": "2016-01-30T15:45:42", + "description": "Reservoir decreased 0.0U over 5.00min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T15:50:42" + }, + { + "amount": 0.2, + "start_at": "2016-01-30T15:40:49", + "description": "Reservoir decreased 0.2U over 4.88min", + "type": "TempBasal", + "unit": "U", + "end_at": "2016-01-30T15:45:42" + } +] diff --git a/LoopTests/Fixtures/insulin_math/suspend_dose.json b/LoopTests/Fixtures/insulin_math/suspend_dose.json new file mode 100644 index 0000000000..513f3c68bf --- /dev/null +++ b/LoopTests/Fixtures/insulin_math/suspend_dose.json @@ -0,0 +1,9 @@ +[ + { + "type": "PumpSuspend", + "start_at": "2015-07-13T12:00:00", + "end_at": "2015-07-13T12:00:00", + "amount": 0.0, + "unit": "U" + } +] diff --git a/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled.json b/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled.json new file mode 100644 index 0000000000..513f3c68bf --- /dev/null +++ b/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled.json @@ -0,0 +1,9 @@ +[ + { + "type": "PumpSuspend", + "start_at": "2015-07-13T12:00:00", + "end_at": "2015-07-13T12:00:00", + "amount": 0.0, + "unit": "U" + } +] diff --git a/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled_normalized.json b/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled_normalized.json new file mode 100644 index 0000000000..c33e93c88c --- /dev/null +++ b/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled_normalized.json @@ -0,0 +1,9 @@ +[ + { + "type": "TempBasal", + "start_at": "2015-07-13T12:00:00", + "end_at": "2015-07-13T12:00:00", + "amount": 0, + "unit": "U/hour" + } +] diff --git a/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled_normalized_iob.json b/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled_normalized_iob.json new file mode 100644 index 0000000000..2b0f383a44 --- /dev/null +++ b/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled_normalized_iob.json @@ -0,0 +1 @@ +[{"unit":"U","date":"2015-07-13T12:00:00","value":0},{"unit":"U","date":"2015-07-13T12:05:00","value":0},{"unit":"U","date":"2015-07-13T12:10:00","value":0},{"unit":"U","date":"2015-07-13T12:15:00","value":0},{"unit":"U","date":"2015-07-13T12:20:00","value":0},{"unit":"U","date":"2015-07-13T12:25:00","value":0},{"unit":"U","date":"2015-07-13T12:30:00","value":0},{"unit":"U","date":"2015-07-13T12:35:00","value":0},{"unit":"U","date":"2015-07-13T12:40:00","value":0},{"unit":"U","date":"2015-07-13T12:45:00","value":0},{"unit":"U","date":"2015-07-13T12:50:00","value":0},{"unit":"U","date":"2015-07-13T12:55:00","value":0},{"unit":"U","date":"2015-07-13T13:00:00","value":0},{"unit":"U","date":"2015-07-13T13:05:00","value":0},{"unit":"U","date":"2015-07-13T13:10:00","value":0},{"unit":"U","date":"2015-07-13T13:15:00","value":0},{"unit":"U","date":"2015-07-13T13:20:00","value":0},{"unit":"U","date":"2015-07-13T13:25:00","value":0},{"unit":"U","date":"2015-07-13T13:30:00","value":0},{"unit":"U","date":"2015-07-13T13:35:00","value":0},{"unit":"U","date":"2015-07-13T13:40:00","value":0},{"unit":"U","date":"2015-07-13T13:45:00","value":0},{"unit":"U","date":"2015-07-13T13:50:00","value":0},{"unit":"U","date":"2015-07-13T13:55:00","value":0},{"unit":"U","date":"2015-07-13T14:00:00","value":0},{"unit":"U","date":"2015-07-13T14:05:00","value":0},{"unit":"U","date":"2015-07-13T14:10:00","value":0},{"unit":"U","date":"2015-07-13T14:15:00","value":0},{"unit":"U","date":"2015-07-13T14:20:00","value":0},{"unit":"U","date":"2015-07-13T14:25:00","value":0},{"unit":"U","date":"2015-07-13T14:30:00","value":0},{"unit":"U","date":"2015-07-13T14:35:00","value":0},{"unit":"U","date":"2015-07-13T14:40:00","value":0},{"unit":"U","date":"2015-07-13T14:45:00","value":0},{"unit":"U","date":"2015-07-13T14:50:00","value":0},{"unit":"U","date":"2015-07-13T14:55:00","value":0},{"unit":"U","date":"2015-07-13T15:00:00","value":0},{"unit":"U","date":"2015-07-13T15:05:00","value":0},{"unit":"U","date":"2015-07-13T15:10:00","value":0},{"unit":"U","date":"2015-07-13T15:15:00","value":0},{"unit":"U","date":"2015-07-13T15:20:00","value":0},{"unit":"U","date":"2015-07-13T15:25:00","value":0},{"unit":"U","date":"2015-07-13T15:30:00","value":0},{"unit":"U","date":"2015-07-13T15:35:00","value":0},{"unit":"U","date":"2015-07-13T15:40:00","value":0},{"unit":"U","date":"2015-07-13T15:45:00","value":0},{"unit":"U","date":"2015-07-13T15:50:00","value":0},{"unit":"U","date":"2015-07-13T15:55:00","value":0},{"unit":"U","date":"2015-07-13T16:00:00","value":0},{"unit":"U","date":"2015-07-13T16:05:00","value":0},{"unit":"U","date":"2015-07-13T16:10:00","value":0}] diff --git a/LoopTests/Managers/CGMStalenessMonitorTests.swift b/LoopTests/Managers/CGMStalenessMonitorTests.swift index 5971d75daa..3f4853ab10 100644 --- a/LoopTests/Managers/CGMStalenessMonitorTests.swift +++ b/LoopTests/Managers/CGMStalenessMonitorTests.swift @@ -9,7 +9,6 @@ import XCTest import Foundation import LoopKit -import LoopAlgorithm @testable import Loop class CGMStalenessMonitorTests: XCTestCase { diff --git a/LoopTests/Managers/DeviceDataManagerTests.swift b/LoopTests/Managers/DeviceDataManagerTests.swift index b3725a8545..bf12ef5ede 100644 --- a/LoopTests/Managers/DeviceDataManagerTests.swift +++ b/LoopTests/Managers/DeviceDataManagerTests.swift @@ -8,7 +8,6 @@ import XCTest import HealthKit -import LoopAlgorithm import LoopKit import LoopKitUI import LoopCore diff --git a/LoopTests/Managers/DoseEnactorTests.swift b/LoopTests/Managers/DoseEnactorTests.swift index 5609d03952..39b408de6b 100644 --- a/LoopTests/Managers/DoseEnactorTests.swift +++ b/LoopTests/Managers/DoseEnactorTests.swift @@ -9,7 +9,6 @@ import XCTest import Foundation import LoopKit -import LoopAlgorithm @testable import Loop diff --git a/LoopTests/Managers/GlucoseAlertManagerTests.swift b/LoopTests/Managers/GlucoseAlertManagerTests.swift index 7091479a3f..ba53cb6733 100644 --- a/LoopTests/Managers/GlucoseAlertManagerTests.swift +++ b/LoopTests/Managers/GlucoseAlertManagerTests.swift @@ -6,7 +6,6 @@ // import XCTest -import LoopAlgorithm import LoopKit import LoopKitUI @testable import Loop diff --git a/LoopTests/Managers/LoopDataManagerTests.swift b/LoopTests/Managers/LoopDataManagerTests.swift index 13bc5b470b..3508ede8e6 100644 --- a/LoopTests/Managers/LoopDataManagerTests.swift +++ b/LoopTests/Managers/LoopDataManagerTests.swift @@ -7,8 +7,8 @@ // import XCTest -import LoopKit import LoopAlgorithm +import LoopKit @testable import LoopCore @testable import Loop @@ -185,7 +185,7 @@ class LoopDataManagerTests: XCTestCase { let insulinSensitivitySchedule = InsulinSensitivitySchedule( unit: .milligramsPerDeciliter, dailyItems: [ - RepeatingScheduleValue(startTime: 0, value: predictionInput.sensitivity.first!.value.doubleValue(for: .milligramsPerDeciliter)) + RepeatingScheduleValue(startTime: 0, value: predictionInput.sensitivityMgdlPerUnit.first!.value) ], timeZone: .utcTimeZone )! @@ -230,7 +230,7 @@ class LoopDataManagerTests: XCTestCase { for (expected, calculated) in zip(expectedPredictedGlucose, predictedGlucose!) { XCTAssertEqual(expected.startDate, calculated.startDate) - XCTAssertEqual(expected.quantity.doubleValue(for: .milligramsPerDeciliter), calculated.quantity.doubleValue(for: .milligramsPerDeciliter), accuracy: defaultAccuracy) + XCTAssertEqual(expected.quantity.doubleValue(for: .milligramsPerDeciliter), calculated.mgdl, accuracy: defaultAccuracy) } await loopDataManager.loop() @@ -255,6 +255,42 @@ class LoopDataManagerTests: XCTestCase { XCTAssertEqual(0.2, deliveryDelegate.lastEnact.bolus!, accuracy: defaultAccuracy) } + func testMmolSettingsDoseTheSameAsMgdl() async throws { + func mmol(_ mgdl: Double) -> Double { + LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: mgdl).doubleValue(for: .millimolesPerLiter) + } + settingsProvider.settings.insulinSensitivitySchedule = InsulinSensitivitySchedule( + unit: .millimolesPerLiter, + dailyItems: [ + RepeatingScheduleValue(startTime: 0, value: mmol(45)), + RepeatingScheduleValue(startTime: 32400, value: mmol(55)) + ], + timeZone: .utcTimeZone + )! + settingsProvider.settings.glucoseTargetRangeSchedule = GlucoseRangeSchedule(unit: .millimolesPerLiter, dailyItems: [ + RepeatingScheduleValue(startTime: TimeInterval(0), value: DoubleRange(minValue: mmol(100), maxValue: mmol(110))), + RepeatingScheduleValue(startTime: TimeInterval(28800), value: DoubleRange(minValue: mmol(90), maxValue: mmol(100))), + RepeatingScheduleValue(startTime: TimeInterval(75600), value: DoubleRange(minValue: mmol(100), maxValue: mmol(110))) + ], timeZone: .utcTimeZone)! + settingsProvider.settings.suspendThreshold = GlucoseThreshold(unit: .millimolesPerLiter, value: mmol(75)) + glucoseStore.storedGlucose = [ + StoredGlucoseSample(startDate: d(.minutes(-1)), quantity: LoopQuantity(unit: .millimolesPerLiter, doubleValue: mmol(120))), + ] + + let input = try await loopDataManager.fetchData(for: now) + let sensitivityNow = input.sensitivityMgdlPerUnit.first { $0.startDate <= now && $0.endDate > now }!.value + let targetNow = input.targetMgdl.first { $0.startDate <= now && $0.endDate > now }!.value + XCTAssertEqual(sensitivityNow, 55, accuracy: 1e-9) + XCTAssertEqual(targetNow.lowerBound, 90, accuracy: 1e-9) + XCTAssertEqual(targetNow.upperBound, 100, accuracy: 1e-9) + XCTAssertEqual(input.suspendThresholdMgdl!, 75, accuracy: 1e-9) + + await loopDataManager.updateDisplayState() + XCTAssertEqual(120, loopDataManager.eventualBG!, accuracy: 1e-9) + + await loopDataManager.loop() + XCTAssertEqual(0.2, deliveryDelegate.lastEnact.bolus!, accuracy: defaultAccuracy) + } func testHighAndFalling() async { glucoseStore.storedGlucose = [ @@ -464,9 +500,9 @@ class LoopDataManagerTests: XCTestCase { func testFetchDataWithHighInsulinNeedsPresetMitigation() async throws { var input = try await loopDataManager.fetchData(for: now) - XCTAssertEqual(input.target.count, 1) - XCTAssertEqual(input.target[0].value.doubleRange(for: .milligramsPerDeciliter), DoubleRange(minValue: 90, maxValue: 100)) - XCTAssertEqual(input.suspendThreshold?.doubleValue(for: .milligramsPerDeciliter), 75.0) + XCTAssertEqual(input.targetMgdl.count, 1) + XCTAssertEqual(input.targetMgdl[0].value, 90...100) + XCTAssertEqual(input.suspendThresholdMgdl, 75.0) let override = TemporaryScheduleOverride( context: .custom, @@ -483,9 +519,9 @@ class LoopDataManagerTests: XCTestCase { temporaryPresetsManager.scheduleOverride = override input = try await loopDataManager.fetchData(for: now) - XCTAssertEqual(input.target.count, 1) - XCTAssertEqual(input.target[0].value.doubleRange(for: .milligramsPerDeciliter), DoubleRange(minValue: 110, maxValue: 110)) - XCTAssertEqual(input.suspendThreshold?.doubleValue(for: .milligramsPerDeciliter), 110.0) + XCTAssertEqual(input.targetMgdl.count, 1) + XCTAssertEqual(input.targetMgdl[0].value, 110...110) + XCTAssertEqual(input.suspendThresholdMgdl, 110.0) } } @@ -524,7 +560,7 @@ extension LoopQuantity { extension LoopDataManager { var eventualBG: Double? { - displayState.output?.predictedGlucose.last?.quantity.doubleValue(for: .milligramsPerDeciliter) + displayState.output?.predictedGlucose.last?.mgdl } } @@ -532,9 +568,7 @@ extension StoredGlucoseSample { static func from(fixture: FixtureGlucoseSample) -> StoredGlucoseSample { return StoredGlucoseSample( startDate: fixture.startDate, - quantity: fixture.quantity, - condition: fixture.condition, - trendRate: fixture.trendRate, + quantity: LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: fixture.mgdl), isDisplayOnly: fixture.isDisplayOnly, wasUserEntered: fixture.wasUserEntered ) @@ -558,7 +592,7 @@ extension StoredCarbEntry { static func from(fixture: FixtureCarbEntry) -> StoredCarbEntry { return StoredCarbEntry( startDate: fixture.startDate, - quantity: fixture.quantity, + quantity: LoopQuantity(unit: .gram, doubleValue: fixture.grams), foodType: fixture.foodType, absorptionTime: fixture.absorptionTime ) diff --git a/LoopTests/Managers/MealDetectionManagerTests.swift b/LoopTests/Managers/MealDetectionManagerTests.swift index 66e0ea6664..ab07e9cb87 100644 --- a/LoopTests/Managers/MealDetectionManagerTests.swift +++ b/LoopTests/Managers/MealDetectionManagerTests.swift @@ -7,22 +7,18 @@ // import XCTest +import LoopAlgorithm import LoopCore import LoopKit -import LoopAlgorithm @testable import Loop -fileprivate class MockGlucoseSample: GlucoseSampleValue { +fileprivate class MockGlucoseSample: LoopAlgorithm.GlucoseSampleValue { let provenanceIdentifier = "" let isDisplayOnly: Bool let wasUserEntered: Bool - let condition: GlucoseCondition? = nil - let trendRate: LoopQuantity? = nil - var trend: LoopKit.GlucoseTrend? - var syncIdentifier: String? - let quantity: LoopQuantity = LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: 100) + let mgdl: Double = 100 let startDate: Date init(startDate: Date, isDisplayOnly: Bool = false, wasUserEntered: Bool = false) { @@ -222,17 +218,17 @@ class MealDetectionManagerTests: XCTestCase { doses: [], carbEntries: testType.carbEntries.map { $0.asStoredCarbEntry }, predictionStart: date, - basal: BasalRateSchedule(dailyItems: [RepeatingScheduleValue(startTime: 0, value: 1.0)])!.between(start: historyStart, end: date), - sensitivity: testType.insulinSensitivitySchedule.quantitiesBetween(start: historyStart, end: date), - carbRatio: testType.carbSchedule.between(start: historyStart, end: date), - target: glucoseTarget!.quantityBetween(start: historyStart, end: date), - suspendThreshold: LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: 65), + basal: BasalRateSchedule(dailyItems: [RepeatingScheduleValue(startTime: 0, value: 1.0)])!.between(start: historyStart, end: date).algorithmSchedule(), + sensitivityMgdlPerUnit: testType.insulinSensitivitySchedule.quantitiesBetween(start: historyStart, end: date).algorithmMgdlSchedule(), + carbRatio: testType.carbSchedule.between(start: historyStart, end: date).algorithmSchedule(), + targetMgdl: glucoseTarget!.quantityBetween(start: historyStart, end: date).algorithmMgdlRanges(), + suspendThresholdMgdl: 65, maxBolus: maximumBolus!, maxBasalRate: maximumBasalRatePerHour, useIntegralRetrospectiveCorrection: false, includePositiveVelocityAndRC: true, carbAbsorptionModel: .piecewiseLinear, - recommendationInsulinModel: ExponentialInsulinModelPreset.rapidActingAdult.model, + recommendationInsulinModel: AlgorithmInsulinModel(ExponentialInsulinModelPreset.rapidActingAdult.model), recommendationType: .automaticBolus) // These tests don't actually run the loop algorithm directly; they were written to take ICE from fixtures, compute carb effects, and subtract them. @@ -243,13 +239,13 @@ class MealDetectionManagerTests: XCTestCase { let carbStatus = carbEntries.map( to: counteractionEffects, carbRatio: algorithmInput.carbRatio, - insulinSensitivity: algorithmInput.sensitivity + insulinSensitivityMgdlPerUnit: algorithmInput.sensitivityMgdlPerUnit ) let carbEffects = carbStatus.dynamicGlucoseEffects( - from: date.addingTimeInterval(-IntegralRetrospectiveCorrection.retrospectionInterval), + from: date.addingTimeInterval(-.minutes(180)), carbRatios: algorithmInput.carbRatio, - insulinSensitivities: algorithmInput.sensitivity, + insulinSensitivitiesMgdlPerUnit: algorithmInput.sensitivityMgdlPerUnit, absorptionModel: algorithmInput.carbAbsorptionModel.model ) @@ -279,15 +275,15 @@ class MealDetectionManagerTests: XCTestCase { } - private func counteractionEffects(for testType: MissedMealTestType) -> [GlucoseEffectVelocity] { + private func counteractionEffects(for testType: MissedMealTestType) -> [LoopAlgorithm.GlucoseEffectVelocity] { let fixture: [JSONDictionary] = loadFixture(testType.counteractionEffectFixture) let dateFormatter = ISO8601DateFormatter.localTimeDate() return fixture.map { - GlucoseEffectVelocity(startDate: dateFormatter.date(from: $0["startDate"] as! String)!, - endDate: dateFormatter.date(from: $0["endDate"] as! String)!, - quantity: LoopQuantity(unit: LoopUnit(from: $0["unit"] as! String), - doubleValue:$0["value"] as! Double)) + LoopAlgorithm.GlucoseEffectVelocity(startDate: dateFormatter.date(from: $0["startDate"] as! String)!, + endDate: dateFormatter.date(from: $0["endDate"] as! String)!, + mgdlPerSecond: LoopQuantity(unit: LoopUnit(from: $0["unit"] as! String), + doubleValue: $0["value"] as! Double).doubleValue(for: .milligramsPerDeciliterPerSecond)) } } diff --git a/LoopTests/Mock Stores/MockDoseStore.swift b/LoopTests/Mock Stores/MockDoseStore.swift index 93aaa73068..51331229e6 100644 --- a/LoopTests/Mock Stores/MockDoseStore.swift +++ b/LoopTests/Mock Stores/MockDoseStore.swift @@ -8,7 +8,6 @@ import HealthKit import LoopKit -import LoopAlgorithm @testable import Loop class MockDoseStore: DoseStoreProtocol { diff --git a/LoopTests/Mock Stores/MockGlucoseStore.swift b/LoopTests/Mock Stores/MockGlucoseStore.swift index eb4c488841..5ec9452116 100644 --- a/LoopTests/Mock Stores/MockGlucoseStore.swift +++ b/LoopTests/Mock Stores/MockGlucoseStore.swift @@ -7,7 +7,6 @@ // import LoopKit -import LoopAlgorithm @testable import Loop class MockGlucoseStore: GlucoseStoreProtocol { diff --git a/LoopTests/Mocks/LoopControlMock.swift b/LoopTests/Mocks/LoopControlMock.swift index af720c315a..3c03d8f9ee 100644 --- a/LoopTests/Mocks/LoopControlMock.swift +++ b/LoopTests/Mocks/LoopControlMock.swift @@ -8,7 +8,6 @@ import XCTest import Foundation -import LoopAlgorithm import LoopKit @testable import Loop diff --git a/LoopTests/Mocks/MockDeliveryDelegate.swift b/LoopTests/Mocks/MockDeliveryDelegate.swift index 9881a8053a..1f37b4bbd1 100644 --- a/LoopTests/Mocks/MockDeliveryDelegate.swift +++ b/LoopTests/Mocks/MockDeliveryDelegate.swift @@ -8,7 +8,6 @@ import Foundation import LoopKit -import LoopAlgorithm @testable import Loop class MockDeliveryDelegate: DeliveryDelegate { diff --git a/LoopTests/Mocks/MockSettingsProvider.swift b/LoopTests/Mocks/MockSettingsProvider.swift index 7f9774ccee..6ea6f8f8b1 100644 --- a/LoopTests/Mocks/MockSettingsProvider.swift +++ b/LoopTests/Mocks/MockSettingsProvider.swift @@ -8,7 +8,6 @@ import Foundation import LoopKit -import LoopAlgorithm @testable import Loop @Observable diff --git a/LoopTests/Models/DoseEntryInsulinMathTests.swift b/LoopTests/Models/DoseEntryInsulinMathTests.swift new file mode 100644 index 0000000000..fb72b9cb7b --- /dev/null +++ b/LoopTests/Models/DoseEntryInsulinMathTests.swift @@ -0,0 +1,510 @@ +// +// DoseEntryInsulinMathTests.swift +// LoopTests +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import XCTest +import LoopAlgorithm +@testable import LoopKit +@testable import Loop + +fileprivate struct NewReservoirValue: ReservoirValue { + let startDate: Date + let unitVolume: Double +} + +fileprivate extension DoseUnit { + var unit: LoopUnit { + switch self { + case .units: + return .internationalUnit + case .unitsPerHour: + return .internationalUnitsPerHour + } + } +} + +fileprivate extension DoseEntry { + + func basalRelativeDose(with insulinModel: LoopKit.InsulinModel) -> BasalRelativeDose { + var basalRelativeDoseType: BasalRelativeDoseType + switch type { + case .bolus: + basalRelativeDoseType = .bolus + case .basal: + basalRelativeDoseType = .basal(scheduledRate: value) + default: + if let scheduledRate = scheduledBasalRate?.doubleValue(for: .internationalUnitsPerHour) { + basalRelativeDoseType = .basal(scheduledRate: scheduledRate) + } else { + basalRelativeDoseType = .basal(scheduledRate: 0) // Seems odd, but this is existing behavior + } + } + + return BasalRelativeDose( + type: basalRelativeDoseType, + startDate: startDate, + endDate: endDate, + volume: deliveredUnits ?? programmedUnits, + insulinModel: AlgorithmInsulinModel(insulinModel) + ) + } +} + +fileprivate extension BasalRelativeDose { + var netBasalUnitsPerHour: Double { + if duration > 0 { + return netBasalUnits / duration.hours + } else { + return 0 + } + } + + public var unitsPerHour: Double { + return volume / duration.hours + } +} + +class DoseEntryInsulinMathTests: XCTestCase { + + private var fixtureDateformatter: DateFormatter! + + private let fixtureTimeZone = TimeZone(secondsFromGMT: 0)! + + let exponentialModel = ExponentialInsulinModel(actionDuration: TimeInterval(minutes: 360), peakActivityTime: TimeInterval(minutes: 75)) + + let standardInsulinModel = ExponentialInsulinModelPreset.rapidActingAdult.model + + let walshModel = WalshInsulinModel(actionDuration: TimeInterval(hours: 4)) + + private func fixtureDate(_ input: String) -> Date { + return fixtureDateformatter.date(from: input)! + } + + override func setUp() { + fixtureDateformatter = DateFormatter.descriptionFormatter + fixtureDateformatter.timeZone = fixtureTimeZone + } + + private func loadFixture(_ resourceName: String) -> T { + let path = Bundle(for: type(of: self)).path(forResource: resourceName, ofType: "json")! + return try! JSONSerialization.jsonObject(with: Data(contentsOf: URL(fileURLWithPath: path)), options: []) as! T + } + + private func printInsulinValues(_ insulinValues: [LoopAlgorithm.InsulinValue]) { + print("\n\n") + print(String(data: try! JSONSerialization.data( + withJSONObject: insulinValues.map({ (value) -> [String: Any] in + return [ + "date": ISO8601DateFormatter.localTimeDate(timeZone: fixtureTimeZone).string(from: value.startDate), + "value": value.value, + "unit": "U" + ] + }), + options: [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]), encoding: .utf8)!) + print("\n\n") + } + + private func loadReservoirFixture(_ resourceName: String) -> [NewReservoirValue] { + + let fixture: [JSONDictionary] = loadFixture(resourceName) + let dateFormatter = ISO8601DateFormatter.localTimeDate(timeZone: fixtureTimeZone) + + return fixture.map { + return NewReservoirValue(startDate: dateFormatter.date(from: $0["date"] as! String)!, unitVolume: $0["amount"] as! Double) + } + } + + func loadDoseFixture(_ resourceName: String, insulinType: InsulinType? = .novolog) -> [DoseEntry] { + let fixture: [JSONDictionary] = loadFixture(resourceName) + let dateFormatter = ISO8601DateFormatter.localTimeDate(timeZone: fixtureTimeZone) + + return fixture.compactMap { + guard let unit = DoseUnit(rawValue: $0["unit"] as! String), + let pumpType = PumpEventType(rawValue: $0["type"] as! String), + let type = DoseType(pumpEventType: pumpType) + else { + return nil + } + + let decisionId = $0["decisionId"] as? String + + var dose = DoseEntry( + type: type, + startDate: dateFormatter.date(from: $0["start_at"] as! String)!, + endDate: dateFormatter.date(from: $0["end_at"] as! String)!, + value: $0["amount"] as! Double, + unit: unit, + decisionId: UUID(uuidString: decisionId ?? ""), + deliveredUnits: $0["delivered"] as? Double, + description: $0["description"] as? String, + syncIdentifier: $0["raw"] as? String, + insulinType: insulinType, + automatic: $0["automatic"] as? Bool, + manuallyEntered: $0["manuallyEntered"] as? Bool ?? false, + isMutable: $0["isMutable"] as? Bool ?? false + ) + + if let scheduled = $0["scheduled"] as? Double { + dose.scheduledBasalRate = LoopQuantity(unit: unit.unit, doubleValue: scheduled) + } + + return dose + } + } + + func loadInsulinValueFixture(_ resourceName: String) -> [InsulinValue] { + let fixture: [JSONDictionary] = loadFixture(resourceName) + let dateFormatter = ISO8601DateFormatter.localTimeDate(timeZone: fixtureTimeZone) + + return fixture.map { + return InsulinValue(startDate: dateFormatter.date(from: $0["date"] as! String)!, value: $0["value"] as! Double) + } + } + + func loadBasalRateScheduleFixture(_ resourceName: String) -> BasalRateSchedule { + let fixture: [JSONDictionary] = loadFixture(resourceName) + + let items = fixture.map { + return RepeatingScheduleValue(startTime: TimeInterval(minutes: $0["minutes"] as! Double), value: $0["rate"] as! Double) + } + + return BasalRateSchedule(dailyItems: items, timeZone: fixtureTimeZone)! + } + + func testIOBFromSuspend() { + let input = loadDoseFixture("suspend_dose") + let reconciledOutput = loadDoseFixture("suspend_dose_reconciled") + let normalizedOutput = loadDoseFixture("suspend_dose_reconciled_normalized") + let iobOutput = loadInsulinValueFixture("suspend_dose_reconciled_normalized_iob") + let basals = loadBasalRateScheduleFixture("basal") + + let reconciled = input.reconciled() + + XCTAssertEqual(reconciledOutput.count, reconciled.count) + + for (expected, calculated) in zip(reconciledOutput, reconciled) { + XCTAssertEqual(expected.startDate, calculated.startDate) + XCTAssertEqual(expected.endDate, calculated.endDate) + XCTAssertEqual(expected.value, calculated.value) + XCTAssertEqual(expected.unit, calculated.unit) + } + + let basalTimeline = basals.between(start: input.first!.startDate, end: input.first!.startDate).algorithmSchedule() + + let reconciledWithModel = reconciled.map { $0.toMockInsulinDose(with: walshModel) } + + let normalized = reconciledWithModel.annotated(with: basalTimeline) + + XCTAssertEqual(normalizedOutput.count, normalized.count) + + for (expected, calculated) in zip(normalizedOutput, normalized) { + XCTAssertEqual(expected.startDate, calculated.startDate) + XCTAssertEqual(expected.endDate, calculated.endDate) + XCTAssertEqual(expected.value, calculated.netBasalUnitsPerHour, accuracy: Double(Float.ulpOfOne)) + } + + let iob = normalized.insulinOnBoardTimeline(longestEffectDuration: walshModel.effectDuration) + + XCTAssertEqual(iobOutput.count, iob.count) + + for (expected, calculated) in zip(iobOutput, iob) { + XCTAssertEqual(expected.startDate, calculated.startDate) + XCTAssertEqual(expected.value, calculated.value, accuracy: Double(Float.ulpOfOne)) + } + } + + func testIOBFromDoses() { + let input = loadDoseFixture("normalized_doses", insulinType: .novolog) + let output = loadInsulinValueFixture("iob_from_doses_output") + + let basalRelativeInput = input.map { $0.basalRelativeDose(with: walshModel) } + + measure { + _ = basalRelativeInput.insulinOnBoardTimeline(longestEffectDuration: walshModel.effectDuration) + } + + let iob = basalRelativeInput.insulinOnBoardTimeline(longestEffectDuration: walshModel.effectDuration) + + XCTAssertEqual(output.count, iob.count) + + for (expected, calculated) in zip(output, iob) { + XCTAssertEqual(expected.startDate, calculated.startDate) + XCTAssertEqual(expected.value, calculated.value, accuracy: 0.5) + } + } + + func testIOBFromNoDoses() { + let input: [DoseEntry] = [] + + let basalRelativeInput = input.map { $0.basalRelativeDose(with: standardInsulinModel) } + + let iob = basalRelativeInput.insulinOnBoardTimeline(longestEffectDuration: standardInsulinModel.effectDuration) + + XCTAssertEqual(0, iob.count) + } + + func testIOBFromDosesExponential() { + let input = loadDoseFixture("normalized_doses", insulinType: .novolog) + let output = loadInsulinValueFixture("iob_from_doses_exponential_output") + + let basalRelativeInput = input.map { $0.basalRelativeDose(with: exponentialModel) } + + measure { + _ = basalRelativeInput.insulinOnBoardTimeline(longestEffectDuration: exponentialModel.effectDuration) + } + + let iob = basalRelativeInput.insulinOnBoardTimeline() + + XCTAssertEqual(output.count, iob.count) + + for (expected, calculated) in zip(output, iob) { + XCTAssertEqual(expected.startDate, calculated.startDate) + XCTAssertEqual(expected.value, calculated.value, accuracy: 0.5) + } + } + + func testIOBFromDosesWithDifferentInsulinCurves() { + let formatter = DateFormatter.descriptionFormatter + let f = { (input) in + return formatter.date(from: input)! + } + let output = loadInsulinValueFixture("iob_from_multiple_curves_output") + + let doses = [ + DoseEntry(type: .basal, startDate: f("2018-05-15 14:42:36 +0000"), endDate: f("2018-05-16 14:42:36 +0000"), value: 0.84999999999999998, unit: .unitsPerHour, decisionId: nil, syncIdentifier: "7b02646a070f120e2200", scheduledBasalRate: nil), + DoseEntry(type: .bolus, startDate: f("2018-05-15 14:44:46 +0000"), endDate: f("2018-05-15 14:44:46 +0000"), value: 0.9, unit: .units, decisionId: nil, syncIdentifier: "01004a004a006d006e22354312", scheduledBasalRate: nil), + DoseEntry(type: .tempBasal, startDate: f("2018-05-15 14:42:36 +0000"), endDate: f("2018-05-15 14:42:36 +0000"), value: 0.0, unit: .unitsPerHour, decisionId: nil, syncIdentifier: "1600646a074f12", scheduledBasalRate: nil), + DoseEntry(type: .tempBasal, startDate: f("2018-05-15 14:32:51 +0000"), endDate: f("2018-05-15 15:02:51 +0000"), value: 1.8999999999999999, unit: .unitsPerHour, decisionId: nil, syncIdentifier: "16017360074f12", scheduledBasalRate: nil), + DoseEntry(type: .bolus, startDate: f("2018-05-15 14:52:51 +0000"), endDate: f("2018-05-15 15:52:51 +0000"), value: 0.9, unit: .units, decisionId: nil, syncIdentifier: "01004a004a006d006e22354312", scheduledBasalRate: nil), + ] + + let basalRelativeInput = doses.map { $0.basalRelativeDose(with: exponentialModel) } + + let iobWithoutModel = basalRelativeInput.insulinOnBoardTimeline() + + let dosesWithModel = [ + DoseEntry(type: .basal, startDate: f("2018-05-15 14:42:36 +0000"), endDate: f("2018-05-16 14:42:36 +0000"), value: 0.84999999999999998, unit: .unitsPerHour, decisionId: nil, syncIdentifier: "7b02646a070f120e2200", scheduledBasalRate: nil), + DoseEntry(type: .bolus, startDate: f("2018-05-15 14:44:46 +0000"), endDate: f("2018-05-15 14:44:46 +0000"), value: 0.9, unit: .units, decisionId: nil, syncIdentifier: "01004a004a006d006e22354312", scheduledBasalRate: nil, insulinType: .fiasp), + DoseEntry(type: .tempBasal, startDate: f("2018-05-15 14:42:36 +0000"), endDate: f("2018-05-15 14:42:36 +0000"), value: 0.0, unit: .unitsPerHour, decisionId: nil, syncIdentifier: "1600646a074f12", scheduledBasalRate: nil), + DoseEntry(type: .tempBasal, startDate: f("2018-05-15 14:32:51 +0000"), endDate: f("2018-05-15 15:02:51 +0000"), value: 1.8999999999999999, unit: .unitsPerHour, decisionId: nil, syncIdentifier: "16017360074f12", scheduledBasalRate: nil), + DoseEntry(type: .bolus, startDate: f("2018-05-15 14:52:51 +0000"), endDate: f("2018-05-15 15:52:51 +0000"), value: 0.9, unit: .units, decisionId: nil, syncIdentifier: "01004a004a006d006e22354312", scheduledBasalRate: nil, insulinType: .novolog), + ] + + let dosesWithModelRelative = dosesWithModel.map { entry in + let insulinModel: InsulinModel + switch entry.insulinType { + case .fiasp: + insulinModel = ExponentialInsulinModelPreset.fiasp + default: + insulinModel = ExponentialInsulinModelPreset.rapidActingChild + } + return entry.basalRelativeDose(with: insulinModel) } + + let iobWithModel = dosesWithModelRelative.insulinOnBoardTimeline() + + XCTAssertEqual(iobWithoutModel.count, iobWithModel.count) + + for (expected, calculated) in zip(output, iobWithModel) { + XCTAssertEqual(expected.startDate, calculated.startDate) + XCTAssertEqual(expected.value, calculated.value, accuracy: 0.00001) + } + + } + + func testIOBFromReservoirDoses() { + let input = loadDoseFixture("normalized_reservoir_history_output") + let output = loadInsulinValueFixture("iob_from_reservoir_output") + + let basalRelativeInput = input.map { $0.basalRelativeDose(with: walshModel) } + + measure { + _ = basalRelativeInput.insulinOnBoardTimeline(longestEffectDuration: walshModel.effectDuration) + } + + let iob = basalRelativeInput.insulinOnBoardTimeline(longestEffectDuration: walshModel.effectDuration) + + XCTAssertEqual(output.count, iob.count) + + printInsulinValues(iob) + + for (expected, calculated) in zip(output, iob) { + XCTAssertEqual(expected.startDate, calculated.startDate) + XCTAssertEqual(expected.value, calculated.value, accuracy: 0.01) + } + } + + func testNormalizeReservoirDoses() { + let input = loadDoseFixture("reservoir_history_with_rewind_and_prime_output") + let output = loadDoseFixture("normalized_reservoir_history_output") + let basals = loadBasalRateScheduleFixture("basal") + + let basalTimeline = basals.between(start: input.last!.startDate, end: input.first!.endDate).algorithmSchedule() + + let inputWithModel = input.map { $0.toMockInsulinDose(with: ExponentialInsulinModelPreset.rapidActingAdult.model) } + + + measure { + _ = inputWithModel.annotated(with: basalTimeline) + } + + let doses = inputWithModel.annotated(with: basalTimeline) + + XCTAssertEqual(output.count, doses.count) + + // Total delivery on split doses should add up to delivery from original doses + XCTAssertEqual( + input.map {$0.unitsInDeliverableIncrements}.reduce(0,+), + doses.map {$0.volume}.reduce(0,+), + accuracy: Double(Float.ulpOfOne)) + + for (expected, calculated) in zip(output, doses) { + XCTAssertEqual(expected.startDate, calculated.startDate) + XCTAssertEqual(expected.endDate, calculated.endDate) + XCTAssertEqual(expected.value, calculated.unitsPerHour, accuracy: Double(Float.ulpOfOne)) + switch calculated.type { + case .basal(let scheduledRate): + XCTAssertEqual(expected.scheduledBasalRate?.doubleValue(for: .internationalUnitsPerHour), scheduledRate) + case .bolus: + XCTFail("Unexpected bolus in dose history") + } + } + } + + func testNormalizeEdgeCaseDoses() { + let input = loadDoseFixture("normalize_edge_case_doses_input") + let output = loadDoseFixture("normalize_edge_case_doses_output") + let basals = loadBasalRateScheduleFixture("basal") + + let basalTimeline = basals.between(start: input.first!.startDate, end: input.last!.endDate).algorithmSchedule() + + let inputWithModel = input.map { $0.toMockInsulinDose(with: ExponentialInsulinModelPreset.rapidActingAdult.model) } + + + measure { + _ = inputWithModel.annotated(with: basalTimeline) + } + + let doses = inputWithModel.annotated(with: basalTimeline) + + XCTAssertEqual(output.count, doses.count) + + for (expected, calculated) in zip(output, doses) { + XCTAssertEqual(expected.startDate, calculated.startDate) + XCTAssertEqual(expected.endDate, calculated.endDate) + XCTAssertEqual(expected.value, expected.unit == .units ? calculated.netBasalUnits : calculated.netBasalUnitsPerHour, accuracy: 0.01) + } + } + + func testNormalizeEdgeCaseDosesMutable() { + let input = loadDoseFixture("normalize_edge_case_doses_mutable_input") + let output = loadDoseFixture("normalize_edge_case_doses_mutable_output") + let basals = loadBasalRateScheduleFixture("basal") + + let basalTimeline = basals.between(start: input.first!.startDate, end: input.last!.endDate).algorithmSchedule() + + let inputWithModel = input.map { $0.toMockInsulinDose(with: ExponentialInsulinModelPreset.rapidActingAdult.model) } + + measure { + _ = inputWithModel.annotated(with: basalTimeline) + } + + let doses = inputWithModel.annotated(with: basalTimeline) + + XCTAssertEqual(output.count, doses.count) + + for (expected, calculated) in zip(output, doses) { + XCTAssertEqual(expected.startDate, calculated.startDate) + XCTAssertEqual(expected.endDate, calculated.endDate) + XCTAssertEqual(expected.value, expected.unit == .units ? calculated.netBasalUnits : calculated.netBasalUnitsPerHour, accuracy: 0.01) + } + } + + func testDoseEntryAnnotateShouldSplitDosesProportionally() { + let startDate = self.fixtureDate("2018-07-16 11:59:00 +0000") + let endDate = startDate.addingTimeInterval(TimeInterval(minutes: 5)) + + let tempBasalRate = 1.0 + + let dose = DoseEntry( + type: .tempBasal, + startDate: startDate, + endDate: endDate, + value: tempBasalRate, + unit: .unitsPerHour, + decisionId: nil, + deliveredUnits: 0.1 + ) + + let delivery = dose.unitsInDeliverableIncrements + + let basals = loadBasalRateScheduleFixture("basal").between(start: startDate, end: endDate).algorithmSchedule() + + let doseWithModel = dose.toMockInsulinDose(with: ExponentialInsulinModelPreset.rapidActingAdult.model) + + let splitDoses = [doseWithModel].annotated(with: basals) + + XCTAssertEqual(2, splitDoses.count) + + // A 5 minute dose starting one minute before midnight, split at midnight, means split should be 1/5, 4/5 + XCTAssertEqual(delivery * 1.0/5.0, splitDoses[0].volume, accuracy: .ulpOfOne) + XCTAssertEqual(delivery * 4.0/5.0, splitDoses[1].volume, accuracy: .ulpOfOne) + } + + func testDoseEntryWithoutDeliveredUnitsShouldSplitDosesProportionally() { + let startDate = self.fixtureDate("2018-07-16 11:59:00 +0000") + let endDate = startDate.addingTimeInterval(TimeInterval(minutes: 5)) + + let tempBasalRate = 1.0 + + let dose = DoseEntry( + type: .tempBasal, + startDate: startDate, + endDate: endDate, + value: tempBasalRate, + unit: .unitsPerHour, + decisionId: nil, + deliveredUnits: 0.05 + ) + + let delivery = dose.unitsInDeliverableIncrements + + let basals = loadBasalRateScheduleFixture("basal").between(start: startDate, end: endDate).algorithmSchedule() + + let doseWithModel = dose.toMockInsulinDose(with: ExponentialInsulinModelPreset.rapidActingAdult.model) + + let splitDoses = [doseWithModel].annotated(with: basals) + + XCTAssertEqual(2, splitDoses.count) + + // A 5 minute dose starting one minute before midnight, split at midnight, means split should be 1/5, 4/5 + XCTAssertEqual(delivery * 1.0/5.0, splitDoses[0].volume, accuracy: .ulpOfOne) + XCTAssertEqual(delivery * 4.0/5.0, splitDoses[1].volume, accuracy: .ulpOfOne) + } +} + +fileprivate struct MockInsulinDose: InsulinDose { + var deliveryType: InsulinDeliveryType + + var startDate: Date + + var endDate: Date + + var volume: Double + + var insulinModel: LoopAlgorithm.InsulinModel +} + +fileprivate extension DoseEntry { + func toMockInsulinDose(with insulinModel: LoopKit.InsulinModel) -> MockInsulinDose { + return MockInsulinDose( + deliveryType: type == .bolus ? .bolus : .basal, + startDate: startDate, + endDate: endDate, + volume: deliveredUnits ?? programmedUnits, + insulinModel: AlgorithmInsulinModel(insulinModel) + ) + } +} diff --git a/LoopTests/Models/GlucoseStatisticsTests.swift b/LoopTests/Models/GlucoseStatisticsTests.swift index 0ad0a77b14..bac7131796 100644 --- a/LoopTests/Models/GlucoseStatisticsTests.swift +++ b/LoopTests/Models/GlucoseStatisticsTests.swift @@ -8,7 +8,6 @@ import XCTest import LoopKit import LoopKitUI -import LoopAlgorithm @testable import Loop final class GlucoseStatisticsTests: XCTestCase { diff --git a/LoopTests/Models/InsulinDeliveryHistoryTests.swift b/LoopTests/Models/InsulinDeliveryHistoryTests.swift index 150161c6db..ddd22f0141 100644 --- a/LoopTests/Models/InsulinDeliveryHistoryTests.swift +++ b/LoopTests/Models/InsulinDeliveryHistoryTests.swift @@ -6,8 +6,8 @@ // import XCTest -import LoopKit import LoopAlgorithm +import LoopKit @testable import Loop final class InsulinDeliveryHistoryTests: XCTestCase { @@ -23,11 +23,11 @@ final class InsulinDeliveryHistoryTests: XCTestCase { } private func bolus(_ units: Double, at start: Date, duration: TimeInterval = 0, automatic: Bool = false) -> SimpleInsulinDose { - SimpleInsulinDose(deliveryType: .bolus, automatic: automatic, startDate: start, endDate: start.addingTimeInterval(duration), volume: units, insulinModel: ExponentialInsulinModelPreset.rapidActingAdult) + SimpleInsulinDose(deliveryType: .bolus, automatic: automatic, startDate: start, endDate: start.addingTimeInterval(duration), volume: units, insulinModel: LoopAlgorithm.ExponentialInsulinModelPreset.rapidActingAdult) } private func tempBasal(rate: Double, from start: Date, to end: Date) -> SimpleInsulinDose { - SimpleInsulinDose(deliveryType: .basal, automatic: true, startDate: start, endDate: end, volume: rate * end.timeIntervalSince(start).hours, insulinModel: ExponentialInsulinModelPreset.rapidActingAdult) + SimpleInsulinDose(deliveryType: .basal, automatic: true, startDate: start, endDate: end, volume: rate * end.timeIntervalSince(start).hours, insulinModel: LoopAlgorithm.ExponentialInsulinModelPreset.rapidActingAdult) } private func scheduledBasal(_ rate: Double, from start: Date, to end: Date) -> [AbsoluteScheduleValue] { diff --git a/LoopTests/Models/SetBolusUserInfoTests.swift b/LoopTests/Models/SetBolusUserInfoTests.swift index 7ce903faa8..b4565319eb 100644 --- a/LoopTests/Models/SetBolusUserInfoTests.swift +++ b/LoopTests/Models/SetBolusUserInfoTests.swift @@ -7,7 +7,6 @@ // import XCTest -import LoopAlgorithm import LoopKit import LoopCore @testable import Loop diff --git a/LoopTests/Models/SetupChangeTests.swift b/LoopTests/Models/SetupChangeTests.swift index 6abfc608ca..4f39d2d889 100644 --- a/LoopTests/Models/SetupChangeTests.swift +++ b/LoopTests/Models/SetupChangeTests.swift @@ -7,7 +7,6 @@ import XCTest import LoopKit -import LoopAlgorithm @testable import Loop final class SetupChangeTests: XCTestCase { diff --git a/LoopTests/Models/SetupComparisonTests.swift b/LoopTests/Models/SetupComparisonTests.swift index f01cded2ad..30ccdb6462 100644 --- a/LoopTests/Models/SetupComparisonTests.swift +++ b/LoopTests/Models/SetupComparisonTests.swift @@ -6,8 +6,8 @@ // import XCTest -import LoopKit import LoopAlgorithm +import LoopKit @testable import Loop final class SetupComparisonTests: XCTestCase { @@ -88,7 +88,7 @@ final class SetupComparisonTests: XCTestCase { let now = changeDate.addingTimeInterval(.days(2)) let start = changeDate - .days(21) let glucose = samples(from: start, to: now) { _ in 120 } - let bolus = SimpleInsulinDose(deliveryType: .bolus, automatic: false, startDate: changeDate + .hours(1), endDate: changeDate + .hours(1), volume: 4, insulinModel: ExponentialInsulinModelPreset.rapidActingAdult) + let bolus = SimpleInsulinDose(deliveryType: .bolus, automatic: false, startDate: changeDate + .hours(1), endDate: changeDate + .hours(1), volume: 4, insulinModel: LoopAlgorithm.ExponentialInsulinModelPreset.rapidActingAdult) let history = InsulinDeliveryHistory( doses: [bolus], scheduledBasal: [AbsoluteScheduleValue(startDate: start, endDate: now, value: 1.0)], diff --git a/LoopTests/Models/SimpleBolusCalculatorTests.swift b/LoopTests/Models/SimpleBolusCalculatorTests.swift index e143bfea83..a03d96db8a 100644 --- a/LoopTests/Models/SimpleBolusCalculatorTests.swift +++ b/LoopTests/Models/SimpleBolusCalculatorTests.swift @@ -8,7 +8,6 @@ import Foundation import XCTest -import LoopAlgorithm import LoopKit @testable import Loop diff --git a/LoopTests/Models/TempBasalRecommendationTests.swift b/LoopTests/Models/TempBasalRecommendationTests.swift index 51366a5fcc..05778fc0ec 100644 --- a/LoopTests/Models/TempBasalRecommendationTests.swift +++ b/LoopTests/Models/TempBasalRecommendationTests.swift @@ -8,7 +8,6 @@ import XCTest import LoopKit -import LoopAlgorithm @testable import Loop class TempBasalRecommendationTests: XCTestCase { diff --git a/LoopTests/Models/TemporaryScheduleOverrideAlgorithmTests.swift b/LoopTests/Models/TemporaryScheduleOverrideAlgorithmTests.swift new file mode 100644 index 0000000000..6c6b435e74 --- /dev/null +++ b/LoopTests/Models/TemporaryScheduleOverrideAlgorithmTests.swift @@ -0,0 +1,139 @@ +// +// TemporaryScheduleOverrideAlgorithmTests.swift +// LoopTests +// +// Copyright © 2026 LoopKit Authors. All rights reserved. +// + +import XCTest +import LoopAlgorithm +@testable import LoopKit +@testable import Loop + +class TemporaryScheduleOverrideAlgorithmTests: XCTestCase { + + let dateFormatter = ISO8601DateFormatter.localTimeDate(timeZone: .currentFixed) + + private func date(at time: String) -> Date { + return dateFormatter.date(from: "2019-01-01T\(time):00")! + } + + func testOverrideScheduleAnnotatingReservoirSplitsDose() { + let schedule = BasalRateSchedule(dailyItems: [ + RepeatingScheduleValue(startTime: 0, value: 0.225), + RepeatingScheduleValue(startTime: 3600.0, value: 0.18000000000000002), + RepeatingScheduleValue(startTime: 10800.0, value: 0.135), + RepeatingScheduleValue(startTime: 12689.855275034904, value: 0.15), + RepeatingScheduleValue(startTime: 21600.0, value: 0.2), + RepeatingScheduleValue(startTime: 32400.0, value: 0.2), + RepeatingScheduleValue(startTime: 50400.0, value: 0.2), + RepeatingScheduleValue(startTime: 52403.79680299759, value: 0.16000000000000003), + RepeatingScheduleValue(startTime: 63743.58014559746, value: 0.2), + RepeatingScheduleValue(startTime: 63743.58014583588, value: 0.16000000000000003), + RepeatingScheduleValue(startTime: 69968.05249071121, value: 0.2), + RepeatingScheduleValue(startTime: 69968.05249094963, value: 0.18000000000000002), + RepeatingScheduleValue(startTime: 79200.0, value: 0.225), + ])! + + let start = date(at: "19:25") + + let dose = FixtureInsulinDose( + deliveryType: .basal, + startDate: start, + endDate: date(at: "19:30"), + volume: 0.8, + insulinType: .novolog + ) + + let timeline = schedule.between(start: start, end: start.addingTimeInterval(InsulinMath.longestInsulinActivityDuration)).algorithmSchedule() + + let annotated = [dose].annotated(with: timeline) + + XCTAssertEqual(3, annotated.count) + XCTAssertEqual(dose.volume, annotated.map { $0.volume }.reduce(0, +)) + } + + func testPreMealPreset() { + let now = ISO8601DateFormatter().date(from: "2020-03-11T12:13:14-0700")! + + var input = AlgorithmInputFixture.mock(for: now) + input.recommendationType = .tempBasal + + func d(_ interval: TimeInterval) -> Date { + return now.addingTimeInterval(interval) + } + + // Flat, in range bg. + input.glucoseHistory = [ + FixtureGlucoseSample(startDate: d(.minutes(-19)), mgdl: 105), + FixtureGlucoseSample(startDate: d(.minutes(-14)), mgdl: 105), + FixtureGlucoseSample(startDate: d(.minutes(-9)), mgdl: 105), + FixtureGlucoseSample(startDate: d(.minutes(-4)), mgdl: 105), + ] + + let scheduledRange = LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: 100)...LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: 110) + let overrideRange = LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: 80)...LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: 90) + + let overrideStartTime = d(.minutes(-10)) + + let overrides: [TemporaryScheduleOverride] = [ + .init( + context: .preMeal, + settings: .init(targetRange: overrideRange), + startDate: overrideStartTime, + duration: .finite(.hours(1)), + enactTrigger: .local, + syncIdentifier: UUID() + ) + ] + + let targetTimeline: [LoopKit.AbsoluteScheduleValue>] = [ + AbsoluteScheduleValue(startDate: d(.hours(-2)), endDate: d(.hours(10)), value: scheduledRange) + ] + + input.targetMgdl = overrides.applyTarget(over: targetTimeline, at: now).algorithmMgdlRanges() + + input.doses = [] + input.carbEntries = [] + + let output = Algorithm.run(input: input) + + let recommendedRate = output.recommendation!.automatic!.basalAdjustment.unitsPerHour + let activeInsulin = output.activeInsulin! + XCTAssertEqual(activeInsulin, 0) + XCTAssertEqual(recommendedRate, 1.727, accuracy: 0.01) + } +} + +fileprivate extension AlgorithmInputFixture { + /// Mocks stable, in range glucose, no insulin, no carbs, with reasonable settings + static func mock(for now: Date = Date()) -> AlgorithmInputFixture { + + func d(_ interval: TimeInterval) -> Date { + return now.addingTimeInterval(interval) + } + + let forecastEnd = now.addingTimeInterval(InsulinMath.defaultInsulinActivityDuration).dateCeiledToTimeInterval(GlucoseMath.defaultDelta) + + return AlgorithmInputFixture( + predictionStart: now, + glucoseHistory: [ + FixtureGlucoseSample(startDate: d(.minutes(-19)), mgdl: 100), + FixtureGlucoseSample(startDate: d(.minutes(-14)), mgdl: 120), + FixtureGlucoseSample(startDate: d(.minutes(-9)), mgdl: 140), + FixtureGlucoseSample(startDate: d(.minutes(-4)), mgdl: 160), + ], + doses: [], + carbEntries: [], + basal: [LoopAlgorithm.AbsoluteScheduleValue(startDate: d(.hours(-10)), endDate: now, value: 1.0)], + sensitivityMgdlPerUnit: [LoopAlgorithm.AbsoluteScheduleValue(startDate: d(.hours(-10)), endDate: forecastEnd, value: 55)], + carbRatio: [LoopAlgorithm.AbsoluteScheduleValue(startDate: d(.hours(-10)), endDate: now, value: 10)], + targetMgdl: [LoopAlgorithm.AbsoluteScheduleValue(startDate: d(.hours(-10)), endDate: now, value: 100...110)], + suspendThresholdMgdl: 65, + maxBolus: 6, + maxBasalRate: 8, + recommendationInsulinType: .novolog, + recommendationType: .tempBasal + ) + } +} diff --git a/LoopTests/Models/WatchHistoricalGlucoseTest.swift b/LoopTests/Models/WatchHistoricalGlucoseTest.swift index 6d0f85be19..9d485def90 100644 --- a/LoopTests/Models/WatchHistoricalGlucoseTest.swift +++ b/LoopTests/Models/WatchHistoricalGlucoseTest.swift @@ -8,7 +8,6 @@ import XCTest import HealthKit -import LoopAlgorithm import LoopKit @testable import Loop diff --git a/LoopTests/ViewModels/BolusEntryViewModelTests.swift b/LoopTests/ViewModels/BolusEntryViewModelTests.swift index dd26c27822..f1e8801cac 100644 --- a/LoopTests/ViewModels/BolusEntryViewModelTests.swift +++ b/LoopTests/ViewModels/BolusEntryViewModelTests.swift @@ -7,12 +7,12 @@ // import HealthKit +import LoopAlgorithm import LoopCore import LoopKit import LoopKitUI import SwiftUI import XCTest -import LoopAlgorithm @testable import Loop @@ -193,7 +193,7 @@ class BolusEntryViewModelTests: XCTestCase { let input = try await delegate.fetchData(for: Self.exampleStartDate, presumePresetEndingNow: false, ensureDosingCoverageStart: nil, projectOngoingDoses: false) let prediction = try input.predictGlucose() await bolusEntryViewModel.update() - XCTAssertEqual(prediction, bolusEntryViewModel.predictedGlucoseValues.map { PredictedGlucoseValue(startDate: $0.startDate, quantity: $0.quantity) }) + XCTAssertEqual(prediction, bolusEntryViewModel.predictedGlucoseValues.map { LoopAlgorithm.PredictedGlucoseValue(startDate: $0.startDate, mgdl: $0.quantity.doubleValue(for: .milligramsPerDeciliter)) }) } catch { XCTFail("Unable to generate prediction") } @@ -205,7 +205,7 @@ class BolusEntryViewModelTests: XCTestCase { let prediction = try input.predictGlucose() await bolusEntryViewModel.update() bolusEntryViewModel.manualGlucoseQuantity = Self.exampleManualGlucoseQuantity - XCTAssertEqual(prediction, bolusEntryViewModel.predictedGlucoseValues.map { PredictedGlucoseValue(startDate: $0.startDate, quantity: $0.quantity) }) + XCTAssertEqual(prediction, bolusEntryViewModel.predictedGlucoseValues.map { LoopAlgorithm.PredictedGlucoseValue(startDate: $0.startDate, mgdl: $0.quantity.doubleValue(for: .milligramsPerDeciliter)) }) } catch { XCTFail("Unable to generate prediction") } @@ -302,7 +302,7 @@ class BolusEntryViewModelTests: XCTestCase { XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) let recommendation = ManualBolusRecommendation(amount: 1.25) - delegate.algorithmOutput.recommendationResult = .success(.init(manual: recommendation)) + delegate.recommendationResult = .success(recommendation) await bolusEntryViewModel.update() @@ -325,7 +325,7 @@ class BolusEntryViewModelTests: XCTestCase { amount: 1.25, notice: BolusRecommendationNotice.glucoseBelowSuspendThreshold(minGlucose: Self.exampleGlucoseValue) ) - delegate.algorithmOutput.recommendationResult = .success(.init(manual: recommendation)) + delegate.recommendationResult = .success(recommendation) await bolusEntryViewModel.update() XCTAssertTrue(bolusEntryViewModel.isBolusRecommended) let recommendedBolus = bolusEntryViewModel.recommendedBolus @@ -338,7 +338,7 @@ class BolusEntryViewModelTests: XCTestCase { XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) delegate.settings.suspendThreshold = nil let recommendation = ManualBolusRecommendation(amount: 1.25, notice: BolusRecommendationNotice.glucoseBelowSuspendThreshold(minGlucose: Self.exampleGlucoseValue)) - delegate.algorithmOutput.recommendationResult = .success(.init(manual: recommendation)) + delegate.recommendationResult = .success(recommendation) await bolusEntryViewModel.update() XCTAssertTrue(bolusEntryViewModel.isBolusRecommended) let recommendedBolus = bolusEntryViewModel.recommendedBolus @@ -350,7 +350,7 @@ class BolusEntryViewModelTests: XCTestCase { func testUpdateRecommendedBolusWithOtherNotice() async throws { XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) let recommendation = ManualBolusRecommendation(amount: 1.25, notice: BolusRecommendationNotice.currentGlucoseBelowTarget(glucose: Self.exampleGlucoseValue)) - delegate.algorithmOutput.recommendationResult = .success(.init(manual: recommendation)) + delegate.recommendationResult = .success(recommendation) await bolusEntryViewModel.update() XCTAssertTrue(bolusEntryViewModel.isBolusRecommended) let recommendedBolus = bolusEntryViewModel.recommendedBolus @@ -361,7 +361,7 @@ class BolusEntryViewModelTests: XCTestCase { func testUpdateRecommendedBolusThrowsMissingDataError() async throws { XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) - delegate.algorithmOutput.recommendationResult = .failure(LoopError.missingDataError(.glucose)) + delegate.recommendationResult = .failure(LoopError.missingDataError(.glucose)) await bolusEntryViewModel.update() XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) let recommendedBolus = bolusEntryViewModel.recommendedBolus @@ -371,7 +371,7 @@ class BolusEntryViewModelTests: XCTestCase { func testUpdateRecommendedBolusThrowsPumpDataTooOld() async throws { XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) - delegate.algorithmOutput.recommendationResult = .failure(LoopError.pumpDataTooOld(date: now)) + delegate.recommendationResult = .failure(LoopError.pumpDataTooOld(date: now)) await bolusEntryViewModel.update() XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) let recommendedBolus = bolusEntryViewModel.recommendedBolus @@ -381,7 +381,7 @@ class BolusEntryViewModelTests: XCTestCase { func testUpdateRecommendedBolusThrowsGlucoseTooOld() async throws { XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) - delegate.algorithmOutput.recommendationResult = .failure(LoopError.glucoseTooOld(date: now)) + delegate.recommendationResult = .failure(LoopError.glucoseTooOld(date: now)) await bolusEntryViewModel.update() XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) let recommendedBolus = bolusEntryViewModel.recommendedBolus @@ -391,7 +391,7 @@ class BolusEntryViewModelTests: XCTestCase { func testUpdateRecommendedBolusThrowsInvalidFutureGlucose() async throws { XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) - delegate.algorithmOutput.recommendationResult = .failure(LoopError.invalidFutureGlucose(date: now)) + delegate.recommendationResult = .failure(LoopError.invalidFutureGlucose(date: now)) await bolusEntryViewModel.update() XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) let recommendedBolus = bolusEntryViewModel.recommendedBolus @@ -401,7 +401,7 @@ class BolusEntryViewModelTests: XCTestCase { func testUpdateRecommendedBolusThrowsOtherError() async throws { XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) - delegate.algorithmOutput.recommendationResult = .failure(LoopError.pumpSuspended) + delegate.recommendationResult = .failure(LoopError.pumpSuspended) await bolusEntryViewModel.update() XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) let recommendedBolus = bolusEntryViewModel.recommendedBolus @@ -423,7 +423,7 @@ class BolusEntryViewModelTests: XCTestCase { XCTAssertFalse(bolusEntryViewModel.isBolusRecommended) let recommendation = ManualBolusRecommendation(amount: 1.25) - delegate.algorithmOutput.recommendationResult = .success(.init(manual: recommendation)) + delegate.recommendationResult = .success(recommendation) await bolusEntryViewModel.update() XCTAssertTrue(bolusEntryViewModel.isBolusRecommended) @@ -874,16 +874,16 @@ fileprivate class MockBolusEntryViewModelDelegate: BolusEntryViewModelDelegate { carbEntries: [], predictionStart: Date(), basal: [], - sensitivity: [], + sensitivityMgdlPerUnit: [], carbRatio: [], - target: [], - suspendThreshold: nil, + targetMgdl: [], + suspendThresholdMgdl: nil, maxBolus: 3, maxBasalRate: 6, useIntegralRetrospectiveCorrection: false, includePositiveVelocityAndRC: true, carbAbsorptionModel: .piecewiseLinear, - recommendationInsulinModel: ExponentialInsulinModelPreset.rapidActingAdult, + recommendationInsulinModel: LoopAlgorithm.ExponentialInsulinModelPreset.rapidActingAdult, recommendationType: .manualBolus, automaticBolusApplicationFactor: 0.4 ) @@ -958,14 +958,7 @@ fileprivate class MockBolusEntryViewModelDelegate: BolusEntryViewModelDelegate { return (historicGlucose: loopStateInput.glucoseHistory, predictedGlucose: prediction) } - var algorithmOutput: AlgorithmOutput = AlgorithmOutput( - recommendationResult: .success(.init()), - predictedGlucose: [], - effects: LoopAlgorithmEffects.emptyMock, - dosesRelativeToBasal: [], - activeInsulin: nil, - activeCarbs: nil - ) + var recommendationResult: Result = .success(nil) var manualGlucoseSampleForBolusRecommendation: NewGlucoseSample? var carbEntryChangeForBolusRecommendation: CarbEntryChange? @@ -979,9 +972,9 @@ fileprivate class MockBolusEntryViewModelDelegate: BolusEntryViewModelDelegate { manualGlucoseSampleForBolusRecommendation = manualGlucoseSample carbEntryChangeForBolusRecommendation = carbEntryChange - switch algorithmOutput.recommendationResult { + switch recommendationResult { case .success(let recommendation): - return recommendation.manual + return recommendation case .failure(let error): throw error } @@ -1034,20 +1027,6 @@ extension ManualBolusRecommendationWithDate: @retroactive Equatable { } } -extension LoopAlgorithmEffects { - public static var emptyMock: LoopAlgorithmEffects { - return LoopAlgorithmEffects( - insulin: [], - carbs: [], - carbStatus: [], - retrospectiveCorrection: [], - momentum: [], - insulinCounteraction: [], - retrospectiveGlucoseDiscrepancies: [] - ) - } -} - extension CarbEntryUpdate { static func mock(_ grams: Double, at date: Date) -> CarbEntryUpdate { CarbEntryUpdate( diff --git a/LoopTests/ViewModels/CGMStatusHUDViewModelTests.swift b/LoopTests/ViewModels/CGMStatusHUDViewModelTests.swift index 8f9bd9d3ac..bd78d1ac9f 100644 --- a/LoopTests/ViewModels/CGMStatusHUDViewModelTests.swift +++ b/LoopTests/ViewModels/CGMStatusHUDViewModelTests.swift @@ -7,7 +7,6 @@ // import XCTest -import LoopAlgorithm import LoopKit @testable import LoopUI diff --git a/LoopTests/ViewModels/ManualEntryDoseViewModelTests.swift b/LoopTests/ViewModels/ManualEntryDoseViewModelTests.swift index 4eaa0fbb3b..15a1cc4414 100644 --- a/LoopTests/ViewModels/ManualEntryDoseViewModelTests.swift +++ b/LoopTests/ViewModels/ManualEntryDoseViewModelTests.swift @@ -9,7 +9,6 @@ import LoopCore import LoopKit import XCTest -import LoopAlgorithm @testable import Loop @@ -87,7 +86,7 @@ fileprivate class MockManualEntryDoseViewModelDelegate: ManualDoseViewModelDeleg } func insulinActivityDuration(for type: InsulinType?) -> TimeInterval { - return InsulinMath.defaultInsulinActivityDuration + return ExponentialInsulinModelPreset.rapidActingAdult.effectDuration } var algorithmDisplayState = AlgorithmDisplayState() diff --git a/LoopTests/ViewModels/SimpleBolusViewModelTests.swift b/LoopTests/ViewModels/SimpleBolusViewModelTests.swift index c29861c24a..65bf01a50d 100644 --- a/LoopTests/ViewModels/SimpleBolusViewModelTests.swift +++ b/LoopTests/ViewModels/SimpleBolusViewModelTests.swift @@ -11,7 +11,6 @@ import HealthKit import LoopKit import LoopKitUI import LoopCore -import LoopAlgorithm @testable import Loop diff --git a/LoopUI/ViewModel/CGMStatusHUDViewModel.swift b/LoopUI/ViewModel/CGMStatusHUDViewModel.swift index 2d6f04bec7..c71781ee9e 100644 --- a/LoopUI/ViewModel/CGMStatusHUDViewModel.swift +++ b/LoopUI/ViewModel/CGMStatusHUDViewModel.swift @@ -6,7 +6,6 @@ // Copyright © 2020 LoopKit Authors. All rights reserved. // -import LoopAlgorithm import LoopKit public class CGMStatusHUDViewModel { diff --git a/LoopUI/Views/CGMStatusHUDView.swift b/LoopUI/Views/CGMStatusHUDView.swift index 47af3dd61e..5692149f1d 100644 --- a/LoopUI/Views/CGMStatusHUDView.swift +++ b/LoopUI/Views/CGMStatusHUDView.swift @@ -7,7 +7,6 @@ // import UIKit -import LoopAlgorithm import LoopKit import LoopKitUI diff --git a/LoopUI/Views/GlucoseHUDView.swift b/LoopUI/Views/GlucoseHUDView.swift index e54bdf2db1..73fd6620fe 100644 --- a/LoopUI/Views/GlucoseHUDView.swift +++ b/LoopUI/Views/GlucoseHUDView.swift @@ -7,7 +7,6 @@ // import UIKit -import LoopAlgorithm import LoopKit import LoopKitUI diff --git a/WatchApp Extension/ComplicationController.swift b/WatchApp Extension/ComplicationController.swift index f0a5c19744..7ce17628a5 100644 --- a/WatchApp Extension/ComplicationController.swift +++ b/WatchApp Extension/ComplicationController.swift @@ -9,8 +9,8 @@ import ClockKit import WatchKit import LoopKit +import LoopCore import os.log -import LoopAlgorithm final class ComplicationController: NSObject, CLKComplicationDataSource { @@ -101,7 +101,7 @@ final class ComplicationController: NSObject, CLKComplicationDataSource { let template = CLKComplicationTemplate.templateForFamily(complication.family, from: context, at: timelineDate, - recencyInterval: LoopAlgorithm.inputDataRecencyInterval, + recencyInterval: LoopCoreConstants.inputDataRecencyInterval, chartGenerator: self.makeChart) { switch complication.family { @@ -132,7 +132,7 @@ final class ComplicationController: NSObject, CLKComplicationDataSource { var futureChangeDates: [Date] = [ // Stale glucose date: just a second after glucose expires - glucoseDate + LoopAlgorithm.inputDataRecencyInterval + 1, + glucoseDate + LoopCoreConstants.inputDataRecencyInterval + 1, ] if let loopLastRunDate = context.loopLastRunDate { @@ -148,7 +148,7 @@ final class ComplicationController: NSObject, CLKComplicationDataSource { if let template = CLKComplicationTemplate.templateForFamily(complication.family, from: context, at: futureChangeDate, - recencyInterval: LoopAlgorithm.inputDataRecencyInterval, + recencyInterval: LoopCoreConstants.inputDataRecencyInterval, chartGenerator: self.makeChart) { template.tintColor = UIColor.tintColor diff --git a/WatchApp Extension/ExtensionDelegate.swift b/WatchApp Extension/ExtensionDelegate.swift index ea1f3ac607..5d7a1b361f 100644 --- a/WatchApp Extension/ExtensionDelegate.swift +++ b/WatchApp Extension/ExtensionDelegate.swift @@ -9,7 +9,6 @@ import WatchConnectivity import WatchKit import HealthKit -import LoopAlgorithm import Intents import os import os.log diff --git a/WatchApp Extension/Extensions/CLKComplicationTemplate.swift b/WatchApp Extension/Extensions/CLKComplicationTemplate.swift index d2b9933a1a..c6984f4f7c 100644 --- a/WatchApp Extension/Extensions/CLKComplicationTemplate.swift +++ b/WatchApp Extension/Extensions/CLKComplicationTemplate.swift @@ -10,7 +10,6 @@ import ClockKit import LoopKit import Foundation import LoopCore -import LoopAlgorithm extension CLKComplicationTemplate { diff --git a/WatchApp Extension/Extensions/EnvironmentValues+GlucoseDisplayUnit.swift b/WatchApp Extension/Extensions/EnvironmentValues+GlucoseDisplayUnit.swift index f7a035aab1..6762b11b54 100644 --- a/WatchApp Extension/Extensions/EnvironmentValues+GlucoseDisplayUnit.swift +++ b/WatchApp Extension/Extensions/EnvironmentValues+GlucoseDisplayUnit.swift @@ -7,7 +7,7 @@ // import SwiftUI -import LoopAlgorithm +import LoopKit @MainActor private struct GlucoseDisplayUnitKey: @preconcurrency EnvironmentKey { diff --git a/WatchApp Extension/Extensions/GlucoseCondition.swift b/WatchApp Extension/Extensions/GlucoseCondition.swift index 5dae0e1a7a..85fdf7ee60 100644 --- a/WatchApp Extension/Extensions/GlucoseCondition.swift +++ b/WatchApp Extension/Extensions/GlucoseCondition.swift @@ -7,7 +7,7 @@ // import Foundation -import LoopAlgorithm +import LoopKit extension GlucoseCondition { var localizedDescription: String { diff --git a/WatchApp Extension/Extensions/WatchContext+WatchApp.swift b/WatchApp Extension/Extensions/WatchContext+WatchApp.swift index 0f3b85fb7d..4a710d3c07 100644 --- a/WatchApp Extension/Extensions/WatchContext+WatchApp.swift +++ b/WatchApp Extension/Extensions/WatchContext+WatchApp.swift @@ -7,7 +7,6 @@ // import Foundation -import LoopAlgorithm import LoopKit import LoopCore diff --git a/WatchApp Extension/Managers/ComplicationChartManager.swift b/WatchApp Extension/Managers/ComplicationChartManager.swift index 4be94984ff..8be5fbb5b1 100644 --- a/WatchApp Extension/Managers/ComplicationChartManager.swift +++ b/WatchApp Extension/Managers/ComplicationChartManager.swift @@ -10,7 +10,6 @@ import Foundation import UIKit import WatchKit import LoopKit -import LoopAlgorithm private let textInsets = UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2) diff --git a/WatchApp Extension/Managers/LoopDataManager.swift b/WatchApp Extension/Managers/LoopDataManager.swift index ab548b96c0..bd1e022d96 100644 --- a/WatchApp Extension/Managers/LoopDataManager.swift +++ b/WatchApp Extension/Managers/LoopDataManager.swift @@ -12,7 +12,6 @@ import LoopKit import LoopCore import WatchConnectivity import os.log -import LoopAlgorithm import UserNotifications import WatchKit @@ -224,7 +223,7 @@ extension LoopDataManager { func requestCarbBackfill() { dispatchPrecondition(condition: .onQueue(.main)) - let start = min(Calendar.current.startOfDay(for: Date()), Date(timeIntervalSinceNow: -CarbMath.maximumAbsorptionTimeInterval)) + let start = min(Calendar.current.startOfDay(for: Date()), Date(timeIntervalSinceNow: -LoopCoreConstants.maximumCarbAbsorptionTimeInterval)) let userInfo = CarbBackfillRequestUserInfo(startDate: start) WCSession.default.sendCarbBackfillRequestMessage(userInfo) { (result) in switch result { diff --git a/WatchApp Extension/Models/GlucoseChartData.swift b/WatchApp Extension/Models/GlucoseChartData.swift index 27903af5b6..f0d5d389b2 100644 --- a/WatchApp Extension/Models/GlucoseChartData.swift +++ b/WatchApp Extension/Models/GlucoseChartData.swift @@ -8,7 +8,6 @@ import Foundation import LoopKit -import LoopAlgorithm struct GlucoseChartData { diff --git a/WatchApp Extension/Models/GlucoseChartScaler.swift b/WatchApp Extension/Models/GlucoseChartScaler.swift index 979eeb5e74..6e0c54eadd 100644 --- a/WatchApp Extension/Models/GlucoseChartScaler.swift +++ b/WatchApp Extension/Models/GlucoseChartScaler.swift @@ -10,7 +10,6 @@ import Foundation import CoreGraphics import LoopKit import WatchKit -import LoopAlgorithm enum CoordinateSystem { diff --git a/WatchApp Extension/Scenes/GlucoseChartValueHashable.swift b/WatchApp Extension/Scenes/GlucoseChartValueHashable.swift index ab5ea5800b..410e62cbcd 100644 --- a/WatchApp Extension/Scenes/GlucoseChartValueHashable.swift +++ b/WatchApp Extension/Scenes/GlucoseChartValueHashable.swift @@ -6,7 +6,6 @@ // import LoopKit -import LoopAlgorithm protocol GlucoseChartValueHashable { diff --git a/WatchApp Extension/View Models/CarbAndBolusFlowViewModel.swift b/WatchApp Extension/View Models/CarbAndBolusFlowViewModel.swift index e5d5cb2742..5ed0dd9675 100644 --- a/WatchApp Extension/View Models/CarbAndBolusFlowViewModel.swift +++ b/WatchApp Extension/View Models/CarbAndBolusFlowViewModel.swift @@ -8,7 +8,6 @@ import Foundation import Combine -import LoopAlgorithm import WatchKit import WatchConnectivity import LoopKit diff --git a/WatchApp Extension/Views/CarbList.swift b/WatchApp Extension/Views/CarbList.swift index 3377ae6d40..aa8ccd7aa6 100644 --- a/WatchApp Extension/Views/CarbList.swift +++ b/WatchApp Extension/Views/CarbList.swift @@ -8,7 +8,7 @@ import SwiftUI import LoopKit -import LoopAlgorithm +import LoopCore struct CarbList: View { @Environment(LoopDataManager.self) var loopManager @@ -29,7 +29,7 @@ struct CarbList: View { @State var entries: [StoredCarbEntry] = [] private func reloadCarbEntries() async { - let start = min(Calendar.current.startOfDay(for: Date()), Date(timeIntervalSinceNow: -CarbMath.maximumAbsorptionTimeInterval)) + let start = min(Calendar.current.startOfDay(for: Date()), Date(timeIntervalSinceNow: -LoopCoreConstants.maximumCarbAbsorptionTimeInterval)) entries = (try? await loopManager.carbStore.getCarbEntries(start: start)) ?? [] } diff --git a/WatchApp Extension/Views/ChartPageView.swift b/WatchApp Extension/Views/ChartPageView.swift index 5833781ef5..0a04879cb7 100644 --- a/WatchApp Extension/Views/ChartPageView.swift +++ b/WatchApp Extension/Views/ChartPageView.swift @@ -9,7 +9,6 @@ import SwiftUI import LoopKit import LoopCore -import LoopAlgorithm import SpriteKit struct ChartPageView: View { diff --git a/WatchApp Extension/Views/PresetWatchCard.swift b/WatchApp Extension/Views/PresetWatchCard.swift index 90367e4330..0c95e9f2b9 100644 --- a/WatchApp Extension/Views/PresetWatchCard.swift +++ b/WatchApp Extension/Views/PresetWatchCard.swift @@ -6,7 +6,6 @@ // Copyright © 2025 LoopKit Authors. All rights reserved. // -import LoopAlgorithm import SwiftUI import LoopKit import LoopCore From e617031d6b281759cf11417ccbcc8c7713b0f662 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sun, 1 Nov 2026 01:31:34 -0500 Subject: [PATCH 2/3] Map the dose direction exhaustively and test mmol/L manual boluses - The automatic dose direction maps by switch instead of rawValue, so a case added on either side fails to compile instead of crashing the loop. - Test that mmol/L settings give the same manual bolus recommendation and bolus preview as mg/dL with a pending carb entry. - Test that LoopCore's copies of the input recency interval and maximum carb absorption time match LoopAlgorithm's. - Fix the indentation of a predictGlucose call. --- Loop/Models/AlgorithmBridge.swift | 14 ++++-- .../ManualEntryDoseViewModel.swift | 2 +- LoopTests/Managers/LoopDataManagerTests.swift | 48 +++++++++++++++++-- 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/Loop/Models/AlgorithmBridge.swift b/Loop/Models/AlgorithmBridge.swift index db816d1b90..53ded55f37 100644 --- a/Loop/Models/AlgorithmBridge.swift +++ b/Loop/Models/AlgorithmBridge.swift @@ -132,11 +132,19 @@ extension LoopAlgorithm.TempBasalRecommendation { } } +extension LoopAlgorithm.AutomaticDoseRecommendation.Direction { + var loopKit: LoopKit.AutomaticDoseRecommendation.Direction { + switch self { + case .decrease: .decrease + case .neutral: .neutral + case .increase: .increase + } + } +} + extension LoopAlgorithm.AutomaticDoseRecommendation { var loopKit: LoopKit.AutomaticDoseRecommendation { - LoopKit.AutomaticDoseRecommendation(basalAdjustment: basalAdjustment.loopKit, - direction: LoopKit.AutomaticDoseRecommendation.Direction(rawValue: direction.rawValue)!, - bolusUnits: bolusUnits) + LoopKit.AutomaticDoseRecommendation(basalAdjustment: basalAdjustment.loopKit, direction: direction.loopKit, bolusUnits: bolusUnits) } } diff --git a/Loop/View Models/ManualEntryDoseViewModel.swift b/Loop/View Models/ManualEntryDoseViewModel.swift index da03bc0a5c..d66b40b5bd 100644 --- a/Loop/View Models/ManualEntryDoseViewModel.swift +++ b/Loop/View Models/ManualEntryDoseViewModel.swift @@ -249,7 +249,7 @@ final class ManualEntryDoseViewModel: ObservableObject { do { predictedGlucoseValues = try input .addingDose(dose: enteredBolusDose) -.predictGlucose() + .predictGlucose() .map(\.loopKit) } catch { predictedGlucoseValues = [] diff --git a/LoopTests/Managers/LoopDataManagerTests.swift b/LoopTests/Managers/LoopDataManagerTests.swift index 3508ede8e6..ea6f416fec 100644 --- a/LoopTests/Managers/LoopDataManagerTests.swift +++ b/LoopTests/Managers/LoopDataManagerTests.swift @@ -255,10 +255,12 @@ class LoopDataManagerTests: XCTestCase { XCTAssertEqual(0.2, deliveryDelegate.lastEnact.bolus!, accuracy: defaultAccuracy) } - func testMmolSettingsDoseTheSameAsMgdl() async throws { - func mmol(_ mgdl: Double) -> Double { - LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: mgdl).doubleValue(for: .millimolesPerLiter) - } + private func mmol(_ mgdl: Double) -> Double { + LoopQuantity(unit: .milligramsPerDeciliter, doubleValue: mgdl).doubleValue(for: .millimolesPerLiter) + } + + /// The setUp therapy settings, expressed in mmol/L + private func useMmolSettings() { settingsProvider.settings.insulinSensitivitySchedule = InsulinSensitivitySchedule( unit: .millimolesPerLiter, dailyItems: [ @@ -273,6 +275,10 @@ class LoopDataManagerTests: XCTestCase { RepeatingScheduleValue(startTime: TimeInterval(75600), value: DoubleRange(minValue: mmol(100), maxValue: mmol(110))) ], timeZone: .utcTimeZone)! settingsProvider.settings.suspendThreshold = GlucoseThreshold(unit: .millimolesPerLiter, value: mmol(75)) + } + + func testMmolSettingsDoseTheSameAsMgdl() async throws { + useMmolSettings() glucoseStore.storedGlucose = [ StoredGlucoseSample(startDate: d(.minutes(-1)), quantity: LoopQuantity(unit: .millimolesPerLiter, doubleValue: mmol(120))), ] @@ -292,6 +298,40 @@ class LoopDataManagerTests: XCTestCase { XCTAssertEqual(0.2, deliveryDelegate.lastEnact.bolus!, accuracy: defaultAccuracy) } + func testMmolSettingsRecommendTheSameManualBolusAsMgdl() async throws { + let values: [Double] = [100, 130, 160, 190] + func history(_ unit: LoopUnit) -> [StoredGlucoseSample] { + values.enumerated().map { index, mgdl in + let value = unit == .milligramsPerDeciliter ? mgdl : mmol(mgdl) + return StoredGlucoseSample(startDate: d(.minutes(Double(5 * index - 18))), quantity: LoopQuantity(unit: unit, doubleValue: value)) + } + } + let carbs = CarbEntryChange.add(NewCarbEntry(syncIdentifier: "mmol-test", quantity: .carbs(value: 30), startDate: d(.minutes(-1)), foodType: nil, absorptionTime: .hours(3))) + + glucoseStore.storedGlucose = history(.milligramsPerDeciliter) + let mgdlRecommendation = try await loopDataManager.recommendManualBolus(carbEntryChange: carbs)! + let mgdlPreview = try await loopDataManager.generatePrediction(carbEntryChange: carbs, potentialDose: nil, manualGlucose: nil).predictedGlucose + + useMmolSettings() + glucoseStore.storedGlucose = history(.millimolesPerLiter) + let mmolRecommendation = try await loopDataManager.recommendManualBolus(carbEntryChange: carbs)! + let mmolPreview = try await loopDataManager.generatePrediction(carbEntryChange: carbs, potentialDose: nil, manualGlucose: nil).predictedGlucose + + XCTAssertGreaterThan(mgdlRecommendation.amount, 0) + XCTAssertEqual(mmolRecommendation.amount, mgdlRecommendation.amount, accuracy: 1e-9) + XCTAssertEqual(mmolRecommendation.notice == nil, mgdlRecommendation.notice == nil) + XCTAssertEqual(mmolPreview.count, mgdlPreview.count) + for (mmolValue, mgdlValue) in zip(mmolPreview, mgdlPreview) { + XCTAssertEqual(mmolValue.startDate, mgdlValue.startDate) + XCTAssertEqual(mmolValue.quantity.doubleValue(for: .milligramsPerDeciliter), mgdlValue.quantity.doubleValue(for: .milligramsPerDeciliter), accuracy: 1e-9) + } + } + + func testLoopCoreConstantsMatchTheAlgorithm() { + XCTAssertEqual(LoopCoreConstants.inputDataRecencyInterval, Algorithm.inputDataRecencyInterval) + XCTAssertEqual(LoopCoreConstants.maximumCarbAbsorptionTimeInterval, LoopAlgorithm.CarbMath.maximumAbsorptionTimeInterval) + } + func testHighAndFalling() async { glucoseStore.storedGlucose = [ StoredGlucoseSample(startDate: d(.minutes(-18)), quantity: .glucose(value: 200)), From 4aab8b011b7c71263647864f254054b04f6c4a3f Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sun, 1 Nov 2026 01:43:56 -0500 Subject: [PATCH 3/3] Move the dose history tests to LoopAlgorithm They test LoopAlgorithm's dose annotation and insulin on board, so they now live there with their fixtures (LoopKit/LoopAlgorithm#17). The override tests stay here because they cross from LoopKit's override schedules into the algorithm. --- Loop.xcodeproj/project.pbxproj | 76 - LoopTests/Fixtures/insulin_math/basal.json | 44 - .../iob_from_doses_exponential_output.json | 642 ------ .../insulin_math/iob_from_doses_output.json | 1 - .../iob_from_multiple_curves_output.json | 1832 ----------------- .../iob_from_reservoir_output.json | 557 ----- .../normalize_edge_case_doses_input.json | 16 - ...rmalize_edge_case_doses_mutable_input.json | 18 - ...malize_edge_case_doses_mutable_output.json | 42 - .../normalize_edge_case_doses_output.json | 37 - .../insulin_math/normalized_doses.json | 171 -- .../normalized_reservoir_history_output.json | 434 ---- ..._history_with_rewind_and_prime_output.json | 434 ---- .../Fixtures/insulin_math/suspend_dose.json | 9 - .../insulin_math/suspend_dose_reconciled.json | 9 - .../suspend_dose_reconciled_normalized.json | 9 - ...uspend_dose_reconciled_normalized_iob.json | 1 - .../Models/DoseEntryInsulinMathTests.swift | 510 ----- 18 files changed, 4842 deletions(-) delete mode 100644 LoopTests/Fixtures/insulin_math/basal.json delete mode 100644 LoopTests/Fixtures/insulin_math/iob_from_doses_exponential_output.json delete mode 100644 LoopTests/Fixtures/insulin_math/iob_from_doses_output.json delete mode 100644 LoopTests/Fixtures/insulin_math/iob_from_multiple_curves_output.json delete mode 100644 LoopTests/Fixtures/insulin_math/iob_from_reservoir_output.json delete mode 100644 LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_input.json delete mode 100644 LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_mutable_input.json delete mode 100644 LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_mutable_output.json delete mode 100644 LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_output.json delete mode 100644 LoopTests/Fixtures/insulin_math/normalized_doses.json delete mode 100644 LoopTests/Fixtures/insulin_math/normalized_reservoir_history_output.json delete mode 100644 LoopTests/Fixtures/insulin_math/reservoir_history_with_rewind_and_prime_output.json delete mode 100644 LoopTests/Fixtures/insulin_math/suspend_dose.json delete mode 100644 LoopTests/Fixtures/insulin_math/suspend_dose_reconciled.json delete mode 100644 LoopTests/Fixtures/insulin_math/suspend_dose_reconciled_normalized.json delete mode 100644 LoopTests/Fixtures/insulin_math/suspend_dose_reconciled_normalized_iob.json delete mode 100644 LoopTests/Models/DoseEntryInsulinMathTests.swift diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj index b0b7b65d80..b147fc93e2 100644 --- a/Loop.xcodeproj/project.pbxproj +++ b/Loop.xcodeproj/project.pbxproj @@ -719,24 +719,7 @@ FEEDB0000000000000000005 /* GlucoseStatisticsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEEDF0000000000000000005 /* GlucoseStatisticsTests.swift */; }; 17EA66859884C0208DED47CF /* LoopKitTypeAliases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FAEFD820F50BFF211AC6D9 /* LoopKitTypeAliases.swift */; }; 1C99EE4A3BB181BB23DEF066 /* LoopAlgorithm in Frameworks */ = {isa = PBXBuildFile; productRef = C42F6B8A2B1B9E0142078BF9 /* LoopAlgorithm */; }; - 82430C768078D9BEDFDECA6C /* DoseEntryInsulinMathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0A246E78F6985AB1207647 /* DoseEntryInsulinMathTests.swift */; }; BD37CA2F10445DB600A35199 /* TemporaryScheduleOverrideAlgorithmTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C372374D3D3D9C5F8E3AD93 /* TemporaryScheduleOverrideAlgorithmTests.swift */; }; - 0D48B6D1CF1EA2B542229625 /* basal.json in Resources */ = {isa = PBXBuildFile; fileRef = 00B649A44AFB50BC4B9A70F6 /* basal.json */; }; - 0131FD2953DE7C998EE3F663 /* iob_from_doses_exponential_output.json in Resources */ = {isa = PBXBuildFile; fileRef = B3DDD98615BAC7131714F89B /* iob_from_doses_exponential_output.json */; }; - C09625393B9711F25BAD3D52 /* iob_from_doses_output.json in Resources */ = {isa = PBXBuildFile; fileRef = 5B3F3FD4EDF5B0DE5F25489A /* iob_from_doses_output.json */; }; - 59D3D2A08D733F67B0F0935E /* iob_from_multiple_curves_output.json in Resources */ = {isa = PBXBuildFile; fileRef = DD57D2F830A1249A6C5622C0 /* iob_from_multiple_curves_output.json */; }; - 3327886285617123471C28DA /* iob_from_reservoir_output.json in Resources */ = {isa = PBXBuildFile; fileRef = C8FF391DE1198B1832CE4313 /* iob_from_reservoir_output.json */; }; - 5EF5D4007DBE6486653E11EF /* normalize_edge_case_doses_input.json in Resources */ = {isa = PBXBuildFile; fileRef = 2DB5AB3635FA0041E6102578 /* normalize_edge_case_doses_input.json */; }; - 3948ACE22554E6743AF410D9 /* normalize_edge_case_doses_mutable_input.json in Resources */ = {isa = PBXBuildFile; fileRef = 2776E774AAD3E4D7CFE1FA6E /* normalize_edge_case_doses_mutable_input.json */; }; - 1C21A7E243CB4F2833F77511 /* normalize_edge_case_doses_mutable_output.json in Resources */ = {isa = PBXBuildFile; fileRef = 1FC4107E6003069B6CE7057A /* normalize_edge_case_doses_mutable_output.json */; }; - 9AC8982609B95285B3F1BB52 /* normalize_edge_case_doses_output.json in Resources */ = {isa = PBXBuildFile; fileRef = 19B187891AF73C340EE9854A /* normalize_edge_case_doses_output.json */; }; - B8668893E285EC0E23BACEFA /* normalized_doses.json in Resources */ = {isa = PBXBuildFile; fileRef = 9C2AC83FE854B422A6BCC7A8 /* normalized_doses.json */; }; - 89E3C86E262B61F3D940E805 /* normalized_reservoir_history_output.json in Resources */ = {isa = PBXBuildFile; fileRef = 69A018B00CD62B1E5349701F /* normalized_reservoir_history_output.json */; }; - 8CA0807CED5865CF795FEA1C /* reservoir_history_with_rewind_and_prime_output.json in Resources */ = {isa = PBXBuildFile; fileRef = A69826F94E3337F0DE9F46C1 /* reservoir_history_with_rewind_and_prime_output.json */; }; - 3B8D727402F48A542A6B3F86 /* suspend_dose.json in Resources */ = {isa = PBXBuildFile; fileRef = 9BE0AA50E7B42B8FBAFA8304 /* suspend_dose.json */; }; - 5530CD403A0857E55CFDD24C /* suspend_dose_reconciled.json in Resources */ = {isa = PBXBuildFile; fileRef = 1EBEB8F96DA092DAD8140367 /* suspend_dose_reconciled.json */; }; - D3311BF2CE4A9689C7FC14C4 /* suspend_dose_reconciled_normalized.json in Resources */ = {isa = PBXBuildFile; fileRef = B78CA313148D4CF2EDBBC165 /* suspend_dose_reconciled_normalized.json */; }; - 0491B8B99FD555646B83E455 /* suspend_dose_reconciled_normalized_iob.json in Resources */ = {isa = PBXBuildFile; fileRef = F57C6E57E2318FE5A3353E85 /* suspend_dose_reconciled_normalized_iob.json */; }; 5E77F6516D69B22A57EB850A /* LoopAlgorithm in Frameworks */ = {isa = PBXBuildFile; productRef = 761F6A285BE2833DF06F7E55 /* LoopAlgorithm */; }; 613BF2B78104AB9694590C65 /* AlgorithmBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36421A4B9676EF68CDDC4ECD /* AlgorithmBridge.swift */; }; 767B36174D190F3191F2EA73 /* LoopKitTypeAliases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FAEFD820F50BFF211AC6D9 /* LoopKitTypeAliases.swift */; }; @@ -1612,24 +1595,7 @@ FEEDF0000000000000000005 /* GlucoseStatisticsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseStatisticsTests.swift; sourceTree = ""; }; 35FAEFD820F50BFF211AC6D9 /* LoopKitTypeAliases.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LoopKitTypeAliases.swift; sourceTree = ""; }; 36421A4B9676EF68CDDC4ECD /* AlgorithmBridge.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AlgorithmBridge.swift; sourceTree = ""; }; - 0E0A246E78F6985AB1207647 /* DoseEntryInsulinMathTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DoseEntryInsulinMathTests.swift; sourceTree = ""; }; 6C372374D3D3D9C5F8E3AD93 /* TemporaryScheduleOverrideAlgorithmTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemporaryScheduleOverrideAlgorithmTests.swift; sourceTree = ""; }; - 00B649A44AFB50BC4B9A70F6 /* basal.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = basal.json; sourceTree = ""; }; - B3DDD98615BAC7131714F89B /* iob_from_doses_exponential_output.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = iob_from_doses_exponential_output.json; sourceTree = ""; }; - 5B3F3FD4EDF5B0DE5F25489A /* iob_from_doses_output.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = iob_from_doses_output.json; sourceTree = ""; }; - DD57D2F830A1249A6C5622C0 /* iob_from_multiple_curves_output.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = iob_from_multiple_curves_output.json; sourceTree = ""; }; - C8FF391DE1198B1832CE4313 /* iob_from_reservoir_output.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = iob_from_reservoir_output.json; sourceTree = ""; }; - 2DB5AB3635FA0041E6102578 /* normalize_edge_case_doses_input.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = normalize_edge_case_doses_input.json; sourceTree = ""; }; - 2776E774AAD3E4D7CFE1FA6E /* normalize_edge_case_doses_mutable_input.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = normalize_edge_case_doses_mutable_input.json; sourceTree = ""; }; - 1FC4107E6003069B6CE7057A /* normalize_edge_case_doses_mutable_output.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = normalize_edge_case_doses_mutable_output.json; sourceTree = ""; }; - 19B187891AF73C340EE9854A /* normalize_edge_case_doses_output.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = normalize_edge_case_doses_output.json; sourceTree = ""; }; - 9C2AC83FE854B422A6BCC7A8 /* normalized_doses.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = normalized_doses.json; sourceTree = ""; }; - 69A018B00CD62B1E5349701F /* normalized_reservoir_history_output.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = normalized_reservoir_history_output.json; sourceTree = ""; }; - A69826F94E3337F0DE9F46C1 /* reservoir_history_with_rewind_and_prime_output.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = reservoir_history_with_rewind_and_prime_output.json; sourceTree = ""; }; - 9BE0AA50E7B42B8FBAFA8304 /* suspend_dose.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = suspend_dose.json; sourceTree = ""; }; - 1EBEB8F96DA092DAD8140367 /* suspend_dose_reconciled.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = suspend_dose_reconciled.json; sourceTree = ""; }; - B78CA313148D4CF2EDBBC165 /* suspend_dose_reconciled_normalized.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = suspend_dose_reconciled_normalized.json; sourceTree = ""; }; - F57C6E57E2318FE5A3353E85 /* suspend_dose_reconciled_normalized_iob.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = suspend_dose_reconciled_normalized_iob.json; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -2857,7 +2823,6 @@ A9BD28E6272226B40071DF15 /* TestLocalizedError.swift */, A9DFAFB424F048A000950D1E /* WatchHistoricalCarbsTests.swift */, A9C1719625366F780053BCBD /* WatchHistoricalGlucoseTest.swift */, - 0E0A246E78F6985AB1207647 /* DoseEntryInsulinMathTests.swift */, 6C372374D3D3D9C5F8E3AD93 /* TemporaryScheduleOverrideAlgorithmTests.swift */, ); path = Models; @@ -3023,34 +2988,10 @@ E9C58A7924DB529A00487A17 /* dynamic_glucose_effect_partially_observed.json */, E9C58A7B24DB529A00487A17 /* insulin_effect.json */, E9C58A7724DB529A00487A17 /* momentum_effect_bouncing.json */, - 6783361D7C37BAC435B5F770 /* insulin_math */, ); path = Fixtures; sourceTree = ""; }; - 6783361D7C37BAC435B5F770 /* insulin_math */ = { - isa = PBXGroup; - children = ( - 00B649A44AFB50BC4B9A70F6 /* basal.json */, - B3DDD98615BAC7131714F89B /* iob_from_doses_exponential_output.json */, - 5B3F3FD4EDF5B0DE5F25489A /* iob_from_doses_output.json */, - DD57D2F830A1249A6C5622C0 /* iob_from_multiple_curves_output.json */, - C8FF391DE1198B1832CE4313 /* iob_from_reservoir_output.json */, - 2DB5AB3635FA0041E6102578 /* normalize_edge_case_doses_input.json */, - 2776E774AAD3E4D7CFE1FA6E /* normalize_edge_case_doses_mutable_input.json */, - 1FC4107E6003069B6CE7057A /* normalize_edge_case_doses_mutable_output.json */, - 19B187891AF73C340EE9854A /* normalize_edge_case_doses_output.json */, - 9C2AC83FE854B422A6BCC7A8 /* normalized_doses.json */, - 69A018B00CD62B1E5349701F /* normalized_reservoir_history_output.json */, - A69826F94E3337F0DE9F46C1 /* reservoir_history_with_rewind_and_prime_output.json */, - 9BE0AA50E7B42B8FBAFA8304 /* suspend_dose.json */, - 1EBEB8F96DA092DAD8140367 /* suspend_dose_reconciled.json */, - B78CA313148D4CF2EDBBC165 /* suspend_dose_reconciled_normalized.json */, - F57C6E57E2318FE5A3353E85 /* suspend_dose_reconciled_normalized_iob.json */, - ); - path = insulin_math; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -3518,22 +3459,6 @@ E9C58A7D24DB529A00487A17 /* basal_profile.json in Resources */, E9C58A7E24DB529A00487A17 /* dynamic_glucose_effect_partially_observed.json in Resources */, E9C58A7C24DB529A00487A17 /* momentum_effect_bouncing.json in Resources */, - 0D48B6D1CF1EA2B542229625 /* basal.json in Resources */, - 0131FD2953DE7C998EE3F663 /* iob_from_doses_exponential_output.json in Resources */, - C09625393B9711F25BAD3D52 /* iob_from_doses_output.json in Resources */, - 59D3D2A08D733F67B0F0935E /* iob_from_multiple_curves_output.json in Resources */, - 3327886285617123471C28DA /* iob_from_reservoir_output.json in Resources */, - 5EF5D4007DBE6486653E11EF /* normalize_edge_case_doses_input.json in Resources */, - 3948ACE22554E6743AF410D9 /* normalize_edge_case_doses_mutable_input.json in Resources */, - 1C21A7E243CB4F2833F77511 /* normalize_edge_case_doses_mutable_output.json in Resources */, - 9AC8982609B95285B3F1BB52 /* normalize_edge_case_doses_output.json in Resources */, - B8668893E285EC0E23BACEFA /* normalized_doses.json in Resources */, - 89E3C86E262B61F3D940E805 /* normalized_reservoir_history_output.json in Resources */, - 8CA0807CED5865CF795FEA1C /* reservoir_history_with_rewind_and_prime_output.json in Resources */, - 3B8D727402F48A542A6B3F86 /* suspend_dose.json in Resources */, - 5530CD403A0857E55CFDD24C /* suspend_dose_reconciled.json in Resources */, - D3311BF2CE4A9689C7FC14C4 /* suspend_dose_reconciled_normalized.json in Resources */, - 0491B8B99FD555646B83E455 /* suspend_dose_reconciled_normalized_iob.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4231,7 +4156,6 @@ E93E86B224DDE21D00FF40C8 /* MockCarbStore.swift in Sources */, C1DA43592B1A784900CBD33F /* MockDeliveryDelegate.swift in Sources */, C18859A02AF1612B0010F21F /* PersistenceController.swift in Sources */, - 82430C768078D9BEDFDECA6C /* DoseEntryInsulinMathTests.swift in Sources */, BD37CA2F10445DB600A35199 /* TemporaryScheduleOverrideAlgorithmTests.swift in Sources */, AF79DFAB0F55143D66022668 /* LoopKitTypeAliases.swift in Sources */, ); diff --git a/LoopTests/Fixtures/insulin_math/basal.json b/LoopTests/Fixtures/insulin_math/basal.json deleted file mode 100644 index 98f572dcef..0000000000 --- a/LoopTests/Fixtures/insulin_math/basal.json +++ /dev/null @@ -1,44 +0,0 @@ -[ - { - "i": 0, - "start": "00:00:00", - "rate": 0.9, - "minutes": 0 - }, - { - "i": 1, - "start": "04:00:00", - "rate": 0.925, - "minutes": 240 - }, - { - "i": 2, - "start": "07:00:00", - "rate": 0.85, - "minutes": 420 - }, - { - "i": 3, - "start": "10:00:00", - "rate": 0.85, - "minutes": 600 - }, - { - "i": 4, - "start": "12:00:00", - "rate": 0.75, - "minutes": 720 - }, - { - "i": 5, - "start": "15:00:00", - "rate": 0.8, - "minutes": 900 - }, - { - "i": 6, - "start": "22:00:00", - "rate": 0.9, - "minutes": 1320 - } -] \ No newline at end of file diff --git a/LoopTests/Fixtures/insulin_math/iob_from_doses_exponential_output.json b/LoopTests/Fixtures/insulin_math/iob_from_doses_exponential_output.json deleted file mode 100644 index 6f86752281..0000000000 --- a/LoopTests/Fixtures/insulin_math/iob_from_doses_exponential_output.json +++ /dev/null @@ -1,642 +0,0 @@ -[ - { - "date" : "2015-10-15T18:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2015-10-15T18:10:00", - "unit" : "U", - "value" : -0.018750000000000003 - }, - { - "date" : "2015-10-15T18:15:00", - "unit" : "U", - "value" : -0.020083333333333335 - }, - { - "date" : "2015-10-15T18:20:00", - "unit" : "U", - "value" : -0.08674967391190945 - }, - { - "date" : "2015-10-15T18:25:00", - "unit" : "U", - "value" : -0.1533638209815783 - }, - { - "date" : "2015-10-15T18:30:00", - "unit" : "U", - "value" : -0.21985157514541492 - }, - { - "date" : "2015-10-15T18:35:00", - "unit" : "U", - "value" : -0.28594868294984466 - }, - { - "date" : "2015-10-15T18:40:00", - "unit" : "U", - "value" : -0.35138913185798915 - }, - { - "date" : "2015-10-15T18:45:00", - "unit" : "U", - "value" : -0.41460789687197663 - }, - { - "date" : "2015-10-15T18:50:00", - "unit" : "U", - "value" : -0.41140454839697138 - }, - { - "date" : "2015-10-15T18:55:00", - "unit" : "U", - "value" : -0.40694042602624531 - }, - { - "date" : "2015-10-15T19:00:00", - "unit" : "U", - "value" : -0.40111368746478182 - }, - { - "date" : "2015-10-15T19:05:00", - "unit" : "U", - "value" : -0.45918724201570321 - }, - { - "date" : "2015-10-15T19:10:00", - "unit" : "U", - "value" : -0.51777937898327886 - }, - { - "date" : "2015-10-15T19:15:00", - "unit" : "U", - "value" : -0.57545506272901459 - }, - { - "date" : "2015-10-15T19:20:00", - "unit" : "U", - "value" : -0.67031079437724594 - }, - { - "date" : "2015-10-15T19:25:00", - "unit" : "U", - "value" : -0.65308790247022863 - }, - { - "date" : "2015-10-15T19:30:00", - "unit" : "U", - "value" : 4.5844563428505971 - }, - { - "date" : "2015-10-15T19:35:00", - "unit" : "U", - "value" : 4.8234744416481918 - }, - { - "date" : "2015-10-15T19:40:00", - "unit" : "U", - "value" : 5.0555348107831648 - }, - { - "date" : "2015-10-15T19:45:00", - "unit" : "U", - "value" : 5.2654029256034685 - }, - { - "date" : "2015-10-15T19:50:00", - "unit" : "U", - "value" : 5.4543457491633829 - }, - { - "date" : "2015-10-15T19:55:00", - "unit" : "U", - "value" : 5.5796818289595951 - }, - { - "date" : "2015-10-15T20:00:00", - "unit" : "U", - "value" : 5.7313776281043038 - }, - { - "date" : "2015-10-15T20:05:00", - "unit" : "U", - "value" : 5.866534076169672 - }, - { - "date" : "2015-10-15T20:10:00", - "unit" : "U", - "value" : 9.3515233597392591 - }, - { - "date" : "2015-10-15T20:15:00", - "unit" : "U", - "value" : 9.2639948506563528 - }, - { - "date" : "2015-10-15T20:20:00", - "unit" : "U", - "value" : 9.1250155710516587 - }, - { - "date" : "2015-10-15T20:25:00", - "unit" : "U", - "value" : 8.959613360663484 - }, - { - "date" : "2015-10-15T20:30:00", - "unit" : "U", - "value" : 8.7679394277654108 - }, - { - "date" : "2015-10-15T20:35:00", - "unit" : "U", - "value" : 8.4976351667316088 - }, - { - "date" : "2015-10-15T20:40:00", - "unit" : "U", - "value" : 8.2718009242472466 - }, - { - "date" : "2015-10-15T20:45:00", - "unit" : "U", - "value" : 7.9677397922764914 - }, - { - "date" : "2015-10-15T20:50:00", - "unit" : "U", - "value" : 7.6531423110771213 - }, - { - "date" : "2015-10-15T20:55:00", - "unit" : "U", - "value" : 7.3305121057107296 - }, - { - "date" : "2015-10-15T21:00:00", - "unit" : "U", - "value" : 7.0039882775409179 - }, - { - "date" : "2015-10-15T21:05:00", - "unit" : "U", - "value" : 6.8599232815908628 - }, - { - "date" : "2015-10-15T21:10:00", - "unit" : "U", - "value" : 6.6827268353420592 - }, - { - "date" : "2015-10-15T21:15:00", - "unit" : "U", - "value" : 6.4975323125878832 - }, - { - "date" : "2015-10-15T21:20:00", - "unit" : "U", - "value" : 6.2311950271168621 - }, - { - "date" : "2015-10-15T21:25:00", - "unit" : "U", - "value" : 5.9662610775540603 - }, - { - "date" : "2015-10-15T21:30:00", - "unit" : "U", - "value" : 5.703410306528899 - }, - { - "date" : "2015-10-15T21:35:00", - "unit" : "U", - "value" : 5.443549906835079 - }, - { - "date" : "2015-10-15T21:40:00", - "unit" : "U", - "value" : 9.1941718730890045 - }, - { - "date" : "2015-10-15T21:45:00", - "unit" : "U", - "value" : 8.9426578501594971 - }, - { - "date" : "2015-10-15T21:50:00", - "unit" : "U", - "value" : 8.6873901964495222 - }, - { - "date" : "2015-10-15T21:55:00", - "unit" : "U", - "value" : 8.419996897114423 - }, - { - "date" : "2015-10-15T22:00:00", - "unit" : "U", - "value" : 8.1495692963244348 - }, - { - "date" : "2015-10-15T22:05:00", - "unit" : "U", - "value" : 8.0510013404305631 - }, - { - "date" : "2015-10-15T22:10:00", - "unit" : "U", - "value" : 7.947479248003801 - }, - { - "date" : "2015-10-15T22:15:00", - "unit" : "U", - "value" : 7.8407880010764028 - }, - { - "date" : "2015-10-15T22:20:00", - "unit" : "U", - "value" : 7.7318648464436839 - }, - { - "date" : "2015-10-15T22:25:00", - "unit" : "U", - "value" : 7.621458400003899 - }, - { - "date" : "2015-10-15T22:30:00", - "unit" : "U", - "value" : 7.504058141533358 - }, - { - "date" : "2015-10-15T22:35:00", - "unit" : "U", - "value" : 7.2072063559501762 - }, - { - "date" : "2015-10-15T22:40:00", - "unit" : "U", - "value" : 6.9106671373789181 - }, - { - "date" : "2015-10-15T22:45:00", - "unit" : "U", - "value" : 6.6150483599313352 - }, - { - "date" : "2015-10-15T22:50:00", - "unit" : "U", - "value" : 6.3215294099560326 - }, - { - "date" : "2015-10-15T22:55:00", - "unit" : "U", - "value" : 6.0312390617306626 - }, - { - "date" : "2015-10-15T23:00:00", - "unit" : "U", - "value" : 5.7451618503011987 - }, - { - "date" : "2015-10-15T23:05:00", - "unit" : "U", - "value" : 5.4641508016258493 - }, - { - "date" : "2015-10-15T23:10:00", - "unit" : "U", - "value" : 5.1889392020133407 - }, - { - "date" : "2015-10-15T23:15:00", - "unit" : "U", - "value" : 4.9201514731443847 - }, - { - "date" : "2015-10-15T23:20:00", - "unit" : "U", - "value" : 4.6583132146527513 - }, - { - "date" : "2015-10-15T23:25:00", - "unit" : "U", - "value" : 4.4038604721990744 - }, - { - "date" : "2015-10-15T23:30:00", - "unit" : "U", - "value" : 4.157148285179713 - }, - { - "date" : "2015-10-15T23:35:00", - "unit" : "U", - "value" : 3.9184585646600665 - }, - { - "date" : "2015-10-15T23:40:00", - "unit" : "U", - "value" : 3.6880073487913116 - }, - { - "date" : "2015-10-15T23:45:00", - "unit" : "U", - "value" : 3.4659514798492381 - }, - { - "date" : "2015-10-15T23:50:00", - "unit" : "U", - "value" : 3.2523947441097145 - }, - { - "date" : "2015-10-15T23:55:00", - "unit" : "U", - "value" : 3.0473935130363934 - }, - { - "date" : "2015-10-16T00:00:00", - "unit" : "U", - "value" : 2.8509619216906583 - }, - { - "date" : "2015-10-16T00:05:00", - "unit" : "U", - "value" : 2.6630766178710337 - }, - { - "date" : "2015-10-16T00:10:00", - "unit" : "U", - "value" : 2.4836811132395682 - }, - { - "date" : "2015-10-16T00:15:00", - "unit" : "U", - "value" : 2.3126897655863949 - }, - { - "date" : "2015-10-16T00:20:00", - "unit" : "U", - "value" : 2.1499914289175757 - }, - { - "date" : "2015-10-16T00:25:00", - "unit" : "U", - "value" : 1.9954542992700548 - }, - { - "date" : "2015-10-16T00:30:00", - "unit" : "U", - "value" : 1.8489281662494723 - }, - { - "date" : "2015-10-16T00:35:00", - "unit" : "U", - "value" : 1.7102522167447076 - }, - { - "date" : "2015-10-16T00:40:00", - "unit" : "U", - "value" : 1.5792522393334008 - }, - { - "date" : "2015-10-16T00:45:00", - "unit" : "U", - "value" : 1.4557417966735475 - }, - { - "date" : "2015-10-16T00:50:00", - "unit" : "U", - "value" : 1.3395243199229128 - }, - { - "date" : "2015-10-16T00:55:00", - "unit" : "U", - "value" : 1.2303950054342776 - }, - { - "date" : "2015-10-16T01:00:00", - "unit" : "U", - "value" : 1.1281412265192725 - }, - { - "date" : "2015-10-16T01:05:00", - "unit" : "U", - "value" : 1.0325384190273319 - }, - { - "date" : "2015-10-16T01:10:00", - "unit" : "U", - "value" : 0.94335795605105055 - }, - { - "date" : "2015-10-16T01:15:00", - "unit" : "U", - "value" : 0.86037059905713076 - }, - { - "date" : "2015-10-16T01:20:00", - "unit" : "U", - "value" : 0.78335347466781946 - }, - { - "date" : "2015-10-16T01:25:00", - "unit" : "U", - "value" : 0.71208475485032652 - }, - { - "date" : "2015-10-16T01:30:00", - "unit" : "U", - "value" : 0.64634339612662106 - }, - { - "date" : "2015-10-16T01:35:00", - "unit" : "U", - "value" : 0.58591097891387478 - }, - { - "date" : "2015-10-16T01:40:00", - "unit" : "U", - "value" : 0.53031810327106477 - }, - { - "date" : "2015-10-16T01:45:00", - "unit" : "U", - "value" : 0.47887549890416919 - }, - { - "date" : "2015-10-16T01:50:00", - "unit" : "U", - "value" : 0.43136991580673895 - }, - { - "date" : "2015-10-16T01:55:00", - "unit" : "U", - "value" : 0.38760420145484975 - }, - { - "date" : "2015-10-16T02:00:00", - "unit" : "U", - "value" : 0.34738572960367692 - }, - { - "date" : "2015-10-16T02:05:00", - "unit" : "U", - "value" : 0.31052608873297527 - }, - { - "date" : "2015-10-16T02:10:00", - "unit" : "U", - "value" : 0.27684765577284548 - }, - { - "date" : "2015-10-16T02:15:00", - "unit" : "U", - "value" : 0.24617422029248465 - }, - { - "date" : "2015-10-16T02:20:00", - "unit" : "U", - "value" : 0.21811262350069069 - }, - { - "date" : "2015-10-16T02:25:00", - "unit" : "U", - "value" : 0.1922648983849887 - }, - { - "date" : "2015-10-16T02:30:00", - "unit" : "U", - "value" : 0.16852209381294375 - }, - { - "date" : "2015-10-16T02:35:00", - "unit" : "U", - "value" : 0.14678704473003373 - }, - { - "date" : "2015-10-16T02:40:00", - "unit" : "U", - "value" : 0.12696661469528853 - }, - { - "date" : "2015-10-16T02:45:00", - "unit" : "U", - "value" : 0.10897012161281953 - }, - { - "date" : "2015-10-16T02:50:00", - "unit" : "U", - "value" : 0.092714235329971514 - }, - { - "date" : "2015-10-16T02:55:00", - "unit" : "U", - "value" : 0.07811720379651968 - }, - { - "date" : "2015-10-16T03:00:00", - "unit" : "U", - "value" : 0.065102801536982405 - }, - { - "date" : "2015-10-16T03:05:00", - "unit" : "U", - "value" : 0.053597770353323318 - }, - { - "date" : "2015-10-16T03:10:00", - "unit" : "U", - "value" : 0.043530781995733972 - }, - { - "date" : "2015-10-16T03:15:00", - "unit" : "U", - "value" : 0.03482327014417011 - }, - { - "date" : "2015-10-16T03:20:00", - "unit" : "U", - "value" : 0.027389826284725402 - }, - { - "date" : "2015-10-16T03:25:00", - "unit" : "U", - "value" : 0.021146086743643433 - }, - { - "date" : "2015-10-16T03:30:00", - "unit" : "U", - "value" : 0.016012309934482737 - }, - { - "date" : "2015-10-16T03:35:00", - "unit" : "U", - "value" : 0.011921441689652637 - }, - { - "date" : "2015-10-16T03:40:00", - "unit" : "U", - "value" : 0.0088100824151718816 - }, - { - "date" : "2015-10-16T03:45:00", - "unit" : "U", - "value" : 0.0066173260136511026 - }, - { - "date" : "2015-10-16T03:50:00", - "unit" : "U", - "value" : 0.0050210961976692075 - }, - { - "date" : "2015-10-16T03:55:00", - "unit" : "U", - "value" : 0.0036823783158275642 - }, - { - "date" : "2015-10-16T04:00:00", - "unit" : "U", - "value" : 0.0025852439700395813 - }, - { - "date" : "2015-10-16T04:05:00", - "unit" : "U", - "value" : 0.0017152598603126484 - }, - { - "date" : "2015-10-16T04:10:00", - "unit" : "U", - "value" : 0.0010585498741866298 - }, - { - "date" : "2015-10-16T04:15:00", - "unit" : "U", - "value" : 0.00059794710049142111 - }, - { - "date" : "2015-10-16T04:20:00", - "unit" : "U", - "value" : 0.00029752570973717093 - }, - { - "date" : "2015-10-16T04:25:00", - "unit" : "U", - "value" : 0.0001207375635234328 - }, - { - "date" : "2015-10-16T04:30:00", - "unit" : "U", - "value" : 3.3298623365256642e-05 - }, - { - "date" : "2015-10-16T04:35:00", - "unit" : "U", - "value" : 3.0976579948276282e-06 - }, - { - "date" : "2015-10-16T04:40:00", - "unit" : "U", - "value" : 0 - } -] \ No newline at end of file diff --git a/LoopTests/Fixtures/insulin_math/iob_from_doses_output.json b/LoopTests/Fixtures/insulin_math/iob_from_doses_output.json deleted file mode 100644 index b6570231a7..0000000000 --- a/LoopTests/Fixtures/insulin_math/iob_from_doses_output.json +++ /dev/null @@ -1 +0,0 @@ -[{"date": "2015-10-15T18:05:00", "value": 0.0, "unit": "U"}, {"date": "2015-10-15T18:10:00", "value": -0.018750000000000006, "unit": "U"}, {"date": "2015-10-15T18:15:00", "value": -0.085416666666666669, "unit": "U"}, {"date": "2015-10-15T18:20:00", "value": -0.15206668087682032, "unit": "U"}, {"date": "2015-10-15T18:25:00", "value": -0.21857935431982956, "unit": "U"}, {"date": "2015-10-15T18:30:00", "value": -0.28464801252543015, "unit": "U"}, {"date": "2015-10-15T18:35:00", "value": -0.35010510247148485, "unit": "U"}, {"date": "2015-10-15T18:40:00", "value": -0.41479512492210602, "unit": "U"}, {"date": "2015-10-15T18:45:00", "value": -0.41190763676098952, "unit": "U"}, {"date": "2015-10-15T18:50:00", "value": -0.40797691999141444, "unit": "U"}, {"date": "2015-10-15T18:55:00", "value": -0.40292107811937894, "unit": "U"}, {"date": "2015-10-15T19:00:00", "value": -0.39698624056940118, "unit": "U"}, {"date": "2015-10-15T19:05:00", "value": -0.45690984213971042, "unit": "U"}, {"date": "2015-10-15T19:10:00", "value": -0.5160939052014526, "unit": "U"}, {"date": "2015-10-15T19:15:00", "value": -0.5741902604296798, "unit": "U"}, {"date": "2015-10-15T19:20:00", "value": -0.66778492452731286, "unit": "U"}, {"date": "2015-10-15T19:25:00", "value": -0.43178101319080897, "unit": "U"}, {"date": "2015-10-15T19:30:00", "value": 4.8055335550120359, "unit": "U"}, {"date": "2015-10-15T19:35:00", "value": 5.0439569248931519, "unit": "U"}, {"date": "2015-10-15T19:40:00", "value": 5.2564032091288739, "unit": "U"}, {"date": "2015-10-15T19:45:00", "value": 5.4534881571023126, "unit": "U"}, {"date": "2015-10-15T19:50:00", "value": 5.5918540183876919, "unit": "U"}, {"date": "2015-10-15T19:55:00", "value": 5.7617970521213326, "unit": "U"}, {"date": "2015-10-15T20:00:00", "value": 5.9186590272588271, "unit": "U"}, {"date": "2015-10-15T20:05:00", "value": 6.0629788559331814, "unit": "U"}, {"date": "2015-10-15T20:10:00", "value": 9.3416452708111812, "unit": "U"}, {"date": "2015-10-15T20:15:00", "value": 9.2679264924395568, "unit": "U"}, {"date": "2015-10-15T20:20:00", "value": 9.1311566183775188, "unit": "U"}, {"date": "2015-10-15T20:25:00", "value": 8.9767537893386322, "unit": "U"}, {"date": "2015-10-15T20:30:00", "value": 8.8018580566191247, "unit": "U"}, {"date": "2015-10-15T20:35:00", "value": 8.552252234133352, "unit": "U"}, {"date": "2015-10-15T20:40:00", "value": 8.2844286507395548, "unit": "U"}, {"date": "2015-10-15T20:45:00", "value": 8.0040683600139655, "unit": "U"}, {"date": "2015-10-15T20:50:00", "value": 7.712867310317252, "unit": "U"}, {"date": "2015-10-15T20:55:00", "value": 7.4123606321714997, "unit": "U"}, {"date": "2015-10-15T21:00:00", "value": 7.1706722555753073, "unit": "U"}, {"date": "2015-10-15T21:05:00", "value": 7.1254496600122827, "unit": "U"}, {"date": "2015-10-15T21:10:00", "value": 6.9617393970761903, "unit": "U"}, {"date": "2015-10-15T21:15:00", "value": 6.7034202910941953, "unit": "U"}, {"date": "2015-10-15T21:20:00", "value": 6.4411294655025397, "unit": "U"}, {"date": "2015-10-15T21:25:00", "value": 6.1756930346329053, "unit": "U"}, {"date": "2015-10-15T21:30:00", "value": 5.9083425409811152, "unit": "U"}, {"date": "2015-10-15T21:35:00", "value": 5.6400399553070137, "unit": "U"}, {"date": "2015-10-15T21:40:00", "value": 9.3783661475995945, "unit": "U"}, {"date": "2015-10-15T21:45:00", "value": 9.1108542204103582, "unit": "U"}, {"date": "2015-10-15T21:50:00", "value": 8.8205731329424886, "unit": "U"}, {"date": "2015-10-15T21:55:00", "value": 8.5236544857453715, "unit": "U"}, {"date": "2015-10-15T22:00:00", "value": 8.4121628508470163, "unit": "U"}, {"date": "2015-10-15T22:05:00", "value": 8.2891516195852599, "unit": "U"}, {"date": "2015-10-15T22:10:00", "value": 8.1623939723762202, "unit": "U"}, {"date": "2015-10-15T22:15:00", "value": 8.031926498827497, "unit": "U"}, {"date": "2015-10-15T22:20:00", "value": 7.898682868513041, "unit": "U"}, {"date": "2015-10-15T22:25:00", "value": 7.7572027441484082, "unit": "U"}, {"date": "2015-10-15T22:30:00", "value": 7.4342948897702952, "unit": "U"}, {"date": "2015-10-15T22:35:00", "value": 7.1098374176912387, "unit": "U"}, {"date": "2015-10-15T22:40:00", "value": 6.7844713305118889, "unit": "U"}, {"date": "2015-10-15T22:45:00", "value": 6.4599143409690667, "unit": "U"}, {"date": "2015-10-15T22:50:00", "value": 6.1370686677850914, "unit": "U"}, {"date": "2015-10-15T22:55:00", "value": 5.8163964384449898, "unit": "U"}, {"date": "2015-10-15T23:00:00", "value": 5.4998105211368991, "unit": "U"}, {"date": "2015-10-15T23:05:00", "value": 5.1880290027561484, "unit": "U"}, {"date": "2015-10-15T23:10:00", "value": 4.8816947056251614, "unit": "U"}, {"date": "2015-10-15T23:15:00", "value": 4.5812274778780608, "unit": "U"}, {"date": "2015-10-15T23:20:00", "value": 4.2866787472586676, "unit": "U"}, {"date": "2015-10-15T23:25:00", "value": 3.9985006890305459, "unit": "U"}, {"date": "2015-10-15T23:30:00", "value": 3.7174102768933639, "unit": "U"}, {"date": "2015-10-15T23:35:00", "value": 3.4413002004784681, "unit": "U"}, {"date": "2015-10-15T23:40:00", "value": 3.1790663931988168, "unit": "U"}, {"date": "2015-10-15T23:45:00", "value": 2.9674459025121354, "unit": "U"}, {"date": "2015-10-15T23:50:00", "value": 2.7629813356602755, "unit": "U"}, {"date": "2015-10-15T23:55:00", "value": 2.5658111309990193, "unit": "U"}, {"date": "2015-10-16T00:00:00", "value": 2.3763386371089359, "unit": "U"}, {"date": "2015-10-16T00:05:00", "value": 2.1936103849532484, "unit": "U"}, {"date": "2015-10-16T00:10:00", "value": 2.0183994767110565, "unit": "U"}, {"date": "2015-10-16T00:15:00", "value": 1.8507002564177386, "unit": "U"}, {"date": "2015-10-16T00:20:00", "value": 1.6985068266336847, "unit": "U"}, {"date": "2015-10-16T00:25:00", "value": 1.5747066972951482, "unit": "U"}, {"date": "2015-10-16T00:30:00", "value": 1.4549325333370042, "unit": "U"}, {"date": "2015-10-16T00:35:00", "value": 1.3388718304255545, "unit": "U"}, {"date": "2015-10-16T00:40:00", "value": 1.2267156075864047, "unit": "U"}, {"date": "2015-10-16T00:45:00", "value": 1.1189147793984533, "unit": "U"}, {"date": "2015-10-16T00:50:00", "value": 1.0147401216299032, "unit": "U"}, {"date": "2015-10-16T00:55:00", "value": 0.9142150372798441, "unit": "U"}, {"date": "2015-10-16T01:00:00", "value": 0.81744236897927469, "unit": "U"}, {"date": "2015-10-16T01:05:00", "value": 0.72449566972835622, "unit": "U"}, {"date": "2015-10-16T01:10:00", "value": 0.63497148245908197, "unit": "U"}, {"date": "2015-10-16T01:15:00", "value": 0.54966882501852599, "unit": "U"}, {"date": "2015-10-16T01:20:00", "value": 0.47039614943892216, "unit": "U"}, {"date": "2015-10-16T01:25:00", "value": 0.3967708826026185, "unit": "U"}, {"date": "2015-10-16T01:30:00", "value": 0.32725818119704375, "unit": "U"}, {"date": "2015-10-16T01:35:00", "value": 0.26174046235113074, "unit": "U"}, {"date": "2015-10-16T01:40:00", "value": 0.20007472515215177, "unit": "U"}, {"date": "2015-10-16T01:45:00", "value": 0.14209255064571641, "unit": "U"}, {"date": "2015-10-16T01:50:00", "value": 0.095810842977274838, "unit": "U"}, {"date": "2015-10-16T01:55:00", "value": 0.080002965672173271, "unit": "U"}, {"date": "2015-10-16T02:00:00", "value": 0.065104330918007297, "unit": "U"}, {"date": "2015-10-16T02:05:00", "value": 0.051088357857381354, "unit": "U"}, {"date": "2015-10-16T02:10:00", "value": 0.036587766581119896, "unit": "U"}, {"date": "2015-10-16T02:15:00", "value": 0.024746042994965325, "unit": "U"}, {"date": "2015-10-16T02:20:00", "value": 0.015304914415581596, "unit": "U"}, {"date": "2015-10-16T02:25:00", "value": 0.0081185642627603517, "unit": "U"}, {"date": "2015-10-16T02:30:00", "value": 0.0030469902521267325, "unit": "U"}, {"date": "2015-10-16T02:35:00", "value": 0.0, "unit": "U"}, {"date": "2015-10-16T02:40:00", "value": 0.0, "unit": "U"}] \ No newline at end of file diff --git a/LoopTests/Fixtures/insulin_math/iob_from_multiple_curves_output.json b/LoopTests/Fixtures/insulin_math/iob_from_multiple_curves_output.json deleted file mode 100644 index 59d6d88351..0000000000 --- a/LoopTests/Fixtures/insulin_math/iob_from_multiple_curves_output.json +++ /dev/null @@ -1,1832 +0,0 @@ -[ - { - "date" : "2018-05-15T14:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T14:35:00", - "unit" : "U", - "value" : 0.068083333333333329 - }, - { - "date" : "2018-05-15T14:40:00", - "unit" : "U", - "value" : 0.22641666666666665 - }, - { - "date" : "2018-05-15T14:45:00", - "unit" : "U", - "value" : 1.2847500000000001 - }, - { - "date" : "2018-05-15T14:50:00", - "unit" : "U", - "value" : 1.4426604218252472 - }, - { - "date" : "2018-05-15T14:55:00", - "unit" : "U", - "value" : 1.6315008168845901 - }, - { - "date" : "2018-05-15T15:00:00", - "unit" : "U", - "value" : 1.856973269641518 - }, - { - "date" : "2018-05-15T15:05:00", - "unit" : "U", - "value" : 2.0051090073701832 - }, - { - "date" : "2018-05-15T15:10:00", - "unit" : "U", - "value" : 2.0541406193487384 - }, - { - "date" : "2018-05-15T15:15:00", - "unit" : "U", - "value" : 2.0944463997593505 - }, - { - "date" : "2018-05-15T15:20:00", - "unit" : "U", - "value" : 2.1265928762508812 - }, - { - "date" : "2018-05-15T15:25:00", - "unit" : "U", - "value" : 2.1516271451485611 - }, - { - "date" : "2018-05-15T15:30:00", - "unit" : "U", - "value" : 2.1704824312840056 - }, - { - "date" : "2018-05-15T15:35:00", - "unit" : "U", - "value" : 2.1839875490356584 - }, - { - "date" : "2018-05-15T15:40:00", - "unit" : "U", - "value" : 2.1928777062534417 - }, - { - "date" : "2018-05-15T15:45:00", - "unit" : "U", - "value" : 2.1978042452867692 - }, - { - "date" : "2018-05-15T15:50:00", - "unit" : "U", - "value" : 2.1993434218318555 - }, - { - "date" : "2018-05-15T15:55:00", - "unit" : "U", - "value" : 2.1657543130517265 - }, - { - "date" : "2018-05-15T16:00:00", - "unit" : "U", - "value" : 2.0869859379909013 - }, - { - "date" : "2018-05-15T16:05:00", - "unit" : "U", - "value" : 2.0061836656333947 - }, - { - "date" : "2018-05-15T16:10:00", - "unit" : "U", - "value" : 1.9238953054745647 - }, - { - "date" : "2018-05-15T16:15:00", - "unit" : "U", - "value" : 1.8408465292538017 - }, - { - "date" : "2018-05-15T16:20:00", - "unit" : "U", - "value" : 1.7576646941017673 - }, - { - "date" : "2018-05-15T16:25:00", - "unit" : "U", - "value" : 1.6748882348269276 - }, - { - "date" : "2018-05-15T16:30:00", - "unit" : "U", - "value" : 1.5929762431399233 - }, - { - "date" : "2018-05-15T16:35:00", - "unit" : "U", - "value" : 1.5123171313102044 - }, - { - "date" : "2018-05-15T16:40:00", - "unit" : "U", - "value" : 1.4332364621749809 - }, - { - "date" : "2018-05-15T16:45:00", - "unit" : "U", - "value" : 1.3560040203811585 - }, - { - "date" : "2018-05-15T16:50:00", - "unit" : "U", - "value" : 1.2808401932907929 - }, - { - "date" : "2018-05-15T16:55:00", - "unit" : "U", - "value" : 1.2079217240712818 - }, - { - "date" : "2018-05-15T17:00:00", - "unit" : "U", - "value" : 1.1373868940784675 - }, - { - "date" : "2018-05-15T17:05:00", - "unit" : "U", - "value" : 1.0693401866831296 - }, - { - "date" : "2018-05-15T17:10:00", - "unit" : "U", - "value" : 1.0038564801514345 - }, - { - "date" : "2018-05-15T17:15:00", - "unit" : "U", - "value" : 0.94098481303325587 - }, - { - "date" : "2018-05-15T17:20:00", - "unit" : "U", - "value" : 0.88075176170717628 - }, - { - "date" : "2018-05-15T17:25:00", - "unit" : "U", - "value" : 0.82316446624830775 - }, - { - "date" : "2018-05-15T17:30:00", - "unit" : "U", - "value" : 0.7682133375981065 - }, - { - "date" : "2018-05-15T17:35:00", - "unit" : "U", - "value" : 0.71587447609950972 - }, - { - "date" : "2018-05-15T17:40:00", - "unit" : "U", - "value" : 0.66611182879349462 - }, - { - "date" : "2018-05-15T17:45:00", - "unit" : "U", - "value" : 0.61887911043380595 - }, - { - "date" : "2018-05-15T17:50:00", - "unit" : "U", - "value" : 0.57412151094614827 - }, - { - "date" : "2018-05-15T17:55:00", - "unit" : "U", - "value" : 0.53177721001913325 - }, - { - "date" : "2018-05-15T18:00:00", - "unit" : "U", - "value" : 0.49177871765069903 - }, - { - "date" : "2018-05-15T18:05:00", - "unit" : "U", - "value" : 0.45405405777089097 - }, - { - "date" : "2018-05-15T18:10:00", - "unit" : "U", - "value" : 0.41852781050631305 - }, - { - "date" : "2018-05-15T18:15:00", - "unit" : "U", - "value" : 0.38512202723086875 - }, - { - "date" : "2018-05-15T18:20:00", - "unit" : "U", - "value" : 0.35375703125023233 - }, - { - "date" : "2018-05-15T18:25:00", - "unit" : "U", - "value" : 0.3243521157834296 - }, - { - "date" : "2018-05-15T18:30:00", - "unit" : "U", - "value" : 0.29682614982439526 - }, - { - "date" : "2018-05-15T18:35:00", - "unit" : "U", - "value" : 0.27109810148064761 - }, - { - "date" : "2018-05-15T18:40:00", - "unit" : "U", - "value" : 0.24708748748724252 - }, - { - "date" : "2018-05-15T18:45:00", - "unit" : "U", - "value" : 0.22471475677456665 - }, - { - "date" : "2018-05-15T18:50:00", - "unit" : "U", - "value" : 0.20390161522154615 - }, - { - "date" : "2018-05-15T18:55:00", - "unit" : "U", - "value" : 0.184571298045277 - }, - { - "date" : "2018-05-15T19:00:00", - "unit" : "U", - "value" : 0.16664879565826193 - }, - { - "date" : "2018-05-15T19:05:00", - "unit" : "U", - "value" : 0.15006103826013523 - }, - { - "date" : "2018-05-15T19:10:00", - "unit" : "U", - "value" : 0.13473704391723001 - }, - { - "date" : "2018-05-15T19:15:00", - "unit" : "U", - "value" : 0.12060803441618617 - }, - { - "date" : "2018-05-15T19:20:00", - "unit" : "U", - "value" : 0.10760752275303831 - }, - { - "date" : "2018-05-15T19:25:00", - "unit" : "U", - "value" : 0.095671375733160369 - }, - { - "date" : "2018-05-15T19:30:00", - "unit" : "U", - "value" : 0.084737854806747129 - }, - { - "date" : "2018-05-15T19:35:00", - "unit" : "U", - "value" : 0.074747637946072754 - }, - { - "date" : "2018-05-15T19:40:00", - "unit" : "U", - "value" : 0.065643825081797474 - }, - { - "date" : "2018-05-15T19:45:00", - "unit" : "U", - "value" : 0.057371929353482794 - }, - { - "date" : "2018-05-15T19:50:00", - "unit" : "U", - "value" : 0.0498798561918899 - }, - { - "date" : "2018-05-15T19:55:00", - "unit" : "U", - "value" : 0.04311787203539412 - }, - { - "date" : "2018-05-15T20:00:00", - "unit" : "U", - "value" : 0.03703856428799019 - }, - { - "date" : "2018-05-15T20:05:00", - "unit" : "U", - "value" : 0.031596793950053138 - }, - { - "date" : "2018-05-15T20:10:00", - "unit" : "U", - "value" : 0.026749642193648854 - }, - { - "date" : "2018-05-15T20:15:00", - "unit" : "U", - "value" : 0.022456352010185483 - }, - { - "date" : "2018-05-15T20:20:00", - "unit" : "U", - "value" : 0.018678265928236615 - }, - { - "date" : "2018-05-15T20:25:00", - "unit" : "U", - "value" : 0.015378760682142785 - }, - { - "date" : "2018-05-15T20:30:00", - "unit" : "U", - "value" : 0.012523179606376668 - }, - { - "date" : "2018-05-15T20:35:00", - "unit" : "U", - "value" : 0.010078763435580854 - }, - { - "date" : "2018-05-15T20:40:00", - "unit" : "U", - "value" : 0.0080145801046833092 - }, - { - "date" : "2018-05-15T20:45:00", - "unit" : "U", - "value" : 0.0063014540666978987 - }, - { - "date" : "2018-05-15T20:50:00", - "unit" : "U", - "value" : 0.0049061628994142908 - }, - { - "date" : "2018-05-15T20:55:00", - "unit" : "U", - "value" : 0.003790314555342678 - }, - { - "date" : "2018-05-15T21:00:00", - "unit" : "U", - "value" : 0.0028989697490798255 - }, - { - "date" : "2018-05-15T21:05:00", - "unit" : "U", - "value" : 0.0021858386616289212 - }, - { - "date" : "2018-05-15T21:10:00", - "unit" : "U", - "value" : 0.0016213951706374548 - }, - { - "date" : "2018-05-15T21:15:00", - "unit" : "U", - "value" : 0.0011748698803442786 - }, - { - "date" : "2018-05-15T21:20:00", - "unit" : "U", - "value" : 0.0008236024229041905 - }, - { - "date" : "2018-05-15T21:25:00", - "unit" : "U", - "value" : 0.00055393548025383656 - }, - { - "date" : "2018-05-15T21:30:00", - "unit" : "U", - "value" : 0.00035322601755862491 - }, - { - "date" : "2018-05-15T21:35:00", - "unit" : "U", - "value" : 0.00020976022448825627 - }, - { - "date" : "2018-05-15T21:40:00", - "unit" : "U", - "value" : 0.00011270366979644896 - }, - { - "date" : "2018-05-15T21:45:00", - "unit" : "U", - "value" : 5.2053208299390748e-05 - }, - { - "date" : "2018-05-15T21:50:00", - "unit" : "U", - "value" : 1.8590657644046435e-05 - }, - { - "date" : "2018-05-15T21:55:00", - "unit" : "U", - "value" : 3.8382512378587476e-06 - }, - { - "date" : "2018-05-15T22:00:00", - "unit" : "U", - "value" : 1.5864094737860769e-08 - }, - { - "date" : "2018-05-15T22:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T22:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T22:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T22:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T22:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T22:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T22:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T22:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T22:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T22:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T22:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T23:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T23:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T23:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T23:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T23:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T23:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T23:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T23:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T23:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T23:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T23:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-15T23:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T00:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T00:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T00:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T00:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T00:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T00:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T00:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T00:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T00:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T00:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T00:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T00:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T01:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T01:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T01:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T01:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T01:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T01:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T01:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T01:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T01:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T01:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T01:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T01:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T02:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T02:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T02:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T02:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T02:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T02:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T02:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T02:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T02:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T02:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T02:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T02:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T03:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T03:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T03:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T03:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T03:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T03:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T03:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T03:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T03:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T03:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T03:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T03:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T04:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T04:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T04:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T04:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T04:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T04:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T04:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T04:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T04:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T04:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T04:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T04:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T05:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T05:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T05:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T05:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T05:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T05:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T05:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T05:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T05:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T05:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T05:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T05:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T06:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T06:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T06:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T06:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T06:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T06:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T06:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T06:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T06:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T06:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T06:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T06:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T07:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T07:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T07:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T07:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T07:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T07:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T07:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T07:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T07:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T07:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T07:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T07:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T08:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T08:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T08:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T08:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T08:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T08:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T08:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T08:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T08:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T08:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T08:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T08:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T09:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T09:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T09:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T09:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T09:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T09:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T09:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T09:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T09:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T09:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T09:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T09:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T10:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T10:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T10:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T10:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T10:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T10:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T10:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T10:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T10:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T10:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T10:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T10:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T11:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T11:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T11:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T11:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T11:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T11:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T11:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T11:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T11:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T11:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T11:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T11:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T12:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T12:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T12:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T12:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T12:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T12:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T12:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T12:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T12:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T12:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T12:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T12:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T13:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T13:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T13:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T13:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T13:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T13:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T13:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T13:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T13:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T13:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T13:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T13:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T14:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T14:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T14:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T14:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T14:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T14:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T14:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T14:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T14:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T14:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T14:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T14:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T15:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T15:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T15:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T15:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T15:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T15:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T15:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T15:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T15:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T15:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T15:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T15:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T16:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T16:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T16:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T16:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T16:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T16:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T16:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T16:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T16:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T16:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T16:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T16:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T17:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T17:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T17:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T17:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T17:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T17:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T17:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T17:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T17:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T17:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T17:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T17:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T18:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T18:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T18:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T18:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T18:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T18:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T18:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T18:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T18:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T18:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T18:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T18:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T19:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T19:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T19:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T19:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T19:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T19:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T19:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T19:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T19:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T19:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T19:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T19:55:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T20:00:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T20:05:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T20:10:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T20:15:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T20:20:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T20:25:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T20:30:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T20:35:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T20:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T20:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T20:50:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2018-05-16T20:55:00", - "unit" : "U", - "value" : 0 - } -] \ No newline at end of file diff --git a/LoopTests/Fixtures/insulin_math/iob_from_reservoir_output.json b/LoopTests/Fixtures/insulin_math/iob_from_reservoir_output.json deleted file mode 100644 index 00dde41e7f..0000000000 --- a/LoopTests/Fixtures/insulin_math/iob_from_reservoir_output.json +++ /dev/null @@ -1,557 +0,0 @@ -[ - { - "date" : "2016-01-30T15:40:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2016-01-30T15:45:00", - "unit" : "U", - "value" : 0.13488888888888889 - }, - { - "date" : "2016-01-30T15:50:00", - "unit" : "U", - "value" : 0.068222222222222226 - }, - { - "date" : "2016-01-30T15:55:00", - "unit" : "U", - "value" : 0.067514484321220022 - }, - { - "date" : "2016-01-30T16:00:00", - "unit" : "U", - "value" : 0.06678896639537546 - }, - { - "date" : "2016-01-30T16:05:00", - "unit" : "U", - "value" : -0.00074881278069904855 - }, - { - "date" : "2016-01-30T16:10:00", - "unit" : "U", - "value" : -0.018427618409960961 - }, - { - "date" : "2016-01-30T16:15:00", - "unit" : "U", - "value" : -0.010875573683178208 - }, - { - "date" : "2016-01-30T16:20:00", - "unit" : "U", - "value" : -0.0029493923329813876 - }, - { - "date" : "2016-01-30T16:25:00", - "unit" : "U", - "value" : -0.020414483345468498 - }, - { - "date" : "2016-01-30T16:30:00", - "unit" : "U", - "value" : 0.062365834815154765 - }, - { - "date" : "2016-01-30T16:35:00", - "unit" : "U", - "value" : 0.14505998900045661 - }, - { - "date" : "2016-01-30T16:40:00", - "unit" : "U", - "value" : 0.32783757546514158 - }, - { - "date" : "2016-01-30T16:45:00", - "unit" : "U", - "value" : 0.4598019301783795 - }, - { - "date" : "2016-01-30T16:50:00", - "unit" : "U", - "value" : 0.59043188942305824 - }, - { - "date" : "2016-01-30T16:55:00", - "unit" : "U", - "value" : 0.74279454295062775 - }, - { - "date" : "2016-01-30T17:00:00", - "unit" : "U", - "value" : 0.91883957574081498 - }, - { - "date" : "2016-01-30T17:05:00", - "unit" : "U", - "value" : 4.7473335690414027 - }, - { - "date" : "2016-01-30T17:10:00", - "unit" : "U", - "value" : 4.7205893142678566 - }, - { - "date" : "2016-01-30T17:15:00", - "unit" : "U", - "value" : 4.6956460306439167 - }, - { - "date" : "2016-01-30T17:20:00", - "unit" : "U", - "value" : 4.6581878823793952 - }, - { - "date" : "2016-01-30T17:25:00", - "unit" : "U", - "value" : 4.5854451394476108 - }, - { - "date" : "2016-01-30T17:30:00", - "unit" : "U", - "value" : 4.6027813624337712 - }, - { - "date" : "2016-01-30T17:35:00", - "unit" : "U", - "value" : 4.6102259682999147 - }, - { - "date" : "2016-01-30T17:40:00", - "unit" : "U", - "value" : 4.5589603458756276 - }, - { - "date" : "2016-01-30T17:45:00", - "unit" : "U", - "value" : 4.548846830954254 - }, - { - "date" : "2016-01-30T17:50:00", - "unit" : "U", - "value" : 4.4565829509093584 - }, - { - "date" : "2016-01-30T17:55:00", - "unit" : "U", - "value" : 4.3325605947143888 - }, - { - "date" : "2016-01-30T18:00:00", - "unit" : "U", - "value" : 4.2267974800496662 - }, - { - "date" : "2016-01-30T18:05:00", - "unit" : "U", - "value" : 5.4155700945160623 - }, - { - "date" : "2016-01-30T18:10:00", - "unit" : "U", - "value" : 5.2494825347097569 - }, - { - "date" : "2016-01-30T18:15:00", - "unit" : "U", - "value" : 5.0467007018788044 - }, - { - "date" : "2016-01-30T18:20:00", - "unit" : "U", - "value" : 4.9041815574952192 - }, - { - "date" : "2016-01-30T18:25:00", - "unit" : "U", - "value" : 4.7641713008881359 - }, - { - "date" : "2016-01-30T18:30:00", - "unit" : "U", - "value" : 4.6196316848788213 - }, - { - "date" : "2016-01-30T18:35:00", - "unit" : "U", - "value" : 4.4708378425452819 - }, - { - "date" : "2016-01-30T18:40:00", - "unit" : "U", - "value" : 4.292838715026746 - }, - { - "date" : "2016-01-30T18:45:00", - "unit" : "U", - "value" : 4.0878775257155624 - }, - { - "date" : "2016-01-30T18:50:00", - "unit" : "U", - "value" : 3.8554187635181547 - }, - { - "date" : "2016-01-30T18:55:00", - "unit" : "U", - "value" : 3.6217340636776383 - }, - { - "date" : "2016-01-30T19:00:00", - "unit" : "U", - "value" : 3.4624685995070617 - }, - { - "date" : "2016-01-30T19:05:00", - "unit" : "U", - "value" : 3.3038194035334003 - }, - { - "date" : "2016-01-30T19:10:00", - "unit" : "U", - "value" : 3.12108881849452 - }, - { - "date" : "2016-01-30T19:15:00", - "unit" : "U", - "value" : 2.9640737031370752 - }, - { - "date" : "2016-01-30T19:20:00", - "unit" : "U", - "value" : 2.8090169147849231 - }, - { - "date" : "2016-01-30T19:25:00", - "unit" : "U", - "value" : 2.6310439784619697 - }, - { - "date" : "2016-01-30T19:30:00", - "unit" : "U", - "value" : 2.7158656703311048 - }, - { - "date" : "2016-01-30T19:35:00", - "unit" : "U", - "value" : 2.8433020866290324 - }, - { - "date" : "2016-01-30T19:40:00", - "unit" : "U", - "value" : 3.4632207817542824 - }, - { - "date" : "2016-01-30T19:45:00", - "unit" : "U", - "value" : 3.3198722041766531 - }, - { - "date" : "2016-01-30T19:50:00", - "unit" : "U", - "value" : 3.1994073575748523 - }, - { - "date" : "2016-01-30T19:55:00", - "unit" : "U", - "value" : 3.1797643299899385 - }, - { - "date" : "2016-01-30T20:00:00", - "unit" : "U", - "value" : 3.1121937375789712 - }, - { - "date" : "2016-01-30T20:05:00", - "unit" : "U", - "value" : 3.1446751787258891 - }, - { - "date" : "2016-01-30T20:10:00", - "unit" : "U", - "value" : 3.1285196330888527 - }, - { - "date" : "2016-01-30T20:15:00", - "unit" : "U", - "value" : 3.1632801791886243 - }, - { - "date" : "2016-01-30T20:20:00", - "unit" : "U", - "value" : 4.1479809029771069 - }, - { - "date" : "2016-01-30T20:25:00", - "unit" : "U", - "value" : 3.9834822751069776 - }, - { - "date" : "2016-01-30T20:30:00", - "unit" : "U", - "value" : 3.8392406781815338 - }, - { - "date" : "2016-01-30T20:35:00", - "unit" : "U", - "value" : 3.6946591732484402 - }, - { - "date" : "2016-01-30T20:40:00", - "unit" : "U", - "value" : 3.5407257995084676 - }, - { - "date" : "2016-01-30T20:45:00", - "unit" : "U", - "value" : 3.3874261680274937 - }, - { - "date" : "2016-01-30T20:50:00", - "unit" : "U", - "value" : 3.2352531696570548 - }, - { - "date" : "2016-01-30T20:55:00", - "unit" : "U", - "value" : 3.085261005079988 - }, - { - "date" : "2016-01-30T21:00:00", - "unit" : "U", - "value" : 2.9372816673332909 - }, - { - "date" : "2016-01-30T21:05:00", - "unit" : "U", - "value" : 2.7907413629475002 - }, - { - "date" : "2016-01-30T21:10:00", - "unit" : "U", - "value" : 2.6471028260569947 - }, - { - "date" : "2016-01-30T21:15:00", - "unit" : "U", - "value" : 2.5111502668268844 - }, - { - "date" : "2016-01-30T21:20:00", - "unit" : "U", - "value" : 2.4064958525345617 - }, - { - "date" : "2016-01-30T21:25:00", - "unit" : "U", - "value" : 2.3012714622124935 - }, - { - "date" : "2016-01-30T21:30:00", - "unit" : "U", - "value" : 2.1960183464844891 - }, - { - "date" : "2016-01-30T21:35:00", - "unit" : "U", - "value" : 2.0910235309730165 - }, - { - "date" : "2016-01-30T21:40:00", - "unit" : "U", - "value" : 1.986502686195295 - }, - { - "date" : "2016-01-30T21:45:00", - "unit" : "U", - "value" : 1.8835249690318872 - }, - { - "date" : "2016-01-30T21:50:00", - "unit" : "U", - "value" : 1.7822280295871071 - }, - { - "date" : "2016-01-30T21:55:00", - "unit" : "U", - "value" : 1.6824769595711171 - }, - { - "date" : "2016-01-30T22:00:00", - "unit" : "U", - "value" : 1.5847700423010984 - }, - { - "date" : "2016-01-30T22:05:00", - "unit" : "U", - "value" : 1.4886582778513744 - }, - { - "date" : "2016-01-30T22:10:00", - "unit" : "U", - "value" : 1.3941551212776522 - }, - { - "date" : "2016-01-30T22:15:00", - "unit" : "U", - "value" : 1.303115763902519 - }, - { - "date" : "2016-01-30T22:20:00", - "unit" : "U", - "value" : 1.2241938781749204 - }, - { - "date" : "2016-01-30T22:25:00", - "unit" : "U", - "value" : 1.1463251788331443 - }, - { - "date" : "2016-01-30T22:30:00", - "unit" : "U", - "value" : 1.0695894037349465 - }, - { - "date" : "2016-01-30T22:35:00", - "unit" : "U", - "value" : 0.99471197396772404 - }, - { - "date" : "2016-01-30T22:40:00", - "unit" : "U", - "value" : 0.92189770714827757 - }, - { - "date" : "2016-01-30T22:45:00", - "unit" : "U", - "value" : 0.85127298068876722 - }, - { - "date" : "2016-01-30T22:50:00", - "unit" : "U", - "value" : 0.78291513570575266 - }, - { - "date" : "2016-01-30T22:55:00", - "unit" : "U", - "value" : 0.71669318624836187 - }, - { - "date" : "2016-01-30T23:00:00", - "unit" : "U", - "value" : 0.65247840707485749 - }, - { - "date" : "2016-01-30T23:05:00", - "unit" : "U", - "value" : 0.59016663173339567 - }, - { - "date" : "2016-01-30T23:10:00", - "unit" : "U", - "value" : 0.52993154731493231 - }, - { - "date" : "2016-01-30T23:15:00", - "unit" : "U", - "value" : 0.47241619989047379 - }, - { - "date" : "2016-01-30T23:20:00", - "unit" : "U", - "value" : 0.41760095998118058 - }, - { - "date" : "2016-01-30T23:25:00", - "unit" : "U", - "value" : 0.36531374345888057 - }, - { - "date" : "2016-01-30T23:30:00", - "unit" : "U", - "value" : 0.31573377830312777 - }, - { - "date" : "2016-01-30T23:35:00", - "unit" : "U", - "value" : 0.26879376195107563 - }, - { - "date" : "2016-01-30T23:40:00", - "unit" : "U", - "value" : 0.22326696667877355 - }, - { - "date" : "2016-01-30T23:45:00", - "unit" : "U", - "value" : 0.18282221358519934 - }, - { - "date" : "2016-01-30T23:50:00", - "unit" : "U", - "value" : 0.14905533718034986 - }, - { - "date" : "2016-01-30T23:55:00", - "unit" : "U", - "value" : 0.12297488953685366 - }, - { - "date" : "2016-01-31T00:00:00", - "unit" : "U", - "value" : 0.098520190008746233 - }, - { - "date" : "2016-01-31T00:05:00", - "unit" : "U", - "value" : 0.075947521254802516 - }, - { - "date" : "2016-01-31T00:10:00", - "unit" : "U", - "value" : 0.05589249490473374 - }, - { - "date" : "2016-01-31T00:15:00", - "unit" : "U", - "value" : 0.037949724121158643 - }, - { - "date" : "2016-01-31T00:20:00", - "unit" : "U", - "value" : 0.022714114635155692 - }, - { - "date" : "2016-01-31T00:25:00", - "unit" : "U", - "value" : 0.0096951335481437178 - }, - { - "date" : "2016-01-31T00:30:00", - "unit" : "U", - "value" : 0.0002680112283814255 - }, - { - "date" : "2016-01-31T00:35:00", - "unit" : "U", - "value" : 0.00022992226001462311 - }, - { - "date" : "2016-01-31T00:40:00", - "unit" : "U", - "value" : 7.3444943720727971e-05 - }, - { - "date" : "2016-01-31T00:45:00", - "unit" : "U", - "value" : 0 - }, - { - "date" : "2016-01-31T00:50:00", - "unit" : "U", - "value" : 0 - } -] \ No newline at end of file diff --git a/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_input.json b/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_input.json deleted file mode 100644 index 5f1b6aba64..0000000000 --- a/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_input.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - { - "type": "TempBasal", - "start_at": "2015-07-12T23:59:59", - "end_at": "2015-07-13T07:54:00", - "amount": 2.0, - "unit": "U/hour" - }, - { - "type": "Bolus", - "start_at": "2015-07-13T11:00:00", - "end_at": "2015-07-13T11:00:00", - "amount": 3.0, - "unit": "U" - } -] \ No newline at end of file diff --git a/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_mutable_input.json b/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_mutable_input.json deleted file mode 100644 index 329f46b2c5..0000000000 --- a/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_mutable_input.json +++ /dev/null @@ -1,18 +0,0 @@ -[ - { - "type": "TempBasal", - "start_at": "2015-07-12T23:59:59", - "end_at": "2015-07-13T07:54:00", - "amount": 2.0, - "unit": "U/hour", - "isMutable": true - }, - { - "type": "Bolus", - "start_at": "2015-07-13T11:00:00", - "end_at": "2015-07-13T11:00:00", - "amount": 3.0, - "unit": "U", - "isMutable": true - } -] diff --git a/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_mutable_output.json b/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_mutable_output.json deleted file mode 100644 index 583c312a83..0000000000 --- a/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_mutable_output.json +++ /dev/null @@ -1,42 +0,0 @@ -[ - { - "type": "TempBasal", - "start_at": "2015-07-12T23:59:59", - "end_at": "2015-07-13T00:00:00", - "amount": 1.1, - "unit": "U/hour", - "isMutable": true - }, - { - "type": "TempBasal", - "start_at": "2015-07-13T00:00:00", - "end_at": "2015-07-13T04:00:00", - "amount": 1.1, - "unit": "U/hour", - "isMutable": true - }, - { - "type": "TempBasal", - "start_at": "2015-07-13T04:00:00", - "end_at": "2015-07-13T07:00:00", - "amount": 1.075, - "unit": "U/hour", - "isMutable": true - }, - { - "type": "TempBasal", - "start_at": "2015-07-13T07:00:00", - "end_at": "2015-07-13T07:54:00", - "amount": 1.15, - "unit": "U/hour", - "isMutable": true - }, - { - "type": "Bolus", - "start_at": "2015-07-13T11:00:00", - "end_at": "2015-07-13T11:00:00", - "amount": 3.0, - "unit": "U", - "isMutable": true - } -] diff --git a/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_output.json b/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_output.json deleted file mode 100644 index 52f09439e4..0000000000 --- a/LoopTests/Fixtures/insulin_math/normalize_edge_case_doses_output.json +++ /dev/null @@ -1,37 +0,0 @@ -[ - { - "type": "TempBasal", - "start_at": "2015-07-12T23:59:59", - "end_at": "2015-07-13T00:00:00", - "amount": 1.1, - "unit": "U/hour" - }, - { - "type": "TempBasal", - "start_at": "2015-07-13T00:00:00", - "end_at": "2015-07-13T04:00:00", - "amount": 1.1, - "unit": "U/hour" - }, - { - "type": "TempBasal", - "start_at": "2015-07-13T04:00:00", - "end_at": "2015-07-13T07:00:00", - "amount": 1.075, - "unit": "U/hour" - }, - { - "type": "TempBasal", - "start_at": "2015-07-13T07:00:00", - "end_at": "2015-07-13T07:54:00", - "amount": 1.15, - "unit": "U/hour" - }, - { - "type": "Bolus", - "start_at": "2015-07-13T11:00:00", - "end_at": "2015-07-13T11:00:00", - "amount": 3.0, - "unit": "U" - } -] \ No newline at end of file diff --git a/LoopTests/Fixtures/insulin_math/normalized_doses.json b/LoopTests/Fixtures/insulin_math/normalized_doses.json deleted file mode 100644 index 127adea5dd..0000000000 --- a/LoopTests/Fixtures/insulin_math/normalized_doses.json +++ /dev/null @@ -1,171 +0,0 @@ -[ - { - "amount": 3.225, - "start_at": "2015-10-15T22:00:00", - "description": "TempBasal: 3.125U/hour over 30min", - "type": "TempBasal", - "unit": "U/hour", - "end_at": "2015-10-15T22:29:50", - "scheduled": 1.0, - "isMutable": true - }, - { - "amount": 3.325, - "start_at": "2015-10-15T21:59:50", - "description": "TempBasal: 3.125U/hour over 30min", - "type": "TempBasal", - "unit": "U/hour", - "end_at": "2015-10-15T22:00:00", - "scheduled": 1.0 - }, - { - "amount": 1.09999999999999998, - "start_at": "2015-10-15T21:39:49", - "description": "TempBasal: 0.9U/hour over 4min", - "type": "TempBasal", - "unit": "U/hour", - "end_at": "2015-10-15T21:43:49", - "scheduled": 1.0 - }, - { - "start_at": "2015-10-15T21:35:12", - "description": "Normal bolus: 4.0U", - "end_at": "2015-10-15T21:35:12", - "amount": 4.0, - "type": "Bolus", - "unit": "U" - }, - { - "amount": 2.075, - "start_at": "2015-10-15T21:04:34", - "description": "TempBasal: 1.875U/hour over 10min", - "type": "TempBasal", - "unit": "U/hour", - "end_at": "2015-10-15T21:14:34", - "scheduled": 1.0 - }, - { - "amount": 2.7, - "start_at": "2015-10-15T21:00:04", - "description": "TempBasal: 2.5U/hour over 4min", - "type": "TempBasal", - "unit": "U/hour", - "end_at": "2015-10-15T21:04:04", - "scheduled": 1.0 - }, - { - "amount": 0.0, - "start_at": "2015-10-15T20:39:52", - "description": "TempBasal: 0.0U/hour over 20min", - "type": "TempBasal", - "unit": "U/hour", - "end_at": "2015-10-15T20:59:52", - "scheduled": 0.8 - }, - { - "amount": 0.05, - "start_at": "2015-10-15T20:34:34", - "description": "TempBasal: 0.05U/hour over 5min", - "type": "TempBasal", - "unit": "U/hour", - "end_at": "2015-10-15T20:39:34", - "scheduled": 0.8 - }, - { - "amount": 0.95, - "start_at": "2015-10-15T20:29:51", - "description": "TempBasal: 0.75U/hour over 4min", - "type": "TempBasal", - "unit": "U/hour", - "end_at": "2015-10-15T20:33:51", - "scheduled": 1.0 - }, - { - "amount": 1.375, - "start_at": "2015-10-15T20:14:37", - "description": "TempBasal: 1.175U/hour over 5min", - "type": "TempBasal", - "unit": "U/hour", - "end_at": "2015-10-15T20:19:37", - "scheduled": 1.0 - }, - { - "amount": 2.075, - "start_at": "2015-10-15T20:09:52", - "description": "TempBasal: 1.875U/hour over 4min", - "type": "TempBasal", - "unit": "U/hour", - "end_at": "2015-10-15T20:13:52", - "scheduled": 1.0 - }, - { - "start_at": "2015-10-15T20:05:10", - "description": "Normal bolus: 3.3U", - "end_at": "2015-10-15T20:05:10", - "amount": 3.3, - "type": "Bolus", - "unit": "U" - }, - { - "amount": 3.7, - "start_at": "2015-10-15T19:54:51", - "description": "TempBasal: 3.5U/hour over 15min", - "type": "TempBasal", - "unit": "U/hour", - "end_at": "2015-10-15T20:09:51", - "scheduled": 1.0 - }, - { - "start_at": "2015-10-15T19:25:54", - "description": "Normal bolus: 5.0U", - "end_at": "2015-10-15T19:25:54", - "amount": 5.0, - "type": "Bolus", - "unit": "U" - }, - { - "amount": 3.7, - "start_at": "2015-10-15T19:24:52", - "description": "TempBasal: 3.5U/hour over 29min", - "type": "TempBasal", - "unit": "U/hour", - "end_at": "2015-10-15T19:53:52", - "scheduled": 1.0 - }, - { - "amount": 0.4, - "start_at": "2015-10-15T19:19:39", - "description": "TempBasal: 0.2U/hour over 5min", - "type": "TempBasal", - "unit": "U/hour", - "end_at": "2015-10-15T19:24:39", - "scheduled": 1.0 - }, - { - "amount": 0.2, - "start_at": "2015-10-15T19:00:07", - "description": "TempBasal: 0.0U/hour over 19min", - "type": "TempBasal", - "unit": "U/hour", - "end_at": "2015-10-15T19:19:07", - "scheduled": 1.0 - }, - { - "amount": 0.0, - "start_at": "2015-10-15T18:14:54", - "description": "TempBasal: 0.0U/hour over 30min", - "type": "TempBasal", - "unit": "U/hour", - "end_at": "2015-10-15T18:44:54", - "scheduled": 0.8 - }, - { - "amount": 0.775, - "start_at": "2015-10-15T18:09:35", - "description": "TempBasal: 0.575U/hour over 5min", - "type": "TempBasal", - "unit": "U/hour", - "end_at": "2015-10-15T18:14:35", - "scheduled": 1.0 - } -] diff --git a/LoopTests/Fixtures/insulin_math/normalized_reservoir_history_output.json b/LoopTests/Fixtures/insulin_math/normalized_reservoir_history_output.json deleted file mode 100644 index 131d0cc93d..0000000000 --- a/LoopTests/Fixtures/insulin_math/normalized_reservoir_history_output.json +++ /dev/null @@ -1,434 +0,0 @@ -[ - { - "start_at" : "2016-01-30T20:30:43", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T20:35:41", - "type" : "TempBasal", - "amount" : 0.90604026845637575, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T20:25:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T20:30:43", - "type" : "TempBasal", - "amount" : 0.89700996677740863, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T20:20:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T20:25:42", - "type" : "TempBasal", - "amount" : 0.60000000000000009, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T20:15:41", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T20:20:42", - "type" : "TempBasal", - "amount" : 14.352159468438538, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T20:10:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T20:15:41", - "type" : "TempBasal", - "amount" : 3.0100334448160533, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T20:05:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T20:10:42", - "type" : "TempBasal", - "amount" : 2.4000000000000004, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T20:00:41", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T20:05:42", - "type" : "TempBasal", - "amount" : 2.9900332225913622, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T19:55:41", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T20:00:41", - "type" : "TempBasal", - "amount" : 1.8, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T19:50:41", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T19:55:41", - "type" : "TempBasal", - "amount" : 2.4000000000000004, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T19:45:41", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T19:50:41", - "type" : "TempBasal", - "amount" : 1.2000000000000002, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T19:40:41", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T19:45:41", - "type" : "TempBasal", - "amount" : 0.90000000000000002, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T19:35:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T19:40:41", - "type" : "TempBasal", - "amount" : 9.6321070234113702, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T19:25:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T19:35:42", - "type" : "TempBasal", - "amount" : 4.2000000000000002, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T19:20:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T19:25:42", - "type" : "TempBasal", - "amount" : 0.60000000000000009, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T19:15:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T19:20:42", - "type" : "TempBasal", - "amount" : 0.90000000000000002, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T19:10:41", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T19:15:42", - "type" : "TempBasal", - "amount" : 0.89700996677740863, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T19:05:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T19:10:41", - "type" : "TempBasal", - "amount" : 0.60200668896321063, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T19:00:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T19:05:42", - "type" : "TempBasal", - "amount" : 0.90000000000000002, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T18:55:41", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T19:00:42", - "type" : "TempBasal", - "amount" : 0.89700996677740863, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T18:50:41", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T18:55:41", - "type" : "TempBasal", - "amount" : 0, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T18:45:41", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T18:50:41", - "type" : "TempBasal", - "amount" : 0, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T18:40:43", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T18:45:41", - "type" : "TempBasal", - "amount" : 0.30201342281879195, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T18:35:41", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T18:40:43", - "type" : "TempBasal", - "amount" : 0.59602649006622521, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T18:30:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T18:35:41", - "type" : "TempBasal", - "amount" : 0.9030100334448159, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T18:25:43", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T18:30:42", - "type" : "TempBasal", - "amount" : 0.9030100334448159, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T18:20:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T18:25:43", - "type" : "TempBasal", - "amount" : 0.89700996677740863, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T18:10:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T18:15:44", - "type" : "TempBasal", - "amount" : 0, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T18:05:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T18:10:42", - "type" : "TempBasal", - "amount" : 0.30000000000000004, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T18:00:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T18:05:42", - "type" : "TempBasal", - "amount" : 16.5, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T17:55:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T18:00:42", - "type" : "TempBasal", - "amount" : 0.90000000000000002, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T17:50:44", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T17:55:42", - "type" : "TempBasal", - "amount" : 0.60402684563758391, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T17:45:44", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T17:50:44", - "type" : "TempBasal", - "amount" : 0.90000000000000002, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T17:40:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T17:45:44", - "type" : "TempBasal", - "amount" : 1.7880794701986755, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T17:35:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T17:40:42", - "type" : "TempBasal", - "amount" : 1.2000000000000002, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T17:30:41", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T17:35:42", - "type" : "TempBasal", - "amount" : 1.7940199335548173, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T17:25:43", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T17:30:41", - "type" : "TempBasal", - "amount" : 1.8120805369127515, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T17:20:44", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T17:25:43", - "type" : "TempBasal", - "amount" : 0.60200668896321063, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T17:15:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T17:20:44", - "type" : "TempBasal", - "amount" : 0.89403973509933776, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T17:10:41", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T17:15:42", - "type" : "TempBasal", - "amount" : 0.89700996677740863, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T17:05:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T17:10:41", - "type" : "TempBasal", - "amount" : 0.60200668896321063, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T17:00:45", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T17:05:42", - "type" : "TempBasal", - "amount" : 46.969696969696969, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T16:50:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T17:00:45", - "type" : "TempBasal", - "amount" : 2.9850746268656714, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T16:45:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T16:50:42", - "type" : "TempBasal", - "amount" : 2.4000000000000004, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T16:40:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T16:45:42", - "type" : "TempBasal", - "amount" : 2.4000000000000004, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T16:35:43", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T16:40:42", - "type" : "TempBasal", - "amount" : 3.0100334448160533, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T16:30:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T16:35:43", - "type" : "TempBasal", - "amount" : 1.7940199335548173, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T16:25:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T16:30:42", - "type" : "TempBasal", - "amount" : 1.8, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T16:20:41", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T16:25:42", - "type" : "TempBasal", - "amount" : 0.59800664451827246, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T16:15:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T16:20:41", - "type" : "TempBasal", - "amount" : 0.9030100334448159, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T16:10:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T16:15:42", - "type" : "TempBasal", - "amount" : 0.90000000000000002, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T16:05:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T16:10:42", - "type" : "TempBasal", - "amount" : 0.60000000000000009, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T16:00:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T16:05:42", - "type" : "TempBasal", - "amount" : 0, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T15:45:42", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T15:50:42", - "type" : "TempBasal", - "amount" : 0, - "unit" : "U/hour" - }, - { - "start_at" : "2016-01-30T15:40:49", - "scheduled" : 0.80000000000000004, - "end_at" : "2016-01-30T15:45:42", - "type" : "TempBasal", - "amount" : 2.4573378839590441, - "unit" : "U/hour" - } -] diff --git a/LoopTests/Fixtures/insulin_math/reservoir_history_with_rewind_and_prime_output.json b/LoopTests/Fixtures/insulin_math/reservoir_history_with_rewind_and_prime_output.json deleted file mode 100644 index ff5dbd2ae9..0000000000 --- a/LoopTests/Fixtures/insulin_math/reservoir_history_with_rewind_and_prime_output.json +++ /dev/null @@ -1,434 +0,0 @@ -[ - { - "amount": 0.075, - "start_at": "2016-01-30T20:30:43", - "description": "Reservoir decreased 0.075U over 4.97min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T20:35:41" - }, - { - "amount": 0.075, - "start_at": "2016-01-30T20:25:42", - "description": "Reservoir decreased 0.075U over 5.02min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T20:30:43" - }, - { - "amount": 0.05, - "start_at": "2016-01-30T20:20:42", - "description": "Reservoir decreased 0.05U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T20:25:42" - }, - { - "amount": 1.2, - "start_at": "2016-01-30T20:15:41", - "description": "Reservoir decreased 1.2U over 5.02min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T20:20:42" - }, - { - "amount": 0.25, - "start_at": "2016-01-30T20:10:42", - "description": "Reservoir decreased 0.25U over 4.98min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T20:15:41" - }, - { - "amount": 0.2, - "start_at": "2016-01-30T20:05:42", - "description": "Reservoir decreased 0.2U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T20:10:42" - }, - { - "amount": 0.25, - "start_at": "2016-01-30T20:00:41", - "description": "Reservoir decreased 0.25U over 5.02min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T20:05:42" - }, - { - "amount": 0.15, - "start_at": "2016-01-30T19:55:41", - "description": "Reservoir decreased 0.15U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T20:00:41" - }, - { - "amount": 0.2, - "start_at": "2016-01-30T19:50:41", - "description": "Reservoir decreased 0.2U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T19:55:41" - }, - { - "amount": 0.1, - "start_at": "2016-01-30T19:45:41", - "description": "Reservoir decreased 0.1U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T19:50:41" - }, - { - "amount": 0.075, - "start_at": "2016-01-30T19:40:41", - "description": "Reservoir decreased 0.075U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T19:45:41" - }, - { - "amount": 0.80, - "start_at": "2016-01-30T19:35:42", - "description": "Reservoir decreased 0.80U over 4.98min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T19:40:41" - }, - { - "amount": 0.7, - "start_at": "2016-01-30T19:25:42", - "description": "Reservoir decreased 0.7U over 10.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T19:35:42" - }, - { - "amount": 0.05, - "start_at": "2016-01-30T19:20:42", - "description": "Reservoir decreased 0.05U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T19:25:42" - }, - { - "amount": 0.075, - "start_at": "2016-01-30T19:15:42", - "description": "Reservoir decreased 0.075U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T19:20:42" - }, - { - "amount": 0.075, - "start_at": "2016-01-30T19:10:41", - "description": "Reservoir decreased 0.075U over 5.02min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T19:15:42" - }, - { - "amount": 0.05, - "start_at": "2016-01-30T19:05:42", - "description": "Reservoir decreased 0.05U over 4.98min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T19:10:41" - }, - { - "amount": 0.075, - "start_at": "2016-01-30T19:00:42", - "description": "Reservoir decreased 0.075U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T19:05:42" - }, - { - "amount": 0.075, - "start_at": "2016-01-30T18:55:41", - "description": "Reservoir decreased 0.075U over 5.02min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T19:00:42" - }, - { - "amount": 0.0, - "start_at": "2016-01-30T18:50:41", - "description": "Reservoir decreased 0.0U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T18:55:41" - }, - { - "amount": 0.0, - "start_at": "2016-01-30T18:45:41", - "description": "Reservoir decreased 0.0U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T18:50:41" - }, - { - "amount": 0.025, - "start_at": "2016-01-30T18:40:43", - "description": "Reservoir decreased 0.025U over 4.97min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T18:45:41" - }, - { - "amount": 0.05, - "start_at": "2016-01-30T18:35:41", - "description": "Reservoir decreased 0.05U over 5.03min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T18:40:43" - }, - { - "amount": 0.075, - "start_at": "2016-01-30T18:30:42", - "description": "Reservoir decreased 0.075U over 4.98min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T18:35:41" - }, - { - "amount": 0.075, - "start_at": "2016-01-30T18:25:43", - "description": "Reservoir decreased 0.075U over 4.98min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T18:30:42" - }, - { - "amount": 0.075, - "start_at": "2016-01-30T18:20:42", - "description": "Reservoir decreased 0.075U over 5.02min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T18:25:43" - }, - { - "amount": 0.0, - "start_at": "2016-01-30T18:10:42", - "description": "Reservoir decreased 0.0U over 5.03min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T18:15:44" - }, - { - "amount": 0.025, - "start_at": "2016-01-30T18:05:42", - "description": "Reservoir decreased 0.025U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T18:10:42" - }, - { - "amount": 1.375, - "start_at": "2016-01-30T18:00:42", - "description": "Reservoir decreased 1.375U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T18:05:42" - }, - { - "amount": 0.075, - "start_at": "2016-01-30T17:55:42", - "description": "Reservoir decreased 0.075U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T18:00:42" - }, - { - "amount": 0.05, - "start_at": "2016-01-30T17:50:44", - "description": "Reservoir decreased 0.05U over 4.97min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T17:55:42" - }, - { - "amount": 0.075, - "start_at": "2016-01-30T17:45:44", - "description": "Reservoir decreased 0.075U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T17:50:44" - }, - { - "amount": 0.15, - "start_at": "2016-01-30T17:40:42", - "description": "Reservoir decreased 0.15U over 5.03min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T17:45:44" - }, - { - "amount": 0.1, - "start_at": "2016-01-30T17:35:42", - "description": "Reservoir decreased 0.1U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T17:40:42" - }, - { - "amount": 0.15, - "start_at": "2016-01-30T17:30:41", - "description": "Reservoir decreased 0.15U over 5.02min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T17:35:42" - }, - { - "amount": 0.15, - "start_at": "2016-01-30T17:25:43", - "description": "Reservoir decreased 0.15U over 4.97min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T17:30:41" - }, - { - "amount": 0.05, - "start_at": "2016-01-30T17:20:44", - "description": "Reservoir decreased 0.05U over 4.98min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T17:25:43" - }, - { - "amount": 0.075, - "start_at": "2016-01-30T17:15:42", - "description": "Reservoir decreased 0.075U over 5.03min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T17:20:44" - }, - { - "amount": 0.075, - "start_at": "2016-01-30T17:10:41", - "description": "Reservoir decreased 0.075U over 5.02min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T17:15:42" - }, - { - "amount": 0.05, - "start_at": "2016-01-30T17:05:42", - "description": "Reservoir decreased 0.05U over 4.98min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T17:10:41" - }, - { - "amount": 3.875, - "start_at": "2016-01-30T17:00:45", - "description": "Reservoir decreased 3.875U over 4.95min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T17:05:42" - }, - { - "amount": 0.5, - "start_at": "2016-01-30T16:50:42", - "description": "Reservoir decreased 0.5U over 10.05min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T17:00:45" - }, - { - "amount": 0.2, - "start_at": "2016-01-30T16:45:42", - "description": "Reservoir decreased 0.2U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T16:50:42" - }, - { - "amount": 0.2, - "start_at": "2016-01-30T16:40:42", - "description": "Reservoir decreased 0.2U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T16:45:42" - }, - { - "amount": 0.25, - "start_at": "2016-01-30T16:35:43", - "description": "Reservoir decreased 0.25U over 4.98min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T16:40:42" - }, - { - "amount": 0.15, - "start_at": "2016-01-30T16:30:42", - "description": "Reservoir decreased 0.15U over 5.02min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T16:35:43" - }, - { - "amount": 0.15, - "start_at": "2016-01-30T16:25:42", - "description": "Reservoir decreased 0.15U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T16:30:42" - }, - { - "amount": 0.05, - "start_at": "2016-01-30T16:20:41", - "description": "Reservoir decreased 0.05U over 5.02min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T16:25:42" - }, - { - "amount": 0.075, - "start_at": "2016-01-30T16:15:42", - "description": "Reservoir decreased 0.075U over 4.98min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T16:20:41" - }, - { - "amount": 0.075, - "start_at": "2016-01-30T16:10:42", - "description": "Reservoir decreased 0.075U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T16:15:42" - }, - { - "amount": 0.05, - "start_at": "2016-01-30T16:05:42", - "description": "Reservoir decreased 0.05U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T16:10:42" - }, - { - "amount": 0.0, - "start_at": "2016-01-30T16:00:42", - "description": "Reservoir decreased 0.0U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T16:05:42" - }, - { - "amount": 0.0, - "start_at": "2016-01-30T15:45:42", - "description": "Reservoir decreased 0.0U over 5.00min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T15:50:42" - }, - { - "amount": 0.2, - "start_at": "2016-01-30T15:40:49", - "description": "Reservoir decreased 0.2U over 4.88min", - "type": "TempBasal", - "unit": "U", - "end_at": "2016-01-30T15:45:42" - } -] diff --git a/LoopTests/Fixtures/insulin_math/suspend_dose.json b/LoopTests/Fixtures/insulin_math/suspend_dose.json deleted file mode 100644 index 513f3c68bf..0000000000 --- a/LoopTests/Fixtures/insulin_math/suspend_dose.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - { - "type": "PumpSuspend", - "start_at": "2015-07-13T12:00:00", - "end_at": "2015-07-13T12:00:00", - "amount": 0.0, - "unit": "U" - } -] diff --git a/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled.json b/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled.json deleted file mode 100644 index 513f3c68bf..0000000000 --- a/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - { - "type": "PumpSuspend", - "start_at": "2015-07-13T12:00:00", - "end_at": "2015-07-13T12:00:00", - "amount": 0.0, - "unit": "U" - } -] diff --git a/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled_normalized.json b/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled_normalized.json deleted file mode 100644 index c33e93c88c..0000000000 --- a/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled_normalized.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - { - "type": "TempBasal", - "start_at": "2015-07-13T12:00:00", - "end_at": "2015-07-13T12:00:00", - "amount": 0, - "unit": "U/hour" - } -] diff --git a/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled_normalized_iob.json b/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled_normalized_iob.json deleted file mode 100644 index 2b0f383a44..0000000000 --- a/LoopTests/Fixtures/insulin_math/suspend_dose_reconciled_normalized_iob.json +++ /dev/null @@ -1 +0,0 @@ -[{"unit":"U","date":"2015-07-13T12:00:00","value":0},{"unit":"U","date":"2015-07-13T12:05:00","value":0},{"unit":"U","date":"2015-07-13T12:10:00","value":0},{"unit":"U","date":"2015-07-13T12:15:00","value":0},{"unit":"U","date":"2015-07-13T12:20:00","value":0},{"unit":"U","date":"2015-07-13T12:25:00","value":0},{"unit":"U","date":"2015-07-13T12:30:00","value":0},{"unit":"U","date":"2015-07-13T12:35:00","value":0},{"unit":"U","date":"2015-07-13T12:40:00","value":0},{"unit":"U","date":"2015-07-13T12:45:00","value":0},{"unit":"U","date":"2015-07-13T12:50:00","value":0},{"unit":"U","date":"2015-07-13T12:55:00","value":0},{"unit":"U","date":"2015-07-13T13:00:00","value":0},{"unit":"U","date":"2015-07-13T13:05:00","value":0},{"unit":"U","date":"2015-07-13T13:10:00","value":0},{"unit":"U","date":"2015-07-13T13:15:00","value":0},{"unit":"U","date":"2015-07-13T13:20:00","value":0},{"unit":"U","date":"2015-07-13T13:25:00","value":0},{"unit":"U","date":"2015-07-13T13:30:00","value":0},{"unit":"U","date":"2015-07-13T13:35:00","value":0},{"unit":"U","date":"2015-07-13T13:40:00","value":0},{"unit":"U","date":"2015-07-13T13:45:00","value":0},{"unit":"U","date":"2015-07-13T13:50:00","value":0},{"unit":"U","date":"2015-07-13T13:55:00","value":0},{"unit":"U","date":"2015-07-13T14:00:00","value":0},{"unit":"U","date":"2015-07-13T14:05:00","value":0},{"unit":"U","date":"2015-07-13T14:10:00","value":0},{"unit":"U","date":"2015-07-13T14:15:00","value":0},{"unit":"U","date":"2015-07-13T14:20:00","value":0},{"unit":"U","date":"2015-07-13T14:25:00","value":0},{"unit":"U","date":"2015-07-13T14:30:00","value":0},{"unit":"U","date":"2015-07-13T14:35:00","value":0},{"unit":"U","date":"2015-07-13T14:40:00","value":0},{"unit":"U","date":"2015-07-13T14:45:00","value":0},{"unit":"U","date":"2015-07-13T14:50:00","value":0},{"unit":"U","date":"2015-07-13T14:55:00","value":0},{"unit":"U","date":"2015-07-13T15:00:00","value":0},{"unit":"U","date":"2015-07-13T15:05:00","value":0},{"unit":"U","date":"2015-07-13T15:10:00","value":0},{"unit":"U","date":"2015-07-13T15:15:00","value":0},{"unit":"U","date":"2015-07-13T15:20:00","value":0},{"unit":"U","date":"2015-07-13T15:25:00","value":0},{"unit":"U","date":"2015-07-13T15:30:00","value":0},{"unit":"U","date":"2015-07-13T15:35:00","value":0},{"unit":"U","date":"2015-07-13T15:40:00","value":0},{"unit":"U","date":"2015-07-13T15:45:00","value":0},{"unit":"U","date":"2015-07-13T15:50:00","value":0},{"unit":"U","date":"2015-07-13T15:55:00","value":0},{"unit":"U","date":"2015-07-13T16:00:00","value":0},{"unit":"U","date":"2015-07-13T16:05:00","value":0},{"unit":"U","date":"2015-07-13T16:10:00","value":0}] diff --git a/LoopTests/Models/DoseEntryInsulinMathTests.swift b/LoopTests/Models/DoseEntryInsulinMathTests.swift deleted file mode 100644 index fb72b9cb7b..0000000000 --- a/LoopTests/Models/DoseEntryInsulinMathTests.swift +++ /dev/null @@ -1,510 +0,0 @@ -// -// DoseEntryInsulinMathTests.swift -// LoopTests -// -// Copyright © 2026 LoopKit Authors. All rights reserved. -// - -import XCTest -import LoopAlgorithm -@testable import LoopKit -@testable import Loop - -fileprivate struct NewReservoirValue: ReservoirValue { - let startDate: Date - let unitVolume: Double -} - -fileprivate extension DoseUnit { - var unit: LoopUnit { - switch self { - case .units: - return .internationalUnit - case .unitsPerHour: - return .internationalUnitsPerHour - } - } -} - -fileprivate extension DoseEntry { - - func basalRelativeDose(with insulinModel: LoopKit.InsulinModel) -> BasalRelativeDose { - var basalRelativeDoseType: BasalRelativeDoseType - switch type { - case .bolus: - basalRelativeDoseType = .bolus - case .basal: - basalRelativeDoseType = .basal(scheduledRate: value) - default: - if let scheduledRate = scheduledBasalRate?.doubleValue(for: .internationalUnitsPerHour) { - basalRelativeDoseType = .basal(scheduledRate: scheduledRate) - } else { - basalRelativeDoseType = .basal(scheduledRate: 0) // Seems odd, but this is existing behavior - } - } - - return BasalRelativeDose( - type: basalRelativeDoseType, - startDate: startDate, - endDate: endDate, - volume: deliveredUnits ?? programmedUnits, - insulinModel: AlgorithmInsulinModel(insulinModel) - ) - } -} - -fileprivate extension BasalRelativeDose { - var netBasalUnitsPerHour: Double { - if duration > 0 { - return netBasalUnits / duration.hours - } else { - return 0 - } - } - - public var unitsPerHour: Double { - return volume / duration.hours - } -} - -class DoseEntryInsulinMathTests: XCTestCase { - - private var fixtureDateformatter: DateFormatter! - - private let fixtureTimeZone = TimeZone(secondsFromGMT: 0)! - - let exponentialModel = ExponentialInsulinModel(actionDuration: TimeInterval(minutes: 360), peakActivityTime: TimeInterval(minutes: 75)) - - let standardInsulinModel = ExponentialInsulinModelPreset.rapidActingAdult.model - - let walshModel = WalshInsulinModel(actionDuration: TimeInterval(hours: 4)) - - private func fixtureDate(_ input: String) -> Date { - return fixtureDateformatter.date(from: input)! - } - - override func setUp() { - fixtureDateformatter = DateFormatter.descriptionFormatter - fixtureDateformatter.timeZone = fixtureTimeZone - } - - private func loadFixture(_ resourceName: String) -> T { - let path = Bundle(for: type(of: self)).path(forResource: resourceName, ofType: "json")! - return try! JSONSerialization.jsonObject(with: Data(contentsOf: URL(fileURLWithPath: path)), options: []) as! T - } - - private func printInsulinValues(_ insulinValues: [LoopAlgorithm.InsulinValue]) { - print("\n\n") - print(String(data: try! JSONSerialization.data( - withJSONObject: insulinValues.map({ (value) -> [String: Any] in - return [ - "date": ISO8601DateFormatter.localTimeDate(timeZone: fixtureTimeZone).string(from: value.startDate), - "value": value.value, - "unit": "U" - ] - }), - options: [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]), encoding: .utf8)!) - print("\n\n") - } - - private func loadReservoirFixture(_ resourceName: String) -> [NewReservoirValue] { - - let fixture: [JSONDictionary] = loadFixture(resourceName) - let dateFormatter = ISO8601DateFormatter.localTimeDate(timeZone: fixtureTimeZone) - - return fixture.map { - return NewReservoirValue(startDate: dateFormatter.date(from: $0["date"] as! String)!, unitVolume: $0["amount"] as! Double) - } - } - - func loadDoseFixture(_ resourceName: String, insulinType: InsulinType? = .novolog) -> [DoseEntry] { - let fixture: [JSONDictionary] = loadFixture(resourceName) - let dateFormatter = ISO8601DateFormatter.localTimeDate(timeZone: fixtureTimeZone) - - return fixture.compactMap { - guard let unit = DoseUnit(rawValue: $0["unit"] as! String), - let pumpType = PumpEventType(rawValue: $0["type"] as! String), - let type = DoseType(pumpEventType: pumpType) - else { - return nil - } - - let decisionId = $0["decisionId"] as? String - - var dose = DoseEntry( - type: type, - startDate: dateFormatter.date(from: $0["start_at"] as! String)!, - endDate: dateFormatter.date(from: $0["end_at"] as! String)!, - value: $0["amount"] as! Double, - unit: unit, - decisionId: UUID(uuidString: decisionId ?? ""), - deliveredUnits: $0["delivered"] as? Double, - description: $0["description"] as? String, - syncIdentifier: $0["raw"] as? String, - insulinType: insulinType, - automatic: $0["automatic"] as? Bool, - manuallyEntered: $0["manuallyEntered"] as? Bool ?? false, - isMutable: $0["isMutable"] as? Bool ?? false - ) - - if let scheduled = $0["scheduled"] as? Double { - dose.scheduledBasalRate = LoopQuantity(unit: unit.unit, doubleValue: scheduled) - } - - return dose - } - } - - func loadInsulinValueFixture(_ resourceName: String) -> [InsulinValue] { - let fixture: [JSONDictionary] = loadFixture(resourceName) - let dateFormatter = ISO8601DateFormatter.localTimeDate(timeZone: fixtureTimeZone) - - return fixture.map { - return InsulinValue(startDate: dateFormatter.date(from: $0["date"] as! String)!, value: $0["value"] as! Double) - } - } - - func loadBasalRateScheduleFixture(_ resourceName: String) -> BasalRateSchedule { - let fixture: [JSONDictionary] = loadFixture(resourceName) - - let items = fixture.map { - return RepeatingScheduleValue(startTime: TimeInterval(minutes: $0["minutes"] as! Double), value: $0["rate"] as! Double) - } - - return BasalRateSchedule(dailyItems: items, timeZone: fixtureTimeZone)! - } - - func testIOBFromSuspend() { - let input = loadDoseFixture("suspend_dose") - let reconciledOutput = loadDoseFixture("suspend_dose_reconciled") - let normalizedOutput = loadDoseFixture("suspend_dose_reconciled_normalized") - let iobOutput = loadInsulinValueFixture("suspend_dose_reconciled_normalized_iob") - let basals = loadBasalRateScheduleFixture("basal") - - let reconciled = input.reconciled() - - XCTAssertEqual(reconciledOutput.count, reconciled.count) - - for (expected, calculated) in zip(reconciledOutput, reconciled) { - XCTAssertEqual(expected.startDate, calculated.startDate) - XCTAssertEqual(expected.endDate, calculated.endDate) - XCTAssertEqual(expected.value, calculated.value) - XCTAssertEqual(expected.unit, calculated.unit) - } - - let basalTimeline = basals.between(start: input.first!.startDate, end: input.first!.startDate).algorithmSchedule() - - let reconciledWithModel = reconciled.map { $0.toMockInsulinDose(with: walshModel) } - - let normalized = reconciledWithModel.annotated(with: basalTimeline) - - XCTAssertEqual(normalizedOutput.count, normalized.count) - - for (expected, calculated) in zip(normalizedOutput, normalized) { - XCTAssertEqual(expected.startDate, calculated.startDate) - XCTAssertEqual(expected.endDate, calculated.endDate) - XCTAssertEqual(expected.value, calculated.netBasalUnitsPerHour, accuracy: Double(Float.ulpOfOne)) - } - - let iob = normalized.insulinOnBoardTimeline(longestEffectDuration: walshModel.effectDuration) - - XCTAssertEqual(iobOutput.count, iob.count) - - for (expected, calculated) in zip(iobOutput, iob) { - XCTAssertEqual(expected.startDate, calculated.startDate) - XCTAssertEqual(expected.value, calculated.value, accuracy: Double(Float.ulpOfOne)) - } - } - - func testIOBFromDoses() { - let input = loadDoseFixture("normalized_doses", insulinType: .novolog) - let output = loadInsulinValueFixture("iob_from_doses_output") - - let basalRelativeInput = input.map { $0.basalRelativeDose(with: walshModel) } - - measure { - _ = basalRelativeInput.insulinOnBoardTimeline(longestEffectDuration: walshModel.effectDuration) - } - - let iob = basalRelativeInput.insulinOnBoardTimeline(longestEffectDuration: walshModel.effectDuration) - - XCTAssertEqual(output.count, iob.count) - - for (expected, calculated) in zip(output, iob) { - XCTAssertEqual(expected.startDate, calculated.startDate) - XCTAssertEqual(expected.value, calculated.value, accuracy: 0.5) - } - } - - func testIOBFromNoDoses() { - let input: [DoseEntry] = [] - - let basalRelativeInput = input.map { $0.basalRelativeDose(with: standardInsulinModel) } - - let iob = basalRelativeInput.insulinOnBoardTimeline(longestEffectDuration: standardInsulinModel.effectDuration) - - XCTAssertEqual(0, iob.count) - } - - func testIOBFromDosesExponential() { - let input = loadDoseFixture("normalized_doses", insulinType: .novolog) - let output = loadInsulinValueFixture("iob_from_doses_exponential_output") - - let basalRelativeInput = input.map { $0.basalRelativeDose(with: exponentialModel) } - - measure { - _ = basalRelativeInput.insulinOnBoardTimeline(longestEffectDuration: exponentialModel.effectDuration) - } - - let iob = basalRelativeInput.insulinOnBoardTimeline() - - XCTAssertEqual(output.count, iob.count) - - for (expected, calculated) in zip(output, iob) { - XCTAssertEqual(expected.startDate, calculated.startDate) - XCTAssertEqual(expected.value, calculated.value, accuracy: 0.5) - } - } - - func testIOBFromDosesWithDifferentInsulinCurves() { - let formatter = DateFormatter.descriptionFormatter - let f = { (input) in - return formatter.date(from: input)! - } - let output = loadInsulinValueFixture("iob_from_multiple_curves_output") - - let doses = [ - DoseEntry(type: .basal, startDate: f("2018-05-15 14:42:36 +0000"), endDate: f("2018-05-16 14:42:36 +0000"), value: 0.84999999999999998, unit: .unitsPerHour, decisionId: nil, syncIdentifier: "7b02646a070f120e2200", scheduledBasalRate: nil), - DoseEntry(type: .bolus, startDate: f("2018-05-15 14:44:46 +0000"), endDate: f("2018-05-15 14:44:46 +0000"), value: 0.9, unit: .units, decisionId: nil, syncIdentifier: "01004a004a006d006e22354312", scheduledBasalRate: nil), - DoseEntry(type: .tempBasal, startDate: f("2018-05-15 14:42:36 +0000"), endDate: f("2018-05-15 14:42:36 +0000"), value: 0.0, unit: .unitsPerHour, decisionId: nil, syncIdentifier: "1600646a074f12", scheduledBasalRate: nil), - DoseEntry(type: .tempBasal, startDate: f("2018-05-15 14:32:51 +0000"), endDate: f("2018-05-15 15:02:51 +0000"), value: 1.8999999999999999, unit: .unitsPerHour, decisionId: nil, syncIdentifier: "16017360074f12", scheduledBasalRate: nil), - DoseEntry(type: .bolus, startDate: f("2018-05-15 14:52:51 +0000"), endDate: f("2018-05-15 15:52:51 +0000"), value: 0.9, unit: .units, decisionId: nil, syncIdentifier: "01004a004a006d006e22354312", scheduledBasalRate: nil), - ] - - let basalRelativeInput = doses.map { $0.basalRelativeDose(with: exponentialModel) } - - let iobWithoutModel = basalRelativeInput.insulinOnBoardTimeline() - - let dosesWithModel = [ - DoseEntry(type: .basal, startDate: f("2018-05-15 14:42:36 +0000"), endDate: f("2018-05-16 14:42:36 +0000"), value: 0.84999999999999998, unit: .unitsPerHour, decisionId: nil, syncIdentifier: "7b02646a070f120e2200", scheduledBasalRate: nil), - DoseEntry(type: .bolus, startDate: f("2018-05-15 14:44:46 +0000"), endDate: f("2018-05-15 14:44:46 +0000"), value: 0.9, unit: .units, decisionId: nil, syncIdentifier: "01004a004a006d006e22354312", scheduledBasalRate: nil, insulinType: .fiasp), - DoseEntry(type: .tempBasal, startDate: f("2018-05-15 14:42:36 +0000"), endDate: f("2018-05-15 14:42:36 +0000"), value: 0.0, unit: .unitsPerHour, decisionId: nil, syncIdentifier: "1600646a074f12", scheduledBasalRate: nil), - DoseEntry(type: .tempBasal, startDate: f("2018-05-15 14:32:51 +0000"), endDate: f("2018-05-15 15:02:51 +0000"), value: 1.8999999999999999, unit: .unitsPerHour, decisionId: nil, syncIdentifier: "16017360074f12", scheduledBasalRate: nil), - DoseEntry(type: .bolus, startDate: f("2018-05-15 14:52:51 +0000"), endDate: f("2018-05-15 15:52:51 +0000"), value: 0.9, unit: .units, decisionId: nil, syncIdentifier: "01004a004a006d006e22354312", scheduledBasalRate: nil, insulinType: .novolog), - ] - - let dosesWithModelRelative = dosesWithModel.map { entry in - let insulinModel: InsulinModel - switch entry.insulinType { - case .fiasp: - insulinModel = ExponentialInsulinModelPreset.fiasp - default: - insulinModel = ExponentialInsulinModelPreset.rapidActingChild - } - return entry.basalRelativeDose(with: insulinModel) } - - let iobWithModel = dosesWithModelRelative.insulinOnBoardTimeline() - - XCTAssertEqual(iobWithoutModel.count, iobWithModel.count) - - for (expected, calculated) in zip(output, iobWithModel) { - XCTAssertEqual(expected.startDate, calculated.startDate) - XCTAssertEqual(expected.value, calculated.value, accuracy: 0.00001) - } - - } - - func testIOBFromReservoirDoses() { - let input = loadDoseFixture("normalized_reservoir_history_output") - let output = loadInsulinValueFixture("iob_from_reservoir_output") - - let basalRelativeInput = input.map { $0.basalRelativeDose(with: walshModel) } - - measure { - _ = basalRelativeInput.insulinOnBoardTimeline(longestEffectDuration: walshModel.effectDuration) - } - - let iob = basalRelativeInput.insulinOnBoardTimeline(longestEffectDuration: walshModel.effectDuration) - - XCTAssertEqual(output.count, iob.count) - - printInsulinValues(iob) - - for (expected, calculated) in zip(output, iob) { - XCTAssertEqual(expected.startDate, calculated.startDate) - XCTAssertEqual(expected.value, calculated.value, accuracy: 0.01) - } - } - - func testNormalizeReservoirDoses() { - let input = loadDoseFixture("reservoir_history_with_rewind_and_prime_output") - let output = loadDoseFixture("normalized_reservoir_history_output") - let basals = loadBasalRateScheduleFixture("basal") - - let basalTimeline = basals.between(start: input.last!.startDate, end: input.first!.endDate).algorithmSchedule() - - let inputWithModel = input.map { $0.toMockInsulinDose(with: ExponentialInsulinModelPreset.rapidActingAdult.model) } - - - measure { - _ = inputWithModel.annotated(with: basalTimeline) - } - - let doses = inputWithModel.annotated(with: basalTimeline) - - XCTAssertEqual(output.count, doses.count) - - // Total delivery on split doses should add up to delivery from original doses - XCTAssertEqual( - input.map {$0.unitsInDeliverableIncrements}.reduce(0,+), - doses.map {$0.volume}.reduce(0,+), - accuracy: Double(Float.ulpOfOne)) - - for (expected, calculated) in zip(output, doses) { - XCTAssertEqual(expected.startDate, calculated.startDate) - XCTAssertEqual(expected.endDate, calculated.endDate) - XCTAssertEqual(expected.value, calculated.unitsPerHour, accuracy: Double(Float.ulpOfOne)) - switch calculated.type { - case .basal(let scheduledRate): - XCTAssertEqual(expected.scheduledBasalRate?.doubleValue(for: .internationalUnitsPerHour), scheduledRate) - case .bolus: - XCTFail("Unexpected bolus in dose history") - } - } - } - - func testNormalizeEdgeCaseDoses() { - let input = loadDoseFixture("normalize_edge_case_doses_input") - let output = loadDoseFixture("normalize_edge_case_doses_output") - let basals = loadBasalRateScheduleFixture("basal") - - let basalTimeline = basals.between(start: input.first!.startDate, end: input.last!.endDate).algorithmSchedule() - - let inputWithModel = input.map { $0.toMockInsulinDose(with: ExponentialInsulinModelPreset.rapidActingAdult.model) } - - - measure { - _ = inputWithModel.annotated(with: basalTimeline) - } - - let doses = inputWithModel.annotated(with: basalTimeline) - - XCTAssertEqual(output.count, doses.count) - - for (expected, calculated) in zip(output, doses) { - XCTAssertEqual(expected.startDate, calculated.startDate) - XCTAssertEqual(expected.endDate, calculated.endDate) - XCTAssertEqual(expected.value, expected.unit == .units ? calculated.netBasalUnits : calculated.netBasalUnitsPerHour, accuracy: 0.01) - } - } - - func testNormalizeEdgeCaseDosesMutable() { - let input = loadDoseFixture("normalize_edge_case_doses_mutable_input") - let output = loadDoseFixture("normalize_edge_case_doses_mutable_output") - let basals = loadBasalRateScheduleFixture("basal") - - let basalTimeline = basals.between(start: input.first!.startDate, end: input.last!.endDate).algorithmSchedule() - - let inputWithModel = input.map { $0.toMockInsulinDose(with: ExponentialInsulinModelPreset.rapidActingAdult.model) } - - measure { - _ = inputWithModel.annotated(with: basalTimeline) - } - - let doses = inputWithModel.annotated(with: basalTimeline) - - XCTAssertEqual(output.count, doses.count) - - for (expected, calculated) in zip(output, doses) { - XCTAssertEqual(expected.startDate, calculated.startDate) - XCTAssertEqual(expected.endDate, calculated.endDate) - XCTAssertEqual(expected.value, expected.unit == .units ? calculated.netBasalUnits : calculated.netBasalUnitsPerHour, accuracy: 0.01) - } - } - - func testDoseEntryAnnotateShouldSplitDosesProportionally() { - let startDate = self.fixtureDate("2018-07-16 11:59:00 +0000") - let endDate = startDate.addingTimeInterval(TimeInterval(minutes: 5)) - - let tempBasalRate = 1.0 - - let dose = DoseEntry( - type: .tempBasal, - startDate: startDate, - endDate: endDate, - value: tempBasalRate, - unit: .unitsPerHour, - decisionId: nil, - deliveredUnits: 0.1 - ) - - let delivery = dose.unitsInDeliverableIncrements - - let basals = loadBasalRateScheduleFixture("basal").between(start: startDate, end: endDate).algorithmSchedule() - - let doseWithModel = dose.toMockInsulinDose(with: ExponentialInsulinModelPreset.rapidActingAdult.model) - - let splitDoses = [doseWithModel].annotated(with: basals) - - XCTAssertEqual(2, splitDoses.count) - - // A 5 minute dose starting one minute before midnight, split at midnight, means split should be 1/5, 4/5 - XCTAssertEqual(delivery * 1.0/5.0, splitDoses[0].volume, accuracy: .ulpOfOne) - XCTAssertEqual(delivery * 4.0/5.0, splitDoses[1].volume, accuracy: .ulpOfOne) - } - - func testDoseEntryWithoutDeliveredUnitsShouldSplitDosesProportionally() { - let startDate = self.fixtureDate("2018-07-16 11:59:00 +0000") - let endDate = startDate.addingTimeInterval(TimeInterval(minutes: 5)) - - let tempBasalRate = 1.0 - - let dose = DoseEntry( - type: .tempBasal, - startDate: startDate, - endDate: endDate, - value: tempBasalRate, - unit: .unitsPerHour, - decisionId: nil, - deliveredUnits: 0.05 - ) - - let delivery = dose.unitsInDeliverableIncrements - - let basals = loadBasalRateScheduleFixture("basal").between(start: startDate, end: endDate).algorithmSchedule() - - let doseWithModel = dose.toMockInsulinDose(with: ExponentialInsulinModelPreset.rapidActingAdult.model) - - let splitDoses = [doseWithModel].annotated(with: basals) - - XCTAssertEqual(2, splitDoses.count) - - // A 5 minute dose starting one minute before midnight, split at midnight, means split should be 1/5, 4/5 - XCTAssertEqual(delivery * 1.0/5.0, splitDoses[0].volume, accuracy: .ulpOfOne) - XCTAssertEqual(delivery * 4.0/5.0, splitDoses[1].volume, accuracy: .ulpOfOne) - } -} - -fileprivate struct MockInsulinDose: InsulinDose { - var deliveryType: InsulinDeliveryType - - var startDate: Date - - var endDate: Date - - var volume: Double - - var insulinModel: LoopAlgorithm.InsulinModel -} - -fileprivate extension DoseEntry { - func toMockInsulinDose(with insulinModel: LoopKit.InsulinModel) -> MockInsulinDose { - return MockInsulinDose( - deliveryType: type == .bolus ? .bolus : .basal, - startDate: startDate, - endDate: endDate, - volume: deliveredUnits ?? programmedUnits, - insulinModel: AlgorithmInsulinModel(insulinModel) - ) - } -}