@@ -106,12 +106,13 @@ inline double smoothstep01(double x) {
106106inline double lowfreq_taper (double f, double f_cut) {
107107 if (!(f_cut > 0.0 )) return 1.0 ;
108108
109- const double f0 = 0.72 * f_cut;
110- const double f1 = 1.20 * f_cut;
109+ // Gentler taper for medium/high seas.
110+ const double f0 = 0.45 * f_cut;
111+ const double f1 = 1.10 * f_cut;
111112
112113 if (f <= f0) {
113114 const double r = std::clamp (f / std::max (f0, 1e-12 ), 0.0 , 1.0 );
114- return std::pow (r, 6 .0 );
115+ return std::pow (r, 3 .0 );
115116 }
116117 if (f >= f1) return 1.0 ;
117118
@@ -132,10 +133,13 @@ inline void smooth_logfreq_3tap_inplace(SpectrumLike& spectrum,
132133
133134 auto wpair = [&](int i) {
134135 const double eps = 1e-12 ;
136+
135137 const double x_im1 = (i > 0 )
136138 ? std::log (std::max (freqs[i - 1 ], eps))
137139 : std::log (std::max (freqs[i], eps));
140+
138141 const double x_i = std::log (std::max (freqs[i], eps));
142+
139143 const double x_ip1 = (i < Nfreq - 1 )
140144 ? std::log (std::max (freqs[i + 1 ], eps))
141145 : std::log (std::max (freqs[i], eps));
@@ -167,15 +171,41 @@ inline void smooth_logfreq_3tap_inplace(SpectrumLike& spectrum,
167171}
168172
169173template <int Nfreq>
170- inline double estimate_lowfreq_cut_from_accel (const std::array<double , Nfreq>& S_aa_meas ,
174+ inline double estimate_lowfreq_cut_from_accel (const std::array<double , Nfreq>& S_aa_true ,
171175 const std::array<double , Nfreq>& freqs,
172176 double Tblk,
173177 double hp_f0_hz) {
178+ if (Nfreq < 4 ) {
179+ return std::max ({
180+ 1.10 * freqs[0 ],
181+ 1.8 / std::max (Tblk, 1e-12 ),
182+ 1.35 * hp_f0_hz
183+ });
184+ }
185+
186+ // Use partially integrated pseudo-velocity-like spectrum to avoid
187+ // right-shifting the cutoff in medium/high sea states.
188+ const double f_floor = std::max ({
189+ 1.10 * freqs[0 ],
190+ 1.80 / std::max (Tblk, 1e-12 ),
191+ 1.35 * hp_f0_hz
192+ });
193+
194+ const double f_pre = std::max ({
195+ 0.85 * hp_f0_hz,
196+ 1.20 / std::max (Tblk, 1e-12 ),
197+ 0.80 * f_floor
198+ });
199+ const double lam_pre = 2.0 * M_PI * f_pre;
200+
174201 std::array<double , Nfreq> E{};
175202 std::array<double , Nfreq> Es{};
176203
177204 for (int i = 0 ; i < Nfreq; ++i) {
178- E[i] = std::max (0.0 , S_aa_meas[i]) * std::max (freqs[i], 1e-12 );
205+ const double f = std::max (freqs[i], 1e-12 );
206+ const double w = 2.0 * M_PI * f;
207+ const double S_mid = std::max (0.0 , S_aa_true[i]) / (w * w + lam_pre * lam_pre);
208+ E[i] = f * S_mid;
179209 }
180210
181211 Es[0 ] = 0.75 * E[0 ] + 0.25 * E[1 ];
@@ -184,12 +214,6 @@ inline double estimate_lowfreq_cut_from_accel(const std::array<double, Nfreq>& S
184214 }
185215 Es[Nfreq - 1 ] = 0.25 * E[Nfreq - 2 ] + 0.75 * E[Nfreq - 1 ];
186216
187- const double f_floor = std::max ({
188- 1.35 * freqs[0 ],
189- 2.8 / std::max (Tblk, 1e-12 ),
190- 2.2 * hp_f0_hz
191- });
192-
193217 int i_floor = 0 ;
194218 while (i_floor + 1 < Nfreq && freqs[i_floor + 1 ] < f_floor) ++i_floor;
195219
@@ -215,16 +239,30 @@ inline double estimate_lowfreq_cut_from_accel(const std::array<double, Nfreq>& S
215239 }
216240 }
217241
242+ double f_left_half = freqs[i_floor];
243+ for (int i = i_peak; i >= i_floor; --i) {
244+ if (Es[i] <= 0.5 * e_peak) {
245+ f_left_half = freqs[i];
246+ break ;
247+ }
248+ }
249+
250+ double left_width = std::log (std::max (freqs[i_peak], 1e-12 ) / std::max (f_left_half, 1e-12 ));
251+ left_width = std::clamp (left_width, 0.05 , 1.50 );
252+
253+ const double rel_cut = std::clamp (0.39 - 0.13 * left_width, 0.22 , 0.38 );
254+
218255 const bool valley_is_good =
219256 (i_valley > i_floor) &&
220257 (e_valley < 0.82 * e_peak) &&
221258 (freqs[i_valley] < 0.88 * freqs[i_peak]);
222259
223260 double f_cut = valley_is_good
224- ? freqs[i_valley]
225- : std::max (f_floor, 0.58 * freqs[i_peak]);
261+ ? std::max (f_floor, 0.85 * freqs[i_valley])
262+ : std::max (f_floor, rel_cut * freqs[i_peak]);
226263
227- f_cut = std::clamp (f_cut, f_floor, 0.90 * freqs[i_peak]);
264+ f_cut = std::min (f_cut, 0.65 * freqs[i_peak]);
265+ f_cut = std::max (f_cut, f_floor);
228266 return f_cut;
229267}
230268
@@ -236,6 +274,7 @@ inline void suppress_lowfreq_from_cut_inplace(SpectrumLike& spectrum,
236274
237275 int i_cut = 0 ;
238276 while (i_cut + 1 < Nfreq && freqs[i_cut + 1 ] <= f_cut_hz) ++i_cut;
277+ if (i_cut <= 0 ) return ;
239278
240279 std::array<double , Nfreq> E{};
241280 for (int i = 0 ; i < Nfreq; ++i) {
@@ -247,9 +286,9 @@ inline void suppress_lowfreq_from_cut_inplace(SpectrumLike& spectrum,
247286 const double E_ref = std::max (E[i_ref], 1e-18 );
248287
249288 double prev = E_ref;
250- constexpr double shape_pow = 4.8 ;
289+ constexpr double shape_pow = 3.0 ;
251290
252- for (int i = i_cut; i >= 0 ; --i) {
291+ for (int i = i_cut - 1 ; i >= 0 ; --i) {
253292 const double r = std::clamp (freqs[i] / f_ref, 0.0 , 1.0 );
254293 const double cap = E_ref * std::pow (r, shape_pow);
255294
@@ -289,6 +328,7 @@ inline double estimate_fp_with_guard(const SpectrumLike& spectrum,
289328 const double f0 = freqs[k - 1 ];
290329 const double f1 = freqs[k];
291330 const double f2 = freqs[k + 1 ];
331+
292332 if (f0 <= 0.0 || f1 <= 0.0 || f2 <= 0.0 ) return f1;
293333
294334 const double x0 = std::log (f0);
0 commit comments