Skip to content

Commit 676a611

Browse files
committed
Merge pull request openaps#3 from openaps/dev
stub out next release: 0.1.0-dev area
2 parents 12751d8 + 0193c69 commit 676a611

File tree

4 files changed

+156
-8
lines changed

4 files changed

+156
-8
lines changed

dexcom_reader/database_records.py

Lines changed: 109 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,21 +103,117 @@ def insertion_time(self):
103103
def session_state(self):
104104
states = [None, 'REMOVED', 'EXPIRED', 'RESIDUAL_DEVIATION',
105105
'COUNTS_DEVIATION', 'SECOND_SESSION', 'OFF_TIME_LOSS',
106-
'STARTED', 'BAD_TRANSMITTER', 'MANUFACTURING_MODE']
106+
'STARTED', 'BAD_TRANSMITTER', 'MANUFACTURING_MODE',
107+
'UNKNOWN1', 'UNKNOWN2', 'UNKNOWN3', 'UNKNOWN4', 'UNKNOWN5',
108+
'UNKNOWN6', 'UNKNOWN7', 'UNKNOWN8']
107109
return states[ord(self.data[3])]
108110

109111
def __repr__(self):
110112
return '%s: state=%s' % (self.display_time, self.session_state)
111113

114+
class G5InsertionRecord (InsertionRecord):
115+
FORMAT = '<3Ic10BH'
112116

113117
class Calibration(GenericTimestampedRecord):
118+
FORMAT = '<2Iddd3cdb'
119+
# CAL_FORMAT = '<2Iddd3cdb'
120+
FIELDS = [ 'slope', 'intercept', 'scale', 'decay', 'numsub', 'raw' ]
114121
@property
115-
def raw(self):
116-
return binascii.hexlify(bytearray(self.data))
122+
def raw (self):
123+
return binascii.hexlify(self.raw_data)
124+
@property
125+
def slope (self):
126+
return self.data[2]
127+
@property
128+
def intercept (self):
129+
return self.data[3]
130+
@property
131+
def scale (self):
132+
return self.data[4]
133+
@property
134+
def decay (self):
135+
return self.data[8]
136+
@property
137+
def numsub (self):
138+
return int(self.data[9])
117139

118140
def __repr__(self):
119141
return '%s: CAL SET:%s' % (self.display_time, self.raw)
120142

143+
LEGACY_SIZE = 148
144+
REV_2_SIZE = 249
145+
@classmethod
146+
def _ClassSize(cls):
147+
148+
return cls.REV_2_SIZE
149+
150+
@classmethod
151+
def Create(cls, data, record_counter):
152+
offset = record_counter * cls._ClassSize()
153+
cal_size = struct.calcsize(cls.FORMAT)
154+
raw_data = data[offset:offset + cls._ClassSize()]
155+
156+
cal_data = data[offset:offset + cal_size]
157+
unpacked_data = cls._ClassFormat().unpack(cal_data)
158+
return cls(unpacked_data, raw_data)
159+
160+
def __init__ (self, data, raw_data):
161+
self.page_data = raw_data
162+
self.raw_data = raw_data
163+
self.data = data
164+
subsize = struct.calcsize(SubCal.FORMAT)
165+
offset = self.numsub * subsize
166+
calsize = struct.calcsize(self.FORMAT)
167+
caldata = raw_data[:calsize]
168+
subdata = raw_data[calsize:calsize + offset]
169+
crcdata = raw_data[calsize+offset:calsize+offset+2]
170+
171+
subcals = [ ]
172+
for i in xrange(self.numsub):
173+
offset = i * subsize
174+
raw_sub = subdata[offset:offset+subsize]
175+
sub = SubCal(raw_sub, self.data[1])
176+
subcals.append(sub)
177+
178+
self.subcals = subcals
179+
180+
self.check_crc()
181+
def to_dict (self):
182+
res = super(Calibration, self).to_dict( )
183+
res['subrecords'] = [ sub.to_dict( ) for sub in self.subcals ]
184+
return res
185+
@property
186+
def crc(self):
187+
return struct.unpack('H', self.raw_data[-2:])[0]
188+
189+
class LegacyCalibration (Calibration):
190+
@classmethod
191+
def _ClassSize(cls):
192+
193+
return cls.LEGACY_SIZE
194+
195+
196+
class SubCal (GenericTimestampedRecord):
197+
FORMAT = '<IIIIc'
198+
BASE_FIELDS = [ ]
199+
FIELDS = [ 'entered', 'meter', 'sensor', 'applied', ]
200+
def __init__ (self, raw_data, displayOffset=None):
201+
self.raw_data = raw_data
202+
self.data = self._ClassFormat().unpack(raw_data)
203+
self.displayOffset = displayOffset
204+
@property
205+
def entered (self):
206+
return util.ReceiverTimeToTime(self.data[0])
207+
@property
208+
def meter (self):
209+
return int(self.data[1])
210+
@property
211+
def sensor (self):
212+
return int(self.data[2])
213+
@property
214+
def applied (self):
215+
return util.ReceiverTimeToTime(self.data[3])
216+
121217
class MeterRecord(GenericTimestampedRecord):
122218
FORMAT = '<2IHIH'
123219
FIELDS = ['meter_glucose', 'meter_time' ]
@@ -133,6 +229,8 @@ def meter_time(self):
133229
def __repr__(self):
134230
return '%s: Meter BG:%s' % (self.display_time, self.meter_glucose)
135231

232+
class G5MeterRecord (MeterRecord):
233+
FORMAT = '<2IHI5BH'
136234

137235
class EventRecord(GenericTimestampedRecord):
138236
# sys_time,display_time,glucose,meter_time,crc
@@ -230,3 +328,11 @@ def __repr__(self):
230328
else:
231329
return '%s: CGM BG:%s (%s) DO:%s' % (self.display_time, self.glucose,
232330
self.trend_arrow, self.display_only)
331+
332+
class G5EGVRecord (EGVRecord):
333+
FORMAT = '<2IHBBBBBBBBBcBH'
334+
@property
335+
def full_trend(self):
336+
return self.data[12]
337+
338+

dexcom_reader/readdata.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ def ReadDatabasePage(self, record_type, page):
251251
assert ord(packet.command) == 1
252252
# first index (uint), numrec (uint), record_type (byte), revision (byte),
253253
# page# (uint), r1 (uint), r2 (uint), r3 (uint), ushort (Crc)
254-
header_format = '<2I2c4IH'
254+
header_format = '<2IcB4IH'
255255
header_data_len = struct.calcsize(header_format)
256256
header = struct.unpack_from(header_format, packet.data)
257257
header_crc = crc16.crc16(packet.data[:header_data_len-2])
@@ -266,15 +266,21 @@ def GenericRecordYielder(self, header, data, record_type):
266266
for x in xrange(header[1]):
267267
yield record_type.Create(data, x)
268268

269-
def ParsePage(self, header, data):
270-
record_type = constants.RECORD_TYPES[ord(header[2])]
271-
generic_parser_map = {
269+
PARSER_MAP = {
272270
'USER_EVENT_DATA': database_records.EventRecord,
273271
'METER_DATA': database_records.MeterRecord,
272+
'CAL_SET': database_records.Calibration,
273+
# 'CAL_SET': database_records.Calibration,
274274
'INSERTION_TIME': database_records.InsertionRecord,
275275
'EGV_DATA': database_records.EGVRecord,
276276
'SENSOR_DATA': database_records.SensorRecord,
277277
}
278+
def ParsePage(self, header, data):
279+
record_type = constants.RECORD_TYPES[ord(header[2])]
280+
revision = int(header[3])
281+
generic_parser_map = self.PARSER_MAP
282+
if revision < 2 and record_type == 'CAL_SET':
283+
generic_parser_map.update(CAL_SET=database_records.LegacyCalibration)
278284
xml_parsed = ['PC_SOFTWARE_PARAMETER', 'MANUFACTURING_DATA']
279285
if record_type in generic_parser_map:
280286
return self.GenericRecordYielder(header, data,
@@ -308,6 +314,20 @@ def ReadRecords(self, record_type):
308314
records.extend(self.ReadDatabasePage(record_type, x))
309315
return records
310316

317+
class DexcomG5 (Dexcom):
318+
PARSER_MAP = {
319+
'USER_EVENT_DATA': database_records.EventRecord,
320+
'METER_DATA': database_records.G5MeterRecord,
321+
'CAL_SET': database_records.Calibration,
322+
'INSERTION_TIME': database_records.G5InsertionRecord,
323+
'EGV_DATA': database_records.G5EGVRecord,
324+
'SENSOR_DATA': database_records.SensorRecord,
325+
}
326+
327+
def GetDevice (port, G5=False):
328+
if G5:
329+
return DexcomG5(port)
330+
return Dexcom(port)
311331

312332
if __name__ == '__main__':
313333
Dexcom.LocateAndDownload()

etc/udev/rules.d/80-dexcom.rules

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
3+
4+
# udev can only match one entry at a time, but this triggers additional add entries.
5+
# 22a3:0047
6+
# 22a3 0047
7+
ATTRS{idVendor}=="22a3", ATTRS{idProduct}=="0047", \
8+
ENV{ID_MM_DEVICE_IGNORE}="1", \
9+
MODE="0664", GROUP="plugdev"
10+
11+
12+
# create a symlink to the ttyUSB device created automatically above
13+
#ACTION=="add", \
14+
# SUBSYSTEM=="tty", \
15+
# ATTRS{idVendor}=="22a3", ATTRS{idProduct}=="0047", \
16+
# SYMLINK+="ttyUSB.Dexcom%n"
17+
18+

setup.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def readme():
1010
return f.read()
1111

1212
setup(name='dexcom_reader',
13-
version='0.0.8', # http://semver.org/
13+
version='0.1.0-dev', # http://semver.org/
1414
description='Audit, and inspect data from Dexcom G4.',
1515
long_description=readme(),
1616
author="Will Nowak",
@@ -34,6 +34,10 @@ def readme():
3434
package_data={
3535
'dexcom_reader': ['etc/udev/rules.d/*']
3636
},
37+
data_files = [
38+
('/etc/udev/rules.d/80-dexcom.rules', ['etc/udev/80-dexcom.rules'] ),
39+
40+
],
3741
zip_safe=False,
3842
include_package_data=True
3943
)

0 commit comments

Comments
 (0)