py-neuromodulation 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,10 @@
1
- # We
2
- # should
3
- # have
4
- # a
5
- # brief
6
- # explanation
7
- # of
8
- # the
9
- # settings
10
- # format
11
- # here
1
+ # Settings should be modified either directly through
2
+ # the default_settings.yaml file or by creating a new
3
+ # settings.yaml file than can be loaded with
4
+ # settings.NMSettings.load(FILE_PATH)
5
+ #
6
+ # Alternatively, the settings can also be modified through the
7
+ # settings object directly, e.g. settings.features.raw_hjorth = False
12
8
 
13
9
  ########################
14
10
  ### General settings ###
@@ -21,9 +17,9 @@ frequency_ranges_hz: # frequency band ranges can be added, removed and altered
21
17
  alpha: [8, 12]
22
18
  low_beta: [13, 20]
23
19
  high_beta: [20, 35]
24
- low_gamma: [60, 80]
25
- high_gamma: [90, 200]
26
- HFA: [200, 400]
20
+ #low_gamma: [60, 80]
21
+ #high_gamma: [90, 200]
22
+ #HFA: [200, 400]
27
23
 
28
24
  # Enabled features
29
25
  features:
@@ -99,7 +95,7 @@ fft_settings:
99
95
  median: false
100
96
  std: false
101
97
  max: false
102
- return_spectrum: true
98
+ return_spectrum: false
103
99
 
104
100
  welch_settings:
105
101
  windowlength_ms: 1000
@@ -109,7 +105,7 @@ welch_settings:
109
105
  median: false
110
106
  std: false
111
107
  max: false
112
- return_spectrum: true
108
+ return_spectrum: false
113
109
 
114
110
  stft_settings:
115
111
  windowlength_ms: 500
@@ -147,7 +143,7 @@ kalman_filter_settings:
147
143
  bursts_settings:
148
144
  threshold: 75
149
145
  time_duration_s: 30
150
- frequency_bands: [low_beta, high_beta, low_gamma]
146
+ frequency_bands: [low_beta, high_beta] # low_gamma
151
147
  burst_features:
152
148
  duration: true
153
149
  amplitude: true
@@ -190,7 +186,7 @@ sharpwave_analysis_settings:
190
186
  apply_estimator_between_peaks_and_troughs: true
191
187
 
192
188
  coherence_settings:
193
- channels: [] # List of channel pairs, empty by default. Each pair is a list of two channels.
189
+ channels: [] # List of channel pairs, empty by default. Each pair is a list of two channels, where the first channel is the seed and the second channel is the target.
194
190
  # Example channels: [[STN_RIGHT_0, ECOG_RIGHT_0], [STN_RIGHT_1, ECOG_RIGHT_1]]
195
191
  frequency_bands: [high_beta]
196
192
  features:
@@ -230,6 +226,8 @@ nolds_settings:
230
226
  frequency_bands: [low_beta]
231
227
 
232
228
  mne_connectiviy_settings:
229
+ channels: [] # List of channel pairs, empty by default. Each pair is a list of two channels, where the first channel is the seed and the second channel is the target.
230
+ # Example channels: [[STN_RIGHT_0, ECOG_RIGHT_0], [STN_RIGHT_1, ECOG_RIGHT_1]]
233
231
  method: plv # One of ['coh', 'cohy', 'imcoh', 'cacoh', 'mic', 'mim', 'plv', 'ciplv', 'ppc', 'pli', 'dpli','wpli', 'wpli2_debiased', 'gc', 'gc_tr']
234
232
  mode: multitaper # One of ['multitaper', 'fourier', 'cwt_morlet']
235
233
 
@@ -1,14 +1,17 @@
1
1
  from collections.abc import Iterable
2
2
  import numpy as np
3
- from typing import TYPE_CHECKING, Literal
3
+
4
+ from typing import TYPE_CHECKING, Annotated, Literal
5
+ from pydantic import Field
4
6
 
5
7
  from py_neuromodulation.utils.types import NMFeature, NMBaseModel
6
8
  from py_neuromodulation.utils.pydantic_extensions import NMField
7
9
 
8
10
  if TYPE_CHECKING:
9
11
  from py_neuromodulation import NMSettings
10
- from mne.io import RawArray
11
- from mne import Epochs
12
+
13
+
14
+ ListOfTwoStr = Annotated[list[str], Field(min_length=2, max_length=2)]
12
15
 
13
16
 
14
17
  MNE_CONNECTIVITY_METHOD = Literal[
@@ -35,6 +38,7 @@ MNE_CONNECTIVITY_MODE = Literal["multitaper", "fourier", "cwt_morlet"]
35
38
  class MNEConnectivitySettings(NMBaseModel):
36
39
  method: MNE_CONNECTIVITY_METHOD = NMField(default="plv")
37
40
  mode: MNE_CONNECTIVITY_MODE = NMField(default="multitaper")
41
+ channels: list[ListOfTwoStr] = []
38
42
 
39
43
 
40
44
  class MNEConnectivity(NMFeature):
@@ -44,102 +48,42 @@ class MNEConnectivity(NMFeature):
44
48
  ch_names: Iterable[str],
45
49
  sfreq: float,
46
50
  ) -> None:
47
- from mne import create_info
48
-
49
51
  self.settings = settings
50
52
 
51
53
  self.ch_names = ch_names
52
54
  self.sfreq = sfreq
53
55
 
56
+ self.channels = settings.mne_connectivity_settings.channels
57
+
54
58
  # Params used by spectral_connectivity_epochs
55
59
  self.mode = settings.mne_connectivity_settings.mode
56
60
  self.method = settings.mne_connectivity_settings.method
61
+ self.indices = ([], []) # convert channel names to channel indices in data
62
+ for con_idx in range(len(self.channels)):
63
+ seed_name = self.channels[con_idx][0]
64
+ target_name = self.channels[con_idx][1]
65
+ seed_name_reref = [ch for ch in self.ch_names if ch.startswith(seed_name)][0]
66
+ target_name_reref = [ch for ch in self.ch_names if ch.startswith(target_name)][0]
67
+ self.indices[0].append(self.ch_names.index(seed_name_reref))
68
+ self.indices[1].append(self.ch_names.index(target_name_reref))
57
69
 
58
70
  self.fbands = settings.frequency_ranges_hz
59
71
  self.fband_ranges: list = []
60
72
  self.result_keys = []
61
73
 
62
- self.raw_info = create_info(ch_names=self.ch_names, sfreq=self.sfreq)
63
- self.raw_array: "RawArray"
64
- self.epochs: "Epochs"
65
74
  self.prev_batch_shape: tuple = (-1, -1) # sentinel value
66
75
 
67
76
  def calc_feature(self, data: np.ndarray) -> dict:
68
- from mne.io import RawArray
69
- from mne import Epochs
70
77
  from mne_connectivity import spectral_connectivity_epochs
71
- import pandas as pd
72
-
73
- time_samples_s = data.shape[1] / self.sfreq
74
- epoch_length: float = 1 # TODO: Make this a parameter?
75
-
76
- if epoch_length > time_samples_s:
77
- raise ValueError(
78
- f"the intended epoch length for mne connectivity: {epoch_length}s"
79
- f" are longer than the passed data array {np.round(time_samples_s, 2)}s"
80
- )
81
-
82
- # Only reinitialize the raw_array and epochs object if the data shape has changed
83
- # That could mean that the channels have been re-selected, or we're in the last batch
84
- # TODO: If sfreq or channels change, do we re-initialize the whole Stream object?
85
- if data.shape != self.prev_batch_shape:
86
- self.raw_array = RawArray(
87
- data=data,
88
- info=self.raw_info,
89
- copy=None, # type: ignore
90
- verbose=False,
91
- )
92
-
93
- # self.events = make_fixed_length_events(self.raw_array, duration=epoch_length)
94
- # Equivalent code for those parameters:
95
- event_times = np.arange(
96
- 0, data.shape[-1], self.sfreq * epoch_length, dtype=int
97
- )
98
- events = np.column_stack(
99
- (
100
- event_times,
101
- np.zeros_like(event_times, dtype=int),
102
- np.ones_like(event_times, dtype=int),
103
- )
104
- )
105
-
106
- # there need to be minimum 2 of two epochs, otherwise mne_connectivity
107
- # is not correctly initialized
108
- if events.shape[0] < 2:
109
- raise RuntimeError(
110
- f"A minimum of 2 epochs is required for mne_connectivity,"
111
- f" got only {events.shape[0]}. Increase settings['segment_length_features_ms']"
112
- )
113
-
114
- self.epochs = Epochs(
115
- self.raw_array,
116
- events=events,
117
- event_id={"rest": 1},
118
- tmin=0,
119
- tmax=epoch_length,
120
- baseline=None,
121
- reject_by_annotation=True,
122
- verbose=False,
123
- )
124
-
125
- # Trick the function "spectral_connectivity_epochs" into not calling "add_annotations_to_metadata"
126
- # TODO: This is a hack, and maybe needs a fix in the mne_connectivity library
127
- self.epochs._metadata = pd.DataFrame(index=np.arange(events.shape[0]))
128
-
129
- else:
130
- # As long as the initialization parameters, channels, sfreq and batch size are the same
131
- # We can re-use the existing epochs object by updating the raw data
132
- self.raw_array._data = data
133
- self.epochs._raw = self.raw_array
134
78
 
135
79
  # n_jobs is here kept to 1, since setup of the multiprocessing Pool
136
80
  # takes longer than most batch computing sizes
137
81
  spec_out = spectral_connectivity_epochs(
138
- data=self.epochs,
82
+ data=np.expand_dims(data, axis=0), # add singleton epoch dimension
139
83
  sfreq=self.sfreq,
140
84
  method=self.method,
141
85
  mode=self.mode,
142
- indices=(np.array([0, 0, 1, 1]), np.array([2, 3, 2, 3])),
86
+ indices=self.indices,
143
87
  verbose=False,
144
88
  )
145
89
  dat_conn: np.ndarray = spec_out.get_data()
@@ -149,20 +93,27 @@ class MNEConnectivity(NMFeature):
149
93
  for fband_range in self.fbands.values():
150
94
  self.fband_ranges.append(
151
95
  np.where(
152
- (np.array(spec_out.freqs) > fband_range[0])
153
- & (np.array(spec_out.freqs) < fband_range[1])
96
+ (np.array(spec_out.freqs) >= fband_range[0])
97
+ & (np.array(spec_out.freqs) <= fband_range[1])
154
98
  )[0]
155
99
  )
156
100
 
157
- # TODO: If I compute the mean for the entire fband, results are almost the same before
158
- # normalization (0.9999999... vs 1.0), but some change wildly after normalization (-3 vs 0)
159
- # Investigate why, is this a bug in normalization?
160
101
  feature_results = {}
161
- for conn in np.arange(dat_conn.shape[0]):
162
- for fband_idx, fband in enumerate(self.fbands):
163
- feature_results["_".join(["ch1", self.method, str(conn), fband])] = (
164
- np.mean(dat_conn[conn, self.fband_ranges[fband_idx]])
165
- )
102
+ for con_idx in np.arange(dat_conn.shape[0]):
103
+ for fband_idx, fband_name in enumerate(self.fbands):
104
+ # TODO: Add support for max_fband and max_allfbands
105
+ feature_results[
106
+ "_".join(
107
+ [
108
+ self.method,
109
+ self.channels[con_idx][0], # seed channel name
110
+ "to",
111
+ self.channels[con_idx][1], # target channel name
112
+ "mean_fband",
113
+ fband_name,
114
+ ]
115
+ )
116
+ ] = np.mean(dat_conn[con_idx, self.fband_ranges[fband_idx]])
166
117
 
167
118
  # Store current experiment parameters to check if re-initialization is needed
168
119
  self.prev_batch_shape = data.shape
@@ -49,7 +49,8 @@ class PyNMState:
49
49
  self,
50
50
  websocket_manager: WebsocketManager | None = None,
51
51
  ) -> None:
52
-
52
+ # Clear stop event to enable _process_queue
53
+ self.stop_event.clear()
53
54
  self.websocket_manager = websocket_manager
54
55
 
55
56
  # Create decoder
@@ -113,6 +114,7 @@ class PyNMState:
113
114
  from mne_lsl.lsl import resolve_streams
114
115
 
115
116
  logger.info("resolving streams")
117
+ logger.info(f"passed lsl_stream_name: {lsl_stream_name}")
116
118
  lsl_streams = resolve_streams()
117
119
 
118
120
  for stream in lsl_streams:
@@ -142,14 +144,21 @@ class PyNMState:
142
144
  # set all used column to 0
143
145
  #channels.loc[:, "used"] = 0
144
146
 
145
- logger.info(channels)
147
+ logger.info(f"channels: {channels}")
146
148
  sfreq = stream.sfreq
149
+ logger.info(f"sfreq: {sfreq}")
147
150
 
148
- self.stream: Stream = Stream(
149
- sfreq=sfreq,
150
- line_noise=line_noise,
151
- channels=channels,
152
- )
151
+ logger.info("Starting setup LSL stream...")
152
+ try:
153
+ self.stream: Stream = Stream(
154
+ sfreq=sfreq,
155
+ line_noise=line_noise,
156
+ channels=channels,
157
+ )
158
+ except Exception as e:
159
+ logger.error(f"Error setting up LSL stream: {e}")
160
+
161
+ logger.info("Finish setup LSL stream...")
153
162
  logger.info("stream setup")
154
163
  logger.info("settings setup")
155
164
 
@@ -39,6 +39,7 @@ class WebsocketManager:
39
39
 
40
40
  # Combine IP and port to create a unique client ID
41
41
  async def send_cbor(self, object: dict):
42
+ self.logger.debug(f"app_socket.send_cbor")
42
43
  if not self.active_connections:
43
44
  self.logger.warning("No active connection to send message.")
44
45
  return
@@ -54,6 +55,7 @@ class WebsocketManager:
54
55
  for connection in self.active_connections:
55
56
  try:
56
57
  await connection.send_bytes(cbor_data)
58
+ self.logger.debug(f"CBOR message sent to {connection.client}")
57
59
  except RuntimeError as e:
58
60
  self.logger.error(f"Error sending CBOR message: {e}")
59
61
  self.disconnected.append(connection)
@@ -14011,8 +14011,8 @@ const useSessionStore = createStore("session", (set2, get) => ({
14011
14011
  throw error;
14012
14012
  }
14013
14013
  },
14014
- initializeLSLStream: async () => {
14015
- const lslSource = get().lslSource;
14014
+ initializeLSLStream: async (lslStreamName) => {
14015
+ get().lslSource;
14016
14016
  const streamParameters = get().streamParameters;
14017
14017
  try {
14018
14018
  const response = await fetch(getBackendURL("/api/setup-LSL-stream"), {
@@ -14022,7 +14022,8 @@ const useSessionStore = createStore("session", (set2, get) => ({
14022
14022
  "Content-Type": "application/json"
14023
14023
  },
14024
14024
  body: JSON.stringify({
14025
- stream_name: lslSource.availableStreams[0].name,
14025
+ stream_name: lslStreamName,
14026
+ //lslSource.availableStreams[0].name,
14026
14027
  line_noise: streamParameters.lineNoise
14027
14028
  })
14028
14029
  });
@@ -299017,7 +299018,7 @@ const StatusBarSettingsInfo = () => {
299017
299018
  return t3;
299018
299019
  };
299019
299020
  const Settings = () => {
299020
- const $ = compilerRuntimeExports.c(117);
299021
+ const $ = compilerRuntimeExports.c(118);
299021
299022
  const navigate = useNavigate();
299022
299023
  const settings = useSettingsStore(_temp4$1);
299023
299024
  const uploadSettings = useSettingsStore(_temp5$1);
@@ -299110,6 +299111,7 @@ const Settings = () => {
299110
299111
  let T2;
299111
299112
  let T3;
299112
299113
  let T4;
299114
+ let postprocessingSettingsKeys;
299113
299115
  let t10;
299114
299116
  let t11;
299115
299117
  let t12;
@@ -299121,7 +299123,6 @@ const Settings = () => {
299121
299123
  let t18;
299122
299124
  let t19;
299123
299125
  let t20;
299124
- let t21;
299125
299126
  let t5;
299126
299127
  let t6;
299127
299128
  let t7;
@@ -299130,33 +299131,33 @@ const Settings = () => {
299130
299131
  if ($[13] !== frequencyRangeOrder || $[14] !== handleChangeSettings || $[15] !== settings || $[16] !== updateFrequencyRangeOrder || $[17] !== validationErrors) {
299131
299132
  const featureSettingsKeys = Object.keys(settings.features).filter((feature) => settings.features[feature]).map(_temp10);
299132
299133
  const enabledFeatures = filterObjectByKeys(settings, featureSettingsKeys);
299133
- let t222;
299134
+ let t212;
299134
299135
  if ($[40] === Symbol.for("react.memo_cache_sentinel")) {
299135
- t222 = ["preprocessing", "raw_resampling_settings", "raw_normalization_settings", "preprocessing_filter"];
299136
- $[40] = t222;
299136
+ t212 = ["preprocessing", "raw_resampling_settings", "raw_normalization_settings", "preprocessing_filter"];
299137
+ $[40] = t212;
299137
299138
  } else {
299138
- t222 = $[40];
299139
+ t212 = $[40];
299139
299140
  }
299140
- const preprocessingSettingsKeys = t222;
299141
- let t232;
299141
+ const preprocessingSettingsKeys = t212;
299142
+ let t222;
299142
299143
  if ($[41] === Symbol.for("react.memo_cache_sentinel")) {
299143
- t232 = ["postprocessing", "feature_normalization_settings", "project_cortex_settings", "project_subcortex_settings"];
299144
- $[41] = t232;
299144
+ t222 = ["postprocessing", "feature_normalization_settings", "project_cortex_settings", "project_subcortex_settings"];
299145
+ $[41] = t222;
299145
299146
  } else {
299146
- t232 = $[41];
299147
+ t222 = $[41];
299147
299148
  }
299148
- const postprocessingSettingsKeys = t232;
299149
- let t242;
299149
+ postprocessingSettingsKeys = t222;
299150
+ let t232;
299150
299151
  if ($[42] === Symbol.for("react.memo_cache_sentinel")) {
299151
- t242 = ["sampling_rate_features_hz", "segment_length_features_ms"];
299152
- $[42] = t242;
299152
+ t232 = ["sampling_rate_features_hz", "segment_length_features_ms"];
299153
+ $[42] = t232;
299153
299154
  } else {
299154
- t242 = $[42];
299155
+ t232 = $[42];
299155
299156
  }
299156
- const generalSettingsKeys = t242;
299157
+ const generalSettingsKeys = t232;
299157
299158
  T4 = Stack;
299158
- t20 = "center";
299159
- t21 = 2;
299159
+ t19 = "center";
299160
+ t20 = 2;
299160
299161
  T3 = Stack;
299161
299162
  t11 = "row";
299162
299163
  t12 = "flex-start";
@@ -299164,59 +299165,59 @@ const Settings = () => {
299164
299165
  t14 = "fit-content";
299165
299166
  t15 = 2;
299166
299167
  t16 = 2;
299167
- let t252;
299168
+ let t242;
299168
299169
  if ($[43] === Symbol.for("react.memo_cache_sentinel")) {
299169
- t252 = {
299170
+ t242 = {
299170
299171
  minWidth: "33%"
299171
299172
  };
299172
- $[43] = t252;
299173
+ $[43] = t242;
299173
299174
  } else {
299174
- t252 = $[43];
299175
+ t242 = $[43];
299175
299176
  }
299176
- let t262;
299177
+ let t252;
299177
299178
  if ($[44] !== handleChangeSettings || $[45] !== settings || $[46] !== validationErrors) {
299178
- t262 = /* @__PURE__ */ jsxRuntimeExports.jsx(TitledBox, { title: "General Settings", children: generalSettingsKeys.map((key) => /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsSection, { settings: settings[key], path: [key], onChange: handleChangeSettings, errors: validationErrors }, `${key}_settingsSection`)) });
299179
+ t252 = /* @__PURE__ */ jsxRuntimeExports.jsx(TitledBox, { title: "General Settings", children: generalSettingsKeys.map((key) => /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsSection, { settings: settings[key], path: [key], onChange: handleChangeSettings, errors: validationErrors }, `${key}_settingsSection`)) });
299179
299180
  $[44] = handleChangeSettings;
299180
299181
  $[45] = settings;
299181
299182
  $[46] = validationErrors;
299182
- $[47] = t262;
299183
+ $[47] = t252;
299183
299184
  } else {
299184
- t262 = $[47];
299185
+ t252 = $[47];
299185
299186
  }
299186
- let t272;
299187
+ let t262;
299187
299188
  if ($[48] !== frequencyRangeOrder || $[49] !== handleChangeSettings || $[50] !== settings.frequency_ranges_hz || $[51] !== updateFrequencyRangeOrder || $[52] !== validationErrors) {
299188
- t272 = /* @__PURE__ */ jsxRuntimeExports.jsx(TitledBox, { title: "Frequency Ranges", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FrequencyRangeList, { ranges: settings.frequency_ranges_hz, rangeOrder: frequencyRangeOrder, onChange: handleChangeSettings, onOrderChange: updateFrequencyRangeOrder, errors: validationErrors }) });
299189
+ t262 = /* @__PURE__ */ jsxRuntimeExports.jsx(TitledBox, { title: "Frequency Ranges", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FrequencyRangeList, { ranges: settings.frequency_ranges_hz, rangeOrder: frequencyRangeOrder, onChange: handleChangeSettings, onOrderChange: updateFrequencyRangeOrder, errors: validationErrors }) });
299189
299190
  $[48] = frequencyRangeOrder;
299190
299191
  $[49] = handleChangeSettings;
299191
299192
  $[50] = settings.frequency_ranges_hz;
299192
299193
  $[51] = updateFrequencyRangeOrder;
299193
299194
  $[52] = validationErrors;
299194
- $[53] = t272;
299195
+ $[53] = t262;
299195
299196
  } else {
299196
- t272 = $[53];
299197
+ t262 = $[53];
299197
299198
  }
299198
- if ($[54] !== t262 || $[55] !== t272) {
299199
- t17 = /* @__PURE__ */ jsxRuntimeExports.jsxs(Stack, { sx: t252, children: [
299200
- t262,
299201
- t272
299199
+ if ($[54] !== t252 || $[55] !== t262) {
299200
+ t17 = /* @__PURE__ */ jsxRuntimeExports.jsxs(Stack, { sx: t242, children: [
299201
+ t252,
299202
+ t262
299202
299203
  ] });
299203
- $[54] = t262;
299204
- $[55] = t272;
299204
+ $[54] = t252;
299205
+ $[55] = t262;
299205
299206
  $[56] = t17;
299206
299207
  } else {
299207
299208
  t17 = $[56];
299208
299209
  }
299209
- let t282;
299210
+ let t272;
299210
299211
  if ($[57] === Symbol.for("react.memo_cache_sentinel")) {
299211
- t282 = {
299212
+ t272 = {
299212
299213
  borderRadius: 3
299213
299214
  };
299214
- $[57] = t282;
299215
+ $[57] = t272;
299215
299216
  } else {
299216
- t282 = $[57];
299217
+ t272 = $[57];
299217
299218
  }
299218
299219
  if ($[58] !== handleChangeSettings || $[59] !== settings || $[60] !== validationErrors) {
299219
- t18 = /* @__PURE__ */ jsxRuntimeExports.jsx(TitledBox, { title: "Preprocessing Settings", sx: t282, children: preprocessingSettingsKeys.map((key_0) => /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsSection, { settings: settings[key_0], path: [key_0], onChange: handleChangeSettings, errors: validationErrors }, `${key_0}_settingsSection`)) });
299220
+ t18 = /* @__PURE__ */ jsxRuntimeExports.jsx(TitledBox, { title: "Preprocessing Settings", sx: t272, children: preprocessingSettingsKeys.map((key_0) => /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsSection, { settings: settings[key_0], path: [key_0], onChange: handleChangeSettings, errors: validationErrors }, `${key_0}_settingsSection`)) });
299220
299221
  $[58] = handleChangeSettings;
299221
299222
  $[59] = settings;
299222
299223
  $[60] = validationErrors;
@@ -299224,69 +299225,51 @@ const Settings = () => {
299224
299225
  } else {
299225
299226
  t18 = $[61];
299226
299227
  }
299227
- let t292;
299228
- if ($[62] === Symbol.for("react.memo_cache_sentinel")) {
299229
- t292 = {
299230
- borderRadius: 3
299231
- };
299232
- $[62] = t292;
299233
- } else {
299234
- t292 = $[62];
299235
- }
299236
- if ($[63] !== handleChangeSettings || $[64] !== settings || $[65] !== validationErrors) {
299237
- t19 = /* @__PURE__ */ jsxRuntimeExports.jsx(TitledBox, { title: "Postprocessing Settings", sx: t292, children: postprocessingSettingsKeys.map((key_1) => /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsSection, { settings: settings[key_1], path: [key_1], onChange: handleChangeSettings, errors: validationErrors }, `${key_1}_settingsSection`)) });
299238
- $[63] = handleChangeSettings;
299239
- $[64] = settings;
299240
- $[65] = validationErrors;
299241
- $[66] = t19;
299242
- } else {
299243
- t19 = $[66];
299244
- }
299245
299228
  T2 = TitledBox;
299246
299229
  t10 = "Feature Settings";
299247
299230
  T1 = Stack;
299248
299231
  t7 = "row";
299249
299232
  t8 = 2;
299250
- let t302;
299251
- if ($[67] === Symbol.for("react.memo_cache_sentinel")) {
299252
- t302 = ["features"];
299253
- $[67] = t302;
299233
+ let t282;
299234
+ if ($[62] === Symbol.for("react.memo_cache_sentinel")) {
299235
+ t282 = ["features"];
299236
+ $[62] = t282;
299254
299237
  } else {
299255
- t302 = $[67];
299238
+ t282 = $[62];
299256
299239
  }
299257
- let t31;
299258
- if ($[68] === Symbol.for("react.memo_cache_sentinel")) {
299259
- t31 = {
299240
+ let t292;
299241
+ if ($[63] === Symbol.for("react.memo_cache_sentinel")) {
299242
+ t292 = {
299260
299243
  alignSelf: "flex-start"
299261
299244
  };
299262
- $[68] = t31;
299245
+ $[63] = t292;
299263
299246
  } else {
299264
- t31 = $[68];
299265
- }
299266
- if ($[69] !== handleChangeSettings || $[70] !== settings.features || $[71] !== validationErrors) {
299267
- t9 = /* @__PURE__ */ jsxRuntimeExports.jsx(Box, { alignSelf: "flex-start", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsSection, { settings: settings.features, path: t302, onChange: handleChangeSettings, sx: t31, errors: validationErrors }) });
299268
- $[69] = handleChangeSettings;
299269
- $[70] = settings.features;
299270
- $[71] = validationErrors;
299271
- $[72] = t9;
299247
+ t292 = $[63];
299248
+ }
299249
+ if ($[64] !== handleChangeSettings || $[65] !== settings.features || $[66] !== validationErrors) {
299250
+ t9 = /* @__PURE__ */ jsxRuntimeExports.jsx(Box, { alignSelf: "flex-start", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsSection, { settings: settings.features, path: t282, onChange: handleChangeSettings, sx: t292, errors: validationErrors }) });
299251
+ $[64] = handleChangeSettings;
299252
+ $[65] = settings.features;
299253
+ $[66] = validationErrors;
299254
+ $[67] = t9;
299272
299255
  } else {
299273
- t9 = $[72];
299256
+ t9 = $[67];
299274
299257
  }
299275
299258
  T0 = Stack;
299276
299259
  t5 = "flex-start";
299277
- let t32;
299278
- if ($[73] !== handleChangeSettings || $[74] !== validationErrors) {
299279
- t32 = (t33) => {
299280
- const [feature_1, featureSettings] = t33;
299260
+ let t302;
299261
+ if ($[68] !== handleChangeSettings || $[69] !== validationErrors) {
299262
+ t302 = (t312) => {
299263
+ const [feature_1, featureSettings] = t312;
299281
299264
  return /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleBox, { title: formatKey(feature_1), defaultExpanded: false, children: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsSection, { settings: featureSettings, path: [feature_1], onChange: handleChangeSettings, errors: validationErrors }, `${feature_1}_settingsSection`) }, `${feature_1}_collapsibleBox`);
299282
299265
  };
299283
- $[73] = handleChangeSettings;
299284
- $[74] = validationErrors;
299285
- $[75] = t32;
299266
+ $[68] = handleChangeSettings;
299267
+ $[69] = validationErrors;
299268
+ $[70] = t302;
299286
299269
  } else {
299287
- t32 = $[75];
299270
+ t302 = $[70];
299288
299271
  }
299289
- t6 = Object.entries(enabledFeatures).map(t32);
299272
+ t6 = Object.entries(enabledFeatures).map(t302);
299290
299273
  $[13] = frequencyRangeOrder;
299291
299274
  $[14] = handleChangeSettings;
299292
299275
  $[15] = settings;
@@ -299297,18 +299280,18 @@ const Settings = () => {
299297
299280
  $[20] = T2;
299298
299281
  $[21] = T3;
299299
299282
  $[22] = T4;
299300
- $[23] = t10;
299301
- $[24] = t11;
299302
- $[25] = t12;
299303
- $[26] = t13;
299304
- $[27] = t14;
299305
- $[28] = t15;
299306
- $[29] = t16;
299307
- $[30] = t17;
299308
- $[31] = t18;
299309
- $[32] = t19;
299310
- $[33] = t20;
299311
- $[34] = t21;
299283
+ $[23] = postprocessingSettingsKeys;
299284
+ $[24] = t10;
299285
+ $[25] = t11;
299286
+ $[26] = t12;
299287
+ $[27] = t13;
299288
+ $[28] = t14;
299289
+ $[29] = t15;
299290
+ $[30] = t16;
299291
+ $[31] = t17;
299292
+ $[32] = t18;
299293
+ $[33] = t19;
299294
+ $[34] = t20;
299312
299295
  $[35] = t5;
299313
299296
  $[36] = t6;
299314
299297
  $[37] = t7;
@@ -299320,139 +299303,159 @@ const Settings = () => {
299320
299303
  T2 = $[20];
299321
299304
  T3 = $[21];
299322
299305
  T4 = $[22];
299323
- t10 = $[23];
299324
- t11 = $[24];
299325
- t12 = $[25];
299326
- t13 = $[26];
299327
- t14 = $[27];
299328
- t15 = $[28];
299329
- t16 = $[29];
299330
- t17 = $[30];
299331
- t18 = $[31];
299332
- t19 = $[32];
299333
- t20 = $[33];
299334
- t21 = $[34];
299306
+ postprocessingSettingsKeys = $[23];
299307
+ t10 = $[24];
299308
+ t11 = $[25];
299309
+ t12 = $[26];
299310
+ t13 = $[27];
299311
+ t14 = $[28];
299312
+ t15 = $[29];
299313
+ t16 = $[30];
299314
+ t17 = $[31];
299315
+ t18 = $[32];
299316
+ t19 = $[33];
299317
+ t20 = $[34];
299335
299318
  t5 = $[35];
299336
299319
  t6 = $[36];
299337
299320
  t7 = $[37];
299338
299321
  t8 = $[38];
299339
299322
  t9 = $[39];
299340
299323
  }
299341
- let t22;
299342
- if ($[76] !== T0 || $[77] !== t5 || $[78] !== t6) {
299343
- t22 = /* @__PURE__ */ jsxRuntimeExports.jsx(T0, { alignSelf: t5, children: t6 });
299344
- $[76] = T0;
299345
- $[77] = t5;
299346
- $[78] = t6;
299347
- $[79] = t22;
299324
+ let t21;
299325
+ if ($[71] !== T0 || $[72] !== t5 || $[73] !== t6) {
299326
+ t21 = /* @__PURE__ */ jsxRuntimeExports.jsx(T0, { alignSelf: t5, children: t6 });
299327
+ $[71] = T0;
299328
+ $[72] = t5;
299329
+ $[73] = t6;
299330
+ $[74] = t21;
299348
299331
  } else {
299349
- t22 = $[79];
299332
+ t21 = $[74];
299350
299333
  }
299351
- let t23;
299352
- if ($[80] !== T1 || $[81] !== t22 || $[82] !== t7 || $[83] !== t8 || $[84] !== t9) {
299353
- t23 = /* @__PURE__ */ jsxRuntimeExports.jsxs(T1, { direction: t7, gap: t8, children: [
299334
+ let t22;
299335
+ if ($[75] !== T1 || $[76] !== t21 || $[77] !== t7 || $[78] !== t8 || $[79] !== t9) {
299336
+ t22 = /* @__PURE__ */ jsxRuntimeExports.jsxs(T1, { direction: t7, gap: t8, children: [
299354
299337
  t9,
299355
- t22
299338
+ t21
299356
299339
  ] });
299357
- $[80] = T1;
299358
- $[81] = t22;
299359
- $[82] = t7;
299360
- $[83] = t8;
299361
- $[84] = t9;
299362
- $[85] = t23;
299340
+ $[75] = T1;
299341
+ $[76] = t21;
299342
+ $[77] = t7;
299343
+ $[78] = t8;
299344
+ $[79] = t9;
299345
+ $[80] = t22;
299346
+ } else {
299347
+ t22 = $[80];
299348
+ }
299349
+ let t23;
299350
+ if ($[81] !== T2 || $[82] !== t10 || $[83] !== t22) {
299351
+ t23 = /* @__PURE__ */ jsxRuntimeExports.jsx(T2, { title: t10, children: t22 });
299352
+ $[81] = T2;
299353
+ $[82] = t10;
299354
+ $[83] = t22;
299355
+ $[84] = t23;
299363
299356
  } else {
299364
- t23 = $[85];
299357
+ t23 = $[84];
299365
299358
  }
299366
299359
  let t24;
299367
- if ($[86] !== T2 || $[87] !== t10 || $[88] !== t23) {
299368
- t24 = /* @__PURE__ */ jsxRuntimeExports.jsx(T2, { title: t10, children: t23 });
299369
- $[86] = T2;
299370
- $[87] = t10;
299371
- $[88] = t23;
299372
- $[89] = t24;
299360
+ if ($[85] === Symbol.for("react.memo_cache_sentinel")) {
299361
+ t24 = {
299362
+ borderRadius: 3
299363
+ };
299364
+ $[85] = t24;
299373
299365
  } else {
299374
- t24 = $[89];
299366
+ t24 = $[85];
299375
299367
  }
299376
299368
  let t25;
299377
- if ($[90] !== T3 || $[91] !== t11 || $[92] !== t12 || $[93] !== t13 || $[94] !== t14 || $[95] !== t15 || $[96] !== t16 || $[97] !== t17 || $[98] !== t18 || $[99] !== t19 || $[100] !== t24) {
299378
- t25 = /* @__PURE__ */ jsxRuntimeExports.jsxs(T3, { direction: t11, alignItems: t12, justifyContent: t13, width: t14, gap: t15, p: t16, children: [
299369
+ if ($[86] !== handleChangeSettings || $[87] !== postprocessingSettingsKeys || $[88] !== settings || $[89] !== validationErrors) {
299370
+ t25 = /* @__PURE__ */ jsxRuntimeExports.jsx(TitledBox, { title: "Postprocessing Settings", sx: t24, children: postprocessingSettingsKeys.map((key_1) => /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsSection, { settings: settings[key_1], path: [key_1], onChange: handleChangeSettings, errors: validationErrors }, `${key_1}_settingsSection`)) });
299371
+ $[86] = handleChangeSettings;
299372
+ $[87] = postprocessingSettingsKeys;
299373
+ $[88] = settings;
299374
+ $[89] = validationErrors;
299375
+ $[90] = t25;
299376
+ } else {
299377
+ t25 = $[90];
299378
+ }
299379
+ let t26;
299380
+ if ($[91] !== T3 || $[92] !== t11 || $[93] !== t12 || $[94] !== t13 || $[95] !== t14 || $[96] !== t15 || $[97] !== t16 || $[98] !== t17 || $[99] !== t18 || $[100] !== t23 || $[101] !== t25) {
299381
+ t26 = /* @__PURE__ */ jsxRuntimeExports.jsxs(T3, { direction: t11, alignItems: t12, justifyContent: t13, width: t14, gap: t15, p: t16, children: [
299379
299382
  t17,
299380
299383
  t18,
299381
- t19,
299382
- t24
299384
+ t23,
299385
+ t25
299383
299386
  ] });
299384
- $[90] = T3;
299385
- $[91] = t11;
299386
- $[92] = t12;
299387
- $[93] = t13;
299388
- $[94] = t14;
299389
- $[95] = t15;
299390
- $[96] = t16;
299391
- $[97] = t17;
299392
- $[98] = t18;
299393
- $[99] = t19;
299394
- $[100] = t24;
299387
+ $[91] = T3;
299388
+ $[92] = t11;
299389
+ $[93] = t12;
299390
+ $[94] = t13;
299391
+ $[95] = t14;
299392
+ $[96] = t15;
299393
+ $[97] = t16;
299394
+ $[98] = t17;
299395
+ $[99] = t18;
299396
+ $[100] = t23;
299395
299397
  $[101] = t25;
299398
+ $[102] = t26;
299396
299399
  } else {
299397
- t25 = $[101];
299400
+ t26 = $[102];
299398
299401
  }
299399
- let t26;
299400
- if ($[102] === Symbol.for("react.memo_cache_sentinel")) {
299401
- t26 = {
299402
+ let t27;
299403
+ if ($[103] === Symbol.for("react.memo_cache_sentinel")) {
299404
+ t27 = {
299402
299405
  position: "absolute",
299403
299406
  bottom: "2.5rem",
299404
299407
  right: "1rem",
299405
299408
  gap: 1
299406
299409
  };
299407
- $[102] = t26;
299410
+ $[103] = t27;
299408
299411
  } else {
299409
- t26 = $[102];
299410
- }
299411
- let t27;
299412
- if ($[103] !== handleResetSettings) {
299413
- t27 = /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "contained", color: "primary", onClick: handleResetSettings, children: "Reset Settings" });
299414
- $[103] = handleResetSettings;
299415
- $[104] = t27;
299416
- } else {
299417
- t27 = $[104];
299412
+ t27 = $[103];
299418
299413
  }
299419
299414
  let t28;
299420
- if ($[105] !== saveAndStream || $[106] !== validationErrors) {
299421
- t28 = /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "contained", color: "primary", onClick: saveAndStream, disabled: validationErrors, children: "Save & Run Stream" });
299422
- $[105] = saveAndStream;
299423
- $[106] = validationErrors;
299424
- $[107] = t28;
299415
+ if ($[104] !== handleResetSettings) {
299416
+ t28 = /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "contained", color: "primary", onClick: handleResetSettings, children: "Reset Settings" });
299417
+ $[104] = handleResetSettings;
299418
+ $[105] = t28;
299425
299419
  } else {
299426
- t28 = $[107];
299420
+ t28 = $[105];
299427
299421
  }
299428
299422
  let t29;
299429
- if ($[108] !== t27 || $[109] !== t28) {
299430
- t29 = /* @__PURE__ */ jsxRuntimeExports.jsxs(Stack, { direction: "row", width: "fit-content", sx: t26, backgroundColor: "background.level3", borderRadius: 2, border: "1px solid", borderColor: "divider", p: 1, children: [
299431
- t27,
299432
- t28
299423
+ if ($[106] !== saveAndStream || $[107] !== validationErrors) {
299424
+ t29 = /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "contained", color: "primary", onClick: saveAndStream, disabled: validationErrors, children: "Save & Run Stream" });
299425
+ $[106] = saveAndStream;
299426
+ $[107] = validationErrors;
299427
+ $[108] = t29;
299428
+ } else {
299429
+ t29 = $[108];
299430
+ }
299431
+ let t30;
299432
+ if ($[109] !== t28 || $[110] !== t29) {
299433
+ t30 = /* @__PURE__ */ jsxRuntimeExports.jsxs(Stack, { direction: "row", width: "fit-content", sx: t27, backgroundColor: "background.level3", borderRadius: 2, border: "1px solid", borderColor: "divider", p: 1, children: [
299434
+ t28,
299435
+ t29
299433
299436
  ] });
299434
- $[108] = t27;
299435
299437
  $[109] = t28;
299436
299438
  $[110] = t29;
299439
+ $[111] = t30;
299437
299440
  } else {
299438
- t29 = $[110];
299441
+ t30 = $[111];
299439
299442
  }
299440
- let t30;
299441
- if ($[111] !== T4 || $[112] !== t20 || $[113] !== t21 || $[114] !== t25 || $[115] !== t29) {
299442
- t30 = /* @__PURE__ */ jsxRuntimeExports.jsxs(T4, { justifyContent: t20, pb: t21, children: [
299443
- t25,
299444
- t29
299443
+ let t31;
299444
+ if ($[112] !== T4 || $[113] !== t19 || $[114] !== t20 || $[115] !== t26 || $[116] !== t30) {
299445
+ t31 = /* @__PURE__ */ jsxRuntimeExports.jsxs(T4, { justifyContent: t19, pb: t20, children: [
299446
+ t26,
299447
+ t30
299445
299448
  ] });
299446
- $[111] = T4;
299447
- $[112] = t20;
299448
- $[113] = t21;
299449
- $[114] = t25;
299450
- $[115] = t29;
299449
+ $[112] = T4;
299450
+ $[113] = t19;
299451
+ $[114] = t20;
299452
+ $[115] = t26;
299451
299453
  $[116] = t30;
299454
+ $[117] = t31;
299452
299455
  } else {
299453
- t30 = $[116];
299456
+ t31 = $[117];
299454
299457
  }
299455
- return t30;
299458
+ return t31;
299456
299459
  };
299457
299460
  function _temp$2(key) {
299458
299461
  return key.startsWith("__");
@@ -299577,7 +299580,7 @@ const theme = createTheme({
299577
299580
  }
299578
299581
  });
299579
299582
  const StreamSelector = () => {
299580
- const $ = compilerRuntimeExports.c(67);
299583
+ const $ = compilerRuntimeExports.c(68);
299581
299584
  const [searchingStreams, setSearchingStreams] = reactExports.useState(false);
299582
299585
  const [selectedStreamName, setSelectedStreamName] = reactExports.useState("");
299583
299586
  const [isStreamNameValid, setIsStreamNameValid] = reactExports.useState(false);
@@ -299700,55 +299703,56 @@ const StreamSelector = () => {
299700
299703
  }
299701
299704
  handleStreamNameChange = t102;
299702
299705
  let t112;
299703
- if ($[30] !== initializeLSLStream || $[31] !== isStreamNameValid) {
299706
+ if ($[30] !== initializeLSLStream || $[31] !== isStreamNameValid || $[32] !== selectedStreamName) {
299704
299707
  t112 = async () => {
299705
299708
  if (isStreamNameValid) {
299706
- await initializeLSLStream();
299709
+ await initializeLSLStream(selectedStreamName);
299707
299710
  }
299708
299711
  };
299709
299712
  $[30] = initializeLSLStream;
299710
299713
  $[31] = isStreamNameValid;
299711
- $[32] = t112;
299714
+ $[32] = selectedStreamName;
299715
+ $[33] = t112;
299712
299716
  } else {
299713
- t112 = $[32];
299717
+ t112 = $[33];
299714
299718
  }
299715
299719
  handleConnectStream = t112;
299716
299720
  let t122;
299717
- if ($[33] !== fetchLSLStreams) {
299721
+ if ($[34] !== fetchLSLStreams) {
299718
299722
  t122 = async () => {
299719
299723
  setSearchingStreams(true);
299720
299724
  await fetchLSLStreams();
299721
299725
  setSearchingStreams(false);
299722
299726
  };
299723
- $[33] = fetchLSLStreams;
299724
- $[34] = t122;
299727
+ $[34] = fetchLSLStreams;
299728
+ $[35] = t122;
299725
299729
  } else {
299726
- t122 = $[34];
299730
+ t122 = $[35];
299727
299731
  }
299728
299732
  const handleSearchStream = t122;
299729
299733
  T0 = TitledBox;
299730
299734
  t6 = "Read data from LSL stream";
299731
299735
  let t132;
299732
- if ($[35] !== searchingStreams) {
299736
+ if ($[36] !== searchingStreams) {
299733
299737
  t132 = searchingStreams ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
299734
299738
  "Searching for streams",
299735
299739
  /* @__PURE__ */ jsxRuntimeExports.jsx(CircularProgress, { size: 20, sx: {
299736
299740
  mx: 1
299737
299741
  }, color: "secondary" })
299738
299742
  ] }) : "Search for LSL Streams";
299739
- $[35] = searchingStreams;
299740
- $[36] = t132;
299743
+ $[36] = searchingStreams;
299744
+ $[37] = t132;
299741
299745
  } else {
299742
- t132 = $[36];
299746
+ t132 = $[37];
299743
299747
  }
299744
- if ($[37] !== handleSearchStream || $[38] !== searchingStreams || $[39] !== t132) {
299748
+ if ($[38] !== handleSearchStream || $[39] !== searchingStreams || $[40] !== t132) {
299745
299749
  t7 = /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "contained", onClick: handleSearchStream, disabled: searchingStreams, children: t132 });
299746
- $[37] = handleSearchStream;
299747
- $[38] = searchingStreams;
299748
- $[39] = t132;
299749
- $[40] = t7;
299750
+ $[38] = handleSearchStream;
299751
+ $[39] = searchingStreams;
299752
+ $[40] = t132;
299753
+ $[41] = t7;
299750
299754
  } else {
299751
- t7 = $[40];
299755
+ t7 = $[41];
299752
299756
  }
299753
299757
  t8 = lslSource.availableStreams.length > 0 ? formatStreams() : "No LSL streams found";
299754
299758
  $[11] = fetchLSLStreams;
@@ -299778,7 +299782,7 @@ const StreamSelector = () => {
299778
299782
  let t10;
299779
299783
  let t11;
299780
299784
  let t9;
299781
- if ($[41] === Symbol.for("react.memo_cache_sentinel")) {
299785
+ if ($[42] === Symbol.for("react.memo_cache_sentinel")) {
299782
299786
  t9 = {
299783
299787
  color: "#f4f4f4",
299784
299788
  flexGrow: 1
@@ -299794,85 +299798,85 @@ const StreamSelector = () => {
299794
299798
  color: "#f4f4f4"
299795
299799
  }
299796
299800
  };
299797
- $[41] = t10;
299798
- $[42] = t11;
299799
- $[43] = t9;
299801
+ $[42] = t10;
299802
+ $[43] = t11;
299803
+ $[44] = t9;
299800
299804
  } else {
299801
- t10 = $[41];
299802
- t11 = $[42];
299803
- t9 = $[43];
299805
+ t10 = $[42];
299806
+ t11 = $[43];
299807
+ t9 = $[44];
299804
299808
  }
299805
299809
  const t12 = !isStreamNameValid && selectedStreamName !== "";
299806
299810
  const t13 = !isStreamNameValid && selectedStreamName !== "" ? "Invalid stream name" : " ";
299807
299811
  let t14;
299808
- if ($[44] !== handleEnterStreamName || $[45] !== handleStreamNameChange || $[46] !== selectedStreamName || $[47] !== t12 || $[48] !== t13) {
299812
+ if ($[45] !== handleEnterStreamName || $[46] !== handleStreamNameChange || $[47] !== selectedStreamName || $[48] !== t12 || $[49] !== t13) {
299809
299813
  t14 = /* @__PURE__ */ jsxRuntimeExports.jsx(TextField, { label: "Selected LSL stream", fullWidth: true, size: "small", sx: t9, InputLabelProps: t10, InputProps: t11, value: selectedStreamName, onChange: handleStreamNameChange, onKeyDown: handleEnterStreamName, error: t12, helperText: t13 });
299810
- $[44] = handleEnterStreamName;
299811
- $[45] = handleStreamNameChange;
299812
- $[46] = selectedStreamName;
299813
- $[47] = t12;
299814
- $[48] = t13;
299815
- $[49] = t14;
299814
+ $[45] = handleEnterStreamName;
299815
+ $[46] = handleStreamNameChange;
299816
+ $[47] = selectedStreamName;
299817
+ $[48] = t12;
299818
+ $[49] = t13;
299819
+ $[50] = t14;
299816
299820
  } else {
299817
- t14 = $[49];
299821
+ t14 = $[50];
299818
299822
  }
299819
299823
  let t15;
299820
- if ($[50] === Symbol.for("react.memo_cache_sentinel")) {
299824
+ if ($[51] === Symbol.for("react.memo_cache_sentinel")) {
299821
299825
  t15 = {
299822
299826
  width: "fit-content"
299823
299827
  };
299824
- $[50] = t15;
299828
+ $[51] = t15;
299825
299829
  } else {
299826
- t15 = $[50];
299830
+ t15 = $[51];
299827
299831
  }
299828
299832
  const t16 = !isStreamNameValid;
299829
299833
  let t17;
299830
- if ($[51] !== handleConnectStream || $[52] !== t16) {
299834
+ if ($[52] !== handleConnectStream || $[53] !== t16) {
299831
299835
  t17 = /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { variant: "contained", onClick: handleConnectStream, sx: t15, disabled: t16, children: "Connect to stream" });
299832
- $[51] = handleConnectStream;
299833
- $[52] = t16;
299834
- $[53] = t17;
299836
+ $[52] = handleConnectStream;
299837
+ $[53] = t16;
299838
+ $[54] = t17;
299835
299839
  } else {
299836
- t17 = $[53];
299840
+ t17 = $[54];
299837
299841
  }
299838
299842
  let t18;
299839
- if ($[54] !== isStreamSetupCorrect || $[55] !== streamSetupMessage) {
299843
+ if ($[55] !== isStreamSetupCorrect || $[56] !== streamSetupMessage) {
299840
299844
  t18 = streamSetupMessage && /* @__PURE__ */ jsxRuntimeExports.jsx(Typography, { variant: "body2", color: isStreamSetupCorrect ? "success" : "error", mt: 2, children: streamSetupMessage });
299841
- $[54] = isStreamSetupCorrect;
299842
- $[55] = streamSetupMessage;
299843
- $[56] = t18;
299845
+ $[55] = isStreamSetupCorrect;
299846
+ $[56] = streamSetupMessage;
299847
+ $[57] = t18;
299844
299848
  } else {
299845
- t18 = $[56];
299849
+ t18 = $[57];
299846
299850
  }
299847
299851
  let t19;
299848
- if ($[57] !== t14 || $[58] !== t17 || $[59] !== t18) {
299852
+ if ($[58] !== t14 || $[59] !== t17 || $[60] !== t18) {
299849
299853
  t19 = /* @__PURE__ */ jsxRuntimeExports.jsxs(Stack, { direction: "row", width: "100%", gap: 2, children: [
299850
299854
  t14,
299851
299855
  t17,
299852
299856
  t18
299853
299857
  ] });
299854
- $[57] = t14;
299855
- $[58] = t17;
299856
- $[59] = t18;
299857
- $[60] = t19;
299858
+ $[58] = t14;
299859
+ $[59] = t17;
299860
+ $[60] = t18;
299861
+ $[61] = t19;
299858
299862
  } else {
299859
- t19 = $[60];
299863
+ t19 = $[61];
299860
299864
  }
299861
299865
  let t20;
299862
- if ($[61] !== T0 || $[62] !== t19 || $[63] !== t6 || $[64] !== t7 || $[65] !== t8) {
299866
+ if ($[62] !== T0 || $[63] !== t19 || $[64] !== t6 || $[65] !== t7 || $[66] !== t8) {
299863
299867
  t20 = /* @__PURE__ */ jsxRuntimeExports.jsxs(T0, { title: t6, children: [
299864
299868
  t7,
299865
299869
  t8,
299866
299870
  t19
299867
299871
  ] });
299868
- $[61] = T0;
299869
- $[62] = t19;
299870
- $[63] = t6;
299871
- $[64] = t7;
299872
- $[65] = t8;
299873
- $[66] = t20;
299872
+ $[62] = T0;
299873
+ $[63] = t19;
299874
+ $[64] = t6;
299875
+ $[65] = t7;
299876
+ $[66] = t8;
299877
+ $[67] = t20;
299874
299878
  } else {
299875
- t20 = $[66];
299879
+ t20 = $[67];
299876
299880
  }
299877
299881
  return t20;
299878
299882
  };
@@ -5,7 +5,7 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/charite.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>PyNeuromodulation</title>
8
- <script type="module" crossorigin src="/assets/index-NbJiOU5a.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-_6V8ZfAS.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/plotly-DTCwMlpS.js">
10
10
  </head>
11
11
  <body>
@@ -30,7 +30,7 @@ def main():
30
30
 
31
31
  player.start_player(chunk_size=30, n_repeat=5999999)
32
32
 
33
- App(run_in_webview=False, dev=False).launch()
33
+ App(run_in_webview=False, dev=False, debug=False).launch()
34
34
 
35
35
  if __name__ == "__main__":
36
36
  main()
@@ -1,5 +1,5 @@
1
1
  from typing import Any
2
- import logging
2
+ import py_neuromodulation as nm
3
3
  import multiprocessing as mp
4
4
 
5
5
 
@@ -13,28 +13,28 @@ class StreamBackendInterface:
13
13
  self.rawdata_queue = raw_data_queue
14
14
  self.control_queue = control_queue
15
15
 
16
- self.logger = logging.getLogger("PyNM")
17
-
18
16
  def send_command(self, command: str) -> None:
19
17
  """Send a command through the control queue"""
20
18
  try:
21
19
  self.control_queue.put(command)
22
20
  except Exception as e:
23
- self.logger.error(f"Error sending command: {e}")
21
+ nm.logger.error(f"Error sending command: {e}")
24
22
 
25
23
  def send_features(self, features: dict[str, Any]) -> None:
26
24
  """Send feature data through the feature queue"""
27
25
  try:
26
+ nm.logger.debug("backend_interface.send_features before feature put in queue")
28
27
  self.feature_queue.put(features)
28
+ nm.logger.debug("backend_interface.send_features after feature put in queue")
29
29
  except Exception as e:
30
- self.logger.error(f"Error sending features: {e}")
30
+ nm.logger.error(f"Error sending features: {e}")
31
31
 
32
32
  def send_raw_data(self, data: dict[str, Any]) -> None:
33
33
  """Send raw data through the rawdata queue"""
34
34
  try:
35
35
  self.rawdata_queue.put(data)
36
36
  except Exception as e:
37
- self.logger.error(f"Error sending raw data: {e}")
37
+ nm.logger.error(f"Error sending raw data: {e}")
38
38
 
39
39
  def check_control_signals(self) -> str | None:
40
40
  """Check for control signals (non-blocking)"""
@@ -43,5 +43,5 @@ class StreamBackendInterface:
43
43
  return self.control_queue.get_nowait()
44
44
  return None
45
45
  except Exception as e:
46
- self.logger.error(f"Error checking control signals: {e}")
46
+ nm.logger.error(f"Error checking control signals: {e}")
47
47
  return None
@@ -109,4 +109,4 @@ class LSLStream:
109
109
  logger.warning("Stream disconnected.")
110
110
  break
111
111
 
112
- yield timestamp - stream_start_time, data
112
+ yield timestamp - stream_start_time, data.astype(float)
@@ -318,6 +318,7 @@ class Stream:
318
318
 
319
319
  # Send data to frontend
320
320
  if backend_interface:
321
+ nm.logger.debug("stream.run: Sending features to frontend")
321
322
  backend_interface.send_features(feature_dict)
322
323
  backend_interface.send_raw_data(self._prepare_raw_data_dict(data_batch))
323
324
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: py_neuromodulation
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Real-time analysis of intracranial neurophysiology recordings.
5
5
  Project-URL: Homepage, https://neuromodulation.github.io/py_neuromodulation/
6
6
  Project-URL: Documentation, https://neuromodulation.github.io/py_neuromodulation/
@@ -175,6 +175,11 @@ Contact information
175
175
  For any question or suggestion please find my contact
176
176
  information at `my GitHub profile <https://github.com/timonmerk>`_.
177
177
 
178
+ Contributing guide
179
+ ------------------
180
+ https://neuromodulation.github.io/py_neuromodulation/contributing.html
181
+
182
+
178
183
  References
179
184
  ----------
180
185
 
@@ -1,9 +1,9 @@
1
1
  py_neuromodulation/__init__.py,sha256=gu0XWs6bzSMJ7JTu8xxFMCPj6TY0nQ6Q0tuBkK7onRI,2996
2
- py_neuromodulation/default_settings.yaml,sha256=68lFKCgKk0fHZaGRCpcZXq6ydbrj8M9wRcrJQvEkmuA,5818
2
+ py_neuromodulation/default_settings.yaml,sha256=tEKmC16yQwy1MFMRI8cdshT8tTdZ35DB5SqqlNWES04,6387
3
3
  py_neuromodulation/grid_cortex.tsv,sha256=k2QOkHY1ej3lJ33LD6DOPVlTynzB3s4BYaoQaoUCyYc,643
4
4
  py_neuromodulation/grid_subcortex.tsv,sha256=oCQDYLDdYSa1DAI9ybwECfuzWulFzXqKHyf7oZ1oDBM,25842
5
5
  py_neuromodulation/lsl_api.cfg,sha256=8j2EMZkPb-T4w3RAXzoDoWL7bwOgk1vMYPYq9Xi0J6o,37
6
- py_neuromodulation/run_gui.py,sha256=Zjy5F4XR6o62m_CSvUBBrUMjfLvji6IUMalzGUilK2U,687
6
+ py_neuromodulation/run_gui.py,sha256=NW6mjSfgNAHoIcFYOD-kebANTvw3UKr470lLplveicI,700
7
7
  py_neuromodulation/ConnectivityDecoding/Automated Anatomical Labeling 3 (Rolls 2020).nii,sha256=Sp-cjF_AuT0Tlilb5s8lB14hVgkXJiR2uKMS9nOQOeg,902981
8
8
  py_neuromodulation/ConnectivityDecoding/_get_grid_hull.m,sha256=2RPDGotbLsCzDJLFB2JXatJtfOMno9UUBCBnsOuse8A,714
9
9
  py_neuromodulation/ConnectivityDecoding/_get_grid_whole_brain.py,sha256=wsWGOmAf42Uzgr3bEjawofWR5HVLJpNQDEooj6sE93g,3030
@@ -39,7 +39,7 @@ py_neuromodulation/features/feature_processor.py,sha256=S1hFg7Ke5CaD64sti0ZaTBL-
39
39
  py_neuromodulation/features/fooof.py,sha256=9HJjjmkFcchDy8ZsSn3DKPla-Ns-Hx5xaln6C0OkTEE,5191
40
40
  py_neuromodulation/features/hjorth_raw.py,sha256=mRPioPHJdN73AGErRbJ5S1Vz__qEQ89jAKaeN5k8eXo,1845
41
41
  py_neuromodulation/features/linelength.py,sha256=8BTctvr9Zj8TEK2HLJqi73j_y2Xgt8lKK-mJhv8eAsM,641
42
- py_neuromodulation/features/mne_connectivity.py,sha256=XkUoYodbLLDHUcIjzapISIZqyT-MpDEuiUw0ZvjNlH0,6142
42
+ py_neuromodulation/features/mne_connectivity.py,sha256=lQHLIXmoyDOWf5agmGMLeq1cQLiG6ud-vYf75CpYTVI,4109
43
43
  py_neuromodulation/features/nolds.py,sha256=jNCKQlIfmcAhmzjTAJMbFPhRuPtYZF5BDzR4qHlLp1k,3374
44
44
  py_neuromodulation/features/oscillatory.py,sha256=KzQQ3EA75G-hJVmL_YBybWTnuNJFZXAM0aBHudr-dvM,7806
45
45
  py_neuromodulation/features/sharpwaves.py,sha256=scFp1jMfq7Eq5th2JtwWm51RpGr8ogQHKCatzv5ZINI,17970
@@ -52,14 +52,14 @@ py_neuromodulation/gui/__init__.py,sha256=nm0rtGTrGJ-QHqGVcN8iDcwViAhHS0Api8xIJ4
52
52
  py_neuromodulation/gui/window_api.py,sha256=znC1GpuPWWV02CnL-9URzlgenL617L2d7OXhQVfsZYk,3465
53
53
  py_neuromodulation/gui/backend/app_backend.py,sha256=iRh9divQFObY1gcUt5CtFEI8FkGZk2DcSi7lRzLjl2A,16267
54
54
  py_neuromodulation/gui/backend/app_manager.py,sha256=EPl9Yircb0LZd5zepCLwZML9hPk5iA2xwZgi61wwPIQ,11735
55
- py_neuromodulation/gui/backend/app_pynm.py,sha256=vzaTTBGKCsYE2fVa6uYzXk7b8_KYYDd3WD7bDLPWhJM,9062
56
- py_neuromodulation/gui/backend/app_socket.py,sha256=PjbLW-CyyyEZnZwnrhb9vMP1u9841S5myDmrf4vaHVE,3526
55
+ py_neuromodulation/gui/backend/app_pynm.py,sha256=hgr9V4Rj_GmuqlHkHztKgNW4B5RaiqOwex11K9OfEtI,9551
56
+ py_neuromodulation/gui/backend/app_socket.py,sha256=QeaD1AKd_F2oPDKtpikgcNL3v9v8nNjnoY0t0AJ06jY,3658
57
57
  py_neuromodulation/gui/backend/app_utils.py,sha256=KgnldjqiYosgGGtE7l7LOg-q3_U1Cpz3IlpnkzGuux8,9693
58
58
  py_neuromodulation/gui/backend/app_window.py,sha256=eOk4yjx4qIKYKZhyP8MPlnbZx6OF7DjFgyG8rXjo2vY,6207
59
59
  py_neuromodulation/gui/frontend/charite.svg,sha256=RlvOSsBUuFKePKjkuadbtI1Yv3zZ41nRbfqfEb0h_88,1112
60
- py_neuromodulation/gui/frontend/index.html,sha256=nNGWz9ouNSuMERXybrKGUZbzOdn_yjK3MlL88TYDIqA,470
60
+ py_neuromodulation/gui/frontend/index.html,sha256=KcUTa9J6SDm0TByHx4jONsKg-71Neb4t6OwLXnkPaZY,470
61
61
  py_neuromodulation/gui/frontend/assets/Figtree-VariableFont_wght-CkXbWBDP.ttf,sha256=_tZBDRp07GKuFOiGalezi5Pedl29EpNUhoJ2lAspjXA,62868
62
- py_neuromodulation/gui/frontend/assets/index-NbJiOU5a.js,sha256=TxZkC_Ih6A7-lelyTbWL7bQteqIC08p5ZtX9_V_0tTU,13847003
62
+ py_neuromodulation/gui/frontend/assets/index-_6V8ZfAS.js,sha256=2w7EYZXY-d-YBFdoMyzJyfzIyds27tV2a5y1-X29lcs,13847236
63
63
  py_neuromodulation/gui/frontend/assets/plotly-DTCwMlpS.js,sha256=O34l2pwjKay4L9VvYbEvRUuJ8PbvTTQGaPqZUcoao0g,1696475
64
64
  py_neuromodulation/liblsl/libpugixml.so.1.12,sha256=_bCOHUjcnGpDiROg1qjgty8ZQhcKHSnaCIP6SMgw6SY,240248
65
65
  py_neuromodulation/liblsl/linux/bionic_amd64/liblsl.1.16.2.so,sha256=YXFbA23CQqWg6mWhk-73WY9gSx79NtgnBr6UFVByC2I,1033592
@@ -85,13 +85,13 @@ py_neuromodulation/processing/projection.py,sha256=JYYRG9_PcPL7a2ge3FjrCWiEIaCXG
85
85
  py_neuromodulation/processing/rereference.py,sha256=n9zLNDkuqeP7zwrP5bhsiXF9eYVPwqI4hZvvQzZ5cks,3304
86
86
  py_neuromodulation/processing/resample.py,sha256=J87gjTSqWM7dDB1ZkoMo9EP3iAL4bI03K9RAMzCCowQ,1354
87
87
  py_neuromodulation/stream/__init__.py,sha256=4kagKtWOLKKZ7l6Dag1EpOFBOf8vv1eu2ll7EYbFD2Y,266
88
- py_neuromodulation/stream/backend_interface.py,sha256=nEkyphVJYrjVOiprULS6c856ONUd4sAiKm6Txssg-B0,1648
88
+ py_neuromodulation/stream/backend_interface.py,sha256=Qn2iUH4l3VNfTXXWNJykseNzZQQAZF0M-5e_nd4qjMM,1789
89
89
  py_neuromodulation/stream/data_processor.py,sha256=-uSeszbqN51xBxFwa46YCpMjX5HHgN_aXDJ--aDdUhQ,12204
90
90
  py_neuromodulation/stream/generator.py,sha256=UKLuM8gz2YLBuVQnQNkkOOKhwsyW27ZgvRJ_5BK7Glo,1588
91
91
  py_neuromodulation/stream/mnelsl_player.py,sha256=KksAWzr78JPqPlECweWZ7JThoxmTVWGo1_-m-fEL0N4,6351
92
- py_neuromodulation/stream/mnelsl_stream.py,sha256=kXJVdR5pJELp5PxDpDOjsoCf6M0b5r5AlkdirIXclCo,4338
92
+ py_neuromodulation/stream/mnelsl_stream.py,sha256=-uHMCNLZwIcjiT9AMGWkJit5GsZjEJYkpki_DoaLzWY,4352
93
93
  py_neuromodulation/stream/settings.py,sha256=--K4NOlR3QNmg4ISgtvC1z_Ucf7MvGChvocck3w6LoI,11832
94
- py_neuromodulation/stream/stream.py,sha256=79tMWRRE5Zv8c4Nmxoe7Ek4XiPLCXNFV6HxNrci-RCI,16329
94
+ py_neuromodulation/stream/stream.py,sha256=rpuhBv2W4NyBRy2I8gUoam-8li3oiyd2Ni7TIRHCCu0,16405
95
95
  py_neuromodulation/utils/__init__.py,sha256=Ok3STMpsflCTclJC9C1iQgdT-3HNGMM7U45w5Oespr4,46
96
96
  py_neuromodulation/utils/channels.py,sha256=bDUZZ8MB3T-Kcs6zI52MUmvC8qx9KAH-F9U0GyND79U,10649
97
97
  py_neuromodulation/utils/database.py,sha256=VEFsmbYDQWwaoZKmJCG8oyWoDTbfSiT_p0n7da9_Pn4,4755
@@ -102,8 +102,8 @@ py_neuromodulation/utils/logging.py,sha256=eIBFBRaAMb3KJnoxNFiCkMrTGzWwgfeDs8m5i
102
102
  py_neuromodulation/utils/perf.py,sha256=10LYM13iTuWA-il-EMMOyZke3-1gcFEa6WLlHsJLO50,5471
103
103
  py_neuromodulation/utils/pydantic_extensions.py,sha256=nkgq8QHeKSJgmf1cBThBLQ9prdeZZ3_Pyeogs-WnHTA,11030
104
104
  py_neuromodulation/utils/types.py,sha256=hT4U9uj4PhHfu8cO7xE1oXj_KT1uHtP-rNY_cDbofr0,4643
105
- py_neuromodulation-0.1.0.dist-info/METADATA,sha256=SQe088vIBDb8VJqHz3SpV3TOBWaU_taV_BQp1gUXyL4,7588
106
- py_neuromodulation-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
107
- py_neuromodulation-0.1.0.dist-info/entry_points.txt,sha256=hImSrCn9vJcwocoeehqNyJ-qj5Hgfrg2o6MPAnIaAa0,60
108
- py_neuromodulation-0.1.0.dist-info/licenses/LICENSE,sha256=EMBwuBRPBo-WkHSjqxZ55E6j95gKNBZ8x30pt-VGfrM,1118
109
- py_neuromodulation-0.1.0.dist-info/RECORD,,
105
+ py_neuromodulation-0.1.2.dist-info/METADATA,sha256=B8BA9dpSlZjWGHrQYtTt9FFdWAwFQcUKTiGFk_JqRP8,7699
106
+ py_neuromodulation-0.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
107
+ py_neuromodulation-0.1.2.dist-info/entry_points.txt,sha256=hImSrCn9vJcwocoeehqNyJ-qj5Hgfrg2o6MPAnIaAa0,60
108
+ py_neuromodulation-0.1.2.dist-info/licenses/LICENSE,sha256=EMBwuBRPBo-WkHSjqxZ55E6j95gKNBZ8x30pt-VGfrM,1118
109
+ py_neuromodulation-0.1.2.dist-info/RECORD,,