#pragma once #include #include enum class Role : uint8_t { SOLO, MASTER, SLAVE }; enum class TestGroup : uint8_t { OPTICS, BOARD }; enum class TestKind : uint8_t { OPTICAL, DRIVER }; enum class BoardTest : uint8_t { ADC, PWM_OUTPUT, RX_INPUT }; enum class LightCode : uint8_t { HH, HL, LH, LL }; enum class FailReason : uint8_t { NONE, NO_SIGNAL, PERIOD_OUT, DUTY_OUT, EXTRA_EDGE, GLITCH, LOST_EDGE, DATA_LOSS, LINK_LOST, UNSUPPORTED, RESOLUTION, ABORTED, ACK_MISSING, ACK_TIMING, DRIVER_FAULT, ACK_MERGED }; const char *roleName(Role role); const char *testGroupName(TestGroup group); const char *testKindName(TestKind kind); const char *boardTestName(BoardTest test); const char *lightCodeName(LightCode code); const char *failName(FailReason reason); struct Settings { uint16_t version; uint8_t role; uint8_t testKind; uint8_t lightCode; uint8_t frequencyIndex; uint8_t maxPulseIndex; uint8_t minPulseIndex; uint8_t accuracyIndex; uint8_t timeIndex; uint8_t testGroup; uint8_t boardTest; uint32_t checksum; }; struct TestParams { uint32_t frequencyHz; uint32_t maxPulseNs; uint32_t minPulseNs; float accuracyPct; uint32_t testTimeMs; }; struct PulsePeriod { uint64_t startTick; uint32_t periodTicks; uint32_t activeTicks; uint32_t activeTickHz; }; struct StageStats { uint32_t periods; uint64_t periodSum; uint64_t activeSum; uint32_t minPeriod; uint32_t maxPeriod; uint32_t minActive; uint32_t maxActive; uint32_t firstBadPeriod; uint8_t firstBadRepeat; float badFrequency; float badDuty; uint32_t droppedItems; FailReason reason; void reset(); }; struct PeriodLimits { uint32_t minPeriodTicks; uint32_t maxPeriodTicks; uint32_t minDutyX100; uint32_t maxDutyX100; }; struct IntegerPwmConfig { uint32_t actualHz; uint32_t dividerRaw; uint32_t dutyCount; uint32_t actualPulseNs; uint8_t bits; }; uint32_t settingsChecksum(const Settings &s); bool txActiveLightOn(const Settings &s); bool rxActiveLightOn(const Settings &s); uint32_t pulseWidthPointCount(uint32_t maxPulseNs, uint32_t minPulseNs); uint32_t pulseWidthAt(uint32_t maxPulseNs, uint32_t minPulseNs, uint32_t index); uint64_t nominalStageUs(uint32_t frequencyHz, uint32_t sampleTimeMs, uint32_t settleCycles); uint64_t nominalTotalUs(const TestParams &p, uint32_t settleCycles); bool periodWithin(float measuredHz, float expectedHz, float tolerancePct); bool dutyWithin(float measuredPct, float expectedPct, float tolerancePct); float effectiveTolerancePct(float configuredPct); uint8_t choosePwmResolution(uint32_t frequencyHz, uint32_t sourceClockHz, uint8_t maxBits); uint8_t chooseStablePwmResolution(uint32_t frequencyHz, uint32_t sourceClockHz, uint8_t maxBits, uint8_t dutyPct); bool choosePwmConfig(uint32_t requestedHz, uint32_t requestedPulseNs, uint32_t sourceClockHz, uint8_t maxBits, IntegerPwmConfig &config); FailReason validateResolution(uint32_t frequencyHz, float dutyPct, float accuracyPct, uint32_t periodResolutionHz, uint32_t pulseResolutionHz, uint8_t pwmBits, uint16_t averagingPeriods); FailReason evaluatePeriod(const PulsePeriod &period, uint32_t tickHz, float expectedHz, float expectedDuty, float tolerancePct, uint8_t repeat, StageStats &stats); bool makePeriodLimits(uint32_t expectedHz, float expectedDuty, float tolerancePct, uint32_t tickHz, PeriodLimits &limits); FailReason evaluatePeriodFast(const PulsePeriod &period, uint32_t tickHz, const PeriodLimits &limits, uint8_t repeat, StageStats &stats); FailReason evaluatePeriodWindow(uint64_t periodSum, uint64_t activeSum, uint32_t periodCount, uint32_t tickHz, float expectedHz, float expectedDuty, float tolerancePct, uint32_t minPeriod, uint32_t maxPeriod, uint8_t repeat, StageStats &stats);