Skip to content

Oscillation Damper

Some engine, alternator, and belt combinations develop a slow rhythmic wobble — a hunt — at low engine speed under heavy load: field drive, engine speed, and bus voltage all swinging together, somewhere between a third of a cycle and three cycles per second. It is not a fault in any one part. It is a feedback loop: near idle, asking the alternator for more output drags the engine speed down, and the speed drop takes back most of the output you asked for. Run a control loop against a plant that behaves like that and, at some combination of speed and load, the loop can end up feeding the wobble instead of damping it.

The oscillation damper is the firmware's answer. It watches for the wobble, runs a controlled experiment to find out whether the regulator is causing it, applies the one fix that experiment justifies, and remembers the result permanently against engine speed so the fix is in place before the wobble can restart on any later trip.

It is off by default (HuntGovEnable). The switch and its settings are in Setup > Alternator > Tuning > Current > Controller Parameters; the live status, speed map, and episode record are in Live Data > Diag > Alternator > Oscillation Damper.

Related pages: the control loops the damper acts on are described in Charging Control. Protection systems are separate and unaffected by anything on this page — see Safeties and Protections.


Detection — six tuned filters, not an FFT

The damper samples the applied field drive every 50 ms into a rolling 6.4-second window and, every 1.6 s, measures the wobble amplitude at six fixed frequencies: 0.31, 0.47, 0.70, 1.05, 1.56, and 2.34 Hz. Each measurement is a Goertzel filter — the standard trick for asking "how much of frequency X is in this signal" without paying for a full FFT. Six of them cover the whole hunt band for tens of microseconds per scan.

The wobble must first be big enough: the loudest bin has to beat the Detection Amplitude setting, measured as percent of field duty swinging at that frequency (default 0.5%, adjustable, no upper limit). That bar belongs to the installation rather than to the firmware — the same physical wobble moves roughly a quarter as much duty on a 48-volt bank as on a 12-volt one. An amplitude number alone is still not a detection. Three more conditions have to hold:

  • It has to be a tone. The loudest bin must beat the median of the other five by 3×. A hunt is a narrow tone and passes; broadband noise from a cycling load raises all six bins together and fails.
  • Engine speed has to be steady. Detection compares window-average speed, so the wobble's own speed ripple does not disqualify it — but a moving throttle does. No steady speed, no detection.
  • It has to persist. Three consecutive scans (about 10 s from onset) must agree before anything acts. Those three scans are averaged and become the episode's baseline measurement.

Scans are discarded wholesale whenever anything else owns or disturbs the field — protection events, recovery, tuning modes, manual override, a commanded setpoint step — so the detector only ever reasons about normal closed-loop operation.

The second signature: a D-term running in circles

One hunt variant barely shows up in field drive at all. Bench-measured in August 2026: a 1.42 Hz voltage oscillation, a third of a volt peak-to-peak at the battery, with only a quarter-percent of duty swing — under the detector's normal trigger floor. In that mode the driver is the voltage loop's derivative term (the D-term): against the sign-flipped idle plant, the D-term's phase lead is exactly what closes the oscillation condition, and the deadband that normally keeps it quiet is what sets the wobble's size.

That variant carries its own tell, and it costs nothing to check: a healthy D-term fires in brief one-sided bursts that decay within a second or two. A D-term caught in a limit cycle alternates direction rhythmically around zero, for as long as the wobble runs — net contribution, nothing. The damper counts those direction changes every scan. When they are present, the duty-amplitude bar drops to 30% of the Detection Amplitude setting (0.15% at the 0.5% default, and it tracks that setting rather than sitting at a fixed number) and the tone test relaxes to 2× — the alternation itself is the periodicity evidence. This relaxed path cannot false-trigger on a quiet system for the same reason it exists: a quiet system's D-term does not alternate.


The experiment — one lever at a time

A confirmed wobble opens a test. The test moves exactly one thing, because moving two would make the result unreadable.

Which lever goes first is decided by the alternation count. If the qualify window showed at least about half the direction changes the detected frequency would produce (with a floor of three), the D-term was participating and gets tested first — pausing the participant is the direct experiment. Otherwise the test goes straight to the classic lever: the current loop's integral gain.

The procedure is the same either way:

Stage Time What happens
Cut instant D test: the D-term is paused entirely, all other gains untouched. Ki test: current-loop integral gain to Damped Gain (default 50%) in one step, bumpless.
Blind wait 6.4 s The measurement window refills with post-cut data. Engine speed must stay steady or the test aborts and everything reverts.
Read 4.8 s Three more scans, averaged.
Verdict ~11 s after the cut Wobble shrank by at least Required Improvement → verified, mapped. Otherwise → revert, move on.

Both sides of the verdict comparison are three-scan averages. Single 6.4-second windows jitter against each other by more than the verify bar on their own; averaged ones do not.

The metric depends on the mode. A duty-mode wobble is judged on duty amplitude. A volts-mode wobble (qualified below the normal duty bar) is judged on voltage movement — the scan-average rate of change of bus voltage — in both stages, because comparing duty numbers that sit below the detector's own noise floor would pass or fail on luck.

The D cut is all or nothing, on purpose. The Ki lever cuts to a percentage; the D lever only ever cuts to zero. The D-driven wobble self-regulates at whatever amplitude makes its deadband-gated gain close the loop — halve the D gain and the wobble does not shrink, it settles at a larger swing that restores the same loop gain. Off is the only depth that changes the answer.

The ladder. If pausing the D-term does not clear the wobble, the D-term is restored immediately and the same episode rolls straight into the Ki test — the readings just taken become the fresh baseline, so no time is wasted. Only when every applicable lever has failed does the damper conclude the wobble is not the regulator — a cycling load, or the engine's own governor — restore full strength everywhere, and stand down for the Retest Cooldown. It does not reach for the mechanical explanation while it still has an untested lever.


The map — fixes remembered by engine speed

A verified fix becomes a permanent entry on a speed map: a pocket. A pocket holds its fix flat across the span of engine speeds where it was verified, ramping linearly back to full strength over the Pocket Edge Taper beyond each end. From then on the fix is applied whenever the engine enters that speed range — before any wobble appears. The taper makes applied gain a continuous function of speed, so there is no edge to flap across and no hysteresis machinery.

Rules the map lives by:

  • Repeat episodes widen a pocket, never deepen it. A new verified episode at a nearby speed extends the flat core; overlapping pockets merge. Gain does not go below Damped Gain, and the D-term is either on or off — there is no ratchet toward ever-weaker control.
  • Ki pockets and D pockets are independent. They are tagged by lever, mapped separately, and may overlap: a speed can legitimately need reduced integral gain and the D-term off. Each lever follows its own map at all times, including while the other lever is mid-test.
  • Pockets survive restarts. They are erased by the Clear System button, and automatically by anything that invalidates them: a system voltage-class change, an engine-speed recalibration (pocket centers are raw engine-speed values, so a new scaling factor makes them meaningless), or the Alternator Health "Start Over" reset after a hardware swap.
  • A wobble inside a pocket, with every lever already applied, raises an alert instead of a deeper cut. The pocket was created by a verified response to a controlled cut, so a wobble that persists with the fixes in place points at a physical cause — belt tension, mounts, engine governor. This is also the self-correction for a pocket that should not exist: an external wobble that slipped past the verify bar keeps wobbling inside its pocket and surfaces here.

Absolute tach calibration error does not matter to the map — it compares the device's own speed reading against its own reading, same calibration both times. Only recalibration breaks the stored centers, which is why it clears the map.


What the dashboard shows

The Oscillation Damper card (Live Data > Diag > Alternator) carries:

  • Status — watching, testing (which lever), in a mapped trouble spot, or cooling down.
  • Current-loop gain now and Voltage damper (D-term) — the two levers' live values.
  • The speed map plot — the applied-gain-vs-speed trapezoid profile, pocket shading (blue for Ki, purple for D-term-off spans), episode dots (filled = verified, hollow = did not respond), and a live engine-speed marker.
  • The episode record — every test the damper has ever run, with its verdict, frequency, before/after amplitudes, and exit gain. The record is permanent and capped; the oldest half is trimmed when the cap is reached.

Settings, all next to the damper switch: Damped Gain (the Ki cut depth and pocket floor), Required Improvement (the verify bar), Pocket Edge Taper (wing width), Retest Cooldown, Speed Steadiness (the RPM-steady tolerance for detection and verification), Detection Confirm Scans (how many consecutive qualifying scans open a test), and Detection Amplitude (the duty-swing bar a scan has to beat).


Implementation details

  • Code: huntGovObserve() (per-tick sampler: one ring write, contamination flags, D-term alternation counter) and runHuntGovernor() (scan analysis and the episode state machine), both in 6_functions.ino. Analysis is self-gated to one Goertzel pass per 32 ring samples — 1.6 s, since the sampler itself is self-clocked to ~50 ms — and never runs inside the control call.
  • The Ki derate is a runtime multiplier applied at the single recomputeCcGains() choke point; the typed PidKi setting is never rewritten. The D lever is a runtime multiplier (g_huntKdScale) applied at the single point where the D-term trim is computed, so anti-windup and telemetry all see the scaled value. Live values stream as huntDerate (CSV1) and huntKdScale (CSV2); episode state as huntState (0 watching, 1 testing Ki, 3 cooldown, 4 testing D).
  • The pocket map lives in RAM and is mirrored to /huntpockets.csv on the device filesystem; the episode record is /huntledger.csv. Both are written only by a service that runs after the field has been off and settled — flash writes are kept out of the control path entirely, because a filesystem operation can stall the loop for tens of milliseconds at exactly the wrong moment.
  • The map and record are served on demand (/huntmap, /huntledger), not streamed. Clear System is a single endpoint that removes both files and resets live state; the lifecycle resets call the same code path.
  • Verdict tokens in the record: damped and external-suspected for Ki tests (amplitudes in % duty), damped-dterm and dterm-no-resp for D tests (amplitudes in V/s of voltage movement), aborted (and aborted-dterm when the episode was on the D rung) for episodes that lost steady conditions before a verdict.