Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.camomile.openlibre.model;

public class CalibrationInfo {
public int i1;
public int i2;
public int i3;
public int i4;
public int i5;
public int i6;

public CalibrationInfo(int i1, int i2, int i3, int i4, int i5, int i6) {
this.i1 = i1;
this.i2 = i2;
this.i3 = i3;
this.i4 = i4;
this.i5 = i5;
this.i6 = i6;
}
}
42 changes: 42 additions & 0 deletions app/src/main/java/com/camomile/openlibre/model/GlucoseData.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,29 @@ public class GlucoseData extends RealmObject implements Comparable<GlucoseData>
private int glucoseLevelRaw = -1; // in mg/l = 0.1 mg/dl
private long date;
private int timezoneOffsetInMinutes;
private boolean hasError = false;
private int error = -1;
private int rawTemperature = 0;
private int temperatureAdjustment = 0;

public GlucoseData() {}

public GlucoseData(SensorData sensor, int ageInSensorMinutes, int timezoneOffsetInMinutes,
int glucoseLevelRaw, boolean isTrendData, long date, int rawTemperature,
int temperatureAdjustment, boolean hasError, int error) {
this.sensor = sensor;
this.ageInSensorMinutes = ageInSensorMinutes;
this.timezoneOffsetInMinutes = timezoneOffsetInMinutes;
this.glucoseLevelRaw = glucoseLevelRaw;
this.isTrendData = isTrendData;
this.date = date;
this.temperatureAdjustment = temperatureAdjustment;
this.rawTemperature = rawTemperature;
this.hasError = hasError;
this.error = error;
id = generateId(sensor, ageInSensorMinutes, isTrendData, glucoseLevelRaw);
}

public GlucoseData(SensorData sensor, int ageInSensorMinutes, int timezoneOffsetInMinutes, int glucoseLevelRaw, boolean isTrendData, long date) {
this.sensor = sensor;
this.ageInSensorMinutes = ageInSensorMinutes;
Expand All @@ -40,6 +61,7 @@ public GlucoseData(SensorData sensor, int ageInSensorMinutes, int timezoneOffset
this.date = date;
id = generateId(sensor, ageInSensorMinutes, isTrendData, glucoseLevelRaw);
}

public GlucoseData(SensorData sensor, int ageInSensorMinutes, int timezoneOffsetInMinutes, int glucoseLevelRaw, boolean isTrendData) {
this(sensor, ageInSensorMinutes, timezoneOffsetInMinutes, glucoseLevelRaw, isTrendData, sensor.getStartDate() + TimeUnit.MINUTES.toMillis(ageInSensorMinutes));
}
Expand Down Expand Up @@ -141,6 +163,22 @@ public String getId() {
return id;
}

public boolean hasError() {
return hasError;
}

public int getError() {
return error;
}

public int getRawTemperature() {
return rawTemperature;
}

public int getTemperatureAdjustment() {
return temperatureAdjustment;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down Expand Up @@ -171,6 +209,10 @@ public String toString() {
", glucoseLevelRaw=" + glucoseLevelRaw +
", date=" + date +
", timezoneOffsetInMinutes=" + timezoneOffsetInMinutes +
", rawTemperature=" + rawTemperature +
", temperatureAdjustment=" + temperatureAdjustment +
", hasError=" + hasError +
", error =" + error +
'}';
}
}
55 changes: 55 additions & 0 deletions app/src/main/java/com/camomile/openlibre/model/RawTagData.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.TimeZone;

import io.realm.RealmObject;
import io.realm.annotations.Ignore;
import io.realm.annotations.PrimaryKey;

import static java.lang.Math.max;
Expand All @@ -30,6 +31,8 @@ public class RawTagData extends RealmObject {
private String tagId;
private byte[] data;
private boolean checkForErrorFlags = false;
@Ignore
private CalibrationInfo calibrationInfo = null;

public RawTagData() {}

Expand All @@ -48,6 +51,7 @@ public RawTagData(String tagId, long utc_date, byte[] data) {
this.tagId = tagId;
id = String.format(Locale.US, "%s_%d", tagId, date);
this.data = data.clone();
setCalibrationInfo();
}

int getTrendValue(int index) {
Expand Down Expand Up @@ -130,6 +134,57 @@ public int getTrendFlags(int index) {
return readBits(data, index * tableEntrySize + offsetTrendTable, 0xe, 0xc);
}

//val temperature = readBits(data, offset, 0x1a, 0xc).shl(2)
// var temperatureAdjustment = readBits(data, offset, 0x26, 0x9) shl 2
// val negativeAdjustment = readBits(data, offset, 0x2f, 0x1)
// if (negativeAdjustment != 0) { temperatureAdjustment = -temperatureAdjustment }
// val error = (readBits(data, offset, 0xe, 0xb)).toUInt() and 0x1ff.toUInt()
// val hasError = readBits(data, offset, 0x19, 0x1) != 0

public boolean checkIfErrorData(int index) {
return readBits(data, index * tableEntrySize + offsetTrendTable, 0x19, 0x1) != 0;
}

public int getErrorOffset(int index) {
return readBits(data, index * tableEntrySize + offsetTrendTable, 0xe, 0xb) & 0x1ff;
}

public int getRawTemperature(int index) {
return readBits(data, index * tableEntrySize + offsetTrendTable, 0x1a, 0xc) << 2;
}

public int getTemperatureAdjustment(int index) {
int temperatureAdjustment = readBits(data, index * tableEntrySize + offsetTrendTable, 0x26, 0x9) << 2;
int negativeAdjustment = readBits(data, index * tableEntrySize + offsetTrendTable, 0x2f, 0x1);
if (negativeAdjustment != 0) {
temperatureAdjustment = -temperatureAdjustment;
}
return temperatureAdjustment;
}

public CalibrationInfo getCalibrationInfo() {
return calibrationInfo;
}

private void setCalibrationInfo() {
// let i1 = readBits(data, 2, 0, 3)
// let i2 = readBits(data, 2, 3, 0xa)
// let i3 = readBits(data, 0x150, 0, 8)
// let i4 = readBits(data, 0x150, 8, 0xe)
// let negativei3 = readBits(data, 0x150, 0x21, 1) != 0
// let i5 = readBits(data, 0x150, 0x28, 0xc) << 2
// let i6 = readBits(data, 0x150, 0x34, 0xc) << 2

int i1 = readBits(data, 2, 0, 3);
int i2 = readBits(data, 2, 3, 0xa);
int i3 = readBits(data, 0x150, 0, 8);
int i4 = readBits(data, 0x150, 8, 0xe);
boolean negativei3 = readBits(data, 0x150, 0x21, 1) != 0;
int i5 = readBits(data, 0x150, 0x28, 0xc) << 2;
int i6 = readBits(data, 0x150, 0x34, 0xc) << 2;
this.calibrationInfo = new CalibrationInfo(i1, i2, negativei3 ? -i3 : i3, i4, i5, i6);
}

private int readBits(byte []buffer, int byteOffset,int bitOffset, int bitCount) {
if (bitCount == 0) {
return 0;
Expand Down
64 changes: 52 additions & 12 deletions app/src/main/java/com/camomile/openlibre/model/ReadingData.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,30 +98,40 @@ public ReadingData(RawTagData rawTagData) {

int glucoseLevelRaw = rawTagData.getTrendValue(index);
int flags = rawTagData.getTrendFlags(index);
int rawTemperature = rawTagData.getRawTemperature(index);
int temperatureAdjustment = rawTagData.getTemperatureAdjustment(index);
boolean isErrorData = rawTagData.checkIfErrorData(index);
int errorOffset = rawTagData.getErrorOffset(index);
if (!rawTagData.isCheckForErrorFlags() || flags != errorFlags) {
int dataAgeInMinutes = numTrendValues - counter;
int ageInSensorMinutes = sensorAgeInMinutes - dataAgeInMinutes;
long dataDate = lastReadingDate + (long) (TimeUnit.MINUTES.toMillis(ageInSensorMinutes - lastSensorAgeInMinutes) * timeDriftFactor);
trend.add(new GlucoseData(sensor, ageInSensorMinutes, timezoneOffsetInMinutes, glucoseLevelRaw, true, dataDate));
trend.add(new GlucoseData(sensor, ageInSensorMinutes, timezoneOffsetInMinutes, glucoseLevelRaw,
true, dataDate, rawTemperature, temperatureAdjustment, isErrorData, errorOffset));
} else if (rawTagData.isCheckForErrorFlags()) {
int dataAgeInMinutes = numTrendValues - counter;
int ageInSensorMinutes = sensorAgeInMinutes - dataAgeInMinutes;
long dataDate = lastReadingDate + (long) (TimeUnit.MINUTES.toMillis(ageInSensorMinutes - lastSensorAgeInMinutes) * timeDriftFactor);
errorList.add(new GlucoseData(sensor, ageInSensorMinutes, timezoneOffsetInMinutes, glucoseLevelRaw, true, dataDate));
errorList.add(new GlucoseData(sensor, ageInSensorMinutes, timezoneOffsetInMinutes, glucoseLevelRaw,
true, dataDate, rawTemperature, temperatureAdjustment, isErrorData, errorOffset));
}
}

int indexHistory = rawTagData.getIndexHistory();

ArrayList<Integer> glucoseLevels = new ArrayList<>();
ArrayList<Integer> ageInSensorMinutesList = new ArrayList<>();
ArrayList<GlucoseData> glucoseDataList = new ArrayList<>();

// read history values from ring buffer, starting at indexHistory (bytes 124-315)
for (int counter = 0; counter < numHistoryValues; counter++) {
int index = (indexHistory + counter) % numHistoryValues;

int glucoseLevelRaw = rawTagData.getHistoryValue(index);
int flags = rawTagData.getHistoryFlags(index);
int rawTemperature = rawTagData.getRawTemperature(index);
int temperatureAdjustment = rawTagData.getTemperatureAdjustment(index);
boolean isErrorData = rawTagData.checkIfErrorData(index);
int errorOffset = rawTagData.getErrorOffset(index);
// skip zero values if the sensor has not filled the ring buffer yet completely
// and skip if we have any error flags
if (glucoseLevelRaw > 0 && (!rawTagData.isCheckForErrorFlags() || flags != errorFlags)) {
Expand All @@ -130,7 +140,8 @@ public ReadingData(RawTagData rawTagData) {

// skip the first hour of sensor data as it is faulty
if (ageInSensorMinutes > minSensorAgeInMinutes) {
glucoseLevels.add(glucoseLevelRaw);
long dataDate = lastReadingDate + (long) (TimeUnit.MINUTES.toMillis(ageInSensorMinutes - lastSensorAgeInMinutes) * timeDriftFactor);
glucoseDataList.add(new GlucoseData(sensor, ageInSensorMinutes, timezoneOffsetInMinutes, glucoseLevelRaw, false, dataDate, rawTemperature, temperatureAdjustment, isErrorData, errorOffset));
ageInSensorMinutesList.add(ageInSensorMinutes);
}
} else if (rawTagData.isCheckForErrorFlags() && flags == errorFlags) {
Expand All @@ -149,7 +160,7 @@ public ReadingData(RawTagData rawTagData) {

// try to shift age to make this reading fit to older readings
try {
shiftAgeToMatchPreviousReadings(realmProcessedData, glucoseLevels, ageInSensorMinutesList);
shiftAgeToMatchPreviousReadings(realmProcessedData, makeGlucoseLevelsList(glucoseDataList), ageInSensorMinutesList);
} catch (RuntimeException e) {
Log.e("OpenLibre::ReadingData", e.getMessage() + " For reading with id " + id);
realmProcessedData.close();
Expand All @@ -158,13 +169,12 @@ public ReadingData(RawTagData rawTagData) {


// create history data point list
for (int i = 0; i < glucoseLevels.size(); i++) {
int glucoseLevelRaw = glucoseLevels.get(i);
for (int i = 0; i < glucoseDataList.size(); i++) {
GlucoseData data = glucoseDataList.get(i);
int ageInSensorMinutes = ageInSensorMinutesList.get(i);
long dataDate = lastReadingDate + (long) (TimeUnit.MINUTES.toMillis(ageInSensorMinutes - lastSensorAgeInMinutes) * timeDriftFactor);

GlucoseData glucoseData = makeGlucoseData(realmProcessedData, glucoseLevelRaw, ageInSensorMinutes, dataDate);
if(glucoseData == null) {
GlucoseData glucoseData = makeGlucoseData(realmProcessedData, data, ageInSensorMinutes);
if (glucoseData == null) {
realmProcessedData.close();
return;
}
Expand All @@ -174,7 +184,29 @@ public ReadingData(RawTagData rawTagData) {
realmProcessedData.close();
}

private GlucoseData makeGlucoseData(Realm realmProcessedData, int glucoseLevelRaw, int ageInSensorMinutes, long dataDate) {
private GlucoseData makeGlucoseData(Realm realmProcessedData, GlucoseData glucoseData, int ageInSensorMinutes) {
// if this data point has been read from this sensor before, reuse the object form the database, instead of changing the old data
RealmResults<GlucoseData> previousGlucoseData = realmProcessedData.where(GlucoseData.class)
.equalTo(GlucoseData.ID, GlucoseData.generateId(sensor, sensorAgeInMinutes, false, glucoseData.getGlucoseLevelRaw())).findAll();
// check if a valid previous data point was found
if (!previousGlucoseData.isEmpty()) {
if (previousGlucoseData.first().getGlucoseLevelRaw() == glucoseData.getGlucoseLevelRaw()) {
return previousGlucoseData.first();
}
// if the old value does not equal the new one and the sensor has been running for more than three hours, there is an error in the data
if (ageInSensorMinutes > 3 * minSensorAgeInMinutes) {
Log.e("OpenLibre::ReadingData", "error in glucose level raw:" + previousGlucoseData.first().getGlucoseLevelRaw() + " != " + glucoseData.getGlucoseLevelRaw()
+ " for glucose data with id: " + previousGlucoseData.first().getId());
history.clear();
trend.clear();
return null;
}
}
return glucoseData;
}

private GlucoseData makeGlucoseData(Realm realmProcessedData, int glucoseLevelRaw, int ageInSensorMinutes, long dataDate, int rawTemperature,
int temperatureAdjustment, boolean hasError, int error) {
// if this data point has been read from this sensor before, reuse the object form the database, instead of changing the old data
RealmResults<GlucoseData> previousGlucoseData = realmProcessedData.where(GlucoseData.class)
.equalTo(GlucoseData.ID, GlucoseData.generateId(sensor, ageInSensorMinutes, false, glucoseLevelRaw)).findAll();
Expand All @@ -193,7 +225,15 @@ private GlucoseData makeGlucoseData(Realm realmProcessedData, int glucoseLevelRa
return null;
}
}
return new GlucoseData(sensor, ageInSensorMinutes, timezoneOffsetInMinutes, glucoseLevelRaw, false, dataDate);
return new GlucoseData(sensor, ageInSensorMinutes, timezoneOffsetInMinutes, glucoseLevelRaw, false, dataDate, rawTemperature, temperatureAdjustment, hasError, error);
}

private ArrayList<Integer> makeGlucoseLevelsList(ArrayList<GlucoseData> glucoseDataList) {
ArrayList<Integer> glucoseLevels = new ArrayList<>();
for(int i=0; i< glucoseDataList.size(); i++) {
glucoseLevels.add(glucoseDataList.get(i).getGlucoseLevelRaw());
}
return glucoseLevels;
}

private void shiftAgeToMatchPreviousReadings(Realm realmProcessedData, ArrayList<Integer> glucoseLevels, ArrayList<Integer> ageInSensorMinutesList) {
Expand Down