Files
Ashin Walpola d107534eb2 Keep vanetza-idf in obu-firmware, so a plain clone builds the firmware
obu-firmware builds against the vanetza-idf C-ITS library, which until now
came from the colleague's microbu-esp32c5 tree beside the repository and was
not tracked here, so a clone of this repository could not build the firmware
it ships. The library alone is now part of obu-firmware, as
obu-firmware/external/vanetza-idf: their external/vanetza-idf at commit
cf4b99f, unchanged (9775 files; see its PROVENANCE.md). CMake takes it from
there by default; -DVANETZA_IDF_DIR still points the build elsewhere.

The rest of the colleague's tree (their own VAM firmware, PKI tooling,
station-link Python tools, the V2X2MAP bridge) stays out of this repository
and gitignored; nothing is pushed to their repository. NOTES.md, docs/06,
TODO.md and the pcap verifier's usage line point at the new location.
2026-09-24 10:56:05 +02:00

156 lines
5.1 KiB
C++

#include <vanetza/common/byte_order.hpp>
#include <vanetza/geodesy/country_data_reader.hpp>
#include <cstring>
namespace vanetza
{
namespace geodesy
{
namespace detail
{
uint16_t read_u16le(const uint8_t* p)
{
uint16_t v;
std::memcpy(&v, p, sizeof(v));
return endian_cast<ByteOrder::LittleEndian>(v).host();
}
uint32_t read_u32le(const uint8_t* p)
{
uint32_t v;
std::memcpy(&v, p, sizeof(v));
return endian_cast<ByteOrder::LittleEndian>(v).host();
}
namespace
{
static constexpr std::size_t wkb_header_length = 5; /*< byte order (1) and type (4) */
static constexpr std::size_t wkb_count_length = 4; /*< counters are uint32_t (4) */
static constexpr std::uint32_t wkb_polygon_type = 3;
static constexpr std::uint32_t wkb_multipolygon_type = 6;
uint32_t read_u32(const uint8_t* p, bool little_endian)
{
uint32_t v;
std::memcpy(&v, p, sizeof(v));
return little_endian
? endian_cast<ByteOrder::LittleEndian>(v).host()
: endian_cast<ByteOrder::BigEndian>(v).host();
}
double read_f64(const uint8_t* p, bool little_endian)
{
uint64_t bits;
std::memcpy(&bits, p, sizeof(bits));
bits = little_endian
? endian_cast<ByteOrder::LittleEndian>(bits).host()
: endian_cast<ByteOrder::BigEndian>(bits).host();
double v;
std::memcpy(&v, &bits, sizeof(v));
return v;
}
// Parse a WKB Polygon body (after byte-order and type fields).
// On success, `consumed` receives the number of bytes consumed from `data`.
// Positions in the returned result are relative to `data` (body start).
CountryReaderResult parse_polygon_body(const uint8_t* data, std::size_t length, bool le, country::Polygon& out)
{
if (length < wkb_count_length) {
return CountryReaderResult::failure("truncated polygon: missing ring count", 0);
}
uint32_t num_rings = read_u32(data, le);
std::size_t offset = wkb_count_length;
for (uint32_t r = 0; r < num_rings; ++r) {
if (length - offset < wkb_count_length) {
return CountryReaderResult::failure("truncated polygon: missing point count", offset);
}
uint32_t num_points = read_u32(data + offset, le);
offset += wkb_count_length;
std::size_t points_bytes = static_cast<std::size_t>(num_points) * 2 * sizeof(double);
if (length - offset < points_bytes) {
return CountryReaderResult::failure("truncated polygon: missinsg point data", offset);
}
country::Ring ring;
ring.reserve(num_points);
for (uint32_t i = 0; i < num_points; ++i) {
double lon = read_f64(data + offset, le);
offset += sizeof(double);
double lat = read_f64(data + offset, le);
offset += sizeof(double);
ring.push_back(country::Point(lon, lat));
}
if (r == 0) {
out.outer() = std::move(ring);
} else {
out.inners().push_back(std::move(ring));
}
}
return CountryReaderResult::success(offset);
}
} // anonymous namespace
CountryReaderResult parse_wkb(const uint8_t* data, std::size_t length, CountryPolygon& out)
{
if (length < wkb_header_length) {
return CountryReaderResult::failure("WKB too short for header", 0);
}
bool le = (data[0] == 0x01);
uint32_t type = read_u32(data + 1, le);
if (type == wkb_polygon_type) {
country::Polygon polygon;
auto inner = parse_polygon_body(data + wkb_header_length, length - wkb_header_length, le, polygon);
if (inner.ok()) {
out.push_back(std::move(polygon));
}
return inner.add_offset(wkb_header_length);
} else if (type == wkb_multipolygon_type) {
if (length < wkb_header_length + wkb_count_length) {
return CountryReaderResult::failure("truncated multi-polygon: missing polygon count", wkb_header_length);
}
uint32_t num_polygons = read_u32(data + wkb_header_length, le);
std::size_t offset = wkb_header_length + wkb_count_length;
for (uint32_t i = 0; i < num_polygons; ++i) {
if (length - offset < wkb_header_length) {
return CountryReaderResult::failure("truncated multi-polygon: missing polygon header", offset);
}
bool poly_le = (data[offset] == 0x01);
uint32_t poly_type = read_u32(data + offset + 1, poly_le);
if (poly_type != wkb_polygon_type) {
return CountryReaderResult::failure("unexpected geometry type inside multi-polygon", offset + 1);
}
offset += wkb_header_length;
country::Polygon polygon;
auto inner = parse_polygon_body(data + offset, length - offset, poly_le, polygon);
if (inner.ok()) {
out.push_back(std::move(polygon));
offset += inner.position();
} else {
return inner.add_offset(offset);
}
}
return CountryReaderResult::success(offset);
} else {
return CountryReaderResult::failure("unsupported WKB geometry type" , 1);
}
}
} // namespace detail
} // namespace geodesy
} // namespace vanetza