diff --git a/lspower/winisd.py b/lspower/winisd.py new file mode 100644 index 0000000..7b620c0 --- /dev/null +++ b/lspower/winisd.py @@ -0,0 +1,244 @@ +from __future__ import annotations + +import math +import re +from dataclasses import dataclass +from pathlib import Path + +import numpy as np + + +HEADROOM_OK_DB = 6.0 +HEADROOM_LIMIT_DB = 0.0 +DEFAULT_HEADROOM_EPS_W = 1e-12 + + +@dataclass(frozen=True) +class ExcursionSanityResult: + ok: bool + median_ratio: float + expected_ratio: float + relative_error: float + message: str + + +def headroom_status(headroom_db: float) -> str: + if not math.isfinite(headroom_db): + return "DISABLED" + if headroom_db <= HEADROOM_LIMIT_DB: + return "LIMIT EXCEEDED" + if headroom_db <= HEADROOM_OK_DB: + return "WARNING" + return "OK" + + +class WinISDLimitCurve: + """Frequency-dependent WinISD maximum-power protection curve.""" + + def __init__( + self, + frequency_hz: np.ndarray, + pmax_w: np.ndarray, + source: Path | None = None, + eps_w: float = DEFAULT_HEADROOM_EPS_W, + ): + if frequency_hz.ndim != 1 or pmax_w.ndim != 1: + raise ValueError("WinISD limit arrays must be one-dimensional") + if len(frequency_hz) != len(pmax_w): + raise ValueError("WinISD limit arrays must have the same length") + if len(frequency_hz) < 2: + raise ValueError("WinISD limit curve needs at least two points") + if eps_w <= 0.0: + raise ValueError("eps_w must be positive") + + valid = np.isfinite(frequency_hz) & np.isfinite(pmax_w) + valid &= frequency_hz > 0.0 + valid &= pmax_w > eps_w + if np.count_nonzero(valid) < 2: + raise ValueError("WinISD limit curve has fewer than two valid points") + + frequency_hz = frequency_hz[valid].astype(np.float64) + pmax_w = pmax_w[valid].astype(np.float64) + order = np.argsort(frequency_hz) + frequency_hz = frequency_hz[order] + pmax_w = pmax_w[order] + + unique_frequency, unique_indices = np.unique(frequency_hz, return_index=True) + self.frequency_hz = unique_frequency + self.pmax_w = pmax_w[unique_indices] + self.source = source + self.eps_w = float(eps_w) + + @classmethod + def from_file(cls, path: str | Path, eps_w: float = DEFAULT_HEADROOM_EPS_W) -> "WinISDLimitCurve": + path = Path(path) + frequency_hz, values = load_two_column_curve(path) + return cls(frequency_hz, values, source=path, eps_w=eps_w) + + def interpolate(self, frequencies_hz: np.ndarray) -> np.ndarray: + frequencies_hz = np.asarray(frequencies_hz, dtype=np.float64) + pmax = np.full_like(frequencies_hz, np.nan, dtype=np.float64) + valid = np.isfinite(frequencies_hz) & (frequencies_hz > 0.0) + if not np.any(valid): + return pmax + + interp_f = np.clip(frequencies_hz[valid], self.frequency_hz[0], self.frequency_hz[-1]) + pmax[valid] = np.interp( + np.log(interp_f), + np.log(self.frequency_hz), + self.pmax_w, + left=self.pmax_w[0], + right=self.pmax_w[-1], + ) + return pmax + + def compute_headroom(self, p_bin: np.ndarray, f_bins: np.ndarray) -> dict: + p_bin = np.asarray(p_bin, dtype=np.float64) + f_bins = np.asarray(f_bins, dtype=np.float64) + if p_bin.shape != f_bins.shape: + raise ValueError("p_bin and f_bins must have the same shape") + + pmax = self.interpolate(f_bins) + protected_power = np.maximum(p_bin, 0.0) + valid = np.isfinite(pmax) & (pmax > self.eps_w) & np.isfinite(protected_power) + valid &= np.isfinite(f_bins) & (f_bins > 0.0) + + utilization = np.zeros_like(protected_power, dtype=np.float64) + headroom = np.full_like(protected_power, np.inf, dtype=np.float64) + utilization[valid] = protected_power[valid] / pmax[valid] + headroom[valid] = 10.0 * np.log10(pmax[valid] / np.maximum(protected_power[valid], self.eps_w)) + + if not np.any(valid): + worst = float("nan") + critical_index = 0 + critical_frequency = float("nan") + critical_power = float("nan") + critical_pmax = float("nan") + else: + valid_indices = np.flatnonzero(valid) + critical_index = int(valid_indices[np.argmin(headroom[valid])]) + worst = float(headroom[critical_index]) + critical_frequency = float(f_bins[critical_index]) + critical_power = float(protected_power[critical_index]) + critical_pmax = float(pmax[critical_index]) + + return { + "utilization_bin": utilization, + "headroom_db_bin": headroom, + "worst_headroom_db": worst, + "critical_frequency_hz": critical_frequency, + "critical_power_w": critical_power, + "critical_pmax_w": critical_pmax, + "over_limit_bool": bool(math.isfinite(worst) and worst <= HEADROOM_LIMIT_DB), + "status": headroom_status(worst), + } + + +def load_two_column_curve(path: str | Path) -> tuple[np.ndarray, np.ndarray]: + path = Path(path) + try: + text = path.read_text(encoding="utf-8-sig", errors="replace") + except OSError as exc: + raise ValueError(f"Could not read WinISD curve '{path}': {exc}") from exc + + rows: list[tuple[float, float]] = [] + for line in text.splitlines(): + stripped = line.strip() + if not stripped or stripped.startswith(("#", "*")): + continue + values = _parse_numeric_line(stripped) + if len(values) >= 2: + rows.append((values[0], values[1])) + + if not rows: + raise ValueError(f"No two-column numeric data found in WinISD curve '{path}'") + + data = np.asarray(rows, dtype=np.float64) + return data[:, 0], data[:, 1] + + +def check_excursion_scaling( + excursion_10w_path: str | Path, + excursion_100w_path: str | Path, + tolerance: float = 0.10, +) -> ExcursionSanityResult: + f10, x10 = load_two_column_curve(excursion_10w_path) + f100, x100 = load_two_column_curve(excursion_100w_path) + valid_10 = np.isfinite(f10) & np.isfinite(x10) & (f10 > 0.0) & (x10 > 0.0) + valid_100 = np.isfinite(f100) & np.isfinite(x100) & (f100 > 0.0) & (x100 > 0.0) + f10 = f10[valid_10] + x10 = x10[valid_10] + f100 = f100[valid_100] + x100 = x100[valid_100] + if len(f10) < 2 or len(f100) < 2: + raise ValueError("Excursion sanity check needs at least two valid rows per file") + + order = np.argsort(f10) + f10 = f10[order] + x10 = x10[order] + in_range = (f100 >= f10[0]) & (f100 <= f10[-1]) + if np.count_nonzero(in_range) < 2: + raise ValueError("Excursion files do not overlap enough in frequency") + + x10_interp = np.interp(np.log(f100[in_range]), np.log(f10), x10) + ratios = x100[in_range] / x10_interp + ratios = ratios[np.isfinite(ratios) & (ratios > 0.0)] + if len(ratios) == 0: + raise ValueError("Excursion sanity check produced no valid ratios") + + expected = math.sqrt(10.0) + median_ratio = float(np.median(ratios)) + relative_error = abs(median_ratio - expected) / expected + ok = relative_error <= tolerance + if ok: + message = ( + f"Excursion scaling OK: median ratio {median_ratio:.3f}, " + f"expected {expected:.3f}." + ) + else: + message = ( + f"Excursion scaling warning: median ratio {median_ratio:.3f}, " + f"expected {expected:.3f}, error {relative_error * 100.0:.1f}%." + ) + return ExcursionSanityResult(ok, median_ratio, expected, relative_error, message) + + +def run_self_test() -> None: + frequencies = np.array([20.0, 100.0, 1000.0], dtype=np.float64) + limit = WinISDLimitCurve(frequencies, np.full_like(frequencies, 100.0)) + bins = np.array([20.0, 50.0, 100.0], dtype=np.float64) + + cases = ( + (50.0, 3.010299956639812, False), + (100.0, 0.0, True), + (200.0, -3.010299956639812, True), + ) + for power, expected_headroom, expected_over_limit in cases: + p_bin = np.array([0.0, power, 0.0], dtype=np.float64) + result = limit.compute_headroom(p_bin, bins) + actual = result["worst_headroom_db"] + if not math.isclose(actual, expected_headroom, abs_tol=1e-6): + raise AssertionError(f"headroom for {power:g} W: expected {expected_headroom}, got {actual}") + if result["over_limit_bool"] is not expected_over_limit: + raise AssertionError( + f"over-limit for {power:g} W: expected {expected_over_limit}, got {result['over_limit_bool']}" + ) + + +def _parse_numeric_line(line: str) -> list[float]: + if ";" in line: + parts = line.split(";") + else: + parts = re.split(r"[\t, ]+", line) + + values: list[float] = [] + for part in parts: + token = part.strip() + if not token: + continue + token = token.replace(",", ".") + try: + values.append(float(token)) + except ValueError: + return [] + return values diff --git a/soundcard_input.py b/soundcard_input.py index 70af408..d67ae85 100644 --- a/soundcard_input.py +++ b/soundcard_input.py @@ -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: diff --git a/winisd/excursion_100w.csv b/winisd/excursion_100w.csv new file mode 100644 index 0000000..a46d795 --- /dev/null +++ b/winisd/excursion_100w.csv @@ -0,0 +1,124 @@ +21,026346153846156; 12,256386445096123 +22,587500000000002; 12,048781362007169 +24,14865384615385; 11,842111436950148 +25,70980769230769; 11,621414141414142 +27,270961538461542; 11,411003584229391 +28,832115384615385; 11,184695340501792 +30,39326923076923; 10,958387096774194 +31,954423076923078; 10,732078853046595 +33,51557692307692; 10,505770609318997 +35,00576923076923; 10,286663082437276 +36,49596153846154; 10,053154121863798 +37,98615384615385; 9,829931899641576 +39,40538461538462; 9,609795698924732 +40,824615384615385; 9,384516129032258 +42,24384615384616; 9,158207885304659 +43,66307692307693; 8,926756272401434 +45,082307692307694; 8,696333333333333 +46,43057692307693; 8,473568299482277 +47,565961538461536; 8,279426523297492 +49,69480769230769; 7,9367356359291845 +51,043076923076924; 7,678974910394266 +52,39134615384616; 7,442608522500995 +53,66865384615385; 7,214014336917563 +54,94596153846154; 6,983134209478296 +56,152307692307694; 6,760112007168459 +57,35865384615385; 6,5350896057347665 +58,565000000000005; 6,304923835125448 +59,70038461538462; 6,08633064516129 +60,83576923076923; 5,8664516129032265 +61,97115384615385; 5,644000896057348 +63,10653846153846; 5,41769265232975 +64,24192307692309; 5,18752688172043 +65,3773076923077; 4,954789426523298 +66,5126923076923; 4,720766129032258 +67,57711538461538; 4,498315412186379 +68,64153846153846; 4,273293010752688 +69,70596153846154; 4,045698924731182 +70,77038461538461; 3,8181048387096777 +71,83480769230769; 3,593082437275985 +72,82826923076924; 3,377060931899642 +73,82173076923078; 3,1610394265232973 +74,81519230769231; 2,9450179211469525 +75,80865384615385; 2,7289964157706095 +76,87307692307692; 2,5065456989247306 +78,00846153846155; 2,266093189964158 +79,14384615384616; 2,035927419354838 +80,27923076923076; 1,8134767025089609 +81,48557692307693; 1,5883114297092789 +82,83384615384617; 1,3639462365591388 +84,32403846153846; 1,1738286086673178 +85,88519230769231; 1,0531932225480602 +87,44634615384615; 1,0335549038774854 +89,0075; 1,099951124144674 +90,56865384615384; 1,2486412512218958 +92,12980769230771; 1,440348647768003 +93,69096153846155; 1,6479537308569583 +95,2521153846154; 1,8574291300097752 +96,81326923076924; 2,0612935809710002 +98,37442307692308; 2,2501955034213097 +99,93557692307692; 2,4260052134245687 +101,49673076923077; 2,584982078853047 +103,05788461538461; 2,7224503095470833 +104,61903846153848; 2,847761485826002 +106,18019230769232; 2,950628869338546 +107,74134615384617; 3,0385337243401747 +109,30250000000001; 3,1096057347670243 +110,86365384615385; 3,1675855327468234 +112,4248076923077; 3,2049918540241116 +113,98596153846154; 3,236787227109806 +115,54711538461538; 3,253620071684587 +117,10826923076922; 3,253620071684587 +118,6694230769231; 3,253620071684587 +120,23057692307694; 3,2377223851417405 +121,79173076923078; 3,216213750407297 +123,35288461538462; 3,185353535353535 +124,91403846153847; 3,1507526881720427 +126,47519230769231; 3,1114760508308894 +128,03634615384615; 3,0665884652981426 +129,5975; 3,020765721733463 +131,15865384615384; 2,967461713913327 +132,7198076923077; 2,9141577060931905 +134,28096153846155; 2,8580482241772565 +135,8421153846154; 2,80100358422939 +137,40326923076924; 2,740218312153795 +138,96442307692308; 2,680368198110134 +140,52557692307693; 2,6186477680026066 +142,08673076923077; 2,556927337895079 +143,6478846153846; 2,495206907787553 +145,20903846153846; 2,4316161616161605 +146,7701923076923; 2,3698957315086346 +148,33134615384614; 2,308175301401107 +149,8925; 2,2464548712935795 +151,45365384615386; 2,1847344411860536 +153,0148076923077; 2,124884327142391 +154,57596153846154; 2,0650342130987305 +156,13711538461538; 2,003313782991203 +157,69826923076923; 1,9472043010752689 +159,25942307692307; 1,8910948191593349 +160,8205769230769; 1,83405017921147 +162,38173076923076; 1,7770055392636035 +163,94288461538463; 1,724636689475398 +165,50403846153847; 1,6713326816552634 +167,0651923076923; 1,6198989898989904 +168,62634615384616; 1,5684652981427174 +170,1875; 1,5189019224503078 +171,74865384615384; 1,4693385467579017 +173,3098076923077; 1,4244509612251548 +174,87096153846153; 1,3758227435646795 +176,43211538461537; 1,3309351580319309 +177,99326923076924; 1,2888530465949817 +179,5544230769231; 1,2449006190941674 +181,11557692307693; 1,203753665689149 +182,67673076923077; 1,1626067122841324 +184,23788461538462; 1,1242652329749099 +185,79903846153846; 1,0859237536656892 +187,3601923076923; 1,048517432388401 +188,92134615384614; 1,0129814271749762 +190,4825; 0,9783805799934822 +192,04365384615386; 0,9437797328119917 +193,6048076923077; 0,9138546757901587 +195,16596153846154; 0,8792538286086682 +196,7271153846154; 0,8502639296187677 +198,28826923076923; 0,8175333985011406 +199,4946153846154; 0,7968040621266432 diff --git a/winisd/excursion_10w.csv b/winisd/excursion_10w.csv new file mode 100644 index 0000000..28aa3cb --- /dev/null +++ b/winisd/excursion_10w.csv @@ -0,0 +1,226 @@ +20,19701646455929; 3,9154911544506223 +21,050168068474605; 3,879470281827402 +21,903283539505352; 3,844592011309437 +22,756471276305234; 3,80742853658096 +23,609677079547396; 3,7696937607998553 +24,462792550578143; 3,7348154902818904 +25,316016420262592; 3,6965094134481573 +26,169204157062474; 3,6593459387196807 +27,022391893862352; 3,6221824639912037 +27,875615763546797; 3,5838763871574706 +28,72887576611581; 3,5444277082184814 +29,582027370031124; 3,5084068355952605 +30,435341571926983; 3,4672442534983867 +31,28863770738056; 3,426652972454142 +32,14175317841131; 3,391774701936177 +32,99506738030717; 3,3506121198393037 +33,84838158220303; 3,30944953774243 +34,70169578409889; 3,268286955645557 +35,555009985994744; 3,2271243735486834 +36,40832418789061; 3,1859617914518097 +37,26163838978647; 3,144799209354937 +38,114952591682325; 3,103636627258063 +38,96826679357818; 3,06247404516119 +39,82158099547404; 3,021311463064316 +40,60378568054524; 2,9835790961421824 +41,31507596636845; 2,9431068930264046 +42,09730233117039; 2,904688964841117 +42,95061653306625; 2,8635263827442436 +43,732972976252626; 2,8209950869800338 +44,51524270051605; 2,7812060362684385 +45,29751242477947; 2,7414169855568438 +46,07986886796585; 2,698885689792634 +46,86218195169075; 2,6577255165547315 +47,64449503541566; 2,616565343316829 +48,426894838063504; 2,572662925026312 +49,13807672523302; 2,5356185282263026 +49,92043316841939; 2,4930872324620927 +50,70283297106725; 2,4491848141715757 +51,41410157715971; 2,409398172318952 +52,12537018325217; 2,3696115304663277 +52,83663878934463; 2,329824888613704 +53,54790739543711; 2,29003824676108 +54,25921936099104; 2,248880482382148 +54,970531326544986; 2,2077227180032173 +55,68184329209893; 2,1665649536242855 +56,39319861711435; 2,124036066719047 +57,10455394212977; 2,081507179813808 +57,815865907683715; 2,040349415434877 +58,527221232699134; 1,9978205285296382 +59,23859823744529; 1,9546060803612457 +59,95001860165293; 1,9100205096665457 +60,59024815004564; 1,8714360088834119 +61,23053189776519; 1,831137604942394 +61,94195226197283; 1,786552034247694 +62,65337262618047; 1,7419664635529943 +63,36479299038811; 1,6973808928582943 +64,00510383777109; 1,6562255373383339 +64,64541468515408; 1,6150701818183735 +65,28572553253706; 1,573914826298413 +65,85495396275881; 1,5353327343742502 +66,4241281936537; 1,4984645456079715 +66,99330242454862; 1,4615963568416928 +67,56253085477037; 1,4230142649175295 +68,13170508566527; 1,3861460761512507 +68,70093351588702; 1,347563984227088 +69,27016194610877; 1,308981892302925 +69,83939037633053; 1,2703998003787618 +70,40859170688886; 1,2326746600335405 +70,97782013711061; 1,1940925681093777 +71,54710276665921; 1,1537965730273307 +72,11624989789068; 1,117785335839994 +72,68547832811244; 1,0792032439158312 +73,25481515698789; 1,0371933456758997 +73,82401648754622; 0,9994682053306789 +74,39324491776797; 0,9608861134065156 +74,96252754731657; 0,9205901183244682 +75,5317288778749; 0,8828649779792475 +76,10095730809664; 0,8442828860550842 +76,6701857383184; 0,8057007941309218 +77,23941416854015; 0,7671187022067585 +77,80858839943505; 0,7302505134404793 +78,44889924681803; 0,6890951579205193 +79,08921009420101; 0,6479398024005585 +79,729520941584; 0,6067844468805985 +80,44087626659942; 0,5642555599753596 +81,15223159161484; 0,5217266730701215 +81,8635001977073; 0,48194003121749684 +82,64576992197073; 0,44215098050590207 +83,49902992453974; 0,40270230156691333 +84,35203699691678; 0,3712518373647171 +85,20482727198645; 0,34665698579405824 +86,05732848397959; 0,33120295106544795 +86,90952256645392; 0,3254610342315143 +87,76142758585172; 0,3288599342396301 +88,61302547573072; 0,34197095214242346 +89,4643523689755; 0,36365148583463824 +90,31549859779743; 0,3910450300531334 +91,16653642796567; 0,42186638058739767 +92,01748392592249; 0,45554423638480124 +92,86839529099475; 0,4903646942874609 +93,71923439029788; 0,5274703564006327 +94,57009155604331; 0,5640047174611773 +95,42098485467328; 0,599396476416465 +96,27187815330326; 0,6347882353717527 +97,12282565126009; 0,6684660911691571 +97,9737912156592; 0,7015726459139326 +98,82481097938515; 0,732965297500825 +99,67588494243796; 0,7626440459298331 +100,52695890549076; 0,7923227943588396 +101,37814126719728; 0,8185737364720787 +102,22937782823061; 0,8431107754274336 +103,08065052214855; 0,8665052122775316 +103,93201354827787; 0,8870431438644903 +104,7833765744072; 0,9075810754514482 +105,63477573342108; 0,9269764049331508 +106,48628329108868; 0,9429439280990843 +107,33779084875627; 0,9589114512650179 +108,189370672193; 0,9725937702204392 +109,04098662851428; 0,9851334870706046 +109,89271098348927; 0,9942453976050007 +110,74439920557967; 1,004499910244654 +111,59615969343923; 1,012469218673795 +112,4479924470679; 1,0181533228924229 +113,29982520069659; 1,0238374271110517 +114,15169408720982; 1,0283789292244245 +115,00363523949221; 1,0306352271272843 +115,85564865754371; 1,0306063208196319 +116,70766207559522; 1,0305774145119795 +117,55967549364672; 1,030548508204327 +118,41168891169823; 1,0305196018966747 +119,26370232974973; 1,0304906955890223 +120,11580608001265; 1,0276052840182297 +120,96794596316013; 1,023577270342181 +121,82008584630762; 1,0195492566661324 +122,67224379589739; 1,0149499419374557 +123,52441981192943; 1,009779326156151 +124,37663196084604; 1,0034661082695902 +125,22886217620496; 0,9965815893304004 +126,08109239156384; 0,9896970703912116 +126,93332260692274; 0,9828125514520218 +127,78557088872392; 0,975356731460205 +128,63785530340968; 0,9667583093631311 +129,49012165165314; 0,9587311883186871 +130,34242413278116; 0,9495614651689852 +131,19470854746692; 0,9409630430719114 +132,04702909503723; 0,9312220188695823 +132,89933157616525; 0,9220522957198805 +133,75165212373557; 0,9123112715175514 +134,60397267130588; 0,9025702473152206 +135,45632935176076; 0,8916866210076355 +136,30864989933107; 0,8819455968053056 +137,16100657978595; 0,8710619704977214 +138,01332712735626; 0,8613209462953915 +138,86568380781114; 0,8504373199878064 +139,71800435538145; 0,8406962957854756 +140,57036103583633; 0,8298126694778905 +141,42273578273347; 0,8183577421176764 +142,27509246318837; 0,8074741158100913 +143,12741301075866; 0,7977330916077605 +143,97980582409812; 0,7857068631949193 +144,832162504553; 0,7748232368873342 +145,6844830521233; 0,7650822126850043 +146,53683973257816; 0,7541985863774192 +147,38921447947533; 0,7427436590172052 +148,2415711599302; 0,7318600327096201 +149,09389170750055; 0,7221190085072893 +149,9462483879554; 0,7112353821997042 +150,79860506841027; 0,7003517558921182 +151,65096174886517; 0,6894681295845322 +152,50331842932005; 0,678584503276948 +153,3555667111212; 0,6711286832851302 +154,25047347644028; 0,6618432546095203 +155,0602620055887; 0,6499327317225863 +155,91267288537043; 0,637335202257117 +156,7649934329407; 0,6275941780547871 +157,6173501133956; 0,616710551747202 +158,46963452808134; 0,6081121296501282 +159,32195507565166; 0,5983711054477983 +160,17431175610653; 0,587487479140214 +161,02661423723455; 0,5783177559905122 +161,87893478480487; 0,5685767317881831 +162,73125533237518; 0,5588357075858523 +163,5835397470609; 0,5502372854887794 +164,43586029463123; 0,5404962612864495 +165,28816277575928; 0,5313265381367485 +166,14044719044503; 0,5227281160396746 +166,99276773801532; 0,5129870918373456 +167,84503408625878; 0,5049599707929007 +168,69733656738683; 0,4957902476431988 +169,5496029156303; 0,48776312659875387 +170,4019053967583; 0,478593403449052 +171,25417174500177; 0,4705662824046071 +172,10643809324523; 0,46253916136016127 +172,95870444148872; 0,45451204031571635 +173,81098885617445; 0,4459136182186425 +174,6632732708602; 0,43731519612156955 +175,51553961910366; 0,42928807507712463 +176,36776983446256; 0,4224035561379358 +177,220036182706; 0,41437643509349 +178,0723025309495; 0,40634931404904506 +178,9245327463084; 0,39946479510985533 +179,7767629616673; 0,3925802761706665 +180,62902930991072; 0,3845531551262207 +181,48125952526962; 0,37766863618703184 +182,33348974062855; 0,3707841172478421 +183,18571995598745; 0,36389959830865415 +184,03795017134632; 0,3570150793694644 +184,89018038670523; 0,3501305604302756 +185,74241060206413; 0,34324604149108673 +186,59460468453847; 0,33750412465715307 +187,44683489989734; 0,33061960571796334 +188,29902898237168; 0,32487768888403057 +189,15125919773058; 0,31799316994484084 +190,00345328020492; 0,31225125311090896 +190,85564736267924; 0,3065093362769762 +191,70784144515358; 0,3007674194430425 +192,5600535940702; 0,2944542015564817 +193,41226574298682; 0,2881409836699209 +194,26442369257657; 0,28354166894124333 +195,1166177750509; 0,27779975210731056 +195,9687757246407; 0,273200437378633 +196,82098787355727; 0,26688721949207217 +197,67316388958935; 0,26171660371076744 +198,52532183917913; 0,25711728898209163 +199,37747978876888; 0,25251797425341405 +199,8746863468307; 0,24621680066170804 diff --git a/winisd/max_power.csv b/winisd/max_power.csv new file mode 100644 index 0000000..607b81f --- /dev/null +++ b/winisd/max_power.csv @@ -0,0 +1,658 @@ +20,07332894765222; 41,2018918697496 +20,499908332075144; 42,3559513231113 +20,92648771649807; 42,81757510445607 +21,353067100920992; 42,81757510445607 +21,779646485343914; 43,27919888580084 +22,20622586976684; 44,202446448490264 +22,632805254189762; 44,202446448490264 +23,059384638612684; 44,202446448490264 +23,485964023035606; 45,58731779252446 +23,912543407458532; 45,58731779252446 +24,339122791881458; 45,58731779252446 +24,76570217630438; 46,510565355213885 +25,192281560727302; 46,972189136558654 +25,618860945150224; 46,972189136558654 +26,04544032957315; 48,35706048059285 +26,472019713996072; 48,35706048059285 +26,898599098418998; 48,35706048059285 +27,32517848284192; 49,741931824627045 +27,751757867264843; 49,741931824627045 +28,178337251687765; 50,203555605971815 +28,60491663611069; 51,12680316866124 +29,031496020533616; 51,12680316866124 +29,45807540495654; 52,511674512695436 +29,88465478937946; 52,511674512695436 +30,311234173802383; 53,43492207538475 +30,737813558225305; 53,89654585672952 +31,16439294264823; 54,3581696380744 +31,590972327071153; 55,28141720076371 +32,01755171149408; 55,28141720076371 +32,444131095917; 56,66628854479791 +32,87071048033992; 56,66628854479791 +33,297289864762845; 58,0511598888321 +33,72386924918577; 58,0511598888321 +34,1504486336087; 59,4360312328663 +34,57702801803162; 59,4360312328663 +35,00360740245454; 60,820902576900494 +35,43018678687747; 61,28252635824526 +35,85676617130039; 62,20577392093469 +36,283345555723315; 63,129021483624115 +36,70992494014624; 63,590645264968884 +37,13650432456916; 64,97551660900308 +37,56308370899208; 65,43714039034785 +37,989663093415004; 66,36038795303716 +38,416242477837926; 67,28363551572659 +38,84282186226085; 68,20688307841613 +39,26940124668378; 69,13013064110555 +39,6959806311067; 70,05337820379498 +40,12256001552962; 70,97662576648452 +40,54913939995255; 71,89987332917394 +40,97571878437547; 73,28474467320814 +41,402298168798396; 74,20799223589756 +41,82887755322132; 75,1312397985871 +42,25545693764424; 76,5161111426213 +42,68203632206716; 77,43935870531061 +43,108615706490085; 78,8242300493448 +43,53519509091301; 80,209101393379 +43,961774475335936; 81,13234895606843 +44,38835385975886; 82,51722030010262 +44,81493324418178; 83,90209164413682 +45,24151262860471; 85,28696298817101 +45,66809201302763; 86,67183433220521 +46,094671397450554; 88,05670567623929 +46,521250781873476; 89,44157702027348 +46,9478301662964; 90,82644836430768 +47,37440955071932; 92,67294348968665 +47,80098893514224; 94,51943861506561 +48,22756831956517; 95,9043099590998 +48,654147703988095; 97,75080508447866 +49,08072708841102; 99,13567642851285 +49,50730647283394; 101,44379533523659 +49,93388585725686; 103,29029046061532 +50,36046524167978; 104,67516180464952 +50,78704462610271; 106,98328071137325 +51,213624010525635; 108,8297758367521 +51,64020339494856; 111,36870663414811 +52,06678277937148; 113,90763743154423 +52,4933621637944; 115,98494444759547 +52,919941548217324; 118,52387524499136 +53,34652093264025; 121,06280604238736 +53,773100317063175; 123,60173683978348 +54,1996797014861; 126,14066763717938 +54,62625908590902; 128,91041032524777 +55,05283847033194; 131,91096490398854 +55,47941785475487; 135,1423313734016 +55,90599723917779; 138,14288595214236 +56,332576623600715; 141,6050643122278 +56,75915600802364; 144,83643078164084 +57,18573539244656; 148,76023292307104 +57,61231477686948; 151,9915993924842 +58,03889416129241; 155,68458964324202 +58,465473545715334; 159,8392036753445 +58,892052930138256; 163,99381770744708 +59,31863231456118; 167,68680795820478 +59,603018570843126; 172,30304577165214 +60,02959795526605; 176,45765980375472 +60,420629057653734; 180,95849167186577 +60,83535901473157; 185,6901354306492 +61,16714298039385; 189,8447494627518 +61,451529236675796; 194,46098727619903 +61,842560339063475; 198,61560130830162 +62,16249487738067; 203,69346290309363 +62,446881133662615; 207,8480769351961 +62,73126738994457; 212,0026909672987 +62,96825593684619; 216,15730499940128 +63,300039902508466; 220,77354281284852 +63,584426158790414; 226,3130281889853 +63,86881241507236; 231,3908897837772 +64,15319867135432; 236,00712759722455 +64,43758492763627; 241,54661297336133 +64,72197118391821; 247,086098349498 +64,95895973081983; 252,16395994429 +65,29074369648211; 259,3768315278014 +65,14855056834114; 254,70289074168602 +65,52773224338372; 264,62780204059766 +65,81211849966569; 271,32134687009625 +65,71732308090503; 267,16673283799366 +66,09650475594763; 278,2457035902671 +66,00170933718698; 274,0910895581645 +66,38089101222958; 285,170060310438 +66,28609559346893; 281,0154462783355 +66,66527726851153; 292,09441703060895 +66,57048184975088; 287,93980299850637 +66,92596467010331; 298,3263380787628 +66,85486810603282; 294,86415971867734 +67,06815779824429; 302,48095211086536 +67,21035092638526; 306,63556614296783 +67,35254405452623; 310,7901801750704 +67,49473718266722; 314,944794207173 +67,6369303108082; 319,0994082392755 +67,77912343894917; 323,25402227137806 +67,99241313116062; 328,3318838661701 +68,13460625930159; 332,9481216796173 +68,27679938744257; 338,0259832744093 +68,41899251558354; 342,64222108785657 +68,56118564372451; 347,7200826826486 +68,63228220779501; 351,6438848240788 +68,84557190000646; 357,0102612822112 +68,98776502814744; 362,9536674670246 +69,05886159221794; 366,8774696084548 +69,22475357504908; 371,49370742190206 +69,36694670319005; 377,0331927980387 +69,50913983133103; 382,5726781741755 +69,651332959472; 388,80459922232933 +69,79352608761297; 394,8057083798109 +69,93571921575395; 401,268441318637 +70,07791234389492; 407,2695504761185 +70,26750318141622; 414,3093131416256 +70,40969630955719; 421,23366986179644 +70,55188943769816; 428,1580265819674 +70,69408256583914; 435,0823833021383 +70,83627569398011; 442,00674002230915 +70,97846882212109; 448,9310967424801 +71,07326424088174; 455,27842373597014 +71,19175851433255; 461,74115667479634 +71,33395164247352; 470,0503847390014 +71,33395164247352; 465,8957707068988 +71,26285507840304; 458,2789783147108 +71,4761447706145; 478,35961280320646 +71,4761447706145; 474,204998771104 +71,61833789875547; 486,66884086741163 +71,61833789875547; 482,51422683530905 +71,76053102689644; 494,9780689316167 +71,76053102689644; 490,8234548995141 +71,9738207191079; 504,2105445585113 +71,90272415503742; 499,13268296371916 +72,11601384724888; 511,9427428960354 +71,9738207191079; 507,44191102792433 +72,25820697538985; 522,2138720309556 +72,18711041131937; 517,1360104361636 +72,35300239415051; 529,1382287511265 +72,25820697538985; 525,4452385003688 +72,49519552229148; 538,8323281593658 +72,47149666760131; 534,1006844005824 +72,63738865043246; 549,4496751302945 +72,61368979574229; 544,1410016448301 +72,77958177857343; 559,6053983198785 +72,75588292388326; 554,5275367250865 +72,96917261609474; 570,7997750174882 +72,89807605202424; 564,9140718053429 +73,11136574423571; 581,8787457697616 +73,04026918016521; 575,9930425576163 +73,25355887237669; 592,9577165220351 +73,18246230830619; 587,0720133098898 +73,34835429113733; 603,4596575476276 +73,32465543644716; 598,1509840621633 +73,46684856458813; 614,0770045185564 +73,46684856458813; 609,9223904864539 +73,63274054741927; 627,0024703962088 +73,60904169272911; 621,6937969107444 +73,60904169272911; 617,8854007146504 +73,53794512865863; 606,4602121263683 +73,82233138494058; 638,5430649298269 +73,75123482087008; 633,4652033350349 +73,96452451308156; 650,4298772994537 +73,89342794901106; 644,5441740873084 +73,82233138494058; 641,77443139924 +73,82233138494058; 630,0030249749495 +74,10671764122253; 664,8556204664765 +74,03562107715203; 659,7777588716845 +74,03562107715203; 655,6231448395818 +74,20151305998317; 675,9345912187498 +74,177814205293; 670,8567296239578 +74,10671764122253; 668,0869869358894 +74,34370618812414; 690,7065522217811 +74,32000733343398; 685,3978787363168 +74,32000733343398; 681,2432647042142 +74,48589931626512; 705,0168894434677 +74,46220046157495; 699,9390278486757 +74,46220046157495; 695,7844138165731 +74,67549015378643; 720,3658801731799 +74,60439358971593; 714,4801769610347 +74,60439358971593; 710,325562928932 +74,77028557254707; 735,022435230875 +74,7465867178569; 729,7137617454106 +74,7465867178569; 725,559147713308 +74,88877984599789; 749,7943962339062 +74,88877984599789; 745,6397822018037 +74,88877984599789; 741,4851681697012 +75,03097297413886; 766,4128523623165 +75,03097297413886; 762,2582383302139 +75,03097297413886; 758,1036242981113 +75,03097297413886; 753,9490102660088 +74,95987641006838; 738,0229898096156 +75,24426266635032; 783,9545560534161 +75,17316610227984; 778,8766944586241 +75,17316610227984; 774,7220804265215 +75,17316610227984; 770,567466394419 +75,33905808511096; 799,188140837792 +75,31535923042081; 794,110279243 +75,31535923042081; 789,9556652108976 +75,24426266635032; 787,1859225228292 +75,48125121325195; 817,1914683102365 +75,45755235856178; 812,1136067154445 +75,45755235856178; 807,9589926833419 +75,45755235856178; 804,1505964872479 +75,62344434139293; 835,1947957826808 +75,59974548670276; 830,1169341878888 +75,59974548670276; 825,9623201557863 +75,59974548670276; 822,1539239596923 +75,7656374695339; 854,1213708178146 +75,74193861484373; 848,8126973323504 +75,74193861484373; 844,6580833002478 +75,74193861484373; 840,5034692681452 +75,8841317429847; 873,0479458529485 +75,8841317429847; 868,8933318208459 +75,8841317429847; 864,7387177887433 +75,8841317429847; 860,5841037566408 +76,05002372581585; 894,282639794806 +76,02632487112568; 888,9739663093416 +76,02632487112568; 884,819352277239 +76,02632487112568; 880,6647382451365 +76,02632487112568; 876,8563420490425 +75,9552283070552; 857,1219253965554 +76,19221685395682; 912,747591048595 +76,16851799926665; 907,669729453803 +76,16851799926665; 903,5151154217004 +76,16851799926665; 899,360501389598 +76,3344099820978; 934,9055325531419 +76,31071112740763; 929,8276709583499 +76,31071112740763; 925,6730569262475 +76,31071112740763; 921,5184428941449 +76,31071112740763; 917,7100466980509 +76,47660311023877; 957,0634740576888 +76,4529042555486; 951,9856124628968 +76,4529042555486; 947,8309984307944 +76,4529042555486; 943,6763843986918 +76,4529042555486; 939,8679882025978 +76,61879623837974; 980,1446631249253 +76,59509738368958; 974,8359896394609 +76,59509738368958; 970,6813756073583 +76,59509738368958; 966,5267615752558 +76,59509738368958; 962,3721475431532 +76,80838707590104; 999,7636738320762 +76,73729051183055; 994,9166241279565 +76,73729051183055; 990,762010095854 +76,73729051183055; 986,6073960637515 +76,80838707590104; 983,1452177036659 +77,23496646032396; 1001,1485451761104 +77,66154584474688; 1001,1485451761104 +78,0881252291698; 1001,1485451761104 +78,51470461359273; 1001,1485451761104 +78,94128399801565; 1001,1485451761104 +79,36786338243857; 1001,1485451761104 +79,7944427668615; 1001,1485451761104 +80,22102215128442; 1001,1485451761104 +80,64760153570734; 1001,1485451761104 +81,07418092013029; 1001,1485451761104 +81,50076030455321; 1001,1485451761104 +81,92733968897613; 1001,1485451761104 +82,35391907339906; 1001,1485451761104 +82,78049845782198; 1001,1485451761104 +83,2070778422449; 1001,1485451761104 +83,63365722666782; 1001,1485451761104 +84,06023661109074; 1001,1485451761104 +84,48681599551367; 1001,1485451761104 +84,91339537993659; 1001,1485451761104 +85,33997476435951; 1001,1485451761104 +85,76655414878243; 1001,1485451761104 +86,19313353320535; 1001,1485451761104 +86,61971291762828; 1001,1485451761104 +87,0462923020512; 1001,1485451761104 +87,47287168647412; 1001,1485451761104 +87,89945107089704; 1001,1485451761104 +88,32603045531997; 1001,1485451761104 +88,75260983974289; 1001,1485451761104 +89,17918922416581; 1001,1485451761104 +89,60576860858873; 1001,1485451761104 +90,03234799301165; 1001,1485451761104 +90,45892737743458; 1001,1485451761104 +90,88550676185753; 1001,1485451761104 +91,31208614628045; 1001,1485451761104 +91,73866553070337; 1001,1485451761104 +92,1652449151263; 1001,1485451761104 +92,59182429954922; 1001,1485451761104 +93,01840368397214; 1001,1485451761104 +93,44498306839506; 1001,1485451761104 +93,87156245281798; 1001,1485451761104 +94,2981418372409; 1001,1485451761104 +94,72472122166383; 1001,1485451761104 +95,15130060608675; 1001,1485451761104 +95,57787999050967; 1001,1485451761104 +96,0044593749326; 1001,1485451761104 +96,43103875935552; 1001,1485451761104 +96,85761814377844; 1001,1485451761104 +97,28419752820136; 1001,1485451761104 +97,71077691262428; 1001,1485451761104 +98,1373562970472; 1001,1485451761104 +98,56393568147013; 1001,1485451761104 +98,99051506589305; 1001,1485451761104 +99,41709445031597; 1001,1485451761104 +99,8436738347389; 1001,1485451761104 +100,27025321916184; 1001,1485451761104 +100,69683260358477; 1001,1485451761104 +101,05231542393719; 999,0712381600591 +101,05231542393719; 994,9166241279565 +101,05231542393719; 990,762010095854 +101,21820740676833; 985,6841485010619 +101,19450855207816; 980,3754750155977 +101,3604005349093; 975,0668015301333 +101,31300282552897; 969,5273161539965 +101,50259366305028; 963,9878307778598 +101,47889480836011; 958,9099691830678 +101,64478679119125; 953,8321075882758 +101,62108793650108; 948,86965193882 +101,78697991933223; 944,1380081800365 +101,76328106464206; 939,1755525305807 +101,9291730474732; 934,4439087717972 +101,90547419278303; 929,4814531223415 +102,07136617561417; 924,7498093635579 +102,047667320924; 919,7873537141021 +102,18986044906498; 914,2478683379654 +102,33205357720595; 909,0546007978372 +102,33205357720595; 904,8999867657346 +102,47424670534693; 900,7453727336322 +102,47424670534693; 896,5907587015296 +102,6164398334879; 892,436144669427 +102,6164398334879; 888,2815306373245 +102,75863296162888; 884,1269166052219 +102,75863296162888; 879,9723025731194 +102,90082608976985; 875,8176885410169 +102,90082608976985; 871,6630745089143 +103,04301921791082; 867,5084604768117 +103,04301921791082; 863,7000642807177 +103,20891120074197; 858,737608631262 +103,11411578198133; 855,7370540525212 +103,32740547419277; 852,2748756924357 +103,32740547419277; 848,4664794963418 +103,46959860233375; 842,926994120205 +103,61179173047472; 836,0026374000341 +103,7539848586157; 829,0782806798632 +103,91987684144684; 822,7309536863731 +104,06206996958781; 816,7298445288917 +104,20426309772878; 810,2671115900655 +104,34645622586976; 804,2660024325841 +104,48864935401073; 797,8032694937579 +104,6308424821517; 792,0329722269488 +104,77303561029268; 786,4934868508121 +104,91522873843365; 780,9540014746754 +105,05742186657463; 775,4145160985386 +105,1996149947156; 769,8750307224019 +105,34180812285658; 764,3355453462652 +105,53139896037789; 758,4498421341199 +105,67359208851886; 752,5641389219746 +105,81578521665983; 747,9479011085273 +106,00537605418114; 741,946791951046 +106,14756918232212; 737,3305541375986 +106,31346116515326; 733,175940105496 +106,45565429329423; 729,0213260733935 +106,5978474214352; 724,866712041291 +106,74004054957618; 720,7120980091884 +106,88223367771715; 716,9037018130944 +107,02442680585813; 713,4415234530089 +107,1666199339991; 709,633127256915 +107,30881306214008; 705,824731060821 +107,47470504497119; 701,3238991927099 +107,735392446563; 697,1692851606074 +107,735392446563; 693,7071068005218 +107,99607984815478; 689,7833046590916 +108,23306839505639; 684,7054430642997 +108,470056941958; 679,165957688163 +108,70704548885965; 674,5497198747157 +108,9914317451416; 670,395105842613 +109,22842029204321; 665,3172442478211 +109,51280654832516; 660,7010064343739 +109,7971928046071; 656,5463924022713 +110,08157906088908; 651,4685308074793 +110,4370618812415; 648,0063524473939 +110,65035157345298; 644,0825503059637 +110,98213553911523; 639,9279362738612 +111,36131721415785; 636,2349460231032 +111,78789659858077; 632,0803319910008 +112,2144759830037; 627,9257179588982 +112,64105536742662; 624,001915817468 +113,06763475184954; 621,2321731293996 +113,49421413627246; 618,4624304413313 +113,92079352069538; 616,1543115346076 +114,3473729051183; 614,0770045185564 +114,77395228954123; 612,2305093931775 +115,20053167396415; 610,8456380491433 +115,62711105838707; 609,4607667051091 +116,05369044281; 608,9991429237643 +116,48026982723292; 607,8450834704025 +116,90684921165584; 607,8450834704025 +117,33342859607876; 607,8450834704025 +117,76000798050168; 607,8450834704025 +118,1865873649246; 609,2299548144367 +118,61316674934753; 609,2299548144367 +119,03974613377045; 610,6148261584709 +119,46632551819337; 611,5380737211603 +119,89290490261632; 612,9229450651945 +120,31948428703924; 614,7694401905735 +120,74606367146217; 616,6159353159524 +121,17264305588509; 619,1548661133484 +121,59922244030801; 621,462985020072 +122,02580182473093; 624,2327277081404 +122,45238120915386; 627,0024703962088 +122,87896059357678; 629,772213084277 +123,3055399779997; 633,2343914443625 +123,73211936242262; 636,4657579137756 +124,15869874684554; 640,3895600552058 +124,58527813126847; 644,5441740873084 +125,01185751569139; 648,6987881194109 +125,43843690011431; 652,8534021515135 +125,86501628453723; 657,0080161836161 +126,24419795957982; 660,7010064343739 +126,5759819252421; 665,3172442478211 +126,95516360028469; 669,0102344985789 +127,28694756594697; 673,6264723120262 +127,67797866833467; 678,1273041801373 +128,1045580527576; 683,666789556274 +128,53113743718052; 688,8600570964022 +128,8510719754977; 693,9379186911942 +129,13545823177964; 698,5541565046415 +129,4909410521321; 702,708770536744 +129,7990261631042; 707,786632131536 +130,08341241938615; 711,9412461636387 +130,41519638504843; 716,5574839770859 +130,69958264133038; 721,6353455718779 +130,936571188232; 725,7899596039804 +131,26835515389428; 730,4061974174276 +131,55274141017622; 735,4840590122196 +131,83712766645817; 740,100296825667 +132,0741162133598; 745,1781584204589 +132,40590017902207; 749,7943962339062 +132,69028643530402; 755,3338816100429 +132,97467269158597; 760,8733669861797 +133,2590589478679; 766,4128523623165 +133,54344520414986; 771,9523377384531 +133,8278314604318; 777,4918231145899 +134,11221771671376; 783,0313084907266 +134,3966039729957; 788,5707938668634 +134,68099022927765; 794,110279243 +134,9653764855596; 799,6497646191368 +135,17866617777108; 804,1505964872479 +135,39195586998252; 809,1707551093718 +135,62894441688414; 814,4217256221681 +135,96072838254642; 821,6345972056795 +135,81853525440545; 816,9606564195641 +136,19771692944803; 826,8855677184757 +136,48210318572998; 833,5791125479743 +136,38730776696934; 829,4244985158717 +136,81388715139227; 841,022796022158 +136,6716940232513; 836,3488552360427 +137,02717684360374; 845,6967368082734 +137,19306882643485; 850,1975686763844 +137,4774550827168; 857,1219253965554 +137,38265966395616; 852,9673113644528 +137,76184133899878; 864,0462821167263 +137,6670459202381; 859,8916680846237 +138,04622759528073; 870,9706388368973 +137,95143217652006; 866,8160248047947 +138,33061385156267; 877,8949955570681 +138,235818432802; 873,7403815249655 +138,59130125315446; 884,1269166052219 +138,52020468908395; 880,6647382451365 +138,7571932359856; 888,9739663093416 +139,01788063757738; 895,2058873574954 +138,94678407350688; 891,74370899741 +139,18377262040852; 900,052937061615 +139,4444600220003; 906,2848581097688 +139,37336345792983; 902,8226797496834 +139,58665315014127; 910,4394721418714 +139,75254513297241; 915,286521845991 +140,0132325345642; 921,5184428941449 +139,94213597049372; 918,0562645340594 +140,15542566270517; 925,6730569262475 +140,29761879084614; 929,8276709583499 +140,43981191898712; 933,9822849904525 +140,5820050471281; 938,1368990225551 +140,72419817526907; 942,2915130546576 +140,8900901581002; 947,1385627587772 +141,2218741237625; 954,2937313696206 +141,07968099562152; 949,9083054468456 +141,36406725190346; 958,9099691830678 +141,43516381597394; 963,0645832151704 +141,5773569441149; 967,2191972472729 +141,71955007225588; 971,3738112793754 +141,86174320039686; 975,528425311478 +142,00393632853783; 979,6830393435805 +142,1461294566788; 983,8376533756831 +142,35941914889028; 988,9155149704751 +142,43051571296076; 992,8393171119053 +142,64380540517223; 998,0325846520334 +143,07038478959515; 1001,1485451761104 +143,49696417401807; 1001,1485451761104 +143,923543558441; 1001,1485451761104 +144,35012294286392; 1001,1485451761104 +144,77670232728684; 1001,1485451761104 +145,20328171170976; 1001,1485451761104 +145,6298610961327; 1001,1485451761104 +146,0564404805556; 1001,1485451761104 +146,48301986497853; 1001,1485451761104 +146,90959924940145; 1001,1485451761104 +147,33617863382437; 1001,1485451761104 +147,7627580182473; 1001,1485451761104 +148,18933740267022; 1001,1485451761104 +148,61591678709314; 1001,1485451761104 +149,04249617151606; 1001,1485451761104 +149,46907555593899; 1001,1485451761104 +149,8956549403619; 1001,1485451761104 +150,32223432478483; 1001,1485451761104 +150,74881370920775; 1001,1485451761104 +151,17539309363067; 1001,1485451761104 +151,60197247805363; 1001,1485451761104 +152,02855186247655; 1001,1485451761104 +152,45513124689947; 1001,1485451761104 +152,8817106313224; 1001,1485451761104 +153,3082900157453; 1001,1485451761104 +153,73486940016824; 1001,1485451761104 +154,16144878459116; 1001,1485451761104 +154,58802816901408; 1001,1485451761104 +155,014607553437; 1001,1485451761104 +155,44118693785992; 1001,1485451761104 +155,86776632228285; 1001,1485451761104 +156,29434570670577; 1001,1485451761104 +156,7209250911287; 1001,1485451761104 +157,1475044755516; 1001,1485451761104 +157,57408385997454; 1001,1485451761104 +158,00066324439746; 1001,1485451761104 +158,42724262882038; 1001,1485451761104 +158,8538220132433; 1001,1485451761104 +159,28040139766622; 1001,1485451761104 +159,70698078208915; 1001,1485451761104 +160,13356016651207; 1001,1485451761104 +160,560139550935; 1001,1485451761104 +160,98671893535794; 1001,1485451761104 +161,41329831978086; 1001,1485451761104 +161,8398777042038; 1001,1485451761104 +162,2664570886267; 1001,1485451761104 +162,69303647304963; 1001,1485451761104 +163,11961585747255; 1001,1485451761104 +163,54619524189548; 1001,1485451761104 +163,9727746263184; 1001,1485451761104 +164,39935401074132; 1001,1485451761104 +164,82593339516424; 1001,1485451761104 +165,25251277958716; 1001,1485451761104 +165,6790921640101; 1001,1485451761104 +166,105671548433; 1001,1485451761104 +166,53225093285593; 1001,1485451761104 +166,95883031727885; 1001,1485451761104 +167,38540970170178; 1001,1485451761104 +167,8119890861247; 1001,1485451761104 +168,23856847054762; 1001,1485451761104 +168,66514785497054; 1001,1485451761104 +169,09172723939346; 1001,1485451761104 +169,5183066238164; 1001,1485451761104 +169,9448860082393; 1001,1485451761104 +170,37146539266223; 1001,1485451761104 +170,79804477708518; 1001,1485451761104 +171,2246241615081; 1001,1485451761104 +171,65120354593103; 1001,1485451761104 +172,07778293035395; 1001,1485451761104 +172,50436231477687; 1001,1485451761104 +172,9309416991998; 1001,1485451761104 +173,35752108362271; 1001,1485451761104 +173,78410046804564; 1001,1485451761104 +174,21067985246856; 1001,1485451761104 +174,63725923689148; 1001,1485451761104 +175,0638386213144; 1001,1485451761104 +175,49041800573733; 1001,1485451761104 +175,91699739016025; 1001,1485451761104 +176,34357677458317; 1001,1485451761104 +176,7701561590061; 1001,1485451761104 +177,196735543429; 1001,1485451761104 +177,62331492785194; 1001,1485451761104 +178,04989431227486; 1001,1485451761104 +178,47647369669778; 1001,1485451761104 +178,9030530811207; 1001,1485451761104 +179,32963246554363; 1001,1485451761104 +179,75621184996655; 1001,1485451761104 +180,18279123438947; 1001,1485451761104 +180,60937061881242; 1001,1485451761104 +181,03595000323534; 1001,1485451761104 +181,46252938765826; 1001,1485451761104 +181,8891087720812; 1001,1485451761104 +182,3156881565041; 1001,1485451761104 +182,74226754092703; 1001,1485451761104 +183,16884692534995; 1001,1485451761104 +183,59542630977288; 1001,1485451761104 +184,0220056941958; 1001,1485451761104 +184,44858507861872; 1001,1485451761104 +184,87516446304164; 1001,1485451761104 +185,30174384746456; 1001,1485451761104 +185,7283232318875; 1001,1485451761104 +186,1549026163104; 1001,1485451761104 +186,58148200073333; 1001,1485451761104 +187,00806138515625; 1001,1485451761104 +187,43464076957918; 1001,1485451761104 +187,8612201540021; 1001,1485451761104 +188,28779953842502; 1001,1485451761104 +188,71437892284794; 1001,1485451761104 +189,14095830727086; 1001,1485451761104 +189,5675376916938; 1001,1485451761104 +189,9941170761167; 1001,1485451761104 +190,42069646053966; 1001,1485451761104 +190,84727584496258; 1001,1485451761104 +191,2738552293855; 1001,1485451761104 +191,70043461380843; 1001,1485451761104 +192,12701399823135; 1001,1485451761104 +192,55359338265427; 1001,1485451761104 +192,9801727670772; 1001,1485451761104 +193,40675215150011; 1001,1485451761104 +193,83333153592304; 1001,1485451761104 +194,25991092034596; 1001,1485451761104 +194,68649030476888; 1001,1485451761104 +195,1130696891918; 1001,1485451761104 +195,53964907361473; 1001,1485451761104 +195,96622845803765; 1001,1485451761104 +196,39280784246057; 1001,1485451761104 +196,8193872268835; 1001,1485451761104 +197,24596661130641; 1001,1485451761104 +197,67254599572934; 1001,1485451761104 +198,09912538015226; 1001,1485451761104 +198,52570476457518; 1001,1485451761104 +198,9522841489981; 1001,1485451761104 +199,37886353342103; 1001,1485451761104 +199,80544291784398; 1001,1485451761104 +200,08982917412592; 1001,1485451761104 diff --git a/winisd/tops.wpr b/winisd/tops.wpr new file mode 100644 index 0000000..151db9e --- /dev/null +++ b/winisd/tops.wpr @@ -0,0 +1,148 @@ +[ProjectInfo] +Description= +Creator=Silas +CreateDate=20260513 +ModifyDate=20260514 + +[Driver] +Brand=B&C Speaker +Model=14NDL76 +Manufacturer= +ProvidedBy=loudspeakerdatabase.com +Comment=loudspeakerdatabase.com/BC/14NDL76 +DateAdded=20260513 +DateModified=20260513 +Qts=0,3 +Znom=8 +Fs=41 +Pe=1000 +SPL=0 +Re=5 +Le=0,0011 +fLe=0 +KLe=0 +BL=19 +Xmax=0,008 +Cms=0,000173 +Qms=8,2 +Qes=0,31 +Rms=2,67 +Mms=0,085 +Sd=0,0707 +Vas=0,123 +Dia=0 +Vd=0,0005656 +no=0 +Dd=0,3 +EBP=0 +numVC=1 +Hc=0 +Hg=0 +SPLmax=0 +SPLmaxLF=0 +USPL=0 +alfaVC=0,0039 +Rt=0 +Ct=0 +gamma=0 +Rme=0 +Mpow=0 +Mcost=0 +Gloss=0 +VCCon=2 +c=343,684120962153 +roo=1,20095217714682 +Thick=0 +Depth=0 +MagDepth=0 +Magnet=0 +Basket=0 +Outer=0 +Vcd=0 +DVol=0 +ParState=EEECEENNEENEEEEEEEEEEECENNCCCNNNCCCCECNNNNNNNNECC + +[Box] +BType=1 +Vf=0 +Ff=0 +Qlf=10 +Qaf=100 +Qpf=100 +Vr=0,062 +Fr=47 +Qlr=10 +Qar=100 +Qpr=100 +Vc=0 +Fc=0 +Qlc=0 +Qac=0 +Qpc=0 +Qiclfr=100 +Qiclfc=0 +Qiclcr=0 +T=293,15 +p=101325 +phi=0,3 +d=1 +Med=0 +Nd=1 +Angle=0 +Isobarik=0 +alfaVC=0,0039 +dTVC=0 +Sdfport=0 +Sdrport=0,0117 + +[VentFront] +Num=1 +Shape=1 +Fb=0 +Vb=0 +dia1=0,102 +dia2=0,102 +carea=0 +len=0 +endcorrection=0,732 +crosscalc=1 + +[VentRear] +Num=2 +Shape=2 +Fb=47 +Vb=0,062 +dia1=0,13 +dia2=0,045 +carea=0,00585 +len=0,19242329054228 +endcorrection=0,732 +crosscalc=1 + +[VentIntra] +Num=1 +Shape=1 +Fb=0 +Vb=0 +dia1=0,102 +dia2=0,102 +carea=0 +len=0 +endcorrection=0,732 +crosscalc=1 + +[PlotSettings] +Color=16744448 +Width=1 + +[SignalSource] +Rg=0,1 +P=10,082 + +[Filters] +Count=0 + +[SimulatorOptions] +VCInd=0 +FlatResponse=0 +TLPorts=0