Files
OptoTest/OpticalChannelTester/DriverTest.h

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;
};