Skip to content
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

High predictions: graph and notifications #441

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions app/src/main/java/com/eveningoutpost/dexdrip/Home.java
Original file line number Diff line number Diff line change
Expand Up @@ -2421,6 +2421,28 @@ private void updateCurrentBgInfo(String source) {
BgGraphBuilder.previous_low_occurs_at = BgGraphBuilder.low_occurs_at;
}

if (BgGraphBuilder.high_occurs_at > 0 && !((BgGraphBuilder.low_occurs_at > 0))) {
final double high_predicted_alarm_minutes = Double.parseDouble(Pref.getString("high_predict_alarm_level", "20"));
final double now = JoH.ts();
final double predicted_high_in_mins = (BgGraphBuilder.high_occurs_at - now) / 60000;

if (predicted_high_in_mins > 1) {
lowPredictText.append(getString(R.string.high_predicted) + "\n" + getString(R.string.in) + ": " + (int) predicted_high_in_mins + getString(R.string.space_mins));
if (predicted_high_in_mins < high_predicted_alarm_minutes) {
lowPredictText.setTextColor(Color.RED); // high front getting too close!
} else {
final double previous_predicted_high_in_mins = (BgGraphBuilder.previous_high_occurs_at - now) / 60000;
if ((BgGraphBuilder.previous_high_occurs_at > 0) && ((previous_predicted_high_in_mins + 5) < predicted_high_in_mins)) {
lowPredictText.setTextColor(Color.GREEN); // high front is getting further away
} else {
lowPredictText.setTextColor(Color.YELLOW); // high front is getting nearer!
}
}
lowPredictText.setVisibility(View.VISIBLE);
}
BgGraphBuilder.previous_high_occurs_at = BgGraphBuilder.high_occurs_at;
}

if (navigationDrawerFragment == null) Log.e("Runtime", "navigationdrawerfragment is null");

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,11 @@ public class BgGraphBuilder {
public final static double NOISE_HIGH = 200;
public final static double NOISE_FORGIVE = 100;
public static double low_occurs_at = -1;
public static double high_occurs_at = -1;
public static double previous_low_occurs_at = -1;
public static double previous_high_occurs_at = -1;
private static double low_occurs_at_processed_till_timestamp = -1;
private static double high_occurs_at_processed_till_timestamp = -1;
private static long noise_processed_till_timestamp = -1;
private final static String TAG = "jamorham graph";
//private final static int pluginColor = Color.parseColor("#AA00FFFF"); // temporary
Expand Down Expand Up @@ -1165,6 +1168,7 @@ private synchronized void addBgReadingValues(final boolean simple) {
final boolean interpret_raw = prefs.getBoolean("interpret_raw", false);
final boolean show_filtered = prefs.getBoolean("show_filtered_curve", false) && has_filtered;
final boolean predict_lows = prefs.getBoolean("predict_lows", true);
final boolean predict_highs = prefs.getBoolean("predict_highs", false);
final boolean show_plugin = prefs.getBoolean("plugin_plot_on_graph", false);
final boolean glucose_from_plugin = prefs.getBoolean("display_glucose_from_plugin", false);
final boolean illustrate_backfilled_data = prefs.getBoolean("illustrate_backfilled_data", false);
Expand Down Expand Up @@ -1410,14 +1414,17 @@ private synchronized void addBgReadingValues(final boolean simple) {
Log.e(TAG, "Error creating back trend: " + e.toString());
}

boolean lowPredicted = false;

// low estimator
// work backwards to see whether we think a low is estimated
low_occurs_at = -1;
try {
if ((predict_lows) && (prediction_enabled) && (poly != null)) {
final double offset = ActivityRecognizedService.raise_limit_due_to_vehicle_mode() ? unitized(ActivityRecognizedService.getVehicle_mode_adjust_mgdl()) : 0;
final double plow_now = JoH.ts();
double plow_timestamp = plow_now + (1000 * 60 * 99); // max look-ahead
int low_lookahead_mins = 99;
tolot27 marked this conversation as resolved.
Show resolved Hide resolved
double plow_timestamp = plow_now + (1000 * 60 * low_lookahead_mins); // max look-ahead
double polyPredicty = poly.predict(plow_timestamp);
Log.d(TAG, "Low predictor at max lookahead is: " + JoH.qs(polyPredicty));
low_occurs_at_processed_till_timestamp = highest_bgreading_timestamp; // store that we have processed up to this timestamp
Expand All @@ -1440,13 +1447,46 @@ private synchronized void addBgReadingValues(final boolean simple) {
}
Log.i(TAG, "LOW PREDICTED AT: " + JoH.dateTimeText((long) low_occurs_at));
predictivehours = Math.max(predictivehours, (int) ((low_occurs_at - plow_now) / (60 * 60 * 1000)) + 1);
lowPredicted = true;
}
}

} catch (NullPointerException e) {
tolot27 marked this conversation as resolved.
Show resolved Hide resolved
//Log.d(TAG,"Error with low prediction trend: "+e.toString());
}

// high estimator
// same logic as low estimator above
{
// we shouldn't be able to predict a high and a low at the same time
if (predict_highs && prediction_enabled && (poly != null) && !lowPredicted) {
final double phigh_now = JoH.ts();
int high_lookahead_mins = 30;
double phigh_timestamp = phigh_now + (1000 * 60 * high_lookahead_mins);
double polyPredicty = poly.predict(phigh_timestamp);
Log.d(TAG, "High predictor at max lookahead is: " + JoH.qs(polyPredicty));
high_occurs_at_processed_till_timestamp = highest_bgreading_timestamp;
if (polyPredicty >= highMark) {
high_occurs_at = phigh_timestamp;
final double highMarkIndicator = (highMark - (highMark / 4));
while (phigh_timestamp > phigh_now) {
phigh_timestamp = phigh_timestamp - FUZZER;
polyPredicty = poly.predict(phigh_timestamp);
if (polyPredicty < highMark) {
polyBgValues.add(new PointValue((float)(phigh_timestamp / FUZZER), (float)polyPredicty));
} else {
high_occurs_at = phigh_timestamp;
if (polyPredicty < highMarkIndicator) {
polyBgValues.add(new PointValue((float)(phigh_timestamp / FUZZER), (float)polyPredicty));
}
}
}
Log.i(TAG, "HIGH PREDICTED AT: " + JoH.dateTimeText((long)high_occurs_at));
predictivehours = Math.max(predictivehours, (int) ((high_occurs_at - phigh_now) / (60 * 60 * 1000)) + 1);
}
}
}

final boolean show_noise_working_line;
if (last_noise > NOISE_TRIGGER ||
(last_noise > BgGraphBuilder.NOISE_TRIGGER_ULTRASENSITIVE
Expand Down Expand Up @@ -1820,6 +1860,23 @@ public static synchronized double getCurrentLowOccursAt() {
return low_occurs_at;
}

public static synchronized double getCurrentHighOccursAt() {
try {
final long last_bg_reading_timestamp = BgReading.last().timestamp;
// TODO remove any duplication by using refreshNoiseIfOlderThan()
if (high_occurs_at_processed_till_timestamp < last_bg_reading_timestamp) {
Log.d(TAG, "Recalculating highOccursAt: " + JoH.dateTimeText((long) high_occurs_at_processed_till_timestamp) + " vs " + JoH.dateTimeText(last_bg_reading_timestamp));
// new only the last hour worth of data for this
(new BgGraphBuilder(xdrip.getAppContext(), System.currentTimeMillis() - 60 * 60 * 1000, System.currentTimeMillis() + 5 * 60 * 1000, 24, true)).addBgReadingValues(false);
} else {
Log.d(TAG, "Cached current high timestamp ok: " + JoH.dateTimeText((long) high_occurs_at_processed_till_timestamp) + " vs " + JoH.dateTimeText(last_bg_reading_timestamp));
}
} catch (Exception e) {
Log.e(TAG, "Got exception in getCurrentHighOccursAt() " + e);
}
return high_occurs_at;
}

public static synchronized void refreshNoiseIfOlderThan(long timestamp) {
if (noise_processed_till_timestamp < timestamp) {
Log.d(TAG, "Refreshing Noise as Older: " + JoH.dateTimeText((long) noise_processed_till_timestamp) + " vs " + JoH.dateTimeText(timestamp));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class NotificationChannels {
public static final String BG_MISSED_ALERT_CHANNEL = "bgMissedAlertChannel";
public static final String BG_RISE_DROP_CHANNEL = "bgRiseDropChannel";
public static final String BG_PREDICTED_LOW_CHANNEL = "bgPredictedLowChannel";
public static final String BG_PREDICTED_HIGH_CHANNEL = "bgPredictedHighChannel";
public static final String BG_PERSISTENT_HIGH_CHANNEL = "bgPersistentHighChannel";
public static final String CALIBRATION_CHANNEL = "calibrationChannel";
public static final String ONGOING_CHANNEL = "ongoingChannel";
Expand All @@ -62,6 +63,7 @@ private static synchronized void initialize_name_map() {
map.put(BG_MISSED_ALERT_CHANNEL, xdrip.getAppContext().getString(R.string.missed_reading_alert));
map.put(BG_RISE_DROP_CHANNEL, xdrip.getAppContext().getString(R.string.bg_rising_fast));
map.put(BG_PREDICTED_LOW_CHANNEL, xdrip.getAppContext().getString(R.string.low_predicted));
map.put(BG_PREDICTED_HIGH_CHANNEL, xdrip.getAppContext().getString(R.string.high_predicted));
map.put(BG_PERSISTENT_HIGH_CHANNEL, xdrip.getAppContext().getString(R.string.persistent_high_alert));
map.put(CALIBRATION_CHANNEL, xdrip.getAppContext().getString(R.string.calibration_alerts));
map.put(ONGOING_CHANNEL, "Ongoing Notification");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ public class Notifications extends IntentService {
public static final int parakeetMissingId = 014;
public static final int persistentHighAlertNotificationId = 015;
public static final int ob1SessionRestartNotificationId = 016;
public static final int highPredictAlertNotificationId = 017;
private static boolean low_notifying = false;
private static boolean high_notifying = false;

private static final int CALIBRATION_REQUEST_MAX_FREQUENCY = (60 * 60 * 6); // don't bug for extra calibrations more than every 6 hours
private static final int CALIBRATION_REQUEST_MIN_FREQUENCY = (60 * 60 * 8); // don't bug for general calibrations more than every 8 hours
Expand Down Expand Up @@ -316,6 +318,7 @@ private boolean notificationSetter(Context context) {
return false;
}


boolean unclearReading = BgReading.getAndRaiseUnclearReading(context);

boolean forced_wear = Home.get_forced_wear();
Expand All @@ -333,6 +336,7 @@ private boolean notificationSetter(Context context) {
//if (watchAlert && bg_persistent_high_alert_enabled_watch) {
PersistentHigh.checkForPersistentHigh();
evaluateLowPredictionAlarm();
evaluateHighPredictionAlarm();
reportNoiseChanges();


Expand Down Expand Up @@ -775,7 +779,7 @@ private void evaluateLowPredictionAlarm() {


// force BgGraphBuilder to calculate `low_occurs_at` and `last_noise`
// Workaround trying to resolve race donditions as by design they are static but updated/read asynchronously.
// Workaround trying to resolve race conditions as by design they are static but updated/read asynchronously.

final double low_occurs_at = BgGraphBuilder.getCurrentLowOccursAt();

Expand Down Expand Up @@ -806,6 +810,43 @@ private void evaluateLowPredictionAlarm() {
}
}

private void evaluateHighPredictionAlarm() {

if (!prefs.getBoolean("predict_highs_alarm", false)) return;


// force BgGraphBuilder to calculate `high_occurs_at` and `last_noise`
// Workaround trying to resolve race conditions as by design they are static but updated/read asynchronously.

final double high_occurs_at = BgGraphBuilder.getCurrentHighOccursAt();

if ((high_occurs_at > 0) && (BgGraphBuilder.last_noise < BgGraphBuilder.NOISE_TOO_HIGH_FOR_PREDICT)) {
final double high_predicted_alarm_minutes = Double.parseDouble(prefs.getString("high_predict_alarm_level", "20"));
final double now = JoH.ts();
final double predicted_high_in_mins = (high_occurs_at - now) / 60000;
android.util.Log.d(TAG, "evaluateHighPredictionAlarm: mins: " + predicted_high_in_mins);
if (predicted_high_in_mins > 1) {
if (predicted_high_in_mins < high_predicted_alarm_minutes) {
Notifications.highPredictAlert(xdrip.getAppContext(), true, getString(R.string.high_predicted)
+" "+getString(R.string.in)+" " + (int) predicted_high_in_mins + getString(R.string.space_mins));
high_notifying = true;
} else {
Notifications.highPredictAlert(xdrip.getAppContext(), false, ""); // cancel it
}
} else {
if (high_notifying) {
Notifications.highPredictAlert(xdrip.getAppContext(), false, ""); // cancel it
high_notifying = false;
}
}
} else {
if (high_notifying) {
Notifications.highPredictAlert(xdrip.getAppContext(), false, ""); // cancel it
high_notifying = false;
}
}
}

private void clearAllCalibrationNotifications() {
notificationDismiss(calibrationNotificationId);
notificationDismiss(extraCalibrationNotificationId);
Expand Down Expand Up @@ -932,6 +973,24 @@ public static void lowPredictAlert(Context context, boolean on, String msg) {
}
}

public static void highPredictAlert(Context context, boolean on, String msg) {
final String type = "bg_predict_alert";
if (on) {
if ((Pref.getLong("alerts_disabled_until", 0) < JoH.tsl()) && (Pref.getLong("high_alerts_disabled_until", 0) < JoH.tsl())) {
OtherAlert(context, type, msg, highPredictAlertNotificationId, NotificationChannels.BG_PREDICTED_HIGH_CHANNEL, false, 20 * 60);
if (Pref.getBooleanDefaultFalse("speak_alerts")) {
if (JoH.pratelimit("high-predict-speak", 1800)) SpeechUtil.say(msg, 4000);
}
} else {
Log.ueh(TAG, "Not High predict alerting due to snooze: " + msg);
}
} else {
NotificationManager mNotifyMgr = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotifyMgr.cancel(highPredictAlertNotificationId);
UserNotification.DeleteNotificationByType(type);
}
}

public static void persistentHighAlert(Context context, boolean on, String msg) {
final String type = "persistent_high_alert";
if (on) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1460,6 +1460,7 @@ public boolean onPreferenceChange(Preference preference, Object newValue) {
bindPreferenceSummaryToValue(findPreference("xplus_liver_maximpact"));

bindPreferenceSummaryToValue(findPreference("low_predict_alarm_level"));
bindPreferenceSummaryToValue(findPreference("high_predict_alarm_level"));
Profile.validateTargetRange();
bindPreferenceSummaryToValue(findPreference("plus_target_range"));

Expand Down
10 changes: 10 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@
<string name="space_mins">" mins"</string>
<string name="till">till</string>
<string name="low_predicted">Low predicted</string>
<string name="high_predicted">High predicted</string>
<string name="in">in</string>
<string name="yes_enter_setup_mode">Yes, enter setup mode</string>
<string name="nokeep_parakeet_as_it_is">No, keep Parakeet as it is</string>
Expand Down Expand Up @@ -236,7 +237,10 @@
<string name="persistent_high_alert">Persistent High Alert</string>
<string name="forecasted_low_alert">Forecasted Low Alert</string>
<string name="extrapolate_data_to_try_to_predict_lows">Extrapolate data to try to predict lows</string>
<string name="forecasted_high_alert">Forecasted High Alert</string>
<string name="extrapolate_data_to_try_to_predict_highs">Extrapolate data to try to predict highs</string>
<string name="alarm_at_forecasted_low_mins">Alarm at Forecasted Low mins</string>
<string name="alarm_at_forecasted_high_mins">Alarm at Forecasted High mins</string>
<string name="other_xdrip_plus_alerts">Other xDrip+ alerts</string>
<string name="xdrip_plus_display_settings">xDrip+ Display Settings</string>
<string name="display_customisations">Display customizations</string>
Expand Down Expand Up @@ -431,10 +435,15 @@
<string name="choose_sound_used_for_persistent_high_alarm">Choose sound used for persistent high alarm.</string>
<string name="persistent_high_sound">Persistent High Sound</string>
<string name="forecast_lows">Forecast Lows</string>
<string name="forecast_highs">Forecast Highs</string>
<string name="raise_alarm_on_forecast_low">Raise alarm on Forecast Low</string>
<string name="raise_alarm_on_forecast_high">Raise alarm on Forecast High</string>
<string name="notify_when_predicted_low_time_reaches_threshold">Notify when predicted low time reaches threshold</string>
<string name="notify_when_predicted_high_time_reaches_threshold">Notify when predicted high time reaches threshold</string>
<string name="predicted_low_sound">Predicted Low Sound</string>
<string name="predicted_high_sound">Predicted High Sound</string>
<string name="choose_sound_used_for_predicted_low_alarm">Choose sound used for predicted low alarm.</string>
<string name="choose_sound_used_for_predicted_high_alarm">Choose sound used for predicted high alarm.</string>
<string name="notify_when_parakeet_device_stops_checking_in">Notify when Parakeet device stops checking in</string>
<string name="parakeet_related_alerts">Parakeet related alerts</string>
<string name="raise_parakeet_notification_silently_when_charging">Raise Parakeet notification silently when charging</string>
Expand Down Expand Up @@ -803,6 +812,7 @@
<string name="override_silent_mode_these">Override Silent mode on these alerts</string>
<string name="persistent_repeat_max">Repeating max every (minutes)</string>
<string name="momentum_indicates_low">When momentum trend indicates a Low would be predicted</string>
<string name="momentum_indicates_high">When momentum trend indicates a High would be predicted</string>
<string name="notify_on_low_battery_level">Notify when battery level goes below</string>
<string name="collector_battery_alerts">Collector battery alerts</string>
<string name="low_battery_percentage">Low battery percentage</string>
Expand Down
Loading