#include "Measurement.h" #include "Config.h" #include bool Measurement::start(float hz, float duty, float tolerance, uint32_t timeMs, uint8_t repeats, uint8_t settleCycles) { if (!hz || !timeMs || !repeats || repeats > 10 || !receiver_.start(static_cast(hz))) return false; expectedHz_ = hz; expectedDuty_ = duty; tolerance_ = tolerance; timeMs_ = timeMs; repeats_ = repeats; settleLeft_ = settleCycles; stats_.reset(); memset(repeatPeriods_, 0, sizeof(repeatPeriods_)); measurementStartTick_ = deadlineTick_ = 0; startedMs_ = millis(); measurementStartMs_ = lastPeriodMs_ = 0; state_ = MeasureState::SETTLING; return true; } void Measurement::fail(FailReason reason) { if (stats_.reason == FailReason::NONE) stats_.reason = reason; receiver_.stop(); state_ = MeasureState::FAIL; } MeasureState Measurement::update() { if (state_ != MeasureState::SETTLING && state_ != MeasureState::RUNNING) return state_; if (receiver_.overflowed()) { fail(FailReason::GLITCH); return state_; } PulsePeriod period; while (receiver_.poll(period)) { if (state_ == MeasureState::SETTLING) { if (settleLeft_) --settleLeft_; if (!settleLeft_) { measurementStartTick_ = period.startTick + period.periodTicks; repeatTicks_ = static_cast(receiver_.tickHz()) * timeMs_ / 1000ULL; deadlineTick_ = measurementStartTick_ + repeatTicks_ * repeats_; stats_.reset(); measurementStartMs_ = lastPeriodMs_ = millis(); state_ = MeasureState::RUNNING; } continue; } const uint64_t endTick = period.startTick + period.periodTicks; if (period.startTick < measurementStartTick_) continue; // leading incomplete period if (endTick > deadlineTick_) break; // trailing incomplete period uint8_t repeat = static_cast((period.startTick - measurementStartTick_) / repeatTicks_); if (repeat >= repeats_) repeat = repeats_ - 1; ++repeatPeriods_[repeat]; lastPeriodMs_ = millis(); const FailReason r = evaluatePeriod(period, receiver_.tickHz(), expectedHz_, expectedDuty_, tolerance_, repeat + 1, stats_); if (r != FailReason::NONE) { fail(r); return state_; } } const uint64_t expectedPeriodMs = static_cast(1000.0f / expectedHz_) + 1; const uint64_t settleTimeout = (static_cast(PWM_SETTLE_CYCLES + NO_SIGNAL_TIMEOUT_PERIODS) * expectedPeriodMs) + 20; if (state_ == MeasureState::SETTLING && millis() - startedMs_ > settleTimeout) fail(FailReason::NO_SIGNAL); if (state_ == MeasureState::RUNNING && measurementStartTick_) { const uint32_t now = millis(); const uint32_t totalMs = timeMs_ * repeats_; const uint32_t edgeTimeoutMs = static_cast(expectedPeriodMs * NO_SIGNAL_TIMEOUT_PERIODS + 2); if (now - measurementStartMs_ < totalMs && now - lastPeriodMs_ > edgeTimeoutMs) { fail(FailReason::LOST_EDGE); return state_; } if (now - measurementStartMs_ > totalMs + expectedPeriodMs + 2) { for (uint8_t i = 0; i < repeats_; ++i) if (!repeatPeriods_[i]) { fail(FailReason::TOO_FEW_PERIODS); return state_; } receiver_.stop(); state_ = MeasureState::PASS; } } return state_; } void Measurement::abort() { if (state_ == MeasureState::SETTLING || state_ == MeasureState::RUNNING) fail(FailReason::ABORTED); }