#pragma once #include #include #include #include namespace microbu { struct C5RadioConfig { std::uint16_t channel_number = 180; double transmit_power_dbm = 10.0; unsigned receive_queue_length = 16; bool laboratory_transmission = true; }; #if CONFIG_MICROBU_TEST_CHANNEL /// Result of polling the PHY's own (undocumented) CCA state as fast as possible for a /// fixed window (feasibility probe for EN 303 797 clause 4.6.2) /// to determine if the platform can observe channel-busy state at >=1 kHz at all. struct CcaSampleResult { std::uint32_t samples = 0; std::uint32_t duration_us = 0; std::uint32_t min_delta_us = 0; std::uint32_t max_delta_us = 0; std::uint32_t busy_count = 0; std::int32_t first_cca = 0; std::int32_t last_cca = 0; std::int32_t noise_floor_dbm = 0; std::int32_t cca_total_cycles_delta = 0; // out[0]/0x600a7c5c: 40 MHz free-running counter std::int32_t cca_busy_cycles_delta = 0; // out[1]/0x600a7c60: increments while channel busy std::int32_t cca_status = 0; }; #endif /// Snapshot of the ESP32-C5 27-bit hardware PHY counters for ETSI TS 102 687 / EN 303 797 DCC. /// total_cycles: 40 MHz FE clock cycles (register 0x600a7c5c[26:0]). /// busy_cycles: cycles during which received RF energy exceeded the CCA threshold (register 0x600a7c60[26:0]). struct CcaCounters { std::uint32_t total_cycles = 0; std::uint32_t busy_cycles = 0; }; /** * ITS-G5 802.11p Radio Access Adapter for ESP32-C5 (EN 303 797 Annex B.2). * Direct Wi-Fi promiscuous RX mode on 5.9 GHz (ITS-G5 Channel 180, 10 MHz BW) * and 802.11p frame transmission via esp_wifi_80211_tx_custom. */ class C5Radio final : public vanetza_idf::Access { public: using Receive = std::function; using Capture = std::function; /// @brief Constructs a radio adapter with the given configuration (not yet started). /// @param config Radio configuration; defaults to channel 180, 10 dBm, 16-deep RX queue. explicit C5Radio(C5RadioConfig config = {}); /// @brief Stops the radio and releases all resources. ~C5Radio() override; C5Radio(const C5Radio&) = delete; C5Radio& operator=(const C5Radio&) = delete; /// @brief Initializes Wi-Fi in promiscuous 802.11p mode and starts the radio. /// @return ESP_OK on success, otherwise an ESP-IDF error code. esp_err_t start(); /// @brief Stops the radio and releases all resources. void stop(); // vanetza_idf::Access /// @brief Encodes and transmits an ITS-G5 frame requested by the access layer. /// @param request Frame and transmit parameters from the access layer. /// @return Result of the transmit attempt. vanetza_idf::Result request(vanetza_idf::AlDataRequest request) override; /// @brief Dispatches queued received frames to the owning task callback. /// @param receive Invoked once per queued frame with its decoded indication. /// @param capture Optional; invoked with the raw frame bytes, RSSI and timestamp for diagnostics. void poll(const Receive& receive, const Capture& capture = {}); #if CONFIG_MICROBU_TEST_CHANNEL /// @brief Sends a burst of raw 802.11p test frames for RF characterization. /// @param channel ITS-G5 channel number to transmit on. /// @param power_dbm Transmit power in dBm. /// @param mcs 802.11p modulation and coding scheme index. /// @param count Number of frames to send. /// @param interval_ms Interval between frames in milliseconds. /// @param payload_len Length of each frame's payload in bytes. /// @return ESP_OK if all frames were sent, otherwise the last transmit error. esp_err_t transmit_burst(std::uint16_t channel, double power_dbm, unsigned mcs, unsigned count, unsigned interval_ms, std::size_t payload_len); /// @brief Polls the CCA state as fast as possible for a fixed window (see CcaSampleResult); requires start(). /// @note This is a diagnostic tool/ feasibility probe for EN 303 797 clause 4.6.2 to determine if the platform can observe channel-busy state at >=1 kHz at all. /// @param duration_ms Duration of the sampling window in milliseconds. /// @return Sampling statistics and counter deltas over the window. CcaSampleResult sample_cca(unsigned duration_ms); #endif /// @brief Non-blocking read of the hardware CCA counters for periodic DCC evaluation. /// Continuous 40 MHz hardware integration satisfies EN 303 797 Profile-1 (>=1 kHz) with zero CPU busy-wait. /// @return Snapshot of total and busy cycle counts. CcaCounters read_cca_counters() const; /// @brief Calculates Channel Busy Ratio (CBR) between two counter snapshots: delta(busy) / delta(total). /// @param current More recent counter snapshot. /// @param previous Earlier counter snapshot. /// @return CBR in [0, 1]. static double calculate_cbr(const CcaCounters& current, const CcaCounters& previous); /// @brief Number of frames dropped since start() due to queue overflow or invalid length. /// @return Dropped frame count. std::uint32_t dropped_frames() const; private: class Impl; std::unique_ptr impl_; }; } // namespace microbu