Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
240 commits
Select commit Hold shift + click to select a range
6a95aba
Update feature extraction for heatmap visualization
cbmorrell Jul 24, 2024
1bf5dc3
Change features to fe in OnlineStreamer
cbmorrell Jul 24, 2024
3bd2657
Offline regression example
cbmorrell Aug 8, 2024
94091d9
Merge pull request #66 from LibEMG/main
eeddy Aug 14, 2024
353e286
Example for new streamer
eeddy Aug 14, 2024
df08317
Revert "Example for new streamer"
eeddy Aug 14, 2024
34de77a
Rename ColumnFetch to ColumnFetcher
cbmorrell Aug 16, 2024
2da2612
Remove duplicate method
cbmorrell Aug 16, 2024
bc0972f
Add feature queue for time series models
cbmorrell Aug 16, 2024
f4d9e4f
Move constructor docstrings outside __init__
cbmorrell Aug 16, 2024
af1c726
Regression explanation
cbmorrell Aug 16, 2024
ae71d0f
Add explicit conditional check instead of relying on casting
cbmorrell Aug 19, 2024
8e71705
Only pop if queue is at max length
cbmorrell Aug 19, 2024
cfc9a84
Add feature queue parameter to child classes
cbmorrell Aug 19, 2024
6d83b96
Add online channel mask
cbmorrell Aug 19, 2024
f27c6e3
Merge branch 'online-model-feature-queue' into feature-extractor-rework
cbmorrell Aug 19, 2024
19f2049
Revert "Revert "Rework FeatureExtractor""
cbmorrell Aug 19, 2024
81318c1
Add skip until buffer fills up
cbmorrell Aug 19, 2024
d30f608
Merge pull request #68 from LibEMG/rename-column-fetch
eeddy Aug 20, 2024
457e5ec
Merge pull request #59 from LibEMG/offline-regression-example
eeddy Aug 20, 2024
e8701ad
Add TODO
cbmorrell Aug 20, 2024
5a6bd18
Add wildcard to regex helper
cbmorrell Aug 21, 2024
88821ef
Add check for None in RegexFilter
cbmorrell Aug 21, 2024
c5c8748
Add wildcard to regex helper
cbmorrell Aug 21, 2024
2e63dfd
Add check for None in RegexFilter
cbmorrell Aug 21, 2024
87a4003
Add MEC24 workshop to landing page
cbmorrell Aug 22, 2024
e8ddc12
Add post-processing visual to regression example
cbmorrell Aug 22, 2024
34cecb3
Add skip until buffer fills up
cbmorrell Aug 19, 2024
784372c
Regression visualization
cbmorrell Aug 23, 2024
ee3503e
Handle coordinates without steady state frames
cbmorrell Aug 23, 2024
30ef842
Animation documentation
cbmorrell Aug 23, 2024
ac127c3
Add heatmap visualization
cbmorrell Aug 23, 2024
a751cbb
Add string parameters for common metadata operations
cbmorrell Aug 26, 2024
124a1dd
Merge pull request #71 from LibEMG/common-metadata-operations
ECEEvanCampbell Aug 26, 2024
60249b0
ninapro db2 dataglove support added
A-R-Hariri Aug 27, 2024
5d92b81
Merge branch 'feature-extractor-rework' into online-model-feature-queue
cbmorrell Aug 27, 2024
29e677c
Revert "Revert "Revert "Rework FeatureExtractor"""
cbmorrell Aug 27, 2024
8aaf19d
Add online standardization method
cbmorrell Aug 27, 2024
80969d8
Use proper FeatureExtractor interface
cbmorrell Aug 27, 2024
3466dad
Fix heatmap feature extraction
cbmorrell Sep 4, 2024
5f20583
Add clarification to install_standardization docstring
cbmorrell Sep 4, 2024
2c10637
Fix online regressor visualize lag
cbmorrell Sep 4, 2024
7c86fa3
Add docstring to OnlineEMGRegressor.visualize
cbmorrell Sep 4, 2024
5bfe048
Skeleton for Controller class
cbmorrell Sep 4, 2024
6f5e72d
Check for correct MacOS string
cbmorrell Sep 5, 2024
318fc0c
ninapro db2
A-R-Hariri Sep 6, 2024
24c1951
Remove unnecessary pop of oldest window
cbmorrell Sep 6, 2024
9aa3f4e
Create SocketController
cbmorrell Sep 6, 2024
f18ec91
Add comment idea
cbmorrell Sep 6, 2024
48beed0
Add abstract timestamp parsing function
cbmorrell Sep 6, 2024
7ce9bc1
RegressorController
cbmorrell Sep 6, 2024
8254942
Merge pull request #79 from LibEMG/oymotion-streamer-incorrect-os-check
ECEEvanCampbell Sep 9, 2024
95e1111
Merge pull request #73 from LibEMG/online-model-feature-queue
ECEEvanCampbell Sep 9, 2024
25460b3
updates sifi streamer to accept bridge version. forked sifi bridge in…
ECEEvanCampbell Sep 9, 2024
deb3a98
Remove model from RegressorController
cbmorrell Sep 9, 2024
8a20f1b
db2 dataglove support limited to NinaproDB2 class
A-R-Hariri Sep 9, 2024
fb41e9a
ClassifierController implementation
cbmorrell Sep 9, 2024
edb395d
Fix online regressor visualize lag
cbmorrell Sep 4, 2024
19769b5
Add option to parse timestamp
cbmorrell Sep 9, 2024
030d6b2
Remove OnlineEMGRegressor.parse_output and references
cbmorrell Sep 9, 2024
6e436a9
documentation for NinaproDB2
A-R-Hariri Sep 9, 2024
c943fff
Merge pull request #80 from AmirRezaHariri/ninapro_regression
cbmorrell Sep 9, 2024
68942af
Merge pull request #81 from LibEMG/sifi-breakout
cbmorrell Sep 9, 2024
7d0a7d6
Remove duplicate method
cbmorrell Sep 13, 2024
5073d27
Throw error when streaming stops during SGT
cbmorrell Sep 13, 2024
e38e66c
Update .gitignore
cbmorrell Sep 13, 2024
2b49cf7
Convert methods to private
cbmorrell Sep 13, 2024
2142233
Regression Fitts task
cbmorrell Sep 13, 2024
e2357e5
Added sifi-bridge-py to dependencies. Renamed executable to sifibridge
Sep 24, 2024
6965053
cleanup init and sifibridge configurations
Sep 24, 2024
190818c
Change raw writes to sbp methods
Sep 24, 2024
c547681
Updated some redundant parameters and docstrings
Sep 24, 2024
29aafb2
Remove executable from repo
Sep 24, 2024
45c8034
Fixed sifibridge process launch, added sifibridge to gitignore
Sep 24, 2024
a7a839f
Fix gitignore
Sep 24, 2024
ef60d25
Fixed cleanup
Sep 24, 2024
48f907b
make streamer function more in-line with sifi-breakout
Sep 27, 2024
62b0064
docstring, change 'device' to 'name'
Sep 27, 2024
9ae16eb
Remove metric calculation functions
cbmorrell Oct 4, 2024
38031de
Implement classifier controller for visualization
cbmorrell Oct 4, 2024
633f173
Add check for data not being received
cbmorrell Oct 4, 2024
063b965
Add EMG hero legacy code
cbmorrell Oct 4, 2024
e71897c
Replace deque with multiprocessing Queue
cbmorrell Oct 5, 2024
e3b27e5
Fix online ClassifierController parsing
cbmorrell Oct 5, 2024
f846b0b
Fix OnlineEMGClassifier.visualize
cbmorrell Oct 5, 2024
e5e1c26
Automatically start Controller in Environment
cbmorrell Oct 5, 2024
2db5c59
Classifier working in FittsLawTest
cbmorrell Oct 5, 2024
7fb7dd6
Rename FittsLawTest to IsoFitts
cbmorrell Oct 5, 2024
bc16d76
Move common functionality to run method
cbmorrell Oct 5, 2024
f40619c
Update OnlineEMGClassifier visualize docstring
cbmorrell Oct 6, 2024
a697bd1
Hide pygame welcome message
cbmorrell Oct 6, 2024
92756ea
Move functionality to Environment class and Fitts parameters
cbmorrell Oct 6, 2024
5812e7a
Rework prediction map
cbmorrell Oct 6, 2024
b022309
Convert EMGHero to Environment
cbmorrell Oct 6, 2024
f7a126f
Rework _get_action for queue
cbmorrell Oct 6, 2024
d687ec9
Remove _parse_timestamp from Controller class
cbmorrell Oct 6, 2024
a6435c8
Fix prediction map for IsoFitts
cbmorrell Oct 6, 2024
a729a9b
Implemented KeyboardController
cbmorrell Oct 6, 2024
5a7721c
Properly receive None in EMGHero
cbmorrell Oct 6, 2024
4e1fb29
Split environments.py into multiple files
cbmorrell Oct 7, 2024
459c1a2
Add environments to __init__
cbmorrell Oct 7, 2024
c2bf8f5
Fix environment imports
cbmorrell Oct 7, 2024
f8ae722
Hide pygame welcome message
cbmorrell Oct 7, 2024
31d5a46
IsoFitts direction fix
cbmorrell Oct 7, 2024
87ff27e
Convert IsoFitts methods to hidden methods
cbmorrell Oct 7, 2024
277cb14
Environments documentation
cbmorrell Oct 7, 2024
d056439
Rework SocketController
cbmorrell Oct 7, 2024
fad6227
Add wait in OnlineEMGRegressor.visualize
cbmorrell Oct 7, 2024
9822c14
Merge pull request #83 from LibEMG/duplicate-method-in-ninaprodb2
ECEEvanCampbell Oct 8, 2024
d28d0f6
Merge pull request #84 from LibEMG/handle-sgt-streaming-interruption
ECEEvanCampbell Oct 8, 2024
391ab53
Merge pull request #74 from LibEMG/online-regressor-visualization-lag
ECEEvanCampbell Oct 8, 2024
b6121a9
Merge branch 'latest' into game-environments
ECEEvanCampbell Oct 8, 2024
a8367ee
Merge pull request #89 from LibEMG/game-environments
ECEEvanCampbell Oct 8, 2024
bf252a2
fixed windows compilation target
Oct 8, 2024
b483dcf
Override terminate method
cbmorrell Oct 8, 2024
157dc45
Allow non-callable dtypes
cbmorrell Oct 8, 2024
ff61e87
Convert Manager to SharedMemory
cbmorrell Oct 8, 2024
265e482
Add proportional control flag to IsoFitts
cbmorrell Oct 8, 2024
75a71ea
Automatically create parent directories when saving coordinates
cbmorrell Oct 9, 2024
278c202
Update Fitts example with regression
cbmorrell Oct 9, 2024
2ffb798
added sifi bridge to setup
ECEEvanCampbell Oct 10, 2024
938d797
Launch the subprocess in run method for pickling issues
gabrielpgagne Oct 10, 2024
f2c0d5a
Merge branch 'latest' into feature/sifibridge-1.1
ECEEvanCampbell Oct 10, 2024
8dce28b
Merge pull request #93 from gabrielpgagne/feature/sifibridge-1.1
ECEEvanCampbell Oct 10, 2024
d51fe42
Merge pull request #91 from LibEMG/socket-controller-shared-memory
ECEEvanCampbell Oct 10, 2024
ac9ce2f
Update streamers.py
ECEEvanCampbell Oct 10, 2024
4a73b76
removed version from sifi bridge import
ECEEvanCampbell Oct 16, 2024
2fb22a1
added more checks for creating variables
ECEEvanCampbell Oct 16, 2024
b4c42e6
SocketController Lock Fix
cbmorrell Oct 16, 2024
9410fff
Add pygame to setup.py
cbmorrell Oct 16, 2024
22cdcf1
Merge pull request #94 from LibEMG/socket-controller-shared-memory-lo…
ECEEvanCampbell Oct 17, 2024
d1ff3ee
Merge pull request #92 from LibEMG/regression-documentation
ECEEvanCampbell Oct 17, 2024
e58b1cd
Simplify SocketController
cbmorrell Oct 17, 2024
0d95a50
Make socket non-blocking
cbmorrell Oct 17, 2024
f9a8644
Replace classifier_smm_writes with model_smm_writes
cbmorrell Oct 17, 2024
67dc160
Pass model timestamp instead of current time
cbmorrell Oct 17, 2024
d6b0486
Fixed saving of IMU data
ulysseTM Oct 18, 2024
c48b3f8
Remove Delsys pasted docstring
cbmorrell Oct 18, 2024
81727d0
Merge pull request #97 from ulysseTM/patch-1
ECEEvanCampbell Oct 20, 2024
4b04b99
Add numpy import to Delsys API streamer
cbmorrell Oct 23, 2024
8e6ad11
Add check for num_channels in delsys_api_streamer
cbmorrell Oct 23, 2024
1e02c49
Add support for .json when logging environment
cbmorrell Oct 24, 2024
25492e4
Add target and distance radii parameters
cbmorrell Oct 24, 2024
96f7e07
Add option to redo final rep
cbmorrell Oct 29, 2024
960d6f9
Show "Rep X of Y" during data collection
cbmorrell Oct 29, 2024
6e32c11
Automatically calculate rep time for videos
cbmorrell Oct 30, 2024
cf1e181
Find labels files and move to corresponding data directory
cbmorrell Oct 30, 2024
1bbff78
Throw ConnectionError during start callback
cbmorrell Oct 30, 2024
08b0428
GUI docstring update
cbmorrell Oct 30, 2024
a27ca1f
Merge pull request #98 from LibEMG/sgt-gui-improvements
eeddy Oct 31, 2024
f5bf631
Merge pull request #96 from LibEMG/simplify-controllers
eeddy Oct 31, 2024
dfb2c69
Add game time parameter
cbmorrell Nov 12, 2024
3113b50
Removed unused dependencies in sifibridge streamer. Set EDA to 0Hz by…
Nov 12, 2024
97be849
Merge pull request #100 from gabrielpgagne/bugfix/sifi-bridge-py-1.2
ECEEvanCampbell Nov 13, 2024
5a89b2d
Add regression info to docstring
cbmorrell Nov 13, 2024
dc296cf
Add timestamps to KeyboardController
cbmorrell Nov 13, 2024
510fd1f
Draw cursor for RotationalIsoFitts
cbmorrell Nov 13, 2024
fb3f835
WIP: converting to using polygons
cbmorrell Nov 20, 2024
56dfe95
Rename isofitts module to fitts
cbmorrell Nov 20, 2024
5617bfd
Rename IsoFitts class to ISOFitts
cbmorrell Nov 20, 2024
a245816
Create PolarFitts
cbmorrell Nov 20, 2024
959caf7
Fitts base class
cbmorrell Nov 20, 2024
d89af62
Remove duplicated code from ISOFitts
cbmorrell Nov 20, 2024
a087460
Inherit from Fitts instead of ISOFitts for PolarFitts
cbmorrell Nov 20, 2024
adb2695
Remove dead code
cbmorrell Nov 20, 2024
e81d6e6
Create Fitts targets in uniform circle
cbmorrell Nov 21, 2024
39dc79a
Handle different target sizes
cbmorrell Nov 21, 2024
fe9ae77
Custom directions for PolarFitts prediction_map
cbmorrell Nov 21, 2024
5bf193e
Set minimum target size based on cursor
cbmorrell Nov 21, 2024
c028774
Reimplement feature parameters online
cbmorrell Nov 21, 2024
1ca93c4
Only add to smm_items list if tag has not already been provided
cbmorrell Nov 21, 2024
bfeacaf
Fix check for cursor being on new goal target
cbmorrell Nov 21, 2024
1a2f147
Keep radius on screen
cbmorrell Nov 21, 2024
b168f98
Fix incorrect polar to cartesian action map
cbmorrell Nov 21, 2024
ad08df5
Restrict PolarFitts to a single side
cbmorrell Nov 21, 2024
1799048
Removed unused dependencies in sifibridge streamer. Set EDA to 0Hz by…
Nov 12, 2024
125ed3a
Stop targets from generating in the center of the screen
cbmorrell Nov 24, 2024
1110a3f
Set theta pointed at bottom of screen instead of right
cbmorrell Nov 24, 2024
02cb852
Docstring for PolarFitts
cbmorrell Nov 24, 2024
2cd4b73
Initialize theta to pi/2
cbmorrell Nov 25, 2024
9a8fc8f
Remap theta direction to correspond to prediction direction
cbmorrell Nov 25, 2024
a7bfbc4
Fix visualize_heatmap throwing error for feature lists of length 1
cbmorrell Nov 25, 2024
143f0a5
WIP: Functional conversion from Cartesian to Polar space
cbmorrell Nov 26, 2024
2b16984
Remove dead code
cbmorrell Nov 26, 2024
ea1a5dd
Pass in rect when drawing to polar space
cbmorrell Nov 26, 2024
84694d5
Add fill parameter for drawing polar circle
cbmorrell Nov 26, 2024
b387b2b
Fix logical error when checking if new target is on cursor
cbmorrell Nov 26, 2024
9557de0
Combine PolarFitts into parent Fitts class
cbmorrell Nov 26, 2024
65e7699
Fix check for cursor remaining on screen
cbmorrell Nov 26, 2024
9ce1a0e
Update Fitts and ISOFitts docstrings
cbmorrell Nov 26, 2024
0a73901
Add option to draw radius in polar Fitts
cbmorrell Nov 26, 2024
1c5f514
Extract origin to variable
cbmorrell Nov 26, 2024
154496c
Improve check for cursor staying in bounds
cbmorrell Nov 27, 2024
e4ed8b3
Add check in ISOFitts for target distance radius and screen size
cbmorrell Nov 27, 2024
ee00681
Improve error handling for small screen size error
cbmorrell Nov 27, 2024
5fe02b8
Fix fstring formatting in error message
cbmorrell Nov 27, 2024
47f1837
Use full screen size when possible
cbmorrell Nov 27, 2024
3275158
Switch polar Fitts to vertical layout instead of horizontal
cbmorrell Nov 27, 2024
569ec35
Change default num_targets in ISOFitts
cbmorrell Nov 27, 2024
b617a4a
Add parameters for target and cursor color
cbmorrell Nov 27, 2024
f3d9e46
Extract Fitts parameters to config dataclass
cbmorrell Nov 27, 2024
d25b360
Merge pull request #103 from LibEMG/visualize-heatmap-fix
eeddy Nov 28, 2024
9356b44
Update colors
cbmorrell Nov 28, 2024
8da18f8
Make OnlineDataHandler processes daemons
cbmorrell Nov 28, 2024
eb9187e
Dataset Updates (#85)
eeddy Nov 29, 2024
defc993
Fixed setup issue
eeddy Nov 29, 2024
bf52580
Remove incorrect docstring
cbmorrell Dec 3, 2024
d672145
Reset dwell timer in _get_new_goal_target
cbmorrell Dec 3, 2024
57cdf2f
Add HyserMVC Dataset (#105)
cbmorrell Dec 5, 2024
e1e5d1e
Wait to start timers until data have been received
cbmorrell Dec 5, 2024
ea17e42
Add cursor_radius parameter to FittsConfig
cbmorrell Dec 5, 2024
46d7d86
Updated the normalization in the feature extractor
eeddy Dec 7, 2024
7b4a727
Modify FittsConfig default parameters
cbmorrell Dec 13, 2024
71845f9
Update _delsys_streamer.py
ECEEvanCampbell Jan 15, 2025
afe7b5e
Added option for dictionary list
eeddy Jan 25, 2025
f40e3ef
Added option for dictionary list
eeddy Jan 25, 2025
2f161f0
Added a fix feature errors to feature extractor
eeddy Jan 28, 2025
7e7808a
Changed feature_extractor to default to fix errors is false
eeddy Jan 28, 2025
a45013d
Changed dataset names
eeddy Jan 29, 2025
aeea667
Updated feature dictionary
eeddy Jan 30, 2025
33a5177
Updates
eeddy Jan 30, 2025
265d0ae
Merge pull request #104 from LibEMG/isofitts-improvements
ECEEvanCampbell Feb 11, 2025
fb716b7
Remove old calls to Controller.start
cbmorrell Mar 27, 2025
49617f7
Merge pull request #113 from LibEMG/online-visualize-bug-fix
ECEEvanCampbell Mar 27, 2025
5a85608
Fixed code example for feature performance (#118)
cbmorrell Jun 3, 2025
17e56e6
added pca visualization to online data handler (#110)
eeddy Jun 3, 2025
54ed325
Regressor Visualize On One Axis (#112)
cbmorrell Jun 3, 2025
b7f3f01
Streamer Cleanup (#115)
cbmorrell Jun 3, 2025
701180c
Updated README
eeddy Jun 3, 2025
0b22023
Merge branch 'main' into latest
eeddy Jun 3, 2025
1e3981e
Updated yaml
eeddy Jun 3, 2025
8dea103
Added option to configure BLE Power and Memory Mode (#121)
eeddy Jun 3, 2025
46d8af5
Updated version
eeddy Jun 3, 2025
37c3b1b
added connection to specific myo armband via MAC address (#122)
Tob1n8tor Jun 24, 2025
f274426
update multi devices biopoint from old venv
Michiboi29 Jun 27, 2025
9985178
bioarmband
Michiboi29 Jun 27, 2025
b503ad7
Merge branch 'latest' into multi_devices
Michiboi29 Jun 27, 2025
120205b
multiple streamer cleanup
Michiboi29 Jul 21, 2025
d3929d8
ugly solution working
Michiboi29 Jul 22, 2025
926ea2b
timestamps
Michiboi29 Jul 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions libemg/_gui/_data_collection_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import json
from datetime import datetime
from ._utils import Media, set_texture, init_matplotlib_canvas, matplotlib_to_numpy

import libemg.utils
import threading
import matplotlib.pyplot as plt

Expand Down Expand Up @@ -234,7 +234,8 @@ def run_sgt(self, media_list):
self.play_collection_visual(media_list[self.i], active=False)
media_list[self.i][0].reset()
self.gui.online_data_handler.reset()


libemg.utils.log_timestamp(self.output_folder, tag=f"C_" + str(media_list[self.i][2]) + "_R_" + str(media_list[self.i][3]) + " ACQUISITION START", append=True, print_timestamp=False)
self.play_collection_visual(media_list[self.i], active=True)

output_path = Path(self.output_folder, "C_" + str(media_list[self.i][2]) + "_R_" + str(media_list[self.i][3]) + ".csv").absolute().as_posix()
Expand Down Expand Up @@ -265,6 +266,7 @@ def run_sgt(self, media_list):
dpg.configure_app(manual_callback_management=False)
if not is_final_media:
dpg.set_value('__dc_rep', value=f"Rep {media_list[self.i][3] + 1} of {self.num_reps}")


def redo_collection_callback(self):
if self.auto_advance:
Expand All @@ -274,12 +276,18 @@ def redo_collection_callback(self):
dpg.hide_item(item="__dc_redo_button")
dpg.hide_item(item="__dc_continue_button")
self.advance = True

current_rep = (self.i // self.num_motions)
libemg.utils.log_timestamp(self.output_folder, tag=f"R_{current_rep} REDO", append=True, print_timestamp=False)

def continue_collection_callback(self):
dpg.hide_item(item="__dc_redo_button")
dpg.hide_item(item="__dc_continue_button")
self.advance = True

current_rep = (self.i // self.num_motions) - 1
libemg.utils.log_timestamp(self.output_folder, tag=f"R_{current_rep} CONTINUE", append=True, print_timestamp=False)

def play_collection_visual(self, media, active=True):
if active:
timer_duration = media[-1]
Expand Down Expand Up @@ -316,6 +324,11 @@ def play_collection_visual(self, media, active=True):
def save_data(self, filename):
file_parts = filename.split('.')

folder = os.path.dirname(filename)
base_name = os.path.basename(filename)
tag = f"{base_name.split('.')[0]} SAVE"
libemg.utils.log_timestamp(folder, tag=tag, append=True, print_timestamp=False)

for mod in self.rep_buffer:
filename = file_parts[0] + "_" + mod + "." + file_parts[1]
data = np.vstack(self.rep_buffer[mod])[::-1,:]
Expand Down
5 changes: 3 additions & 2 deletions libemg/_streamers/_myo_streamer.py
Original file line number Diff line number Diff line change
Expand Up @@ -516,14 +516,15 @@ def on_battery(self, battery_level):
import numpy as np
from multiprocessing import Process, Event
class MyoStreamer(Process):
def __init__(self, filtered, emg, imu, shared_memory_items=[]):
def __init__(self, filtered, emg, imu, shared_memory_items=[], addr=None):
Process.__init__(self, daemon=True)
self.filtered = filtered
self.emg = emg
self.imu = imu
self.smm = SharedMemoryManager()
self.shared_memory_items = shared_memory_items
self.signal = Event()
self.addr = addr

def run(self):
for item in self.shared_memory_items:
Expand All @@ -533,7 +534,7 @@ def run(self):
if not self.filtered:
mode = emg_mode.RAW
self.m = Myo(mode=mode)
self.m.connect()
self.m.connect(addr=self.addr)

if self.emg:
def write_emg(emg):
Expand Down
76 changes: 61 additions & 15 deletions libemg/_streamers/_sifi_bridge_streamer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from multiprocessing import Process, Event
import time
import numpy as np
from collections.abc import Callable

Expand All @@ -18,6 +19,8 @@ class SiFiBridgeStreamer(Process):
----------
name : str
The name of the devie (eg BioArmband, BioPoint_v1_2, BioPoint_v1_3, etc.). None to auto-connect to any device.
device_id : int | None
If multiple devices are connected, this is the device ID to differentiate them. None by default (just one device).
shared_memory_items : list
Shared memory configuration parameters for the streamer in format:
["tag", (size), datatype, Lock()].
Expand Down Expand Up @@ -51,6 +54,7 @@ class SiFiBridgeStreamer(Process):
def __init__(
self,
name: str | None = None,
device_id: int | None = None,
shared_memory_items: list = [],
ecg: bool = False,
emg: bool = True,
Expand Down Expand Up @@ -80,9 +84,11 @@ def __init__(
self.eda_handlers = []
self.ecg_handlers = []
self.ppg_handlers = []
self.temp_handlers = []


self.name = name
self.device_id = device_id if device_id is not None else ""
self.ecg = ecg
self.emg = emg
self.eda = eda
Expand Down Expand Up @@ -130,7 +136,7 @@ def connect(self):
print(f"Could not connect to {self.handle}. Retrying.")

self.connected = True
print("Connected to Sifi device.")
print(f"Connected to Sifi device{self.device_id}.")

self.sb.stop()
self.sb.start()
Expand All @@ -149,6 +155,9 @@ def add_ecg_handler(self, closure: Callable):

def add_eda_handler(self, closure: Callable):
self.eda_handlers.append(closure)

def add_temperature_handler(self, closure: Callable):
self.temp_handlers.append(closure)

def process_packet(self, data: dict):
if "data" in list(data.keys()):
Expand Down Expand Up @@ -212,6 +221,10 @@ def process_packet(self, data: dict):
self.old_ppg_packet = None
for h in self.ppg_handlers:
h(ppg)
if "temperature" in list(data["data"].keys()):
temperature = np.stack((data["data"]["temperature"],)).T
for h in self.temp_handlers:
h(temperature)

def run(self):
# process is started beyond this point!
Expand Down Expand Up @@ -241,69 +254,85 @@ def run(self):
def write_emg(emg):
# update the samples in "emg"
self.smm.modify_variable(
"emg", lambda x: np.vstack((np.flip(emg, 0), x))[: x.shape[0], :]
f"emg{self.device_id}", lambda x: np.vstack((np.flip(emg, 0), x))[: x.shape[0], :]
)
# update the number of samples retrieved
self.smm.modify_variable("emg_count", lambda x: x + emg.shape[0])
self.smm.modify_variable(f"emg{self.device_id}_count", lambda x: x + emg.shape[0])

self.add_emg_handler(write_emg)

def write_imu(imu):
# update the samples in "imu"
self.smm.modify_variable(
"imu", lambda x: np.vstack((np.flip(imu, 0), x))[: x.shape[0], :]
f"imu{self.device_id}", lambda x: np.vstack((np.flip(imu, 0), x))[: x.shape[0], :]
)
# update the number of samples retrieved
self.smm.modify_variable("imu_count", lambda x: x + imu.shape[0])
self.smm.modify_variable(f"imu{self.device_id}_count", lambda x: x + imu.shape[0])
# sock.sendto(data_arr, (self.ip, self.port))

self.add_imu_handler(write_imu)

def write_eda(eda):
# update the samples in "eda"
self.smm.modify_variable(
"eda", lambda x: np.vstack((np.flip(eda, 0), x))[: x.shape[0], :]
f"eda{self.device_id}", lambda x: np.vstack((np.flip(eda, 0), x))[: x.shape[0], :]
)
# update the number of samples retrieved
self.smm.modify_variable("eda_count", lambda x: x + eda.shape[0])
self.smm.modify_variable(f"eda{self.device_id}_count", lambda x: x + eda.shape[0])

self.add_eda_handler(write_eda)

def write_ppg(ppg):
# update the samples in "ppg"
self.smm.modify_variable(
"ppg", lambda x: np.vstack((np.flip(ppg, 0), x))[: x.shape[0], :]
f"ppg{self.device_id}", lambda x: np.vstack((np.flip(ppg, 0), x))[: x.shape[0], :]
)
# update the number of samples retrieved
self.smm.modify_variable("ppg_count", lambda x: x + ppg.shape[0])
self.smm.modify_variable(f"ppg{self.device_id}_count", lambda x: x + ppg.shape[0])

self.add_ppg_handler(write_ppg)

def write_ecg(ecg):
# update the samples in "ecg"
self.smm.modify_variable(
"ecg", lambda x: np.vstack((np.flip(ecg, 0), x))[: x.shape[0], :]
f"ecg{self.device_id}", lambda x: np.vstack((np.flip(ecg, 0), x))[: x.shape[0], :]
)
# update the number of samples retrieved
self.smm.modify_variable("ecg_count", lambda x: x + ecg.shape[0])
self.smm.modify_variable(f"ecg{self.device_id}_count", lambda x: x + ecg.shape[0])

self.add_ecg_handler(write_ecg)

def write_temperature(temperature):
# update the samples in "temperature"
self.smm.modify_variable(
f"temp{self.device_id}", lambda x: np.vstack((np.flip(temperature, 0), x))[: x.shape[0], :]
)
# update the number of samples retrieved
self.smm.modify_variable(f"temp{self.device_id}_count", lambda x: x + temperature.shape[0])

self.add_temperature_handler(write_temperature)

self.connect()

self.old_ppg_packet = (
None # required for now since ppg sends non-uniform packet length
)

print("LibEMG -> SiFiBridgeStreamer (process started).")
while True:
try:
new_packet = self.sb.get_data()
self.process_packet(new_packet)
except Exception as e:
print("Error Occurred: " + str(e))
print("Error Occurred maybe: ", e, "type : ", type(e), "representation : ", repr(e), " -> continuing.")
import traceback
traceback.print_exc()
continue
if self.signal.is_set():
print("LibEMG -> SiFiBridgeStreamer (signal received).")
self.cleanup()
break

print("LibEMG -> SiFiBridgeStreamer (process ended).")

def stop_sampling(self):
Expand All @@ -315,7 +344,17 @@ def turnoff(self):
return

def disconnect(self):
self.connected = self.sb.disconnect()["connected"]
try:
result = self.sb.disconnect()
# Handle both dictionary and boolean return types
if isinstance(result, dict) and "connected" in result:
self.connected = result["connected"]
else:
# If it returns a boolean directly, assume False means disconnected
self.connected = not result if isinstance(result, bool) else False
except Exception as e:
print(f"Error during disconnect: {e}")
self.connected = False
return self.connected

def deep_sleep(self):
Expand All @@ -324,8 +363,15 @@ def deep_sleep(self):
def cleanup(self):
self.stop_sampling() # stop sampling
print("LibEMG -> SiFiBridgeStreamer (sampling stopped).")
self.deep_sleep() # stops status packets
print("LibEMG -> SiFiBridgeStreamer (device sleeped).")
time.sleep(1)
if self.mac == "CE:9B:59:A6:BD:EC" or self.mac == "DD:67:FD:19:06:03":
self.turnoff()
print("LibEMG -> SiFiBridgeStreamer (device turned off).")
else:
self.deep_sleep() # stops status packets
print("LibEMG -> SiFiBridgeStreamer (device sleeped).")

time.sleep(1)
self.disconnect() # disconnect
print("LibEMG -> SiFiBridgeStreamer (device disconnected).")
self.sb._bridge.kill()
Expand Down
5 changes: 5 additions & 0 deletions libemg/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ def _on_window_close(self):
print("Window is closing. Performing clean-up...")
if 'streamer' in self.args.keys():
self.args['streamer'].signal.set()
print("Streamer stopped in window closed.")
if 'streamers' in self.args.keys():
for streamer in self.args['streamers']:
streamer.signal.set()
print(f"Streamer stopped in window closed.")
time.sleep(3)

def download_gestures(self, gesture_ids, folder, download_imgs=True, download_gifs=False, redownload=False):
Expand Down
Loading