diff --git a/Python/Xiao_BLE_tools/xiao_calibration_tool.py b/Python/Xiao_BLE_tools/xiao_calibration_tool.py index 177bc48..48e0795 100644 --- a/Python/Xiao_BLE_tools/xiao_calibration_tool.py +++ b/Python/Xiao_BLE_tools/xiao_calibration_tool.py @@ -44,8 +44,10 @@ debug_mode = 3 # 0=OFF, 1=RAW, 2=TRIGGERS, 3=FULL (actif par défaut) import numpy as np X = np.arange(WINDOW_SIZE) # axe X fixe, ne change jamais -# Fenêtre de moyenne glissante en samples (60ms window / 50ms debug rate = ~1.2 → 3 samples min) -AVG_WINDOW = 3 # correspond à la accelWindow/gyroWindow/audioWindow envoyée au XIAO (60ms @ 20Hz) +# Fenêtre en samples correspondant à la Window du XIAO (60ms / 50ms debug rate = ~1.2 → 3 samples) +# Le XIAO ne fait PAS de moyenne : il compare la valeur brute au seuil et maintient le trigger 60ms. +# On affiche donc le MAX glissant sur 3 samples = ce que le XIAO "retient" comme pic actif. +AVG_WINDOW = 3 # ─── BLE ──────────────────────────────────────────────── def debug_callback(sender, data): @@ -231,9 +233,9 @@ axes[3].set_yticks([]) # pas de graduations Y sur la timeline line_a, = axes[0].plot(X, list(accel_buf), color=CA, lw=1.0, alpha=0.5, label='Brut') line_g, = axes[1].plot(X, list(gyro_buf), color=CG, lw=1.0, alpha=0.5, label='Brut') line_m, = axes[2].plot(X, list(audio_buf), color=CM, lw=1.0, alpha=0.5, label='Brut') -line_a2, = axes[0].plot(X, list(accel_buf), color=CA2, lw=2.0, label=f'Moyenne ({AVG_WINDOW} pts / 60ms)') -line_g2, = axes[1].plot(X, list(gyro_buf), color=CG2, lw=2.0, label=f'Moyenne ({AVG_WINDOW} pts / 60ms)') -line_m2, = axes[2].plot(X, list(audio_buf), color=CM2, lw=2.0, label=f'Moyenne ({AVG_WINDOW} pts / 60ms)') +line_a2, = axes[0].plot(X, list(accel_buf), color=CA2, lw=2.0, label=f'Pic maintenu ({AVG_WINDOW} pts / 60ms) ← logique XIAO') +line_g2, = axes[1].plot(X, list(gyro_buf), color=CG2, lw=2.0, label=f'Pic maintenu ({AVG_WINDOW} pts / 60ms) ← logique XIAO') +line_m2, = axes[2].plot(X, list(audio_buf), color=CM2, lw=2.0, label=f'Pic maintenu ({AVG_WINDOW} pts / 60ms) ← logique XIAO') line_s, = axes[3].plot(X, list(shot_buf), color=CS, lw=0, marker='|', markersize=20, markeredgewidth=2.5) # barres verticales @@ -276,10 +278,12 @@ def update_spans(spans, trig_list): for i, span in enumerate(spans): span.set_alpha(0.25 if i < len(tl) and tl[i] else 0) -def rolling_avg(arr, window): - """Moyenne glissante simple — même logique que le XIAO""" - kernel = np.ones(window) / window - return np.convolve(arr, kernel, mode='same') +def rolling_max(arr, window): + """Max glissant — simule le trigger qui reste actif pendant 'window' samples (logique XIAO)""" + from numpy.lib.stride_tricks import sliding_window_view + pad = window - 1 + padded = np.pad(arr, (pad, 0), mode='edge') + return np.max(sliding_window_view(padded, window), axis=1) def update(frame): # Snapshot des buffers (rapide) @@ -288,10 +292,10 @@ def update(frame): m = np.array(audio_buf, dtype=float) s = np.array(shot_buf) - # Moyennes glissantes (même fenêtre que le XIAO) - a_avg = rolling_avg(a, AVG_WINDOW) - g_avg = rolling_avg(g, AVG_WINDOW) - m_avg = rolling_avg(m, AVG_WINDOW) + # Pic maintenu sur la fenêtre (même logique que le trigger XIAO) + a_avg = rolling_max(a, AVG_WINDOW) + g_avg = rolling_max(g, AVG_WINDOW) + m_avg = rolling_max(m, AVG_WINDOW) # Mise à jour des données des lignes line_a.set_ydata(a)