Branch data Line data Source code
1 : : #ifndef ASYNCLOGGER_H
2 : : #define ASYNCLOGGER_H
3 : :
4 : : #include <vector>
5 : : #include <string>
6 : : #include <fstream>
7 : : #include <thread>
8 : : #include <mutex>
9 : : #include <condition_variable>
10 : : #include <atomic>
11 : : #include <chrono>
12 : : #include <iomanip>
13 : : #include <sstream>
14 : : #include <algorithm> // For std::max
15 : : #include <filesystem>
16 : : #include <cstdint> // For uint8_t
17 : : #include <lz4.h> // For LZ4 compression
18 : :
19 : : // Forward declaration
20 : : struct TelemInfoV01;
21 : : class FFBEngine;
22 : :
23 : : // Log frame structure - captures one physics tick
24 : : #pragma pack(push, 1)
25 : : struct LogFrame {
26 : : double timestamp; // Time
27 : : double delta_time; // DeltaTime
28 : :
29 : : // --- PROCESSED 400Hz DATA (Smooth) ---
30 : : float speed; // Speed
31 : : float lat_accel; // LatAccel
32 : : float long_accel; // LongAccel
33 : : float yaw_rate; // YawRate
34 : :
35 : : float steering; // Steering
36 : : float throttle; // Throttle
37 : : float brake; // Brake
38 : :
39 : : // --- RAW 100Hz GAME DATA (Step-function) ---
40 : : float raw_steering;
41 : : float raw_throttle;
42 : : float raw_brake;
43 : : float raw_lat_accel;
44 : : float raw_long_accel;
45 : : float raw_game_yaw_accel;
46 : : float raw_game_shaft_torque;
47 : : float raw_game_gen_torque;
48 : :
49 : : float raw_load_fl;
50 : : float raw_load_fr;
51 : : float raw_load_rl;
52 : : float raw_load_rr;
53 : :
54 : : float raw_slip_vel_lat_fl;
55 : : float raw_slip_vel_lat_fr;
56 : : float raw_slip_vel_lat_rl;
57 : : float raw_slip_vel_lat_rr;
58 : :
59 : : float raw_slip_vel_long_fl;
60 : : float raw_slip_vel_long_fr;
61 : : float raw_slip_vel_long_rl;
62 : : float raw_slip_vel_long_rr;
63 : :
64 : : float raw_ride_height_fl;
65 : : float raw_ride_height_fr;
66 : : float raw_ride_height_rl;
67 : : float raw_ride_height_rr;
68 : :
69 : : float raw_susp_deflection_fl;
70 : : float raw_susp_deflection_fr;
71 : : float raw_susp_deflection_rl;
72 : : float raw_susp_deflection_rr;
73 : :
74 : : float raw_susp_force_fl;
75 : : float raw_susp_force_fr;
76 : : float raw_susp_force_rl;
77 : : float raw_susp_force_rr;
78 : :
79 : : float raw_brake_pressure_fl;
80 : : float raw_brake_pressure_fr;
81 : : float raw_brake_pressure_rl;
82 : : float raw_brake_pressure_rr;
83 : :
84 : : float raw_rotation_fl;
85 : : float raw_rotation_fr;
86 : : float raw_rotation_rl;
87 : : float raw_rotation_rr;
88 : :
89 : : // --- ALGORITHM STATE (400Hz) ---
90 : : float slip_angle_fl;
91 : : float slip_angle_fr;
92 : : float slip_angle_rl;
93 : : float slip_angle_rr;
94 : :
95 : : float slip_ratio_fl;
96 : : float slip_ratio_fr;
97 : : float slip_ratio_rl;
98 : : float slip_ratio_rr;
99 : :
100 : : float grip_fl;
101 : : float grip_fr;
102 : : float grip_rl;
103 : : float grip_rr;
104 : :
105 : : float load_fl;
106 : : float load_fr;
107 : : float load_rl;
108 : : float load_rr;
109 : :
110 : : float ride_height_fl;
111 : : float ride_height_fr;
112 : : float ride_height_rl;
113 : : float ride_height_rr;
114 : :
115 : : float susp_deflection_fl;
116 : : float susp_deflection_fr;
117 : : float susp_deflection_rl;
118 : : float susp_deflection_rr;
119 : :
120 : : float calc_slip_angle_front;
121 : : float calc_slip_angle_rear;
122 : : float calc_grip_front;
123 : : float calc_grip_rear;
124 : : float grip_delta;
125 : : float calc_rear_lat_force;
126 : :
127 : : float smoothed_yaw_accel;
128 : : float lat_load_norm;
129 : :
130 : : float dG_dt;
131 : : float dAlpha_dt;
132 : : float slope_current;
133 : : float slope_raw_unclamped;
134 : : float slope_numerator;
135 : : float slope_denominator;
136 : : float hold_timer;
137 : : float input_slip_smoothed;
138 : : float slope_smoothed;
139 : : float confidence;
140 : :
141 : : float surface_type_fl;
142 : : float surface_type_fr;
143 : : float slope_torque;
144 : : float slew_limited_g;
145 : :
146 : : float session_peak_torque;
147 : : float long_load_factor;
148 : : float structural_mult;
149 : : float vibration_mult;
150 : : float steering_angle_deg;
151 : : float steering_range_deg;
152 : : float debug_freq;
153 : : float tire_radius;
154 : :
155 : : // --- FFB COMPONENTS (400Hz) ---
156 : : float ffb_total;
157 : : float ffb_base;
158 : : float ffb_understeer_drop;
159 : : float ffb_oversteer_boost;
160 : : float ffb_sop;
161 : : float ffb_rear_torque;
162 : : float ffb_scrub_drag;
163 : : float ffb_yaw_kick;
164 : : float ffb_gyro_damping;
165 : : float ffb_road_texture;
166 : : float ffb_slide_texture;
167 : : float ffb_lockup_vibration;
168 : : float ffb_spin_vibration;
169 : : float ffb_bottoming_crunch;
170 : : float ffb_abs_pulse;
171 : : float ffb_soft_lock;
172 : :
173 : : float extrapolated_yaw_accel;
174 : : float derived_yaw_accel;
175 : :
176 : : float ffb_shaft_torque;
177 : : float ffb_gen_torque;
178 : : float ffb_grip_factor;
179 : : float speed_gate;
180 : : float front_load_peak_ref;
181 : :
182 : : float approx_load_fl;
183 : : float approx_load_fr;
184 : : float approx_load_rl;
185 : : float approx_load_rr;
186 : :
187 : : // --- SYSTEM (400Hz) ---
188 : : float physics_rate;
189 : : uint8_t clipping;
190 : : uint8_t warn_bits;
191 : : uint8_t marker;
192 : : };
193 : : #pragma pack(pop)
194 : :
195 : : // Session metadata for header
196 : : struct SessionInfo {
197 : : std::string driver_name;
198 : : std::string vehicle_name;
199 : : std::string vehicle_class; // v0.7.132
200 : : std::string vehicle_brand; // v0.7.132
201 : : std::string track_name;
202 : : std::string app_version;
203 : :
204 : : // Key settings snapshot
205 : : float gain;
206 : : float understeer_effect;
207 : : float sop_effect;
208 : : float lat_load_effect; // v0.7.152
209 : : float long_load_effect;
210 : : float sop_scale; // v0.7.152
211 : : float sop_smoothing; // v0.7.152
212 : : float optimal_slip_angle; // v0.7.173
213 : : float optimal_slip_ratio; // v0.7.173
214 : : bool slope_enabled;
215 : : float slope_sensitivity;
216 : : float slope_threshold;
217 : : float slope_alpha_threshold;
218 : : float slope_decay_rate;
219 : : bool torque_passthrough; // v0.7.63
220 : : bool dynamic_normalization;
221 : : bool auto_load_normalization;
222 : : };
223 : :
224 : : class AsyncLogger {
225 : : public:
226 : 23389 : static AsyncLogger& Get() {
227 [ + + + - : 23389 : static AsyncLogger instance;
+ - - - ]
228 : 23389 : return instance;
229 : : }
230 : :
231 : : // Start logging - called from GUI
232 : 19 : void Start(const SessionInfo& info, const std::string& base_path = "") {
233 [ + - ]: 19 : std::lock_guard<std::mutex> lock(m_mutex);
234 [ + + ]: 19 : if (m_running) return;
235 : :
236 [ + - ]: 18 : m_buffer_active.reserve(BUFFER_THRESHOLD * 2);
237 [ + - ]: 18 : m_buffer_writing.reserve(BUFFER_THRESHOLD * 2);
238 : 18 : m_frame_count = 0;
239 : 18 : m_file_size_bytes = 0; // Reset for new file (#269)
240 : 18 : m_pending_marker = false;
241 : 18 : m_decimation_counter = 0;
242 : :
243 : : // Generate filename
244 : 18 : auto now = std::chrono::system_clock::now();
245 : 18 : auto in_time_t = std::chrono::system_clock::to_time_t(now);
246 : :
247 : : // Use localtime_s for thread safety (MSVC)
248 : : std::tm time_info;
249 : : #ifdef _WIN32
250 : : localtime_s(&time_info, &in_time_t);
251 : : #else
252 : 18 : localtime_r(&in_time_t, &time_info);
253 : : #endif
254 : :
255 [ + - ]: 18 : std::stringstream ss;
256 [ + - ]: 18 : ss << std::put_time(&time_info, "%Y-%m-%d_%H-%M-%S");
257 [ + - ]: 18 : std::string timestamp_str = ss.str();
258 : :
259 [ + - ]: 18 : std::string brand = SanitizeFilename(info.vehicle_brand);
260 [ + - ]: 18 : std::string vclass = SanitizeFilename(info.vehicle_class);
261 [ + - ]: 18 : std::string track = SanitizeFilename(info.track_name);
262 : :
263 [ + - ]: 18 : std::string path_prefix = base_path;
264 [ + - ]: 18 : if (!path_prefix.empty()) {
265 : : // Ensure directory exists
266 : : try {
267 [ + - + + ]: 19 : std::filesystem::create_directories(path_prefix);
268 : 1 : } catch (...) {
269 : : // Ignore, let file open fail if necessary
270 [ + - ]: 1 : }
271 : :
272 [ + + + - : 18 : if (path_prefix.back() != '/' && path_prefix.back() != '\\') {
+ + ]
273 [ + - ]: 17 : path_prefix += "/";
274 : : }
275 : : }
276 : :
277 [ + - + - : 18 : m_filename = path_prefix + "lmuffb_log_" + timestamp_str + "_" + brand + "_" + vclass + "_" + track + ".bin";
+ - + - +
- + - + -
+ - + - ]
278 : :
279 : : // Open file in binary mode
280 [ + - ]: 18 : m_file.open(m_filename, std::ios::out | std::ios::binary);
281 [ + + ]: 18 : if (m_file.is_open()) {
282 [ + - ]: 17 : WriteHeader(info);
283 : 17 : m_running = true;
284 [ + - ]: 17 : m_worker = std::thread(&AsyncLogger::WorkerThread, this);
285 : : }
286 [ + + ]: 19 : }
287 : :
288 : : // Stop logging and flush
289 : 32 : void Stop() noexcept {
290 : : try {
291 : : {
292 [ + - ]: 32 : std::lock_guard<std::mutex> lock(m_mutex);
293 [ + + ]: 32 : if (!m_running) return;
294 : 17 : m_running = false;
295 [ + + ]: 32 : }
296 : 17 : m_cv.notify_one();
297 [ + - ]: 17 : if (m_worker.joinable()) {
298 [ + - ]: 17 : m_worker.join();
299 : : }
300 [ + - ]: 17 : if (m_file.is_open()) {
301 [ + - ]: 17 : m_file.close();
302 : : }
303 : 17 : m_buffer_active.clear();
304 : 17 : m_buffer_writing.clear();
305 : 0 : } catch (...) {
306 : : // Destructor/Stop should not throw
307 : 0 : }
308 : : }
309 : :
310 : : // Log a frame - called from FFB thread (must be fast!)
311 : 2968 : void Log(const LogFrame& frame) {
312 [ + + ]: 2968 : if (!m_running) return;
313 : :
314 : : // Decimation: 400Hz -> 400Hz (v0.7.126: decimation removed, DECIMATION_FACTOR=1)
315 [ - + - - : 2967 : if (++m_decimation_counter < DECIMATION_FACTOR && !frame.marker && !m_pending_marker) {
- - - + ]
316 : 0 : return;
317 : : }
318 : 2967 : m_decimation_counter = 0; // Reset counter to prevent overflow (Review fix)
319 : :
320 : 2967 : LogFrame f = frame;
321 [ + + ]: 2967 : if (m_pending_marker) {
322 : 3 : f.marker = 1;
323 : 3 : m_pending_marker = false;
324 : : }
325 : :
326 : 2967 : bool should_notify = false;
327 : : {
328 [ + - ]: 2967 : std::lock_guard<std::mutex> lock(m_mutex);
329 [ - + ]: 2967 : if (!m_running) return;
330 [ + - ]: 2967 : m_buffer_active.push_back(f);
331 : 2967 : should_notify = (m_buffer_active.size() >= BUFFER_THRESHOLD);
332 [ + - ]: 2967 : }
333 : :
334 : 2967 : m_frame_count++;
335 : :
336 [ + + ]: 2967 : if (should_notify) {
337 : 726 : m_cv.notify_one();
338 : : }
339 : : }
340 : :
341 : : // Trigger a user marker
342 : 3 : void SetMarker() { m_pending_marker = true; }
343 : :
344 : : // Toggle compression
345 : 4 : void EnableCompression(bool enable) { m_lz4_enabled = enable; }
346 : :
347 : : // Status getters
348 : 21382 : bool IsLogging() const { return m_running; }
349 : 8 : size_t GetFrameCount() const { return m_frame_count; }
350 : 6 : std::string GetFilename() const { return m_filename; }
351 : 3 : size_t GetFileSizeBytes() const { return m_file_size_bytes; }
352 : :
353 : : private:
354 : 1 : AsyncLogger() : m_running(false), m_pending_marker(false), m_frame_count(0), m_decimation_counter(0),
355 : 1 : m_file_size_bytes(0), m_last_flush_time(std::chrono::steady_clock::now()),
356 : 1 : m_lz4_enabled(true) {}
357 : 1 : ~AsyncLogger() { Stop(); }
358 : :
359 : : // No copy
360 : : AsyncLogger(const AsyncLogger&) = delete;
361 : : AsyncLogger& operator=(const AsyncLogger&) = delete;
362 : :
363 : 17 : void WorkerThread() {
364 : 17 : std::vector<char> compressed_buffer;
365 : 17 : std::vector<LogFrame> local_buffer;
366 : : while (true) {
367 : : {
368 [ + - ]: 46 : std::unique_lock<std::mutex> lock(m_mutex);
369 [ + - + + : 101 : m_cv.wait(lock, [this] { return !m_running || !m_buffer_active.empty(); });
+ + ]
370 : :
371 [ + + ]: 46 : if (!m_buffer_active.empty()) {
372 : 29 : local_buffer.swap(m_buffer_active);
373 [ + - ]: 17 : } else if (!m_running) {
374 : 17 : break;
375 : : }
376 [ + + ]: 46 : }
377 : :
378 : : // Write buffer to disk
379 [ + - ]: 29 : if (!local_buffer.empty()) {
380 : 29 : size_t src_size = local_buffer.size() * sizeof(LogFrame);
381 : 29 : const char* src_ptr = reinterpret_cast<const char*>(local_buffer.data());
382 : :
383 [ + + ]: 29 : if (m_lz4_enabled) {
384 [ + - ]: 3 : int max_dst_size = LZ4_compressBound((int)src_size);
385 [ + - ]: 3 : compressed_buffer.resize(max_dst_size);
386 : :
387 [ + - ]: 3 : int compressed_size = LZ4_compress_default(src_ptr, compressed_buffer.data(), (int)src_size, max_dst_size);
388 : :
389 [ + - ]: 3 : if (compressed_size > 0) {
390 : : // Write block size header (compressed, then uncompressed)
391 : 3 : uint32_t header[2] = { (uint32_t)compressed_size, (uint32_t)src_size };
392 [ + - ]: 3 : m_file.write(reinterpret_cast<const char*>(header), 8);
393 : : // Write payload
394 [ + - ]: 3 : m_file.write(compressed_buffer.data(), compressed_size);
395 : 3 : m_file_size_bytes += (8 + (size_t)compressed_size);
396 : : }
397 : : } else {
398 [ + - ]: 26 : m_file.write(src_ptr, src_size);
399 : 26 : m_file_size_bytes += src_size;
400 : : }
401 : 29 : local_buffer.clear();
402 : : }
403 : :
404 : : // Periodic flush to minimize data loss on crash
405 : 29 : auto now = std::chrono::steady_clock::now();
406 [ + - + - ]: 29 : auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - m_last_flush_time).count();
407 [ + + ]: 29 : if (elapsed >= FLUSH_INTERVAL_SECONDS) {
408 [ + - ]: 1 : m_file.flush();
409 : 1 : m_last_flush_time = now;
410 : : }
411 : 29 : }
412 : 17 : }
413 : :
414 : 17 : void WriteHeader(const SessionInfo& info) {
415 : 17 : m_file << "# LMUFFB Telemetry Log v1.2\n";
416 : 17 : m_file << "# App Version: " << info.app_version << "\n";
417 [ + + ]: 17 : m_file << "# Compression: " << (m_lz4_enabled ? "LZ4" : "None") << "\n";
418 : 17 : m_file << "# ========================\n";
419 : 17 : m_file << "# Session Info\n";
420 : 17 : m_file << "# ========================\n";
421 : 17 : m_file << "# Driver: " << info.driver_name << "\n";
422 : 17 : m_file << "# Vehicle: " << info.vehicle_name << "\n";
423 : 17 : m_file << "# Car Class: " << info.vehicle_class << "\n";
424 : 17 : m_file << "# Car Brand: " << info.vehicle_brand << "\n";
425 : 17 : m_file << "# Track: " << info.track_name << "\n";
426 : 17 : m_file << "# ========================\n";
427 : 17 : m_file << "# FFB Settings\n";
428 : 17 : m_file << "# ========================\n";
429 : 17 : m_file << "# Gain: " << info.gain << "\n";
430 : 17 : m_file << "# Understeer Effect: " << info.understeer_effect << "\n";
431 : 17 : m_file << "# SoP Effect: " << info.sop_effect << "\n";
432 : 17 : m_file << "# Lateral Load Effect: " << info.lat_load_effect << "\n";
433 : 17 : m_file << "# Long Load Effect: " << info.long_load_effect << "\n";
434 : 17 : m_file << "# SoP Scale: " << info.sop_scale << "\n";
435 : 17 : m_file << "# SoP Smoothing: " << info.sop_smoothing << "\n";
436 : 17 : m_file << "# Optimal Slip Angle: " << info.optimal_slip_angle << "\n";
437 : 17 : m_file << "# Optimal Slip Ratio: " << info.optimal_slip_ratio << "\n";
438 [ + + ]: 17 : m_file << "# Slope Detection: " << (info.slope_enabled ? "Enabled" : "Disabled") << "\n";
439 : 17 : m_file << "# Slope Sensitivity: " << info.slope_sensitivity << "\n";
440 : 17 : m_file << "# Slope Threshold: " << info.slope_threshold << "\n";
441 : 17 : m_file << "# Slope Alpha Threshold: " << info.slope_alpha_threshold << "\n";
442 : 17 : m_file << "# Slope Decay Rate: " << info.slope_decay_rate << "\n";
443 [ + + ]: 17 : m_file << "# Torque Passthrough: " << (info.torque_passthrough ? "Enabled" : "Disabled") << "\n";
444 [ + + ]: 17 : m_file << "# Dynamic Normalization: " << (info.dynamic_normalization ? "Enabled" : "Disabled") << "\n";
445 [ + + ]: 17 : m_file << "# Auto Load Normalization: " << (info.auto_load_normalization ? "Enabled" : "Disabled") << "\n";
446 : 17 : m_file << "# ========================\n";
447 : :
448 : : // CSV Header for human readability
449 : : m_file << "# Fields: Time,DeltaTime,Speed,LatAccel,LongAccel,YawRate,Steering,Throttle,Brake,"
450 : : << "RawSteering,RawThrottle,RawBrake,RawLatAccel,RawLongAccel,RawGameYawAccel,RawGameShaftTorque,RawGameGenTorque,"
451 : : << "RawLoadFL,RawLoadFR,RawLoadRL,RawLoadRR,"
452 : : << "RawSlipVelLatFL,RawSlipVelLatFR,RawSlipVelLatRL,RawSlipVelLatRR,"
453 : : << "RawSlipVelLongFL,RawSlipVelLongFR,RawSlipVelLongRL,RawSlipVelLongRR,"
454 : : << "RawRideHeightFL,RawRideHeightFR,RawRideHeightRL,RawRideHeightRR,"
455 : : << "RawSuspDeflectionFL,RawSuspDeflectionFR,RawSuspDeflectionRL,RawSuspDeflectionRR,"
456 : : << "RawSuspForceFL,RawSuspForceFR,RawSuspForceRL,RawSuspForceRR,"
457 : : << "RawBrakePressureFL,RawBrakePressureFR,RawBrakePressureRL,RawBrakePressureRR,"
458 : : << "RawRotationFL,RawRotationFR,RawRotationRL,RawRotationRR,"
459 : : << "SlipAngleFL,SlipAngleFR,SlipAngleRL,SlipAngleRR,"
460 : : << "SlipRatioFL,SlipRatioFR,SlipRatioRL,SlipRatioRR,"
461 : : << "GripFL,GripFR,GripRL,GripRR,"
462 : : << "LoadFL,LoadFR,LoadRL,LoadRR,"
463 : : << "RideHeightFL,RideHeightFR,RideHeightRL,RideHeightRR,"
464 : : << "SuspDeflectionFL,SuspDeflectionFR,SuspDeflectionRL,SuspDeflectionRR,"
465 : : << "CalcSlipAngleFront,CalcSlipAngleRear,CalcGripFront,CalcGripRear,GripDelta,CalcRearLatForce,"
466 : : << "SmoothedYawAccel,LatLoadNorm,"
467 : : << "dG_dt,dAlpha_dt,SlopeCurrent,SlopeRaw,SlopeNum,SlopeDenom,HoldTimer,InputSlipSmooth,SlopeSmoothed,Confidence,"
468 : : << "SurfaceFL,SurfaceFR,SlopeTorque,SlewLimitedG,"
469 : : << "SessionPeakTorque,LongitudinalLoadFactor,StructuralMult,VibrationMult,SteeringAngleDeg,SteeringRangeDeg,DebugFreq,TireRadius,"
470 : : << "FFBTotal,FFBBase,FFBUndersteerDrop,FFBOversteerBoost,FFBSoP,FFBRearTorque,FFBScrubDrag,FFBYawKick,FFBGyroDamping,FFBRoadTexture,FFBSlideTexture,FFBLockupVibration,FFBSpinVibration,FFBBottomingCrunch,FFBABSPulse,FFBSoftLock,"
471 : : << "ExtrapolatedYawAccel,DerivedYawAccel,"
472 : : << "FFBShaftTorque,FFBGenTorque,GripFactor,SpeedGate,FrontLoadPeakRef,"
473 : : << "ApproxLoadFL,ApproxLoadFR,ApproxLoadRL,ApproxLoadRR,"
474 : 17 : << "PhysicsRate,Clipping,WarnBits,Marker\n";
475 : 17 : m_file << "[DATA_START]\n";
476 : 17 : }
477 : :
478 : 54 : std::string SanitizeFilename(const std::string& input) {
479 : 54 : std::string out = input;
480 : : // Replace invalid Windows filename characters
481 : 54 : std::replace(out.begin(), out.end(), ' ', '_');
482 : 54 : std::replace(out.begin(), out.end(), '/', '_');
483 : 54 : std::replace(out.begin(), out.end(), '\\', '_');
484 : 54 : std::replace(out.begin(), out.end(), ':', '_');
485 : 54 : std::replace(out.begin(), out.end(), '*', '_');
486 : 54 : std::replace(out.begin(), out.end(), '?', '_');
487 : 54 : std::replace(out.begin(), out.end(), '"', '_');
488 : 54 : std::replace(out.begin(), out.end(), '<', '_');
489 : 54 : std::replace(out.begin(), out.end(), '>', '_');
490 : 54 : std::replace(out.begin(), out.end(), '|', '_');
491 : 54 : return out;
492 : : }
493 : :
494 : : std::ofstream m_file;
495 : : std::string m_filename;
496 : : std::thread m_worker;
497 : :
498 : : std::vector<LogFrame> m_buffer_active;
499 : : std::vector<LogFrame> m_buffer_writing;
500 : :
501 : : std::mutex m_mutex;
502 : : std::condition_variable m_cv;
503 : : std::atomic<bool> m_running;
504 : : std::atomic<bool> m_pending_marker;
505 : : std::atomic<size_t> m_frame_count;
506 : :
507 : : int m_decimation_counter;
508 : : std::atomic<size_t> m_file_size_bytes;
509 : : std::chrono::steady_clock::time_point m_last_flush_time;
510 : :
511 : : bool m_lz4_enabled; // Internal compression toggle
512 : :
513 : : static const int DECIMATION_FACTOR = 1; // 400Hz -> 400Hz
514 : : static const size_t BUFFER_THRESHOLD = 200; // ~0.5s of data
515 : : static const int FLUSH_INTERVAL_SECONDS = 5; // Flush every 5 seconds
516 : : };
517 : :
518 : : #endif // ASYNCLOGGER_H
|