Skip to content

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

Merged
merged 59 commits into from
Feb 20, 2023

Conversation

novalegra
Copy link
Collaborator

@novalegra novalegra commented Nov 8, 2022

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 each time, we define the unexpected BG deviation as the sum of the carb effects and insulin counteraction effects from time...now (this is summation is exactly how the CarbAbsorptionViewController computes its deviations).

We then compute 2 thresholds:

  1. the total deviation (carb effect) we would expect to observe from a 15 g CHO meal with medium absorption that started at time
  2. the effect associated with a 2 mg/dl/min increase over 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

  1. Add a new MealDetectionManager to detect missed meals using the data available in Loop.
  2. Add missed meal notification generation call to the loop function in LoopDataManager (since it's the main driver of the Loop algorithm updates, I'm open to putting it elsewhere if there's a better place)
  3. Add switch in Alert Permissions to enable/disable missed meal notifications
  4. Update the watch carb/bolus flow to allow it to be initialized with a carb entry quantity and time
  5. Add tests for the changes made

Papers Consulted

  • Harvey, R.A., et al., Design of the Glucose Rate Increase Detector: A Meal Detection Module for the Health Monitoring System. J Diabetes Sci Technol, 2014. 8(2): p. 307-320.
  • Cameron, F., G. Niemeyer, and B.A. Buckingham, Probabilistic evolving meal detection and estimation of meal total glucose appearance. J Diabetes Sci Technol, 2009. 3(5): p. 1022-30.
  • Weimer, J., et al., Physiology-Invariant Meal Detection for Type 1 Diabetes. Diabetes Technol Ther, 2016. 18(10): p. 616-624.

# 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
Copy link
Collaborator

@ps2 ps2 left a 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 {
Copy link
Collaborator

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?

Copy link
Collaborator Author

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
@ps2
Copy link
Collaborator

ps2 commented Feb 19, 2023

@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.

@ps2
Copy link
Collaborator

ps2 commented Feb 20, 2023

Once the conflicts with dev are fixed, I'll merge this in!

@novalegra
Copy link
Collaborator Author

@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.

That sounds good to me! I've merged in dev and moved up the switch

@ps2 ps2 merged commit 64996c1 into LoopKit:dev Feb 20, 2023
@ps2
Copy link
Collaborator

ps2 commented Feb 20, 2023

Thanks @novalegra!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Status: Done
Development

Successfully merging this pull request may close these issues.

2 participants