package com.hawhamburg.micr0bu import com.hawhamburg.micr0bu.domain.asn1.CamUperCodec import com.hawhamburg.micr0bu.domain.cam.StationType import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Test /** * A roadside unit's CAM must decode, not be dropped. * * ETSI's `HighFrequencyContainer` is a CHOICE, and an RSU picks `rsuContainerHighFrequency` * instead of `basicVehicleContainerHighFrequency`. That container holds no kinematics at all - * only an optional protected-zone list - so an earlier version of the decoder bailed on it and * every RSU CAM was silently discarded. The bench RSU sends CAM and SPATEM from the same station * id, so dropping its CAM meant the one station a rider most wants to see never appeared. * * The fixture is a real 26-byte RSU CAM taken live from the OBU's `v2x/rx/cam` topic; the * expected values are asn1tools' decoding of those same bytes using the ETSI modules in the * `C-ITS-Parser` checkout. */ class RsuCamDecodeTest { private val rsuCam = "020239b9de898b8b00fab215af6e286477c0c20c200033fa4e80" private fun String.hexToBytes(): ByteArray = chunked(2).map { it.toInt(16).toByte() }.toByteArray() @Test fun `decodes a roadside unit CAM for position and station type`() { val cam = CamUperCodec.decode(rsuCam.hexToBytes(), receivedAtEpochMs = 1_787_100_000_000L) assertNotNull("an RSU CAM must not be dropped", cam) cam!! assertEquals(968_482_441L, cam.stationId) assertEquals(StationType.ROAD_SIDE_UNIT, cam.stationType) assertEquals(15, cam.stationType) // the enumeration jumps 11 -> 15; 12 would be wrong assertEquals(53.5544955, cam.latitude, 1e-7) assertEquals(10.0225470, cam.longitude, 1e-7) // An RSU has no kinematics to report. Zero is a placeholder, which is only safe because // CamUseCaseRepository keeps RSU CAMs out of UseCaseDetectionEngine - otherwise this // would read as a permanently stopped vehicle and raise a standing false alert. assertEquals(0.0, cam.speedMps, 0.0) assertEquals(0.0, cam.headingDeg, 0.0) } }