Skip to content

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)

  1. Select channel (CHAN1…CHAN4).
  2. Pick a window (Rect, Hann, Flat-top). Hann is a good default; Flat-top gives best amplitude accuracy when harmonics must be measured precisely.
  3. Set # Harmonics (e.g., 25).
  4. (Optional) Include DC if fundamental contains a DC component.
  5. Keep RAW if possible enabled for highest fidelity; the UI will decimate only for the live plot (analysis stays full-res).
  6. Press Measure (one shot) or Auto (updates ~every 2 s).
  7. Read harmonics in the table; use the spectrum overlays to spot non-harmonic spurs quickly.
  8. 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 — deviation f_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 % of V₁.
  • cumTHD_pct — cumulative THD up to that k: 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 with frequency_hz, dBr1
    • known_house_lines with frequency_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 harmonics k=1…N at/near k·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)

  1. Compute relative spectrum rel_db = 20·log10(|Y|/|Y(f₁)|) on the display spectrum (decimated if needed).
  2. Find peaks by prominence ≥ 6 dB (tunable).
  3. Exclude peaks within harmonic windows: ±max(1.5%·f₁, 2 bins).
  4. 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

  • 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) → try 4.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; check df_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.