From 7dd84144f1bc52254e4d27ef42b43d6a75660d39 Mon Sep 17 00:00:00 2001 From: nmstudioo Date: Fri, 13 Nov 2026 00:16:09 +0600 Subject: [PATCH 1/2] feat(dsp): integrate NextGEN SLSQP fitter into DSP CI job --- .github/workflows/ci.yml | 7 + build/generator/fit_nextgen.py | 505 ++++++++++++++++++ .../data/Raw/kz_edx_pro_raw_nextgen.txt | 384 +++++++++++++ 3 files changed, 896 insertions(+) create mode 100644 build/generator/fit_nextgen.py create mode 100755 build/generator/inputs/data/Raw/kz_edx_pro_raw_nextgen.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c3e81b..271555c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,6 +72,13 @@ jobs: run: python3 build/build.py --out /tmp/rel - name: Validate acceptance result (fresh or restored from cache) run: python3 tests/check_acceptance.py /tmp/rel/acceptance.json + + - name: NextGEN IEM VDC fitter + run: python3 build/generator/fit_nextgen.py + - name: Upload NextGEN candidate artifact + uses: actions/upload-artifact@v7 + with: { name: nextgen-candidate, path: build/generator/out/iem_nextgen/, if-no-files-found: ignore } + - name: Generator reproduction vs releases/5.0.0 (informational) if: needs.prep.outputs.gen == 'true' continue-on-error: true diff --git a/build/generator/fit_nextgen.py b/build/generator/fit_nextgen.py new file mode 100644 index 0000000..6c8430c --- /dev/null +++ b/build/generator/fit_nextgen.py @@ -0,0 +1,505 @@ +#!/usr/bin/env python3 +"""Fit a candidate 10-section IEM NextGEN VDC against the v7 IEM IRS. + +This script is intentionally candidate-only: +- it reads src/profiles/iem-hd/EDX_HD.irs without modifying it; +- it uses the existing target_iem() median-alignment convention; +- it fits independent 48 kHz and 44.1 kHz VDC banks; +- it constrains every biquad's pole radius during optimization; +- it writes only to build/generator/out/iem_nextgen/ unless --out is supplied. + +Run from any working directory: + python3 build/generator/fit_nextgen.py + +Dependencies are the repository's existing NumPy/SciPy requirements. +""" +from __future__ import annotations + +import argparse +import hashlib +import json +import os +import sys +from pathlib import Path +from typing import Any + +import numpy as np +from scipy.optimize import least_squares, minimize + +HERE = Path(__file__).resolve().parent +ROOT = HERE.parents[1] +sys.path.insert(0, str(HERE)) +import lib as L # noqa: E402 + +# Do not silently fit a different or modified IRS. +EXPECTED_V7_IRS_SHA256 = "cf4aa68cbc005b50960d6c91392829f6fa3bbf7f385bf1c54a8f948968cc0620" +POLE_LIMIT = 0.985 +# Optimize a little inside the release limit to leave room for 9-decimal VDC output. +POLE_OPT_LIMIT = 0.9848 +N_SECTIONS = 10 +F_MIN = 30.0 +F_MAX = 16000.0 +MAX_BANK_DIFFERENCE_DB = 0.3 +MID_BAND = (300.0, 3000.0) + +IRS_PATH = ROOT / "src/profiles/iem-hd/EDX_HD.irs" +RAW_PATH = HERE / "inputs/data/Raw/kz_edx_pro_raw_nextgen.txt" +TARGET_PATH = HERE / "inputs/data/target/kz_edx_pro_target.txt" +DEFAULT_OUT = HERE / "out/iem_nextgen/EDX_HD_NextGEN_candidate.vdc" + + +def sha256(path: Path) -> str: + h = hashlib.sha256() + with path.open("rb") as f: + for block in iter(lambda: f.read(1024 * 1024), b""): + h.update(block) + return h.hexdigest() + + +def require_file(path: Path, purpose: str) -> None: + if not path.is_file(): + raise FileNotFoundError( + f"Missing {purpose}: {path}\n" + "Place the expected input in the repository path shown above; " + "the fitter will not substitute another measurement." + ) + + +def load_two_column(path: Path, *, delimiter: str | None, skiprows: int = 0) -> np.ndarray: + data = np.loadtxt(path, delimiter=delimiter, comments="#", skiprows=skiprows, dtype=float) + data = np.atleast_2d(data) + if data.shape[1] < 2: + raise ValueError(f"Expected at least two columns in {path}; got shape {data.shape}") + data = data[:, :2] + if not np.all(np.isfinite(data)): + raise ValueError(f"Non-finite values in {path}") + freq = data[:, 0] + if np.any(freq <= 0) or np.any(np.diff(freq) <= 0): + raise ValueError(f"Frequencies in {path} must be positive and strictly increasing") + if freq[0] > L.f[0] or freq[-1] < L.f[-1]: + raise ValueError( + f"{path} must cover the full {L.f[0]:g}-{L.f[-1]:g} Hz fitting grid; " + f"found {freq[0]:g}-{freq[-1]:g} Hz" + ) + return data + + +def interpolate_curve(data: np.ndarray) -> np.ndarray: + return np.interp(np.log(L.f), np.log(data[:, 0]), data[:, 1]) + + +def ear_response(irs: np.ndarray) -> np.ndarray: + """Match build_v5.py's IEM earM(): H[0] + H[2] for 4-channel IRS.""" + if irs.ndim != 2 or irs.shape[1] not in (1, 2, 4): + raise ValueError(f"Unexpected IRS channel shape: {irs.shape}") + h = [L.Hm(irs[:, channel]) for channel in range(irs.shape[1])] + return h[0] + h[2] if irs.shape[1] == 4 else h[0] + + +def target_iem_nextgen(irs_response: np.ndarray, raw_db: np.ndarray, target_db: np.ndarray) -> np.ndarray: + """Reproduce build_v5.py target_iem() exactly, using the NextGEN raw curve.""" + correction = target_db - raw_db - L.dB(irs_response) + mid = (L.f >= MID_BAND[0]) & (L.f <= MID_BAND[1]) + correction = correction - np.median(correction[mid]) + hi = L.f > 16000.0 + below_16k = np.flatnonzero(L.f <= 16000.0) + correction[hi] = correction[below_16k[-1]] + return correction + + +def pole_margins(ty: list[str], params: np.ndarray, fs: int, radius: float) -> np.ndarray: + """Jury stability margins for every section with all poles inside radius. + + lib.bq() returns denominator terms d1,d2 used as + 1 - d1*z - d2*z**2 + so the pole polynomial is z**2 - d1*z - d2. Applying the second-order + Jury conditions after scaling the pole radius by ``radius`` gives the + four inequalities returned here. All must be positive. + """ + margins: list[float] = [] + for i, kind in enumerate(ty): + fc, gain, q = params[3 * i:3 * i + 3] + section = L.bq(kind, float(fc), float(gain), float(q), fs) + d1, d2 = float(section[3]), float(section[4]) + r2 = radius * radius + margins.extend((r2 - d1 * radius - d2, + r2 + d1 * radius - d2, + r2 - d2, + r2 + d2)) + return np.asarray(margins, dtype=float) / (radius * radius) + + +def pole_radii(sections: np.ndarray) -> list[float]: + result = [] + for section in sections: + roots = np.roots([1.0, -float(section[3]), -float(section[4])]) + result.append(float(np.max(np.abs(roots)))) + return result + + +def constrained_refine( + ty: list[str], + start: list[float], + lower: list[float], + upper: list[float], + target: np.ndarray, + weight: np.ndarray, + fs: int, +) -> np.ndarray: + lo = np.asarray(lower, dtype=float) + hi = np.asarray(upper, dtype=float) + p0 = np.clip(np.asarray(start, dtype=float), lo + 1e-9, hi - 1e-9) + if np.any(hi <= lo): + raise ValueError("Invalid optimizer parameter bounds") + + def residual(p: np.ndarray) -> np.ndarray: + tonal = (L.resp_db(L.mkS(ty, p, fs), L.f, fs) - target) * weight + gain_regularizer = 0.03 * np.asarray(p)[1::3] + return np.concatenate((tonal, gain_regularizer)) + + # Match the existing fitter's least-squares objective to obtain a good + # unconstrained starting point; the following SLSQP pass enforces stability. + seed_fit = least_squares( + residual, + p0, + bounds=(lo, hi), + max_nfev=400, + x_scale="jac", + method="trf", + ) + seed = np.asarray(seed_fit.x, dtype=float) + span = hi - lo + + def unpack(y: np.ndarray) -> np.ndarray: + return lo + np.asarray(y, dtype=float) * span + + def objective(y: np.ndarray) -> float: + r = residual(unpack(y)) + return 0.5 * float(np.dot(r, r)) + + def stability(y: np.ndarray) -> np.ndarray: + return pole_margins(ty, unpack(y), fs, POLE_OPT_LIMIT) + + y0 = np.clip((seed - lo) / span, 1e-9, 1.0 - 1e-9) + opt = minimize( + objective, + y0, + method="SLSQP", + jac="2-point", + bounds=[(0.0, 1.0)] * len(y0), + constraints=[{"type": "ineq", "fun": stability}], + options={"maxiter": 1000, "ftol": 1e-9, "disp": False}, + ) + fitted = unpack(opt.x) + margins = pole_margins(ty, fitted, fs, POLE_OPT_LIMIT) + feasible = bool(np.all(np.isfinite(fitted)) and np.min(margins) >= -1e-8) + if not opt.success or not feasible: + worst = float(np.min(margins)) if margins.size else float("nan") + raise RuntimeError( + f"Constrained fit failed at {fs} Hz: optimizer success={opt.success}, " + f"message={opt.message!s}, worst normalized pole margin={worst:.6g}" + ) + if not np.all(np.isfinite(L.resp_db(L.mkS(ty, fitted, fs), L.f, fs))): + raise RuntimeError(f"Non-finite response after constrained fit at {fs} Hz") + return fitted + + +def fit_bank( + correction: np.ndarray, + weight: np.ndarray, + fs: int, + *, + sections: int = N_SECTIONS, + warm: tuple[list[str], list[float], list[float], list[float]] | None = None, + gain_bounds: tuple[float, float] = (-12.0, 6.0), +) -> tuple[list[str], list[float], list[float], list[float]]: + if warm is None: + kinds = ["LS", "HS"] + params = [150.0, -2.0, 0.6, 6000.0, -2.0, 0.6] + lower = [20.0, -14.0, 0.3, 3000.0, -14.0, 0.3] + upper = [500.0, 8.0, 1.0, 16000.0, 8.0, 1.0] + else: + kinds = list(warm[0]) + params = list(warm[1]) + lower = list(warm[2]) + upper = list(warm[3]) + + params_np = constrained_refine(kinds, params, lower, upper, correction, weight, fs) + params = params_np.tolist() + + while len(kinds) < sections: + residual_db = correction - L.resp_db(L.mkS(kinds, params, fs), L.f, fs) + smoothed = np.convolve(residual_db, np.ones(7, dtype=float) / 7.0, mode="same") * weight + index = int(np.argmax(np.abs(smoothed))) + center = float(L.f[index]) + gain = float(np.clip(residual_db[index], -8.0, 5.0)) + kinds.append("PK") + params.extend((center, gain, 2.5)) + lower.extend((max(20.0, center / 1.6), gain_bounds[0], 0.5)) + upper.extend((min(19500.0, center * 1.6), gain_bounds[1], 6.0)) + params_np = constrained_refine(kinds, params, lower, upper, correction, weight, fs) + params = params_np.tolist() + + return kinds, params, lower, upper + + +def make_vdc_text(bank_44: np.ndarray, bank_48: np.ndarray, gain_db: float) -> str: + gain = 10.0 ** (gain_db / 20.0) + + def encode(bank: np.ndarray) -> str: + values = np.array(bank, dtype=float, copy=True) + if values.ndim != 2 or values.shape[1] != 5 or len(values) == 0: + raise ValueError("Invalid VDC bank shape") + values[0, :3] *= gain + return ",".join(f"{value:.9f}" for value in values.reshape(-1)) + + return f"SR_44100:{encode(bank_44)}\nSR_48000:{encode(bank_48)}\n" + + +def parse_vdc_text(text: str) -> tuple[np.ndarray, np.ndarray]: + parsed: dict[str, np.ndarray] = {} + for line in text.splitlines(): + if ":" not in line: + continue + key, values = line.split(":", 1) + if key not in ("SR_44100", "SR_48000"): + continue + vector = np.asarray([float(v) for v in values.split(",") if v.strip()], dtype=float) + if vector.size == 0 or vector.size % 5: + raise ValueError(f"Malformed {key} VDC line") + parsed[key] = vector.reshape(-1, 5) + if set(parsed) != {"SR_44100", "SR_48000"}: + raise ValueError("VDC output must contain both SR_44100 and SR_48000 banks") + if not all(np.all(np.isfinite(bank)) for bank in parsed.values()): + raise ValueError("VDC output contains non-finite coefficients") + return parsed["SR_44100"], parsed["SR_48000"] + + +def rms(values: np.ndarray) -> float: + return float(np.sqrt(np.mean(np.square(values)))) + + +def build_report( + raw_db: np.ndarray, + target_db: np.ndarray, + irs_db: np.ndarray, + bank_44: np.ndarray, + bank_48: np.ndarray, + gain_db: float, + raw_hash: str, + target_hash: str, + irs_hash: str, +) -> dict[str, Any]: + # The caller passes serialized banks whose first-section numerator + # already includes gain_db. Do not apply the normalization a second time. + v48 = L.resp_db(bank_48, L.f, 48000) + v44 = L.resp_db(bank_44, L.f, 44100) + chain48 = irs_db + v48 + chain44 = irs_db + v44 + predicted = raw_db + chain48 + absolute_error = predicted - target_db + mid = (L.f >= MID_BAND[0]) & (L.f <= MID_BAND[1]) + aligned_error = absolute_error - np.median(absolute_error[mid]) + evaluation = (L.f >= F_MIN) & (L.f <= F_MAX) + bands = [(20, 100), (100, 500), (500, 2000), (2000, 6000), (6000, 10000), (10000, 16000)] + band_report = [] + for low, high in bands: + mask = (L.f >= low) & (L.f < high) + band_report.append({ + "band_hz": [low, high], + "aligned_rms_db": rms(aligned_error[mask]), + "absolute_rms_db": rms(absolute_error[mask]), + }) + radii_44 = pole_radii(bank_44) + radii_48 = pole_radii(bank_48) + return { + "status": "candidate_validated_for_review", + "fit_convention": { + "method": "target_iem median alignment", + "median_alignment_hz": list(MID_BAND), + "above_16khz": "held at the last correction value at or below 16 kHz", + "absolute_reference_used_for_fit": False, + }, + "inputs": { + "raw_path": str(RAW_PATH.relative_to(ROOT)), + "raw_sha256": raw_hash, + "target_path": str(TARGET_PATH.relative_to(ROOT)), + "target_sha256": target_hash, + "irs_path": str(IRS_PATH.relative_to(ROOT)), + "irs_sha256": irs_hash, + "irs_preserved_without_rescaling_or_peakfix": True, + }, + "optimizer": { + "sections": int(len(bank_48)), + "pole_radius_limit": POLE_LIMIT, + "optimization_pole_radius_limit": POLE_OPT_LIMIT, + "max_44khz_vs_48khz_bank_difference_db": MAX_BANK_DIFFERENCE_DB, + "sample_rates_hz": [44100, 48000], + }, + "normalization": { + "gain_db_applied_to_vdc": float(gain_db), + "chain_peak_48khz_db_30_16k": float(np.max(chain48[evaluation])), + "chain_peak_44khz_db_30_16k": float(np.max(chain44[evaluation])), + "target_chain_peak_db": -0.5, + }, + "pole_radii": { + "sr_44100_max": float(max(radii_44)), + "sr_48000_max": float(max(radii_48)), + "sr_44100_per_section": radii_44, + "sr_48000_per_section": radii_48, + }, + "response_error_db": { + "absolute_reference_rms_30_16k": rms(absolute_error[evaluation]), + "absolute_reference_max_abs_30_16k": float(np.max(np.abs(absolute_error[evaluation]))), + "median_aligned_rms_30_16k": rms(aligned_error[evaluation]), + "median_aligned_max_abs_30_16k": float(np.max(np.abs(aligned_error[evaluation]))), + "absolute_error_median_300_3000_hz": float(np.median(absolute_error[mid])), + "bank_difference_max_abs_db_30_20k": float(np.max(np.abs(v44 - v48))), + "bands": band_report, + }, + } + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--raw", type=Path, default=RAW_PATH, help="NextGEN raw response (whitespace-delimited frequency dB)") + parser.add_argument("--target", type=Path, default=TARGET_PATH, help="KZ EDX Pro target CSV already in the repository") + parser.add_argument("--irs", type=Path, default=IRS_PATH, help="v7 EDX_HD.irs (must match the pinned SHA-256)") + parser.add_argument("--out", type=Path, default=DEFAULT_OUT, help="candidate VDC output path (never writes into src/profiles by default)") + args = parser.parse_args() + + require_file(args.raw, "NextGEN raw response") + require_file(args.target, "IEM target curve") + require_file(args.irs, "v7 IEM IRS") + + irs_hash = sha256(args.irs) + if irs_hash != EXPECTED_V7_IRS_SHA256: + raise RuntimeError( + "The selected IRS is not the pinned v7 IEM HD IRS.\n" + f"Expected SHA-256: {EXPECTED_V7_IRS_SHA256}\n" + f"Actual SHA-256: {irs_hash}\n" + "No candidate was written. Do not substitute the old v4 generator IRS." + ) + + raw_data = load_two_column(args.raw, delimiter=None) + # The checked-in target starts with a comment line, then 'frequency,target'. + target_data = load_two_column(args.target, delimiter=",", skiprows=2) + raw_db = interpolate_curve(raw_data) + target_db = interpolate_curve(target_data) + + irs, fmt, _extra, _file_size, _data_size, _data_len = L.rdirs(str(args.irs)) + tag, channels, sample_rate, _byte_rate, _block_align, bits = fmt + if (tag, channels, sample_rate, bits) != (3, 4, 48000, 32): + raise RuntimeError( + "Unexpected v7 IRS WAV format; expected IEEE Float32, 4-channel, 48 kHz. " + f"Found format={tag}, channels={channels}, sample_rate={sample_rate}, bits={bits}." + ) + if irs.shape[0] == 0 or not np.all(np.isfinite(irs)): + raise RuntimeError("The v7 IRS contains no usable samples or non-finite values") + + # IMPORTANT: no scale_to_maxgain() and no peakfix(). The released v7 IRS is + # used exactly as-is; this fitter never rewrites or rescales it. + irs_response = ear_response(irs) + irs_db = L.dB(irs_response) + correction = target_iem_nextgen(irs_response, raw_db, target_db) + weight = np.where(L.f < 30.0, 0.3, np.where(L.f <= 16000.0, 1.0, 0.3)) + + # Fit 48 kHz first using the existing greedy section-placement strategy, + # now with explicit pole-radius constraints. + fit48 = fit_bank(correction, weight, 48000) + types48, params48 = fit48[0], np.asarray(fit48[1], dtype=float) + bank48 = np.asarray(L.mkS(types48, params48, 48000), dtype=float) + + # Preserve build_vdc()'s 44.1 kHz approach: warm-start from the 48 kHz + # solution and fit the 44.1 kHz bank toward the 48 kHz bank response. + correction44 = L.resp_db(bank48, L.f, 48000) + weight44 = np.where(L.f > 19000.0, 0.2, 1.0) + fit44 = fit_bank(correction44, weight44, 44100, warm=fit48) + types44, params44 = fit44[0], np.asarray(fit44[1], dtype=float) + bank44 = np.asarray(L.mkS(types44, params44, 44100), dtype=float) + + # Validate nominal-rate pole radii before gain normalization or file output. + radii44 = pole_radii(bank44) + radii48 = pole_radii(bank48) + if max(radii44) > POLE_LIMIT or max(radii48) > POLE_LIMIT: + raise RuntimeError( + "Pole-radius release gate failed; no candidate was written. " + f"44.1 kHz max={max(radii44):.9f}, 48 kHz max={max(radii48):.9f}, " + f"limit={POLE_LIMIT:.6f}." + ) + + # Normalize against the worse of the two sample-rate banks so both chains + # peak at or below -0.5 dB over the established 30 Hz–16 kHz evaluation band. + evaluation = (L.f >= F_MIN) & (L.f <= F_MAX) + unnormalized48 = irs_db + L.resp_db(bank48, L.f, 48000) + unnormalized44 = irs_db + L.resp_db(bank44, L.f, 44100) + worst_chain_peak = max(float(np.max(unnormalized48[evaluation])), + float(np.max(unnormalized44[evaluation]))) + gain_db = -0.5 - worst_chain_peak + + vdc_text = make_vdc_text(bank44, bank48, gain_db) + encoded44, encoded48 = parse_vdc_text(vdc_text) + # Recheck serialized (9-decimal) coefficients, not just in-memory doubles. + final_radii44 = pole_radii(encoded44) + final_radii48 = pole_radii(encoded48) + if max(final_radii44) > POLE_LIMIT or max(final_radii48) > POLE_LIMIT: + raise RuntimeError("Serialized VDC pole-radius gate failed; no candidate was written") + + report = build_report( + raw_db, target_db, irs_db, encoded44, encoded48, gain_db, + sha256(args.raw), sha256(args.target), irs_hash, + ) + if (report["normalization"]["chain_peak_48khz_db_30_16k"] > 0.0 or + report["normalization"]["chain_peak_44khz_db_30_16k"] > 0.0): + raise RuntimeError("Serialized chain headroom gate failed; no candidate was written") + if report["response_error_db"]["bank_difference_max_abs_db_30_20k"] > MAX_BANK_DIFFERENCE_DB: + raise RuntimeError( + "44.1/48 kHz bank matching gate failed; no candidate was written. " + f"Maximum difference={report['response_error_db']['bank_difference_max_abs_db_30_20k']:.4f} dB, " + f"limit={MAX_BANK_DIFFERENCE_DB:.3f} dB." + ) + + out_path = args.out if args.out.is_absolute() else (ROOT / args.out).resolve() + profiles_root = (ROOT / "src/profiles").resolve() + try: + out_path.relative_to(profiles_root) + except ValueError: + pass + else: + raise RuntimeError( + "Refusing to write a candidate inside src/profiles/. " + "Use the default build/generator/out/ path and promote files only after review." + ) + report_path = out_path.with_suffix(".json") + out_path.parent.mkdir(parents=True, exist_ok=True) + tmp_vdc = out_path.with_suffix(out_path.suffix + ".tmp") + tmp_json = report_path.with_suffix(report_path.suffix + ".tmp") + try: + tmp_vdc.write_text(vdc_text, encoding="utf-8", newline="\n") + tmp_json.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8", newline="\n") + os.replace(tmp_vdc, out_path) + os.replace(tmp_json, report_path) + finally: + for temp in (tmp_vdc, tmp_json): + if temp.exists(): + temp.unlink() + + print("IEM NextGEN candidate generated and validated for review.") + print(f"VDC: {out_path}") + print(f"Report: {report_path}") + print(f"Pole radius max: 44.1 kHz={max(final_radii44):.6f}, 48 kHz={max(final_radii48):.6f} (limit {POLE_LIMIT:.3f})") + print(f"Chain peak: 44.1 kHz={report['normalization']['chain_peak_44khz_db_30_16k']:.3f} dB, " + f"48 kHz={report['normalization']['chain_peak_48khz_db_30_16k']:.3f} dB") + print(f"Median-aligned RMS error, 30 Hz–16 kHz: {report['response_error_db']['median_aligned_rms_30_16k']:.3f} dB") + print(f"Absolute-reference RMS error, 30 Hz–16 kHz (diagnostic only): " + f"{report['response_error_db']['absolute_reference_rms_30_16k']:.3f} dB") + print("No release profile, v7 IRS, v7 VDC, or v7 EEL file was modified.") + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except Exception as exc: + print(f"ERROR: {exc}", file=sys.stderr) + raise SystemExit(1) diff --git a/build/generator/inputs/data/Raw/kz_edx_pro_raw_nextgen.txt b/build/generator/inputs/data/Raw/kz_edx_pro_raw_nextgen.txt new file mode 100755 index 0000000..4eaeecb --- /dev/null +++ b/build/generator/inputs/data/Raw/kz_edx_pro_raw_nextgen.txt @@ -0,0 +1,384 @@ +20 7.99 +20.36 7.99 +20.73 8 +21.11 8 +21.5 8 +21.89 8.01 +22.29 8.01 +22.69 8.01 +23.1 8.01 +23.52 8.02 +23.95 8.02 +24.39 8.02 +24.83 8.02 +25.28 8.02 +25.74 8.02 +26.21 8.02 +26.69 8.01 +27.18 8.01 +27.67 8.01 +28.17 8 +28.69 8 +29.21 7.99 +29.74 7.98 +30.28 7.97 +30.83 7.96 +31.39 7.95 +31.97 7.94 +32.55 7.93 +33.14 7.91 +33.74 7.9 +34.36 7.88 +34.98 7.86 +35.62 7.84 +36.27 7.82 +36.93 7.8 +37.6 7.78 +38.28 7.75 +38.98 7.72 +39.69 7.7 +40.41 7.67 +41.15 7.64 +41.9 7.6 +42.66 7.57 +43.44 7.54 +44.23 7.5 +45.03 7.46 +45.85 7.42 +46.68 7.38 +47.53 7.34 +48.4 7.3 +49.28 7.26 +50.18 7.21 +51.09 7.16 +52.02 7.12 +52.97 7.07 +53.93 7.01 +54.91 6.96 +55.91 6.91 +56.93 6.86 +57.97 6.8 +59.02 6.74 +60.09 6.68 +61.19 6.62 +62.3 6.56 +63.44 6.5 +64.59 6.44 +65.77 6.37 +66.96 6.31 +68.18 6.24 +69.42 6.18 +70.69 6.11 +71.97 6.04 +73.28 5.97 +74.62 5.9 +75.97 5.83 +77.36 5.76 +78.76 5.69 +80.2 5.61 +81.66 5.54 +83.14 5.46 +84.66 5.39 +86.2 5.31 +87.77 5.22 +89.36 5.14 +90.99 5.06 +92.65 4.97 +94.33 4.88 +96.05 4.79 +97.8 4.7 +99.58 4.6 +101.39 4.51 +103.23 4.41 +105.11 4.31 +107.03 4.21 +108.97 4.11 +110.96 4.02 +112.98 3.92 +115.03 3.82 +117.13 3.73 +119.26 3.63 +121.43 3.54 +123.64 3.44 +125.89 3.34 +128.18 3.24 +130.51 3.14 +132.89 3.04 +135.31 2.94 +137.77 2.83 +140.28 2.73 +142.83 2.62 +145.43 2.51 +148.07 2.4 +150.77 2.3 +153.51 2.19 +156.31 2.08 +159.15 1.97 +162.05 1.86 +165 1.75 +168 1.65 +171.06 1.54 +174.17 1.43 +177.34 1.33 +180.57 1.22 +183.86 1.11 +187.2 1 +190.61 0.9 +194.08 0.79 +197.61 0.68 +201.21 0.57 +204.87 0.45 +208.6 0.34 +212.39 0.22 +216.26 0.11 +220.19 -0.01 +224.2 -0.13 +228.28 -0.24 +232.44 -0.36 +236.67 -0.47 +240.97 -0.58 +245.36 -0.69 +249.82 -0.8 +254.37 -0.91 +259 -1.01 +263.71 -1.1 +268.51 -1.2 +273.4 -1.29 +278.38 -1.38 +283.44 -1.47 +288.6 -1.56 +293.85 -1.65 +299.2 -1.75 +304.65 -1.85 +310.19 -1.95 +315.84 -2.05 +321.59 -2.16 +327.44 -2.27 +333.4 -2.37 +339.47 -2.47 +345.64 -2.58 +351.93 -2.68 +358.34 -2.78 +364.86 -2.88 +371.5 -2.98 +378.26 -3.08 +385.15 -3.18 +392.16 -3.27 +399.29 -3.36 +406.56 -3.45 +413.96 -3.53 +421.49 -3.62 +429.16 -3.7 +436.97 -3.79 +444.93 -3.87 +453.02 -3.95 +461.27 -4.03 +469.66 -4.11 +478.21 -4.19 +486.91 -4.27 +495.78 -4.34 +504.8 -4.41 +513.99 -4.49 +523.34 -4.56 +532.87 -4.63 +542.56 -4.69 +552.44 -4.76 +562.49 -4.82 +572.73 -4.88 +583.15 -4.94 +593.77 -5 +604.57 -5.05 +615.57 -5.11 +626.78 -5.15 +638.18 -5.2 +649.8 -5.24 +661.63 -5.28 +673.67 -5.32 +685.93 -5.35 +698.41 -5.38 +711.12 -5.41 +724.06 -5.44 +737.24 -5.46 +750.66 -5.48 +764.32 -5.49 +778.23 -5.5 +792.39 -5.5 +806.82 -5.5 +821.5 -5.48 +836.45 -5.46 +851.67 -5.43 +867.17 -5.39 +882.96 -5.33 +899.02 -5.27 +915.39 -5.2 +932.05 -5.11 +949.01 -5.02 +966.28 -4.92 +983.87 -4.81 +1001.77 -4.69 +1020 -4.57 +1038.57 -4.43 +1057.47 -4.28 +1076.71 -4.12 +1096.31 -3.96 +1116.26 -3.79 +1136.58 -3.62 +1157.26 -3.44 +1178.32 -3.25 +1199.77 -3.07 +1221.6 -2.89 +1243.84 -2.7 +1266.47 -2.51 +1289.52 -2.32 +1312.99 -2.13 +1336.89 -1.93 +1361.22 -1.73 +1385.99 -1.52 +1411.22 -1.3 +1436.9 -1.06 +1463.05 -0.81 +1489.68 -0.54 +1516.79 -0.25 +1544.4 0.05 +1572.5 0.36 +1601.12 0.68 +1630.26 1 +1659.93 1.34 +1690.14 1.67 +1720.9 2.02 +1752.22 2.37 +1784.11 2.73 +1816.58 3.1 +1849.64 3.47 +1883.3 3.84 +1917.58 4.2 +1952.48 4.55 +1988.01 4.89 +2024.19 5.2 +2061.03 5.48 +2098.54 5.72 +2136.73 5.9 +2175.62 6.02 +2215.22 6.07 +2255.53 6.05 +2296.58 5.96 +2338.38 5.8 +2380.94 5.58 +2424.27 5.32 +2468.39 5.01 +2513.31 4.68 +2559.05 4.33 +2605.63 3.97 +2653.05 3.62 +2701.33 3.27 +2750.5 2.95 +2800.55 2.64 +2851.52 2.35 +2903.42 2.09 +2956.26 1.86 +3010.06 1.66 +3064.85 1.48 +3120.62 1.33 +3177.42 1.22 +3235.25 1.13 +3294.13 1.07 +3354.08 1.05 +3415.12 1.06 +3477.27 1.1 +3540.56 1.18 +3605 1.29 +3670.6 1.43 +3737.41 1.6 +3805.43 1.81 +3874.68 2.05 +3945.2 2.33 +4017 2.65 +4090.11 3.03 +4164.55 3.46 +4240.34 3.94 +4317.51 4.48 +4396.09 5.07 +4476.1 5.68 +4557.56 6.3 +4640.5 6.86 +4724.96 7.32 +4810.95 7.63 +4898.51 7.71 +4987.66 7.54 +5078.43 7.08 +5170.86 6.36 +5264.97 5.42 +5360.79 4.33 +5458.35 3.22 +5557.69 2.23 +5658.84 1.5 +5761.82 1.12 +5866.69 1 +5973.46 0.95 +6082.17 0.83 +6192.87 0.6 +6305.57 0.3 +6420.33 0 +6537.18 -0.24 +6656.15 -0.37 +6777.29 -0.34 +6900.63 -0.12 +7026.22 0.31 +7154.1 0.95 +7284.3 1.79 +7416.87 2.76 +7551.85 3.76 +7689.29 4.63 +7829.23 5.2 +7971.72 5.32 +8116.8 4.88 +8264.53 3.93 +8414.94 2.58 +8568.09 1.01 +8724.02 -0.61 +8882.79 -2.18 +9044.46 -3.59 +9209.06 -4.82 +9376.66 -5.83 +9547.31 -6.63 +9721.07 -7.22 +9897.99 -7.57 +10078.13 -7.67 +10261.55 -7.5 +10448.3 -7.01 +10638.45 -6.17 +10832.07 -4.99 +11029.21 -3.51 +11229.94 -1.87 +11434.32 -0.31 +11642.41 0.85 +11854.3 1.27 +12070.04 0.75 +12289.71 -0.6 +12513.38 -2.46 +12741.12 -4.45 +12973 -6.28 +13209.1 -7.72 +13449.5 -8.66 +13694.28 -9.06 +13943.51 -8.91 +14197.27 -8.28 +14455.66 -7.26 +14718.74 -6.03 +14986.62 -4.75 +15259.37 -3.55 +15537.08 -2.47 +15819.85 -1.65 +16107.76 -1.41 +16400.92 -1.94 +16699.41 -3.3 +17003.33 -5.49 +17312.78 -8.43 +17627.86 -11.92 +17948.68 -15.65 +18275.34 -19.3 +18607.94 -22.72 +18946.6 -25.83 +19291.42 -28.72 +19642.52 -31.5 +20000 -34.28 \ No newline at end of file From 62b06e368d609d2485a063283ec0d701a50865fb Mon Sep 17 00:00:00 2001 From: nmstudioo Date: Fri, 13 Nov 2026 00:29:02 +0600 Subject: [PATCH 2/2] feat(dsp): fit NextGEN VDC against hybrid target (CSV, hardened) --- build/generator/fit_nextgen.py | 2 +- .../data/target/kz_edx_pro_hybrid_target.txt | 1082 +++++++++++++++++ 2 files changed, 1083 insertions(+), 1 deletion(-) create mode 100644 build/generator/inputs/data/target/kz_edx_pro_hybrid_target.txt diff --git a/build/generator/fit_nextgen.py b/build/generator/fit_nextgen.py index 6c8430c..eb7a481 100644 --- a/build/generator/fit_nextgen.py +++ b/build/generator/fit_nextgen.py @@ -44,7 +44,7 @@ IRS_PATH = ROOT / "src/profiles/iem-hd/EDX_HD.irs" RAW_PATH = HERE / "inputs/data/Raw/kz_edx_pro_raw_nextgen.txt" -TARGET_PATH = HERE / "inputs/data/target/kz_edx_pro_target.txt" +TARGET_PATH = HERE / "inputs/data/target/kz_edx_pro_hybrid_target.txt" DEFAULT_OUT = HERE / "out/iem_nextgen/EDX_HD_NextGEN_candidate.vdc" diff --git a/build/generator/inputs/data/target/kz_edx_pro_hybrid_target.txt b/build/generator/inputs/data/target/kz_edx_pro_hybrid_target.txt new file mode 100644 index 0000000..d6935aa --- /dev/null +++ b/build/generator/inputs/data/target/kz_edx_pro_hybrid_target.txt @@ -0,0 +1,1082 @@ +# KZ EDX Pro Hybrid target +frequency,target +20.000000,6.835000 +20.200000,6.837211 +20.360000,6.839004 +20.400000,6.839455 +20.600000,6.846747 +20.730000,6.848101 +20.810000,6.848940 +21.010000,6.851307 +21.110000,6.852387 +21.220000,6.853580 +21.430000,6.855891 +21.500000,6.856672 +21.640000,6.858195 +21.860000,6.860381 +21.890000,6.860717 +22.070000,6.862739 +22.290000,6.865000 +22.510000,6.867239 +22.690000,6.868917 +22.740000,6.869385 +22.960000,6.871697 +23.100000,6.870000 +23.190000,6.868921 +23.420000,6.871182 +23.520000,6.872180 +23.650000,6.873479 +23.890000,6.875692 +23.950000,6.875000 +24.120000,6.871115 +24.360000,6.870676 +24.390000,6.870603 +24.610000,6.872489 +24.830000,6.870000 +24.850000,6.869776 +25.100000,6.871989 +25.280000,6.870000 +25.340000,6.868685 +25.600000,6.868024 +25.740000,6.867806 +25.850000,6.868822 +26.110000,6.866056 +26.210000,6.865000 +26.370000,6.861646 +26.630000,6.861240 +26.690000,6.860000 +26.890000,6.857948 +27.160000,6.860202 +27.180000,6.860000 +27.430000,6.854876 +27.670000,6.854447 +27.700000,6.854395 +27.970000,6.848979 +28.170000,6.845000 +28.250000,6.843450 +28.530000,6.843057 +28.690000,6.840000 +28.810000,6.838838 +29.100000,6.836050 +29.210000,6.835000 +29.390000,6.831584 +29.680000,6.831123 +29.740000,6.830000 +29.970000,6.823578 +30.270000,6.815275 +30.280000,6.815000 +30.570000,6.809705 +30.830000,6.805000 +30.870000,6.804280 +31.180000,6.803729 +31.390000,6.800000 +31.490000,6.798263 +31.800000,6.792912 +31.970000,6.790000 +32.120000,6.786095 +32.440000,6.777824 +32.550000,6.775000 +32.760000,6.771420 +33.080000,6.766009 +33.140000,6.765000 +33.410000,6.758217 +33.740000,6.755000 +34.080000,6.746740 +34.360000,6.740000 +34.410000,6.739187 +34.760000,6.733528 +34.980000,6.730000 +35.100000,6.727167 +35.450000,6.718958 +35.620000,6.715000 +35.800000,6.710819 +36.160000,6.702519 +36.270000,6.700000 +36.510000,6.692685 +36.880000,6.681503 +36.930000,6.680000 +37.240000,6.673026 +37.600000,6.665000 +37.610000,6.664703 +37.980000,6.653779 +38.280000,6.645000 +38.360000,6.643272 +38.740000,6.635112 +38.980000,6.630000 +39.130000,6.625744 +39.510000,6.615036 +39.690000,6.610000 +39.910000,6.603851 +40.300000,6.593032 +40.410000,6.590000 +40.700000,6.582119 +41.100000,6.566340 +41.150000,6.565000 +41.510000,6.552944 +41.900000,6.535243 +41.920000,6.534469 +42.340000,6.523377 +42.660000,6.511186 +42.760000,6.506769 +43.180000,6.488283 +43.440000,6.476971 +43.610000,6.469582 +44.040000,6.445972 +44.230000,6.437835 +44.480000,6.427139 +44.920000,6.408411 +45.030000,6.402546 +45.370000,6.384579 +45.820000,6.360907 +45.850000,6.359665 +46.270000,6.339752 +46.680000,6.316082 +46.730000,6.313517 +47.190000,6.289946 +47.530000,6.272756 +47.660000,6.265483 +48.130000,6.239252 +48.400000,6.224363 +48.610000,6.212792 +49.090000,6.186432 +49.280000,6.174166 +49.580000,6.153263 +50.070000,6.124244 +50.180000,6.117791 +50.570000,6.097077 +51.070000,6.065654 +51.090000,6.064606 +51.580000,6.036480 +52.020000,6.007050 +52.090000,6.002399 +52.610000,5.968189 +52.970000,5.948066 +53.130000,5.938283 +53.650000,5.901593 +53.930000,5.882203 +54.190000,5.865653 +54.720000,5.836737 +54.910000,5.824801 +55.270000,5.800517 +55.810000,5.763968 +55.910000,5.757310 +56.370000,5.726871 +56.930000,5.695000 +57.490000,5.655668 +57.970000,5.622359 +58.060000,5.616111 +58.640000,5.576193 +59.020000,5.553437 +59.220000,5.541527 +59.810000,5.501698 +60.090000,5.482863 +60.400000,5.462235 +61.000000,5.422715 +61.190000,5.410234 +61.600000,5.383284 +62.210000,5.343619 +62.300000,5.337084 +62.830000,5.296642 +63.440000,5.255241 +63.450000,5.254561 +64.080000,5.207063 +64.590000,5.169046 +64.720000,5.158892 +65.360000,5.108997 +65.770000,5.077362 +66.010000,5.059844 +66.660000,5.012521 +66.960000,4.990885 +67.320000,4.963667 +67.990000,4.908501 +68.180000,4.894392 +68.670000,4.856161 +69.350000,4.798358 +69.420000,4.792452 +70.040000,4.743025 +70.690000,4.696154 +70.730000,4.693109 +71.430000,4.635181 +71.970000,4.590963 +72.140000,4.577152 +72.850000,4.519576 +73.280000,4.485244 +73.580000,4.461472 +74.310000,4.398784 +74.620000,4.374354 +75.040000,4.339653 +75.790000,4.278600 +75.970000,4.263978 +76.540000,4.218203 +77.300000,4.152782 +77.360000,4.148043 +78.070000,4.091890 +78.760000,4.033103 +78.840000,4.026358 +79.620000,3.966039 +80.200000,3.917946 +80.410000,3.899853 +81.210000,3.836441 +81.660000,3.798089 +82.010000,3.769523 +82.830000,3.708519 +83.140000,3.683624 +83.650000,3.641372 +84.480000,3.578224 +84.660000,3.563546 +85.320000,3.509845 +86.160000,3.446802 +86.200000,3.443598 +87.020000,3.375659 +87.770000,3.318184 +87.880000,3.310117 +88.750000,3.241707 +89.360000,3.197644 +89.630000,3.177483 +90.520000,3.106487 +90.990000,3.071914 +91.420000,3.040442 +92.320000,2.974802 +92.650000,2.951004 +93.240000,2.908507 +94.160000,2.842528 +94.330000,2.830460 +95.100000,2.776257 +96.040000,2.710432 +96.050000,2.709736 +96.990000,2.644546 +97.800000,2.589107 +97.960000,2.578656 +98.930000,2.515416 +99.580000,2.473391 +99.910000,2.451225 +100.900000,2.390171 +101.390000,2.357719 +101.900000,2.324076 +102.910000,2.262947 +103.230000,2.242130 +103.930000,2.196916 +104.960000,2.135935 +105.110000,2.126379 +106.000000,2.072394 +107.030000,2.015379 +107.050000,2.014272 +108.120000,1.955515 +108.970000,1.905120 +109.190000,1.891641 +110.270000,1.830852 +110.960000,1.789146 +111.360000,1.766038 +112.470000,1.707554 +112.980000,1.680786 +113.580000,1.649382 +114.710000,1.585844 +115.030000,1.569366 +115.850000,1.527516 +117.000000,1.469297 +117.130000,1.462749 +118.160000,1.410993 +119.260000,1.351489 +119.330000,1.347722 +120.510000,1.289523 +121.430000,1.244649 +121.710000,1.231061 +122.910000,1.167983 +123.640000,1.133009 +124.130000,1.109648 +125.360000,1.051376 +125.890000,1.026496 +126.610000,0.992855 +127.860000,0.929706 +128.180000,0.914942 +129.130000,0.871307 +130.410000,0.812979 +130.510000,0.808053 +131.700000,0.752353 +132.890000,0.701824 +133.010000,0.696749 +134.330000,0.641181 +135.310000,0.596555 +135.660000,0.580680 +137.010000,0.524957 +137.770000,0.493799 +138.370000,0.470559 +139.740000,0.412817 +140.280000,0.392150 +141.120000,0.360116 +142.520000,0.302237 +142.830000,0.290617 +143.940000,0.249252 +145.360000,0.191601 +145.430000,0.189030 +146.810000,0.138502 +148.070000,0.088262 +148.260000,0.081097 +149.730000,0.031068 +150.770000,-0.007476 +151.220000,-0.024101 +152.720000,-0.079244 +153.510000,-0.105488 +154.230000,-0.127944 +155.760000,-0.180250 +156.310000,-0.198899 +157.310000,-0.234479 +158.870000,-0.294621 +159.150000,-0.304477 +160.440000,-0.345118 +162.030000,-0.394692 +162.050000,-0.395312 +163.640000,-0.447061 +165.000000,-0.495205 +165.260000,-0.503932 +166.900000,-0.553594 +168.000000,-0.586594 +168.560000,-0.603296 +170.230000,-0.652874 +171.060000,-0.679771 +171.920000,-0.706133 +173.620000,-0.752978 +174.170000,-0.768021 +175.340000,-0.799848 +177.080000,-0.846746 +177.340000,-0.853709 +178.840000,-0.893666 +180.570000,-0.939438 +180.610000,-0.940491 +182.400000,-0.987338 +183.860000,-1.025185 +184.210000,-1.033697 +186.040000,-1.077916 +187.200000,-1.102632 +187.880000,-1.117030 +189.750000,-1.161232 +190.610000,-1.179173 +191.630000,-1.200354 +193.530000,-1.239494 +194.080000,-1.250749 +195.450000,-1.276707 +197.390000,-1.308146 +197.610000,-1.312266 +199.340000,-1.346898 +201.210000,-1.379171 +201.320000,-1.380910 +203.320000,-1.412361 +204.870000,-1.440440 +205.330000,-1.448108 +207.370000,-1.476806 +208.600000,-1.493974 +209.430000,-1.506616 +211.500000,-1.538004 +212.390000,-1.553500 +213.600000,-1.572865 +215.720000,-1.601539 +216.260000,-1.608799 +217.860000,-1.630233 +220.020000,-1.658928 +220.190000,-1.661175 +222.200000,-1.687588 +224.200000,-1.713642 +224.400000,-1.715989 +226.630000,-1.741955 +228.280000,-1.761014 +228.880000,-1.768634 +231.150000,-1.797296 +232.440000,-1.813468 +233.440000,-1.824761 +235.750000,-1.850681 +236.670000,-1.860915 +238.090000,-1.874983 +240.450000,-1.898200 +240.970000,-1.902184 +242.840000,-1.918564 +245.250000,-1.944503 +245.360000,-1.945682 +247.680000,-1.967836 +249.820000,-1.983740 +250.130000,-1.986031 +252.610000,-2.009230 +254.370000,-2.022022 +255.120000,-2.026632 +257.650000,-2.042103 +259.000000,-2.052929 +260.210000,-2.063879 +262.790000,-2.082091 +263.710000,-2.088550 +265.390000,-2.098521 +268.020000,-2.113987 +268.510000,-2.116850 +270.680000,-2.129460 +273.370000,-2.144939 +273.400000,-2.145056 +276.080000,-2.155404 +278.380000,-2.168431 +278.810000,-2.170857 +281.580000,-2.186345 +283.440000,-2.193339 +284.370000,-2.196816 +287.190000,-2.207285 +288.600000,-2.214960 +290.040000,-2.221380 +292.920000,-2.229121 +293.850000,-2.231609 +295.820000,-2.238703 +298.760000,-2.249184 +299.200000,-2.250000 +301.720000,-2.254646 +304.650000,-2.264900 +304.710000,-2.265109 +307.730000,-2.275582 +310.190000,-2.284037 +310.780000,-2.285526 +313.870000,-2.293267 +315.840000,-2.295000 +316.980000,-2.296997 +320.120000,-2.307461 +321.590000,-2.312317 +323.300000,-2.316471 +326.500000,-2.324203 +327.440000,-2.325000 +329.740000,-2.328880 +333.010000,-2.339351 +333.400000,-2.340593 +336.310000,-2.347408 +339.470000,-2.350000 +339.650000,-2.350294 +343.020000,-2.360776 +345.640000,-2.368857 +346.420000,-2.370625 +349.850000,-2.373357 +351.930000,-2.378003 +353.320000,-2.382184 +356.830000,-2.392661 +358.340000,-2.395000 +360.370000,-2.396566 +363.940000,-2.404300 +364.860000,-2.405000 +367.550000,-2.405000 +371.190000,-2.410000 +371.500000,-2.410000 +374.880000,-2.410000 +378.260000,-2.414557 +378.590000,-2.415000 +382.350000,-2.415000 +385.150000,-2.418699 +386.140000,-2.420000 +389.970000,-2.420000 +392.160000,-2.420000 +393.840000,-2.417627 +397.740000,-2.417159 +399.290000,-2.415000 +401.690000,-2.411679 +405.670000,-2.406215 +406.560000,-2.406111 +409.690000,-2.405748 +413.760000,-2.400268 +413.960000,-2.400000 +417.860000,-2.389596 +421.490000,-2.380000 +422.000000,-2.378659 +426.190000,-2.372702 +429.160000,-2.365000 +430.410000,-2.361775 +434.680000,-2.350827 +436.970000,-2.345000 +438.990000,-2.338613 +443.350000,-2.324927 +444.930000,-2.320000 +447.740000,-2.311265 +452.180000,-2.297575 +453.020000,-2.295000 +456.670000,-2.283884 +461.200000,-2.270210 +461.270000,-2.270000 +465.770000,-2.256535 +469.660000,-2.245000 +470.390000,-2.242848 +475.060000,-2.224158 +478.210000,-2.215000 +479.770000,-2.210484 +484.520000,-2.196823 +486.910000,-2.190000 +489.330000,-2.183134 +494.180000,-2.164476 +495.780000,-2.160000 +499.080000,-2.152641 +504.030000,-2.141693 +504.800000,-2.139227 +509.030000,-2.125750 +513.990000,-2.115000 +514.080000,-2.114757 +519.180000,-2.101067 +523.340000,-2.085957 +524.330000,-2.082905 +529.530000,-2.071968 +532.870000,-2.061813 +534.780000,-2.056029 +540.080000,-2.045084 +542.560000,-2.037680 +545.440000,-2.029133 +550.850000,-2.018194 +552.440000,-2.013539 +556.310000,-2.002256 +561.830000,-1.991302 +562.490000,-1.989405 +567.400000,-1.975365 +572.730000,-1.965000 +573.020000,-1.964438 +578.710000,-1.948478 +583.150000,-1.936128 +584.450000,-1.932532 +590.240000,-1.921608 +593.770000,-1.911982 +596.100000,-1.904568 +602.010000,-1.890885 +604.570000,-1.882850 +607.980000,-1.873761 +614.010000,-1.857815 +615.570000,-1.855000 +620.100000,-1.844843 +626.250000,-1.826172 +626.780000,-1.825000 +632.460000,-1.814990 +638.180000,-1.800437 +638.730000,-1.798806 +645.060000,-1.785144 +649.800000,-1.771292 +651.460000,-1.767172 +657.920000,-1.751233 +661.630000,-1.745000 +664.440000,-1.739125 +671.030000,-1.720443 +673.670000,-1.715000 +677.690000,-1.706753 +684.410000,-1.688075 +685.930000,-1.685000 +691.200000,-1.676510 +698.050000,-1.660572 +698.410000,-1.660000 +704.970000,-1.647040 +711.120000,-1.630598 +711.960000,-1.628691 +719.030000,-1.617732 +724.060000,-1.606468 +726.160000,-1.600986 +733.360000,-1.587313 +737.240000,-1.577325 +740.630000,-1.569914 +747.980000,-1.558965 +750.660000,-1.555000 +755.390000,-1.548034 +762.880000,-1.532091 +764.320000,-1.530000 +770.450000,-1.521142 +778.090000,-1.505200 +778.230000,-1.505000 +785.810000,-1.496937 +792.390000,-1.490000 +793.600000,-1.488309 +801.470000,-1.472373 +806.820000,-1.465000 +809.420000,-1.463216 +817.450000,-1.452741 +821.500000,-1.450000 +825.550000,-1.445910 +833.740000,-1.432699 +836.450000,-1.430000 +842.010000,-1.424489 +850.360000,-1.411280 +851.670000,-1.410000 +858.790000,-1.405384 +867.170000,-1.395082 +867.310000,-1.394866 +875.910000,-1.386664 +882.960000,-1.375935 +884.590000,-1.373977 +893.370000,-1.368498 +899.020000,-1.361806 +902.230000,-1.357037 +911.170000,-1.348841 +915.390000,-1.342660 +920.210000,-1.334177 +929.340000,-1.318229 +932.050000,-1.315000 +938.550000,-1.307292 +947.860000,-1.291345 +949.010000,-1.289386 +957.260000,-1.275401 +966.280000,-1.260246 +966.750000,-1.259326 +976.340000,-1.240647 +983.870000,-1.226106 +986.020000,-1.221368 +995.800000,-1.199946 +1001.770000,-1.186973 +1005.680000,-1.178520 +1015.650000,-1.157110 +1020.000000,-1.145668 +1025.720000,-1.129152 +1035.890000,-1.100012 +1038.570000,-1.092383 +1046.170000,-1.068829 +1056.540000,-1.031951 +1057.470000,-1.028663 +1067.020000,-0.992562 +1076.710000,-0.956256 +1077.600000,-0.952939 +1088.290000,-0.908315 +1096.310000,-0.875116 +1099.080000,-0.863703 +1109.980000,-0.819078 +1116.260000,-0.790710 +1120.990000,-0.768280 +1132.110000,-0.715922 +1136.580000,-0.697008 +1143.330000,-0.666939 +1154.670000,-0.606834 +1157.260000,-0.594323 +1166.120000,-0.553855 +1177.690000,-0.496483 +1178.320000,-0.493374 +1189.370000,-0.436543 +1199.770000,-0.383522 +1201.160000,-0.376468 +1213.070000,-0.316373 +1221.600000,-0.270147 +1225.100000,-0.250485 +1237.250000,-0.187666 +1243.840000,-0.151159 +1249.520000,-0.121102 +1261.910000,-0.056003 +1266.470000,-0.032212 +1274.430000,0.010843 +1287.070000,0.078674 +1289.520000,0.091747 +1299.830000,0.146490 +1312.720000,0.219316 +1312.990000,0.220729 +1325.740000,0.287143 +1336.890000,0.348942 +1338.890000,0.359973 +1352.170000,0.432808 +1361.220000,0.478658 +1365.580000,0.500640 +1379.120000,0.573467 +1385.990000,0.610152 +1392.790000,0.646278 +1406.610000,0.719117 +1411.220000,0.743262 +1420.560000,0.791948 +1434.640000,0.864763 +1436.900000,0.876379 +1448.870000,0.939899 +1463.050000,1.014474 +1463.240000,1.015468 +1477.750000,1.091025 +1489.680000,1.152581 +1492.410000,1.167106 +1507.210000,1.245408 +1516.790000,1.295695 +1522.150000,1.323688 +1537.250000,1.406993 +1544.400000,1.446167 +1552.490000,1.493180 +1567.890000,1.586974 +1572.500000,1.614875 +1583.440000,1.680751 +1599.140000,1.774512 +1601.120000,1.786269 +1615.000000,1.873071 +1630.260000,1.972403 +1631.020000,1.977455 +1647.190000,2.084418 +1659.930000,2.167929 +1663.530000,2.191411 +1680.020000,2.303367 +1690.140000,2.371495 +1696.690000,2.417518 +1713.510000,2.534946 +1720.900000,2.586170 +1730.500000,2.653928 +1747.670000,2.774142 +1752.220000,2.807128 +1765.000000,2.899321 +1782.500000,3.024494 +1784.110000,3.035488 +1800.180000,3.147175 +1816.580000,3.264744 +1818.030000,3.275088 +1836.060000,3.403012 +1849.640000,3.498534 +1854.270000,3.530942 +1872.660000,3.658872 +1883.300000,3.732321 +1891.230000,3.787953 +1909.990000,3.918617 +1917.580000,3.971125 +1928.930000,4.047628 +1948.060000,4.175550 +1952.480000,4.204935 +1967.370000,4.307660 +1986.890000,4.441094 +1988.010000,4.448713 +2006.590000,4.571895 +2024.190000,4.691945 +2026.490000,4.707870 +2046.590000,4.846273 +2061.030000,4.944888 +2066.880000,4.985430 +2087.380000,5.136567 +2098.540000,5.215527 +2108.080000,5.283952 +2128.990000,5.437835 +2136.730000,5.494424 +2150.100000,5.593416 +2171.420000,5.750001 +2175.620000,5.780661 +2192.960000,5.908815 +2214.700000,6.068113 +2215.220000,6.071903 +2236.670000,6.227484 +2255.530000,6.363075 +2258.850000,6.387640 +2281.250000,6.552442 +2296.580000,6.660910 +2303.870000,6.711356 +2326.720000,6.863429 +2338.380000,6.942982 +2349.800000,7.020515 +2373.100000,7.167571 +2380.940000,7.216729 +2396.630000,7.312808 +2420.400000,7.452155 +2424.270000,7.473906 +2444.400000,7.581897 +2468.390000,7.704487 +2468.640000,7.705730 +2493.130000,7.826888 +2513.310000,7.917683 +2517.850000,7.937008 +2542.820000,8.047667 +2559.050000,8.119010 +2568.040000,8.155413 +2593.510000,8.252861 +2605.630000,8.296536 +2619.230000,8.342422 +2645.200000,8.424391 +2653.050000,8.444505 +2671.430000,8.485626 +2697.930000,8.539413 +2701.330000,8.544362 +2724.680000,8.573399 +2750.500000,8.600442 +2751.700000,8.601451 +2778.990000,8.619286 +2800.550000,8.629337 +2806.550000,8.630933 +2834.380000,8.633287 +2851.520000,8.634727 +2862.490000,8.633515 +2890.880000,8.625401 +2903.420000,8.619649 +2919.550000,8.610751 +2948.500000,8.599899 +2956.260000,8.597009 +2977.740000,8.583028 +3007.270000,8.558972 +3010.060000,8.557182 +3037.100000,8.537437 +3064.850000,8.508133 +3067.220000,8.505429 +3097.630000,8.470900 +3120.620000,8.441281 +3128.350000,8.429314 +3159.380000,8.381578 +3177.420000,8.351149 +3190.710000,8.327686 +3222.350000,8.272215 +3235.250000,8.249759 +3254.310000,8.211858 +3286.580000,8.143181 +3294.130000,8.128373 +3319.170000,8.079503 +3352.090000,8.020823 +3354.080000,8.016993 +3385.330000,7.949432 +3415.120000,7.890057 +3418.910000,7.882540 +3452.810000,7.820657 +3477.270000,7.776385 +3487.050000,7.757993 +3521.630000,7.698375 +3540.560000,7.665989 +3556.560000,7.637501 +3591.830000,7.580146 +3605.000000,7.558873 +3627.450000,7.522787 +3663.420000,7.465429 +3670.600000,7.455040 +3699.750000,7.410881 +3736.440000,7.350791 +3737.410000,7.349343 +3773.500000,7.293031 +3805.430000,7.239383 +3810.920000,7.230603 +3848.710000,7.175510 +3874.680000,7.134557 +3886.880000,7.114542 +3925.430000,7.051712 +3945.200000,7.019728 +3964.360000,6.988883 +4003.670000,6.790000 +4017.000000,6.766425 +4043.380000,6.720000 +4083.480000,6.650000 +4090.110000,6.638491 +4123.970000,6.580000 +4164.550000,6.500623 +4164.870000,6.500000 +4206.170000,6.420000 +4240.340000,6.354419 +4247.890000,6.340000 +4290.010000,6.250000 +4317.510000,6.198206 +4332.560000,6.170000 +4375.520000,6.080000 +4396.090000,6.041985 +4418.920000,6.000000 +4462.740000,5.910000 +4476.100000,5.882740 +4507.000000,5.820000 +4551.690000,5.740000 +4557.560000,5.728246 +4596.830000,5.650000 +4640.500000,5.573353 +4642.420000,5.570000 +4688.460000,5.480000 +4724.960000,5.417124 +4734.950000,5.400000 +4781.910000,5.320000 +4810.950000,5.277050 +4829.330000,5.250000 +4877.230000,5.170000 +4898.510000,5.139113 +4925.590000,5.100000 +4974.440000,5.030000 +4987.660000,5.013862 +5023.770000,4.970000 +5073.590000,4.910000 +5078.430000,4.904203 +5123.910000,4.850000 +5170.860000,4.794537 +5174.720000,4.790000 +5226.040000,4.730000 +5264.970000,4.684878 +5277.870000,4.670000 +5330.210000,4.620000 +5360.790000,4.591014 +5383.070000,4.570000 +5436.450000,4.510000 +5458.350000,4.489633 +5490.370000,4.460000 +5544.820000,4.410000 +5557.690000,4.398251 +5599.800000,4.360000 +5655.340000,4.310000 +5658.840000,4.306865 +5711.420000,4.260000 +5761.820000,4.215484 +5768.060000,4.210000 +5825.270000,4.150000 +5866.690000,4.114095 +5883.030000,4.100000 +5941.380000,4.050000 +5973.460000,4.022715 +6000.300000,4.000000 +6059.800000,3.950000 +6082.170000,3.931332 +6119.900000,3.900000 +6180.590000,3.850000 +6192.870000,3.839942 +6241.880000,3.800000 +6303.790000,3.750000 +6305.570000,3.748569 +6366.300000,3.700000 +6420.330000,3.657184 +6429.440000,3.650000 +6493.200000,3.610000 +6537.180000,3.575795 +6557.590000,3.560000 +6622.620000,3.510000 +6656.150000,3.484413 +6688.300000,3.460000 +6754.630000,3.400000 +6777.290000,3.383029 +6821.610000,3.350000 +6889.260000,3.290000 +6900.630000,3.281646 +6957.590000,3.240000 +7026.220000,3.180312 +7026.580000,3.180000 +7096.270000,3.110000 +7154.100000,3.060649 +7166.640000,3.050000 +7237.710000,2.990000 +7284.300000,2.944487 +7309.490000,2.920000 +7381.980000,2.850000 +7416.870000,2.816554 +7455.190000,2.780000 +7529.120000,2.710000 +7551.850000,2.685564 +7603.790000,2.630000 +7679.200000,2.550000 +7689.290000,2.539354 +7755.350000,2.470000 +7829.230000,2.393137 +7832.260000,2.390000 +7909.930000,2.300000 +7971.720000,2.229039 +7988.380000,2.210000 +8067.600000,2.120000 +8116.800000,2.058391 +8147.610000,2.020000 +8228.410000,1.920000 +8264.530000,1.871175 +8310.010000,1.810000 +8392.420000,1.690000 +8414.940000,1.657414 +8475.650000,1.570000 +8559.700000,1.450000 +8568.090000,1.438087 +8644.590000,1.330000 +8724.020000,1.209510 +8730.320000,1.200000 +8816.900000,1.070000 +8882.790000,0.971909 +8904.330000,0.940000 +8992.640000,0.810000 +9044.460000,0.734305 +9081.820000,0.680000 +9171.880000,0.530000 +9209.060000,0.468508 +9262.840000,0.380000 +9354.700000,0.230000 +9376.660000,0.191983 +9447.470000,0.070000 +9541.170000,-0.090000 +9547.310000,-0.100431 +9635.790000,-0.250000 +9721.070000,-0.383948 +9731.340000,-0.400000 +9827.850000,-0.550000 +9897.990000,-0.658101 +9925.310000,-0.700000 +10023.740000,-0.830000 +10078.130000,-0.901286 +10123.150000,-0.960000 +10223.540000,-1.090000 +10261.550000,-1.135126 +10324.930000,-1.210000 +10427.320000,-1.330000 +10448.300000,-1.354442 +10530.730000,-1.450000 +10635.170000,-1.560000 +10638.450000,-1.563437 +10740.640000,-1.670000 +10832.070000,-1.764492 +10847.150000,-1.780000 +10954.720000,-1.890000 +11029.210000,-1.965539 +11063.360000,-2.000000 +11173.080000,-2.120000 +11229.940000,-2.176585 +11283.880000,-2.230000 +11395.790000,-2.340000 +11434.320000,-2.381046 +11508.800000,-2.460000 +11622.930000,-2.580000 +11642.410000,-2.602060 +11738.200000,-2.710000 +11854.300000,-2.829682 +11854.610000,-2.830000 +11972.170000,-2.970000 +12070.040000,-3.085503 +12090.900000,-3.110000 +12210.800000,-3.250000 +12289.710000,-3.341382 +12331.900000,-3.390000 +12454.200000,-3.530000 +12513.380000,-3.602057 +12577.710000,-3.680000 +12702.440000,-3.830000 +12741.120000,-3.879297 +12828.410000,-3.990000 +12955.630000,-4.150000 +12973.000000,-4.171724 +13084.110000,-4.310000 +13209.100000,-4.464146 +13213.870000,-4.470000 +13344.910000,-4.640000 +13449.500000,-4.774492 +13477.250000,-4.810000 +13610.910000,-4.980000 +13694.280000,-5.091386 +13745.890000,-5.160000 +13882.210000,-5.340000 +13943.510000,-5.424833 +14019.880000,-5.530000 +14158.920000,-5.720000 +14197.270000,-5.772081 +14299.330000,-5.910000 +14441.140000,-6.110000 +14455.660000,-6.131386 +14584.350000,-6.320000 +14718.740000,-6.515186 +14728.990000,-6.530000 +14875.050000,-6.750000 +14986.620000,-6.916587 +15022.570000,-6.970000 +15171.550000,-7.200000 +15259.370000,-7.334521 +15322.010000,-7.430000 +15473.960000,-7.670000 +15537.080000,-7.769008 +15627.410000,-7.910000 +15782.390000,-8.150000 +15819.850000,-8.210058 +15938.910000,-8.400000 +16096.980000,-8.660000 +16107.760000,-8.677639 +16256.610000,-8.920000 +16400.920000,-9.161805 +16417.830000,-9.190000 +16580.650000,-9.460000 +16699.410000,-9.662507 +16745.080000,-9.740000 +16911.140000,-10.030000 +17003.330000,-10.195276 +17078.850000,-10.330000 +17248.220000,-10.630000 +17312.780000,-10.747364 +17419.270000,-10.940000 +17592.020000,-11.250000 +17627.860000,-11.315997 +17766.480000,-11.570000 +17942.670000,-11.890000 +17948.680000,-11.901539 +18120.610000,-12.230000 +18275.340000,-12.522940 +18300.320000,-12.570000 +18481.800000,-12.920000 +18607.940000,-13.161240 +18665.090000,-13.270000 +18850.190000,-13.640000 +18946.600000,-13.836444 +19037.130000,-14.020000 +19225.920000,-14.400000 +19291.420000,-14.534409 +19416.590000,-14.790000 +19609.140000,-15.190000 +19642.520000,-15.258940 +19803.610000,-15.590000 +20000.000000,-16.000000