Files
lspower/LLM_readme.md
2026-05-14 08:38:16 +02:00

5.7 KiB

LsPower Session Notes

Current Goal

Build a Python tool to estimate electrical power delivered to a DIY active 2-way loudspeaker from measured DSP-output audio. Current measurement point is the analog DSP output / amplifier input, captured through a MOTU UltraLite input. Amplifier output is not measured directly yet; amplifier gain is modeled as an ideal scalar voltage gain in dB.

Project Structure

  • soundcard_input.py
    • Single runnable entry point.
    • Provides console voltage monitor, calibration routine, GUI monitor, and console power estimator mode.
  • lspower/audio.py
    • Shared sounddevice helpers.
    • SoundDeviceManager
    • QueuedInputStream
    • channel parsing/formatting helpers.
  • lspower/impedance.py
    • ImpedanceCurve
    • Loads REW impedance exports with columns: frequency Hz, impedance magnitude ohm, phase degrees.
    • Interpolates complex impedance to FFT bin frequencies.
  • lspower/power.py
    • VoltageSpectrumEstimator
    • PowerEstimator
    • WayConfig
    • PowerFrame
    • shared Hann/Welch voltage spectrum scaling helper.
  • impedance.txt
    • REW impedance export, currently usable directly.
  • BC 14NDL76.txt
    • Thiele-Small parameters for B&C 14NDL76. Useful later for safety/excursion/thermal modeling, not required for current power calculation.
  • dsp_power_estimator.py
    • Deleted as source. If an IDE tab still shows it, that is stale.

Calibration State

The project uses a path-specific voltage scale:

voltage = sounddevice_sample * volts_per_sample_unit

This is not a MOTU hardware full-scale spec. It includes the Windows/MME capture path, analog gain, routing, and any software gain.

Built-in reference:

DEFAULT_INPUT_CALIBRATION_REFERENCE_RMS_V = 2.32
DEFAULT_INPUT_CALIBRATION_REFERENCE_RMS_SAMPLE = 0.021384357
DEFAULT_INPUT_VOLTS_PER_SAMPLE_UNIT ~= 108.490522

Later calibration files are saved under:

calibrations/calibration_YYYYMMDD_HHMMSS.json

Startup behavior:

  • If --input-volts-per-sample-unit is provided, it is used.
  • Otherwise the newest calibration JSON is loaded.
  • If no calibration file exists, the built-in default is used.

CLI calibration example:

python soundcard_input.py --device 3 --channels 1 --calibrate-known-rms-v 2.32 --calibrate-only

GUI calibration saves the same JSON format.

GUI Current Behavior

Start with:

python soundcard_input.py --gui

Main sections:

  • Input setup and calibration.
  • Input-side metrics:
    • RMS voltage
    • Peak voltage
    • Crest factor
    • Crest dB
    • Digital peak
  • Estimated amplifier output power:
    • LF/HF enable
    • channel
    • impedance file
    • amp gain dB
    • Vdsp
    • Vamp
    • P
    • Q
    • Sapp
  • Input waveform plot:
    • calibrated voltage y-axis
    • manual Y limit field, blank means auto
  • Input PSD plot:
    • Hann/Welch scaling
    • dB V^2/Hz
  • Output power history plot:
    • P as solid line
    • Sapp as dashed line
    • moving window default 30 s, user-editable

Important GUI detail:

  • If LF and HF are both used, include both channels in Channels, e.g. 1,2.
  • Power estimation uses the same FFT size, overlap, and smoothing controls as the input PSD.

Power Estimation Math

For each way:

V_amp(f,t) = G_amp * V_DSP(f,t)
G_amp = 10^(G_amp_dB / 20)
Z(f) = |Z(f)| * exp(j * phase_Z(f))
S_k(t) = |V_amp,k(t)|^2 / conj(Z_k)
P_k(t) = real(S_k)
Q_k(t) = imag(S_k)
S_app,k(t) = |V_amp,k(t)|^2 / |Z_k|

Totals:

P_total = sum(P_k)
Q_total = sum(Q_k)
Sapp_total = sum(S_app,k)

FFT scaling:

  • DC removed.
  • Hann window.
  • rFFT.
  • Welch scaling to one-sided PSD in V^2/Hz.
  • Multiply by df to get bin-integrated RMS voltage squared in V^2.

Synthetic validation already performed:

1 Vrms sine into 8 ohm with 0 dB gain -> 0.125 W

Current Known Hardware Context

  • Audio interface: MOTU UltraLite.
  • Working input path used so far:
Device 3: Line In 3-4 (UltraLite-mk5) | MME | 2 in | 44100 Hz
  • WASAPI device 67 previously captured silence for this setup.
  • REW generator was used for calibration and signal testing.
  • At one point, generator was running at 50 Hz, 1 Vrms.

Commands

List devices:

python soundcard_input.py --list-devices

GUI:

python soundcard_input.py --gui

Console voltage monitor:

python soundcard_input.py --device 3 --channels 1 --input-volts-per-sample-unit 108.490522

Console power estimator:

python soundcard_input.py --device 3 --way LF:1:impedance.txt:34 --input-volts-per-sample-unit 108.490522

Verification Commands

.\.venv\Scripts\python.exe -m py_compile soundcard_input.py lspower\audio.py lspower\impedance.py lspower\power.py

Synthetic power scaling:

.\.venv\Scripts\python.exe -c "import numpy as np; from pathlib import Path; from lspower.impedance import ImpedanceCurve; from lspower.power import PowerEstimator, WayConfig; fs=8192; n=8192; t=np.arange(n)/fs; x=np.sqrt(2)*np.sin(2*np.pi*1000*t); z=ImpedanceCurve(np.array([20.,20000.]), np.array([8.,8.]), np.array([0.,0.])); way=WayConfig('test',0,Path('none'),0.0); frame=PowerEstimator(way,z,fs,n,1.0,0.0).estimate(x); print(frame.rms_input_v, frame.total_p_w, frame.total_s_va)"

Expected output approximately:

1.0 0.125 0.125

Likely Next Steps

  • Improve GUI visual polish further if needed.
  • Add file picker buttons for impedance files.
  • Add measured amplifier transfer function support H_amp(f).
  • Add driver safety panel:
    • compare P against Pmax
    • later estimate excursion against Xmax
  • Use T/S parameters from BC 14NDL76.txt only for model-based safety/excursion extensions, not for current impedance-based power estimation.