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