JohanDegraeve / xdripswift

xdrip for iOS, written in Swift
GNU General Public License v3.0
328 stars 324 forks source link

Enhancement - Show (and maintain) calibrated slope and intercept values #150

Open ville-juhani-honka opened 3 years ago

ville-juhani-honka commented 3 years ago

Business case: Many of us are experienced CGM users and can see quite soon after starting a new sensor that what is the quality of the values it is giving back - whether they are +2 or -1 (mmol/l) or something else vs finger pricks. Making a calibration of course mitigates the possible bad raw values (if you get the calibration done correctly). In order to validate a calibration, it would be great to see what the calibration did. Also, sometimes (e.g. when a sensor is getting older) it would be beneficial to make small adjustments to the calibration manually, without taking a gamble that if a completely new value is entered, it will mess slope and intercept values completely.

Enhancement request 1: After a calibration has been done, have a possibility to see the calibrated slope and intercept values. Maybe even a graph. This would be beneficial for both fixed and non-fixed slopes. EDIT: I just found out that there had been #117 which basically resolves this first request - I can see the calibrations in Nightscout - great!

Enhancement request 2: Make it possible to enter and/or adjust slope and intercept values manually (this of course would need to have a big disclaimer, so that only those persons, who know what these values (and adjusting them) mean, would do this adjustment).

ville-juhani-honka commented 3 years ago

Background for the request: For us, the time has shown that 9 sensors out of 10 require calibration of slope ~1,10x and intercept ~+1,0 (mmol/l) to work very accurately. To make a calibration just by one or two finger pricks is always a bit of a gamble to get it correct, but we'd rather insert these manually and then check, if it still needs to be adjusted/calibrated.

We use MM2 & FSL2.

JohanDegraeve commented 3 years ago

The calibration algorithm in xdrip4iOS is ported from xdrip-exerpimental back in 2016 - the same is used in Spike.

This is a rather complex algorithm and I wonder why. It doesn't seem to be a simple linear regression. Calculating a linear regression is just a few lines of code. I'm thinking of rewriting it or let the user choose between the existing algorithm and a simple linear regression (which is about 10 lines of code)

There was an interesting note made by Andrei0105 here https://github.com/SpikeApp/Spike/issues/145#issuecomment-648148672 where he says that the Spike algorithm (which is equal to the xDrip4iOS algorithm) gives diffent results than a linear regression.

I'm saying this because I wonder if I would do as you request, will it then also behave as you expect ?

ville-juhani-honka commented 3 years ago

@JohanDegraeve , that's interesting to hear. I do understand that using a simple linear regression can be a simplification, but I find it actually more problematic when the whole concept of "calibration" is kind of a black box; it's very dependent on the successful and correctly-timed value entry (possible errors in timing, finger prick, sensor filament accuracy etc.) and even if one would be able to do it well (multiple times!), still the impact is not that clear. I.e. "Ok, I just made a calibration, how does it impact to the returned/shown values?"

So, long story short: yes, I'd like to try a simple (maybe even manual) linear regression as a way of calibration. With an option to see also the calibrated slope and intercept values in the app.

I would even be happy to try out a completely manual entry of the calibration values slope and intercept.

All this would then be based on an assumption that the raw values returned by FSL sensors' are linear (or at least close to it). We've seen that the FSL reader/app tends to show lows as too low and highs as too high, but I assume that this is coming from the built-in calibration algorithm. This makes of course sense from Abbott's security-concerned point of view, but not, when one wants to see accurate values (and who wouldn't).

ville-juhani-honka commented 3 years ago

I found interesting discussion in Gitter from 2018. This below is exactly, how I would like to do the calibration (manually). At the moment, there are too many variables in my opinion and entering a new BG calibration value makes it possible that the calibration graph gets screwed. Then you are kind of back to square one.

@JohanDegraeve , what kind of a work would it be to have just a simple manual entries for slope and intercept values? This would of course (to keep things simple) override the calibration based on possible earlier BG entries and make also non-fixed slope disabled/impossible to select.

"With a newly setted sensor i always started with slope 1.0 intercept 0.0 and observe (after warm up time 18-24hrs). Then if necessary i adjust only intercept and observe again. But if the normal values are good but higher values are too low, i begin to raise the slope 1.1 ... 1.2 and so on." https://gitter.im/jamorham/xDrip-plus?at=5b6e0a1ea3a93d242249920e

JohanDegraeve commented 3 years ago

A new calibrator to be created, similar to "nocalibrator" but then one that takes a slope and an intercept as parameter. In the NoCalibrator class you see that bgreadingValue = rawvalue, this one is used if you use oop web, because in that case, rawvalue is already calibrated by the oop web service Additional in the settings add option to set intercept and slope and also option to use that new calibrator, those settings are stored in userdefaults, when are read in the Calibrator class. Then in RootViewController, if user has selected to do so, select this new calibrator.

Alternative , specicifically for transmitters that support oop webservice : add a field in the bluetooth screen (where you enable oop web), add two fields in there, set the slope and intercept calculated by the oop webservice, editable fields so that you can override them.

ville-juhani-honka commented 3 years ago

Thanks. I'm not a programmer as such, but due to my profession in IT I can read (and probably even understand) some code and do "copy-paste" and "trial-and-horror" type of coding. I will investigate those classes and their logic more closely.

Based on what you said, I would think:

I would leave the oop webservice option out from this; if I understood it correctly, in this approach the app would always call oop service, even if it would then ignore the returned rawvalue. I want/need the app to work without internet connection, so I don't like it calling oop.

JohanDegraeve commented 3 years ago

I would propose not to change the home screen. Extend the settings, I propose to add it in the developer section, if I have time later I may reshuffle a bit. You would need three settings

then indeed a new calibrator class which uses those settings (only slope and intercept would be used by the new calibrator).

In RootviewController there's a function "private func getCalibrator(cgmTransmitter: CGMTransmitter) " You would just need to add a check in there, if the new setting is enabled , then use the new calibrator class

To avoid that calibrations happen, here you see that if oop web is enabled, an alert is shown. You could extend this with the new setting. Text shown would also need adaptation https://github.com/JohanDegraeve/xdripswift/blob/d2e3b9b340026726b437aa78c2043b7fd00a4729/xdrip/View%20Controllers/Root%20View%20Controller/RootViewController.swift#L19

The last thing is that you need to detect that user changes the setting (disable or enable) When this happens the calibrator needs to be changed

I propose you add an observer in the rootviewcontroller for this new settings for example https://github.com/JohanDegraeve/xdripswift/blob/d2e3b9b340026726b437aa78c2043b7fd00a4729/xdrip/View%20Controllers/Root%20View%20Controller/RootViewController.swift#L325

Add a similar line for your new userdefaults key, then here https://github.com/JohanDegraeve/xdripswift/blob/d2e3b9b340026726b437aa78c2043b7fd00a4729/xdrip/View%20Controllers/Root%20View%20Controller/RootViewController.swift#L792 you add also your new userdefaults key, a new case and then do

calibrator = YourNewCalibratorClass()

Ideally you sould check if it goes from true to false or false to true, (enabled or disabled), but start with assuming it goes to true. when going to false you should call getCalibrator(cgmTransmitter: CGMTransmitter)

that should do it

ville-juhani-honka commented 3 years ago

Excellent - I really appreciate your comments.

Few questions still:

I assume that all the calculations are done with mg/dl units? Only if the setting in the app is to show mmol/l, the values are converted before they are shown to the user. Obviously, the rawbg divider (1000) needs to be used for pure rawbg values, when doing the calculations.

Also, one oddity that I've noticed: when I look from Nightscout reports and "Calibrations", I see that the calibration graph is kind of inverted: higher/bigger calibrated intercept figures return lower BG readings (and vice versa). I.e. raw value is on Y axis and calculated bg value on the X. This of course has an impact to the slope as well. Is this just a quirk in Nightscout's way of showing and handling the calibrations, and doesn't need to be taken into account on the app side?

I.e. if in Xdrip4iOS we'd have a following scenario, it should do the following

The "manualCalibrator" would do the following:

  1. Rawbg would be divided by 1000: 120000/1000=120, this is the uncalibrated rawbg value as mg/dl
  2. Calibrated/calculated actual bg value would be calculated: calculatedBG=1,10*120+18 = 150
  3. Calibrator would return an actual bg value of 150, which would then be stored as a newest bg reading, shown in the UI, sent to NS etc. etc.
JohanDegraeve commented 3 years ago

indeed all values are in mgdl, only when showing them they are converted to mmol But you need to convert to mgdl when you receive a value for intercept in the UI There are a few methods to convert in the two ways (i mean text to double and vice versa) and also quite some examples . You can search for example here https://github.com/JohanDegraeve/xdripswift/blob/d2e3b9b340026726b437aa78c2043b7fd00a4729/xdrip/View%20Controllers/SettingsNavigationController/SettingsViewController/SettingsViewModels/SettingsViewHomeScreenSettingsViewModel.swift for urgentHighMarkValue In the userdefaults I have actually two parameters for this kind. Simple copy what's done for urgentHighMarkValue, but then off course with a new name and put it in the DevelopmentSettingsViewModel

I don't know about the calibrations, upload to NS is done here https://github.com/JohanDegraeve/xdripswift/blob/d2e3b9b340026726b437aa78c2043b7fd00a4729/xdrip/Managers/NightScout/NightScoutUploadManager.swift#L243 (sorry no time to look in detail now)

1., 2., 3. yes correct

JohanDegraeve commented 3 years ago

Please use the development branch