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).
156 lines
5.1 KiB
C++
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
|