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_lines
withfrequency_hz, dBr1
known_house_lines
withfrequency_hz, dBr1
-
Save Spectrum PNG → writes to
oszi_csv/harmonics/spectrum_<CHAN>_<UTC>.png
with 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…N
at/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_HZ
against 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 possible
ON,Include DC
OFF. - 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-top
to stabilize amplitudes; checkdf_hz
in the table. - NORM too coarse. Use RAW if possible; NORM may be limited by
WAV_POINTS
in 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.