diff --git a/dsp/framed_model.cpp b/dsp/framed_model.cpp index c40cf32..b33c9bb 100644 --- a/dsp/framed_model.cpp +++ b/dsp/framed_model.cpp @@ -200,6 +200,23 @@ static void process_band_structural( double res_k = std::max(static_cast(res[k]), 1e-12); raw_level[k] = static_cast(static_cast(am[k]) / res_k * scale_factor_x); } + // Apply k-mapping to raw_level as well (VLAW path uses raw_level, not lvl_in) + if (kmap_on) { + double k_sens; + if (band.sens < 12) k_sens = 0.44 + (band.sens - 6.0) * (0.56 / 6.0); + else if (band.sens == 12) k_sens = 1.0; + else if (band.sens < 24) k_sens = std::exp((band.sens - 12.0) * std::log(22.0) / 12.0); + else k_sens = 22.0; + double k_q; + if (band.q >= 2.0) k_q = 0.403; + else if (band.q <= 0.5) k_q = 1.0; + else { + double t = (std::log(band.q) - std::log(0.5)) / (std::log(2.0) - std::log(0.5)); + k_q = 1.0 + t * (0.403 - 1.0); + } + double k_tot2 = k_sens * k_q; + if (k_tot2 > 1e-9) for (size_t k = 0; k < nbin; k++) raw_level[k] = static_cast(raw_level[k] / k_tot2); + } // RT_VLAW=1 (NOTES 24m): decoded two-stage detector law. // cutS(b) = alpha * ln(1 + lvl_raw / beta) + c + Delta(b) [stage-S] @@ -312,6 +329,14 @@ static void process_band_structural( }; auto [vlaw_alpha, vlaw_beta, vlaw_c, vlaw_delta] = get_vlaw_params(band.fc, band.q, band.sens, num_bands); + // STATE-dependent Δ: opt-in RT_DELTA_STATE=1, default OFF (canon). + // Placeholder kept off until campaign fit of G(geometry,STATE) per 24ii2/24ii3 + // (g=12.15 vs 1.85 needs live STATE capture, not Haar proxy). + static const int delta_state = getenv("RT_DELTA_STATE") ? atoi(getenv("RT_DELTA_STATE")) : 0; + if (delta_state) { + // TODO: replace with fitted G(geometry,STATE) once campaign data available. + // Current: keep canon Δ, STATE scaling disabled pending live capture. + } for (size_t k2 = 0; k2 < nbin; k2++) { double cs = vlaw_alpha * std::log1p(static_cast(raw_level[k2]) / vlaw_beta) + vlaw_c