fix(of): hantek E3 restart + ring 1M for timebase

Context: deux régressions liées :
- Changer le sample rate figeait le stream — le FX2 attend la
  séquence stop/setrate/start (E3=0, E2=code, E3=1) ; sans le
  re-start, le bulk endpoint cesse d'émettre.
- Le slider Time ms/div n'avait aucun effet visible — le code
  clampait winSamples à trace1_.size() (2048), donc quel que soit
  le timebase, on n'affichait jamais que ~256 µs à 8 MS/s.
  De plus le ring d'historique était trop petit (32k samples = 4 ms)
  pour des timebases lents.

Approach:
- HantekDevice::setSampleRate envoie maintenant E3=0 (stop) →
  E2 (sample rate) → E3=1 (start) pour redémarrer proprement la
  capture après changement.
- Suppression du clamp winSamples = min(winSamples, trace1_.size())
  — on downsample dans la boucle de remplissage à la place.
- Ring d'historique 1 M samples par canal (8 Mo total) : permet
  jusqu'à ~125 ms à 8 MS/s ou ~1 s à 1 MS/s.
This commit is contained in:
L'électron rare
2026-05-07 18:55:43 +02:00
parent cf41b41116
commit 365fb6d4f0
2 changed files with 16 additions and 7 deletions
+8 -1
View File
@@ -167,8 +167,15 @@ void HantekDevice::stop() {
void HantekDevice::setSampleRate(uint32_t hz) {
sampleRateHz_.store(hz);
if (handle_ != nullptr && running_.load()) {
const uint8_t code = sampleRateCode(hz);
// Le FX2 (firmware OpenHantek6022) attend la séquence : stop (0xE3=0)
// → set sample rate (0xE2) → start (0xE3=1). Sans le re-start, le
// bulk endpoint cesse d'émettre et le stream se fige.
const uint8_t stopTrig = 0x00;
const uint8_t code = sampleRateCode(hz);
const uint8_t startTrig = 0x01;
sendVendorControl(0xE3, 0, 0, &stopTrig, 1);
sendVendorControl(kReqSetSampleRate, 0, 0, &code, 1);
sendVendorControl(0xE3, 0, 0, &startTrig, 1);
}
}
+8 -6
View File
@@ -9,10 +9,11 @@ void WaveformVis::setup(int w, int h) {
h_ = h;
trace1_.assign(syntheticSize_, 0.0f);
trace2_.assign(syntheticSize_, 0.0f);
// Ring d'historique : ~16x la fenêtre pour pouvoir scroller en arrière
// sans trou même avec un timebase lent.
ring1_.assign(syntheticSize_ * 16, 0.0f);
ring2_.assign(syntheticSize_ * 16, 0.0f);
// Ring d'historique : assez grand pour que le timebase puisse aller
// jusqu'à ~125 ms à 8 MS/s (ou 1 s à 1 MS/s). 1 M floats ≈ 4 Mo par
// canal, soit 8 Mo total — acceptable pour une vue scope live.
ring1_.assign(1 << 20, 0.0f);
ring2_.assign(1 << 20, 0.0f);
ringHead_ = 0;
}
@@ -42,12 +43,13 @@ void WaveformVis::update(const VisFrame& frame) {
if (scrollSpeed_ < 0.01f) return;
// Largeur de la fenêtre = timebase × divisions, convertie en samples.
// (sampleRate * (ms/div × divX) / 1000)
// (sampleRate * (ms/div × divX) / 1000). On NE clamp PAS à
// trace1_.size() : on downsample lors du remplissage du trace, ce
// qui rend le slider timebase réellement effectif.
const float winSec = (timeMsPerDiv_ * 0.001f) * divX_;
std::size_t winSamples = static_cast<std::size_t>(
std::max(64.0f, std::min(static_cast<float>(cap),
winSec * sampleRateHz_)));
winSamples = std::min(winSamples, trace1_.size());
// Position de lecture : tail = head - winSamples (mode freeze) ou
// tail décalé pour un effet de scroll continu.