#pragma once #include "Receiver.h" enum class MeasureState : uint8_t { IDLE, SETTLING, RUNNING, STEP_READY, PASS, FAIL }; class Measurement { public: explicit Measurement(PulseReceiver &receiver) : receiver_(receiver) {} bool start(float expectedHz, float expectedDuty, float tolerancePct, uint32_t testTimeMs, uint8_t settleCycles); MeasureState update(); bool continueAfterDisplay(); void abort(); MeasureState state() const { return state_; } FailReason reason() const { return stats_.reason; } const StageStats &stats() const { return stats_; } uint8_t progressStep() const { return currentStep_; } bool statsSnapshot(StageStats &out) const; private: static void taskEntry(void *context); void taskLoop(); MeasureState processOnce(); void fail(FailReason reason); void completeMeasurement(); void publishStats(); PulseReceiver &receiver_; volatile MeasureState state_ = MeasureState::IDLE; TaskHandle_t task_ = nullptr; StageStats stats_ = {}; StageStats publishedStats_ = {}; mutable portMUX_TYPE statsMux_ = portMUX_INITIALIZER_UNLOCKED; PeriodLimits limits_ = {}; uint32_t expectedHz_ = 0; float expectedDutyPct_ = 0.0f; uint8_t settleCycles_ = 0, settleLeft_ = 0; uint64_t measurementStartTick_ = 0, deadlineTick_ = 0, stepTicks_ = 0; uint32_t startedMs_ = 0, measurementStartMs_ = 0, lastPeriodMs_ = 0; uint32_t stepTimeMs_ = 1, expectedPeriodMs_ = 1; volatile uint8_t currentStep_ = 0; PulsePeriod periodBatch_[PERIOD_BATCH_SIZE] = {}; };