-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Add missed meal notifications to Loop #1825
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
# Conflicts: # Loop.xcodeproj/project.pbxproj
# Conflicts: # Loop.xcodeproj/project.pbxproj # Loop/Managers/LoopDataManager.swift # Loop/View Controllers/StatusTableViewController.swift # Loop/View Models/SettingsViewModel.swift # Loop/Views/NotificationsCriticalAlertPermissionsView.swift # Loop/Views/SettingsView.swift # LoopTests/Managers/LoopDataManagerTests.swift
# Conflicts: # Loop/View Controllers/CarbEntryViewController.swift
# Conflicts: # Loop.xcodeproj/project.pbxproj
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there are any blockers here, but I did make several comments as I went through it. Thanks for all your work on this so far! I think we're close to merging it in!
/// Use the higher of the 2 thresholds to ensure noisy CGM data doesn't cause false-positives for more recent times | ||
let effectThreshold = max(deviationChangeThreshold, modeledMealEffectThreshold) | ||
|
||
if unexpectedDeviation >= effectThreshold { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I think I understand how the time detection is working. As you go farther back in time, deviationChangeThreshold
grows, as it's based on glucoseRiseThreshold * minutesAgo
, When unexpectedDeviation
falls below this, that's what marks the start of the meal, correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The deviationChangeThreshold
does grow as we go further back in time, but the unexpectedDeviation
has to be higher than the deviationChangeThreshold
to mark the start of the meal.
Part of the reason this rate-based threshold is helpful is that it applies a higher required unexpectedDeviation
(and this 'burden of proof') for meals that are further in the past, making it harder for a bunch of random CGM deviations to accumulate to trigger a meal start.
These changes also remove the requirement that there be no carbs entered after the missed meal was detected - since we're now observing direct absorption, I didn't think this requirement made sense anymore
…wards an earlier meal time instead of a later one
@novalegra I think this is ready for bringing into dev, if you agree. Since it is optional and behind I switch, it shouldn't impact too many people, and we can keep refining it on dev if needed. I was just looking at the enable switch, and think maybe it could be moved up to the "Alert Management" screen, and not be down in permissions, as it's not really a permission option. |
Once the conflicts with dev are fixed, I'll merge this in! |
# Conflicts: # LoopTests/Managers/LoopDataManagerTests.swift
That sounds good to me! I've merged in |
Thanks @novalegra! |
This PR and the associated LoopKit PR add missed meal notifications to Loop.
RPReplay_Final1676573791.mov
Motivation
It's easy to forget to log a meal, and the Loop algorithm works best when it has information about all the carbs and insulin that could affect blood glucose levels. This PR uses the insulin dosing information in Loop to automatically detect when unexpected rise in BG is so large that the only likely explanation is a missed meal, then notify the user so they can log the meal and administer insulin if needed.
Algorithm Design
The algorithm was inspired by several different papers on missed meal detection, especially Harvey et al., Cameron et al., and Weimer, J., et al. However, this algorithm different in that it's designed to take advantage of the insulin and carb data that Loop has access to, and to be conservative in its detection to avoid false-positive notifications (which are frustrating to users).
The algorithm searches the closed interval of
now - 15 mins...now - 2 hours
in 5-minute increments to try to identify if any of those times could be the start of a missed meal. At eachtime
, we define the unexpected BG deviation as the sum of the carb effects and insulin counteraction effects fromtime...now
(this is summation is exactly how theCarbAbsorptionViewController
computes its deviations).We then compute 2 thresholds:
time
time...now
The unexpected deviation has to be higher than both thresholds to be considered a missed meal. The second threshold is similar to the threshold proposed in Harvey et al. and applies a lower bound to what deviations are considered missed meals, making it so physiologically incorrect settings and CGM noise are less likely to trigger false-positives.
Notification Design
For a missed meal notification to be delivered, at least 95 minutes must have passed since the last missed meal notification
The notification contains metadata about the start time of the missed meal, and when the user taps on it (either on iPhone or Apple Watch), they're taken directly to the carb & bolus flow where the meal start time & carb quantity have been pre-filled.
IMG_2515.2.mov
(note that this video includes a notification title, "Potential Unannounced Meal", from an earlier version - the title would actually be "Potential Missed Meal")
Changes Made
MealDetectionManager
to detect missed meals using the data available in Loop.loop
function inLoopDataManager
(since it's the main driver of the Loop algorithm updates, I'm open to putting it elsewhere if there's a better place)Papers Consulted