Skip to content

Commit 8661390

Browse files
author
AJ Keller
authored
Merge pull request #69 from aj-ptw/1.0.0
1.0.0
2 parents 04ea38f + a6f5d75 commit 8661390

File tree

5 files changed

+175
-10
lines changed

5 files changed

+175
-10
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@
1111
* Refactored library for pip
1212
* Moved plugins folder into openbci dir so plugins can be imported when installed with pip
1313

14+
15+
## Beta 0
16+
17+
* Adds high speed for Daisy over WiFi - now all boards are supported!
18+
19+
## Alpha 1
20+
21+
* Adds high speed for Ganglion over WiFi
22+
23+
## Alpha 0
24+
25+
* Adds high speed for Cyton over WiFi
26+
1427
# v0.1
1528

1629
## dev

openbci/utils/parse.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,57 @@ 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+
"""
184+
/**
185+
* @description Used to make one sample object from two sample objects. The sample number of the new daisy sample will
186+
* be the upperSampleObject's sample number divded by 2. This allows us to preserve consecutive sample numbers that
187+
* flip over at 127 instead of 255 for an 8 channel. The daisySampleObject will also have one `channelData` array
188+
* with 16 elements inside it, with the lowerSampleObject in the lower indices and the upperSampleObject in the
189+
* upper set of indices. The auxData from both channels shall be captured in an object called `auxData` which
190+
* contains two arrays referenced by keys `lower` and `upper` for the `lowerSampleObject` and `upperSampleObject`,
191+
* respectively. The timestamps shall be averaged and moved into an object called `timestamp`. Further, the
192+
* un-averaged timestamps from the `lowerSampleObject` and `upperSampleObject` shall be placed into an object called
193+
* `_timestamps` which shall contain two keys `lower` and `upper` which contain the original timestamps for their
194+
* respective sampleObjects.
195+
* @param lowerSampleObject {Object} - Lower 8 channels with odd sample number
196+
* @param upperSampleObject {Object} - Upper 8 channels with even sample number
197+
* @returns {Object} - The new merged daisy sample object
198+
*/
199+
"""
200+
daisy_sample_object = OpenBCISample()
201+
202+
if lower_sample_object.channel_data is not None:
203+
daisy_sample_object.channel_data = lower_sample_object.channel_data + upper_sample_object.channel_data
204+
205+
daisy_sample_object.sample_number = upper_sample_object.sample_number
206+
daisy_sample_object.id = daisy_sample_object.sample_number
207+
208+
daisy_sample_object.aux_data = {
209+
'lower': lower_sample_object.aux_data,
210+
'upper': upper_sample_object.aux_data
211+
}
212+
213+
if lower_sample_object.timestamp:
214+
daisy_sample_object.timestamp = lower_sample_object.timestamp
215+
216+
daisy_sample_object.stop_byte = lower_sample_object.stop_byte
217+
218+
daisy_sample_object._timestamps = {
219+
'lower': lower_sample_object.timestamp,
220+
'upper': upper_sample_object.timestamp
221+
}
222+
223+
if lower_sample_object.accel_data is not None:
224+
if lower_sample_object.accel_data[0] > 0 or lower_sample_object.accel_data[1] > 0 or lower_sample_object.accel_data[2] > 0:
225+
daisy_sample_object.accel_data = lower_sample_object.accel_data
226+
else:
227+
daisy_sample_object.accel_data = upper_sample_object.accel_data
228+
229+
daisy_sample_object.valid = True
230+
231+
return daisy_sample_object
232+
182233
"""
183234
/**
184235
* @description Used transform raw data packets into fully qualified packets
@@ -279,4 +330,6 @@ def __init__(self,
279330
self.sample_number = sample_number
280331
self.start_byte = start_byte
281332
self.stop_byte = stop_byte
333+
self.timestamp = 0
334+
self._timestamps = {}
282335
self.valid = valid

openbci/wifi.py

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def handle_sample(sample):
2828
import requests
2929
import xmltodict
3030

31-
from openbci.utils import k, ParseRaw, ssdp
31+
from openbci.utils import k, ParseRaw, OpenBCISample, ssdp
3232

3333
SAMPLE_RATE = 0 # Hz
3434

@@ -56,14 +56,17 @@ class OpenBCIWiFi(object):
5656
"""
5757

5858
def __init__(self, ip_address=None, shield_name=None, sample_rate=None, log=True, timeout=3,
59-
max_packets_to_skip=20, latency=10000, high_speed=True, ssdp_attempts=5):
59+
max_packets_to_skip=20, latency=10000, high_speed=True, ssdp_attempts=5,
60+
num_channels=8):
6061
# these one are used
62+
self.daisy = False
6163
self.high_speed = high_speed
6264
self.impedance = False
6365
self.ip_address = ip_address
6466
self.latency = latency
6567
self.log = log # print_incoming_text needs log
6668
self.max_packets_to_skip = max_packets_to_skip
69+
self.num_channles = num_channels
6770
self.sample_rate = sample_rate
6871
self.shield_name = shield_name
6972
self.ssdp_attempts = ssdp_attempts
@@ -157,8 +160,14 @@ def connect(self):
157160
gains = None
158161
if self.board_type == k.BOARD_CYTON:
159162
gains = [24, 24, 24, 24, 24, 24, 24, 24]
163+
self.daisy = False
164+
elif self.board_type == k.BOARD_DAISY:
165+
gains = [24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24]
166+
self.daisy = True
160167
elif self.board_type == k.BOARD_GANGLION:
161168
gains = [51, 51, 51, 51]
169+
self.daisy = False
170+
self.local_wifi_server.set_daisy(daisy=self.daisy)
162171
self.local_wifi_server.set_parser(ParseRaw(gains=gains, board_type=self.board_type))
163172

164173
if self.high_speed:
@@ -444,7 +453,7 @@ def warn(self, text):
444453
# log how many packets where sent succesfully in between warnings
445454
if self.log_packet_count:
446455
logging.info('Data packets received:' + str(self.log_packet_count))
447-
self.log_packet_count = 0;
456+
self.log_packet_count = 0
448457
logging.warning(text)
449458
print("Warning: %s" % text)
450459

@@ -472,11 +481,14 @@ def reconnect(self):
472481

473482

474483
class WiFiShieldHandler(asyncore.dispatcher_with_send):
475-
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):
476486
asyncore.dispatcher_with_send.__init__(self, sock)
477487

478-
self.high_speed = high_speed
479488
self.callback = callback
489+
self.daisy = daisy
490+
self.high_speed = high_speed
491+
self.last_odd_sample = OpenBCISample()
480492
self.parser = parser if parser is not None else ParseRaw(gains=[24, 24, 24, 24, 24, 24, 24, 24])
481493

482494
def handle_read(self):
@@ -490,8 +502,22 @@ def handle_read(self):
490502
samples = self.parser.transform_raw_data_packets_to_sample(raw_data_packets=raw_data_packets)
491503

492504
for sample in samples:
493-
if self.callback is not None:
494-
self.callback(sample)
505+
# if a daisy module is attached, wait to concatenate two samples (main board + daisy)
506+
# before passing it to callback
507+
if self.daisy:
508+
# odd sample: daisy sample, save for later
509+
if ~sample.sample_number % 2:
510+
self.last_odd_sample = sample
511+
# even sample: concatenate and send if last sample was the fist part, otherwise drop the packet
512+
elif sample.sample_number - 1 == self.last_odd_sample.sample_number:
513+
# the aux data will be the average between the two samples, as the channel
514+
# samples themselves have been averaged by the board
515+
daisy_sample = self.parser.make_daisy_sample_object_wifi(self.last_odd_sample, sample)
516+
if self.callback is not None:
517+
self.callback(daisy_sample)
518+
else:
519+
if self.callback is not None:
520+
self.callback(sample)
495521

496522
else:
497523
try:
@@ -516,11 +542,12 @@ def handle_read(self):
516542

517543
class WiFiShieldServer(asyncore.dispatcher):
518544

519-
def __init__(self, host, port, callback=None, gains=None, high_speed=True):
545+
def __init__(self, host, port, callback=None, gains=None, high_speed=True, daisy=False):
520546
asyncore.dispatcher.__init__(self)
521547
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
522548
self.set_reuse_addr()
523549
self.bind((host, port))
550+
self.daisy = daisy
524551
self.listen(5)
525552
self.callback = None
526553
self.handler = None
@@ -532,13 +559,19 @@ def handle_accept(self):
532559
if pair is not None:
533560
sock, addr = pair
534561
print 'Incoming connection from %s' % repr(addr)
535-
self.handler = WiFiShieldHandler(sock, self.callback, high_speed=self.high_speed, parser=self.parser)
562+
self.handler = WiFiShieldHandler(sock, self.callback, high_speed=self.high_speed,
563+
parser=self.parser, daisy=self.daisy)
536564

537565
def set_callback(self, callback):
538566
self.callback = callback
539567
if self.handler is not None:
540568
self.handler.callback = callback
541569

570+
def set_daisy(self, daisy):
571+
self.daisy = daisy
572+
if self.handler is not None:
573+
self.handler.daisy = daisy
574+
542575
def set_gains(self, gains):
543576
self.parser.set_ads1299_scale_factors(gains)
544577

scripts/stream_data_wifi.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55

66
def printData(sample):
7-
print(sample)
7+
print(sample.sample_number)
8+
print(sample.channel_data)
89

910

1011
if __name__ == '__main__':

tests/test_parse.py

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

44
from openbci.utils import (k,
5+
OpenBCISample,
56
ParseRaw,
67
sample_packet,
78
sample_packet_standard_raw_aux,
@@ -245,6 +246,70 @@ def test_transform_raw_data_packets_to_sample(self):
245246
for i in range(len(samples)):
246247
self.assertEqual(samples[i].sample_number, i)
247248

249+
def test_make_daisy_sample_object_wifi(self):
250+
parser = ParseRaw(gains=[24, 24, 24, 24, 24, 24, 24, 24])
251+
# Make the lower sample(channels 1 - 8)
252+
lower_sample_object = OpenBCISample(sample_number=1)
253+
lower_sample_object.channel_data = [1, 2, 3, 4, 5, 6, 7, 8]
254+
lower_sample_object.aux_data = [0, 1, 2]
255+
lower_sample_object.timestamp = 4
256+
lower_sample_object.accel_data = [0, 0, 0]
257+
# Make the upper sample(channels 9 - 16)
258+
upper_sample_object = OpenBCISample(sample_number=2)
259+
upper_sample_object.channel_data = [9, 10, 11, 12, 13, 14, 15, 16]
260+
upper_sample_object.accel_data = [0, 1, 2]
261+
upper_sample_object.aux_data = [3, 4, 5]
262+
upper_sample_object.timestamp = 8
263+
264+
daisy_sample_object = parser.make_daisy_sample_object_wifi(lower_sample_object, upper_sample_object);
265+
266+
# should have valid object true
267+
self.assertTrue(daisy_sample_object.valid)
268+
269+
# should make a channelData array 16 elements long
270+
self.assertEqual(len(daisy_sample_object.channel_data), k.NUMBER_OF_CHANNELS_DAISY)
271+
272+
# should make a channelData array with lower array in front of upper array
273+
for i in range(16):
274+
self.assertEqual(daisy_sample_object.channel_data[i], i + 1)
275+
276+
self.assertEqual(daisy_sample_object.id, daisy_sample_object.sample_number)
277+
self.assertEqual(daisy_sample_object.sample_number, daisy_sample_object.sample_number)
278+
279+
# should put the aux packets in an object
280+
self.assertIsNotNone(daisy_sample_object.aux_data['lower'])
281+
self.assertIsNotNone(daisy_sample_object.aux_data['upper'])
282+
283+
# should put the aux packets in an object in the right order
284+
for i in range(3):
285+
self.assertEqual(daisy_sample_object.aux_data['lower'][i], i)
286+
self.assertEqual(daisy_sample_object.aux_data['upper'][i], i + 3)
287+
288+
# should take the lower timestamp
289+
self.assertEqual(daisy_sample_object.timestamp, lower_sample_object.timestamp)
290+
291+
# should take the lower stopByte
292+
self.assertEqual(daisy_sample_object.stop_byte, lower_sample_object.stop_byte)
293+
294+
# should place the old timestamps in an object
295+
self.assertEqual(daisy_sample_object._timestamps['lower'], lower_sample_object.timestamp)
296+
self.assertEqual(daisy_sample_object._timestamps['upper'], upper_sample_object.timestamp)
297+
298+
# should store an accelerometer value if present
299+
self.assertIsNotNone(daisy_sample_object.accel_data)
300+
self.assertListEqual(daisy_sample_object.accel_data, [0, 1, 2])
301+
302+
lower_sample = OpenBCISample(sample_number=1)
303+
lower_sample.accel_data = [0, 1, 2]
304+
upper_sample = OpenBCISample(sample_number=2)
305+
upper_sample.accel_data = [0, 0, 0]
306+
307+
# Call the function under test
308+
daisy_sample = parser.make_daisy_sample_object_wifi(lower_sample, upper_sample)
309+
310+
self.assertIsNotNone(daisy_sample.accel_data)
311+
self.assertListEqual(daisy_sample.accel_data, [0, 1, 2])
312+
248313

249314
if __name__ == '__main__':
250315
main()

0 commit comments

Comments
 (0)