Upload openbci_impedance.py
Browse files- openbci_impedance.py +26 -17
openbci_impedance.py
CHANGED
|
@@ -51,7 +51,7 @@ SCALE_UV = (ADS1299_VREF / ADS1299_GAIN) / (2**23 - 1) * 1_000_000
|
|
| 51 |
# Pleine echelle ADS1299 en Β΅V (seuil railing)
|
| 52 |
ADC_MAX_UV = (2**23 - 1) * SCALE_UV # β 187 500 Β΅V
|
| 53 |
|
| 54 |
-
LEAD_OFF_FREQ = 31.25 # Hz β frΓ©quence exacte ADS1299
|
| 55 |
LEAD_OFF_AMPS = 6e-9 # A
|
| 56 |
SERIES_RESISTOR = 2200 # Ξ©
|
| 57 |
|
|
@@ -142,19 +142,15 @@ def parse_packet(data: bytes):
|
|
| 142 |
return None
|
| 143 |
if data[0] != START_BYTE:
|
| 144 |
return None
|
| 145 |
-
|
| 146 |
-
if (stop_byte & 0xF0) != 0xC0:
|
| 147 |
return None
|
| 148 |
sample_num = data[1]
|
| 149 |
-
# FIX : stop byte 0xC0 = Cyton packet, 0xC1 = Daisy packet
|
| 150 |
-
# C'est le nibble bas qui indique la source, PAS la paritΓ© du sample_num
|
| 151 |
-
is_daisy = (stop_byte & 0x0F) == 0x01
|
| 152 |
channels_uv = []
|
| 153 |
for ch in range(8):
|
| 154 |
offset = 2 + ch * 3
|
| 155 |
raw = parse_24bit_signed(data[offset], data[offset+1], data[offset+2])
|
| 156 |
channels_uv.append(raw * SCALE_UV)
|
| 157 |
-
return sample_num, channels_uv
|
| 158 |
|
| 159 |
|
| 160 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -186,7 +182,7 @@ def _make_bandpass_sos(low_hz: float, high_hz: float, order: int = 4,
|
|
| 186 |
# Pre-calculer les filtres une seule fois
|
| 187 |
# Californie = reseau 60 Hz uniquement (pas de 50 Hz)
|
| 188 |
_NOTCH_60_SOS = _make_notch_sos(60.0)
|
| 189 |
-
_BANDPASS_SOS = _make_bandpass_sos(30.5, 32.0) #
|
| 190 |
|
| 191 |
|
| 192 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -257,6 +253,8 @@ class CytonDaisyBoard:
|
|
| 257 |
|
| 258 |
self._buf_size = SAMPLE_RATE * 4
|
| 259 |
self._buffers = [deque(maxlen=self._buf_size) for _ in range(N_CHANNELS)]
|
|
|
|
|
|
|
| 260 |
self._rx_thread = None
|
| 261 |
|
| 262 |
def connect(self):
|
|
@@ -421,17 +419,28 @@ class CytonDaisyBoard:
|
|
| 421 |
print(f"[Board] Erreur lecture : {e}")
|
| 422 |
time.sleep(0.005)
|
| 423 |
|
| 424 |
-
def _handle_sample(self, sample_num: int, ch_data_uv: list
|
| 425 |
-
#
|
|
|
|
|
|
|
| 426 |
with self._lock:
|
| 427 |
-
if
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
self._buffers[i].append(ch_data_uv[i])
|
| 431 |
else:
|
| 432 |
-
#
|
| 433 |
-
|
| 434 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 435 |
|
| 436 |
def get_impedances(self) -> list:
|
| 437 |
with self._lock:
|
|
|
|
| 51 |
# Pleine echelle ADS1299 en Β΅V (seuil railing)
|
| 52 |
ADC_MAX_UV = (2**23 - 1) * SCALE_UV # β 187 500 Β΅V
|
| 53 |
|
| 54 |
+
LEAD_OFF_FREQ = 31.25 # Hz β frΓ©quence exacte ADS1299
|
| 55 |
LEAD_OFF_AMPS = 6e-9 # A
|
| 56 |
SERIES_RESISTOR = 2200 # Ξ©
|
| 57 |
|
|
|
|
| 142 |
return None
|
| 143 |
if data[0] != START_BYTE:
|
| 144 |
return None
|
| 145 |
+
if (data[32] & 0xF0) != 0xC0:
|
|
|
|
| 146 |
return None
|
| 147 |
sample_num = data[1]
|
|
|
|
|
|
|
|
|
|
| 148 |
channels_uv = []
|
| 149 |
for ch in range(8):
|
| 150 |
offset = 2 + ch * 3
|
| 151 |
raw = parse_24bit_signed(data[offset], data[offset+1], data[offset+2])
|
| 152 |
channels_uv.append(raw * SCALE_UV)
|
| 153 |
+
return sample_num, channels_uv
|
| 154 |
|
| 155 |
|
| 156 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 182 |
# Pre-calculer les filtres une seule fois
|
| 183 |
# Californie = reseau 60 Hz uniquement (pas de 50 Hz)
|
| 184 |
_NOTCH_60_SOS = _make_notch_sos(60.0)
|
| 185 |
+
_BANDPASS_SOS = _make_bandpass_sos(30.5, 32.0) # bande Γ©troite Β±0.75 Hz autour de 31.25 Hz
|
| 186 |
|
| 187 |
|
| 188 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 253 |
|
| 254 |
self._buf_size = SAMPLE_RATE * 4
|
| 255 |
self._buffers = [deque(maxlen=self._buf_size) for _ in range(N_CHANNELS)]
|
| 256 |
+
self._last_cyton_data = None
|
| 257 |
+
self._last_cyton_snum = None
|
| 258 |
self._rx_thread = None
|
| 259 |
|
| 260 |
def connect(self):
|
|
|
|
| 419 |
print(f"[Board] Erreur lecture : {e}")
|
| 420 |
time.sleep(0.005)
|
| 421 |
|
| 422 |
+
def _handle_sample(self, sample_num: int, ch_data_uv: list):
|
| 423 |
+
# Protocole OpenBCI Cyton+Daisy : odd sample_num = Cyton, even = Daisy
|
| 424 |
+
# Ref: https://docs.openbci.com/Cyton/CytonDataFormat/
|
| 425 |
+
is_cyton = (sample_num % 2) == 1
|
| 426 |
with self._lock:
|
| 427 |
+
if is_cyton:
|
| 428 |
+
self._last_cyton_data = ch_data_uv[:]
|
| 429 |
+
self._last_cyton_snum = sample_num
|
|
|
|
| 430 |
else:
|
| 431 |
+
# VΓ©rification de cohΓ©rence : le sample Daisy doit suivre
|
| 432 |
+
# immΓ©diatement le sample Cyton (sample_num = last_cyton + 1)
|
| 433 |
+
if self._last_cyton_data is not None:
|
| 434 |
+
expected = (self._last_cyton_snum + 1) % 256
|
| 435 |
+
if sample_num != expected:
|
| 436 |
+
# DΓ©synchronisation β on jette ce couple et on repart
|
| 437 |
+
self._last_cyton_data = None
|
| 438 |
+
return
|
| 439 |
+
for i in range(8):
|
| 440 |
+
self._buffers[i].append(self._last_cyton_data[i])
|
| 441 |
+
for i in range(8):
|
| 442 |
+
self._buffers[8 + i].append(ch_data_uv[i])
|
| 443 |
+
self._last_cyton_data = None
|
| 444 |
|
| 445 |
def get_impedances(self) -> list:
|
| 446 |
with self._lock:
|