# 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: ```text 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: ```python 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: ```text 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: ```powershell 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: ```powershell 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: ```text 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: ```text 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: ```text 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: ```text 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: ```powershell python soundcard_input.py --list-devices ``` GUI: ```powershell python soundcard_input.py --gui ``` Console voltage monitor: ```powershell python soundcard_input.py --device 3 --channels 1 --input-volts-per-sample-unit 108.490522 ``` Console power estimator: ```powershell python soundcard_input.py --device 3 --way LF:1:impedance.txt:34 --input-volts-per-sample-unit 108.490522 ``` ## Verification Commands ```powershell .\.venv\Scripts\python.exe -m py_compile soundcard_input.py lspower\audio.py lspower\impedance.py lspower\power.py ``` Synthetic power scaling: ```powershell .\.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: ```text 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.