diff --git a/.gitignore b/.gitignore index 77156f6..72212cf 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ build/ .pytest_cache/ .DS_Store temp.txt -*.pyc +*.pyc \ No newline at end of file diff --git a/README.md b/README.md index ab28ea5..9c1a770 100755 --- a/README.md +++ b/README.md @@ -35,3 +35,9 @@ [prefix (default: /rtbio)] --filename [filename] [filename (default: temp.txt)] + + +To run from Python: +- You can use the `art-bio.yaml` file to intitialize the Anaconda environment with the necessary packages +- Run from terminal with for example the command `python art-bio.py -d biosignalsplux -c recg ws` to try the banana demo +- The biosignalsplux library files for Win64/Python 3.8 are included by default, for other platforms copy the files from the corresponding folder at [https://github.com/biosignalsplux/python-samples/tree/master/PLUX-API-Python3](https://github.com/biosignalsplux/python-samples/tree/master/PLUX-API-Python3) to the `devices/pluxlib` folder. \ No newline at end of file diff --git a/argparser.py b/argparser.py index e31faf2..c15761b 100644 --- a/argparser.py +++ b/argparser.py @@ -46,13 +46,21 @@ def getSamplingFrequency(args): def getFilename(args): if '--filename' in args: - index = next(i for i, arg in enumerate(args) if arg == '--filename' or arg == '-f') + index = next(i for i, arg in enumerate(args) if arg == '--filename') try: return args[index+1] except IndexError: raise ValueError('filename is empty') return None +def getPort(args): + if '--port' in args: + index = next(i for i, arg in enumerate(args) if arg == '--port') + try: + return args[index+1] + except IndexError: + raise ValueError('port is empty') + return None source_types = ['ecg', 'eda', 'eeg', 'emg'] diff --git a/art-bio.py b/art-bio.py index be0ec6d..ca654ae 100644 --- a/art-bio.py +++ b/art-bio.py @@ -1,3 +1,4 @@ +# coding=UTF-8 readme = ''' 🐍 rt-bio : A Real-Time BIOsignal feature extraction tool 🦆 https://github.com/vatte/rt-bio @@ -42,6 +43,7 @@ import argparser as parser from devices import * +from devices.Biosignalsplux import Biosignalsplux from init_sources import init_sources from router import Router @@ -58,6 +60,7 @@ router.osc_address = '127.0.0.1:4810' router.osc_prefix = '/rtbio' router.filename = 'temp.txt' +router.ws_port = 5678 args = sys.argv @@ -82,6 +85,10 @@ device_list = OpenBCI.list_devices(None) for i, dev in enumerate(device_list): print('[{}] {}'.format(i, dev)) + elif(device_name == 'biosignalsplux'): + device_list = Biosignalsplux.list_devices(None) + for i, dev in enumerate(device_list): + print('[{}] {}'.format(i, dev)) else: raise ValueError('No such device: ' + device_name) print('exiting...') @@ -102,6 +109,11 @@ if filename_arg: router.filename = filename_arg +#get ws port from args +port_arg = parser.getPort(args) +if port_arg: + router.ws_port = port_arg + #get sampling_frequency from args sampling_frequency_arg = parser.getSamplingFrequency(args) @@ -134,6 +146,14 @@ def noDigital(): raise ValueError('openbci has no digital output defined') router.digital_out_func = noDigital +elif device_name == 'biosignalsplux': + device = Biosignalsplux(device_index) + if 'biosignalsplux' in config: + channels = {} + for k in config['biosignalsplux']: + channels[k] = [ int(c) for c in config['biosignalsplux'][k].split(',') ] + device.channel_map = channels + else: raise ValueError('No such device: ' + device_name) @@ -145,15 +165,34 @@ def noDigital(): #start streaming device.start(sampling_frequency, sources.keys()) +## filter experiment here for powerline +# from scipy import signal + +# b,a = signal.iirnotch(50, 25, 100) + +# print("B: " + str(b) + "A: " + str(a)) + +# z = [0, 0, 0, 0, 0, 0, 0, 0] +# for i, zz in enumerate(z): +# z[i] = signal.lfilter_zi(b, a) + try: while True: # Read samples samples = device.read() + #print(samples) for source in samples.keys(): features = [] for i, channel in enumerate(samples[source]): - features.append(sources[source][i].add_data(channel)) + #for j, s in enumerate(channel): + #y, z[i] = signal.lfilter(b,a,[s], zi = z[i]) + #channel[j] = y[0] + if source != 'unknown' and source in sources: + features.append(sources[source][i].add_data(channel)) router.route_data(source, connections, features, samples[source]) -except KeyboardInterrupt: +except (Exception, KeyboardInterrupt) as e: device.close() + if router.ws_server: + router.ws_server.stop() + raise(e) diff --git a/art-bio.yaml b/art-bio.yaml new file mode 100644 index 0000000..0f3ec35 --- /dev/null +++ b/art-bio.yaml @@ -0,0 +1,31 @@ +name: art-bio +channels: + - defaults +dependencies: + - blas=1.0=mkl + - ca-certificates=2021.10.26=haa95532_2 + - certifi=2021.10.8=py38haa95532_0 + - icc_rt=2019.0.0=h0cc432a_1 + - intel-openmp=2021.4.0=haa95532_3556 + - mkl=2021.4.0=haa95532_640 + - mkl-service=2.4.0=py38h2bbff1b_0 + - mkl_fft=1.3.1=py38h277e83a_0 + - mkl_random=1.2.2=py38hf11a4ad_0 + - numpy=1.21.2=py38hfca59bb_0 + - numpy-base=1.21.2=py38h0829f74_0 + - openssl=1.1.1l=h2bbff1b_0 + - pip=21.0.1=py38haa95532_0 + - pyserial=3.5=py38haa95532_0 + - python=3.8.12=h6244533_0 + - scipy=1.7.1=py38hbe87c03_2 + - six=1.16.0=pyhd3eb1b0_0 + - sqlite=3.36.0=h2bbff1b_0 + - vc=14.2=h21ff451_1 + - vs2015_runtime=14.27.29016=h5e58377_2 + - wheel=0.37.0=pyhd3eb1b0_1 + - wincertstore=0.2=py38haa95532_2 + - pip: + - bitalino==1.2.1 + - python-osc==1.8.0 + - setuptools==57.5.0 + - websockets==10.1 diff --git a/biosignals.bat b/biosignals.bat new file mode 100644 index 0000000..b94e080 --- /dev/null +++ b/biosignals.bat @@ -0,0 +1,5 @@ +@ECHO on +call C:\Users\MTG-user\anaconda3\Scripts\activate.bat activate art-bio +cd C:\Users\MTG-user\Desktop\art-bio-biosignalsplux +python art-bio.py -d biosignalsplux -c reeg ws -c recg ws -c reda ws -c reeg file -c recg file -c reda file --filename readings.txt +PAUSE \ No newline at end of file diff --git a/config.ini b/config.ini index 905c137..3b8861b 100644 --- a/config.ini +++ b/config.ini @@ -1,7 +1,12 @@ #bitalino channel configuration for custom setups #analog inputs are starting with index 0 [bitalino] -emg: 0 +emg: 0,4 ecg: 1 eda: 2 -eeg: 3 \ No newline at end of file +eeg: 3 + +[biosignalsplux] +eeg: 0,1 +ecg: 2 +eda: 3 \ No newline at end of file diff --git a/devices/Biosignalsplux.py b/devices/Biosignalsplux.py new file mode 100644 index 0000000..a1d26a3 --- /dev/null +++ b/devices/Biosignalsplux.py @@ -0,0 +1,133 @@ +# biosignalsplux + +import glob +import sys +import platform +import time + +from threading import Thread + +sys.path.append('devices/pluxlib') #add the plux library for your platform to this folder +import plux + +from .Device import Device + +class Biosignalsplux(Device): + + def __init__(self, dev_i = 0): + + #default channel numbers for biosignalsplux + self.channel_map = { + 'emg': [0], + 'ecg': [1], + 'eda': [2], + 'eeg': [3] + } + + # find device + print('Looking for biosignalsplux device') + retries = 0 + self.device_list = [] + + while retries < 3 and len(self.device_list) == 0: + self.device_list = self.list_devices() + + if len(self.device_list) == 0: + raise Exception('No biosignalsplux device found') + + try: + device_address = self.device_list[dev_i] + except IndexError: + raise Exception('No biosignalsplux found with index ' + str(dev_i)) + + print("Connecting to " + device_address) + + class PluxDevice(plux.SignalsDev): + def __init__(self, address): + plux.MemoryDev.__init__(address) + self.started = True + self.in_samples = [] + + def onRawFrame(self, nSeq, data): # onRawFrame takes three arguments + self.in_samples.append(data) + if not self.started: + return True + return False + + # Connect to biosignalsplux + self.device = PluxDevice(device_address) + + #find all devices + def list_devices(self): + devices = [] + for mac in plux.BaseDev.findDevices(): + devices.append(mac[0]) + return devices + + #start streaming + def start(self, fs, channels): + self.fs = fs + self.channels = [] + chans = [] + #order channels for biosignalsplux + for c in self.channel_map.keys(): + if c in channels: + for index in self.channel_map[c]: + chans.append(index) + chans.sort() + self.n_chans = chans[-1] + 1 + for c in range(self.n_chans): + found = False + for k in self.channel_map: + if c in self.channel_map[k]: + self.channels.append(k) + found = True + break + if not found: + self.channels.append('unknown') + print(self.channels) + + codes = [ 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF] #codes for biosignalsplux device for different n_chans + + # Start Acquisition + self.started = True + self.device.started = True + self.device.start(fs, codes[self.n_chans-1], 16) + + def myfunc(): + self.device.loop() + + t = Thread(target=myfunc) + t.start() + + + + #read samples (blocking) + def read(self): + samples = {} + + while len(self.device.in_samples) == 0: + time.sleep(0.01) + + for i, c in enumerate(self.channels): + if not c in samples: + samples[c] = [] + samples[c].append([s[i] for s in self.device.in_samples]) + #if 'eeg' in samples: + # samples['eeg'] = [ [ [samples['eeg'][i][j] for i in range(len(samples['eeg'])) ] for j in range(len(samples['eeg'][0]))] ] + #print(samples) + self.device.in_samples = [] # clear the sample buffer + return samples + + #stop streaming + def stop(self): + # Stop acquisition + self.started = False + self.device.started = False + + #close connection + def close(self): + if self.started: + self.started = False + self.device.started = False + \ No newline at end of file diff --git a/devices/pluxlib/LibFT4222-64.dll b/devices/pluxlib/LibFT4222-64.dll new file mode 100644 index 0000000..b385fcb Binary files /dev/null and b/devices/pluxlib/LibFT4222-64.dll differ diff --git a/devices/pluxlib/LibFT4222AB-64.dll b/devices/pluxlib/LibFT4222AB-64.dll new file mode 100644 index 0000000..0defcf9 Binary files /dev/null and b/devices/pluxlib/LibFT4222AB-64.dll differ diff --git a/devices/pluxlib/msvcp100.dll b/devices/pluxlib/msvcp100.dll new file mode 100644 index 0000000..6f0cdf1 Binary files /dev/null and b/devices/pluxlib/msvcp100.dll differ diff --git a/devices/pluxlib/msvcr100.dll b/devices/pluxlib/msvcr100.dll new file mode 100644 index 0000000..b1c3a5e Binary files /dev/null and b/devices/pluxlib/msvcr100.dll differ diff --git a/devices/pluxlib/plux.pyd b/devices/pluxlib/plux.pyd new file mode 100644 index 0000000..2fd7818 Binary files /dev/null and b/devices/pluxlib/plux.pyd differ diff --git a/examples/banana_demo/banana.jpg b/examples/banana_demo/banana.jpg new file mode 100644 index 0000000..d078ecb Binary files /dev/null and b/examples/banana_demo/banana.jpg differ diff --git a/examples/banana_demo/banana.png b/examples/banana_demo/banana.png new file mode 100644 index 0000000..ba6f3a1 Binary files /dev/null and b/examples/banana_demo/banana.png differ diff --git a/examples/banana_demo/heart.png b/examples/banana_demo/heart.png new file mode 100644 index 0000000..28dced8 Binary files /dev/null and b/examples/banana_demo/heart.png differ diff --git a/examples/banana_demo/lungs1.png b/examples/banana_demo/lungs1.png new file mode 100644 index 0000000..ea2904b Binary files /dev/null and b/examples/banana_demo/lungs1.png differ diff --git a/examples/banana_demo/superbanana.wav b/examples/banana_demo/superbanana.wav new file mode 100644 index 0000000..9b39993 Binary files /dev/null and b/examples/banana_demo/superbanana.wav differ diff --git a/examples/banana_demo/superbanana2.wav b/examples/banana_demo/superbanana2.wav new file mode 100644 index 0000000..ac8513f Binary files /dev/null and b/examples/banana_demo/superbanana2.wav differ diff --git a/examples/banana_demo/visualize_banana.html b/examples/banana_demo/visualize_banana.html new file mode 100644 index 0000000..348c08f --- /dev/null +++ b/examples/banana_demo/visualize_banana.html @@ -0,0 +1,190 @@ + + + + Raw data visualizer + + + +

Grab the banana! Firmly, but gently...

+ + + + + + + + + + + + \ No newline at end of file diff --git a/examples/banana_demo/visualize_biosignals.html b/examples/banana_demo/visualize_biosignals.html new file mode 100644 index 0000000..14a4019 --- /dev/null +++ b/examples/banana_demo/visualize_biosignals.html @@ -0,0 +1,422 @@ + + + + Raw data visualizer + + + + + +
La sesión ha iniciado.
+ +
+ +

Ritmo cardíaco

+ +

Respiración

+ + +

Alpha

+ + +
+
+ + + + + + + diff --git a/examples/receive_eda_to_pd.pd b/examples/receive_eda_to_pd.pd index 89d466e..735eb8c 100644 --- a/examples/receive_eda_to_pd.pd +++ b/examples/receive_eda_to_pd.pd @@ -1,31 +1,167 @@ -#N canvas 363 113 450 300 10; -#X obj 40 80 mrpeach/udpreceive 4810; +#N canvas 141 134 1477 835 10; #X obj 40 105 mrpeach/unpackOSC; #X floatatom 40 154 5 0 0 0 - - -; -#N canvas 0 23 450 300 smooth-more 0; +#N canvas 0 23 1215 808 smooth-more 0; #X obj 301 138 f; #X obj 252 222 +; #X obj 243 137 t f b; #X obj 234 52 inlet; #X obj 247 262 outlet; -#X obj 302 192 * 0.95; -#X obj 242 163 * 0.05; -#X connect 0 0 5 0; +#X obj 242 163 * 0.01; +#X obj 302 192 * 0.99; +#X connect 0 0 6 0; #X connect 1 0 0 1; #X connect 1 0 4 0; -#X connect 2 0 6 0; +#X connect 2 0 5 0; #X connect 2 1 0 0; #X connect 3 0 2 0; -#X connect 5 0 1 1; -#X connect 6 0 1 0; +#X connect 5 0 1 0; +#X connect 6 0 1 1; #X restore 40 177 pd smooth-more; #X floatatom 40 200 5 0 0 0 - - -; #X obj 40 130 route /rtbio/eda/raw /rtbio/eda/edr; #X obj 143 155 print EDR:; #X text 35 26 COMMAND: art-bio -c reda osc; -#X connect 0 0 1 0; -#X connect 1 0 5 0; +#N canvas 0 50 450 250 (subpatch) 0; +#X array array1 1000 float 1; +#A 0 35335.8 35380.2 35424.9 35469.7 35514.6 35559.4 35603.9 35648.6 +35693.6 35738.1 35782.2 35826.3 35870.7 35915.6 35960.3 36005 36049.1 +36093.4 36137.9 36182.3 36226.1 36269.6 36313.5 36357.3 36401 36444.7 +36488.2 36531.5 36574.8 36617.8 36661.1 36705 36748.2 36791.3 36834.5 +36877.5 36920.7 36963.6 37006.6 37049.8 37091.7 37134.4 37176.2 37218.2 +37261.1 37302.7 37344.8 37387.1 37429.2 37471.2 37512.5 37555 37596.3 +37638.3 37679.8 37721.3 37762.3 37804.6 37845.9 37887.4 37927.8 37968.3 +38008.6 38050.3 38091.9 38132.8 38172.7 38213.2 38253.7 38294.7 38334.8 +38375.4 38416.2 38456.8 38497.7 38537.7 38578.4 38618 38657.9 38696.9 +38736.8 38777.5 38817.4 38857.3 38895.9 38935.3 38975 39015.1 39055.9 +39096.9 39136.1 39175.5 39213.7 39252.8 39292.4 39332.9 39373.5 39413.2 +39452 39490.8 39530.7 39571.1 39611.8 39651.8 39691.7 39731 39769.7 +39809.4 39850.1 39890.1 39930 39968.6 40007.4 40046.8 40086.7 40126 +40164.9 40203.3 40242.1 40280.6 40318.8 40357.9 40396.8 40435 40472.6 +40509.7 40546.6 40583.8 40621.7 40659.4 40696.5 40733.5 40770 40806.7 +40843.1 40879.4 40916 40951.3 40986.8 41022.8 41057.8 41092.7 41127.7 +41162.4 41197.8 41232.5 41267.2 41301 41334.6 41369.4 41404 41437.7 +41472 41506.3 41539.6 41573.4 41607.7 41641.6 41675.3 41708 41740.8 +41773.8 41806.4 41839.1 41872 41904.3 41935.9 41967.5 41999.7 42031.2 +42063.3 42095.4 42127.4 42159 42190.1 42220.8 42251.8 42282.9 42313.7 +42344.2 42374.4 42404.8 42434.9 42465 42494.2 42524 42553.8 42583.4 +42613 42642.1 42670.6 42699.5 42728.7 42757.9 42786.4 42814.9 42843.4 +42870.9 42898.6 42926.6 42954.8 42982.9 43010.4 43038 43064.2 43092.2 +43120.1 43147.2 43174.8 43202.3 43229.8 43257 43284.4 43311.5 43338.1 +43364.4 43391.4 43418.4 43444.9 43472 43499.2 43525.6 43551 43577.8 +43603.9 43630.9 43657.7 43684.6 43710.3 43736.6 43763.3 43790.3 43817.3 +43844.1 43870.5 43896.2 43922.5 43948.2 43974.5 44000.8 44027.2 44053.2 +44079.3 44105.6 44131 44157 44182.5 44207.8 44233.5 44259.5 44285 44310.6 +44336.4 44361.3 44386.2 44411.8 44437.4 44462.9 44487.7 44512.9 44538 +44562.4 44586.6 44611.6 44636.4 44660.5 44685.2 44710.1 44734.4 44759 +44783.4 44807 44831.7 44856.1 44880.3 44904.3 44928.5 44952.4 44975.9 +44999.8 45023.8 45047.1 45071 45094.5 45117 45139.8 45163.2 45186.7 +45210.3 45233.9 45256.5 45279.9 45302.2 45325.4 45348.5 45369.9 45391.6 +45414.2 45436.6 45459.2 45481.6 45503.5 45525.6 45547.7 45569.7 45591.8 +45613.8 45635.5 45657.3 45678.7 45699.5 45721.4 45743.1 45764.9 45786.3 +45807.1 45827.6 45848.8 45869.9 45890.6 45911.4 45932.2 45952.1 45972.6 +45993.4 46013.4 46032.2 46051.1 46070.9 46091.3 46110.8 46131.3 46151.3 +46170.4 46189.9 46209.6 46229.2 46248.4 46268 46287.2 46306 46323.4 +46342.5 46362.1 46381.5 46400.4 46417.1 46434.4 46452.5 46471.4 46490.1 +46508.9 46527 46544.4 46561.3 46579.4 46597.8 46616.6 46634.8 46652.1 +46669.5 46686.7 46704.2 46722.2 46739.7 46756.9 46773.8 46790.6 46807.3 +46824.7 46841.8 46858.5 46875.4 46891.5 46907.8 46924.9 46942.1 46958.9 +46975.3 46991.1 47006.8 47022.3 47038.1 47054.7 47070.8 47086.5 47101.5 +47117.3 47132.7 47149 47164.9 47180.8 47196.6 47211.9 47227.7 47243.3 +47258.8 47274.5 47289.9 47305.3 47320.7 47335.6 47350.8 47365.9 47380.5 +47395.4 47410.4 47425.5 47440.2 47455.3 47469.6 47484.2 47498.8 47513 +47526.3 47541 47555.2 47569.7 47584 47598.5 47612.5 47626.8 47641 47655.3 +47669.5 47683.8 47697.8 47712.1 47725.9 47739.9 47753 47767.3 47780.9 +47794.9 47808.7 47822.5 47836.2 47850.3 47863.7 47877.4 47889.9 47903.6 +47917 47930.5 47943.8 47956.8 47969.8 47982.7 47995.7 48008.8 48022.5 +48035.1 48047.7 48060.3 48073.8 48086.8 48099.6 48112.3 48124.9 48137.3 +48150.3 48162.9 48175.6 48188.4 48201.5 48213.8 48225.9 48238.6 48250.9 +48263.7 48277 48289.6 48302.1 48314.6 48325.6 48338.3 48351.1 48363.8 +48376 48388.6 48400.6 48411.3 48423.8 48436.6 48448.5 48458.9 48470.9 +48482.5 48494.6 48506.4 48518.5 48530.9 48542.8 48554.8 48566.5 48578.3 +48589.8 48600.6 48611.8 48623 48634.3 48645.5 48657 48668.2 48679.2 +48689.8 48700.4 48710.8 48721.4 48732.1 48741.9 48752.2 48762.2 48772.3 +48782.5 48792.3 48802.4 48811.5 48821.7 48831.3 48840.8 48850.1 48860.1 +48870.2 48880.1 48890.1 48900 48909.4 48918.7 48928.4 48937.5 48947.2 +48956.7 48966.1 48975.4 48984.5 48993.5 49002.1 49010.8 49019.7 49028.6 +49037.4 49046.3 49055 49063.5 49071.2 49079.3 49087.1 49095.6 49103.9 +49112.5 49119.2 49127.4 49135.8 49143.1 49151.3 49159.7 49168.5 49176.6 +49184.6 49193 49201.5 49209.6 49217.6 49225.8 49233.7 49241.5 49249.2 +49256.9 49264.9 49272.4 49280.5 49288.6 49296.4 49304.2 49312 49320.4 +49328.2 49333.4 49341.3 49347.1 49354.3 49361.4 49369.8 49378.5 49387 +49394.8 49401.6 49408.2 49414.6 49422.1 49429.5 49437.9 49445.9 49453 +49459 49465.3 49471.1 49479.3 49487.1 49494.7 49501 49507.3 49513.9 +49520.7 49528.2 49535.3 49540.5 49547.4 49553.3 49559.7 49565.6 49572.3 +49578.4 49584.6 49591.2 49596.2 49602.1 49608.2 49614 49620.3 49626.6 +49632.7 49638.1 49643.4 49649.1 49654 49659.6 49665.2 49670.8 49676.1 +49681.4 49686.5 49691.3 49696.8 49701.5 49706.5 49711.3 49715.9 49720.4 +49725.2 49729.9 49733.9 49738 49741.9 49744.6 49748.4 49752.4 49756.5 +49760.3 49763.1 49765.2 49768 49771.5 49774.9 49777.9 49780.2 49781.5 +49784.4 49787.1 49789.6 49792.4 49794.9 49797.4 49799.8 49801.5 49803.4 +49805.4 49806.7 49808.7 49810.7 49811.9 49813.7 49815.1 49816.4 49818.1 +49820.1 49821.7 49822.8 49824 49824.9 49826.2 49827.5 49829 49829.6 +49829.7 49830.4 49829.9 49831 49831.8 49832.6 49833 49833 49833.5 49834.2 +49834.6 49835.5 49834.1 49834.3 49834.8 49834.7 49835.2 49835.5 49835.9 +49835.7 49836 49836 49835.9 49834.5 49834.8 49834.5 49834.3 49834.4 +49834.3 49834.1 49834 49833.8 49832.9 49832 49831.2 49830.5 49829.9 +49829.3 49828.6 49827.9 49826.9 49825.9 49825 49823.9 49822.7 49821.7 +49820.9 49819.9 49818.7 49817.5 49815.7 49814.4 49812.7 49811.2 49810 +49808.1 49805.5 49803.8 49801.5 49799.9 49797.4 49795.3 49793.6 49791.3 +49788.6 49786.5 49784.8 49782.8 49780.4 49777.7 49775.6 49773.3 49770.5 +49768.4 49766.2 49763.6 49760.6 49757.8 49754.8 49751.4 49748.8 49745.4 +49742.9 49740.2 49737.8 49735.2 49731.5 49728.9 49726.1 49723.5 49721 +49717.5 49713.9 49711.4 49708.6 49705.8 49703 49699.8 49696.8 49693.8 +49690.3 49687.3 49684.3 49681.3 49678.3 49675.1 49671.8 49667.5 49664.1 +49660.3 49657.2 49654.1 49651.2 49648.5 49645.3 49642.4 49638.4 49635.4 +49631.6 49628.5 49624.7 49621.6 49618.5 49614.5 49611.1 49607.9 49604.6 +49600.8 49597.3 49593.5 49590 49586.5 49583 49579.1 49575.1 49571.2 +49567.2 49563.2 49559.2 49555.4 49551.5 49547.6 49543.5 49538.9 49534.7 +49530.8 49526.3 49521.9 49517.9 49514 49509.8 49505.8 49501.2 49497.2 +49493.3 49489 49485.4 49481.3 49477.3 49472.4 49468.6 49462 49458.3 +49454.4 49450 49446.4 49442.6 49438.7 49433.3 49429.1 49424.9 49419 +49415.1 49411.1 49407.3 49403.3 49399.3 49395.4 49391 49387 49383.1 +49378.1 49372.8 49368.8 49364.6 49360.4 49355.9 49351.5 49345.2 49341.1 +49336.5 49331.8 49327.1 49322.9 49318.2 49312.4 49308.3 49303.9 30538 +30568.7 30599.5 30630 30661.5 30693.2 30723.6 30755.7 30788.7 30821.3 +30855 30888.6 30923.4 30958.7 30992.8 31027.6 31062.3 31097.4 31132.9 +31168.5 31204.4 31240.2 31276.5 31313.1 31349.9 31386.9 31424.4 31462.2 +31499.6 31537.5 31575 31612.2 31651.7 31690.2 31728.3 31767.4 31807 +31846.2 31885.7 31926.1 31966.8 32007.4 32048.2 32089.6 32131.4 32174.4 +32216.7 32259.2 32302.8 32345.9 32389.6 32433.9 32478 32520.6 32563.7 +32607 32651.7 32695.5 32739.6 32783.6 32827.8 32871.3 32915.5 32957.8 +33000.9 33044.1 33088.3 33132.7 33176.5 33220.7 33264.1 33306.5 33350.6 +33394.3 33437.7 33480.5 33523.8 33566.1 33610 33652.9 33696.9 33740.1 +33783.5 33827.3 33870 33914.1 33956.3 34002 34046.2 34090.8 34135 34177.4 +34222.4 34265.5 34310.1 34355 34399.7 34443.9 34487.7 34532.2 34577.9 +34622.9 34667.5 34712.4 34756 34800.8 34845.5 34889.3 34933.4 34977.8 +35022.3 35067.3 35112 35156.2 35201.1 35246.2 35290.8; +#X coords 0 65000 999 0 600 400 1 0 0; +#X restore 432 136 graph; +#X obj 223 413 tabwrite array1; +#X obj 270 309 + 1; +#X obj 203 313 0; +#X obj 191 235 t b; +#X obj 232 499 print; +#X obj 310 344 % 1000; +#X obj 47 263 send eda; +#X obj 365 59 r data; +#X obj 36 327 send fundamental; +#X obj 36 295 / 80; +#X obj 41 80 mrpeach/udpreceive 4811; +#X connect 0 0 4 0; +#X connect 1 0 2 0; #X connect 2 0 3 0; -#X connect 3 0 4 0; -#X connect 5 0 2 0; -#X connect 5 1 6 0; +#X connect 3 0 11 0; +#X connect 3 0 8 0; +#X connect 3 0 14 0; +#X connect 3 0 17 0; +#X connect 4 0 1 0; +#X connect 4 1 5 0; +#X connect 9 0 13 0; +#X connect 10 0 9 0; +#X connect 10 0 8 1; +#X connect 11 0 10 0; +#X connect 13 0 10 1; +#X connect 15 0 4 0; +#X connect 17 0 16 0; +#X connect 18 0 0 0; diff --git a/examples/receive_eeg_to_pd.pd b/examples/receive_eeg_to_pd.pd index f6fbb80..6cf816d 100644 --- a/examples/receive_eeg_to_pd.pd +++ b/examples/receive_eeg_to_pd.pd @@ -1,4 +1,4 @@ -#N canvas 159 56 1212 620 10; +#N canvas 58 261 1247 635 10; #X obj 37 48 mrpeach/unpackOSC; #X floatatom 37 134 10 0 0 0 - - -; #X floatatom 102 134 10 0 0 0 - - -; @@ -10,39 +10,47 @@ #N canvas 0 50 1377 540 graph 0; #N canvas 350 172 1009 430 (subpatch) 0; #X array \$0_array 500 float 1; -#A 0 -76 -87 -65 -205 -281 -428 -464 -150 87 506 506 506 506 506 506 -506 506 506 506 506 506 506 506 506 506 506 238 55 -136 165 -100 -145 --180 -84 -119 -135 130 53 -40 132 212 243 341 85 328 323 188 506 251 -233 166 -48 -163 -252 -241 -199 -185 -119 -182 -181 -204 -281 -324 --281 -269 -313 -50 -236 -218 -189 -277 -339 -261 -190 -398 -452 -426 --344 -245 -181 -277 -241 -281 -248 -299 -91 -42 8 -12 -93 -189 -165 --206 -194 -203 -147 -129 -81 -40 -101 -143 15 24 22 12 83 23 -150 -104 --224 -124 -1 -205 -196 -180 -279 -229 -215 -295 -208 -188 -44 -148 --157 -210 -250 -305 -177 -180 -127 -262 -235 -186 -23 24 64 65 197 -341 327 175 162 112 6 28 147 53 -71 -128 -127 -254 -85 -76 -96 -512 --512 -512 -512 -512 -512 -512 -512 -512 -512 -512 -512 -467 -243 -288 -121 11 105 266 386 476 506 388 506 506 506 506 506 267 506 453 506 -492 506 506 506 167 -33 -115 -63 -139 -349 -512 -512 -512 -512 -512 --512 -512 -512 -512 -75 506 506 506 506 506 506 506 506 506 506 506 -506 506 506 506 506 506 506 506 506 506 506 506 506 476 348 213 246 -283 199 85 71 100 138 217 134 208 163 223 64 49 99 110 167 105 191 -187 164 229 316 331 206 263 199 69 -60 -161 -186 -402 -287 -151 -168 --298 -244 -229 -174 -203 -168 -288 -304 -415 -383 -336 -328 -333 -166 --167 -261 -458 -467 -447 -341 -302 -250 -251 -301 -478 -186 -289 -128 --305 -209 -212 -111 -178 -154 -383 -512 -428 -344 -190 -187 -123 -28 -66 101 72 75 129 52 -25 -112 -261 -377 -410 -477 -499 -303 -282 -316 --276 -237 -107 -200 -210 -220 -98 -88 -50 -33 -73 -72 39 63 -18 106 -46 243 -62 17 62 231 -512 -512 -512 -512 -512 -512 -512 -512 -353 -512 --512 -512 -433 -328 -257 -157 -68 -133 -58 182 75 277 418 149 -184 --512 -512 92 -276 -42 -147 -127 -316 -360 -280 -37 -409 -96 102 78 --61 309 326 -156 -100 -128 -208 -187 -336 -260 -136 -466 -323 -428 --319 111 -319 -208 -42 97 -221 -177 -130 -217 -180 -83 81 -17 66 60 -10 169 181 129 276 335 394 287 143 337 177 211 342 346 391 106 126 -506 468 399 494 441 370 437 329 485 432 443 506 354 506 317 215 506 -368 287 372 408 396 18 91 36 222 214 29 289 321 338 314 244 238 219 -49 86 192 352 76 333 273 159 179 150 111 480 177 101 134 155 161 217 -53 225 177 130 22 82 119 225 -13 -98 -51 -80; -#X coords 0 400 499 -400 500 100 1 0 0; +#A 0 64920 -466 64928 -464 64932 -463 64930 -464 64948 -464 64936 -466 +-465 -465 -464 -463 64924 -462 64936 -466 64936 -460 -466 -463 -466 +-465 64932 -465 64932 64920 -464 -466 64928 -462 64928 -465 64928 -466 +-465 -460 -465 -461 -462 -463 -462 64940 64936 64936 64936 64936 64936 +-464 64912 -466 -465 -464 -466 -464 64928 -465 64936 -464 -462 -464 +64924 -462 64936 -466 -460 -466 64928 -460 -466 -464 64928 -466 64940 +-464 -465 -464 -465 -466 64928 -463 64952 -466 -465 -466 -466 -464 +64920 -461 -465 -465 -466 64912 64916 64932 64936 -462 64940 -465 -464 +-466 -465 64936 64920 -461 -459 -466 -464 -464 -465 61280 64932 64928 +64928 -466 64936 -466 64924 -465 -465 -466 64940 -466 -464 -466 -465 +-465 -466 64944 64928 64924 64940 64920 64936 64932 64932 -463 -459 +-465 -465 -467 -467 -466 -466 -464 -466 -466 -465 -465 -466 64920 64928 +-459 -464 -466 64928 64925 64928 64932 64940 -465 64950 -464 -466 -466 +-466 -465 64920 -465 64936 -465 -464 -467 64916 -460 -464 -464 64928 +-460 -464 -465 -465 -465 -461 -464 64940 64944 64940 64936 -465 -465 +-465 64924 -464 64924 -461 -465 64932 64932 -464 -462 -466 -466 64924 +64928 64928 64928 64936 64924 64924 64936 64924 64924 64940 64920 64944 +64928 -466 -466 -462 -465 -466 -464 -465 64928 64936 -461 -464 -465 +-464 -465 -465 -465 -466 -466 64932 64936 64936 64948 -455 -464 -464 +-466 64928 64928 64944 64932 -462 -465 -465 -465 -466 -465 -466 -465 +64916 64920 -465 64920 64928 64924 64924 64928 64916 64932 64940 -455 +-464 -466 -465 -464 64936 64932 -465 -465 64944 -464 -466 -465 64928 +64936 64944 64928 64936 64924 64928 64928 -465 -465 64920 64928 -464 +64920 64932 64940 64928 64952 64932 64932 64932 64924 64924 64920 64932 +64944 64924 64932 64928 64936 64936 64928 64940 64933 64936 64940 -461 +-465 -465 -465 -466 64934 -463 -465 -465 -464 -464 -465 -464 -466 64920 +64912 64936 64936 64940 64924 64940 64932 64936 64932 64938 64940 64932 +-459 -466 -464 -465 -466 -466 -465 -466 -466 -466 -464 -465 -465 -465 +-466 -464 -466 -467 -466 -464 -466 -465 -466 -464 -465 -466 -464 -465 +-466 -464 -465 -465 -466 -465 -466 -466 -465 -465 -464 -465 -466 64928 +64940 64932 64936 64936 64940 64946 64948 64932 64936 64944 64928 64928 +64944 64928 64936 64936 64940 64932 64940 64928 64944 64932 64936 64936 +64944 64928 -464 -465 -466 -465 -465 -464 64924 64928 64940 64928 64944 +64932 64936 64928 64944 64936 64940 64932 64928 64936 64936 64940 64932 +64944 64932 64936 64940 64940 64932 64928 64920 64936 64920 64936 64936 +64936 64944 64936 64941 64936 64932 64928 64936 64940 64940 64936 64936 +64936 64928 64920 64936 64936 64944 64940 64928 64928 64928 64920 64940 +64932 64924 64924 64932 64932 64932 64912 -465 64944 -465 64932 -465 +64932 -463 -462 -465 -463 -462 -465 44308 64936 64928 64932 -464 64936 +-465 -465 -466; +#X coords 0 65000 499 0 690 170 1 0 0; #X restore 304 124 graph; #X obj 172 301 + 1; #X obj 155 258 0; @@ -59,34 +67,41 @@ #X connect 4 0 2 1; #X connect 6 0 7 0; #X connect 7 0 3 0; -#X coords 0 -1 1 1 510 130 1 300 100; +#X coords 0 0 1 100000 700 200 1 300 100; #X restore 299 189 pd graph; #N canvas 0 23 450 300 baseline 0; #X obj 84 90 f; #X obj 107 42 inlet; #X obj 146 126 +; -#X obj 146 102 * 0.001; -#X obj 177 127 * 0.999; #X obj 69 49 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X text 39 50 init; #X obj 156 185 outlet; -#X connect 0 0 4 0; -#X connect 0 0 3 0; +#X obj 146 102 * 0.001; +#X obj 178 127 * 0.999; +#X connect 0 0 6 0; +#X connect 0 0 7 0; #X connect 1 0 0 1; -#X connect 1 0 3 0; -#X connect 2 0 4 0; +#X connect 1 0 6 0; +#X connect 2 0 5 0; #X connect 2 0 7 0; -#X connect 3 0 2 0; -#X connect 4 0 2 1; -#X connect 5 0 0 0; +#X connect 3 0 0 0; +#X connect 6 0 2 0; +#X connect 7 0 2 1; #X coords 0 -1 1 1 60 40 1 30 30; #X restore 52 157 pd baseline; #X obj 45 205 -; #X floatatom 45 235 5 0 0 0 - - -; +#X obj 70 318 s alpha; +#X floatatom 80 200 5 0 0 0 - - -; +#X obj 464 67 s data; +#X obj 187 219 vsl 50 300 100 300 0 0 empty empty empty 0 -9 0 10 -262144 +-1 -1 16998 1; #X connect 0 0 6 0; +#X connect 0 0 13 0; #X connect 1 0 8 0; #X connect 1 0 9 0; +#X connect 1 0 14 0; #X connect 5 0 0 0; #X connect 6 0 1 0; #X connect 6 1 2 0; @@ -94,4 +109,6 @@ #X connect 6 3 4 0; #X connect 6 4 7 0; #X connect 8 0 9 1; +#X connect 8 0 12 0; #X connect 9 0 10 0; +#X connect 10 0 11 0; diff --git a/examples/visualize.html b/examples/visualize.html index 45fc613..5c27e35 100644 --- a/examples/visualize.html +++ b/examples/visualize.html @@ -3,7 +3,7 @@ Raw data visualizer - + diff --git a/init_sources.py b/init_sources.py index d2bcae2..78b731c 100644 --- a/init_sources.py +++ b/init_sources.py @@ -19,10 +19,7 @@ def init_sources(connections, channel_map, fs): elif source == 'emg': sources[source].append(MusclePower(fs)) elif source == 'eeg': - #have to get eeg chan numbers from somewhere FIX THIS - num_chans = 1 if fs == 100 else 8 - print('number of chans {}'.format(num_chans)) - sources[source].append(Oscillations(fs, num_chans, 256, 0.8, + sources[source].append(Oscillations(fs, 256, 0.8, { 'theta': [4.0, 8.0], 'alpha': [8.0, 13.0], diff --git a/physiology/HeartRate.py b/physiology/HeartRate.py index a1ab578..a5be043 100755 --- a/physiology/HeartRate.py +++ b/physiology/HeartRate.py @@ -10,27 +10,35 @@ class HeartRate(DataCollector): def __init__(self, _Fs, _filter_r): - DataCollector.__init__(self, _Fs) self.filter_r = _filter_r + self.Fs = _Fs + self.initialize() + + + def initialize(self): + DataCollector.__init__(self, self.Fs) + + #todo: stores unnecessarily all the previous data + self.clead = [] self.filtered_clead = [] - self.emg_filter_r = int(0.5*_Fs/34) - self.clead_filter_r = int(0.5*_Fs/24) + self.emg_filter_r = int(0.5*self.Fs/34) + self.clead_filter_r = int(0.5*self.Fs/24) self.initialized = False self.init_time = 5 self.beats = [] self.last = 0 - self.detect_interval = int(0.2*_Fs) + self.detect_interval = int(0.2*self.Fs) self.r = 0 self.rr = 0 self.RR = [0, 0, 0, 0, 0] self.r_i = -1 - self.f_interval = int(_Fs*0.35) - self.f_interval2 = int(_Fs*0.05) + self.f_interval = int(self.Fs*0.35) + self.f_interval2 = int(self.Fs*0.05) def add_data(self, new_data): self.data.extend(new_data) @@ -57,6 +65,16 @@ def add_data(self, new_data): ibis = self.detect_beats(0) elif self.initialized: ibis = self.detect_beats(last_clead_i) + + #if len(ibis) > 0: + # print(ibis) + + #reinitialize if no beats are detected for duration of self.init_time + if len(self.filtered_clead) - self.last > self.init_time * self.Fs: + print('re-initializing...') + self.initialize() + print(len(self.filtered_clead), self.last) + return {'ibi': ibis} diff --git a/physiology/Oscillations.py b/physiology/Oscillations.py index 62d91a6..25bf652 100644 --- a/physiology/Oscillations.py +++ b/physiology/Oscillations.py @@ -1,25 +1,20 @@ import numpy as np class Oscillations: - def __init__(self, sample_rate, num_chans, fft_size, overlap, freqs): + def __init__(self, sample_rate, fft_size, overlap, freqs): self.idx = 0 self.sample_rate = sample_rate - self.num_chans = num_chans self.fft_size = fft_size self.overlap = overlap self.hanning = np.hanning(fft_size) self.pad = np.zeros(fft_size) self.freqs = freqs - self.spectdata = [np.zeros(fft_size) for _ in range(num_chans)] + self.spectdata = np.zeros(fft_size) def add_data(self, samples): freqs = None for i in range(len(samples)): - #out = [] - #for c in samples: - # out.append(c[i]) - #freqs = self.add_samples(out) - freqs_in = self.add_samples(samples[i]) + freqs_in = self.add_sample(samples[i]) #freqs = freqs_in if freqs_in != None else freqs if freqs_in != None: if freqs == None: @@ -32,36 +27,34 @@ def add_data(self, samples): return freqs - def add_samples(self, samples): - for i, sample in enumerate(samples): - self.spectdata[i][self.idx] = sample + def add_sample(self, sample): + self.spectdata[self.idx] = sample self.idx += 1 if self.idx >= self.fft_size: - result = [[] for i in range(self.num_chans)] + #result = [[] for i in range(self.num_chans)] freqpower = {} for f in self.freqs: - freqpower[f] = [0 for _ in range(len(samples))] - raw_electrodes = [[] for _ in range(len(self.spectdata))] - for i, electrode_data in enumerate(self.spectdata): - windowed = electrode_data * self.hanning - padded = np.append(windowed, self.pad) - spectrum = np.fft.fft(padded) - autopower = abs(spectrum * np.conj(spectrum)) - result[i] = autopower[:self.fft_size] - - powerspect = 10.0 * np.log10(np.power(abs(spectrum),2)) - freqC = np.fft.fftfreq(len(padded), 1.0/self.sample_rate) - - for f in self.freqs: - freq = self.freqs[f] - start = np.where((freqC <= freq[0]) & (freqC >= 0))[0][-1] - stop = np.where((freqC >= freq[1]) & (freqC >= 0))[0][0] - freqpower[f][i] = np.mean(powerspect[start:stop]) + np.mean(powerspect[-stop-1:-start-1]) - - self.idx = int(self.fft_size * self.overlap) - raw_electrodes[i] = [float(electrode_data[a]) for a in range(self.idx, len(electrode_data))] - if self.idx > 0: - electrode_data[:self.idx] = electrode_data[-self.idx:] + freqpower[f] = 0 + #raw_electrode = [] + windowed = self.spectdata * self.hanning + padded = np.append(windowed, self.pad) + spectrum = np.fft.fft(padded) + autopower = abs(spectrum * np.conj(spectrum)) + result = autopower[:self.fft_size] + + powerspect = 10.0 * np.log10(np.power(abs(spectrum),2)) + freqC = np.fft.fftfreq(len(padded), 1.0/self.sample_rate) + + for f in self.freqs: + freq = self.freqs[f] + start = np.where((freqC <= freq[0]) & (freqC >= 0))[0][-1] + stop = np.where((freqC >= freq[1]) & (freqC >= 0))[0][0] + freqpower[f] = np.mean(powerspect[start:stop]) + np.mean(powerspect[-stop-1:-start-1]) + + self.idx = int(self.fft_size * self.overlap) + #raw_electrode = [float(self.spectdata[a]) for a in range(self.idx, len(self.spectdata))] + if self.idx > 0: + self.spectdata[:self.idx] = self.spectdata[-self.idx:] return freqpower return None diff --git a/router.py b/router.py index c518ee5..7eae571 100644 --- a/router.py +++ b/router.py @@ -15,10 +15,14 @@ def __init__(self): self.osc_prefix = None self.osc_address = None self.filename = None + self.ws_port = None def digital_out(): pass self.digital_out_func = digital_out + #self.current_time = time.time() + self.ini_time = 0 + def init_destinations(self, connections): destinations = [] @@ -31,54 +35,63 @@ def init_destinations(self, connections): self.osc_client = udp_client.SimpleUDPClient(address_port[0], int(address_port[1])) if 'file' in destinations: self.file_handle = open(self.filename, 'w') - print("initializing file " + self.filename) + print("initializing file" + self.filename) + + self.ini_time = time.time() + self.file_handle.write('{}: {}\n'.format('initial time',self.ini_time)) + + if 'ws' in destinations: - self.ws_server = WSServer() + self.ws_server = WSServer(self.ws_port) def route_data(self, source, connections, features, raw_data): source_connections = [] + + current_time = time.time() + if source in connections: source_connections += connections[source] - current_time = time.time() - + + # raw data if 'r' + source in connections: source_connections += connections['r' + source] - for destination in connections['r' + source]: - osc_dest = '/{}/raw'.format(source) - if destination == 'osc': - for i, channel in enumerate(raw_data): + for destination in connections['r' + source]: + for i, channel in enumerate(raw_data): + src = source + if i > 0: + src += str(i) + + osc_dest = '/{}/raw'.format(src) + if destination == 'osc': for data in channel: osc_address = self.osc_prefix + osc_dest - if i > 0: - osc_address += str(i) self.osc_client.send_message(osc_address, data) - elif destination == 'file': - self.file_handle.write('{} {}: {}\n'.format(current_time, osc_dest, str(raw_data))) - elif destination == 'ws': - self.ws_server.add_message('{}|{}/{}'.format(current_time, osc_dest, str(raw_data))) + elif destination == 'file': + self.file_handle.write('{} {}: {}\n'.format(current_time-self.ini_time, osc_dest, str(raw_data))) + elif destination == 'ws': + self.ws_server.add_message('{}|{}/{}'.format(current_time, osc_dest, str(raw_data))) # features if features: for destination in source_connections: for i, features_chan in enumerate(features): - for feature in features_chan.keys(): - osc_dest = '/{}/{}'.format(source, feature) - if i > 0: - osc_dest += str(i) - for data in features_chan[feature]: - if destination == 'osc': - self.osc_client.send_message(self.osc_prefix + osc_dest, data) - elif destination == 'file': - self.file_handle.write('{} {}: {}\n'.format(current_time, osc_dest, str(data))) - elif destination == 'ws': - self.ws_server.add_message('{}|{}/{}'.format(current_time, osc_dest, str(raw_data))) - elif destination == 'digital': - if self.digital_out_func: - self.digital_out_func() - - - + src = source + if i > 0: + src += str(i) + if features_chan: + for feature in features_chan.keys(): + osc_dest = '/{}/{}'.format(src, feature) + for data in features_chan[feature]: + if destination == 'osc': + self.osc_client.send_message(self.osc_prefix + osc_dest, data) + elif destination == 'file': + self.file_handle.write('{} {}: {}\n'.format(current_time-self.ini_time, osc_dest, str(data))) + elif destination == 'ws': + self.ws_server.add_message('{}|{}/{}'.format(current_time, osc_dest, str(data))) + elif destination == 'digital': + if self.digital_out_func: + self.digital_out_func() diff --git a/wsserver.py b/wsserver.py index 61a6187..ede4c46 100644 --- a/wsserver.py +++ b/wsserver.py @@ -2,20 +2,28 @@ import asyncio import websockets from threading import Thread - +import ssl +import pathlib all_websockets = [] + class WSServer(): - def __init__(self): + def __init__(self, port = 5678): self.all_websockets = [] - self.run() + self.loop = None + #self.ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + #path_cert = pathlib.Path(__file__).with_name("cert.pem") + #path_key = pathlib.Path(__file__).with_name("key.pem") + #self.ssl_context.load_cert_chain(path_cert, keyfile=path_key) + + self.run(port) def add_message(self, message): for websocket in self.all_websockets: websocket.art_bio_messages.append(message) - def run(self): + def run(self, port): async def data(websocket, path): self.all_websockets.append(websocket) websocket.art_bio_messages = [] @@ -27,15 +35,22 @@ async def data(websocket, path): await asyncio.sleep(0.1) def run_server(loop): + print('websocket server starting on port ' + str(port)) asyncio.set_event_loop(loop) - server = websockets.serve(data, '127.0.0.1', 5678) + #server = websockets.serve(data, '127.0.0.1', port, ssl=self.ssl_context) + server = websockets.serve(data, '127.0.0.1', port) loop.run_until_complete(server) loop.run_forever() - loop = asyncio.new_event_loop() - thread = Thread(target = run_server, args=(loop,)) + self.loop = asyncio.new_event_loop() + thread = Thread(target = run_server, args=(self.loop,)) thread.start() + def stop(self): + self.loop.call_soon_threadsafe(self.loop.stop) + self.loop.call_soon_threadsafe(self.loop.close) + #self.loop.close() + if __name__ == "__main__": wsserver = WSServer() @@ -44,4 +59,4 @@ def run_server(loop): while True: wsserver.add_message('/ecg/raw/[1 2 3 4 5]') - time.sleep(1) \ No newline at end of file + time.sleep(1)