Files
MicrOBU/microbu-esp32c5/tools/v2x2map-0.3.0/bridge/vam.py
T

75 lines
2.9 KiB
Python
Raw Normal View History

"""VAM v3 ASN.1 UPER decoding (ETSI TS 103 300-3 V2.3.1 / CDD V2.4.1).
Pinned schemas and notices are bundled in asn1/. See ../docs/vam-support.md.
"""
from functools import lru_cache
from pathlib import Path
@lru_cache(maxsize=1)
def codec():
try:
import asn1tools
except ImportError as exc:
raise ValueError("VAM decoder unavailable: install bridge/requirements.txt") from exc
directory = Path(__file__).resolve().parent / "asn1"
return asn1tools.compile_files([
str(directory / "TS102894-2v241-CDD.asn"),
str(directory / "VAM-PDU-Descriptions.asn"),
], codec="uper")
def json_value(value):
"""Preserve ASN.1 CHOICE/bit-string values in JSON-safe form."""
if isinstance(value, bytes):
return value.hex()
if isinstance(value, dict):
return {k: json_value(v) for k, v in value.items()}
if isinstance(value, (tuple, list)):
return [json_value(v) for v in value]
return value
def decode_vam(data):
if not data or len(data) > 4096:
raise ValueError("empty/oversized VAM")
decoder = codec()
import asn1tools
try:
message = decoder.decode("VAM", data, check_constraints=True)
# Detect trailing bytes, nonzero padding and unknown extensions that the
# library may skip. Do not silently call partially understood data valid.
if decoder.encode("VAM", message, check_constraints=True) != data:
raise ValueError("noncanonical VAM or unsupported extension/trailing data")
except (asn1tools.DecodeError, asn1tools.EncodeError, asn1tools.ConstraintsError) as exc:
raise ValueError("invalid VAM UPER: " + str(exc)) from exc
header = message["header"]
if header["protocolVersion"] != 3 or header["messageId"] != 16:
raise ValueError("unsupported VAM protocol version/message ID")
body = message["vam"]
containers = body["vamParameters"]
basic = containers["basicContainer"]
pos = basic["referencePosition"]
hf = containers["vruHighFrequencyContainer"]
result = dict(station_id=header["stationId"],
generation_delta_time=body["generationDeltaTime"],
station_type=basic["stationType"],
containers=json_value(containers))
lat, lon = pos["latitude"], pos["longitude"]
if lat != 900000001 and lon != 1800000001:
result.update(lat=lat / 1e7, lon=lon / 1e7)
altitude = pos["altitude"]["altitudeValue"]
if altitude != 800001:
result["altitude_m"] = altitude / 100
heading = hf["heading"]["value"]
if heading != 3601:
result["heading_deg"] = heading / 10
speed = hf["speed"]["speedValue"]
if speed != 16383:
result["speed_mps"] = speed / 100
acceleration = hf["longitudinalAcceleration"]["longitudinalAccelerationValue"]
if acceleration != 161:
result["longitudinal_acceleration_mps2"] = acceleration / 10
return result