Harmonics & THD Tab — Operator Guide & Technical Notes
Purpose. This tab provides repeatable, lab-grade harmonic analysis of a selected channel without altering the oscilloscope’s measurement state. It fetches a waveform once, analyzes on full-resolution data for accuracy, and renders a spectrum with clear overlays for harmonics, interharmonics, and known (“house”) lines. All exports are silent and deterministic.

Quick start (operator)
- Select channel (CHAN1…CHAN4).
- Pick a window (Rect, Hann, Flat-top). Hann is a good default; Flat-top gives best amplitude accuracy when harmonics must be measured precisely.
- Set # Harmonics (e.g., 25).
- (Optional) Include DC if fundamental contains a DC component.
- Keep RAW if possible enabled for highest fidelity; the UI will decimate only for the live plot (analysis stays full-res).
- Press Measure (one shot) or Auto (updates ~every 2 s).
- Read harmonics in the table; use the spectrum overlays to spot non-harmonic spurs quickly.
- Save CSV or Save Spectrum PNG – files are written silently to
oszi_csv/harmonics/with UTC timestamps.
What you see
A) Spectrum plot (top)
- Live spectrum of the captured waveform (RMS per FFT bin). For display responsiveness, very long RAW captures are decimated only for plotting; the analysis and table use full data.
- Harmonic windows (shaded bands). ±tolerance around each
k·f₁. Anything inside is treated as a true harmonic; outside = candidate interharmonic. Tolerance =max(±1.5%·f₁, ±2 bins). - Interharmonics (orange, dotted line + ▼ marker). Peaks not aligned with integer harmonics above a prominence threshold (default: 6 dB relative to the fundamental in the display spectrum).
- Known/house lines (green ■). User-defined frequencies of interest (e.g., 50/100 Hz, 16.67 Hz rail) marked even if weak. Edit the list in
KNOWN_LINES_HZ. - Legend shows what’s drawn (auto-suppressed if nothing applies).
Status line (bottom of controls) summarizes: f₁, V₁,rms, THD, optional THD+N, number of cycles captured, capture mode (RAW/NORM), sample count / span, and if a display decimation was used.
B) Harmonics table (bottom)
Columns:
k— harmonic index.f_hz— measured harmonic frequency.f_pred— expected frequency,k·f₁.df_hz— deviationf_hz − f_pred(diagnoses bin drift / incoherent capture).mag_rms— RMS magnitude of the harmonic.dBr1— level relative to the fundamental:20·log10(Vₖ / V₁).percent— harmonic as % ofV₁.cumTHD_pct— cumulative THD up to thatk:100·sqrt(Σ₂…k Vₙ²)/V₁.phase_deg— phase of the harmonic (deg).
Below the table, a compact readout lists:
- Non-harmonic lines (dBr₁): top interharmonic peaks by prominence, relative to fundamental (display spectrum).
- Known/house lines: any from your list visible within ±2 bins, with their dBr₁.
Controls
- Channel — SCPI source (
:WAV:SOUR CHANx). - Window — Rect, Hann, Flat-top. (Internal map:
"Rect"→rect,"Hann"→hann,"Flat-top"→flattop.) - # Harmonics — how many harmonics to solve.
- Include DC — include k=0 in analysis (usually off for AC-only).
- RAW if possible — fetch 25M points from the scope memory when available; otherwise use NORM. RAW is stopped/resumed safely and VISA settings are restored.
- THD+N — include noise band in the “THD+N” figure (displayed in the status line when enabled).
- Persistence (heat-map) — leaves a fading trail of the last ~15 spectra (visual only). Clear trail removes it.
- Measure — one analysis cycle.
- Auto — toggles periodic measurement (~2 s cadence).
Exports
- Save CSV → writes to
oszi_csv/harmonics/harmonics_<CHAN>_<UTC>.csv. - Metadata header rows (
# channel,# window,# f1_hz,# v1_rms,# THD_pct,# THDN_pct). - Harmonic table (k, f_hz, f_pred, df_hz, mag_rms, dBr1, percent, cumTHD_pct, phase_deg).
-
Sections appended (if present):
non_harmonic_lineswithfrequency_hz, dBr1known_house_lineswithfrequency_hz, dBr1
-
Save Spectrum PNG → writes to
oszi_csv/harmonics/spectrum_<CHAN>_<UTC>.pngwith the current overlay. - Copy Markdown Summary → the same table and spur lists, ready for pasting into lab notes.
Files are saved silently (no dialogs) and paths are also written to the Debug Log.
Data integrity & safety
- Read-only SCPI. The tab configures
:WAV:*parameters and temporarily stops acquisition for RAW transfers, then restores the previous state. VISA timeout/chunk tweaks are also restored. - Full-resolution math. Harmonics/THD are computed on the full capture. Only the plot uses a decimated copy when needed for UI speed.
- No hidden scaling. All values are in SI units inherited from scope preamble; relative figures (dBr₁, %) are explicitly labeled.
- Deterministic exports. No interactive prompts; filenames include UTC to ease correlation with other logs.
Algorithms (concise)
Fundamental & harmonics
- Apply selected window; compute FFT; identify fundamental
f₁; evaluate harmonicsk=1…Nat/neark·f₁. Table uses full-resolution data.
THD / THD+N
- THD =
√(Σₖ≥2 Vₖ²) / V₁. - THD+N extends THD by including noise in a defined band (as implemented in code and toggled in UI). Displayed in the status line when enabled.
Interharmonics (non-harmonic lines)
- Compute relative spectrum
rel_db = 20·log10(|Y|/|Y(f₁)|)on the display spectrum (decimated if needed). - Find peaks by prominence ≥ 6 dB (tunable).
- Exclude peaks within harmonic windows:
±max(1.5%·f₁, 2 bins). - Sort by level and list top N. (Also marked on the plot.)
Known/house lines
- Check set
KNOWN_LINES_HZagainst the axis within ±2 bins; report any above a floor (−80 dBr₁). Also marked on the plot. - Edit the list in code (class attribute):
python KNOWN_LINES_HZ = [50.0, 100.0, 150.0, 200.0, 16.67, 20000.0, 25000.0, 30000.0, 40000.0, 45000.0]
Debug Log messages
Typical entries:
- Fetch:
⏬ RAW-local/CHAN3: 25,000,000 pts (23.8 MiB) in 4.16s → 5.74 MiB/s - Analysis summary:
📈 Analysis | ch=CHAN1 | window=Hann | bins=524288, df=9.54 Hz | f1=1.000 kHz, V1rms=1.234 | THD=1.234% | harmonic tol=±15.3 Hz - Interharmonics:
🔶 Interharmonics: 15.0 kHz (−18.4 dBr₁), 25.0 kHz (−21.3 dBr₁) - Known lines:
🟩 Known/house lines: 50.0 Hz (−72.0 dBr₁) - Saves:
💾 Saved Harmonics CSV → oszi_csv/harmonics/harmonics_CHAN1_YYYY-MM-DDTHH-MM-SSZ.csv
🖼️ Saved Spectrum PNG → oszi_csv/harmonics/spectrum_CHAN1_YYYY-MM-DDTHH-MM-SSZ.png
Recommended settings
- General AC:
Hann, 25–40 harmonics,RAW if possibleON,Include DCOFF. - Amplitude-critical:
Flat-top, increase #harmonics so skirts don’t leak into neighbors. - Mains analysis: add 50/100/150… Hz to known lines and keep the default interharmonic threshold (6 dBr₁) for a balanced view.
Troubleshooting
- “No data to export yet.” Run one Measure first; the latest result is stored after each analysis.
- Few or no interharmonics listed. Lower the display prominence threshold in the call:
self._update_interharmonics_readout(res, min_prom_db=6.0)→ try4.0. (Only affects the readout/overlay.) - Harmonics don’t align with bin centers. Increase time span (more cycles) or use
Flat-topto stabilize amplitudes; checkdf_hzin the table. - NORM too coarse. Use RAW if possible; NORM may be limited by
WAV_POINTSin your configuration.
Change log (feature highlights)
- Interharmonic detection with plotted overlays & readout (prominence-based).
- Known/house line markers and list.
- Extended table:
f_pred,df_hz,dBr1,cumTHD_pct. - Silent exports to
oszi_csv/harmonics/(CSV/PNG) + “Copy Markdown Summary.” - Persistence trail for spectra.
- Rich debug logging for traceability.
Appendix — Equations
- THD
\( \mathrm{THD} = \frac{\sqrt{\sum_{k=2}^{N} V_k^2}}{V_1} \) - dBr₁
\( \mathrm{dBr}_1(k) = 20\log_{10}\!\left(\frac{V_k}{V_1}\right) \) - Cumulative THD% up to k
\( \mathrm{cumTHD\%}(k) = 100 \cdot \frac{\sqrt{\sum_{n=2}^{k} V_n^2}}{V_1} \)
All values derive from the scope preamble scaling (SI units). Only the plot may use display-decimated data; the table and CSV are driven by the full-resolution analysis.
Version: v0.9.8h-testing • Folder: oszi_csv/harmonics/ • Scope control: read-only waveform access with state restore.