-
-
Notifications
You must be signed in to change notification settings - Fork 116
fix(hl2): rate-limit the repeated ADC-overload warning #5381
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
922e9d5
fix(hl2): rate-limit the repeated ADC-overload warning
on8st 8e20af6
docs(hermes): mark 6a done and correct 15.7's stale severity
on8st 776a70d
fix(hl2): correct the flush comment, and stop QDebug spacing the paren
on8st e3d28da
fix(hl2): start() not restart() on the overload clock, behind a polic…
on8st dbbf86d
Merge current main into HL2 overload fix. Principle X.
Ozy311 079e169
Merge released memory-tab fix into overload branch. Principle X.
Ozy311 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| #pragma once | ||
|
|
||
| // When the ADC-overload warning may be emitted, as a pure decision. | ||
| // | ||
| // The AD9866's overload flag is a per-frame sample of a level comparator, not | ||
| // an event. On a strong band it chatters, so nearly every telemetry sample is a | ||
| // rising edge and one message repeats at the full telemetry cadence. An edge | ||
| // gate alone is necessary and never sufficient. | ||
| // | ||
| // Two properties make this worth a seam rather than an inline condition, and | ||
| // both are timing-dependent in a way that is otherwise only reachable by | ||
| // running a radio for ten seconds: | ||
| // | ||
| // * THE FLUSH IS NOT ON AN EDGE. A burst that stops must still report its | ||
| // tally. Deciding only when the flag next asserts would hold the count | ||
| // until the band goes loud again -- which may be hours away, or never. | ||
| // * THE FIRST ASSERTION IS IMMEDIATE. A limiter that made the operator wait | ||
| // out a window before the first warning would be silent during exactly the | ||
| // interval when the front end is being slammed and nobody knows yet. | ||
| // | ||
| // Hl2Backend evaluates these functions rather than its own copy, so what the | ||
| // suite exercises is what the radio runs -- the reasoning Hl2TxLevelPolicy.h | ||
| // states, and the same reason it applies here. | ||
|
|
||
| #include <cstdint> | ||
|
|
||
| namespace AetherSDR::hl2 { | ||
|
|
||
| // What publishTelemetry should do with the overload counter this update. | ||
| struct AdcOverloadWarn { | ||
| bool warn = false; // emit anything at all? | ||
| bool aggregate = false; // the "(N times in M ms)" form rather than a bare line | ||
| int count = 0; // assertions being reported; meaningful when warn | ||
| bool restartClock = false; | ||
| }; | ||
|
|
||
| // `assertions` counts RISING EDGES of the flag seen since the last flush -- not | ||
| // telemetry updates, and not the flag's level. `clockValid` is false before the | ||
| // first flush has ever run. | ||
| inline AdcOverloadWarn adcOverloadWarn(int assertions, | ||
| bool clockValid, | ||
| std::int64_t elapsedMs, | ||
| std::int64_t intervalMs) | ||
| { | ||
| AdcOverloadWarn out; | ||
| if (assertions <= 0) { | ||
| return out; // nothing seen; the window keeps running | ||
| } | ||
| // An invalid clock is the first assertion ever: report it now rather than | ||
| // making the operator wait out a window that has not started. | ||
| const bool windowOpen = clockValid && elapsedMs < intervalMs; | ||
| if (windowOpen) { | ||
| return out; // suppressed; the count keeps accruing | ||
| } | ||
| out.warn = true; | ||
| out.count = assertions; | ||
| // ONE assertion is a hint and gets the bare line. More than one is the | ||
| // rate, and the rate IS the severity: a flag that sets once is a hint, one | ||
| // that sets on every sample for a minute is a front end being slammed. | ||
| out.aggregate = assertions > 1; | ||
| out.restartClock = true; | ||
| return out; | ||
| } | ||
|
|
||
| } // namespace AetherSDR::hl2 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| // The ADC-overload warning's rate limit, as a deterministic decision. | ||
| // | ||
| // The behaviour is timing-dependent and was previously only reachable by | ||
| // running a radio into a strong band for ten seconds — which is why it shipped | ||
| // with a bug the compiler could not see: the flush called restart() on a | ||
| // QElapsedTimer that the first-assertion path guarantees is invalid, and | ||
| // reading elapsed time from a timer that was never started is undefined. | ||
| // (#5381 review.) | ||
| // | ||
| // Hl2Backend evaluates these same functions rather than its own copy, so what | ||
| // passes here is what the radio runs | ||
| // (core/backends/hl2/Hl2OverloadPolicy.h). | ||
|
|
||
| #include "core/backends/hl2/Hl2OverloadPolicy.h" | ||
|
|
||
| #include <cstdio> | ||
|
|
||
| using AetherSDR::hl2::adcOverloadWarn; | ||
|
|
||
| namespace { | ||
|
|
||
| int g_failures = 0; | ||
|
|
||
| void check(bool ok, const char* what) | ||
| { | ||
| std::printf("%s %s\n", ok ? "[ OK ]" : "[FAIL]", what); | ||
| if (!ok) { | ||
| ++g_failures; | ||
| } | ||
| } | ||
|
|
||
| constexpr std::int64_t kInterval = 10000; // kAdcOverloadWarnIntervalMs | ||
|
|
||
| } // namespace | ||
|
|
||
| int main() | ||
| { | ||
| // ---- 1. The FIRST assertion reports immediately ------------------------ | ||
| // | ||
| // The clock is invalid before the first flush has ever run. A limiter that | ||
| // made the operator wait out a window here would be silent during exactly | ||
| // the interval when the front end is being slammed and nobody knows yet. | ||
| { | ||
| const auto w = adcOverloadWarn(/*assertions=*/1, /*clockValid=*/false, | ||
| /*elapsedMs=*/0, kInterval); | ||
| check(w.warn, "the first assertion warns immediately, invalid clock"); | ||
| check(!w.aggregate, "and takes the bare form, not the count form"); | ||
| check(w.restartClock, "and starts the window"); | ||
| } | ||
|
|
||
| // ---- 2. Chatter inside the window is SUPPRESSED, and accrues ----------- | ||
| { | ||
| const auto w = adcOverloadWarn(/*assertions=*/40, /*clockValid=*/true, | ||
| /*elapsedMs=*/2500, kInterval); | ||
| check(!w.warn, "chatter inside the window emits nothing"); | ||
| check(!w.restartClock, "and does not restart the window"); | ||
| } | ||
|
|
||
| // ---- 3. …then AGGREGATES when the window expires ----------------------- | ||
| { | ||
| const auto w = adcOverloadWarn(/*assertions=*/133, /*clockValid=*/true, | ||
| /*elapsedMs=*/kInterval, kInterval); | ||
| check(w.warn && w.aggregate, "the expired window reports the aggregate"); | ||
| check(w.count == 133, "carrying the full count the window swallowed"); | ||
| } | ||
|
|
||
| // ---- 4. The window flushes WITHOUT a new assertion --------------------- | ||
| // | ||
| // The decision is made on every telemetry update, not on an edge. A burst | ||
| // that stops must still report its tally: deciding only when the flag next | ||
| // asserts would hold the count until the band goes loud again, which may be | ||
| // hours away or never. Same inputs as case 3 — the point is that reaching | ||
| // this decision requires no new assertion, only the update that carries it. | ||
| { | ||
| const auto w = adcOverloadWarn(/*assertions=*/7, /*clockValid=*/true, | ||
| /*elapsedMs=*/kInterval + 5000, kInterval); | ||
| check(w.warn && w.count == 7, | ||
| "a stopped burst still reports its tally once the window expires"); | ||
| } | ||
|
|
||
| // ---- 5. An isolated assertion after a quiet interval is immediate ------ | ||
| { | ||
| const auto w = adcOverloadWarn(/*assertions=*/1, /*clockValid=*/true, | ||
| /*elapsedMs=*/600000, kInterval); | ||
| check(w.warn && !w.aggregate, | ||
| "an isolated overload after a long quiet reports at once, bare"); | ||
| } | ||
|
|
||
| // ---- 6. Nothing seen means nothing said, however long the window ------- | ||
| // | ||
| // Guards the flush being unconditional on every telemetry update: without | ||
| // this the limiter would warn on a quiet radio forever. | ||
| { | ||
| const auto a = adcOverloadWarn(0, true, kInterval * 100, kInterval); | ||
| const auto b = adcOverloadWarn(0, false, 0, kInterval); | ||
| check(!a.warn && !a.restartClock, | ||
| "no assertions, expired window: silence"); | ||
| check(!b.warn && !b.restartClock, | ||
| "no assertions, invalid clock: silence"); | ||
| } | ||
|
|
||
| // ---- 7. The boundary is expiry, not strictly-greater -------------------- | ||
| { | ||
| const auto below = adcOverloadWarn(5, true, kInterval - 1, kInterval); | ||
| const auto at = adcOverloadWarn(5, true, kInterval, kInterval); | ||
| check(!below.warn, "one ms before expiry is still suppressed"); | ||
| check(at.warn, "at expiry it reports — hasExpired() is inclusive"); | ||
| } | ||
|
|
||
| if (g_failures == 0) { | ||
| std::printf("\nALL PASS\n"); | ||
| return 0; | ||
| } | ||
| std::printf("\nFAILURES PRESENT\n"); | ||
| return 1; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.