Libre OOP Investigation - UPetersen/LibreMonitor GitHub Wiki
The following paragraphs describe an investigation into temperature dependence of the Freestyle Libre data.
SwiftLibreOOPWeb by @dabear allows to feed the original Abbott algorithm with real data from a sensor and then returns the correct glucose value(s). But SwiftLibreOOPWeb can do more since it can also be fed with artificial data to investigate how the Abbott algorithm works. This is used for the following analyses. SwiftLibreOOPWeb uses @tzachi-dar’s LibreOOPAlgorithm running on an Android phone.
SwiftLibreOOPWeb needs the following data as input:
- FRAM data of the Freestyle Libre sensor, i.e. header, body and footer bytes, as described in this wiki, and
- a state vector containing 32 bytes of data.
SwiftLibreOOPWeb then returns the following data as output:
- The current glucose value with corresponding error flag and minute counter,
- 32 sets of history data for the last eight hours, each with glucose value, error flag and minute counter, and
- a new state vector.
The state vector returned by the OOP algorithm is to be used as input state vector for the next set/reading of FRAM data. This provides some smoothing of otherwise sometimes „jumpy“ data, see here.
In order to investigate how specific features of the original Abbott algorithm work, only one parameter is changed with each test run. To achieve this, the FRAM data is manipulated such that the record of six bytes for one set of glucose data is used for each of the 32 history and each of the 16 trend data sets. (That somewhat resembles placing the sensor in an environment with constant glucose and constant temperature for eight hours and then get a full reading which would return all the same constant values for the last eight hours and the current glucose, i.e. a flat constant line on the display of the reader). That means the body section of the FRAM data is fully artificially constructed. In order to have valid FRAM, the CRC for the body section, which resides in its first two bytes, has to be recalculated so that it matches the rest of the data. Otherwise (wrong crc) LibreOOP will return an error and no useful results.
Contrary to the artificial body section data, the header and footer data are taken from an existing sensor.
The record of six bytes shall be
0x e8 03 c8 d4 5b 00
Thereof being 0xe803 the raw glucose value. Its decimal value can be calculated like this:
0x e8 03 c8 d4 5b 00
\ /
\ / swap mask with 0x3FFF decimal raw glucose
+--------> 0x03e8 -------------------> 0x03e8 -----------> 1000
The remaining four bytes 0xc8d45b00 contain temperature related data and some possible flags and were taken from a reading of a normal sensor.
Furthermore, data for header and footer is needed, which is also taken from some real sensor reading. The complete test input data then is in hex:
2d b7 c8 15 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
11 d6 08 10 -> crc and trend and history index
e8 03 c8 d4 5b 00 -> record of six bytes of one trend glucose reading
e8 03 c8 d4 5b 00 -> dito
e8 03 c8 d4 5b 00 -> ...
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00 -> last record of trend glucose reading
e8 03 c8 d4 5b 00 -> record of six bytes of one history glucose reading
e8 03 c8 d4 5b 00 -> dito
e8 03 c8 d4 5b 00 -> ...
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00
e8 03 c8 d4 5b 00 -> last record of history glucose reading
d9 11 00 00 -> minute counter and last two bytes which seem always zero
ad 71 00 00 da 03 a3 50 14 07 96 80 5a 00 ed a6 0a 81 1a e9 04 ae 2c 70
ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00
This is the default value for an input state vector according to SwiftLibreOOPWeb.
The response for the above data is
currentBg: 90 FullAlgoResults: {
"currenTrend":0,"currentBg":90.0,"currentTime":4568,
"historicBg":[
{"bg":90.0,"quality":0,"time":4095},
{"bg":90.0,"quality":0,"time":4110},
{"bg":90.0,"quality":0,"time":4125},
{"bg":90.0,"quality":0,"time":4140},
{"bg":90.0,"quality":0,"time":4155},
{"bg":90.0,"quality":0,"time":4170},
{"bg":90.0,"quality":0,"time":4185},
{"bg":90.0,"quality":0,"time":4200},
{"bg":90.0,"quality":0,"time":4215},
{"bg":90.0,"quality":0,"time":4230},
{"bg":90.0,"quality":0,"time":4245},
{"bg":90.0,"quality":0,"time":4260},
{"bg":90.0,"quality":0,"time":4275},
{"bg":90.0,"quality":0,"time":4290},
{"bg":90.0,"quality":0,"time":4305},
{"bg":90.0,"quality":0,"time":4320},
{"bg":90.0,"quality":0,"time":4335},
{"bg":90.0,"quality":0,"time":4350},
{"bg":90.0,"quality":0,"time":4365},
{"bg":90.0,"quality":0,"time":4380},
{"bg":90.0,"quality":0,"time":4395},
{"bg":90.0,"quality":0,"time":4410},
{"bg":90.0,"quality":0,"time":4425},
{"bg":90.0,"quality":0,"time":4440},
{"bg":90.0,"quality":0,"time":4455},
{"bg":90.0,"quality":0,"time":4470},
{"bg":90.0,"quality":0,"time":4485},
{"bg":90.0,"quality":0,"time":4500},
{"bg":90.0,"quality":0,"time":4515},
{"bg":90.0,"quality":0,"time":4530},
{"bg":90.0,"quality":0,"time":4545},
{"bg":90.0,"quality":0,"time":4560}
]}
and the state vector
d8 11 00 00 00 00 00 00 d8 b0 74 26 bd 82 56 40
d8 11 00 00 00 00 00 00 9a 35 f2 9f 19 5d 31 bd
Obviously, LibreOOP returns the same glucose value of 90 (results are always rounded to integer numbers), the same quality value of zero (this rather seems to be an error flag, which is zero for a "good" response) for the current glucose value and all 32 history glucose values. Time is the value of the minute counter of the respective glucose value.
LibreOOP does not return trend glucose values but obviously uses the trend (input) data to calculate a current glucose value. By providing a "flat" input of the same raw data over time, the response for the current glucose is flat, too, as expected. So this method neglects any possible extrapolation for e.g. rising trend data. Such effects could be studied in a separate investigation. It also neglects any effect of the state input vector, since the default input vector is always used for this study.
The first investigation is on whether the glucose values of the LibreOOP algorithm behave linear with respect to changes of the raw glucose values when the remaining four bytes remain the same, i.e. for the same constant temperature and same constant other flag values. The raw values from 700 to 5200 are used as input with the same remaining four bytes 0xC8D45B00 (as in above example) in each run. The table and the graph below show the input and the corresponding glucose values returned by LibreOOP.
| raw glucose | therefrom constructed record of six bytes | glucose value returned by LibreOOP [mg/dl] |
|---|---|---|
| 300 | 2C01C8D45B00 | 39 |
| 400 | 9001C8D45B00 | 39 |
| 500 | F401C8D45B00 | 39 |
| 600 | 5802C8D45B00 | 46 |
| 700 | BC02C8D45B00 | 58 |
| 800 | 2003C8D45B00 | 69 |
| 900 | 8403C8D45B00 | 80 |
| 1000 | E803C8D45B00 | 92 |
| 1100 | 4C04C8D45B00 | 103 |
| 1200 | B004C8D45B00 | 114 |
| 1300 | 1405C8D45B00 | 126 |
| 1400 | 7805C8D45B00 | 137 |
| 1500 | DC05C8D45B00 | 148 |
| 1600 | 4006C8D45B00 | 160 |
| 1700 | A406C8D45B00 | 171 |
| 1800 | 0807C8D45B00 | 182 |
| 1900 | 6C07C8D45B00 | 194 |
| 2000 | D007C8D45B00 | 205 |
| 2200 | 9808C8D45B00 | 228 |
| 2400 | 6009C8D45B00 | 250 |
| 2600 | 280AC8D45B00 | 273 |
| 2800 | F00AC8D45B00 | 296 |
| 3000 | B80BC8D45B00 | 318 |
| 3200 | 800CC8D45B00 | 341 |
| 3400 | 480DC8D45B00 | 364 |
| 3600 | 100EC8D45B00 | 386 |
| 3800 | D80EC8D45B00 | 409 |
| 4000 | A00FC8D45B00 | 432 |
| 4200 | 6810C8D45B00 | 454 |
| 4400 | 3011C8D45B00 | 477 |
| 4600 | F811C8D45B00 | 500 |
| 4800 | C012C8D45B00 | 501 |
| 5000 | 8813C8D45B00 | 501 |
| 5200 | 5014C8D45B00 | 501 |
Figure: LibreOOP glucose value for different raw glucose values but otherwise constant data (i.e. the same constant four remaining bytes are used for each trend and each history record)
OOP returns values between 39 and 501 mgl/dl. The response is linear from 40 to 500 mg/dl. The values 39 and 501 mg/dl denote the lower and upper threshold, so 39 mg/dl is the infamous „LO“ and 501 mg/dl probably denotes „HI“. (I never experienced such high values myself, so this is a guess). For the linear range the relationship can be calculated using the formula
glucose = slope * raw_glucose + offset
where glucose denotes the glucose returned by LibreOOP, raw_glucose denotes the raw glucose input and is without dimension. Slope and offset can be calculated from two data sets [(raw_glucose1; glucose1); (raw_glucose2; glucose_2)] as
slope = (glucose2 - glucose1) / (raw_glucose2 - raw_glucose1)
and
offset = glucose2 - slope * raw_glucose2
which in this case, using [(700; 58);(3000; 318)], yields
slope = (318 mg/dl - 58 mg/dl) / (3000 - 700) = 0.113 mg/dl
and
offset = 318 mg/dl - 0.113 mg/dl * 3000 = -21.1 mg/dl
and finally the equation for calculation fo glucose from raw glucose is
glucose = 0.113 mg/dl * raw_glucose -21.1 mgl/dl.
Up to this point it can only be stated that this result holds for 0xC8D45B00 as the remaining four bytes being identical for each input record. Does it also hold for variations of the remaining four bytes, i.e. different temperatures?
Carrying out some experiments with varying temperatures and looking at the six bytes of the trend data records, one can see, that bytes three and four change significantly with changing temperature. Furthermore it is noticeable, that the changes in byte three are quicker than those in byte four, which is an indication for swapped byes, as already seen with the raw glucose value. A closer look also reveals that the fourth byte should be masked to exclude its first two bits, similar to the masking of raw glucose, in order to get useful results. Thus one can calculate a „raw temperature" from byte three and byte four in the same manner as raw glucose from byte zero and byte one.
An example for calculating raw temperature from a six bytes record of raw data (the one already used above) looks like this:
0x e8 03 c8 d4 5b 00
\ /
\ / swap mask with 0x3FFF decimal raw temperature
+--------> 0x5bd4 ------------------> 0x1bd4 --------> 7124
Now one could continue and try to derive real temperature values in degrees Centigrade or Fahrenheit from this raw temperature. There are also various ways discussed to do this, especially in Pierre Vandevenne's blog, see e.g. here. But for the following investigation only raw temperature will be used.
In order to be sure to get valid data for the remaining four bytes, a set of real trend data records is used, where my upper arm with the freestyle libre sensor was hold under warm water from a shower. Four records were chosen. The first record corresponds to room temperature of approximately 25 degrees Celsius (this is a guess since I did not measure room temperature) and the last value to the highest temperature, having applied very warm water under the shower for some minutes (again, no exact temperature measurement). The other two are taken from in between. So omitting the first two bytes of raw glucose the set of data is
0xC8149900, 0xC800D800, 0xC8B89600, 0xC89C5800
Additionally, the last four bytes of the well known example record from above are used, too, so that the complete set for the temperature investigation is
0xC8D45B00, 0xC8149900, 0xC800D800, 0xC8B89600, 0xC89C5800
Example: The table below shows complete six byte records built from the above set of four remaining bytes for a raw glucose value of 700 (0xBC02) and the corresponding raw temperature values:
| Record of six bytes of raw data | Raw temperature in decimal calculated from byte three and four |
|---|---|
| 0x BC 02 C8 D4 5B 00 | 7124 |
| 0x BC 02 C8 14 99 00 | 6420 |
| 0x BC 02 C8 9C 58 00 | 6300 |
| 0x BC 02 C8 00 D8 00 | 6144 |
| 0x BC 02 C8 B8 96 00 | 5816 |
In a first step the LibreOOP glucose values are calculated for raw glucose values of 700, 1000, 1500, 2000, 2500, and 3000 (a subset of what was used above) and this for all of the five different temperatures. The results are given in the table and picture below.
| Raw glucose | Glucose for raw temperature 7124 [mg/dl] | Glucose for raw temperature 6420 [mg/dl] | Glucose for raw temperature 6300 [mg/dl] | Glucose for raw temperature 6144 [mg/dl] | Glucose for raw temperature 5816 [mg/dl] |
|---|---|---|---|---|---|
| 700 | 58 | 50 | 49 | 47 | 44 |
| 1000 | 92 | 81 | 79 | 77 | 72 |
| 1500 | 148 | 132 | 129 | 126 | 118 |
| 2000 | 205 | 183 | 180 | 175 | 165 |
| 2500 | 262 | 235 | 230 | 224 | 211 |
| 3000 | 318 | 286 | 280 | 273 | 257 |

As can be seen, the relationship is linear for each temperature set, but the slopes are different. So, unfortunately, there is no general constant slope for a Freestyle Libre sensor that could be just easily used independently of temperature. With this linear relationship it is possible to calculate slope and offset individually for each set, just as it was done in the above section. This yields
| Raw temperature | 7124 | 6420 | 6300 | 6144 | 5816 |
|---|---|---|---|---|---|
| Slope [mg/dl] | 0,1130 | 0,1026 | 0,1004 | 0,0983 | 0,0926 |
| Offset [mg/dl] | -21,1304 | -21,8261 | -21,3043 | -21,7826 | -20,8261 |
Contrary to the slopes, the offsets are pretty much in the same range of ca. 21 mg/dl.
The „transposed“ question is, whether the glucose values are linear with respect to raw temperature. The following picture shows the corresponding graph, where the glucose values are printed over raw temperature.

The results look linear, but focusing on just the results for raw glucose = 700 one gets the following graph.

The points are not exactly on a straight line due to the fact, that the LibreOOP algorithm only returns integer numbers without any fractional digits. But as can be seen the linearity assumption is a good approximation.
To sum up: We have
- a linear glucose response by the LibreOOP algorithm with respect to raw glucose variations, when raw temperature is kept constant, and
- a linear glucose response by the LibreOOP algorithm with respect to raw temperature variations, when raw glucose is kept constant.
Overall we are interested in an algorithm that allows to calculate glucose from one or more records of six bytes. With the above simplification of only equal and constant values, this is reduced to finding an algorithm that uses raw glucose and raw temperature of only one record of six bytes. Since there are two linear relationships we can try the approach to interpolate the slope and offset over raw temperature and thus get the individual slope and offset for each raw temperature. Therefore, the following graph shows the slope and offset from the above table with respect to raw temperature.

The slope is obviously linear with respect to raw temperature. Although the offset does not look really linear, the deviations between the offset values are within +- 1 mg/dl and can be considered minor and a linear approach is for offset is possible, too.
So in order to include raw temperature into an algorithm, slope and offset are linearly interpolated with respect to raw temperature, which then allows for glucose calculation using raw temperature and raw glucose from
glucose = slope(raw_temperature) * raw_glucose + offset(raw_temperature)
Where slope(raw_temperature) and offset(raw_temperature) themselves are derived from the linear approximation
slope(raw_temperature) = slopeslope * raw_temperature + offsetslope
offset(raw_temperature) = slopeoffset * raw_temperature + offsetoffset.
One could argue whether it would be easier to just use a constant offset, but the more general approach is used here.
Slopeslope and offsetslope are calculated from two sets of data [(raw_temperature1; slope1); (raw_temperature2; slope2)] (using libre office), e.g. [(5816; 0.0926); (7124; 0.113)] as
slopeslope = (slope2 - slope1) / (raw_temperature2 - raw_temperature1)
slopeslope = (0.113 - 0.0926) / (7124 - 5816) = 0.000015623
and
offsetslope = slope2 - slopeslope * raw_temperature2
offsetslope = 0.113 - 0.000015623 * 7214 = 0.0017457
and one finally gets
slope(raw_temperature) = 0,000015623 * raw_temperature + 0.0017457.
Slopeoffset and offsetoffset are are calculated similarly from two sets of data [(raw_temperature1; offset1); (raw_temperature2; offset2)] (using libre office), e.g. [(5816; -20.8261); (7124; -21.1304)] as
slopeoffset = -0.0002327 offsetoffset = -19.47
and finally
offset(raw_temperature) = -0.0002327 * raw_temperature -19.47.
Using this equation one gets slope and offset for the raw temperatures from above as listed in the following table:
| Raw temperature | 7124 | 6420 | 6300 | 6144 | 5816 |
|---|---|---|---|---|---|
| Interpolated slope [mg/dl] | 0.1130 | 0.1020 | 0.1002 | 0.0977 | 0.0926 |
| Interpolated offset [mg/dl] | -21.1304 | -21.9666 | -20.9387 | -20.9024 | -20.8261 |
In order to find out how good the approximation is, these slopes and offsets where used to calculate a glucose value from raw glucose and (and thus also from raw temperature) and listed in the following table. The glucose values where rounded and differences shown in parentheses, for cases where the results were not identical to the LibreOOP glucose value.
| Raw glucose | Calculated glucose for raw temperature 7124 [mg/dl] | Calculated glucose for raw temperature 6420 [mg/dl] | Calculated glucose for raw temperature 6300 [mg/dl] | Calculated glucose for raw temperature 6144 [mg/dl] | Calculated glucose for raw temperature 5816 [mg/dl] |
|---|---|---|---|---|---|
| 700 | 58 | 50 | 49 | 48 (+1) | 44 |
| 1000 | 92 | 81 | 79 | 77 | 72 |
| 1500 | 148 | 132 | 129 | 126 | 118 |
| 2000 | 205 | 183 | 179 (-1) | 175 | 164 (-1) |
| 2500 | 261 (-1) | 234 (-1) | 229 (-1) | 223 (-1) | 211 |
| 3000 | 318 | 285 (-1) | 280 | 272 (-1) | 257 |
The largest deviation is 1 mg/dl which accounts for eight of 30 values, all other values are the same, which is a very good approximation for the libreOOP glucose values.
One might be tempted to use the equations derived in this section as an algorithm to calculate glucose from raw glucose and raw temperature. In general this could be done, but the parameters were only calculated for the header and footer data of one single sensor. Thus it is not yet clear, if these parameters are also valid for any other sensor.
To understand the effect of using a different sensor on the parameters for the derived equations, the same tests as above where made with data from six other different sensors. To be more precise: their header and footer data was used and the body data was tweaked as described in the above sections. From these overal seven sensors, the two sensors with the most extreme results (highest glucose value responses from LibreOOP algorithm and lowest glucose value responses from LibreOOP algorithm) where chosen and the corresponding data is displayed below. To reduce the amount of data only a subset of three raw glucose values (1000, 2000 and 3000) and three raw temperatures (7124, 6300, and 5816) was used.
The header bytes are
A4 1F 98 16 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
and the footer bytes are
8C EF 00 01 B6 05 04 51, 14 07 96 80 5A 00 ED A6 12 7D 1A C8 04 C0 69 7A
The glucose values returned by LibreOOP are
| Raw glucose | Glucose for raw temperature 7124 [mg/dl] | Glucose for raw temperature 6300 [mg/dl] | Glucose for raw temperature 5816 [mg/dl] |
|---|---|---|---|
| 1000 | 78 | 67 | 60 |
| 2000 | 177 | 155 | 141 |
| 3000 | 277 | 243 | 223 |
Slope and offset for each raw temperature if using the equation
glucose = slope * raw_glucose + offset
are
| Raw temperature | 7124 | 6300 | 5816 |
|---|---|---|---|
| Slope [mg/dl] | 0.0995 | 0.0882 | 0.0815 |
| Offset [mg/dl] | -21.50 | -21.50 | -21.50 |
The equations to calculate glucose from raw glucose and raw temperature are
glucose = slope(raw_temperature) * raw_glucose + offset(raw_temperature)
where
slope(raw_temperature) = 0,00001376 * raw_temperature + 0.0014633 and
offset(raw_temperature) = 0 * raw_temperature -21.50.
The header bytes are
50 7A 88 13 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
and the footer bytes are
6C 0C 00 01 D3 04 7D 51 14 07 96 80 5A 00 ED A6 14 76 1A C8 04 E4 39 6C
The glucose values returned by LibreOOP are
| Raw glucose | Glucose for raw temperature 7124 [mg/dl] | Glucose for raw temperature 6300 [mg/dl] | Glucose for raw temperature 5816 [mg/dl] |
|---|---|---|---|
| 1000 | 101 | 87 | 79 |
| 2000 | 226 | 198 | 181 |
| 3000 | 351 | 309 | 284 |
Slope and offset for each raw temperature if using the equation
glucose = slope * raw_glucose + offset
are
| Raw temperature | 7124 | 6300 | 5816 |
|---|---|---|---|
| Slope [mg/dl] | 0.1252 | 0.1110 | 0.1026 |
| Offset [mg/dl] | -24.65 | -24.13 | -23.83 |
The equations to calculate glucose from raw glucose and raw temperature are
glucose = slope(raw_temperature) * raw_glucose + offset(raw_temperature)
where
slope(raw_temperature) = 0.00001729 * raw_temperature + 0.002080 and
offset(raw_temperature) = -0,0006316 * raw_temperature -20.15.
The difference of the glucose values is given in the table below, in absolute values and percentages.
| Raw glucose | Glucose for raw temperature 7124 [mg/dl (%)] | Glucose for raw temperature 6300 [mg/dl (%)] | Glucose for raw temperature 5816 [mg/dl (%)] |
|---|---|---|---|
| 1000 | 23 (29%) | 20 (30%) | 19 (32%) |
| 2000 | 49 (28%) | 43 (28%) | 40 (28%) |
| 3000 | 74 (27%) | 66 (27%) | 61 (27%) |
The difference is in the range of 30%. Conclusion is that each sensor has its individual parameters to calculate glucose from raw glucose and raw temperature and this must be taken into account for any such approach. Since only seven sensors where considered for this analysis, the difference might be much higher for any other sensor.
Unfortunately, I have no clue, how header and footer data could be used to compensate for this effect. Any ideas on this are welcome.
Note, that although these results look very good, they are made and thus are only valid under/for the following assumptions
- Constant glucose values and no dynamic changes of any kind.
- Temperature is in a valid range. The borders of this range have yet to be determined.
- The equation parameters are valid for an individual sensor (and its individual header and footer data). The parameters are different for any other sensor (with different header and footer data) and the difference is significant.
Further unknowns are:
- Flags are not yet understood. Investigation needed on the other bits and bytes of the six bytes records.
- Header and footer not yet understood. Some quick experiments with tweaking many of the header and footer bytes (just one at a time) showed that LibreOOP glucose response mostly changes dramatically.
Despite these assumptions and unknowns, one can still think about an algorithm based on the results of this investigation. One possibility would be to get the LibreOOP glucose response for four artificial data points (two different raw glucose values and two different raw temperatures) and calculate slope and offset functions as described. Then use another e.g. two data points and their LibreOOP glucose response and compare with algorithm calculated results to validate if everything is fine. This would be valid for the current sensor and would need to be repeated with every new sensor. It would also require internet access and access to SwiftLibreOOPWeb or a similar service.
Everything that is written here are just the results of my personal investigations and they could be totally wrong. There is no collaboration of any kind with Abbott. Use at your own risk.