Repository navigation
Treatment of "on the line" readings (in range vs out of range) #779
Description
Issue
LoopFollow does not use one rule for a reading that sits exactly on the low or high line. Some places treat it as in range, others as low/high. This shows up most in mmol/L, where several mg/dL readings display the same value (for example 70 and 71 mg/dL both display 3.9, and 180 and 181 both display 10.0).
Current behavior on dev
| Surface | Reading exactly on a line (mg/dL) |
|---|---|
| Chart, BG text, contact image, Live Activity colors | low or high (<= low, >= high) |
| Stats pie (low / in range / high) | in range (< low, > high) |
| TIR screen | in range (<, >), legend "70–180" |
| GRI | in range, fixed 54 / 70 / 180 / 250 cutoffs |
| Alarms (High / Low BG) | triggered (>= high, <= low) |
I had done some work similar to #762, now sitting in draft in #777, and brought this up with Jonas.
mmol/L makes it worse
The app compares raw mg/dL values, but in mmol/L the user sees the value rounded to one decimal. Several different mg/dL readings therefore show the same number:
| mg/dL | mmol/L shown |
|---|---|
| 69 | 3.8 |
| 70 | 3.9 |
| 71 | 3.9 |
| 72 | 4.0 |
| 179 | 9.9 |
| 180 | 10.0 |
| 181 | 10.0 |
| 182 | 10.1 |
With the high line at 180 mg/dL (10.0 mmol/L), a user in mmol/L can see this:
- a reading of 180 mg/dL shows "10.0" and is treated as in range (or as high, depending on the surface),
- a reading of 181 mg/dL also shows "10.0" but is above 180, so it is treated as high.
Two readings that show exactly the same number on screen get different colors and are counted in different bands in the stats. From the user's point of view, 10.0 is both "in range" and "high".
Custom lines add a second source of error: the picker converted mmol/L to mg/dL with a hardcoded factor of 18, while the display uses 18.01559. A custom 7.8 mmol/L high is stored as 140.4 mg/dL, so a reading of 140 (displayed 7.8) is below the line even though it shows the same number.
#777 suggests a move to the more specific value (18.01559) rather than the rounded 18.
Reference
At first glance, the international consensus on time in range (Battelino et al., Diabetes Care, 2019) defines in range as 70–180 mg/dL (3.9–10.0 mmol/L), with below range under 70 (3.9) and above range over 180 (10.0). Under that definition a reading on 70 or 180 is in range. I went down the rabbit hole of readhing the GRI paper; it is built on the same cutoffs.
Options
- Inclusive everywhere (on a line is in range), at display precision. Matches the consensus and the TIR screen. 3.9 and 10.0 display as in range in mmol/L.
- Exclusive for status (colors, Live Activity), inclusive for statistics (pie, TIR, GRI). Consistent with Trio, but inconsistent within the app.
- Exclusive everywhere (on a line is low/high). One rule, but TIR and GRI no longer match the consensus or other tools' numbers at the boundary.
Implications for mmol/L users: compare at display precision
Whatever rule is chosen, the comparison should be made on the number the user sees, not on the underlying mg/dL value:
- Round the reading to what is displayed (one decimal in mmol/L, whole number in mg/dL).
- Round the low and high line the same way.
- Compare the two rounded numbers.
Result: any readings that show the same number get the same result (same color, same low / in range / high bucket in the stats). Example with the high line at 10.0 mmol/L:
| mg/dL reading | Shown | Result under "on a line is in range" | Result under "on a line is low/high" |
|---|---|---|---|
| 179 | 9.9 | in range | in range |
| 180 | 10.0 | in range | high |
| 181 | 10.0 | in range | high |
| 182 | 10.1 | high | high |
180 and 181 always land in the same band, whichever rule is chosen. Today they do not.
One consequence to be aware of: the result depends on the display unit. 181 mg/dL is high when the user is in mg/dL, but in range (it displays 10.0) when the user is in mmol/L under the inclusive rule. I think this is correct because the classification should match the number shown, but it is a deliberate trade-off.
Alarms (open question)
Alarm thresholds are triggers rather than ranges, and a Low alarm that fires at <= its threshold fires earlier than one that fires at <. Even if the range becomes inclusive, I would keep alarms as they are (>= high, <= low) and only compare them at display precision. This means a reading displayed 10.0 is in range but would trigger a High alarm set at 10.0. I wouldn't want to introduce a safety regression, albeit a very small one.
Related
- Use the selected range mode for Live Activity colors #762 (states the current rule for colors)
- Fix range and mmol/L colors in chart and Live Activity #777 (implements option 1 plus display-precision comparison; open as a draft, stacked on Use the selected range mode for Live Activity colors #762)
Activity