Branch data Line data Source code
1 : : #ifndef CONFIG_H
2 : : #define CONFIG_H
3 : :
4 : : #include "FFBEngine.h"
5 : : #include <string>
6 : : #include <vector>
7 : : #include <chrono>
8 : : #include <map>
9 : : #include <atomic>
10 : : #include "Version.h"
11 : :
12 : : struct Preset {
13 : : std::string name;
14 : : bool is_builtin = false;
15 : : std::string app_version = LMUFFB_VERSION; // NEW: Track if this is hardcoded or user-created
16 : :
17 : : // 1. SINGLE SOURCE OF TRUTH: Default Preset Values
18 : : // These defaults are used by:
19 : : // - FFBEngine constructor (via ApplyDefaultsToEngine)
20 : : // - "Default" preset in LoadPresets()
21 : : // - "Reset Defaults" button in GUI
22 : : // - Test presets that don't explicitly set these values
23 : : //
24 : : // âš ï¸ IMPORTANT: When changing these defaults, you MUST also update:
25 : : // 1. SetAdvancedBraking() default parameters below (abs_f, lockup_f)
26 : : // 2. test_ffb_engine.cpp: expected_abs_freq and expected_lockup_freq_scale
27 : : // 3. Any test presets in Config.cpp that rely on these defaults
28 : : //
29 : : // Current defaults match: GT3 DD 15 Nm (Simagic Alpha) - v0.6.35
30 : : float gain = 1.0f;
31 : : float understeer = 1.0f; // New scale: 0.0-2.0, where 1.0 = proportional
32 : : float understeer_gamma = 1.0f; // NEW: shapes the grip loss curve
33 : : float sop = 1.666f;
34 : : float lateral_load = 0.0f; // New v0.7.121
35 : : int lat_load_transform = 0; // New v0.7.154 (Issue #282)
36 : : float sop_scale = 1.0f;
37 : : float sop_smoothing = 0.0f;
38 : : float slip_smoothing = 0.002f;
39 : : float min_force = 0.0f;
40 : : float oversteer_boost = 2.52101f;
41 : : float long_load_effect = 0.0f; // Renamed from dynamic_weight_gain (#301)
42 : : float long_load_smoothing = 0.15f; // Renamed from dynamic_weight_smoothing (#301)
43 : : int long_load_transform = 0; // New #301
44 : :
45 : : // FFB Safety (Issue #316)
46 : : float safety_window_duration = FFBEngine::DEFAULT_SAFETY_WINDOW_DURATION;
47 : : float safety_gain_reduction = FFBEngine::DEFAULT_SAFETY_GAIN_REDUCTION;
48 : : float safety_smoothing_tau = FFBEngine::DEFAULT_SAFETY_SMOOTHING_TAU;
49 : : float spike_detection_threshold = FFBEngine::DEFAULT_SPIKE_DETECTION_THRESHOLD;
50 : : float immediate_spike_threshold = FFBEngine::DEFAULT_IMMEDIATE_SPIKE_THRESHOLD;
51 : : float safety_slew_full_scale_time_s = FFBEngine::DEFAULT_SAFETY_SLEW_FULL_SCALE_TIME_S;
52 : : bool stutter_safety_enabled = FFBEngine::DEFAULT_STUTTER_SAFETY_ENABLED;
53 : : float stutter_threshold = FFBEngine::DEFAULT_STUTTER_THRESHOLD;
54 : :
55 : : float grip_smoothing_steady = 0.05f; // v0.7.47
56 : : float grip_smoothing_fast = 0.005f; // v0.7.47
57 : : float grip_smoothing_sensitivity = 0.1f; // v0.7.47
58 : :
59 : : bool lockup_enabled = true;
60 : : float lockup_gain = 0.37479f;
61 : : float lockup_start_pct = 1.0f; // New v0.5.11
62 : : float lockup_full_pct = 5.0f; // New v0.5.11
63 : : float lockup_rear_boost = 10.0f; // New v0.5.11
64 : : float lockup_gamma = 0.1f; // New v0.6.0
65 : : float lockup_prediction_sens = 10.0f; // New v0.6.0
66 : : float lockup_bump_reject = 0.1f; // New v0.6.0
67 : : float brake_load_cap = 2.0f; // New v0.5.11
68 : : float texture_load_cap = 1.5f; // NEW v0.6.25
69 : :
70 : : bool abs_pulse_enabled = false; // New v0.6.0
71 : : float abs_gain = 2.0f; // New v0.6.0
72 : : float abs_freq = 25.5f; // New v0.6.20
73 : :
74 : : bool spin_enabled = true;
75 : : float spin_gain = 0.5f;
76 : : float spin_freq_scale = 1.0f; // New v0.6.20
77 : :
78 : : bool slide_enabled = false;
79 : : float slide_gain = 0.226562f;
80 : : float slide_freq = 1.0f;
81 : :
82 : : bool road_enabled = true;
83 : : float road_gain = 0.0f;
84 : : float vibration_gain = 1.0f; // New v0.7.110 (Issue #206)
85 : :
86 : : bool dynamic_normalization_enabled = false;
87 : : bool auto_load_normalization_enabled = false;
88 : :
89 : : bool soft_lock_enabled = true;
90 : : float soft_lock_stiffness = 20.0f;
91 : : float soft_lock_damping = 0.5f;
92 : :
93 : : float wheelbase_max_nm = 15.0f; // Default DD
94 : : float target_rim_nm = 10.0f; // Default target
95 : :
96 : : float lockup_freq_scale = 1.02f; // New v0.6.20
97 : : int bottoming_method = 0;
98 : : float scrub_drag_gain = 0.0f;
99 : :
100 : : float rear_align_effect = 0.666f;
101 : : float kerb_strike_rejection = 0.0f; // NEW
102 : : float sop_yaw_gain = 0.333f;
103 : : float gyro_gain = 0.0f;
104 : :
105 : : float steering_shaft_gain = 1.0f;
106 : : float ingame_ffb_gain = 1.0f; // New v0.7.71 (Issue #160)
107 : : int torque_source = 0; // 0=Shaft, 1=Direct
108 : : bool torque_passthrough = false; // v0.7.63
109 : :
110 : : // NEW: Grip & Smoothing (v0.5.7)
111 : : float optimal_slip_angle = 0.1f;
112 : : float optimal_slip_ratio = 0.12f;
113 : : float steering_shaft_smoothing = 0.0f;
114 : :
115 : : // NEW: Advanced Smoothing (v0.5.8)
116 : : float gyro_smoothing = 0.0f;
117 : : float yaw_smoothing = 0.001f;
118 : : float chassis_smoothing = 0.0f;
119 : :
120 : : // v0.4.41: Signal Filtering
121 : : bool flatspot_suppression = false;
122 : : float notch_q = 2.0f;
123 : : float flatspot_strength = 1.0f;
124 : :
125 : : bool static_notch_enabled = false;
126 : : float static_notch_freq = 11.0f;
127 : : float static_notch_width = 2.0f; // New v0.6.10
128 : : float yaw_kick_threshold = 0.0f; // New v0.6.10
129 : :
130 : : // Unloaded Yaw Kick (Braking/Lift-off) - v0.7.164 (Issue #322)
131 : : float unloaded_yaw_gain = 0.0f;
132 : : float unloaded_yaw_threshold = 0.2f;
133 : : float unloaded_yaw_sens = 1.0f;
134 : : float unloaded_yaw_gamma = 0.5f;
135 : : float unloaded_yaw_punch = 0.05f;
136 : :
137 : : // Power Yaw Kick (Acceleration) - v0.7.164 (Issue #322)
138 : : float power_yaw_gain = 0.0f;
139 : : float power_yaw_threshold = 0.2f;
140 : : float power_slip_threshold = 0.10f;
141 : : float power_yaw_gamma = 0.5f;
142 : : float power_yaw_punch = 0.05f;
143 : :
144 : : // v0.6.23 New Settings with HIGHER DEFAULTS
145 : : float speed_gate_lower = 1.0f; // 3.6 km/h
146 : : float speed_gate_upper = 5.0f; // 18.0 km/h (Fixes idle shake)
147 : :
148 : : // Reserved for future implementation (v0.6.23+)
149 : : float road_fallback_scale = 0.05f; // Planned: Road texture fallback scaling
150 : : bool understeer_affects_sop = false; // Planned: Understeer modulation of SoP
151 : :
152 : : // ===== SLOPE DETECTION (v0.7.0 → v0.7.1 defaults) =====
153 : : bool slope_detection_enabled = false;
154 : : int slope_sg_window = 15;
155 : : float slope_sensitivity = 0.5f; // Reduced from 1.0 (less aggressive)
156 : :
157 : : float slope_smoothing_tau = 0.04f; // Changed from 0.02 (smoother transitions)
158 : :
159 : : // v0.7.3: Slope detection stability fixes
160 : : float slope_alpha_threshold = 0.02f;
161 : : float slope_decay_rate = 5.0f;
162 : : bool slope_confidence_enabled = true;
163 : :
164 : : // v0.7.11: Min/Max Threshold System
165 : : float slope_min_threshold = -0.3f;
166 : : float slope_max_threshold = -2.0f;
167 : :
168 : : // NEW v0.7.40: Advanced Slope Settings
169 : : float slope_g_slew_limit = 50.0f;
170 : : bool slope_use_torque = true;
171 : : float slope_torque_sensitivity = 0.5f;
172 : : float slope_confidence_max_rate = 0.10f;
173 : :
174 : : bool rest_api_enabled = false;
175 : : int rest_api_port = 6397;
176 : :
177 : : // 2. Constructors
178 [ + - ]: 2049 : Preset(std::string n, bool builtin = false) : name(n), is_builtin(builtin), app_version(LMUFFB_VERSION) {}
179 [ + - + - ]: 7055 : Preset() : name("Unnamed"), is_builtin(false), app_version(LMUFFB_VERSION) {} // Default constructor for file loading
180 : :
181 : : // 3. Fluent Setters (The "Python Dictionary" feel)
182 : 308 : Preset& SetGain(float v) { gain = v; return *this; }
183 : 448 : Preset& SetUndersteer(float v) { understeer = v; return *this; }
184 : : Preset& SetUndersteerGamma(float v) { understeer_gamma = v; return *this; }
185 : 448 : Preset& SetSoP(float v) { sop = v; return *this; }
186 : 168 : Preset& SetSoPScale(float v) { sop_scale = v; return *this; }
187 : 448 : Preset& SetSmoothing(float v) { sop_smoothing = v; return *this; }
188 : : Preset& SetMinForce(float v) { min_force = v; return *this; }
189 : 252 : Preset& SetOversteer(float v) { oversteer_boost = v; return *this; }
190 : : Preset& SetLongitudinalLoad(float v) { long_load_effect = v; return *this; }
191 : : Preset& SetLongitudinalLoadSmoothing(float v) { long_load_smoothing = v; return *this; }
192 : : Preset& SetLongitudinalLoadTransform(int v) { long_load_transform = v; return *this; }
193 : : Preset& SetGripSmoothing(float steady, float fast, float sens) {
194 : : grip_smoothing_steady = steady;
195 : : grip_smoothing_fast = fast;
196 : : grip_smoothing_sensitivity = sens;
197 : : return *this;
198 : : }
199 : 448 : Preset& SetSlipSmoothing(float v) { slip_smoothing = v; return *this; }
200 : :
201 : 280 : Preset& SetLockup(bool enabled, float g, float start = 5.0f, float full = 15.0f, float boost = 1.5f) {
202 : 280 : lockup_enabled = enabled;
203 : 280 : lockup_gain = g;
204 : 280 : lockup_start_pct = start;
205 : 280 : lockup_full_pct = full;
206 : 280 : lockup_rear_boost = boost;
207 : 280 : return *this;
208 : : }
209 : : Preset& SetBrakeCap(float v) { brake_load_cap = v; return *this; }
210 : 280 : Preset& SetSpin(bool enabled, float g, float scale = 1.0f) {
211 : 280 : spin_enabled = enabled;
212 : 280 : spin_gain = g;
213 : 280 : spin_freq_scale = scale;
214 : 280 : return *this;
215 : : }
216 : 448 : Preset& SetSlide(bool enabled, float g, float f = 1.0f) {
217 : 448 : slide_enabled = enabled;
218 : 448 : slide_gain = g;
219 : 448 : slide_freq = f;
220 : 448 : return *this;
221 : : }
222 : 280 : Preset& SetRoad(bool enabled, float g) { road_enabled = enabled; road_gain = g; return *this; }
223 : : Preset& SetVibrationGain(float v) { vibration_gain = v; return *this; }
224 : : Preset& SetDynamicNormalization(bool enabled) { dynamic_normalization_enabled = enabled; return *this; }
225 : :
226 : : Preset& SetSoftLock(bool enabled, float stiffness, float damping) {
227 : : soft_lock_enabled = enabled;
228 : : soft_lock_stiffness = stiffness;
229 : : soft_lock_damping = damping;
230 : : return *this;
231 : : }
232 : :
233 : : Preset& SetHardwareScaling(float wheelbase, float target) {
234 : : wheelbase_max_nm = wheelbase;
235 : : target_rim_nm = target;
236 : : return *this;
237 : : }
238 : :
239 : : Preset& SetBottoming(int method) { bottoming_method = method; return *this; }
240 : 252 : Preset& SetScrub(float v) { scrub_drag_gain = v; return *this; }
241 : 448 : Preset& SetRearAlign(float v) { rear_align_effect = v; return *this; }
242 : : Preset& SetKerbStrikeRejection(float v) { kerb_strike_rejection = v; return *this; }
243 : 258 : Preset& SetSoPYaw(float v) { sop_yaw_gain = v; return *this; }
244 : 168 : Preset& SetGyro(float v) { gyro_gain = v; return *this; }
245 : :
246 : : Preset& SetShaftGain(float v) { steering_shaft_gain = v; return *this; }
247 : : Preset& SetInGameGain(float v) { ingame_ffb_gain = v; return *this; }
248 : : Preset& SetTorqueSource(int v, bool passthrough = false) { torque_source = v; torque_passthrough = passthrough; return *this; }
249 : : Preset& SetFlatspot(bool enabled, float strength = 1.0f, float q = 2.0f) {
250 : : flatspot_suppression = enabled;
251 : : flatspot_strength = strength;
252 : : notch_q = q;
253 : : return *this;
254 : : }
255 : :
256 : : Preset& SetStaticNotch(bool enabled, float freq, float width = 2.0f) {
257 : : static_notch_enabled = enabled;
258 : : static_notch_freq = freq;
259 : : static_notch_width = width;
260 : : return *this;
261 : : }
262 : 29 : Preset& SetYawKickThreshold(float v) { yaw_kick_threshold = v; return *this; }
263 : :
264 : 2 : Preset& SetUnloadedYawKick(float gain, float threshold, float sens, float gamma, float punch) {
265 : 2 : unloaded_yaw_gain = gain;
266 : 2 : unloaded_yaw_threshold = threshold;
267 : 2 : unloaded_yaw_sens = sens;
268 : 2 : unloaded_yaw_gamma = gamma;
269 : 2 : unloaded_yaw_punch = punch;
270 : 2 : return *this;
271 : : }
272 : :
273 : 5 : Preset& SetPowerYawKick(float gain, float threshold, float slip, float gamma, float punch) {
274 : 5 : power_yaw_gain = gain;
275 : 5 : power_yaw_threshold = threshold;
276 : 5 : power_slip_threshold = slip;
277 : 5 : power_yaw_gamma = gamma;
278 : 5 : power_yaw_punch = punch;
279 : 5 : return *this;
280 : : }
281 : :
282 : 34 : Preset& SetSpeedGate(float lower, float upper) { speed_gate_lower = lower; speed_gate_upper = upper; return *this; }
283 : :
284 : 28 : Preset& SetOptimalSlip(float angle, float ratio) {
285 : 28 : optimal_slip_angle = angle;
286 : 28 : optimal_slip_ratio = ratio;
287 : 28 : return *this;
288 : : }
289 : : Preset& SetShaftSmoothing(float v) { steering_shaft_smoothing = v; return *this; }
290 : :
291 : : Preset& SetGyroSmoothing(float v) { gyro_smoothing = v; return *this; }
292 : 28 : Preset& SetYawSmoothing(float v) { yaw_smoothing = v; return *this; }
293 : : Preset& SetChassisSmoothing(float v) { chassis_smoothing = v; return *this; }
294 : :
295 : : Preset& SetSlopeDetection(bool enabled, int window = 15, float min_thresh = -0.3f, float max_thresh = -2.0f, float tau = 0.04f) {
296 : : slope_detection_enabled = enabled;
297 : : slope_sg_window = window;
298 : : slope_min_threshold = min_thresh;
299 : : slope_max_threshold = max_thresh;
300 : : slope_smoothing_tau = tau;
301 : : return *this;
302 : : }
303 : :
304 : : Preset& SetSlopeStability(float alpha_thresh = 0.02f, float decay = 5.0f, bool conf = true) {
305 : : slope_alpha_threshold = alpha_thresh;
306 : : slope_decay_rate = decay;
307 : : slope_confidence_enabled = conf;
308 : : return *this;
309 : : }
310 : :
311 : : Preset& SetSlopeAdvanced(float slew = 50.0f, bool use_torque = true, float torque_sens = 0.5f) {
312 : : slope_g_slew_limit = slew;
313 : : slope_use_torque = use_torque;
314 : : slope_torque_sensitivity = torque_sens;
315 : : return *this;
316 : : }
317 : :
318 : : Preset& SetRestApiFallback(bool enabled, int port = 6397) {
319 : : rest_api_enabled = enabled;
320 : : rest_api_port = port;
321 : : return *this;
322 : : }
323 : :
324 : : Preset& SetSafety(float duration, float gain, float smoothing, float threshold, float immediate, float slew, bool stutter = false, float stutter_thresh = 1.5f) {
325 : : safety_window_duration = duration;
326 : : safety_gain_reduction = gain;
327 : : safety_smoothing_tau = smoothing;
328 : : spike_detection_threshold = threshold;
329 : : immediate_spike_threshold = immediate;
330 : : safety_slew_full_scale_time_s = slew;
331 : : stutter_safety_enabled = stutter;
332 : : stutter_threshold = stutter_thresh;
333 : : return *this;
334 : : }
335 : :
336 : : // Advanced Braking (v0.6.0)
337 : : // âš ï¸ IMPORTANT: Default parameters (abs_f, lockup_f) must match Config.h defaults!
338 : : // When changing Config.h defaults, update these values to match.
339 : : // Current: abs_f=25.5, lockup_f=1.02 (GT3 DD 15 Nm defaults - v0.6.35)
340 : 56 : Preset& SetAdvancedBraking(float gamma, float sens, float bump, bool abs, float abs_g, float abs_f = 25.5f, float lockup_f = 1.02f) {
341 : 56 : lockup_gamma = gamma;
342 : 56 : lockup_prediction_sens = sens;
343 : 56 : lockup_bump_reject = bump;
344 : 56 : abs_pulse_enabled = abs;
345 : 56 : abs_gain = abs_g;
346 : 56 : abs_freq = abs_f;
347 : 56 : lockup_freq_scale = lockup_f;
348 : 56 : return *this;
349 : : }
350 : :
351 : : // 4. Static method to apply defaults to FFBEngine (Single Source of Truth)
352 : : // This is called by FFBEngine constructor to initialize with T300 defaults
353 : 825 : static void ApplyDefaultsToEngine(FFBEngine& engine) {
354 [ + - ]: 825 : Preset defaults; // Uses default member initializers (T300 values)
355 [ + - ]: 825 : defaults.Apply(engine);
356 : 825 : }
357 : :
358 : : // Apply this preset to an engine instance
359 : : // v0.7.16: Added comprehensive safety clamping to prevent crashes/NaN from invalid config values
360 : 854 : void Apply(FFBEngine& engine) const {
361 : 854 : engine.m_dynamic_normalization_enabled = dynamic_normalization_enabled;
362 : 854 : engine.m_auto_load_normalization_enabled = auto_load_normalization_enabled;
363 : 854 : engine.m_gain = (std::max)(0.0f, gain);
364 : 854 : engine.m_understeer_effect = (std::max)(0.0f, (std::min)(2.0f, understeer));
365 : 854 : engine.m_understeer_gamma = (std::max)(0.1f, (std::min)(4.0f, understeer_gamma));
366 : 854 : engine.m_sop_effect = (std::max)(0.0f, (std::min)(2.0f, sop));
367 : 854 : engine.m_lat_load_effect = (std::max)(0.0f, (std::min)(2.0f, lateral_load));
368 [ + - ]: 854 : engine.m_lat_load_transform = static_cast<LoadTransform>(std::clamp(lat_load_transform, 0, 3));
369 : 854 : engine.m_sop_scale = (std::max)(0.01f, sop_scale);
370 : 854 : engine.m_sop_smoothing_factor = (std::max)(0.0f, (std::min)(1.0f, sop_smoothing));
371 : 854 : engine.m_slip_angle_smoothing = (std::max)(0.0001f, slip_smoothing);
372 : 854 : engine.m_min_force = (std::max)(0.0f, min_force);
373 : 854 : engine.m_oversteer_boost = (std::max)(0.0f, oversteer_boost);
374 : 854 : engine.m_long_load_effect = (std::max)(0.0f, (std::min)(10.0f, long_load_effect));
375 : 854 : engine.m_long_load_smoothing = (std::max)(0.0f, long_load_smoothing);
376 [ + - ]: 854 : engine.m_long_load_transform = static_cast<LoadTransform>(std::clamp(long_load_transform, 0, 3));
377 : 854 : engine.m_grip_smoothing_steady = (std::max)(0.0f, grip_smoothing_steady);
378 : 854 : engine.m_grip_smoothing_fast = (std::max)(0.0f, grip_smoothing_fast);
379 : 854 : engine.m_grip_smoothing_sensitivity = (std::max)(0.001f, grip_smoothing_sensitivity);
380 : :
381 : : // FFB Safety (Issue #316)
382 : 854 : engine.m_safety.m_safety_window_duration = (std::max)(0.0f, safety_window_duration);
383 : 854 : engine.m_safety.m_safety_gain_reduction = (std::max)(0.0f, (std::min)(1.0f, safety_gain_reduction));
384 : 854 : engine.m_safety.m_safety_smoothing_tau = (std::max)(0.001f, safety_smoothing_tau);
385 : 854 : engine.m_safety.m_spike_detection_threshold = (std::max)(1.0f, spike_detection_threshold);
386 : 854 : engine.m_safety.m_immediate_spike_threshold = (std::max)(1.0f, immediate_spike_threshold);
387 : 854 : engine.m_safety.m_safety_slew_full_scale_time_s = (std::max)(0.01f, safety_slew_full_scale_time_s);
388 : 854 : engine.m_safety.m_stutter_safety_enabled = stutter_safety_enabled;
389 : 854 : engine.m_safety.m_stutter_threshold = (std::max)(1.01f, stutter_threshold);
390 : :
391 : 854 : engine.m_lockup_enabled = lockup_enabled;
392 : 854 : engine.m_lockup_gain = (std::max)(0.0f, lockup_gain);
393 : 854 : engine.m_lockup_start_pct = (std::max)(0.1f, lockup_start_pct);
394 : 854 : engine.m_lockup_full_pct = (std::max)(0.2f, lockup_full_pct);
395 : 854 : engine.m_lockup_rear_boost = (std::max)(0.0f, lockup_rear_boost);
396 : 854 : engine.m_lockup_gamma = (std::max)(0.1f, lockup_gamma); // Critical: prevent pow(0, negative) crash
397 : 854 : engine.m_lockup_prediction_sens = (std::max)(1.0f, lockup_prediction_sens);
398 : 854 : engine.m_lockup_bump_reject = (std::max)(0.01f, lockup_bump_reject);
399 : 854 : engine.m_brake_load_cap = (std::max)(1.0f, brake_load_cap);
400 : 854 : engine.m_texture_load_cap = (std::max)(1.0f, texture_load_cap);
401 : :
402 : 854 : engine.m_abs_pulse_enabled = abs_pulse_enabled;
403 : 854 : engine.m_abs_gain = (std::max)(0.0f, abs_gain);
404 : :
405 : 854 : engine.m_spin_enabled = spin_enabled;
406 : 854 : engine.m_spin_gain = (std::max)(0.0f, spin_gain);
407 : 854 : engine.m_slide_texture_enabled = slide_enabled;
408 : 854 : engine.m_slide_texture_gain = (std::max)(0.0f, slide_gain);
409 : 854 : engine.m_slide_freq_scale = (std::max)(0.1f, slide_freq);
410 : 854 : engine.m_road_texture_enabled = road_enabled;
411 : 854 : engine.m_road_texture_gain = (std::max)(0.0f, road_gain);
412 : 854 : engine.m_vibration_gain = (std::max)(0.0f, (std::min)(2.0f, vibration_gain));
413 : :
414 : 854 : engine.m_soft_lock_enabled = soft_lock_enabled;
415 : 854 : engine.m_soft_lock_stiffness = (std::max)(0.0f, soft_lock_stiffness);
416 : 854 : engine.m_soft_lock_damping = (std::max)(0.0f, soft_lock_damping);
417 : :
418 : 854 : engine.m_wheelbase_max_nm = (std::max)(1.0f, wheelbase_max_nm);
419 : 854 : engine.m_target_rim_nm = (std::max)(1.0f, target_rim_nm);
420 : 854 : engine.m_abs_freq_hz = (std::max)(1.0f, abs_freq);
421 : 854 : engine.m_lockup_freq_scale = (std::max)(0.1f, lockup_freq_scale);
422 : 854 : engine.m_spin_freq_scale = (std::max)(0.1f, spin_freq_scale);
423 : 854 : engine.m_bottoming_method = bottoming_method;
424 : 854 : engine.m_scrub_drag_gain = (std::max)(0.0f, scrub_drag_gain);
425 : 854 : engine.m_rear_align_effect = (std::max)(0.0f, rear_align_effect);
426 : 854 : engine.m_kerb_strike_rejection = (std::max)(0.0f, (std::min)(1.0f, kerb_strike_rejection));
427 : 854 : engine.m_sop_yaw_gain = (std::max)(0.0f, sop_yaw_gain);
428 : 854 : engine.m_gyro_gain = (std::max)(0.0f, gyro_gain);
429 : 854 : engine.m_steering_shaft_gain = (std::max)(0.0f, steering_shaft_gain);
430 : 854 : engine.m_ingame_ffb_gain = (std::max)(0.0f, ingame_ffb_gain);
431 : 854 : engine.m_torque_source = torque_source;
432 : 854 : engine.m_torque_passthrough = torque_passthrough;
433 : 854 : engine.m_flatspot_suppression = flatspot_suppression;
434 : 854 : engine.m_notch_q = (std::max)(0.1f, notch_q); // Critical for biquad division
435 : 854 : engine.m_flatspot_strength = (std::max)(0.0f, (std::min)(1.0f, flatspot_strength));
436 : 854 : engine.m_static_notch_enabled = static_notch_enabled;
437 : 854 : engine.m_static_notch_freq = (std::max)(1.0f, static_notch_freq);
438 : 854 : engine.m_static_notch_width = (std::max)(0.1f, static_notch_width);
439 : 854 : engine.m_yaw_kick_threshold = (std::max)(0.0f, yaw_kick_threshold);
440 : :
441 : : // v0.7.164 (Issue #322)
442 : 854 : engine.m_unloaded_yaw_gain = (std::max)(0.0f, unloaded_yaw_gain);
443 : 854 : engine.m_unloaded_yaw_threshold = (std::max)(0.0f, unloaded_yaw_threshold);
444 : 854 : engine.m_unloaded_yaw_sens = (std::max)(0.1f, unloaded_yaw_sens);
445 : 854 : engine.m_unloaded_yaw_gamma = (std::max)(0.1f, (std::min)(4.0f, unloaded_yaw_gamma));
446 : 854 : engine.m_unloaded_yaw_punch = (std::max)(0.0f, (std::min)(1.0f, unloaded_yaw_punch));
447 : :
448 : 854 : engine.m_power_yaw_gain = (std::max)(0.0f, power_yaw_gain);
449 : 854 : engine.m_power_yaw_threshold = (std::max)(0.0f, power_yaw_threshold);
450 : 854 : engine.m_power_slip_threshold = (std::max)(0.01f, (std::min)(1.0f, power_slip_threshold));
451 : 854 : engine.m_power_yaw_gamma = (std::max)(0.1f, (std::min)(4.0f, power_yaw_gamma));
452 : 854 : engine.m_power_yaw_punch = (std::max)(0.0f, (std::min)(1.0f, power_yaw_punch));
453 : :
454 : 854 : engine.m_speed_gate_lower = (std::max)(0.0f, speed_gate_lower);
455 : 854 : engine.m_speed_gate_upper = (std::max)(0.1f, speed_gate_upper);
456 : :
457 : : // NEW: Grip & Smoothing (v0.5.7/v0.5.8)
458 : 854 : engine.m_optimal_slip_angle = (std::max)(0.01f, optimal_slip_angle); // Critical for grip division
459 : 854 : engine.m_optimal_slip_ratio = (std::max)(0.01f, optimal_slip_ratio); // Critical for grip division
460 : 854 : engine.m_steering_shaft_smoothing = (std::max)(0.0f, steering_shaft_smoothing);
461 : 854 : engine.m_gyro_smoothing = (std::max)(0.0f, gyro_smoothing);
462 : 854 : engine.m_yaw_accel_smoothing = (std::max)(0.0f, yaw_smoothing);
463 : 854 : engine.m_chassis_inertia_smoothing = (std::max)(0.0f, chassis_smoothing);
464 : 854 : engine.m_road_fallback_scale = (std::max)(0.0f, road_fallback_scale);
465 : 854 : engine.m_understeer_affects_sop = understeer_affects_sop;
466 : :
467 : : // Slope Detection (v0.7.0)
468 : 854 : engine.m_slope_detection_enabled = slope_detection_enabled;
469 : 854 : engine.m_slope_sg_window = (std::max)(5, (std::min)(41, slope_sg_window));
470 [ - + ]: 854 : if (engine.m_slope_sg_window % 2 == 0) engine.m_slope_sg_window++; // Must be odd for SG
471 : 854 : engine.m_slope_sensitivity = (std::max)(0.1f, slope_sensitivity);
472 : :
473 : 854 : engine.m_slope_smoothing_tau = (std::max)(0.001f, slope_smoothing_tau);
474 : :
475 : : // v0.7.3: Slope stability fixes
476 : 854 : engine.m_slope_alpha_threshold = (std::max)(0.001f, slope_alpha_threshold); // Critical for slope division
477 : 854 : engine.m_slope_decay_rate = (std::max)(0.1f, slope_decay_rate);
478 : 854 : engine.m_slope_confidence_enabled = slope_confidence_enabled;
479 : 854 : engine.m_slope_confidence_max_rate = (std::max)(engine.m_slope_alpha_threshold + 0.01f, slope_confidence_max_rate);
480 : :
481 : : // v0.7.11: Min/Max thresholds
482 : 854 : engine.m_slope_min_threshold = slope_min_threshold;
483 : 854 : engine.m_slope_max_threshold = slope_max_threshold;
484 : :
485 : : // NEW v0.7.40: Advanced Slope Settings
486 : 854 : engine.m_slope_g_slew_limit = (std::max)(1.0f, slope_g_slew_limit);
487 : 854 : engine.m_slope_use_torque = slope_use_torque;
488 : 854 : engine.m_slope_torque_sensitivity = (std::max)(0.01f, slope_torque_sensitivity);
489 : :
490 : 854 : engine.m_rest_api_enabled = rest_api_enabled;
491 : 854 : engine.m_rest_api_port = (std::max)(1, rest_api_port);
492 : :
493 : : // Stage 1 & 2 Normalization (Issue #152 & #153)
494 : : // Initialize session peak from target rim torque to provide a sane starting point.
495 : 854 : engine.m_session_peak_torque = (std::max)(1.0, (double)target_rim_nm);
496 : 854 : engine.m_smoothed_structural_mult = 1.0 / engine.m_session_peak_torque;
497 : 854 : }
498 : :
499 : : // NEW: Ensure values are within safe ranges (v0.7.16)
500 : 25 : void Validate() {
501 : 25 : gain = (std::max)(0.0f, gain);
502 : 25 : understeer = (std::max)(0.0f, (std::min)(2.0f, understeer));
503 : 25 : understeer_gamma = (std::max)(0.1f, (std::min)(4.0f, understeer_gamma));
504 : 25 : sop = (std::max)(0.0f, (std::min)(2.0f, sop));
505 : 25 : lateral_load = (std::max)(0.0f, (std::min)(2.0f, lateral_load));
506 [ + - ]: 25 : lat_load_transform = std::clamp(lat_load_transform, 0, 3);
507 : 25 : sop_scale = (std::max)(0.01f, sop_scale);
508 : 25 : sop_smoothing = (std::max)(0.0f, (std::min)(1.0f, sop_smoothing));
509 : 25 : slip_smoothing = (std::max)(0.0001f, slip_smoothing);
510 : 25 : min_force = (std::max)(0.0f, min_force);
511 : 25 : oversteer_boost = (std::max)(0.0f, oversteer_boost);
512 : 25 : long_load_effect = (std::max)(0.0f, (std::min)(10.0f, long_load_effect));
513 : 25 : long_load_smoothing = (std::max)(0.0f, long_load_smoothing);
514 [ + - ]: 25 : long_load_transform = std::clamp(long_load_transform, 0, 3);
515 : 25 : grip_smoothing_steady = (std::max)(0.0f, grip_smoothing_steady);
516 : 25 : grip_smoothing_fast = (std::max)(0.0f, grip_smoothing_fast);
517 : 25 : grip_smoothing_sensitivity = (std::max)(0.001f, grip_smoothing_sensitivity);
518 : 25 : lockup_gain = (std::max)(0.0f, lockup_gain);
519 : 25 : lockup_start_pct = (std::max)(0.1f, lockup_start_pct);
520 : 25 : lockup_full_pct = (std::max)(0.2f, lockup_full_pct);
521 : 25 : lockup_rear_boost = (std::max)(0.0f, lockup_rear_boost);
522 : 25 : lockup_gamma = (std::max)(0.1f, lockup_gamma);
523 : 25 : lockup_prediction_sens = (std::max)(1.0f, lockup_prediction_sens);
524 : 25 : lockup_bump_reject = (std::max)(0.01f, lockup_bump_reject);
525 : 25 : brake_load_cap = (std::max)(1.0f, brake_load_cap);
526 : 25 : texture_load_cap = (std::max)(1.0f, texture_load_cap);
527 : 25 : abs_gain = (std::max)(0.0f, abs_gain);
528 : 25 : spin_gain = (std::max)(0.0f, spin_gain);
529 : 25 : slide_gain = (std::max)(0.0f, slide_gain);
530 : 25 : slide_freq = (std::max)(0.1f, slide_freq);
531 : 25 : road_gain = (std::max)(0.0f, road_gain);
532 : 25 : vibration_gain = (std::max)(0.0f, (std::min)(2.0f, vibration_gain));
533 : 25 : soft_lock_stiffness = (std::max)(0.0f, soft_lock_stiffness);
534 : 25 : soft_lock_damping = (std::max)(0.0f, soft_lock_damping);
535 : 25 : wheelbase_max_nm = (std::max)(1.0f, wheelbase_max_nm);
536 : 25 : target_rim_nm = (std::max)(1.0f, target_rim_nm);
537 : 25 : abs_freq = (std::max)(1.0f, abs_freq);
538 : 25 : lockup_freq_scale = (std::max)(0.1f, lockup_freq_scale);
539 : 25 : spin_freq_scale = (std::max)(0.1f, spin_freq_scale);
540 : 25 : scrub_drag_gain = (std::max)(0.0f, scrub_drag_gain);
541 : 25 : rear_align_effect = (std::max)(0.0f, rear_align_effect);
542 : 25 : kerb_strike_rejection = (std::max)(0.0f, (std::min)(1.0f, kerb_strike_rejection));
543 : 25 : sop_yaw_gain = (std::max)(0.0f, sop_yaw_gain);
544 : 25 : gyro_gain = (std::max)(0.0f, gyro_gain);
545 : 25 : steering_shaft_gain = (std::max)(0.0f, steering_shaft_gain);
546 : 25 : ingame_ffb_gain = (std::max)(0.0f, ingame_ffb_gain);
547 : 25 : torque_source = (std::max)(0, (std::min)(1, torque_source));
548 : : // torque_passthrough is bool, no clamp needed
549 : 25 : notch_q = (std::max)(0.1f, notch_q);
550 : 25 : flatspot_strength = (std::max)(0.0f, (std::min)(1.0f, flatspot_strength));
551 : 25 : static_notch_freq = (std::max)(1.0f, static_notch_freq);
552 : 25 : static_notch_width = (std::max)(0.1f, static_notch_width);
553 : :
554 : : // v0.7.164 (Issue #322)
555 : 25 : unloaded_yaw_gain = (std::max)(0.0f, unloaded_yaw_gain);
556 : 25 : unloaded_yaw_threshold = (std::max)(0.0f, unloaded_yaw_threshold);
557 : 25 : unloaded_yaw_sens = (std::max)(0.1f, unloaded_yaw_sens);
558 : 25 : unloaded_yaw_gamma = (std::max)(0.1f, (std::min)(4.0f, unloaded_yaw_gamma));
559 : 25 : unloaded_yaw_punch = (std::max)(0.0f, (std::min)(1.0f, unloaded_yaw_punch));
560 : 25 : power_yaw_gain = (std::max)(0.0f, power_yaw_gain);
561 : 25 : power_yaw_threshold = (std::max)(0.0f, power_yaw_threshold);
562 : 25 : power_slip_threshold = (std::max)(0.01f, (std::min)(1.0f, power_slip_threshold));
563 : 25 : power_yaw_gamma = (std::max)(0.1f, (std::min)(4.0f, power_yaw_gamma));
564 : 25 : power_yaw_punch = (std::max)(0.0f, (std::min)(1.0f, power_yaw_punch));
565 : :
566 : 25 : speed_gate_upper = (std::max)(0.1f, speed_gate_upper);
567 : 25 : optimal_slip_angle = (std::max)(0.01f, optimal_slip_angle);
568 : 25 : optimal_slip_ratio = (std::max)(0.01f, optimal_slip_ratio);
569 : 25 : steering_shaft_smoothing = (std::max)(0.0f, steering_shaft_smoothing);
570 : 25 : gyro_smoothing = (std::max)(0.0f, gyro_smoothing);
571 : 25 : yaw_smoothing = (std::max)(0.0f, yaw_smoothing);
572 : 25 : chassis_smoothing = (std::max)(0.0f, chassis_smoothing);
573 : 25 : road_fallback_scale = (std::max)(0.0f, road_fallback_scale);
574 : 25 : slope_sg_window = (std::max)(5, (std::min)(41, slope_sg_window));
575 [ - + ]: 25 : if (slope_sg_window % 2 == 0) slope_sg_window++;
576 : 25 : slope_sensitivity = (std::max)(0.1f, slope_sensitivity);
577 : 25 : slope_smoothing_tau = (std::max)(0.001f, slope_smoothing_tau);
578 : 25 : slope_alpha_threshold = (std::max)(0.001f, slope_alpha_threshold);
579 : 25 : slope_decay_rate = (std::max)(0.1f, slope_decay_rate);
580 : 25 : slope_g_slew_limit = (std::max)(1.0f, slope_g_slew_limit);
581 : 25 : slope_torque_sensitivity = (std::max)(0.01f, slope_torque_sensitivity);
582 : 25 : slope_confidence_max_rate = (std::max)(slope_alpha_threshold + 0.01f, slope_confidence_max_rate);
583 : 25 : rest_api_port = (std::max)(1, rest_api_port);
584 : :
585 : : // FFB Safety (Issue #316)
586 : 25 : safety_window_duration = (std::max)(0.0f, safety_window_duration);
587 : 25 : safety_gain_reduction = (std::max)(0.0f, (std::min)(1.0f, safety_gain_reduction));
588 : 25 : safety_smoothing_tau = (std::max)(0.001f, safety_smoothing_tau);
589 : 25 : spike_detection_threshold = (std::max)(1.0f, spike_detection_threshold);
590 : 25 : immediate_spike_threshold = (std::max)(1.0f, immediate_spike_threshold);
591 : 25 : safety_slew_full_scale_time_s = (std::max)(0.01f, safety_slew_full_scale_time_s);
592 : 25 : stutter_threshold = (std::max)(1.01f, stutter_threshold);
593 : 25 : }
594 : :
595 : : // NEW: Capture current engine state into this preset
596 : 394 : void UpdateFromEngine(const FFBEngine& engine) {
597 : 394 : dynamic_normalization_enabled = engine.m_dynamic_normalization_enabled;
598 : 394 : auto_load_normalization_enabled = engine.m_auto_load_normalization_enabled;
599 : 394 : gain = engine.m_gain;
600 : 394 : understeer = engine.m_understeer_effect;
601 : 394 : understeer_gamma = engine.m_understeer_gamma;
602 : 394 : sop = engine.m_sop_effect;
603 : 394 : lateral_load = engine.m_lat_load_effect;
604 : 394 : lat_load_transform = static_cast<int>(engine.m_lat_load_transform);
605 : 394 : sop_scale = engine.m_sop_scale;
606 : 394 : sop_smoothing = engine.m_sop_smoothing_factor;
607 : 394 : slip_smoothing = engine.m_slip_angle_smoothing;
608 : 394 : min_force = engine.m_min_force;
609 : 394 : oversteer_boost = engine.m_oversteer_boost;
610 : 394 : long_load_effect = engine.m_long_load_effect;
611 : 394 : long_load_smoothing = engine.m_long_load_smoothing;
612 : 394 : long_load_transform = static_cast<int>(engine.m_long_load_transform);
613 : 394 : grip_smoothing_steady = engine.m_grip_smoothing_steady;
614 : 394 : grip_smoothing_fast = engine.m_grip_smoothing_fast;
615 : 394 : grip_smoothing_sensitivity = engine.m_grip_smoothing_sensitivity;
616 : :
617 : : // FFB Safety (Issue #316)
618 : 394 : safety_window_duration = engine.m_safety.m_safety_window_duration;
619 : 394 : safety_gain_reduction = engine.m_safety.m_safety_gain_reduction;
620 : 394 : safety_smoothing_tau = engine.m_safety.m_safety_smoothing_tau;
621 : 394 : spike_detection_threshold = engine.m_safety.m_spike_detection_threshold;
622 : 394 : immediate_spike_threshold = engine.m_safety.m_immediate_spike_threshold;
623 : 394 : safety_slew_full_scale_time_s = engine.m_safety.m_safety_slew_full_scale_time_s;
624 : 394 : stutter_safety_enabled = engine.m_safety.m_stutter_safety_enabled;
625 : 394 : stutter_threshold = engine.m_safety.m_stutter_threshold;
626 : :
627 : 394 : lockup_enabled = engine.m_lockup_enabled;
628 : 394 : lockup_gain = engine.m_lockup_gain;
629 : 394 : lockup_start_pct = engine.m_lockup_start_pct;
630 : 394 : lockup_full_pct = engine.m_lockup_full_pct;
631 : 394 : lockup_rear_boost = engine.m_lockup_rear_boost;
632 : 394 : lockup_gamma = engine.m_lockup_gamma;
633 : 394 : lockup_prediction_sens = engine.m_lockup_prediction_sens;
634 : 394 : lockup_bump_reject = engine.m_lockup_bump_reject;
635 : 394 : brake_load_cap = engine.m_brake_load_cap;
636 : 394 : texture_load_cap = engine.m_texture_load_cap; // NEW v0.6.25
637 : 394 : abs_pulse_enabled = engine.m_abs_pulse_enabled;
638 : 394 : abs_gain = engine.m_abs_gain;
639 : :
640 : 394 : spin_enabled = engine.m_spin_enabled;
641 : 394 : spin_gain = engine.m_spin_gain;
642 : 394 : slide_enabled = engine.m_slide_texture_enabled;
643 : 394 : slide_gain = engine.m_slide_texture_gain;
644 : 394 : slide_freq = engine.m_slide_freq_scale;
645 : 394 : road_enabled = engine.m_road_texture_enabled;
646 : 394 : road_gain = engine.m_road_texture_gain;
647 : 394 : vibration_gain = engine.m_vibration_gain;
648 : :
649 : 394 : soft_lock_enabled = engine.m_soft_lock_enabled;
650 : 394 : soft_lock_stiffness = engine.m_soft_lock_stiffness;
651 : 394 : soft_lock_damping = engine.m_soft_lock_damping;
652 : :
653 : 394 : wheelbase_max_nm = engine.m_wheelbase_max_nm;
654 : 394 : target_rim_nm = engine.m_target_rim_nm;
655 : 394 : abs_freq = engine.m_abs_freq_hz;
656 : 394 : lockup_freq_scale = engine.m_lockup_freq_scale;
657 : 394 : spin_freq_scale = engine.m_spin_freq_scale;
658 : 394 : bottoming_method = engine.m_bottoming_method;
659 : 394 : scrub_drag_gain = engine.m_scrub_drag_gain;
660 : 394 : rear_align_effect = engine.m_rear_align_effect;
661 : 394 : kerb_strike_rejection = engine.m_kerb_strike_rejection;
662 : 394 : sop_yaw_gain = engine.m_sop_yaw_gain;
663 : 394 : gyro_gain = engine.m_gyro_gain;
664 : 394 : steering_shaft_gain = engine.m_steering_shaft_gain;
665 : 394 : ingame_ffb_gain = engine.m_ingame_ffb_gain;
666 : 394 : torque_source = engine.m_torque_source;
667 : 394 : torque_passthrough = engine.m_torque_passthrough;
668 : 394 : flatspot_suppression = engine.m_flatspot_suppression;
669 : 394 : notch_q = engine.m_notch_q;
670 : 394 : flatspot_strength = engine.m_flatspot_strength;
671 : 394 : static_notch_enabled = engine.m_static_notch_enabled;
672 : 394 : static_notch_freq = engine.m_static_notch_freq;
673 : 394 : static_notch_width = engine.m_static_notch_width;
674 : 394 : yaw_kick_threshold = engine.m_yaw_kick_threshold;
675 : :
676 : : // v0.7.164 (Issue #322)
677 : 394 : unloaded_yaw_gain = engine.m_unloaded_yaw_gain;
678 : 394 : unloaded_yaw_threshold = engine.m_unloaded_yaw_threshold;
679 : 394 : unloaded_yaw_sens = engine.m_unloaded_yaw_sens;
680 : 394 : unloaded_yaw_gamma = engine.m_unloaded_yaw_gamma;
681 : 394 : unloaded_yaw_punch = engine.m_unloaded_yaw_punch;
682 : 394 : power_yaw_gain = engine.m_power_yaw_gain;
683 : 394 : power_yaw_threshold = engine.m_power_yaw_threshold;
684 : 394 : power_slip_threshold = engine.m_power_slip_threshold;
685 : 394 : power_yaw_gamma = engine.m_power_yaw_gamma;
686 : 394 : power_yaw_punch = engine.m_power_yaw_punch;
687 : :
688 : 394 : speed_gate_lower = engine.m_speed_gate_lower;
689 : 394 : speed_gate_upper = engine.m_speed_gate_upper;
690 : :
691 : : // NEW: Grip & Smoothing (v0.5.7/v0.5.8)
692 : 394 : optimal_slip_angle = engine.m_optimal_slip_angle;
693 : 394 : optimal_slip_ratio = engine.m_optimal_slip_ratio;
694 : 394 : steering_shaft_smoothing = engine.m_steering_shaft_smoothing;
695 : 394 : gyro_smoothing = engine.m_gyro_smoothing;
696 : 394 : yaw_smoothing = engine.m_yaw_accel_smoothing;
697 : 394 : chassis_smoothing = engine.m_chassis_inertia_smoothing;
698 : 394 : road_fallback_scale = engine.m_road_fallback_scale;
699 : 394 : understeer_affects_sop = engine.m_understeer_affects_sop;
700 : :
701 : : // Slope Detection (v0.7.0)
702 : 394 : slope_detection_enabled = engine.m_slope_detection_enabled;
703 : 394 : slope_sg_window = engine.m_slope_sg_window;
704 : 394 : slope_sensitivity = engine.m_slope_sensitivity;
705 : :
706 : 394 : slope_smoothing_tau = engine.m_slope_smoothing_tau;
707 : :
708 : : // v0.7.3: Slope stability fixes
709 : 394 : slope_alpha_threshold = engine.m_slope_alpha_threshold;
710 : 394 : slope_decay_rate = engine.m_slope_decay_rate;
711 : 394 : slope_confidence_enabled = engine.m_slope_confidence_enabled;
712 : 394 : slope_confidence_max_rate = engine.m_slope_confidence_max_rate;
713 : :
714 : : // v0.7.11: Min/Max thresholds
715 : 394 : slope_min_threshold = engine.m_slope_min_threshold;
716 : 394 : slope_max_threshold = engine.m_slope_max_threshold;
717 : :
718 : : // NEW v0.7.40: Advanced Slope Settings
719 : 394 : slope_g_slew_limit = engine.m_slope_g_slew_limit;
720 : 394 : slope_use_torque = engine.m_slope_use_torque;
721 : 394 : slope_torque_sensitivity = engine.m_slope_torque_sensitivity;
722 : :
723 : 394 : rest_api_enabled = engine.m_rest_api_enabled;
724 : 394 : rest_api_port = engine.m_rest_api_port;
725 : :
726 : 394 : app_version = LMUFFB_VERSION;
727 : 394 : }
728 : :
729 : 460 : bool Equals(const Preset& p) const {
730 : 460 : const float eps = 0.0001f;
731 : 15772 : auto is_near = [](float a, float b, float epsilon) { return std::abs(a - b) < epsilon; };
732 : :
733 [ + + ]: 460 : if (!is_near(gain, p.gain, eps)) return false;
734 [ + + ]: 458 : if (!is_near(understeer, p.understeer, eps)) return false;
735 [ - + ]: 457 : if (!is_near(understeer_gamma, p.understeer_gamma, eps)) return false;
736 [ + + ]: 457 : if (!is_near(sop, p.sop, eps)) return false;
737 [ - + ]: 452 : if (!is_near(lateral_load, p.lateral_load, eps)) return false;
738 [ - + ]: 452 : if (lat_load_transform != p.lat_load_transform) return false;
739 [ + + ]: 452 : if (!is_near(sop_scale, p.sop_scale, eps)) return false;
740 [ + + ]: 451 : if (!is_near(sop_smoothing, p.sop_smoothing, eps)) return false;
741 [ + + ]: 450 : if (!is_near(slip_smoothing, p.slip_smoothing, eps)) return false;
742 [ + + ]: 449 : if (!is_near(min_force, p.min_force, eps)) return false;
743 [ + + ]: 448 : if (!is_near(oversteer_boost, p.oversteer_boost, eps)) return false;
744 [ + + ]: 441 : if (!is_near(long_load_effect, p.long_load_effect, eps)) return false;
745 [ + + ]: 440 : if (!is_near(long_load_smoothing, p.long_load_smoothing, eps)) return false;
746 [ - + ]: 439 : if (long_load_transform != p.long_load_transform) return false;
747 [ + + ]: 439 : if (!is_near(grip_smoothing_steady, p.grip_smoothing_steady, eps)) return false;
748 [ + + ]: 438 : if (!is_near(grip_smoothing_fast, p.grip_smoothing_fast, eps)) return false;
749 [ + + ]: 437 : if (!is_near(grip_smoothing_sensitivity, p.grip_smoothing_sensitivity, eps)) return false;
750 : :
751 : : // FFB Safety (Issue #316)
752 [ - + ]: 436 : if (!is_near(safety_window_duration, p.safety_window_duration, eps)) return false;
753 [ - + ]: 436 : if (!is_near(safety_gain_reduction, p.safety_gain_reduction, eps)) return false;
754 [ - + ]: 436 : if (!is_near(safety_smoothing_tau, p.safety_smoothing_tau, eps)) return false;
755 [ - + ]: 436 : if (!is_near(spike_detection_threshold, p.spike_detection_threshold, eps)) return false;
756 [ - + ]: 436 : if (!is_near(immediate_spike_threshold, p.immediate_spike_threshold, eps)) return false;
757 [ - + ]: 436 : if (!is_near(safety_slew_full_scale_time_s, p.safety_slew_full_scale_time_s, eps)) return false;
758 [ - + ]: 436 : if (stutter_safety_enabled != p.stutter_safety_enabled) return false;
759 [ - + ]: 436 : if (!is_near(stutter_threshold, p.stutter_threshold, eps)) return false;
760 : :
761 [ + + ]: 436 : if (lockup_enabled != p.lockup_enabled) return false;
762 [ + + ]: 435 : if (!is_near(lockup_gain, p.lockup_gain, eps)) return false;
763 [ + + ]: 434 : if (!is_near(lockup_start_pct, p.lockup_start_pct, eps)) return false;
764 [ + + ]: 433 : if (!is_near(lockup_full_pct, p.lockup_full_pct, eps)) return false;
765 [ + + ]: 432 : if (!is_near(lockup_rear_boost, p.lockup_rear_boost, eps)) return false;
766 [ + + ]: 431 : if (!is_near(lockup_gamma, p.lockup_gamma, eps)) return false;
767 [ + + ]: 430 : if (!is_near(lockup_prediction_sens, p.lockup_prediction_sens, eps)) return false;
768 [ + + ]: 429 : if (!is_near(lockup_bump_reject, p.lockup_bump_reject, eps)) return false;
769 [ + + ]: 428 : if (!is_near(brake_load_cap, p.brake_load_cap, eps)) return false;
770 [ + + ]: 427 : if (!is_near(texture_load_cap, p.texture_load_cap, eps)) return false;
771 : :
772 [ + + ]: 426 : if (abs_pulse_enabled != p.abs_pulse_enabled) return false;
773 [ + + ]: 69 : if (!is_near(abs_gain, p.abs_gain, eps)) return false;
774 [ + + ]: 68 : if (!is_near(abs_freq, p.abs_freq, eps)) return false;
775 : :
776 [ + + ]: 67 : if (spin_enabled != p.spin_enabled) return false;
777 [ - + ]: 66 : if (!is_near(spin_gain, p.spin_gain, eps)) return false;
778 [ + + ]: 66 : if (!is_near(spin_freq_scale, p.spin_freq_scale, eps)) return false;
779 : :
780 [ + + ]: 65 : if (slide_enabled != p.slide_enabled) return false;
781 [ + + ]: 64 : if (!is_near(slide_gain, p.slide_gain, eps)) return false;
782 [ + + ]: 63 : if (!is_near(slide_freq, p.slide_freq, eps)) return false;
783 : :
784 [ + + ]: 62 : if (road_enabled != p.road_enabled) return false;
785 [ + + ]: 61 : if (!is_near(road_gain, p.road_gain, eps)) return false;
786 [ - + ]: 60 : if (!is_near(vibration_gain, p.vibration_gain, eps)) return false;
787 : :
788 [ - + ]: 60 : if (dynamic_normalization_enabled != p.dynamic_normalization_enabled) return false;
789 [ - + ]: 60 : if (auto_load_normalization_enabled != p.auto_load_normalization_enabled) return false;
790 : :
791 [ + + ]: 60 : if (soft_lock_enabled != p.soft_lock_enabled) return false;
792 [ + + ]: 59 : if (!is_near(soft_lock_stiffness, p.soft_lock_stiffness, eps)) return false;
793 [ - + ]: 58 : if (!is_near(soft_lock_damping, p.soft_lock_damping, eps)) return false;
794 : :
795 [ + + ]: 58 : if (!is_near(wheelbase_max_nm, p.wheelbase_max_nm, eps)) return false;
796 [ + + ]: 57 : if (!is_near(target_rim_nm, p.target_rim_nm, eps)) return false;
797 [ + + ]: 56 : if (!is_near(lockup_freq_scale, p.lockup_freq_scale, eps)) return false;
798 [ + + ]: 55 : if (bottoming_method != p.bottoming_method) return false;
799 [ + + ]: 54 : if (!is_near(scrub_drag_gain, p.scrub_drag_gain, eps)) return false;
800 [ + + ]: 53 : if (!is_near(rear_align_effect, p.rear_align_effect, eps)) return false;
801 [ - + ]: 52 : if (!is_near(kerb_strike_rejection, p.kerb_strike_rejection, eps)) return false;
802 [ + + ]: 52 : if (!is_near(sop_yaw_gain, p.sop_yaw_gain, eps)) return false;
803 [ + + ]: 51 : if (!is_near(gyro_gain, p.gyro_gain, eps)) return false;
804 [ + + ]: 50 : if (!is_near(steering_shaft_gain, p.steering_shaft_gain, eps)) return false;
805 [ + + ]: 49 : if (!is_near(ingame_ffb_gain, p.ingame_ffb_gain, eps)) return false;
806 [ + + ]: 48 : if (torque_source != p.torque_source) return false;
807 [ + + ]: 43 : if (torque_passthrough != p.torque_passthrough) return false;
808 : :
809 [ + + ]: 42 : if (!is_near(optimal_slip_angle, p.optimal_slip_angle, eps)) return false;
810 [ + + ]: 41 : if (!is_near(optimal_slip_ratio, p.optimal_slip_ratio, eps)) return false;
811 [ + + ]: 40 : if (!is_near(steering_shaft_smoothing, p.steering_shaft_smoothing, eps)) return false;
812 [ + + ]: 39 : if (!is_near(gyro_smoothing, p.gyro_smoothing, eps)) return false;
813 [ + + ]: 38 : if (!is_near(yaw_smoothing, p.yaw_smoothing, eps)) return false;
814 [ + + ]: 37 : if (!is_near(chassis_smoothing, p.chassis_smoothing, eps)) return false;
815 : :
816 [ + + ]: 36 : if (flatspot_suppression != p.flatspot_suppression) return false;
817 [ + + ]: 35 : if (!is_near(notch_q, p.notch_q, eps)) return false;
818 [ + + ]: 34 : if (!is_near(flatspot_strength, p.flatspot_strength, eps)) return false;
819 : :
820 [ + + ]: 33 : if (static_notch_enabled != p.static_notch_enabled) return false;
821 [ + + ]: 32 : if (!is_near(static_notch_freq, p.static_notch_freq, eps)) return false;
822 [ + + ]: 31 : if (!is_near(static_notch_width, p.static_notch_width, eps)) return false;
823 [ + + ]: 30 : if (!is_near(yaw_kick_threshold, p.yaw_kick_threshold, eps)) return false;
824 : :
825 : : // v0.7.164 (Issue #322)
826 [ - + ]: 29 : if (!is_near(unloaded_yaw_gain, p.unloaded_yaw_gain, eps)) return false;
827 [ - + ]: 29 : if (!is_near(unloaded_yaw_threshold, p.unloaded_yaw_threshold, eps)) return false;
828 [ - + ]: 29 : if (!is_near(unloaded_yaw_sens, p.unloaded_yaw_sens, eps)) return false;
829 [ - + ]: 29 : if (!is_near(unloaded_yaw_gamma, p.unloaded_yaw_gamma, eps)) return false;
830 [ - + ]: 29 : if (!is_near(unloaded_yaw_punch, p.unloaded_yaw_punch, eps)) return false;
831 [ - + ]: 29 : if (!is_near(power_yaw_gain, p.power_yaw_gain, eps)) return false;
832 [ - + ]: 29 : if (!is_near(power_yaw_threshold, p.power_yaw_threshold, eps)) return false;
833 [ - + ]: 29 : if (!is_near(power_slip_threshold, p.power_slip_threshold, eps)) return false;
834 [ - + ]: 29 : if (!is_near(power_yaw_gamma, p.power_yaw_gamma, eps)) return false;
835 [ - + ]: 29 : if (!is_near(power_yaw_punch, p.power_yaw_punch, eps)) return false;
836 : :
837 [ + + ]: 29 : if (!is_near(speed_gate_lower, p.speed_gate_lower, eps)) return false;
838 [ + + ]: 28 : if (!is_near(speed_gate_upper, p.speed_gate_upper, eps)) return false;
839 : :
840 [ + + ]: 27 : if (!is_near(road_fallback_scale, p.road_fallback_scale, eps)) return false;
841 [ + + ]: 26 : if (understeer_affects_sop != p.understeer_affects_sop) return false;
842 : :
843 [ + + ]: 25 : if (slope_detection_enabled != p.slope_detection_enabled) return false;
844 [ + + ]: 24 : if (slope_sg_window != p.slope_sg_window) return false;
845 [ - + ]: 23 : if (!is_near(slope_sensitivity, p.slope_sensitivity, eps)) return false;
846 : :
847 [ + + ]: 23 : if (!is_near(slope_smoothing_tau, p.slope_smoothing_tau, eps)) return false;
848 [ + + ]: 22 : if (!is_near(slope_alpha_threshold, p.slope_alpha_threshold, eps)) return false;
849 [ + + ]: 21 : if (!is_near(slope_decay_rate, p.slope_decay_rate, eps)) return false;
850 [ + + ]: 20 : if (slope_confidence_enabled != p.slope_confidence_enabled) return false;
851 [ + + ]: 19 : if (!is_near(slope_min_threshold, p.slope_min_threshold, eps)) return false;
852 [ + + ]: 18 : if (!is_near(slope_max_threshold, p.slope_max_threshold, eps)) return false;
853 : :
854 [ + + ]: 17 : if (!is_near(slope_g_slew_limit, p.slope_g_slew_limit, eps)) return false;
855 [ + + ]: 16 : if (slope_use_torque != p.slope_use_torque) return false;
856 [ - + ]: 15 : if (!is_near(slope_torque_sensitivity, p.slope_torque_sensitivity, eps)) return false;
857 [ + + ]: 15 : if (!is_near(slope_confidence_max_rate, p.slope_confidence_max_rate, eps)) return false;
858 : :
859 [ - + ]: 14 : if (rest_api_enabled != p.rest_api_enabled) return false;
860 [ - + ]: 14 : if (rest_api_port != p.rest_api_port) return false;
861 : :
862 : 14 : return true;
863 : : }
864 : : };
865 : :
866 : : class Config {
867 : : public:
868 : : static std::string m_config_path; // Default: "config.ini"
869 : : static void Save(const FFBEngine& engine, const std::string& filename = "");
870 : : static void Load(FFBEngine& engine, const std::string& filename = "");
871 : :
872 : : // Preset Management
873 : : static std::vector<Preset> presets;
874 : : static std::string m_last_preset_name; // NEW (v0.7.14)
875 : : static void LoadPresets(); // Populates presets vector
876 : : static void ApplyPreset(int index, FFBEngine& engine);
877 : :
878 : : // NEW: Add a user preset
879 : : static void AddUserPreset(const std::string& name, const FFBEngine& engine);
880 : :
881 : : // NEW: Delete and Duplicate (v0.7.14)
882 : : static void DeletePreset(int index, const FFBEngine& engine);
883 : : static void DuplicatePreset(int index, const FFBEngine& engine);
884 : : static bool IsEngineDirtyRelativeToPreset(int index, const FFBEngine& engine);
885 : :
886 : : // NEW: Import/Export (v0.7.12)
887 : : static void ExportPreset(int index, const std::string& filename);
888 : : static bool ImportPreset(const std::string& filename, const FFBEngine& engine);
889 : :
890 : : // NEW: Persist selected device
891 : : static std::string m_last_device_guid;
892 : :
893 : : // Global App Settings (not part of FFB Physics)
894 : : static bool m_always_on_top; // NEW: Keep window on top
895 : : static bool m_auto_start_logging; // NEW: Auto-start logging
896 : : static std::string m_log_path; // NEW: Path to save logs
897 : :
898 : : // Window Geometry Persistence (v0.5.5)
899 : : static int win_pos_x, win_pos_y;
900 : : static int win_w_small, win_h_small; // Dimensions for Config Only
901 : : static int win_w_large, win_h_large; // Dimensions for Config + Graphs
902 : : static bool show_graphs; // Remember if graphs were open
903 : :
904 : : // Persistent storage for vehicle static loads (v0.7.70)
905 : : static std::map<std::string, double> m_saved_static_loads;
906 : : static std::recursive_mutex m_static_loads_mutex;
907 : :
908 : : // Flag to request a save from the main thread (avoids File I/O on FFB thread)
909 : : static std::atomic<bool> m_needs_save;
910 : :
911 : : // Thread-safe access to static loads map (v0.7.70)
912 : : static void SetSavedStaticLoad(const std::string& vehicleName, double value);
913 : : static bool GetSavedStaticLoad(const std::string& vehicleName, double& value);
914 : :
915 : : private:
916 : : // Helper for parsing preset lines (v0.7.12)
917 : : static void ParsePresetLine(const std::string& line, Preset& p, std::string& version, bool& needs_save, bool& legacy_torque_hack, float& legacy_torque_val);
918 : : static bool ParseSystemLine(const std::string& key, const std::string& value, Preset& p, std::string& version, bool& needs_save, bool& legacy_torque_hack, float& legacy_torque_val);
919 : : static bool ParsePhysicsLine(const std::string& key, const std::string& value, Preset& p);
920 : : static bool ParseBrakingLine(const std::string& key, const std::string& value, Preset& p);
921 : : static bool ParseVibrationLine(const std::string& key, const std::string& value, Preset& p);
922 : : static bool ParseSafetyLine(const std::string& key, const std::string& value, Preset& p);
923 : :
924 : : static bool SyncSystemLine(const std::string& key, const std::string& value, FFBEngine& engine, std::string& version, bool& legacy_torque_hack, float& legacy_torque_val, bool& needs_save);
925 : : static bool SyncPhysicsLine(const std::string& key, const std::string& value, FFBEngine& engine, std::string& version, bool& needs_save);
926 : : static bool SyncBrakingLine(const std::string& key, const std::string& value, FFBEngine& engine);
927 : : static bool SyncVibrationLine(const std::string& key, const std::string& value, FFBEngine& engine);
928 : : static bool SyncSafetyLine(const std::string& key, const std::string& value, FFBEngine& engine);
929 : :
930 : : // Helper for writing preset fields (v0.7.12)
931 : : static void WritePresetFields(std::ofstream& file, const Preset& p);
932 : : };
933 : :
934 : :
935 : :
936 : :
937 : : #endif
938 : :
939 : :
940 : :
941 : :
942 : :
|