M7
RTHS · online stability indicator

Output-only marginality · method 7

The structure warns you before it goes unstable — if you listen to the output.

In a real-time hybrid simulation the loop can lose stability at a single sharp resonance near 37 Hz. M7 hears that resonance in the measured displacement dm and rescales the model's known margin into a live reading — no input excitation, no dither. Precisely: not a from-scratch output-only margin, but a model-anchored NCF, tracked by the output.

spectrum of dm  ·  output displacement
healthy
0.588
NCF margin b
Spk = 2.00 Spk,ref = 2.00 peak/floor of PSD

Drag damage or let it sweep. As the 37 Hz peak grows, Spk rises and the margin b falls toward the 0.40 alarm — the entire method, in one panel.

PEAK BAND 25–42 Hz FLOOR BAND 55–75 Hz bnom 0.588 CALIBRATE first 7.5 s ALARM b < 0.40

The premise

Why the output knows what the input can't

Near the stability edge the loop grows a tall sensitivity peak at about 37 Hz. The closed loop amplifies its own internal noise — servo hum, friction, quantization — right there, so the resonance shows up in the measured displacement dm even when the test input never visits 37 Hz.

That growth is the incipient instability. So the output carries a warning the input physically cannot deliver — which is exactly why M7 can stay blind to the command and still see trouble coming.

The measurement

One number from the spectrum

Take a short window of dm. Compare the energy in the resonance band to a quiet reference band off to the side:

Spk = sqrt( peak PSD over 25–42 Hz / median PSD over 55–75 Hz )

A flat spectrum gives Spk ≈ 1. A sharpening 37 Hz peak drives it up. The floor band sits above the 60 Hz line so the ratio tracks the resonance, not the overall noise level.

The conversion

From a peak to a margin, in three steps

01

The peak is the sensitivity

When the dominant internal noise is output- or sensor-side, the output PSD near resonance is |S(jω)|² times that noise, so the peak/floor ratio is the height of the sensitivity peak. For actuator-side noise it is only proportional (an extra plant-gain factor) — which is exactly why the constant gets calibrated in step 3.

Spk ∝ ‖S‖∞  (= holds for output / sensor noise)
02

Peak sensitivity is closeness to −1

‖S‖∞ is the inverse of the closest the loop comes to the −1 instability point. The NCF margin is that distance, normalized — so the margin is inversely proportional to the peak.

‖S‖∞ = 1 / min|1+L|  ⟹  b ∝ 1 / Spk
03

Pin the constant on a healthy start

Assume the model is right at the beginning. Take the median Spk over the first 7.5 s as the reference and the model's binding-band margin bnom = 0.588. Healthy reads bnom; as the peak grows, b falls.

b(t) = bnom · Spk,ref / Spk(t)

Because the constant is calibrated, M7 is a relative reading — it needs the healthy reference. That is the price of being output-only. A companion method that estimates b straight from the closed-loop S and T reads it absolutely, but needs the band excited.

Why output-only

Where the FRF methods get fooled

FRF methods

Locked to a false alarm

Under a low-frequency input, the open-loop FRF dm/dc collapses to the tautology −1/C at 37 Hz — it reports a near-zero margin even when the loop is perfectly healthy.

Method 7

Reads the resonance directly

M7 ignores the input, so the lock can't fool it. It reads the 37 Hz resonance in dm — and is the only indicator that still detects when the input has no energy up there.

What it can miss

It only looks where you told it to

The peak band is a built-in assumption that the instability will appear near 37 Hz. Where it does, M7 is sharp and sensitive. But a marginality whose sensitivity peak lands outside that band is under-read or missed entirely — and that is not hypothetical. Verified on the real model:

scenariopeak ends up attrue margin bM7 reports
too much integral gain (low-PM mode)~20 Hz0.087 (near unstable)0.40 — misses the severity
phase-delay instabilitymigrates to ~4 Hzfallingblind (peak below the band)
gain-type damage (the design case)35 → 40 Hz, stays in band0.340.38 — handled
Gain damage keeps the peak inside the band, so M7 handles what it was built for. A phase-delay or integral-driven instability moves the peak out of the band, and the fixed reading says "safe."

Calibration & robustification

What you fit on the healthy phase

The reading is only as honest as its healthy reference. Four things are fit once, on the calibration window (the first seconds, assumed healthy); three guards keep a noisy spectrum from lying.

  1. bnom — the model's loop-only NCF over the band. The anchor the reading is rescaled from, taken from the known initial dynamics.
  2. Baseline floor — the off-resonance level. Use the spectrum's quiet shelf (where the healthy PSD sits in its lower 40%) as the denominator, so the input level cancels.
  3. Spk,ref — the healthy value of the peak statistic, as the median over many healthy windows (not one). Dividing it out removes the bias from picking the worst of many noisy bins.
  4. Band & line — scan only where the loop lives and excise the 58–62 Hz mains line; runs out to 100 Hz.
b(t) = bnom · Spk,ref / Spk(t)  healthy reads bnom; the peak grows, b falls

Robustness is three knobs, none tuned to the damage: averaging inside the window (more Welch segments lower per-bin variance), a 3-bin median on the spectrum before the peak (kills single-bin spikes without flattening a ~6-bin resonance), and the quiet-shelf floor.

Making it sharp

Measure the absolute peak, not the change

A first attempt scanned the spectrum relative to baseline, max|S/S0|², and read the real damage as a blunt 0.42 — the resonance moves and broadens, so the ratio dilutes. The fix: read the absolute sensitivity peak the way M7 does (peak over floor), but with the peak found anywhere in the band instead of a fixed window. With one steady input it is sharp again:

steady testtrue bsharp scan (peak anywhere)M7 (fixed band)
healthy0.5880.58 ±0.040.59 ±0.06
damage at 37 Hz0.3440.36 — sharp now0.37
low-PM mode at 20 Hz0.0870.16 — caught0.36 — misses
With one steady input the sharp scan matches M7 in-band and catches the out-of-band mode M7 misses. Then comes the catch.

The test battery

Five cases, one mechanism each

To score any indicator honestly you need cases that isolate distinct failure mechanisms. These five do — three keep the binding mode in M7's band, two migrate it out (one by integral action, one by pure phase lag with the gain unchanged). Every number below is from the verified battery (run_osi_test_cases.m); "detect" uses the relative alarm b < 0.8·b_nom ≈ 0.47.

case — what changesbinding fbandtrue bM7PP scanM9.1 (codex)
C1 healthy (reference)35 Hzin0.590.62 ok0.64 ok0.54 ok
C2 gain damage ×2.4 (measured 7000→5000)39 Hzin0.340.42 detect0.41 detect0.44 detect
C3 excess gain ×3.8 (extrapolated)36 Hzin0.250.31 detect0.32 detect0.37 detect
C4 integral action ×10 (I 20→200)20 HzOUT0.090.45 — blind (5× over-read)0.17 detect0.14 detect
C5 phase lag, +160-sample delay (gain unchanged)2.3 HzOUT0.310.67 miss0.56 miss0.52 miss
Output-only battery (healthy→case jump; each method calibrated on the healthy phase). C1–C3 (in band): all three handle them. C4: the mode jumps to 20 Hz — M7 reads a near-nominal 0.45 (effectively blind to the true 0.09); the scan and M9.1 catch it. C5: a phase-driven instability whose peak lands at 2.3 Hz, hidden under the 2.4 Hz structural mode — all three output-only methods miss it. Note M9.1 here runs on its residual branch only: with no command its phase-aware S,T branch is dormant, so it tracks the scan (catches C4, misses C5). Its S,T advantage shows when a command excites the band — see the 3-case study below, where M9.1 is the only method to detect all three. (C4–C5 are the "raise I" and "add phase delay" probes, made into fixed cases.)

The 3-case reality check

Under real input, the wide scan gets fooled

Run the healthy→damaged jump (at t = 10 s) under three input characters, 8 disturbance seeds each. M7's peak and floor bands are adjacent, so a broadband input cancels in the ratio. The wide scan takes the tallest peak anywhere — and under broadband input that peak is often input-driven, not the sensitivity resonance, so it locks onto the wrong thing and misses the damage. The seeds tell the story:

Strictly backward-looking. Every point uses only the 4 s of data ending at that time (timestamped at the window end, not its center), a trailing median, and a calibration fit on the pre-jump phase only — so nothing peeks ahead. Verified by a truncation test: zeroing all future samples leaves every past value unchanged to 0 (a centered median, by contrast, shifts them by ~4e-3 — and is not used).

high-band input — M7 detects, scan misses
low-band input — scan detects, M7 marginal
mixed input — M7 detects, scan misses
M7 mean scan mean M9.1 mean (codex) individual seeds (faint) damage @10s 0.40 alarm

The verdict. No output-only scan beat M7 across the three inputs — there is a real tension between looking everywhere (catches surprises, but a broadband input contaminates it) and input-robustness (M7's adjacent bands, which assume the location). So the honest moves, each verified, are:

instead of…do this (verified)
hard-coding 37 Hz ("cheating")place M7's band at the model-predicted instability frequency — the −180° crossing of C·H₀ — and re-derive it if the model updates
an output-only wide scan as primarykeep it only as a side watchdog for clean (internal-noise) excitation; it is contaminated by broadband input
guessing an unknown locationuse an input-aware, coherence-gated margin over a wide band — coherence is what rejects the input contamination

M9.1 is that fusion (the violet curve above, from the codex folder, run here unchanged). It uses the phase-aware closed-loop S, T branch on coherent excited bins and a coherence-gated residual-output branch where the band is quiet — and it is the only one that detects in all three cases: damaged 0.34 / 0.41 / 0.34 (true 0.344), and earliest (≈10.5 s high/mixed vs M7's 13.5 s). It is the input-aware, coherence-gated answer the table points to. (One caveat, unchanged from its source: M9.1 smooths with a centered 3-tap median, a mild ±0.5 s acausal step.)

Speed

Why it lags — and how to make it fast

M7's resolution comes from a multi-second PSD buffer, so the reading lags by roughly half the buffer, and the resonance itself takes a few cycles to build. That is a configuration choice, not a law.

Swap the block PSD for a recursive narrowband tracker — a bandpass at 37 Hz plus an exponential power estimate — and Spk updates every sample with ~0.2–0.5 s of memory instead of seconds. The real floor is physics: telling a 37 Hz tone from noise needs only a few of its cycles, about 0.1 s.