Всякие тесты неинтересные
This commit is contained in:
236
OpticalChannelTester/SignalProbe.cpp
Normal file
236
OpticalChannelTester/SignalProbe.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
#include "SignalProbe.h"
|
||||
#include "Config.h"
|
||||
#include <esp32-hal-cpu.h>
|
||||
|
||||
namespace {
|
||||
constexpr uint32_t DISPLAY_INTERVAL_MS = 250;
|
||||
constexpr uint8_t PERIOD_TOLERANCE_PCT = 10;
|
||||
|
||||
bool pwmPointAvailable(uint32_t frequencyHz, uint32_t pulseNs) {
|
||||
return static_cast<uint64_t>(frequencyHz) * pulseNs < 1000000000ULL;
|
||||
}
|
||||
|
||||
void formatWidth(uint64_t nanoseconds, char *out, size_t size) {
|
||||
if (nanoseconds < 1000ULL)
|
||||
snprintf(out, size, "%lluns", nanoseconds);
|
||||
else if (nanoseconds < 1000000ULL)
|
||||
snprintf(out, size, "%.2fus", nanoseconds / 1000.0);
|
||||
else
|
||||
snprintf(out, size, "%.2fms", nanoseconds / 1000000.0);
|
||||
}
|
||||
|
||||
void formatPulseCount(uint64_t count, char *out, size_t size) {
|
||||
if (count < 1000000ULL) snprintf(out, size, "%llu", count);
|
||||
else if (count < 1000000000ULL) snprintf(out, size, "%lluM", count / 1000000ULL);
|
||||
else snprintf(out, size, "%lluG", count / 1000000000ULL);
|
||||
}
|
||||
}
|
||||
|
||||
SignalProbe::SignalProbe() : startButton_(GPIO_BUTTON_START),
|
||||
modeButton_(GPIO_BUTTON_MODE) {}
|
||||
|
||||
void SignalProbe::begin() {
|
||||
Serial.begin(SERIAL_BAUD);
|
||||
#if ARDUINO_USB_CDC_ON_BOOT
|
||||
Serial.setTxTimeoutMs(SERIAL_TX_TIMEOUT_MS);
|
||||
#endif
|
||||
setCpuFrequencyMhz(160);
|
||||
startButton_.begin();
|
||||
modeButton_.begin();
|
||||
pwm_.begin();
|
||||
pwm_.configureActiveLight(true);
|
||||
display_.begin();
|
||||
receiverInitialized_ = receiver_.begin();
|
||||
rxReady_ = receiverInitialized_ && captureStart();
|
||||
applyPwm();
|
||||
Serial.printf("Signal probe: MODE short=frequency, MODE long=pulse, START short=reset RX minimum, START long=PWM on/off, hold both=RX active level; RX=%s\n",
|
||||
rxActiveHigh_ ? "HIGH" : "LOW");
|
||||
show();
|
||||
}
|
||||
|
||||
bool SignalProbe::captureStart() {
|
||||
if (!receiver_.startRaw()) {
|
||||
Serial.println("RX capture start failed");
|
||||
return false;
|
||||
}
|
||||
havePulseStart_ = false;
|
||||
havePulseEnd_ = false;
|
||||
extraEdgeInPeriod_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SignalProbe::resetMinimum() {
|
||||
if (rxReady_) receiver_.stop();
|
||||
rxReady_ = receiverInitialized_ && captureStart();
|
||||
minTicks_ = UINT64_MAX;
|
||||
pulseCount_ = 0;
|
||||
edgeCount_ = 0;
|
||||
rejectedPeriods_ = 0;
|
||||
displayDirty_ = true;
|
||||
Serial.println("RX minimum reset");
|
||||
}
|
||||
|
||||
void SignalProbe::updateCapture() {
|
||||
if (!rxReady_) return;
|
||||
CaptureEvent edges[64];
|
||||
for (uint8_t batch = 0; batch < 8; ++batch) {
|
||||
const size_t count = receiver_.readRawEdges(edges, countOf(edges));
|
||||
if (!count) break;
|
||||
uint64_t batchMinimum = minTicks_;
|
||||
uint64_t batchPulses = 0;
|
||||
uint64_t batchRejected = 0;
|
||||
const bool activeRising = rxActiveHigh_;
|
||||
const uint32_t expectedHz = PWM_FREQUENCY_OPTIONS_HZ[frequencyIndex_];
|
||||
const uint64_t expectedPeriod = receiver_.tickHz() / expectedHz;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
if (edges[i].rising == activeRising) {
|
||||
if (havePulseStart_) {
|
||||
const uint64_t period = edges[i].tick - pulseStartTick_;
|
||||
const uint64_t width = havePulseEnd_
|
||||
? pulseEndTick_ - pulseStartTick_ : 0;
|
||||
const uint64_t error = period > expectedPeriod
|
||||
? period - expectedPeriod : expectedPeriod - period;
|
||||
if (havePulseEnd_ && !extraEdgeInPeriod_ && period && width && width < period &&
|
||||
error * 100U <= expectedPeriod * PERIOD_TOLERANCE_PCT) {
|
||||
if (width < batchMinimum) batchMinimum = width;
|
||||
++batchPulses;
|
||||
} else {
|
||||
++batchRejected;
|
||||
}
|
||||
}
|
||||
pulseStartTick_ = edges[i].tick;
|
||||
havePulseStart_ = true;
|
||||
havePulseEnd_ = false;
|
||||
extraEdgeInPeriod_ = false;
|
||||
} else if (havePulseStart_ && !havePulseEnd_ &&
|
||||
edges[i].tick > pulseStartTick_) {
|
||||
pulseEndTick_ = edges[i].tick;
|
||||
havePulseEnd_ = true;
|
||||
} else if (havePulseStart_) {
|
||||
extraEdgeInPeriod_ = true;
|
||||
}
|
||||
}
|
||||
const uint32_t dropped = receiver_.takeDroppedItems();
|
||||
if (dropped) {
|
||||
Serial.printf("RX capture overflow: %lu edges lost; restarting capture\n", dropped);
|
||||
receiver_.stop();
|
||||
rxReady_ = captureStart();
|
||||
displayDirty_ = true;
|
||||
return;
|
||||
}
|
||||
edgeCount_ += count;
|
||||
pulseCount_ += batchPulses;
|
||||
rejectedPeriods_ += batchRejected;
|
||||
displayDirty_ = true;
|
||||
if (batchMinimum < minTicks_) {
|
||||
minTicks_ = batchMinimum;
|
||||
displayDirty_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SignalProbe::applyPwm() {
|
||||
pwm_.stop();
|
||||
if (!pwmEnabled_) {
|
||||
Serial.println("PWM off");
|
||||
displayDirty_ = true;
|
||||
return;
|
||||
}
|
||||
const uint32_t frequency = PWM_FREQUENCY_OPTIONS_HZ[frequencyIndex_];
|
||||
const uint32_t pulse = MAX_PULSE_OPTIONS_NS[pulseIndex_];
|
||||
if (!pwm_.start(frequency, pulse, actual_)) {
|
||||
Serial.printf("PWM start failed: %lu Hz, %lu ns\n", frequency, pulse);
|
||||
pwmEnabled_ = false;
|
||||
} else {
|
||||
Serial.printf("PWM GPIO=%u requested=%luHz/%luns actual=%luHz/%luns\n",
|
||||
GPIO_PWM, frequency, pulse, actual_.actualHz, actual_.actualPulseNs);
|
||||
}
|
||||
displayDirty_ = true;
|
||||
}
|
||||
|
||||
void SignalProbe::advanceFrequency() {
|
||||
const uint8_t count = static_cast<uint8_t>(countOf(PWM_FREQUENCY_OPTIONS_HZ));
|
||||
for (uint8_t step = 0; step < count; ++step) {
|
||||
frequencyIndex_ = static_cast<uint8_t>((frequencyIndex_ + 1U) % count);
|
||||
if (pwmPointAvailable(PWM_FREQUENCY_OPTIONS_HZ[frequencyIndex_],
|
||||
MAX_PULSE_OPTIONS_NS[pulseIndex_])) break;
|
||||
}
|
||||
applyPwm();
|
||||
resetMinimum();
|
||||
}
|
||||
|
||||
void SignalProbe::advancePulse() {
|
||||
const uint8_t count = static_cast<uint8_t>(countOf(MAX_PULSE_OPTIONS_NS));
|
||||
for (uint8_t step = 0; step < count; ++step) {
|
||||
pulseIndex_ = static_cast<uint8_t>((pulseIndex_ + 1U) % count);
|
||||
if (pwmPointAvailable(PWM_FREQUENCY_OPTIONS_HZ[frequencyIndex_],
|
||||
MAX_PULSE_OPTIONS_NS[pulseIndex_])) break;
|
||||
}
|
||||
applyPwm();
|
||||
resetMinimum();
|
||||
}
|
||||
|
||||
void SignalProbe::show() {
|
||||
char frequency[16], pulse[16], first[64], second[64];
|
||||
const uint32_t shownFrequency = pwm_.running() ? actual_.actualHz
|
||||
: PWM_FREQUENCY_OPTIONS_HZ[frequencyIndex_];
|
||||
const uint32_t shownPulse = pwm_.running() ? actual_.actualPulseNs
|
||||
: MAX_PULSE_OPTIONS_NS[pulseIndex_];
|
||||
Display::formatPwmFrequency(shownFrequency,
|
||||
frequency, sizeof(frequency));
|
||||
Display::formatPulse(shownPulse, pulse, sizeof(pulse));
|
||||
snprintf(first, sizeof(first), "PWM%s %s %s", pwmEnabled_ ? "" : " OFF",
|
||||
frequency, pulse);
|
||||
if (!rxReady_) {
|
||||
snprintf(second, sizeof(second), "RX: ERROR");
|
||||
} else if (minTicks_ == UINT64_MAX) {
|
||||
snprintf(second, sizeof(second), "RX E:%llu BAD:%llu", edgeCount_, rejectedPeriods_);
|
||||
} else {
|
||||
char width[24], count[12];
|
||||
const uint64_t nanoseconds =
|
||||
(minTicks_ * 1000000000ULL + receiver_.tickHz() / 2U) / receiver_.tickHz();
|
||||
formatWidth(nanoseconds, width, sizeof(width));
|
||||
formatPulseCount(pulseCount_, count, sizeof(count));
|
||||
snprintf(second, sizeof(second), "MIN:%s N:%s", width, count);
|
||||
Serial.printf("RX minimum=%lluns, periods=%llu, rejected=%llu, edges=%llu\n",
|
||||
nanoseconds, pulseCount_, rejectedPeriods_, edgeCount_);
|
||||
}
|
||||
display_.show(first, second, 0, 0, rxActiveHigh_ ? "H" : "L");
|
||||
lastDisplayMs_ = millis();
|
||||
displayDirty_ = false;
|
||||
}
|
||||
|
||||
void SignalProbe::update() {
|
||||
const uint32_t now = millis();
|
||||
const ButtonEvent start = startButton_.update(now);
|
||||
const ButtonEvent mode = modeButton_.update(now);
|
||||
if (startButton_.pressed() && modeButton_.pressed()) {
|
||||
if (!bothHeld_) {
|
||||
bothHeld_ = true;
|
||||
bothHeldSinceMs_ = now;
|
||||
startButton_.suppressUntilRelease();
|
||||
modeButton_.suppressUntilRelease();
|
||||
}
|
||||
if (!bothHeldHandled_ && now - bothHeldSinceMs_ >= BUTTON_LONG_PRESS_MS) {
|
||||
rxActiveHigh_ = !rxActiveHigh_;
|
||||
bothHeldHandled_ = true;
|
||||
resetMinimum();
|
||||
Serial.printf("RX active level=%s\n", rxActiveHigh_ ? "HIGH" : "LOW");
|
||||
}
|
||||
} else if (bothHeld_) {
|
||||
if (!startButton_.pressed() && !modeButton_.pressed()) {
|
||||
bothHeld_ = false;
|
||||
bothHeldHandled_ = false;
|
||||
}
|
||||
} else {
|
||||
if (mode == ButtonEvent::SHORT) advanceFrequency();
|
||||
else if (mode == ButtonEvent::LONG) advancePulse();
|
||||
if (start == ButtonEvent::SHORT) resetMinimum();
|
||||
else if (start == ButtonEvent::LONG) {
|
||||
pwmEnabled_ = !pwmEnabled_;
|
||||
applyPwm();
|
||||
}
|
||||
}
|
||||
updateCapture();
|
||||
if (displayDirty_ && now - lastDisplayMs_ >= DISPLAY_INTERVAL_MS) show();
|
||||
}
|
||||
Reference in New Issue
Block a user