diff --git a/Python/Xiao_BLE_tools/xiao_calibration_tool.py b/Python/Xiao_BLE_tools/xiao_calibration_tool.py index d23dba1..8f9af5f 100644 --- a/Python/Xiao_BLE_tools/xiao_calibration_tool.py +++ b/Python/Xiao_BLE_tools/xiao_calibration_tool.py @@ -34,6 +34,8 @@ thresholds = {"accel": 2.5, "gyro": 200.0, "audio": 3000} # PDM : 0-32767 min_sensors = 3 # Nb de capteurs requis simultanément (1-3) — NUM6=+1 NUM4=-1 shot_count = 0 shot_pending = False # Flag : un tir reçu, à enregistrer dans shot_buf au prochain debug tick +# Pic max au moment du dernier tir (mis à jour au moment où shot_pending devient True) +last_shot_peak = {"accel": 0.0, "gyro": 0.0, "audio": 0} ble_status = "🔍 Connexion..." ble_running = True audio_max_global = 1000 # Tracks le max absolu jamais vu @@ -66,10 +68,14 @@ def debug_callback(sender, data): shot_pending = False def shot_callback(sender, data): - global shot_count, shot_pending + global shot_count, shot_pending, last_shot_peak if data[0] == 1: shot_count += 1 shot_pending = True + # Capturer le pic courant au moment exact du tir (meilleure approximation possible en BLE) + last_shot_peak["accel"] = accel_buf[-1] if accel_buf else 0.0 + last_shot_peak["gyro"] = gyro_buf[-1] if gyro_buf else 0.0 + last_shot_peak["audio"] = audio_buf[-1] if audio_buf else 0 async def find_device(): """Scan par nom (toutes les 0.5s pendant 30s max). @@ -235,11 +241,16 @@ thr_a = axes[0].axhline(thresholds["accel"], color=CT, ls='--', lw=1.5, label='S thr_g = axes[1].axhline(thresholds["gyro"], color=CT, ls='--', lw=1.5, label='Seuil (trigger XIAO)') thr_m = axes[2].axhline(thresholds["audio"], color=CT, ls='--', lw=1.5, label='Seuil (trigger XIAO)') -# Légendes — incluent le fond rouge = trigger actif +# Ligne horizontale orange = pic au moment du dernier tir (invisible au départ) +peak_a = axes[0].axhline(0, color=CS, ls='-', lw=1.5, alpha=0.0, label='Pic dernier tir') +peak_g = axes[1].axhline(0, color=CS, ls='-', lw=1.5, alpha=0.0, label='Pic dernier tir') +peak_m = axes[2].axhline(0, color=CS, ls='-', lw=1.5, alpha=0.0, label='Pic dernier tir') + +# Légendes for ax in axes[:3]: ax.legend(loc='upper left', fontsize=7, facecolor=PANEL, edgecolor='#444466', labelcolor=TEXT, framealpha=0.8, - handles=[ax.get_lines()[0], ax.get_lines()[1], + handles=[ax.get_lines()[0], ax.get_lines()[1], ax.get_lines()[2], plt.Rectangle((0,0),1,1, fc=CT, alpha=0.25, label='Trigger actif (XIAO)')]) from matplotlib.patches import Rectangle @@ -279,17 +290,25 @@ def update(frame): m = np.array(audio_buf, dtype=float) s = np.array(shot_buf) - # Pic max sur la dernière seconde (pour affichage dans le titre) - a_peak = float(a[-PEAK_WINDOW:].max()) - g_peak = float(g[-PEAK_WINDOW:].max()) - m_peak = float(m[-PEAK_WINDOW:].max()) - - # Mise à jour des données des lignes + # Mise à jour des données des lignes brutes line_a.set_ydata(a) line_g.set_ydata(g) line_m.set_ydata(m) line_s.set_ydata(s) + # Ligne orange = pic au moment du dernier tir (visible seulement si un tir a eu lieu) + if shot_count > 0: + peak_a.set_ydata([last_shot_peak["accel"]] * 2) + peak_g.set_ydata([last_shot_peak["gyro"]] * 2) + peak_m.set_ydata([last_shot_peak["audio"]] * 2) + peak_a.set_alpha(0.9) + peak_g.set_alpha(0.9) + peak_m.set_alpha(0.9) + else: + peak_a.set_alpha(0.0) + peak_g.set_alpha(0.0) + peak_m.set_alpha(0.0) + # Mise à jour des seuils thr_a.set_ydata([thresholds["accel"], thresholds["accel"]]) thr_g.set_ydata([thresholds["gyro"], thresholds["gyro"]]) @@ -310,22 +329,22 @@ def update(frame): for i, span in enumerate(shot_spans): span.set_alpha(0.85 if i < len(sl) and sl[i] > 0 else 0) - # Titres avec valeurs courantes - a_ratio = a_peak / thresholds['accel'] * 100 - g_ratio = g_peak / thresholds['gyro'] * 100 - m_ratio = m_peak / thresholds['audio'] * 100 + # Titres avec valeurs courantes + pic dernier tir + pa = last_shot_peak["accel"] if shot_count > 0 else None + pg = last_shot_peak["gyro"] if shot_count > 0 else None + pm = last_shot_peak["audio"] if shot_count > 0 else None titles[0].set_text( - f"Accelerometre " - f"val: {a[-1]:.2f} G pic(1s): {a_peak:.2f} G seuil: {thresholds['accel']:.1f} G " - f"({a_ratio:.0f}% du seuil) [NUM7=+0.1 NUM1=-0.1]") + f"Accelerometre val: {a[-1]:.2f} G seuil: {thresholds['accel']:.1f} G" + + (f" ── pic dernier tir: {pa:.2f} G ({pa/thresholds['accel']*100:.0f}%)" if pa else "") + + f" [NUM7=+0.1 NUM1=-0.1]") titles[1].set_text( - f"Gyroscope " - f"val: {g[-1]:.0f} dps pic(1s): {g_peak:.0f} dps seuil: {thresholds['gyro']:.0f} dps " - f"({g_ratio:.0f}% du seuil) [NUM8=+10 NUM2=-10]") + f"Gyroscope val: {g[-1]:.0f} dps seuil: {thresholds['gyro']:.0f} dps" + + (f" ── pic dernier tir: {pg:.0f} dps ({pg/thresholds['gyro']*100:.0f}%)" if pg else "") + + f" [NUM8=+10 NUM2=-10]") titles[2].set_text( - f"Microphone PDM " - f"val: {m[-1]:.0f} pic(1s): {m_peak:.0f} seuil: {thresholds['audio']} " - f"({m_ratio:.0f}% du seuil) [NUM9=+500 NUM3=-500]") + f"Microphone PDM val: {m[-1]:.0f} seuil: {thresholds['audio']}" + + (f" ── pic dernier tir: {pm:.0f} ({pm/thresholds['audio']*100:.0f}%)" if pm else "") + + f" [NUM9=+500 NUM3=-500]") titles[3].set_text(f"Tirs detectes : {shot_count} (fenetre glissante)") status_txt.set_text( @@ -336,6 +355,7 @@ def update(frame): return (line_a, line_g, line_m, line_s, thr_a, thr_g, thr_m, + peak_a, peak_g, peak_m, *titles, status_txt, debug_txt) def on_key(event):