#pragma once #include #include enum class Role : uint8_t { SOLO, MASTER, SLAVE }; enum class FailReason : uint8_t { NONE, NO_SIGNAL, PERIOD_OUT, DUTY_OUT, EXTRA_EDGE, GLITCH, LOST_EDGE, TOO_FEW_PERIODS, LINK_LOST, UNSUPPORTED, RESOLUTION, ABORTED }; const char *roleName(Role role); const char *failName(FailReason reason); struct Settings { uint16_t version; uint8_t role; uint8_t startIndex; uint8_t endIndex; uint8_t accuracyIndex; uint8_t timeIndex; uint8_t dutyIndex; uint32_t checksum; }; struct TestParams { uint32_t startHz; uint32_t endHz; float accuracyPct; uint32_t testTimeMs; uint8_t dutyPct; }; struct PulsePeriod { uint64_t startTick; uint32_t periodTicks; uint32_t activeTicks; }; 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; uint16_t divider; uint8_t bits; }; uint32_t settingsChecksum(const Settings &s); uint32_t frequencyPointCount(uint32_t startHz, uint32_t endHz); uint32_t frequencyAt(uint32_t startHz, uint32_t endHz, 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 chooseIntegerPwmConfig(uint32_t requestedHz, uint32_t sourceClockHz, uint8_t maxBits, uint8_t dutyPct, IntegerPwmConfig &config); FailReason validateResolution(uint32_t frequencyHz, float dutyPct, float accuracyPct, uint32_t captureResolutionHz, uint8_t pwmBits); 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);