LCOV - code coverage report
Current view: top level - logging - AsyncLogger.h (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 98.1 % 158 155
Test Date: 2026-03-18 19:01:10 Functions: 100.0 % 16 16
Branches: 62.5 % 160 100

             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
        

Generated by: LCOV version 2.0-1