Keep the colleague's microbu-esp32c5 tree in this repository

obu-firmware builds against vanetza-idf from microbu-esp32c5/external, but
that tree was gitignored, so a clone of this repository could not build the
firmware it ships. It is now committed here as ordinary files in its own
folder, microbu-esp32c5/: the colleague's commit cf4b99f plus the V2X2MAP
bridge's signature verification (--trust) used on the bench. Nothing is
fetched from or pushed to the colleague's repository; this repository and
its remotes carry everything. The folder's own .gitignore keeps build output,
downloaded components and private key material out, as it did there; the
committed file set is identical to that repository's tracked files.

The ESP32-C5 is still flashed from obu-firmware/, which only takes
vanetza-idf from microbu-esp32c5/, so the two stay separate folders.
FLASHING.md says how to take a newer version of the colleague's tree (copy
it over the folder, rebuild, test, commit).
This commit is contained in:
Ashin Walpola
2026-09-23 17:46:40 +02:00
parent 2f60623e18
commit 0e9525162d
9881 changed files with 1582523 additions and 17 deletions
@@ -0,0 +1,10 @@
set(CXX_SOURCES
access_category.cpp
data_rates.cpp
g5_link_layer.cpp
)
add_vanetza_component(access ${CXX_SOURCES})
target_link_libraries(access PUBLIC net)
add_test_subdirectory(tests)
@@ -0,0 +1,33 @@
#include <vanetza/access/access_category.hpp>
#include <iostream>
namespace vanetza
{
namespace access
{
std::ostream& operator<<(std::ostream& os, AccessCategory ac)
{
switch (ac) {
case AccessCategory::BK:
os << "AC_BK";
break;
case AccessCategory::BE:
os << "AC_BE";
break;
case AccessCategory::VI:
os << "AC_VI";
break;
case AccessCategory::VO:
os << "AC_VO";
break;
default:
os << "<unknown AC>";
break;
};
return os;
}
} // namespace access
} // namespace vanetza
@@ -0,0 +1,38 @@
#ifndef ACCESS_CATEGORY_HPP_QAWSOPED
#define ACCESS_CATEGORY_HPP_QAWSOPED
#include <cstdint>
#include <iosfwd>
namespace vanetza
{
namespace access
{
/**
* \enum AccessCategory
* \brief AccessCategory represents packet priority at link layer
*
* Each enumerator's value matches the user priority UP (802.1D)
* of the respective access category AC (802.11).
*
* See ETSI EN 302 663 V1.2.1 (2013-07), Table B.3
*/
enum class AccessCategory {
BK = 1, //!< Background (lowest priority)
BE = 3, //!< Best effort
VI = 5, //!< Video
VO = 7 //!< Voice (highest priority)
}; /**< \enum */
std::ostream& operator<<(std::ostream&, AccessCategory);
constexpr std::uint8_t user_priority(AccessCategory ac)
{
return static_cast<std::uint8_t>(ac) & 0x7;
}
} // namespace access
} // namespace vanetza
#endif /* ACCESS_CATEGORY_HPP_QAWSOPED */
@@ -0,0 +1,25 @@
#include "data_rates.hpp"
#include <cassert>
namespace vanetza
{
namespace access
{
std::size_t DataRateG5::data_length(std::size_t psdu) const
{
static const unsigned service_bits = 16;
static const unsigned tail_bits = 6;
const unsigned body = service_bits + tail_bits + psdu * 8;
unsigned padding = body % m_coded_bits_per_symbol;
if (padding > 0) {
padding = m_coded_bits_per_symbol - padding;
}
assert((body + padding) % 8 == 0);
return (body + padding) / 8;
}
} // namespace access
} // namespace vanetza
@@ -0,0 +1,54 @@
#ifndef DATA_RATES_802DOT11P_HPP_SL3RZPZO
#define DATA_RATES_802DOT11P_HPP_SL3RZPZO
#include <cstddef>
namespace vanetza
{
namespace access
{
class DataRateG5
{
public:
/**
* Create data rate for ITS-G5 band
* \param kbps kilo-bits per second transfer rate
* \param cbits number of coded bits per symbol
*/
constexpr DataRateG5(unsigned kbps, unsigned cbits) :
m_bytes_per_second(kbps * 1000 / 8),
m_coded_bits_per_symbol(cbits) {}
/**
* Get tranfer rate as number of bytes per second
* \return transfer rate
*/
unsigned bytes_per_second() const { return m_bytes_per_second; }
/**
* Calculate length of PHY data length
* \param psdu size of PSDU, i.e. MPDU (MAC header + payload)
* \return length in bytes
*/
std::size_t data_length(std::size_t psdu) const;
private:
unsigned m_bytes_per_second;
unsigned m_coded_bits_per_symbol;
};
static const DataRateG5 G5_3Mbps { 3000, 48 };
static const DataRateG5 G5_4dot5Mbps { 4500, 48 };
static const DataRateG5 G5_6Mbps { 6000, 96 };
static const DataRateG5 G5_9Mbps { 9000, 96 };
static const DataRateG5 G5_12Mbps { 12000, 192 };
static const DataRateG5 G5_18bps { 18000, 192 };
static const DataRateG5 G5_24Mbps { 24000, 288 };
static const DataRateG5 G5_27Mbps { 27000, 288 };
} // namespace access
} // namespace vanetza
#endif /* DATA_RATES_802DOT11P_HPP_SL3RZPZO */
@@ -0,0 +1,27 @@
#ifndef DATA_REQUEST_HPP_3OGGPFWF
#define DATA_REQUEST_HPP_3OGGPFWF
#include <vanetza/access/access_category.hpp>
#include <vanetza/common/byte_order.hpp>
#include <vanetza/net/mac_address.hpp>
namespace vanetza
{
namespace access
{
struct DataRequest
{
DataRequest() : access_category(AccessCategory::BK) {}
uint16be_t ether_type;
MacAddress source_addr;
MacAddress destination_addr;
AccessCategory access_category;
};
} // namespace access
} // namespace vanetza
#endif /* DATA_REQUEST_HPP_3OGGPFWF */
@@ -0,0 +1,25 @@
#ifndef ETHERTYPE_HPP_ED3SIJFX
#define ETHERTYPE_HPP_ED3SIJFX
#include <vanetza/common/byte_order.hpp>
namespace vanetza
{
namespace access
{
using EtherType = uint16be_t;
namespace ethertype
{
static const EtherType GeoNetworking = host_cast<uint16_t>(0x8947);
static const EtherType WSMP = host_cast<uint16_t>(0x88DC);
} // namespace ethertype
} // namespace access
} // namespace vanetza
#endif /* ETHERTYPE_HPP_ED3SIJFX */
@@ -0,0 +1,134 @@
#include <vanetza/access/g5_link_layer.hpp>
#include <vanetza/access/ethertype.hpp>
#include <cstring>
namespace vanetza
{
namespace access
{
static const ieee802::LlcSnapHeader default_llc_header { ethertype::GeoNetworking };
G5LinkLayer::G5LinkLayer() :
llc_snap_header(default_llc_header)
{
}
void serialize(OutputArchive& ar, const G5LinkLayer& g5)
{
serialize(ar, g5.mac_header);
serialize(ar, g5.llc_snap_header);
}
void deserialize(InputArchive& ar, G5LinkLayer& g5)
{
deserialize(ar, g5.mac_header);
deserialize(ar, g5.llc_snap_header);
}
bool check_fixed_fields(const G5LinkLayer& link_layer)
{
static const auto default_frame_control = ieee802::dot11::FrameControl::qos_data_frame();
static const ieee802::dot11::QosControl default_qos_control;
// all frame control flags are fixed for now
static const std::uint16_t frame_control_fixed = 0xFFFF;
// EOSP + A-MSDU + TXOP limit fixed, TID (LSB part = UP) and Ack policy are variable
static const std::uint16_t qos_control_fixed = 0xFF98;
const ieee802::dot11::QosDataHeader& mac = link_layer.mac_header;
const bool frame_control_ok =
(mac.frame_control.raw.get() & frame_control_fixed) == (default_frame_control.raw.get() & frame_control_fixed);
const bool qos_control_ok =
(mac.qos_control.raw.get() & qos_control_fixed) == (default_qos_control.raw.get() & qos_control_fixed);
return frame_control_ok && qos_control_ok &&
mac.sequence_control.fragment_number() == 0 &&
mac.bssid == ieee802::dot11::bssid_wildcard &&
link_layer.llc_snap_header == default_llc_header;
}
namespace ieee802
{
namespace dot11
{
// Operation outside of a BSS, must be set to wildcard (all bits 1)
const MacAddress bssid_wildcard = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
void QosControl::user_priority(AccessCategory access_category)
{
std::uint16_t tmp = raw.get();
tmp &= ~0x000F; // clear all TID bits
tmp |= static_cast<std::uint16_t>(access_category) & 0x07;
raw = static_cast<uint16be_t>(tmp);
}
FrameControl FrameControl::qos_data_frame()
{
FrameControl frame_control;
frame_control.raw = host_cast<std::uint16_t>(0x8800);
return frame_control;
}
void serialize(OutputArchive& ar, const QosDataHeader& mac)
{
serialize(ar, mac.frame_control.raw);
serialize(ar, mac.duration_or_id);
serialize(ar, mac.destination);
serialize(ar, mac.source);
serialize(ar, mac.bssid);
serialize(ar, mac.sequence_control.raw);
serialize(ar, mac.qos_control.raw);
}
void deserialize(InputArchive& ar, QosDataHeader& mac)
{
deserialize(ar, mac.frame_control.raw);
deserialize(ar, mac.duration_or_id);
deserialize(ar, mac.destination);
deserialize(ar, mac.source);
deserialize(ar, mac.bssid);
deserialize(ar, mac.sequence_control.raw);
deserialize(ar, mac.qos_control.raw);
}
} // namespace dot11
void serialize(OutputArchive& ar, const LlcSnapHeader& snap)
{
serialize(ar, snap.dsap);
serialize(ar, snap.ssap);
serialize(ar, snap.control);
for (std::uint8_t byte : snap.oui) {
ar << byte;
}
serialize(ar, snap.protocol_id);
}
void deserialize(InputArchive& ar, LlcSnapHeader& snap)
{
deserialize(ar, snap.dsap);
deserialize(ar, snap.ssap);
deserialize(ar, snap.control);
for (std::uint8_t& byte : snap.oui) {
ar >> byte;
}
deserialize(ar, snap.protocol_id);
}
bool operator==(const LlcSnapHeader& a, const LlcSnapHeader& b)
{
return a.dsap == b.dsap && a.ssap == b.ssap && a.control == b.control &&
a.oui == b.oui && a.protocol_id == b.protocol_id;
}
bool operator!=(const LlcSnapHeader& a, const LlcSnapHeader& b)
{
return !(a == b);
}
} // namespace ieee802
} // namespace access
} // namespace vanetza
@@ -0,0 +1,164 @@
#ifndef G5_LINK_LAYER_HPP_CESAPUOW
#define G5_LINK_LAYER_HPP_CESAPUOW
#include <array>
#include <cstdint>
#include <vanetza/access/access_category.hpp>
#include <vanetza/common/bit_number.hpp>
#include <vanetza/common/byte_order.hpp>
#include <vanetza/common/serialization.hpp>
#include <vanetza/net/mac_address.hpp>
namespace vanetza
{
namespace access
{
namespace ieee802
{
namespace dot11
{
/**
* \brief QoS Control field in IEEE 802.11 MAC header.
*/
struct QosControl
{
vanetza::uint16be_t raw { 0 };
/**
* Set user priority by access category
* \param ac map this AccessCategory to user priority
*/
void user_priority(AccessCategory ac);
};
/**
* \brief Frame Control field in IEEE 802.11 MAC header
*/
struct FrameControl
{
vanetza::uint16be_t raw { 0 };
using Protocol = BitNumber<std::uint16_t, 2>; /**< represents protocol version */
using Type = BitNumber<std::uint16_t, 2>;
using SubType = BitNumber<std::uint16_t, 4>;
Protocol protocol() const { return Protocol(raw.get()); }
Type type() const { return Type(raw.get() >> 2); }
SubType sub_type() const { return SubType(raw.get() >> 4); }
bool to_ds() const { return raw.get() & 0x0100; }
bool from_ds() const { return raw.get() & 0x0200; }
bool more_fragments() const { return raw.get() & 0x0400; }
bool retry() const { return raw.get() & 0x0800; }
/**
* Create frame control for QoS data frame without any flags
* \return FrameControl field for QoS data frame
**/
static FrameControl qos_data_frame();
};
/**
* \brief Sequence Control field in IEEE 802.11 MAC header
*/
struct SequenceControl
{
vanetza::uint16be_t raw { 0 };
using SequenceNumber = BitNumber<std::uint16_t, 12>;
using FragmentNumber = BitNumber<std::uint16_t, 4>;
SequenceNumber sequence_number() const
{
return SequenceNumber(raw.get() >> 4);
}
FragmentNumber fragment_number() const
{
return FragmentNumber(raw.get());
}
};
/** MAC address representing the BSSID wildcard (all bits set) */
extern const MacAddress bssid_wildcard;
/**
* \brief MAC header of QoS data frames
*/
struct QosDataHeader
{
FrameControl frame_control = FrameControl::qos_data_frame();
uint16be_t duration_or_id;
MacAddress destination;
MacAddress source;
MacAddress bssid = bssid_wildcard;
SequenceControl sequence_control;
QosControl qos_control;
/** length of serialized QoSDataHeader in bytes */
static constexpr std::size_t length_bytes = 26;
};
/** length of frame check sequence in bytes */
static constexpr std::size_t fcs_length_bytes = 4;
void serialize(OutputArchive&, const QosDataHeader&);
void deserialize(InputArchive&, QosDataHeader&);
} // namespace dot11
/**
* \brief Logical Link Control header with SNAP extension
*/
struct LlcSnapHeader
{
std::uint8_t dsap = 0xAA;
std::uint8_t ssap = 0xAA;
std::uint8_t control = 0x03;
std::array<std::uint8_t, 3> oui = {{ 0x00, 0x00, 0x00 }};
uint16be_t protocol_id;
LlcSnapHeader(uint16be_t protocol_id) : protocol_id(protocol_id) {}
static constexpr std::size_t length_bytes = 8;
};
bool operator==(const LlcSnapHeader&, const LlcSnapHeader&);
bool operator!=(const LlcSnapHeader&, const LlcSnapHeader&);
void serialize(OutputArchive&, const LlcSnapHeader&);
void deserialize(InputArchive&, LlcSnapHeader&);
} // namespace ieee802
/**
* \brief Link layer header used by ITS-G5 stations
*/
struct G5LinkLayer
{
ieee802::dot11::QosDataHeader mac_header;
ieee802::LlcSnapHeader llc_snap_header;
G5LinkLayer();
static constexpr std::size_t length_bytes =
ieee802::dot11::QosDataHeader::length_bytes +
ieee802::LlcSnapHeader::length_bytes;
};
void serialize(OutputArchive&, const G5LinkLayer&);
void deserialize(InputArchive&, G5LinkLayer&);
/**
* \brief Check whether some link layer header fields contain their expected values.
* For most explicitly initialized link layer header fields, their (default) value is expected in received frames.
*
* \param link_layer G5LinkLayer to check
* \return whether all fields are set as expected
*/
bool check_fixed_fields(const G5LinkLayer& link_layer);
} // namespace access
} // namespace vanetza
#endif /* G5_LINK_LAYER_HPP_CESAPUOW */
@@ -0,0 +1,25 @@
#ifndef INTERFACE_HPP_EUPJ90MD
#define INTERFACE_HPP_EUPJ90MD
#include <vanetza/net/chunk_packet.hpp>
#include <memory>
namespace vanetza
{
namespace access
{
struct DataRequest;
class Interface
{
public:
virtual void request(const DataRequest&, std::unique_ptr<ChunkPacket>) = 0;
virtual ~Interface() {}
};
} // namespace access
} // namespace vanetza
#endif /* INTERFACE_HPP_EUPJ90MD */
@@ -0,0 +1,35 @@
#pragma once
#include <vanetza/access/access_category.hpp>
#include <cstdint>
namespace vanetza
{
namespace access
{
/**
* \brief map access category to PPPP for C-V2X
*
* Mapping is according to EN 303 613 V1.1.1 Table B.7
*
* \param ac access category from 802.11
* \return matching PPPP value
*/
constexpr std::uint8_t pppp_from_ac(AccessCategory ac)
{
switch (ac) {
case AccessCategory::VO:
return 2;
case AccessCategory::VI:
return 4;
case AccessCategory::BE:
return 5;
case AccessCategory::BK:
default:
return 7;
}
}
} // namespace access
} // namespace vanetza
@@ -0,0 +1,5 @@
include(UseGTest)
configure_gtest_directory(LINK_LIBRARIES access)
add_gtest(DataRates data_rates.cpp)
@@ -0,0 +1,19 @@
#include <gtest/gtest.h>
#include <vanetza/access/data_rates.hpp>
using namespace vanetza::access;
TEST(DataRates, bytes_per_second)
{
EXPECT_EQ(G5_3Mbps.bytes_per_second(), 3 * 1000 * 1000 / 8);
EXPECT_EQ(G5_6Mbps.bytes_per_second(), 6 * 1000 * 1000 / 8);
EXPECT_EQ(G5_12Mbps.bytes_per_second(), 12 * 1000 * 1000 / 8);
}
TEST(DataRates, data_length)
{
const std::size_t psdu_bytes = 385;
const std::size_t data_bytes = G5_6Mbps.data_length(psdu_bytes);
EXPECT_GE(data_bytes, psdu_bytes);
EXPECT_LT(data_bytes, psdu_bytes + 12);
}