implement new monitoring values based on max_power limits from winISD
This commit is contained in:
@@ -25,6 +25,7 @@ from lspower.audio import (
|
||||
)
|
||||
from lspower.impedance import ImpedanceCurve
|
||||
from lspower.power import PowerEstimator, PowerFrame, VoltageSpectrumEstimator, VoltageSpectrumFrame, WayConfig
|
||||
from lspower.winisd import WinISDLimitCurve, check_excursion_scaling, headroom_status, run_self_test
|
||||
|
||||
np = None
|
||||
|
||||
@@ -45,6 +46,12 @@ DEFAULT_POWER_UPDATE_INTERVAL_S = 0.25
|
||||
DEFAULT_AMP_GAIN_DB = 32.0
|
||||
DEFAULT_POWER_WARNING_W = 400.0
|
||||
DEFAULT_POWER_CRITICAL_W = 550.0
|
||||
WINISD_MAX_POWER_FILE = Path("winisd/max_power.csv")
|
||||
WINISD_EXCURSION_10W_FILE = Path("winisd/excursion_10w.csv")
|
||||
WINISD_EXCURSION_100W_FILE = Path("winisd/excursion_100w.csv")
|
||||
ENABLE_WINISD_HEADROOM = True
|
||||
HEADROOM_EPS_W = 1e-12
|
||||
HEADROOM_SMOOTHING_ALPHA = 0.9
|
||||
|
||||
# Convert sounddevice's normalized floating-point samples to volts.
|
||||
#
|
||||
@@ -252,6 +259,21 @@ def format_levels(
|
||||
return " | ".join(parts)
|
||||
|
||||
|
||||
def smooth_headroom_db(previous: float | None, raw: float, alpha: float) -> float:
|
||||
if previous is None or not math.isfinite(previous) or not math.isfinite(raw) or alpha <= 0.0:
|
||||
return raw
|
||||
return alpha * previous + (1.0 - alpha) * raw
|
||||
|
||||
|
||||
def format_headroom(headroom: dict | None) -> str:
|
||||
if not headroom:
|
||||
return "WinISD headroom disabled"
|
||||
return (
|
||||
f"WinISD headroom={headroom['worst_headroom_db']:+6.2f} dB "
|
||||
f"@ {headroom['critical_frequency_hz']:7.1f} Hz | {headroom['status']}"
|
||||
)
|
||||
|
||||
|
||||
class PowerConsoleRunner:
|
||||
"""Live console runner for estimated amplifier output power."""
|
||||
|
||||
@@ -276,6 +298,8 @@ class PowerConsoleRunner:
|
||||
)
|
||||
self.estimators: dict[str, PowerEstimator] = {}
|
||||
self.buffers: dict[str, object] = {}
|
||||
self.headroom_curves: dict[str, WinISDLimitCurve] = {}
|
||||
self.headroom_smoothed_db: dict[str, float] = {}
|
||||
self.hop_size = 0
|
||||
|
||||
@staticmethod
|
||||
@@ -379,10 +403,30 @@ class PowerConsoleRunner:
|
||||
way.name: np.zeros(0, dtype=np.float64)
|
||||
for way in self.ways
|
||||
}
|
||||
self._create_headroom_curves()
|
||||
|
||||
@staticmethod
|
||||
def format_power_line(name: str, frame: PowerFrame) -> str:
|
||||
return (
|
||||
def _create_headroom_curves(self) -> None:
|
||||
self.headroom_curves = {}
|
||||
self.headroom_smoothed_db = {}
|
||||
if self.args.disable_winisd_headroom:
|
||||
return
|
||||
|
||||
path = Path(self.args.winisd_max_power)
|
||||
lf_ways = [way for way in self.ways if way.name.upper() == "LF"]
|
||||
if not lf_ways:
|
||||
return
|
||||
try:
|
||||
curve = WinISDLimitCurve.from_file(path, eps_w=HEADROOM_EPS_W)
|
||||
except Exception as exc:
|
||||
print(f"WinISD headroom disabled: {exc}", file=sys.stderr)
|
||||
return
|
||||
|
||||
for way in lf_ways:
|
||||
self.headroom_curves[way.name] = curve
|
||||
|
||||
def format_power_line(self, name: str, frame: PowerFrame) -> str:
|
||||
headroom = self._compute_headroom(name, frame)
|
||||
line = (
|
||||
f"{name}: "
|
||||
f"Vdsp={frame.rms_input_v:9.4f} Vrms, "
|
||||
f"Vamp={frame.rms_amp_v:9.2f} Vrms, "
|
||||
@@ -390,6 +434,26 @@ class PowerConsoleRunner:
|
||||
f"Q={frame.total_q_var:10.2f} var, "
|
||||
f"Sapp={frame.total_s_va:10.2f} VA"
|
||||
)
|
||||
if headroom:
|
||||
line += ", " + format_headroom(headroom)
|
||||
return line
|
||||
|
||||
def _compute_headroom(self, name: str, frame: PowerFrame) -> dict | None:
|
||||
curve = self.headroom_curves.get(name)
|
||||
if curve is None:
|
||||
return None
|
||||
headroom = curve.compute_headroom(frame.p_w_per_bin, frame.frequencies_hz)
|
||||
raw = float(headroom["worst_headroom_db"])
|
||||
smoothed = smooth_headroom_db(
|
||||
self.headroom_smoothed_db.get(name),
|
||||
raw,
|
||||
self.args.headroom_smoothing_alpha,
|
||||
)
|
||||
self.headroom_smoothed_db[name] = smoothed
|
||||
headroom["worst_headroom_db"] = smoothed
|
||||
headroom["status"] = headroom_status(smoothed)
|
||||
headroom["over_limit_bool"] = bool(math.isfinite(smoothed) and smoothed <= 0.0)
|
||||
return headroom
|
||||
|
||||
def run(self) -> int:
|
||||
try:
|
||||
@@ -410,6 +474,15 @@ class PowerConsoleRunner:
|
||||
)
|
||||
print(f"Input voltage scale: {self.args.input_volts_per_sample_unit:g} V / sample unit")
|
||||
print("Assumption: ideal linear amplifier voltage gain per way.")
|
||||
if self.headroom_curves:
|
||||
print(f"WinISD headroom: {Path(self.args.winisd_max_power)}")
|
||||
if WINISD_EXCURSION_10W_FILE.exists() and WINISD_EXCURSION_100W_FILE.exists():
|
||||
try:
|
||||
print(check_excursion_scaling(WINISD_EXCURSION_10W_FILE, WINISD_EXCURSION_100W_FILE).message)
|
||||
except Exception as exc:
|
||||
print(f"Excursion check skipped: {exc}")
|
||||
else:
|
||||
print("WinISD headroom: disabled")
|
||||
for way in self.ways:
|
||||
curve = self.impedances[way.name]
|
||||
print(
|
||||
@@ -617,6 +690,8 @@ class LevelMonitorGUI:
|
||||
self.power_rows: dict[str, dict] = {}
|
||||
self.power_history: dict[str, list[tuple[float, float, float]]] = {}
|
||||
self.power_max_hold_w: dict[str, float] = {}
|
||||
self.headroom_curves: dict[str, WinISDLimitCurve] = {}
|
||||
self.headroom_smoothed_db: dict[str, float] = {}
|
||||
self.power_history_start_s: float | None = None
|
||||
self.power_hop_size = 0
|
||||
self.level_rows: list[dict] = []
|
||||
@@ -646,6 +721,10 @@ class LevelMonitorGUI:
|
||||
self.power_fft_size_var = tk.StringVar(value=str(DEFAULT_POWER_FFT_SIZE))
|
||||
self.power_overlap_var = tk.StringVar(value=f"{DEFAULT_POWER_OVERLAP:g}")
|
||||
self.power_smoothing_var = tk.StringVar(value=f"{DEFAULT_POWER_SMOOTHING_ALPHA:g}")
|
||||
self.winisd_enabled_var = tk.BooleanVar(value=not args.disable_winisd_headroom and ENABLE_WINISD_HEADROOM)
|
||||
self.winisd_max_power_var = tk.StringVar(value=str(args.winisd_max_power or WINISD_MAX_POWER_FILE))
|
||||
self.headroom_smoothing_var = tk.StringVar(value=f"{args.headroom_smoothing_alpha:g}")
|
||||
self.winisd_status_var = tk.StringVar(value="WinISD headroom uses per-bin P/Pmax for LF.")
|
||||
self.lf_enabled_var = tk.BooleanVar(value=True)
|
||||
self.lf_channel_var = tk.StringVar(value="1")
|
||||
self.lf_impedance_var = tk.StringVar(value="impedance.txt" if Path("impedance.txt").exists() else "")
|
||||
@@ -776,19 +855,49 @@ class LevelMonitorGUI:
|
||||
ttk.Entry(self.power_frame, textvariable=self.power_smoothing_var, width=8).grid(
|
||||
row=1, column=5, sticky="w", padx=(8, 16)
|
||||
)
|
||||
ttk.Checkbutton(self.power_frame, text="WinISD", variable=self.winisd_enabled_var).grid(
|
||||
row=2, column=0, sticky="w", pady=(8, 0)
|
||||
)
|
||||
ttk.Entry(self.power_frame, textvariable=self.winisd_max_power_var, width=44).grid(
|
||||
row=2, column=1, columnspan=3, sticky="ew", padx=(8, 12), pady=(8, 0)
|
||||
)
|
||||
ttk.Label(self.power_frame, text="Headroom smooth").grid(row=2, column=4, sticky="e", pady=(8, 0))
|
||||
ttk.Entry(self.power_frame, textvariable=self.headroom_smoothing_var, width=8).grid(
|
||||
row=2, column=5, sticky="w", padx=(8, 16), pady=(8, 0)
|
||||
)
|
||||
ttk.Label(self.power_frame, textvariable=self.winisd_status_var).grid(
|
||||
row=2, column=6, columnspan=9, sticky="w", pady=(8, 0)
|
||||
)
|
||||
|
||||
headings = ("Way", "On", "Ch", "Impedance file", "Gain dB", "Vdsp", "Vamp", "P", "Q", "Sapp", "P meter", "", "Max P")
|
||||
headings = (
|
||||
"Way",
|
||||
"On",
|
||||
"Ch",
|
||||
"Impedance file",
|
||||
"Gain dB",
|
||||
"Vdsp",
|
||||
"Vamp",
|
||||
"P",
|
||||
"Q",
|
||||
"Sapp",
|
||||
"Headroom",
|
||||
"Crit Hz",
|
||||
"Limit",
|
||||
"P meter",
|
||||
"",
|
||||
"Max P",
|
||||
)
|
||||
for col, heading in enumerate(headings):
|
||||
ttk.Label(self.power_frame, text=heading).grid(
|
||||
row=2,
|
||||
row=3,
|
||||
column=col,
|
||||
sticky="w" if col <= 4 else "e",
|
||||
pady=(8, 0),
|
||||
padx=(0, 12 if col < 9 else 0),
|
||||
)
|
||||
|
||||
self._build_power_config_row("LF", 3, self.lf_enabled_var, self.lf_channel_var, self.lf_impedance_var, self.lf_gain_var)
|
||||
self._build_power_config_row("HF", 4, self.hf_enabled_var, self.hf_channel_var, self.hf_impedance_var, self.hf_gain_var)
|
||||
self._build_power_config_row("LF", 4, self.lf_enabled_var, self.lf_channel_var, self.lf_impedance_var, self.lf_gain_var)
|
||||
self._build_power_config_row("HF", 5, self.hf_enabled_var, self.hf_channel_var, self.hf_impedance_var, self.hf_gain_var)
|
||||
|
||||
plots_frame = ttk.Frame(outer)
|
||||
plots_frame.pack(fill="both", expand=True, pady=(12, 0))
|
||||
@@ -892,9 +1001,12 @@ class LevelMonitorGUI:
|
||||
"p": self.tk.StringVar(value="-"),
|
||||
"q": self.tk.StringVar(value="-"),
|
||||
"s": self.tk.StringVar(value="-"),
|
||||
"headroom": self.tk.StringVar(value="-"),
|
||||
"crit_hz": self.tk.StringVar(value="-"),
|
||||
"limit": self.tk.StringVar(value="-"),
|
||||
"max_p": self.tk.StringVar(value="-"),
|
||||
}
|
||||
for offset, key in enumerate(("vdsp", "vamp", "p", "q", "s"), start=5):
|
||||
for offset, key in enumerate(("vdsp", "vamp", "p", "q", "s", "headroom", "crit_hz", "limit"), start=5):
|
||||
ttk.Label(self.power_frame, textvariable=vars_by_metric[key], width=12, anchor="e").grid(
|
||||
row=row, column=offset, sticky="e", padx=(0, 12 if offset < 9 else 0), pady=4
|
||||
)
|
||||
@@ -906,12 +1018,12 @@ class LevelMonitorGUI:
|
||||
style="PowerOk.Horizontal.TProgressbar",
|
||||
length=150,
|
||||
)
|
||||
meter.grid(row=row, column=10, sticky="ew", padx=(8, 8), pady=4)
|
||||
meter.grid(row=row, column=13, sticky="ew", padx=(8, 8), pady=4)
|
||||
light = self.tk.Canvas(self.power_frame, width=18, height=18, highlightthickness=0)
|
||||
light.grid(row=row, column=11, sticky="w", padx=(0, 6), pady=4)
|
||||
light.grid(row=row, column=14, sticky="w", padx=(0, 6), pady=4)
|
||||
light_id = light.create_oval(3, 3, 15, 15, fill="#22a447", outline="#555555")
|
||||
ttk.Label(self.power_frame, textvariable=vars_by_metric["max_p"], width=12, anchor="e").grid(
|
||||
row=row, column=12, sticky="e", pady=4
|
||||
row=row, column=15, sticky="e", pady=4
|
||||
)
|
||||
vars_by_metric["meter"] = meter
|
||||
vars_by_metric["light"] = light
|
||||
@@ -972,12 +1084,15 @@ class LevelMonitorGUI:
|
||||
fft_size = int(self.power_fft_size_var.get())
|
||||
overlap = float(self.power_overlap_var.get())
|
||||
smoothing = float(self.power_smoothing_var.get())
|
||||
headroom_smoothing = float(self.headroom_smoothing_var.get())
|
||||
if fft_size <= 0 or fft_size % 2:
|
||||
raise ValueError("power FFT size must be a positive even number")
|
||||
if not 0.0 <= overlap < 1.0:
|
||||
raise ValueError("power overlap must be >= 0 and < 1")
|
||||
if not 0.0 <= smoothing < 1.0:
|
||||
raise ValueError("power smoothing must be >= 0 and < 1")
|
||||
if not 0.0 <= headroom_smoothing < 1.0:
|
||||
raise ValueError("headroom smoothing must be >= 0 and < 1")
|
||||
|
||||
hop_size = int(round(fft_size * (1.0 - overlap)))
|
||||
if hop_size <= 0:
|
||||
@@ -1011,9 +1126,10 @@ class LevelMonitorGUI:
|
||||
names = {way.name for way in ways}
|
||||
for name, row in self.power_rows.items():
|
||||
if name not in names:
|
||||
for key in ("vdsp", "vamp", "p", "q", "s", "max_p"):
|
||||
for key in ("vdsp", "vamp", "p", "q", "s", "headroom", "crit_hz", "limit", "max_p"):
|
||||
row[key].set("-")
|
||||
row["meter"]["value"] = 0.0
|
||||
row["meter"]["maximum"] = DEFAULT_POWER_CRITICAL_W
|
||||
row["meter"].configure(style="PowerOk.Horizontal.TProgressbar")
|
||||
row["light"].itemconfigure(row["light_id"], fill="#22a447")
|
||||
|
||||
@@ -1021,6 +1137,8 @@ class LevelMonitorGUI:
|
||||
self.power_buffers = {}
|
||||
self.power_history = {}
|
||||
self.power_max_hold_w = {}
|
||||
self.headroom_curves = {}
|
||||
self.headroom_smoothed_db = {}
|
||||
self.power_history_start_s = None
|
||||
self.power_hop_size = hop_size
|
||||
self.input_spectrum_estimators = {}
|
||||
@@ -1050,6 +1168,42 @@ class LevelMonitorGUI:
|
||||
self.power_history[way.name] = []
|
||||
self.power_max_hold_w[way.name] = 0.0
|
||||
|
||||
self._load_gui_winisd_curves()
|
||||
|
||||
def _load_gui_winisd_curves(self) -> None:
|
||||
self.headroom_curves = {}
|
||||
self.headroom_smoothed_db = {}
|
||||
if not self.winisd_enabled_var.get():
|
||||
self.winisd_status_var.set("WinISD headroom disabled.")
|
||||
return
|
||||
|
||||
lf_estimator = self.power_estimators.get("LF")
|
||||
if lf_estimator is None:
|
||||
self.winisd_status_var.set("WinISD headroom disabled: LF way is not active.")
|
||||
return
|
||||
|
||||
path = Path(self.winisd_max_power_var.get().strip() or WINISD_MAX_POWER_FILE)
|
||||
try:
|
||||
curve = WinISDLimitCurve.from_file(path, eps_w=HEADROOM_EPS_W)
|
||||
except Exception as exc:
|
||||
self.winisd_status_var.set(f"WinISD headroom disabled: {exc}")
|
||||
return
|
||||
|
||||
self.headroom_curves["LF"] = curve
|
||||
status = (
|
||||
f"WinISD headroom active: {path}, "
|
||||
f"{curve.frequency_hz[0]:.1f}-{curve.frequency_hz[-1]:.1f} Hz."
|
||||
)
|
||||
excursion_10w = WINISD_EXCURSION_10W_FILE
|
||||
excursion_100w = WINISD_EXCURSION_100W_FILE
|
||||
if excursion_10w.exists() and excursion_100w.exists():
|
||||
try:
|
||||
check = check_excursion_scaling(excursion_10w, excursion_100w)
|
||||
status += " " + check.message
|
||||
except Exception as exc:
|
||||
status += f" Excursion check skipped: {exc}"
|
||||
self.winisd_status_var.set(status)
|
||||
|
||||
def _process_input_spectrum_block(self, block) -> None:
|
||||
if np is None or not self.input_spectrum_estimators:
|
||||
return
|
||||
@@ -1109,29 +1263,73 @@ class LevelMonitorGUI:
|
||||
row["p"].set(f"{latest.total_p_w:.1f} W")
|
||||
row["q"].set(f"{latest.total_q_var:.1f} var")
|
||||
row["s"].set(f"{latest.total_s_va:.1f} VA")
|
||||
self._update_power_meter(name, latest.total_p_w)
|
||||
headroom = self._compute_gui_headroom(name, latest)
|
||||
if headroom is None:
|
||||
row["headroom"].set("-")
|
||||
row["crit_hz"].set("-")
|
||||
row["limit"].set("-")
|
||||
else:
|
||||
row["headroom"].set(f"{headroom['worst_headroom_db']:+.1f} dB")
|
||||
row["crit_hz"].set(f"{headroom['critical_frequency_hz']:.1f}")
|
||||
row["limit"].set(headroom["status"])
|
||||
self._update_power_meter(name, latest.total_p_w, headroom)
|
||||
self._append_power_history(name, latest)
|
||||
|
||||
self.draw_power_history()
|
||||
|
||||
def _update_power_meter(self, name: str, power_w: float) -> None:
|
||||
def _compute_gui_headroom(self, name: str, frame: PowerFrame) -> dict | None:
|
||||
curve = self.headroom_curves.get(name)
|
||||
if curve is None:
|
||||
return None
|
||||
headroom = curve.compute_headroom(frame.p_w_per_bin, frame.frequencies_hz)
|
||||
raw = float(headroom["worst_headroom_db"])
|
||||
try:
|
||||
alpha = float(self.headroom_smoothing_var.get())
|
||||
except ValueError:
|
||||
alpha = HEADROOM_SMOOTHING_ALPHA
|
||||
alpha = min(max(alpha, 0.0), 0.999999)
|
||||
smoothed = smooth_headroom_db(self.headroom_smoothed_db.get(name), raw, alpha)
|
||||
self.headroom_smoothed_db[name] = smoothed
|
||||
headroom["worst_headroom_db"] = smoothed
|
||||
headroom["status"] = headroom_status(smoothed)
|
||||
headroom["over_limit_bool"] = bool(math.isfinite(smoothed) and smoothed <= 0.0)
|
||||
return headroom
|
||||
|
||||
def _update_power_meter(self, name: str, power_w: float, headroom: dict | None = None) -> None:
|
||||
row = self.power_rows.get(name)
|
||||
if row is None:
|
||||
return
|
||||
|
||||
max_hold = max(self.power_max_hold_w.get(name, 0.0), float(power_w))
|
||||
self.power_max_hold_w[name] = max_hold
|
||||
meter_value = min(max(float(power_w), 0.0), DEFAULT_POWER_CRITICAL_W)
|
||||
|
||||
if power_w >= DEFAULT_POWER_CRITICAL_W:
|
||||
color = "#d62728"
|
||||
style = "PowerCritical.Horizontal.TProgressbar"
|
||||
elif power_w >= DEFAULT_POWER_WARNING_W:
|
||||
color = "#e6a400"
|
||||
style = "PowerWarn.Horizontal.TProgressbar"
|
||||
if headroom is not None and math.isfinite(float(headroom["critical_pmax_w"])):
|
||||
critical_pmax = max(float(headroom["critical_pmax_w"]), HEADROOM_EPS_W)
|
||||
critical_power = max(float(headroom["critical_power_w"]), 0.0)
|
||||
meter_value = min((critical_power / critical_pmax) * 100.0, 100.0)
|
||||
row["meter"]["maximum"] = 100.0
|
||||
status = headroom["status"]
|
||||
if status == "LIMIT EXCEEDED":
|
||||
color = "#d62728"
|
||||
style = "PowerCritical.Horizontal.TProgressbar"
|
||||
elif status == "WARNING":
|
||||
color = "#e6a400"
|
||||
style = "PowerWarn.Horizontal.TProgressbar"
|
||||
else:
|
||||
color = "#22a447"
|
||||
style = "PowerOk.Horizontal.TProgressbar"
|
||||
else:
|
||||
color = "#22a447"
|
||||
style = "PowerOk.Horizontal.TProgressbar"
|
||||
meter_value = min(max(float(power_w), 0.0), DEFAULT_POWER_CRITICAL_W)
|
||||
row["meter"]["maximum"] = DEFAULT_POWER_CRITICAL_W
|
||||
if power_w >= DEFAULT_POWER_CRITICAL_W:
|
||||
color = "#d62728"
|
||||
style = "PowerCritical.Horizontal.TProgressbar"
|
||||
elif power_w >= DEFAULT_POWER_WARNING_W:
|
||||
color = "#e6a400"
|
||||
style = "PowerWarn.Horizontal.TProgressbar"
|
||||
else:
|
||||
color = "#22a447"
|
||||
style = "PowerOk.Horizontal.TProgressbar"
|
||||
|
||||
row["meter"]["value"] = meter_value
|
||||
row["meter"].configure(style=style)
|
||||
@@ -1505,15 +1703,18 @@ class LevelMonitorGUI:
|
||||
self.power_buffers = {}
|
||||
self.power_history = {}
|
||||
self.power_max_hold_w = {}
|
||||
self.headroom_curves = {}
|
||||
self.headroom_smoothed_db = {}
|
||||
self.power_history_start_s = None
|
||||
self.power_hop_size = 0
|
||||
for row in self.input_metric_rows.values():
|
||||
for value_var in row.values():
|
||||
value_var.set("-")
|
||||
for row in self.power_rows.values():
|
||||
for key in ("vdsp", "vamp", "p", "q", "s", "max_p"):
|
||||
for key in ("vdsp", "vamp", "p", "q", "s", "headroom", "crit_hz", "limit", "max_p"):
|
||||
row[key].set("-")
|
||||
row["meter"]["value"] = 0.0
|
||||
row["meter"]["maximum"] = DEFAULT_POWER_CRITICAL_W
|
||||
row["meter"].configure(style="PowerOk.Horizontal.TProgressbar")
|
||||
row["light"].itemconfigure(row["light_id"], fill="#22a447")
|
||||
self.draw_spectrum_grid()
|
||||
@@ -1830,6 +2031,27 @@ def build_arg_parser() -> argparse.ArgumentParser:
|
||||
default=DEFAULT_POWER_UPDATE_INTERVAL_S,
|
||||
help=f"Power estimator console update interval. Default: {DEFAULT_POWER_UPDATE_INTERVAL_S:g}.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--winisd-max-power",
|
||||
default=str(WINISD_MAX_POWER_FILE),
|
||||
help=f"WinISD frequency-dependent max-power CSV for LF headroom. Default: {WINISD_MAX_POWER_FILE}.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--disable-winisd-headroom",
|
||||
action="store_true",
|
||||
help="Disable WinISD max-power headroom monitoring.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--headroom-smoothing-alpha",
|
||||
type=float,
|
||||
default=HEADROOM_SMOOTHING_ALPHA,
|
||||
help=f"WinISD headroom smoothing alpha. Default: {HEADROOM_SMOOTHING_ALPHA:g}.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--self-test-winisd",
|
||||
action="store_true",
|
||||
help="Run synthetic WinISD headroom tests and exit.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--list-devices",
|
||||
action="store_true",
|
||||
@@ -1869,9 +2091,21 @@ def main(argv: list[str] | None = None) -> int:
|
||||
parser.error("--power-overlap must be >= 0 and < 1")
|
||||
if not 0.0 <= args.power_smoothing_alpha < 1.0:
|
||||
parser.error("--power-smoothing-alpha must be >= 0 and < 1")
|
||||
if not 0.0 <= args.headroom_smoothing_alpha < 1.0:
|
||||
parser.error("--headroom-smoothing-alpha must be >= 0 and < 1")
|
||||
if args.samplerate is not None and args.samplerate <= 0:
|
||||
parser.error("--samplerate must be positive")
|
||||
|
||||
if args.self_test_winisd:
|
||||
run_self_test()
|
||||
for path in (Path(args.winisd_max_power), WINISD_EXCURSION_10W_FILE, WINISD_EXCURSION_100W_FILE):
|
||||
if path.exists():
|
||||
WinISDLimitCurve.from_file(path, eps_w=HEADROOM_EPS_W)
|
||||
if WINISD_EXCURSION_10W_FILE.exists() and WINISD_EXCURSION_100W_FILE.exists():
|
||||
print(check_excursion_scaling(WINISD_EXCURSION_10W_FILE, WINISD_EXCURSION_100W_FILE).message)
|
||||
print("WinISD self-test OK")
|
||||
return 0
|
||||
|
||||
sd = load_dependencies()
|
||||
|
||||
if args.list_devices:
|
||||
|
||||
Reference in New Issue
Block a user