181 lines
5.6 KiB
C++
181 lines
5.6 KiB
C++
#pragma once
|
|
|
|
#include <Arduino.h>
|
|
|
|
#include "Receiver.h"
|
|
|
|
enum class DriverState : uint8_t {
|
|
IDLE, SETTLING, RUNNING, SUBSAMPLE_DONE, PASS, FAIL
|
|
};
|
|
|
|
struct DriverEdgeStats {
|
|
uint32_t responses;
|
|
uint32_t minDelayTicks;
|
|
uint32_t maxDelayTicks;
|
|
uint64_t delaySumTicks;
|
|
uint32_t minResponseTicks;
|
|
uint32_t maxResponseTicks;
|
|
uint64_t responseSumTicks;
|
|
void reset();
|
|
};
|
|
|
|
struct DriverStats {
|
|
uint32_t inputEdges;
|
|
uint32_t responses;
|
|
uint32_t minDelayTicks;
|
|
uint32_t maxDelayTicks;
|
|
uint32_t minResponseTicks;
|
|
uint32_t maxResponseTicks;
|
|
uint32_t lastDelayTicks;
|
|
uint32_t lastResponseTicks;
|
|
uint32_t droppedItems;
|
|
uint32_t unexpectedResponses;
|
|
uint64_t errorElapsedTicks;
|
|
uint32_t errorTriggerTicks;
|
|
uint32_t errorDelayTicks;
|
|
uint32_t errorPulseTicks;
|
|
bool errorTriggerValid;
|
|
bool errorDelayValid;
|
|
bool errorPulseValid;
|
|
DriverEdgeStats turnOn;
|
|
DriverEdgeStats turnOff;
|
|
FailReason reason;
|
|
void reset();
|
|
};
|
|
|
|
class DriverTest {
|
|
public:
|
|
explicit DriverTest(PulseReceiver &receiver) : receiver_(receiver) {}
|
|
bool start(uint32_t frequencyHz, uint32_t pulseNs, float tolerancePct,
|
|
uint32_t testTimeMs, uint8_t settleCycles,
|
|
bool activeTxLightOn, bool activeRxLightOn);
|
|
DriverState update() const { return state_; }
|
|
void abort();
|
|
void forceFail(FailReason reason);
|
|
bool resumeSubsample();
|
|
bool takeProgressUpdate();
|
|
void printSummary() const;
|
|
void printTrace() const;
|
|
uint8_t progressStep() const { return currentStep_; }
|
|
uint8_t progressSteps() const { return subsampleCount_; }
|
|
uint32_t tickHz() const { return captureHz_; }
|
|
const DriverStats &stats() const { return publishedStats_; }
|
|
|
|
private:
|
|
enum class Source : uint8_t { TX, RX, WINDOW_END };
|
|
struct RawEvent { uint32_t tick; bool rising; Source source; };
|
|
struct TimedEvent { uint64_t tick; bool rising; Source source; };
|
|
struct PendingTx {
|
|
uint64_t tick;
|
|
bool lightOn;
|
|
};
|
|
struct Response {
|
|
bool active;
|
|
bool associated;
|
|
uint64_t startTick;
|
|
PendingTx tx;
|
|
};
|
|
struct TraceEvent {
|
|
uint64_t tick;
|
|
uint8_t source;
|
|
uint8_t rising;
|
|
uint8_t state;
|
|
uint8_t pending;
|
|
};
|
|
|
|
static void analyzerTaskEntry(void *context);
|
|
static void pollTaskEntry(void *context);
|
|
void analyzerTaskLoop();
|
|
void IRAM_ATTR pollTaskLoop();
|
|
void processEvent(const TimedEvent &event);
|
|
void processSettling(const TimedEvent &event);
|
|
void processRunning(const TimedEvent &event);
|
|
void processTx(const TimedEvent &event, bool lightOn);
|
|
void processRx(const TimedEvent &event, bool activeNow);
|
|
void expirePending(uint64_t now);
|
|
void completeIfPossible(uint64_t now);
|
|
bool addPending(uint64_t tick, bool lightOn);
|
|
int8_t matchingPending(uint64_t rxTick) const;
|
|
void removePending(uint8_t index);
|
|
uint64_t mergeGuardTicks() const;
|
|
void acceptAcknowledgement(uint64_t delay, uint64_t width, bool lightOn);
|
|
void fail(FailReason reason, uint64_t tick = 0, uint64_t delay = 0,
|
|
uint64_t pulseWidth = 0, uint64_t triggerAfterTx = 0);
|
|
void publishStats();
|
|
void rememberTrace(const TimedEvent &event);
|
|
bool armCapture();
|
|
void requestCaptureStop();
|
|
bool waitCaptureStopped(uint32_t timeoutMs);
|
|
void clearCapture();
|
|
void recordRaw(uint32_t tick, bool rising, Source source);
|
|
size_t readRaw(TimedEvent *events, size_t capacity, TickType_t waitTicks);
|
|
uint32_t takeDropped();
|
|
uint64_t nsToTicks(uint32_t ns) const;
|
|
uint64_t ticksToNs(uint64_t ticks) const;
|
|
|
|
static constexpr uint8_t MAX_PENDING = 8;
|
|
static constexpr uint16_t RING_CAPACITY = 2048;
|
|
static constexpr uint8_t TRACE_CAPACITY = 32;
|
|
static_assert((RING_CAPACITY & (RING_CAPACITY - 1U)) == 0,
|
|
"driver ring capacity must be a power of two");
|
|
|
|
PulseReceiver &receiver_;
|
|
TaskHandle_t analyzerTask_ = nullptr;
|
|
TaskHandle_t pollTask_ = nullptr;
|
|
volatile DriverState state_ = DriverState::IDLE;
|
|
DriverStats stats_ = {};
|
|
DriverStats publishedStats_ = {};
|
|
mutable portMUX_TYPE statsMux_ = portMUX_INITIALIZER_UNLOCKED;
|
|
|
|
RawEvent ring_[RING_CAPACITY] = {};
|
|
volatile uint16_t ringWrite_ = 0;
|
|
volatile uint16_t ringRead_ = 0;
|
|
volatile uint32_t droppedItems_ = 0;
|
|
volatile bool captureActive_ = false;
|
|
volatile bool captureReady_ = false;
|
|
volatile bool core0WdtDisabled_ = false;
|
|
uint32_t captureHz_ = 0;
|
|
uint32_t captureFrequencyHz_ = 0;
|
|
uint32_t capturePulseNs_ = 0;
|
|
uint32_t pollPeriodCycles_ = 0;
|
|
uint32_t pollWindowBeforeCycles_ = 0;
|
|
uint32_t pollWindowAfterCycles_ = 0;
|
|
bool pollTxStartRawHigh_ = false;
|
|
bool captureTxLightOn_ = true;
|
|
portMUX_TYPE pollMux_ = portMUX_INITIALIZER_UNLOCKED;
|
|
|
|
PendingTx pending_[MAX_PENDING] = {};
|
|
uint8_t pendingCount_ = 0;
|
|
Response response_ = {};
|
|
uint64_t ackStartMaxTicks_ = 0;
|
|
uint64_t faultLongTicks_ = 0;
|
|
uint64_t stuckTicks_ = 0;
|
|
uint64_t testTicks_ = 0;
|
|
uint64_t subsampleTicks_ = 0;
|
|
uint64_t settlingTimeoutUs_ = 0;
|
|
uint64_t settlingDeadlineUs_ = 0;
|
|
uint64_t measurementStartTick_ = 0;
|
|
uint64_t deadlineTick_ = 0;
|
|
uint64_t pointOriginTick_ = 0;
|
|
uint64_t lastEventTick_ = 0;
|
|
uint64_t lastActiveTxTick_ = 0;
|
|
uint8_t settleCycles_ = 0;
|
|
uint8_t settledCycles_ = 0;
|
|
uint8_t subsampleCount_ = DRIVER_SHORT_SAMPLE_PROGRESS_STEPS;
|
|
uint8_t completedSubsamples_ = 0;
|
|
bool rxActiveRawHigh_ = true;
|
|
bool txPulseLightOn_ = true;
|
|
bool rxActive_ = false;
|
|
bool measurementClosed_ = false;
|
|
bool havePointOrigin_ = false;
|
|
bool haveLastActiveTx_ = false;
|
|
bool haveRawTick_ = false;
|
|
uint32_t lastRawTick_ = 0;
|
|
uint64_t tickEpoch_ = 0;
|
|
volatile uint8_t currentStep_ = 0;
|
|
volatile bool progressUpdatePending_ = false;
|
|
TraceEvent trace_[TRACE_CAPACITY] = {};
|
|
uint8_t traceWrite_ = 0;
|
|
uint8_t traceCount_ = 0;
|
|
};
|