| 132 | } |
| 133 | |
| 134 | void consumerLoop() { |
| 135 | // Rely solely on pop()'s internal lock + shutdown signal, |
| 136 | // NOT on an unguarded queue_.size() check which would be a data race. |
| 137 | while (true) { |
| 138 | auto maybe_reading = queue_.pop(); |
| 139 | if (!maybe_reading) break; // shutdown() was called and queue is drained |
| 140 | |
| 141 | // Simple magnitude filter (Module 03: arithmetic) |
| 142 | const auto& r = *maybe_reading; |
| 143 | [[maybe_unused]] double magnitude = std::sqrt( |
| 144 | r.accel_x * r.accel_x + |
| 145 | r.accel_y * r.accel_y + |
| 146 | r.accel_z * r.accel_z); |
| 147 | |
| 148 | // Latency tracking (Module 15: std::chrono profiling) |
| 149 | auto latency = std::chrono::steady_clock::now() - r.timestamp; |
| 150 | auto latency_us = std::chrono::duration_cast< |
| 151 | std::chrono::microseconds>(latency).count(); |
| 152 | |
| 153 | if (latency_us > 5000) { |
| 154 | std::cerr << "[WARN] " << name_ << " pipeline latency: " |
| 155 | << latency_us << " µs\n"; |
| 156 | } |
| 157 | |
| 158 | processed_count_.fetch_add(1, std::memory_order_relaxed); |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | std::string name_; |
| 163 | BoundedQueue<IMUReading, config::QUEUE_CAPACITY> queue_; |