Skip to content

Commit 407553c

Browse files
author
AJ Keller
committed
ADD: Tests for making daisy packets
1 parent 644cbc3 commit 407553c

File tree

4 files changed

+243
-5
lines changed

4 files changed

+243
-5
lines changed

openbci/utils/parse.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,76 @@ def transform_raw_data_packet_to_sample(self, raw_data):
179179

180180
return sample
181181

182+
def make_daisy_sample_object_wifi(self, lower_sample_object, upper_sample_object):
183+
pass
184+
185+
"""
186+
/**
187+
* @description Used to make one sample object from two sample objects. The sample number of the new daisy sample will
188+
* be the upperSampleObject's sample number divded by 2. This allows us to preserve consecutive sample numbers that
189+
* flip over at 127 instead of 255 for an 8 channel. The daisySampleObject will also have one `channelData` array
190+
* with 16 elements inside it, with the lowerSampleObject in the lower indices and the upperSampleObject in the
191+
* upper set of indices. The auxData from both channels shall be captured in an object called `auxData` which
192+
* contains two arrays referenced by keys `lower` and `upper` for the `lowerSampleObject` and `upperSampleObject`,
193+
* respectively. The timestamps shall be averaged and moved into an object called `timestamp`. Further, the
194+
* un-averaged timestamps from the `lowerSampleObject` and `upperSampleObject` shall be placed into an object called
195+
* `_timestamps` which shall contain two keys `lower` and `upper` which contain the original timestamps for their
196+
* respective sampleObjects.
197+
* @param lowerSampleObject {Object} - Lower 8 channels with odd sample number
198+
* @param upperSampleObject {Object} - Upper 8 channels with even sample number
199+
* @returns {Object} - The new merged daisy sample object
200+
*/
201+
function makeDaisySampleObjectWifi (lowerSampleObject, upperSampleObject) {
202+
let daisySampleObject = {};
203+
204+
if (lowerSampleObject.hasOwnProperty('channelData')) {
205+
daisySampleObject['channelData'] = lowerSampleObject.channelData.concat(upperSampleObject.channelData);
206+
}
207+
208+
if (lowerSampleObject.hasOwnProperty('channelDataCounts')) {
209+
daisySampleObject['channelDataCounts'] = lowerSampleObject.channelDataCounts.concat(upperSampleObject.channelDataCounts);
210+
}
211+
212+
daisySampleObject['sampleNumber'] = upperSampleObject.sampleNumber;
213+
214+
daisySampleObject['auxData'] = {
215+
'lower': lowerSampleObject.auxData,
216+
'upper': upperSampleObject.auxData
217+
};
218+
219+
if (lowerSampleObject.hasOwnProperty('timestamp')) {
220+
daisySampleObject['timestamp'] = lowerSampleObject.timestamp;
221+
}
222+
223+
daisySampleObject.stopByte = lowerSampleObject.stopByte;
224+
225+
daisySampleObject['_timestamps'] = {
226+
'lower': lowerSampleObject.timestamp,
227+
'upper': upperSampleObject.timestamp
228+
};
229+
230+
if (lowerSampleObject.hasOwnProperty('accelData')) {
231+
if (lowerSampleObject.accelData[0] > 0 || lowerSampleObject.accelData[1] > 0 || lowerSampleObject.accelData[2] > 0) {
232+
daisySampleObject.accelData = lowerSampleObject.accelData;
233+
} else {
234+
daisySampleObject.accelData = upperSampleObject.accelData;
235+
}
236+
}
237+
238+
if (lowerSampleObject.hasOwnProperty('accelDataCounts')) {
239+
if (lowerSampleObject.accelDataCounts[0] > 0 || lowerSampleObject.accelDataCounts[1] > 0 || lowerSampleObject.accelDataCounts[2] > 0) {
240+
daisySampleObject.accelDataCounts = lowerSampleObject.accelDataCounts;
241+
} else {
242+
daisySampleObject.accelDataCounts = upperSampleObject.accelDataCounts;
243+
}
244+
}
245+
246+
daisySampleObject['valid'] = true;
247+
248+
return daisySampleObject;
249+
}
250+
"""
251+
182252
"""
183253
/**
184254
* @description Used transform raw data packets into fully qualified packets

openbci/utils/utilities.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,33 @@ def sample_packet_impedance(channel_number):
6262

6363
def sample_packet_user_defined():
6464
return bytearray([0xA0, 0x00, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, make_tail_byte_from_packet_type(k.OBCIStreamPacketUserDefinedType)]);
65+
66+
67+
def new_sample(sample_number):
68+
sample_number = sample_number_normalize(sample_number)
69+
return {
70+
'start_byte': k.RAW_BYTE_START,
71+
'sample_number': sample_number,
72+
'channel_data': [],
73+
'accel_data': [],
74+
'aux_data': None,
75+
'stop_byte': k.RAW_BYTE_STOP,
76+
'board_time': 0,
77+
'timestamp': 0,
78+
'valid': True
79+
}
80+
81+
82+
def new_sample_no_scale(sample_number):
83+
sample_number = sample_number_normalize(sample_number)
84+
return {
85+
'start_byte': k.RAW_BYTE_START,
86+
'sample_number': sample_number,
87+
'channel_data_counts': [],
88+
'accel_data_counts': [],
89+
'aux_data': None,
90+
'stop_byte': k.RAW_BYTE_STOP,
91+
'board_time': 0,
92+
'timestamp': 0,
93+
'valid': True
94+
}

openbci/wifi.py

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ def __init__(self, ip_address=None, shield_name=None, sample_rate=None, log=True
5959
max_packets_to_skip=20, latency=10000, high_speed=True, ssdp_attempts=5,
6060
num_channels=8):
6161
# these one are used
62+
self.daisy = False
6263
self.high_speed = high_speed
6364
self.impedance = False
6465
self.ip_address = ip_address
@@ -159,10 +160,14 @@ def connect(self):
159160
gains = None
160161
if self.board_type == k.BOARD_CYTON:
161162
gains = [24, 24, 24, 24, 24, 24, 24, 24]
163+
self.daisy = False
162164
elif self.board_type == k.BOARD_DAISY:
163165
gains = [24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24]
166+
self.daisy = True
164167
elif self.board_type == k.BOARD_GANGLION:
165168
gains = [51, 51, 51, 51]
169+
self.daisy = False
170+
self.local_wifi_server.set_daisy(daisy=self.daisy)
166171
self.local_wifi_server.set_parser(ParseRaw(gains=gains, board_type=self.board_type))
167172

168173
if self.high_speed:
@@ -448,7 +453,7 @@ def warn(self, text):
448453
# log how many packets where sent succesfully in between warnings
449454
if self.log_packet_count:
450455
logging.info('Data packets received:' + str(self.log_packet_count))
451-
self.log_packet_count = 0;
456+
self.log_packet_count = 0
452457
logging.warning(text)
453458
print("Warning: %s" % text)
454459

@@ -476,11 +481,13 @@ def reconnect(self):
476481

477482

478483
class WiFiShieldHandler(asyncore.dispatcher_with_send):
479-
def __init__(self, sock, callback=None, high_speed=True, parser=None):
484+
def __init__(self, sock, callback=None, high_speed=True,
485+
parser=None, daisy=False):
480486
asyncore.dispatcher_with_send.__init__(self, sock)
481487

482-
self.high_speed = high_speed
483488
self.callback = callback
489+
self.high_speed = high_speed
490+
self.last_odd_sample = None
484491
self.parser = parser if parser is not None else ParseRaw(gains=[24, 24, 24, 24, 24, 24, 24, 24])
485492

486493
def handle_read(self):
@@ -494,6 +501,26 @@ def handle_read(self):
494501
samples = self.parser.transform_raw_data_packets_to_sample(raw_data_packets=raw_data_packets)
495502

496503
for sample in samples:
504+
# if a daisy module is attached, wait to concatenate two samples (main board + daisy)
505+
# before passing it to callback
506+
if self.daisy:
507+
# odd sample: daisy sample, save for later
508+
if ~sample.sample_number % 2:
509+
self.last_odd_sample = sample
510+
# even sample: concatenate and send if last sample was the fist part, otherwise drop the packet
511+
elif sample.sample_number - 1 == self.last_odd_sample.id:
512+
# the aux data will be the average between the two samples, as the channel
513+
# samples themselves have been averaged by the board
514+
avg_aux_data = list(
515+
(np.array(sample.aux_data) + np.array(self.last_odd_sample.aux_data)) / 2)
516+
whole_sample = OpenBCISample(sample.id,
517+
sample.channel_data + self.last_odd_sample.channel_data,
518+
avg_aux_data)
519+
for call in callback:
520+
call(whole_sample)
521+
else:
522+
for call in callback:
523+
call(sample)
497524
if self.callback is not None:
498525
self.callback(sample)
499526

@@ -520,11 +547,12 @@ def handle_read(self):
520547

521548
class WiFiShieldServer(asyncore.dispatcher):
522549

523-
def __init__(self, host, port, callback=None, gains=None, high_speed=True):
550+
def __init__(self, host, port, callback=None, gains=None, high_speed=True, daisy=False):
524551
asyncore.dispatcher.__init__(self)
525552
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
526553
self.set_reuse_addr()
527554
self.bind((host, port))
555+
self.daisy = daisy
528556
self.listen(5)
529557
self.callback = None
530558
self.handler = None
@@ -536,13 +564,19 @@ def handle_accept(self):
536564
if pair is not None:
537565
sock, addr = pair
538566
print 'Incoming connection from %s' % repr(addr)
539-
self.handler = WiFiShieldHandler(sock, self.callback, high_speed=self.high_speed, parser=self.parser)
567+
self.handler = WiFiShieldHandler(sock, self.callback, high_speed=self.high_speed,
568+
parser=self.parser, daisy=self.daisy)
540569

541570
def set_callback(self, callback):
542571
self.callback = callback
543572
if self.handler is not None:
544573
self.handler.callback = callback
545574

575+
def set_daisy(self, daisy):
576+
self.daisy = daisy
577+
if self.handler is not None:
578+
self.handler.daisy = daisy
579+
546580
def set_gains(self, gains):
547581
self.parser.set_ads1299_scale_factors(gains)
548582

tests/test_parse.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import mock
33

44
from openbci.utils import (k,
5+
new_sample,
6+
new_sample_no_scale,
57
ParseRaw,
68
sample_packet,
79
sample_packet_standard_raw_aux,
@@ -245,6 +247,108 @@ def test_transform_raw_data_packets_to_sample(self):
245247
for i in range(len(samples)):
246248
self.assertEqual(samples[i].sample_number, i)
247249

250+
def test_make_daisy_sample_object_wifi(self):
251+
parser = ParseRaw(gains=[24, 24, 24, 24, 24, 24, 24, 24])
252+
# Make the lower sample(channels 1 - 8)
253+
lower_sample_object = new_sample(1)
254+
lower_sample_object.channel_data = [1, 2, 3, 4, 5, 6, 7, 8]
255+
lower_sample_object.aux_data = [0, 1, 2]
256+
lower_sample_object.timestamp = 4
257+
lower_sample_object.accel_data = [0, 0, 0]
258+
# Make the upper sample(channels 9 - 16)
259+
upper_sample_object = new_sample(2)
260+
upper_sample_object.channelData = [9, 10, 11, 12, 13, 14, 15, 16]
261+
upper_sample_object.accelData = [0, 1, 2]
262+
lower_sample_object.auxData = [3, 4, 5]
263+
upper_sample_object.timestamp = 8
264+
265+
daisy_sample_object = parser.make_daisy_sample_object_wifi(lower_sample_object, upper_sample_object);
266+
267+
lower_sample_object_no_scale = new_sample(1)
268+
lower_sample_object_no_scale.channelDataCounts = [1, 2, 3, 4, 5, 6, 7, 8]
269+
lower_sample_object_no_scale.auxData = [0, 1, 2]
270+
lower_sample_object_no_scale.accelDataCounts = [0, 0, 0]
271+
lower_sample_object_no_scale.timestamp = 4
272+
273+
# Make the upper sample(channels 9 - 16)
274+
upper_sample_object_no_scale = new_sample(2)
275+
upper_sample_object_no_scale.channelDataCounts = [9, 10, 11, 12, 13, 14, 15, 16]
276+
lower_sample_object_no_scale.accelDataCounts = [0, 1, 2]
277+
upper_sample_object_no_scale.auxData = [3, 4, 5]
278+
upper_sample_object_no_scale.timestamp = 8
279+
280+
# Call the function under test
281+
daisy_sample_object_no_scale = parser.make_daisy_sample_object_wifi(lower_sample_object_no_scale,
282+
upper_sample_object_no_scale)
283+
284+
# should have valid object true
285+
self.assertTrue(daisy_sample_object.valid)
286+
self.assertTrue(daisy_sample_object_no_scale.valid)
287+
288+
# should make a channelData array 16 elements long
289+
self.assertEqual(len(daisy_sample_object.channel_data), k.NUMBER_OF_CHANNELS_DAISY)
290+
self.assertEqual(len(daisy_sample_object_no_scale.channel_data_counts), k.NUMBER_OF_CHANNELS_DAISY)
291+
292+
# should make a channelData array with lower array in front of upper array
293+
for i in range(16):
294+
self.assertEqual(daisy_sample_object.channel_data[i], i + 1)
295+
self.assertEqual(daisy_sample_object_no_scale.channel_data_counts[i], i + 1)
296+
297+
self.assertEqual(daisy_sample_object.sample_number, daisy_sample_object.sample_number)
298+
self.assertEqual(daisy_sample_object_no_scale.sample_number, daisy_sample_object_no_scale.sample_number)
299+
300+
# should put the aux packets in an object
301+
self.assertIsNotNone(daisy_sample_object.aux_data.lower)
302+
self.assertIsNotNone(daisy_sample_object.aux_data.upper)
303+
self.assertIsNotNone(daisy_sample_object_no_scale.aux_data.lower)
304+
self.assertIsNotNone(daisy_sample_object_no_scale.aux_data.upper)
305+
306+
# should put the aux packets in an object in the right order
307+
for i in range(3):
308+
self.assertEqual(daisy_sample_object.aux_data['lower'][i], i)
309+
self.assertEqual(daisy_sample_object.aux_data['upper'][i], i + 3)
310+
self.assertEqual(daisy_sample_object_no_scale.aux_data['lower'][i], i)
311+
self.assertEqual(daisy_sample_object_no_scale.aux_data['upper'][i], i + 3)
312+
313+
# should take the lower timestamp
314+
self.assertEqual(daisy_sample_object.timestamp, lower_sample_object.timestamp)
315+
self.assertEqual(daisy_sample_object_no_scale.timestamp, lower_sample_object.timestamp)
316+
317+
# should take the lower stopByte
318+
self.assertEqual(daisy_sample_object.stop_byte, lower_sample_object.stop_byte)
319+
self.assertEqual(daisy_sample_object_no_scale.stop_byte, lower_sample_object.stop_byte)
320+
321+
# should place the old timestamps in an object
322+
self.assertEqual(daisy_sample_object._timestamps.lower, lower_sample_object.timestamp)
323+
self.assertEqual(daisy_sample_object._timestamps.upper, upper_sample_object.timestamp)
324+
self.assertEqual(daisy_sample_object_no_scale._timestamps.lower, lower_sample_object_no_scale.timestamp)
325+
self.assertEqual(daisy_sample_object_no_scale._timestamps.upper, upper_sample_object_no_scale.timestamp)
326+
327+
# should store an accelerometer value if present
328+
self.assertIsNotNone(daisy_sample_object.accel_data)
329+
self.assertListEqual(daisy_sample_object.accel_data, [0, 1, 2])
330+
self.assertIsNotNone(daisy_sample_object_no_scale.accel_data_counts)
331+
self.assertListEqual(daisy_sample_object_no_scale.accel_data_counts, [0, 1, 2])
332+
333+
lower_sample = new_sample(1)
334+
lower_sample.accel_data = [0, 1, 2]
335+
upper_sample = new_sample(2)
336+
upper_sample.accel_data = [0, 0, 0]
337+
338+
lower_sample_no_scale = new_sample_no_scale(1)
339+
lower_sample_no_scale.accel_data_counts = [0, 1, 2]
340+
upper_sample_no_scale = new_sample_no_scale(2)
341+
upper_sample_no_scale.accel_data_counts = [0, 0, 0]
342+
343+
# Call the function under test
344+
daisy_sample = parser.make_daisy_sample_object_wifi(lower_sample, upper_sample)
345+
daisy_sample_no_scale = parser.make_daisy_sample_object_wifi(lower_sample_no_scale, upper_sample_no_scale)
346+
347+
self.assertIsNotNone(daisy_sample.accel_data)
348+
self.assertListEqual(daisy_sample.accel_data, [0, 1, 2])
349+
self.assertIsNotNone(daisy_sample_no_scale.accel_data_counts)
350+
self.assertListEqual(daisy_sample_no_scale.accel_data_counts, [0, 1, 2])
351+
248352

249353
if __name__ == '__main__':
250354
main()

0 commit comments

Comments
 (0)