#include "Core.h" #include #include const char *roleName(Role r) { static const char *names[] = {"SOLO", "MASTER", "SLAVE"}; const uint8_t i = static_cast(r); return i < 3 ? names[i] : "?"; } const char *failName(FailReason r) { static const char *names[] = {"NONE", "NO SIGNAL", "PERIOD OUT", "DUTY OUT", "EXTRA EDGE", "GLITCH", "LOST EDGE", "TOO FEW PERIODS", "LINK LOST", "UNSUPPORTED", "RESOLUTION", "ABORTED"}; const uint8_t i = static_cast(r); return i < (sizeof(names) / sizeof(names[0])) ? names[i] : "UNKNOWN"; } void StageStats::reset() { memset(this, 0, sizeof(*this)); minPeriod = minActive = UINT32_MAX; reason = FailReason::NONE; } uint32_t settingsChecksum(const Settings &s) { const uint8_t *p = reinterpret_cast(&s); const size_t n = offsetof(Settings, checksum); uint32_t hash = 2166136261UL; for (size_t i = 0; i < n; ++i) { hash ^= p[i]; hash *= 16777619UL; } return hash; } uint32_t frequencyPointCount(uint32_t startHz, uint32_t endHz, uint32_t stepHz) { if (!startHz || !stepHz || endHz <= startHz) return 0; const uint64_t span = static_cast(endHz) - startHz; return static_cast(span / stepHz + 1U + ((span % stepHz) ? 1U : 0U)); } uint32_t frequencyAt(uint32_t startHz, uint32_t endHz, uint32_t stepHz, uint32_t index) { const uint32_t count = frequencyPointCount(startHz, endHz, stepHz); if (!count || index >= count) return 0; if (index == count - 1) return endHz; const uint64_t v = static_cast(startHz) + static_cast(stepHz) * index; return v < endHz ? static_cast(v) : endHz; } uint64_t nominalTotalUs(const TestParams &p, uint32_t settleCycles) { uint64_t total = 0; const uint32_t count = frequencyPointCount(p.startHz, p.endHz, p.stepHz); for (uint32_t i = 0; i < count; ++i) { const uint32_t f = frequencyAt(p.startHz, p.endHz, p.stepHz, i); total += (1000000ULL * settleCycles + f - 1) / f; total += static_cast(p.testTimeMs) * 1000ULL * p.repeats; } return total; } bool periodWithin(float measured, float expected, float tolerance) { return expected > 0.0f && fabsf(measured - expected) * 100.0f / expected <= tolerance + 0.0001f; } bool dutyWithin(float measured, float expected, float tolerance) { return fabsf(measured - expected) <= tolerance + 0.0001f; } uint8_t choosePwmResolution(uint32_t frequencyHz, uint32_t sourceClockHz, uint8_t maxBits) { if (!frequencyHz || !sourceClockHz || !maxBits) return 0; uint8_t bits = maxBits; while (bits > 1 && static_cast(frequencyHz) * (1ULL << bits) > sourceClockHz) --bits; return bits; } FailReason validateResolution(uint32_t frequencyHz, float dutyPct, float accuracyPct, uint32_t captureHz, uint8_t pwmBits) { if (!frequencyHz || !captureHz || !pwmBits) return FailReason::RESOLUTION; const float periodTicks = static_cast(captureHz) / frequencyHz; const float activeTicks = periodTicks * dutyPct / 100.0f; const float inactiveTicks = periodTicks - activeTicks; if (periodTicks < 4.0f || activeTicks < 2.0f || inactiveTicks < 2.0f) return FailReason::RESOLUTION; const float timerPeriodError = 100.0f / periodTicks; const float timerDutyError = 100.0f / periodTicks; const float pwmDutyStep = 100.0f / static_cast((1UL << pwmBits) - 1UL); return (timerPeriodError > accuracyPct || timerDutyError > accuracyPct || pwmDutyStep > accuracyPct) ? FailReason::RESOLUTION : FailReason::NONE; } FailReason evaluatePeriod(const PulsePeriod &p, uint32_t tickHz, float expectedHz, float expectedDuty, float tolerance, uint8_t repeat, StageStats &s) { if (!p.periodTicks || p.activeTicks >= p.periodTicks) return FailReason::EXTRA_EDGE; const float hz = static_cast(tickHz) / p.periodTicks; const float duty = 100.0f * p.activeTicks / p.periodTicks; ++s.periods; s.periodSum += p.periodTicks; s.activeSum += p.activeTicks; if (p.periodTicks < s.minPeriod) s.minPeriod = p.periodTicks; if (p.periodTicks > s.maxPeriod) s.maxPeriod = p.periodTicks; if (p.activeTicks < s.minActive) s.minActive = p.activeTicks; if (p.activeTicks > s.maxActive) s.maxActive = p.activeTicks; FailReason reason = FailReason::NONE; if (!periodWithin(hz, expectedHz, tolerance)) reason = FailReason::PERIOD_OUT; else if (!dutyWithin(duty, expectedDuty, tolerance)) reason = FailReason::DUTY_OUT; if (reason != FailReason::NONE && s.reason == FailReason::NONE) { s.reason = reason; s.firstBadPeriod = s.periods; s.firstBadRepeat = repeat; s.badFrequency = hz; s.badDuty = duty; } return reason; }