diff --git a/core/src/main/java/com/nightscout/core/dexcom/records/EGVRecord.java b/core/src/main/java/com/nightscout/core/dexcom/records/EGVRecord.java index 6a54dbad..0bcadb3d 100644 --- a/core/src/main/java/com/nightscout/core/dexcom/records/EGVRecord.java +++ b/core/src/main/java/com/nightscout/core/dexcom/records/EGVRecord.java @@ -18,20 +18,40 @@ import java.util.List; public class EGVRecord extends GenericTimestampRecord { - public final static int RECORD_SIZE = 12; + public final static int G4_RECORD_SIZE = 12; + public final static int G5_RECORD_SIZE = 22; + public final static int G5_TOUCH_RECORD_SIZE = 24; private GlucoseReading reading; private TrendArrow trend; private G4Noise noiseMode; + private int recordVersion = 0; - public EGVRecord(byte[] packet) { + public EGVRecord(byte[] packet, int recordVersion) { super(packet); - if (packet.length != RECORD_SIZE) { + this.recordVersion = recordVersion; + if (recordVersion < 4 && packet.length != G4_RECORD_SIZE) { throw new InvalidRecordLengthException("Unexpected record size: " + packet.length + - ". Expected size: " + RECORD_SIZE + ". Unparsed record: " + Utils.bytesToHex(packet)); + ". Expected size: " + G4_RECORD_SIZE + ". Unparsed record: " + Utils.bytesToHex(packet)); + } + else if (recordVersion == 4 && packet.length != G5_RECORD_SIZE) { + throw new InvalidRecordLengthException("Unexpected record size: " + packet.length + + ". Expected size: " + G5_RECORD_SIZE + ". Unparsed record: " + Utils.bytesToHex(packet)); + } + else if (recordVersion == 5 && packet.length != G5_TOUCH_RECORD_SIZE) { + throw new InvalidRecordLengthException("Unexpected record size: " + packet.length + + ". Expected size: " + G5_TOUCH_RECORD_SIZE + ". Unparsed record: " + Utils.bytesToHex(packet)); } int bGValue = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getShort(8) & Constants.EGV_VALUE_MASK; reading = new GlucoseReading(bGValue, GlucoseUnit.MGDL); - byte trendAndNoise = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).get(10); + byte trendAndNoise; + if(recordVersion < 4) { + trendAndNoise = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).get(10); + } + else { + //A G5 has 'systemTimeSeconds' (4 bytes), 'transmitterTimeSeconds' (4 bytes), and 'filteredRateByte' (1 byte) before the trendArrowAndNoiseMode byte + trendAndNoise = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).get(19); + } + int trendValue = trendAndNoise & Constants.EGV_TREND_ARROW_MASK; byte noiseValue = (byte) ((trendAndNoise & Constants.EGV_NOISE_MASK) >> 4); trend = TrendArrow.values()[trendValue]; diff --git a/core/src/main/java/com/nightscout/core/dexcom/records/MeterRecord.java b/core/src/main/java/com/nightscout/core/dexcom/records/MeterRecord.java index bb2ba1bb..67cd6e62 100644 --- a/core/src/main/java/com/nightscout/core/dexcom/records/MeterRecord.java +++ b/core/src/main/java/com/nightscout/core/dexcom/records/MeterRecord.java @@ -12,19 +12,33 @@ import java.util.List; public class MeterRecord extends GenericTimestampRecord { - public final static int RECORD_SIZE = 15; + public final static int G4_RECORD_SIZE = 15; + public final static int G5_RECORD_SIZE = 20; private int meterTime; private GlucoseReading reading; + private int recordVersion = 0; - public MeterRecord(byte[] packet) { + public MeterRecord(byte[] packet, int recordVersion) { super(packet); - if (packet.length != RECORD_SIZE) { + this.recordVersion = recordVersion; + if (recordVersion < 3 && packet.length != G4_RECORD_SIZE) { throw new InvalidRecordLengthException("Unexpected record size: " + packet.length + - ". Expected size: " + RECORD_SIZE + " record: " + Utils.bytesToHex(packet)); + ". Expected size: " + G4_RECORD_SIZE + " record: " + Utils.bytesToHex(packet)); + } + else if (recordVersion >= 3 && packet.length != G5_RECORD_SIZE) { + throw new InvalidRecordLengthException("Unexpected record size: " + packet.length + + ". Expected size: " + G5_RECORD_SIZE + " record: " + Utils.bytesToHex(packet)); } int meterBG = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getShort(8); reading = new GlucoseReading(meterBG, GlucoseUnit.MGDL); - meterTime = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(10); + if(recordVersion < 3) { + meterTime = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(10); + } + else { + //The Dexcom G5 has added an entryType field after the meterValue byte field, which must be skipped for this + meterTime = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(11); + } + } public MeterRecord(int meterBgMgdl, int meterTime, Date displayTime, Date systemTime) { diff --git a/core/src/main/java/com/nightscout/core/drivers/ReadData.java b/core/src/main/java/com/nightscout/core/drivers/ReadData.java index 097def07..841eb108 100644 --- a/core/src/main/java/com/nightscout/core/drivers/ReadData.java +++ b/core/src/main/java/com/nightscout/core/drivers/ReadData.java @@ -236,8 +236,21 @@ private List parsePage(byte[] data, Class< int startIdx; switch (pageHeader.getRecordType()) { case EGV_DATA: - startIdx = PageHeader.HEADER_SIZE + (EGVRecord.RECORD_SIZE + 1) * i; - records.add(clazz.cast(new EGVRecord(Arrays.copyOfRange(data, startIdx, startIdx + EGVRecord.RECORD_SIZE)))); + + if(pageHeader.getRevision() == 0x5) { + //This is a record from a Dexcom G5 Touch device + startIdx = PageHeader.HEADER_SIZE + (EGVRecord.G5_TOUCH_RECORD_SIZE + 1) * i; + records.add(clazz.cast(new EGVRecord(Arrays.copyOfRange(data, startIdx, startIdx + EGVRecord.G5_TOUCH_RECORD_SIZE),pageHeader.getRevision()))); + } + else if(pageHeader.getRevision() == 0x4) { + //This is a record from a Dexcom G5 device + startIdx = PageHeader.HEADER_SIZE + (EGVRecord.G5_RECORD_SIZE + 1) * i; + records.add(clazz.cast(new EGVRecord(Arrays.copyOfRange(data, startIdx, startIdx + EGVRecord.G5_RECORD_SIZE),pageHeader.getRevision()))); + } + else { + startIdx = PageHeader.HEADER_SIZE + (EGVRecord.G4_RECORD_SIZE + 1) * i; + records.add(clazz.cast(new EGVRecord(Arrays.copyOfRange(data, startIdx, startIdx + EGVRecord.G4_RECORD_SIZE),pageHeader.getRevision()))); + } break; case CAL_SET: int recordLength = (pageHeader.getRevision() <= 2) ? CalRecord.RECORD_SIZE : CalRecord.RECORD_V2_SIZE; @@ -245,8 +258,14 @@ private List parsePage(byte[] data, Class< records.add(clazz.cast(new CalRecord(Arrays.copyOfRange(data, startIdx, startIdx + recordLength)))); break; case METER_DATA: - startIdx = PageHeader.HEADER_SIZE + (MeterRecord.RECORD_SIZE + 1) * i; - records.add(clazz.cast(new MeterRecord(Arrays.copyOfRange(data, startIdx, startIdx + MeterRecord.RECORD_SIZE)))); + if(pageHeader.getRevision() == 0x03) { + //This is a record from a Dexcom G5 device + startIdx = PageHeader.HEADER_SIZE + (MeterRecord.G5_RECORD_SIZE + 1) * i; + records.add(clazz.cast(new MeterRecord(Arrays.copyOfRange(data, startIdx, startIdx + MeterRecord.G5_RECORD_SIZE),pageHeader.getRevision()))); + }else { + startIdx = PageHeader.HEADER_SIZE + (MeterRecord.G4_RECORD_SIZE + 1) * i; + records.add(clazz.cast(new MeterRecord(Arrays.copyOfRange(data, startIdx, startIdx + MeterRecord.G4_RECORD_SIZE),pageHeader.getRevision()))); + } break; case SENSOR_DATA: startIdx = PageHeader.HEADER_SIZE + (SensorRecord.RECORD_SIZE + 1) * i;