diff --git a/Code/Stimulator/README.MD b/Code/Stimulator/README.MD new file mode 100644 index 0000000..f9ca488 --- /dev/null +++ b/Code/Stimulator/README.MD @@ -0,0 +1,8 @@ +## The python wrapper is coming from: the [Human Computer Integration Lab, Chicago](https://github.com/humancomputerintegration/rehamove-integration-lib) + # please read the license and limitations + +## Notes: +- had to disable USB sleep in power settings on a windows laptop to keep the rehastim working reliably +- one of the usb port is not working reliably, even with the new power settings (need to use the left port on ABD's laptop) +- the pre-compiled python library for windows that is available on github requires python 3.7 (otherwise recompile) +- keep using the low-level mode, to send individual pulses diff --git a/Code/Stimulator/libs/linux/_rehamovelib.so b/Code/Stimulator/libs/linux/_rehamovelib.so new file mode 100755 index 0000000..e8c559f Binary files /dev/null and b/Code/Stimulator/libs/linux/_rehamovelib.so differ diff --git a/Code/Stimulator/libs/rehamove.py b/Code/Stimulator/libs/linux/rehamove.py old mode 100755 new mode 100644 similarity index 100% rename from Code/Stimulator/libs/rehamove.py rename to Code/Stimulator/libs/linux/rehamove.py diff --git a/Code/Stimulator/libs/linux/rehamovelib.py b/Code/Stimulator/libs/linux/rehamovelib.py new file mode 100644 index 0000000..2732024 --- /dev/null +++ b/Code/Stimulator/libs/linux/rehamovelib.py @@ -0,0 +1,210 @@ +# This file was automatically generated by SWIG (http://www.swig.org). +# Version 3.0.12 +# +# Do not make changes to this file unless you know what you are doing--modify +# the SWIG interface file instead. + +from sys import version_info as _swig_python_version_info +if _swig_python_version_info >= (2, 7, 0): + def swig_import_helper(): + import importlib + pkg = __name__.rpartition('.')[0] + mname = '.'.join((pkg, '_rehamovelib')).lstrip('.') + try: + return importlib.import_module(mname) + except ImportError: + return importlib.import_module('_rehamovelib') + _rehamovelib = swig_import_helper() + del swig_import_helper +elif _swig_python_version_info >= (2, 6, 0): + def swig_import_helper(): + from os.path import dirname + import imp + fp = None + try: + fp, pathname, description = imp.find_module('_rehamovelib', [dirname(__file__)]) + except ImportError: + import _rehamovelib + return _rehamovelib + try: + _mod = imp.load_module('_rehamovelib', fp, pathname, description) + finally: + if fp is not None: + fp.close() + return _mod + _rehamovelib = swig_import_helper() + del swig_import_helper +else: + import _rehamovelib +del _swig_python_version_info + +try: + _swig_property = property +except NameError: + pass # Python < 2.2 doesn't have 'property'. + +try: + import builtins as __builtin__ +except ImportError: + import __builtin__ + +def _swig_setattr_nondynamic(self, class_type, name, value, static=1): + if (name == "thisown"): + return self.this.own(value) + if (name == "this"): + if type(value).__name__ == 'SwigPyObject': + self.__dict__[name] = value + return + method = class_type.__swig_setmethods__.get(name, None) + if method: + return method(self, value) + if (not static): + if _newclass: + object.__setattr__(self, name, value) + else: + self.__dict__[name] = value + else: + raise AttributeError("You cannot add attributes to %s" % self) + + +def _swig_setattr(self, class_type, name, value): + return _swig_setattr_nondynamic(self, class_type, name, value, 0) + + +def _swig_getattr(self, class_type, name): + if (name == "thisown"): + return self.this.own() + method = class_type.__swig_getmethods__.get(name, None) + if method: + return method(self) + raise AttributeError("'%s' object has no attribute '%s'" % (class_type.__name__, name)) + + +def _swig_repr(self): + try: + strthis = "proxy of " + self.this.__repr__() + except __builtin__.Exception: + strthis = "" + return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) + +try: + _object = object + _newclass = 1 +except __builtin__.Exception: + class _object: + pass + _newclass = 0 + +class RehamoveDevice(_object): + __swig_setmethods__ = {} + __setattr__ = lambda self, name, value: _swig_setattr(self, RehamoveDevice, name, value) + __swig_getmethods__ = {} + __getattr__ = lambda self, name: _swig_getattr(self, RehamoveDevice, name) + __repr__ = _swig_repr + __swig_setmethods__["device"] = _rehamovelib.RehamoveDevice_device_set + __swig_getmethods__["device"] = _rehamovelib.RehamoveDevice_device_get + if _newclass: + device = _swig_property(_rehamovelib.RehamoveDevice_device_get, _rehamovelib.RehamoveDevice_device_set) + __swig_setmethods__["battery"] = _rehamovelib.RehamoveDevice_battery_set + __swig_getmethods__["battery"] = _rehamovelib.RehamoveDevice_battery_get + if _newclass: + battery = _swig_property(_rehamovelib.RehamoveDevice_battery_get, _rehamovelib.RehamoveDevice_battery_set) + __swig_setmethods__["mode"] = _rehamovelib.RehamoveDevice_mode_set + __swig_getmethods__["mode"] = _rehamovelib.RehamoveDevice_mode_get + if _newclass: + mode = _swig_property(_rehamovelib.RehamoveDevice_mode_get, _rehamovelib.RehamoveDevice_mode_set) + __swig_setmethods__["current"] = _rehamovelib.RehamoveDevice_current_set + __swig_getmethods__["current"] = _rehamovelib.RehamoveDevice_current_get + if _newclass: + current = _swig_property(_rehamovelib.RehamoveDevice_current_get, _rehamovelib.RehamoveDevice_current_set) + __swig_setmethods__["pulse_width"] = _rehamovelib.RehamoveDevice_pulse_width_set + __swig_getmethods__["pulse_width"] = _rehamovelib.RehamoveDevice_pulse_width_get + if _newclass: + pulse_width = _swig_property(_rehamovelib.RehamoveDevice_pulse_width_get, _rehamovelib.RehamoveDevice_pulse_width_set) + + def __init__(self): + this = _rehamovelib.new_RehamoveDevice() + try: + self.this.append(this) + except __builtin__.Exception: + self.this = this + __swig_destroy__ = _rehamovelib.delete_RehamoveDevice + __del__ = lambda self: None +RehamoveDevice_swigregister = _rehamovelib.RehamoveDevice_swigregister +RehamoveDevice_swigregister(RehamoveDevice) + + +def open_port(port_name): + return _rehamovelib.open_port(port_name) +open_port = _rehamovelib.open_port + +def close_port(r): + return _rehamovelib.close_port(r) +close_port = _rehamovelib.close_port + +def initialize_low_level(r): + return _rehamovelib.initialize_low_level(r) +initialize_low_level = _rehamovelib.initialize_low_level + +def stop_low_level(r): + return _rehamovelib.stop_low_level(r) +stop_low_level = _rehamovelib.stop_low_level + +def pulse(r, channel, current, pulse_width): + return _rehamovelib.pulse(r, channel, current, pulse_width) +pulse = _rehamovelib.pulse + +def custom_pulse(r, channel, num_points, c0, w0, c1, w1, c2, w2, c3, w3, c4, w4, c5, w5, c6, w6, c7, w7, c8, w8, c9, w9, c10, w10, c11, w11, c12, w12, c13, w13, c14, w14, c15, w15): + return _rehamovelib.custom_pulse(r, channel, num_points, c0, w0, c1, w1, c2, w2, c3, w3, c4, w4, c5, w5, c6, w6, c7, w7, c8, w8, c9, w9, c10, w10, c11, w11, c12, w12, c13, w13, c14, w14, c15, w15) +custom_pulse = _rehamovelib.custom_pulse + +def change_mode(r, mode): + return _rehamovelib.change_mode(r, mode) +change_mode = _rehamovelib.change_mode + +def set_pulse_data(r, current, pulse_width): + return _rehamovelib.set_pulse_data(r, current, pulse_width) +set_pulse_data = _rehamovelib.set_pulse_data + +def run(r, channel, period, total_milliseconds): + return _rehamovelib.run(r, channel, period, total_milliseconds) +run = _rehamovelib.run + +def midlevel_start(r, channel, period): + return _rehamovelib.midlevel_start(r, channel, period) +midlevel_start = _rehamovelib.midlevel_start + +def midlevel_update(r): + return _rehamovelib.midlevel_update(r) +midlevel_update = _rehamovelib.midlevel_update + +def midlevel_end(r): + return _rehamovelib.midlevel_end(r) +midlevel_end = _rehamovelib.midlevel_end + +def get_version(): + return _rehamovelib.get_version() +get_version = _rehamovelib.get_version + +def get_battery(r): + return _rehamovelib.get_battery(r) +get_battery = _rehamovelib.get_battery + +def get_mode(r): + return _rehamovelib.get_mode(r) +get_mode = _rehamovelib.get_mode + +def get_current(r): + return _rehamovelib.get_current(r) +get_current = _rehamovelib.get_current + +def get_pulse_width(r): + return _rehamovelib.get_pulse_width(r) +get_pulse_width = _rehamovelib.get_pulse_width + +def battery_request(r): + return _rehamovelib.battery_request(r) +battery_request = _rehamovelib.battery_request +# This file is compatible with both classic and new-style classes. + + diff --git a/Code/Stimulator/libs/_rehamovelib.pyd b/Code/Stimulator/libs/windows/_rehamovelib.pyd similarity index 100% rename from Code/Stimulator/libs/_rehamovelib.pyd rename to Code/Stimulator/libs/windows/_rehamovelib.pyd diff --git a/Code/Stimulator/libs/windows/rehamove.py b/Code/Stimulator/libs/windows/rehamove.py new file mode 100755 index 0000000..21bf4db --- /dev/null +++ b/Code/Stimulator/libs/windows/rehamove.py @@ -0,0 +1,240 @@ +import rehamovelib + +class Rehamove: + + current_version = "v1.6" + + channel0 = ['r', 'red'] + channel1 = ['b', 'blue'] + channel2 = ['g1', 'gray1', 'grey1', 'black'] + channel3 = ['g2', 'gray2', 'grey2', 'white'] + + MODE_LOW_LEVEL = 0 + MODE_MID_LEVEL = 1 + + def __init__(self, port_name): + self.rehamove = rehamovelib.open_port(port_name) + self.mode = 0 + + def version(self): + c_version = rehamovelib.get_version() + print("Rehamove Version: Python-side " + str(Rehamove.current_version) + ", C-side " + str(c_version)) + return Rehamove.current_version + + def get_mode(self): + result = rehamovelib.get_mode(self.rehamove) + return result + + def get_current(self): + result = rehamovelib.get_current(self.rehamove) + return result + + def get_pulse_width(self): + result = rehamovelib.get_pulse_width(self.rehamove) + return result + + def info(self): + mode = self.get_mode() + current = self.get_current() + pulse_width = self.get_pulse_width() + + if mode == Rehamove.MODE_LOW_LEVEL: + return "Rehamove device in low-level mode. Mid-level pulse is set to {} mA and {} us.".format(current, pulse_width) + elif mode == Rehamove.MODE_MID_LEVEL: + return "Rehamove device in mid-level mode. Mid-level pulse is set to {} mA and {} us.".format(current, pulse_width) + else: + return "Rehamove info() ERROR!" + + def get_channel(self, channel): + chosen_channel = channel + if isinstance(channel, str): + channel = channel.lower() + if channel in Rehamove.channel0: + chosen_channel = 0 + elif channel in Rehamove.channel1: + chosen_channel = 1 + elif channel in Rehamove.channel2: + chosen_channel = 2 + elif channel in Rehamove.channel3: + chosen_channel = 3 + else: + chosen_channel = 0 # Default + elif isinstance(channel, int): + if channel < 0 and channel > 3: + chosen_channel = 0 # Default + else: + chosen_channel = 0 + return chosen_channel + + def pulse(self, channel, current, pulse_width): + if self.rehamove == None: + print("python Rehamove pulse() ERROR! Rehamove object does not exist.") + return -1 + chosen_channel = self.get_channel(channel) + result = rehamovelib.pulse(self.rehamove, chosen_channel, current, pulse_width) + if result != 0: + print("python Rehamove pulse() ERROR!") + return -1 + else: + print("python Rehamove pulse() sent.") + return 0 + + def custom_pulse(self, channel, points_array): + if self.rehamove == None: + print("python Rehamove custom_pulse() ERROR! Rehamove object does not exist.") + return -1 + chosen_channel = self.get_channel(channel) + original_length = len(points_array) + num_points = len(points_array) + # Error handling (warning) if too many points. + if num_points > 16: + print("python Rehamove custom_pulse() WARNING! Maximum of 16 points allowed, truncating points array.") + num_points = 16 + + # Error handling (exception) if malformed points. + try: + for i in range(0, num_points): + current = points_array[i][0] + pulse_width = points_array[i][1] + except: + print("python Rehamove custom_pulse() ERROR! Malformed points array, should be: [ (current0, pulse_width0), (current1, pulse_width1), ... ]") + return -1 + + # Handle if the user supplies less than 16 points: fill up empty points in the array. + remaining_points = 16 - num_points + for _ in range(0, remaining_points): + points_array.append((0.0, 0)) + + # Get all of our point data. + c0, w0 = points_array[0][0], points_array[0][1] + c1, w1 = points_array[1][0], points_array[1][1] + c2, w2 = points_array[2][0], points_array[2][1] + c3, w3 = points_array[3][0], points_array[3][1] + c4, w4 = points_array[4][0], points_array[4][1] + c5, w5 = points_array[5][0], points_array[5][1] + c6, w6 = points_array[6][0], points_array[6][1] + c7, w7 = points_array[7][0], points_array[7][1] + c8, w8 = points_array[8][0], points_array[8][1] + c9, w9 = points_array[9][0], points_array[9][1] + c10, w10 = points_array[10][0], points_array[10][1] + c11, w11 = points_array[11][0], points_array[11][1] + c12, w12 = points_array[12][0], points_array[12][1] + c13, w13 = points_array[13][0], points_array[13][1] + c14, w14 = points_array[14][0], points_array[14][1] + c15, w15 = points_array[15][0], points_array[15][1] + + result = rehamovelib.custom_pulse(self.rehamove, chosen_channel, original_length, c0, w0, c1, w1, c2, w2, c3, w3, c4, w4, c5, w5, c6, w6, c7, w7, c8, w8, c9, w9, c10, w10, c11, w11, c12, w12, c13, w13, c14, w14, c15, w15) + if result != 0: + print("python Rehamove custom_pulse() ERROR!") + return -1 + else: + print("python Rehamove custom_pulse() sent.") + return 0 + + def battery(self): + if self.rehamove == None: + print("python Rehamove ERROR! Rehamove object does not exist.") + return -1 + result = rehamovelib.battery_request(self.rehamove) + if result != 0: + print("python Rehamove battery() ERROR!") + return -1 + else: + battery_level = rehamovelib.get_battery(self.rehamove) + print("python Rehamove battery(): " + str(battery_level) + "%") + return battery_level + + def change_mode(self, mode): + if self.rehamove == None: + print("python Rehamove change_mode() ERROR! Rehamove object does not exist.") + return -1 + result = rehamovelib.change_mode(self.rehamove, mode) + if result != 0: + print("python Rehamove change_mode() ERROR!") + return -1 + else: + print("python Rehamove change_mode(): Changed mode to " + str(mode) + ".") + self.mode = mode + return 0 + + def set_pulse(self, current, pulse_width): + if self.rehamove == None: + print("python Rehamove set_pulse() ERROR! Rehamove object does not exist.") + return -1 + result = rehamovelib.set_pulse_data(self.rehamove, current, pulse_width) + if result != 0: + print("python Rehamove set_pulse() ERROR!") + return -1 + else: + print("python Rehamove set_pulse(): Set pulse current to " + str(current) + " and pulse width to " + str(pulse_width) + ".") + return 0 + + def run(self, channel, period, total_milliseconds): + if self.rehamove == None: + print("python Rehamove run() ERROR! Rehamove object does not exist.") + return -1 + if self.mode != Rehamove.MODE_MID_LEVEL: + print("python Rehamove run() ERROR! Mode must be set to mid-level.") + return -1 + chosen_channel = self.get_channel(channel) + result = rehamovelib.run(self.rehamove, chosen_channel, period, total_milliseconds) + if result != 0: + print("python Rehamove run() ERROR!") + return -1 + else: + print("python Rehamove run(): Completed.") + return 0 + + def start(self, channel, period): + if self.rehamove == None: + print("python Rehamove start() ERROR! Rehamove object does not exist.") + return -1 + if self.mode != Rehamove.MODE_MID_LEVEL: + print("python Rehamove start() ERROR! Mode must be set to mid-level.") + return -1 + chosen_channel = self.get_channel(channel) + result = rehamovelib.midlevel_start(self.rehamove, chosen_channel, period) + if result != 0: + print("python Rehamove start() ERROR!") + return -1 + else: + print("python Rehamove start(): Completed.") + return 0 + + def update(self): + if self.rehamove == None: + print("python Rehamove update() ERROR! Rehamove object does not exist.") + return -1 + if self.mode != Rehamove.MODE_MID_LEVEL: + print("python Rehamove update() ERROR! Mode must be set to mid-level.") + return -1 + result = rehamovelib.midlevel_update(self.rehamove) + if result != 0: + print("python Rehamove update() ERROR!") + return -1 + else: + print("python Rehamove update(): Completed.") + return 0 + + def end(self): + if self.rehamove == None: + print("python Rehamove end() ERROR! Rehamove object does not exist.") + return -1 + if self.mode != Rehamove.MODE_MID_LEVEL: + print("python Rehamove end() ERROR! Mode must be set to mid-level.") + return -1 + result = rehamovelib.midlevel_end(self.rehamove) + if result != 0: + print("python Rehamove end() ERROR!") + return -1 + else: + print("python Rehamove end(): Completed.") + return 0 + + def __del__(self): + # Only close the port if we have a Rehamove object to close + if self.rehamove != None: + result = rehamovelib.close_port(self.rehamove) + if result != 0: + print("python Rehamove close_port() ERROR!") + diff --git a/Code/Stimulator/libs/rehamovelib.py b/Code/Stimulator/libs/windows/rehamovelib.py similarity index 100% rename from Code/Stimulator/libs/rehamovelib.py rename to Code/Stimulator/libs/windows/rehamovelib.py diff --git a/Code/Stimulator/stimulator_test.py b/Code/Stimulator/stimulator_test.py old mode 100755 new mode 100644 index 4feecdb..d2acc35 --- a/Code/Stimulator/stimulator_test.py +++ b/Code/Stimulator/stimulator_test.py @@ -1,45 +1,59 @@ -# -*- coding: utf-8 -*- -""" -https://github.com/humancomputerintegration/rehamove-integration-lib - -Notes: - - does not run on university computer that restricts access to USB/COM ports from scripts - - the pre-compiled python library for windows that is available on github requires python 3.7 (otherwise recompile) - - keep using the low-level mode, to send individual pulses -""" -# Debug: clear variables of IDE -from IPython import get_ipython -try: __IPYTHON__ -except: pass -else: get_ipython().run_line_magic('reset','-sf') - -# imports (load pre-compiled windows library in python3.7) -import sys, os -dirpath = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(dirpath+'\\libs') -import rehamove - -# Detect which COM port is in use -comPort = '' -from serial.tools import list_ports -ports = list_ports.comports() -for port, desc, hwid in sorted(ports): - if 'PID=0403:6014' in hwid: # this is the ID of our rehamove - # if it's a match, we save the COM port number - comPort = port - -# create instance -r = rehamove.Rehamove(comPort) - -# get info -r.info() -r.battery() -r.version() - -# set to low-level mode to control each individual pulse -r.change_mode(0) - -# send pulse -r.pulse("blue", 30, 50) # cable_color, milliAmps, duration in microseconds - - +# -*- coding: utf-8 -*- +""" +The python wrapper is coming from: https://github.com/humancomputerintegration/rehamove-integration-lib + # please read the license and limitations + +Notes: + - had to disable USB sleep in power settings on a windows laptop + - one of the usb port is not working reliably (need to use the left port on ABD's laptop) + - the pre-compiled python library for windows that is available on github requires python 3.7 (otherwise recompile) + - keep using the low-level mode, to send individual pulses + +2do: + - make anaconda env including serial-tool + - include test to load the lib. windows/linux + - add a loop to wait for the hardware to be connected + +""" + +# Debug: clear variables of IDE +from IPython import get_ipython +try: __IPYTHON__ +except: pass +else: get_ipython().run_line_magic('reset','-sf') + +# imports (load pre-compiled windows library in python3.7) +import sys, os +dirpath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(dirpath+'\\windows\\libs') # windows +#sys.path.append(dirpath+'/libs/linux') # linux (need to give permission in /dev/ttyUSB*) +import rehamove + +# Detect which COM port is in use +comPort = '' +from serial.tools import list_ports +ports = list_ports.comports() +for port, desc, hwid in sorted(ports): + if 'PID=0403:6014' in hwid: # this is the ID of our rehamove + # if it's a match, we save the COM port number + comPort = port + +# create instance +r = rehamove.Rehamove(comPort) + +# get info +r.info() +r.battery() +r.version() + +# low-level mode: control each individual pulse +r.change_mode(0) # set to low-level mode to control each individual pulse +r.pulse("blue", 30, 50) # send pulse: cable_color, milliAmps, duration in microseconds + +# mid-level mode: continuous stimulation that we need to update +# r.change_mode(1) +# r.set_pulse(5, 200) # Set the pulse used in mid-level mode +# r.run("blue", 100, 10000) # Run that set pulse every 100 ms for 10s total + + +