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.
This commit is contained in:
Ashin Walpola
2026-09-24 10:56:05 +02:00
parent 2f60623e18
commit d107534eb2
9781 changed files with 1560475 additions and 17 deletions
@@ -0,0 +1 @@
/cache
@@ -0,0 +1,113 @@
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.9"
# dependencies = ["pyshp", "shapely"]
# ///
"""Convert Natural Earth shapefile to Vanetza country data binary format.
Downloads the Natural Earth admin-0 countries shapefile at the requested
resolution, then converts it to a compact binary format.
Output format:
version : uint16 (little-endian)
followed by a sequence of entries, each:
m49_code : uint16 (little-endian)
wkb_size : uint32 (little-endian)
wkb_data : bytes (OGC WKB MultiPolygon)
"""
import argparse
import struct
import sys
import urllib.request
import zipfile
from pathlib import Path
import shapefile
from shapely.geometry import MultiPolygon, Polygon, shape
RESOLUTIONS = ["110m", "50m", "10m"]
FORMAT_VERSION = 1
def download_shapefile(resolution: str, cache_dir: Path) -> Path:
name = f"ne_{resolution}_admin_0_countries"
shp_dir = cache_dir / name
shp_path = shp_dir / f"{name}.shp"
if shp_path.exists():
return shp_path
url = f"https://naciscdn.org/naturalearth/{resolution}/cultural/{name}.zip"
print(f"Downloading {url} ...", file=sys.stderr)
data, _ = urllib.request.urlretrieve(url)
with zipfile.ZipFile(data) as zf:
shp_dir.mkdir(parents=True, exist_ok=True)
zf.extractall(shp_dir)
return shp_path
def convert(shp_path: Path, output: Path):
sf = shapefile.Reader(str(shp_path))
fields = [f[0] for f in sf.fields[1:]] # skip DeletionFlag
if "ISO_N3_EH" not in fields:
print("Error: shapefile missing ISO_N3_EH field", file=sys.stderr)
sys.exit(1)
iso_n3_eh_idx = fields.index("ISO_N3_EH")
count = 0
skipped = 0
with open(output, "wb") as out:
out.write(struct.pack("<H", FORMAT_VERSION))
for sr in sf.shapeRecords():
iso_n3_eh = sr.record[iso_n3_eh_idx]
# Skip non-numeric codes (e.g., "-99" for disputed territories)
try:
m49 = int(iso_n3_eh)
except (ValueError, TypeError):
skipped += 1
continue
if m49 < 0 or m49 > 65535:
skipped += 1
continue
geom = shape(sr.shape.__geo_interface__)
# Normalize to MultiPolygon
if isinstance(geom, Polygon):
geom = MultiPolygon([geom])
elif not isinstance(geom, MultiPolygon):
skipped += 1
continue
wkb = geom.wkb
out.write(struct.pack("<H", m49))
out.write(struct.pack("<I", len(wkb)))
out.write(wkb)
count += 1
print(f"Wrote {count} countries, skipped {skipped} entries", file=sys.stderr)
def main():
parser = argparse.ArgumentParser(description="Convert Natural Earth to country data binary")
parser.add_argument("--output", required=True, type=Path, help="Path to output .bin file")
parser.add_argument("--resolution", choices=RESOLUTIONS, default="50m",
help="Natural Earth resolution (default: 50m)")
parser.add_argument("--cache-dir", type=Path,
default=Path(__file__).parent / "cache",
help="Cache directory for downloaded shapefiles")
args = parser.parse_args()
shp_path = download_shapefile(args.resolution, args.cache_dir)
convert(shp_path, args.output)
if __name__ == "__main__":
main()
@@ -0,0 +1,44 @@
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.9"
# dependencies = []
# ///
"""Generate a C++ source file embedding a binary file as std::array."""
import argparse
import sys
from pathlib import Path
def main():
parser = argparse.ArgumentParser(description="Embed binary file as C++ std::array")
parser.add_argument("--input", required=True, type=Path, help="Path to binary input file")
parser.add_argument("--output", required=True, type=Path, help="Path to C++ output file")
args = parser.parse_args()
data = args.input.read_bytes()
with open(args.output, "w") as out:
out.write("#include <vanetza/common/byte_view.hpp>\n\n")
out.write(f"static const std::array<uint8_t, {len(data)}> vanetza_country_data_storage = {{\n")
for i in range(0, len(data), 16):
chunk = data[i:i + 16]
out.write(" " + ", ".join(f"0x{b:02x}" for b in chunk) + ",\n")
out.write("};\n\n")
out.write("namespace vanetza {\n")
out.write("namespace geodesy {\n")
out.write("namespace country {\n\n")
out.write("byte_view_range embedded()\n")
out.write("{\n")
out.write(" byte_view_iterator begin(vanetza_country_data_storage.data());\n")
out.write(" byte_view_iterator end(vanetza_country_data_storage.data() + vanetza_country_data_storage.size());\n")
out.write(" return {begin, end};\n")
out.write("}\n\n")
out.write("} // namespace country\n")
out.write("} // namespace geodesy\n")
out.write("} // namespace vanetza\n")
print(f"Embedded {len(data)} bytes into {args.output.name}", file=sys.stderr)
if __name__ == "__main__":
main()
@@ -0,0 +1,68 @@
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.9"
# dependencies = ["matplotlib", "shapely"]
# ///
"""Plot country polygons from a Vanetza country data binary file."""
import argparse
import colorsys
import struct
import sys
import matplotlib.pyplot as plt
from shapely import wkb
EXPECTED_FORMAT_VERSION = 1
def main():
parser = argparse.ArgumentParser(description="Plot country polygons from .bin file")
parser.add_argument("input", help="Path to country_data.bin")
args = parser.parse_args()
fig, ax = plt.subplots(figsize=(16, 8))
with open(args.input, "rb") as f:
data = f.read()
if len(data) < 2:
print("Error: file too short to contain version header", file=sys.stderr)
sys.exit(1)
version, = struct.unpack_from("<H", data, 0)
if version != EXPECTED_FORMAT_VERSION:
print(f"Error: unsupported country data format version {version}", file=sys.stderr)
sys.exit(1)
offset = 2
while offset < len(data):
m49, = struct.unpack_from("<H", data, offset)
offset += 2
wkb_size, = struct.unpack_from("<I", data, offset)
offset += 4
geom = wkb.loads(data[offset:offset + wkb_size])
offset += wkb_size
h = (m49 * 0.618) % 1.0
s = 0.5 + (hash(m49) % 50) / 100.0
l = 0.4 + (hash(m49 * 7) % 30) / 100.0
color = colorsys.hls_to_rgb(h, l, s)
for polygon in geom.geoms:
x, y = polygon.exterior.xy
ax.fill(x, y, alpha=0.4, edgecolor="black", linewidth=0.3, facecolor=color)
if polygon.area > 1.0:
centroid = polygon.centroid
ax.text(centroid.x, centroid.y, str(m49),
fontsize=5, ha="center", va="center")
ax.set_aspect("equal")
ax.set_xlabel("Longitude")
ax.set_ylabel("Latitude")
ax.set_title("Country Polygons")
plt.tight_layout()
plt.show()
if __name__ == "__main__":
main()