|
| void | tone_cfg_defaults (tone_cfg_t *c, uint32_t tick_hz) |
| | Defaults for a clock of tick_hz: 8000us windows, 400 to 6500Hz, carrier from 7kHz, 500ns glitch, 8000ns hold-off, 3000us gap, 8% split, 3 periods a beep, 2 a window.
|
| bool | tone_init (tone_t *d, const tone_cfg_t *c) |
| | Start from nothing.
|
| void | tone_edge (tone_t *d, uint64_t t, bool level) |
| | One edge at tick t, level after it.
|
| void | tone_feed (tone_t *d, const tone_edge_t *e, size_t n) |
| | n edges in time order.
|
| void | tone_advance (tone_t *d, uint64_t now) |
| | No edge up to tick now: a silence ends a beep, a window closes.
|
| void | tone_flush (tone_t *d) |
| | End the beep under way at its last edge, as a silence would, and forget the line's level, so the first rise after it starts a run: the caller's edges have a hole (a buffer overrun) or the recording ends.
|
| bool | tone_window (const tone_t *d, tone_window_t *w) |
| | The last window finished; false before the first.
|
| bool | tone_next_beep (tone_t *d, tone_beep_t *b) |
| | Take the oldest beep reported; false with none waiting.
|
| bool | tone_busy (const tone_t *d) |
| | A run of bursts is under way.
|
| const tone_stats_t * | tone_stats (const tone_t *d) |
| | The counters of the detector.
|
| uint64_t | tone_ns_ticks (uint64_t tick_hz, uint64_t ns) |
| | ns as ticks of a tick_hz clock, rounded up: the conversion of every time setting.
|
| uint64_t | tone_ticks_us (const tone_t *d, uint64_t ticks) |
| | ticks of the detector's clock in microseconds, rounded down.
|
| void | tone_holdoff_init (tone_holdoff_t *h, uint32_t tick_hz, uint32_t hold_ns) |
| | hold_ns of a clock of tick_hz, rounded up to whole ticks; the line starts low.
|
| size_t | tone_holdoff_edge (tone_holdoff_t *h, uint64_t t, bool level, tone_edge_t out[2]) |
| | One edge at t; the edges reported, 0 to 2, into out in time order: a held fall whose low lasted the hold-off, then the rise.
|
| size_t | tone_holdoff_advance (tone_holdoff_t *h, uint64_t now, tone_edge_t out[1]) |
| | No edge up to now: a held fall whose low has lasted the hold-off by then, into out; 0 or 1.
|
| uint64_t | tone_holdoff_horizon (const tone_holdoff_t *h, uint64_t now) |
| | now less the hold-off, at least 0, and never before the latest edge handed out: every edge before it is out, and it never runs backward, so advancing the detector to it after feeding the edges out keeps the detector's time in order.
|
ESC (electronic speed controller) tones from the edges of one motor phase.
An ESC beeps by driving the motor windings at the tone frequency, often chopped by its PWM (pulse-width modulation) carrier at 8 to 48kHz. One phase reaches an IOmcu input through a series resistor and a zener clamp, so the input is a logic level: high while the phase is driven high, or floats above the input threshold, and low otherwise. A PIO (programmable input/output) state machine stamps each edge on a counter; this module turns those stamps into tones.
The signal's three time scales, and how each is found:
- Carrier. A rise that follows the rise before it by at most the period of carrier_min_hz is a carrier-rate rise. The carrier period is the shortest rise interval inside a burst, not counting the interval from the burst's first rise, which a burst gated from a carrier running free can cut short. Until a burst has had a third rise, it is the shortest carrier-rate low plus the longest carrier-rate high. Both are kept from the run's first rise. Once a carrier is seen, a rise starts a burst when the low before it is at least 1.25 carrier periods: a low inside a burst is shorter than one period, and the quarter is room for edges that move. With no carrier seen, the drive is not chopped and every pulse is a burst. A duty that changes inside a beep moves lows and highs and leaves the period. A low shorter than glitch_ns is no low: the pulse goes on. With a capture hold-off, a rise starts a burst only after a low of at least twice the hold-off: a carrier low within the edges' jitter of the hold-off is reported at some periods and not at others, and one reported after a stretch of swallowed ones follows its rise by several carrier periods, too far apart to read as carrier. The tone's own low between bursts has to be that long.
- Tone. The interval from one burst's first rise to the next is one tone period. A period between half the period of f_max_hz and the period of f_min_hz is in range: a burst start moved by a free-running carrier shortens one period and lengthens the next, and the pair still counts. A window holds a tone when at least window_min_periods in-range periods end in it and their mean lies between the periods of f_max_hz and f_min_hz; its frequency is that mean's.
- Beep. A beep is a run of bursts. It ends where the line has had no edge for gap_us, or where a block of TONE_BLOCK periods gives a pitch more than split_pct away from the beep's mean: a pitch change with no silence between, which splits the beep at the first period nearer the new pitch than the old. A block's pitch is the mean of its periods within TONE_OUTLIER_PCT of their median; it has moved when all but one of the block's periods lie more than half of split_pct from the beep's mean on its side. A beep with fewer than TONE_BLOCK periods in its mean does not split: when a block moves by TONE_OUTLIER_PCT it starts its mean again and keeps its start, its first periods having come before the carrier was known. A beep reports its first rise and its last edge, the mean of its in-range periods as its frequency, and its burst count. A period more than TONE_OUTLIER_PCT from the beep's mean is a burst but not part of the mean: a burst missed doubles one period. A run with fewer than min_periods in-range periods, or whose mean lies outside the tone range, is not a beep: a click at power-up, a stuck line, a drive faster than f_max_hz.
Bounds, each in whole ticks:
- Inclusive. A rise interval of exactly the period of carrier_min_hz is carrier-rate. A low of exactly 1.25 carrier periods starts a burst. A low of exactly glitch_ns is a low, and one of exactly twice the hold-off can start a burst. A silence of exactly gap_us ends a beep. A tone of exactly f_min_hz or f_max_hz is in range, and a period of exactly half the period of f_max_hz. A beep of exactly min_periods periods, and a window of exactly window_min_periods, hold a tone. An edge at the same tick as the last is taken.
- Exclusive. A pitch change is more than split_pct, so a block exactly split_pct away does not split; a period is an outlier when more than TONE_OUTLIER_PCT from the mean. A window is [n, n + 1) window_us: a period that ends on the boundary belongs to the next window.
- Rounding. The period of f_max_hz rounds down, the periods of f_min_hz and carrier_min_hz round up, so a period exactly at a bound, which the tick clock sees as the tick below or the tick above, is inside it. Every time setting – window_us, glitch_ns, hold_ns, gap_us – rounds up to whole ticks by one rule, and start_low is twice the hold-off so rounded, the hold-off the capture applies: a low or a silence counts once it has lasted that long.
What the edges cannot tell:
- A drive that commutates, as a motor start does, at a rate in the tone range is a tone here. The caller listens only while the ESC sounds its menu.
- A tone whose drive is off for less than 1.25 carrier periods between bursts merges them: the tone is heard as one long burst and reported as no tone.
- The tapped phase sees the bursts of the phase pair the ESC drives. An ESC that reverses the pair every half period shows two bursts a period on the floating phase, and the frequency reads one octave high. Pitches compared on one bench are compared on one phase, so a recorded reference carries the same factor.
- A carrier that runs free of the tone moves each burst's first rise by up to one carrier period, so a single period is known to one carrier period; a mean over n periods to one carrier period in n.
Time is a count of ticks of tick_hz, 64 bits wide, never wrapping on a bench; the caller extends the PIO's counter. Edges arrive in time order. tone_advance() tells the detector time has passed without an edge, so a silence ends a beep and a window closes without one.
Pure C, no SDK (software development kit), no allocation; no division and no floating point per edge. Host-tested in test_tone.
SPDX-License-Identifier: MIT
Start from nothing.
False, leaving d unusable, when c contradicts itself or names a range the tick clock cannot resolve: f_min_hz >= f_max_hz, carrier_min_hz <= f_max_hz, f_max_hz or carrier_min_hz above tick_hz (a period under one tick), hold_ns above TONE_HOLD_NS_MAX, glitch_ns outside 1 to TONE_GLITCH_NS_MAX or under one tick, gap_us under the period of f_min_hz or above TONE_GAP_US_MAX, a window outside 1000us to TONE_WINDOW_US_MAX, f_min_hz under 50Hz, a minimum of 0, split_pct above 100. Also refused, as settings under which no signal makes a tone, in whole ticks: the period of f_min_hz, or the gap, at most the glitch or at most twice the hold-off (no low could part two bursts without being a glitch, being too short to start one, or ending the run); the period of f_max_hz rounded down at most the period of carrier_min_hz rounded up, where the tone and carrier ranges meet in whole ticks; window_min_periods - 1 of the shortest burst-start spacings (tone_t's spacing) filling a window.
- Parameters
-
| d | Detector state; set up by this call. |
| c | Settings to check and use. |
- Returns
- true when the detector is ready.