diff --git a/README.md b/README.md index 5f7ee1392..704a3028b 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ they are using the following versions: |:-----------------:|:-----------------------------------------------------------------------------------:|:---------------------------------------------------------:|:-----------------------------------------------------------------------------------:|:-------------------------------------------:| | **Bootstrap** | | | | | | **CAM** | [2.2.0](schema/cam/cam_schema_2-2-0.json) [1.1.3](schema/cam/cam_schema_1-1-3.json) | [1.1.3](schema/cam/cam_schema_1-1-3.json) | [2.3.0](schema/cam/cam_schema_2-3-0.json) [1.1.3](schema/cam/cam_schema_1-1-3.json) | [1.1.3](schema/cam/cam_schema_1-1-3.json) | -| **CPM** | [2.1.0](schema/cpm/cpm_schema_2-1-0.json) | [2.1.1](schema/cpm/cpm_schema_2-1-1.json) | [1.2.1](schema/cpm/cpm_schema_1-2-1.json) | | +| **CPM** | [2.1.0](schema/cpm/cpm_schema_2-1-0.json) | [2.1.1](schema/cpm/cpm_schema_2-1-1.json) | [2.1.1](schema/cpm/cpm_schema_2-1-1.json) [1.2.1](schema/cpm/cpm_schema_1-2-1.json) | | | **DENM** | [2.2.0](schema/denm/denm_schema_2-2-0.json) | [1.1.3](schema/denm/denm_schema_1-1-3.json) | [1.1.3](schema/denm/denm_schema_1-1-3.json) | [1.1.3](schema/denm/denm_schema_1-1-3.json) | | **Information** | [2.1.0](schema/information/information_schema_2-1-0.json) | [1.2.0](schema/information/information_schema_1-2-0.json) | | | | **MAPEM** | | | | | diff --git a/java/iot3/examples/src/main/java/com/orange/CpmV121Factory.java b/java/iot3/examples/src/main/java/com/orange/CpmV121Factory.java new file mode 100644 index 000000000..6bdc6502e --- /dev/null +++ b/java/iot3/examples/src/main/java/com/orange/CpmV121Factory.java @@ -0,0 +1,131 @@ +package com.orange; + +import com.orange.iot3mobility.TrueTime; +import com.orange.iot3mobility.messages.EtsiConverter; +import com.orange.iot3mobility.messages.cpm.v121.model.CpmEnvelope121; +import com.orange.iot3mobility.messages.cpm.v121.model.CpmMessage121; +import com.orange.iot3mobility.messages.cpm.v121.model.Origin; +import com.orange.iot3mobility.messages.cpm.v121.model.defs.AreaCircular; +import com.orange.iot3mobility.messages.cpm.v121.model.defs.Offset; +import com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer.ManagementConfidence; +import com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer.ManagementContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer.ReferencePosition; +import com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer.ObjectClass; +import com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer.ObjectClassVru; +import com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer.ObjectClassification; +import com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer.PerceivedObject; +import com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer.PerceivedObjectConfidence; +import com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer.PerceivedObjectContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.sensorinformationcontainer.DetectionArea; +import com.orange.iot3mobility.messages.cpm.v121.model.sensorinformationcontainer.SensorInformation; +import com.orange.iot3mobility.messages.cpm.v121.model.sensorinformationcontainer.SensorInformationContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer.OriginatingRsuContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer.StationDataContainer; +import com.orange.iot3mobility.quadkey.LatLng; + +import java.util.List; + +final class CpmV121Factory { + private static final int PROTOCOL_VERSION = 2; + private static final long STATION_ID = 123456L; + private static final int SENSOR_ID = 123; + private static final int MAP_REGION = 123; + private static final int MAP_INTERSECTION_ID = 123; + private static final int MAP_ROAD_SEGMENT_ID = 123; + + private CpmV121Factory() { + // Factory class + } + + static CpmEnvelope121 createTestCpmEnvelope( + LatLng position, + String sourceUuid, + int pedestrianX, + int pedestrianY, + int bicycleX, + int bicycleY) { + PerceivedObject pedestrianPo = PerceivedObject.builder() + .objectId(15) + .timeOfMeasurement(0) + .objectAge(1500) + .distance(pedestrianX, pedestrianY) + .speed(0, 0) + .planarObjectDimension(10, 10) + .verticalObjectDimension(20) + .classification(List.of(new ObjectClassification( + new ObjectClass(null, + new ObjectClassVru(1, null, null, null), + null, + null), + 100))) + .sensorIdList(List.of(SENSOR_ID)) + .confidence( + PerceivedObjectConfidence.builder() + .object(15) + .distance(4095, 4095) + .speed(0, 0) + .build()) + .build(); + + PerceivedObject bicyclePo = PerceivedObject.builder() + .objectId(34) + .timeOfMeasurement(0) + .objectAge(1500) + .distance(bicycleX, bicycleY) + .speed(0, 0) + .planarObjectDimension(20, 20) + .verticalObjectDimension(15) + .classification(List.of(new ObjectClassification( + new ObjectClass(null, + new ObjectClassVru(null, 1, null, null), + null, + null), + 100))) + .sensorIdList(List.of(SENSOR_ID)) + .confidence( + PerceivedObjectConfidence.builder() + .object(15) + .distance(4095, 4095) + .speed(0, 0) + .build()) + .build(); + + return CpmEnvelope121.builder() + .origin(Origin.SELF.value) + .sourceUuid(sourceUuid) + .timestamp(TrueTime.getAccurateTime()) + .message(CpmMessage121.builder() + .protocolVersion(PROTOCOL_VERSION) + .stationId(STATION_ID) + .generationDeltaTime(EtsiConverter.generationDeltaTimeEtsi(TrueTime.getAccurateETSITime())) + .managementContainer(ManagementContainer.builder() + .stationType(com.orange.iot3mobility.messages.StationType.ROAD_SIDE_UNIT.value) + .referencePosition(new ReferencePosition( + EtsiConverter.latitudeEtsi(position.getLatitude()), + EtsiConverter.longitudeEtsi(position.getLongitude()), + 0)) + .confidence(new ManagementConfidence( + new com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer + .PositionConfidenceEllipse(4095, 4095, 3601), + 15)) + .build()) + .stationDataContainer(StationDataContainer.builder() + .originatingRsuContainer(new OriginatingRsuContainer( + MAP_REGION, + MAP_INTERSECTION_ID, + MAP_ROAD_SEGMENT_ID)) + .build()) + .sensorInformationContainer(new SensorInformationContainer( + List.of(new SensorInformation( + SENSOR_ID, + 4, + DetectionArea.builder() + .stationarySensorCircular(new AreaCircular( + new Offset(0, 0, 0), 200)) + .build())))) + .perceivedObjectContainer(new PerceivedObjectContainer(List.of(pedestrianPo, bicyclePo))) + .build()) + .build(); + } +} + diff --git a/java/iot3/examples/src/main/java/com/orange/CpmV211Factory.java b/java/iot3/examples/src/main/java/com/orange/CpmV211Factory.java new file mode 100644 index 000000000..e9bf11efa --- /dev/null +++ b/java/iot3/examples/src/main/java/com/orange/CpmV211Factory.java @@ -0,0 +1,107 @@ +package com.orange; + +import com.orange.iot3mobility.TrueTime; +import com.orange.iot3mobility.messages.EtsiConverter; +import com.orange.iot3mobility.messages.cpm.v211.model.CpmEnvelope211; +import com.orange.iot3mobility.messages.cpm.v211.model.CpmMessage211; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.Altitude; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.CartesianCoordinateWithConfidence; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.CartesianPosition3d; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.CartesianPosition3dWithConfidence; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.Circular; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.MapReference; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.PositionConfidenceEllipse; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.ReferencePosition; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.RoadSegment; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.Shape; +import com.orange.iot3mobility.messages.cpm.v211.model.managementcontainer.ManagementContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.originatingrsucontainer.OriginatingRsuContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer.ObjectClass; +import com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer.ObjectClassVru; +import com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer.ObjectClassification; +import com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer.PerceivedObject; +import com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer.PerceivedObjectContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.sensorinformationcontainer.SensorInformation; +import com.orange.iot3mobility.messages.cpm.v211.model.sensorinformationcontainer.SensorInformationContainer; +import com.orange.iot3mobility.quadkey.LatLng; + +import java.util.List; + +final class CpmV211Factory { + private static final int PROTOCOL_VERSION = 2; + private static final long STATION_ID = 123456L; + private static final int SENSOR_ID = 123; + private static final int MAP_REGION = 123; + private static final int MAP_ROAD_SEGMENT_ID = 123; + + private CpmV211Factory() { + // Factory class + } + + static CpmEnvelope211 createTestCpmEnvelope( + LatLng position, + String sourceUuid, + int pedestrianX, + int pedestrianY, + int bicycleX, + int bicycleY) { + PerceivedObject pedestrianPo = PerceivedObject.builder() + .objectId(15) + .measurementDeltaTime(0) + .objectAge(1500) + .position(new CartesianPosition3dWithConfidence( + new CartesianCoordinateWithConfidence(pedestrianX, 100), + new CartesianCoordinateWithConfidence(pedestrianY, 100), + null)) + .classification(List.of(new ObjectClassification( + ObjectClass.vru(ObjectClassVru.pedestrian(1)), + 100))) + .sensorIdList(List.of(SENSOR_ID)) + .build(); + + PerceivedObject bicyclePo = PerceivedObject.builder() + .objectId(34) + .measurementDeltaTime(0) + .objectAge(1500) + .position(new CartesianPosition3dWithConfidence( + new CartesianCoordinateWithConfidence(bicycleX, 100), + new CartesianCoordinateWithConfidence(bicycleY, 100), + null)) + .classification(List.of(new ObjectClassification( + ObjectClass.vru(ObjectClassVru.bicyclistAndLightVruVehicle(1)), + 100))) + .sensorIdList(List.of(SENSOR_ID)) + .build(); + + return CpmEnvelope211.builder() + .sourceUuid(sourceUuid) + .timestamp(TrueTime.getAccurateTime()) + .message(CpmMessage211.builder() + .protocolVersion(PROTOCOL_VERSION) + .stationId(STATION_ID) + .managementContainer(ManagementContainer.builder() + .referenceTime(TrueTime.getAccurateETSITime()) + .referencePosition(new ReferencePosition( + EtsiConverter.latitudeEtsi(position.getLatitude()), + EtsiConverter.longitudeEtsi(position.getLongitude()), + new PositionConfidenceEllipse(4095, 4095, 3601), + new Altitude(0, 15))) + .build()) + .originatingRsuContainer(new OriginatingRsuContainer(List.of( + MapReference.roadSegment(new RoadSegment(MAP_ROAD_SEGMENT_ID, MAP_REGION))))) + .sensorInformationContainer(new SensorInformationContainer( + List.of(new SensorInformation( + SENSOR_ID, + 4, + Shape.circular(new Circular( + 200, + new CartesianPosition3d(0, 0, null), + null)), + 100, + true)))) + .perceivedObjectContainer(new PerceivedObjectContainer(List.of(pedestrianPo, bicyclePo))) + .build()) + .build(); + } +} + diff --git a/java/iot3/examples/src/main/java/com/orange/Iot3MobilityBootstrapExample.java b/java/iot3/examples/src/main/java/com/orange/Iot3MobilityBootstrapExample.java index 184cd1abc..3b2c624ad 100644 --- a/java/iot3/examples/src/main/java/com/orange/Iot3MobilityBootstrapExample.java +++ b/java/iot3/examples/src/main/java/com/orange/Iot3MobilityBootstrapExample.java @@ -5,20 +5,17 @@ import com.orange.iot3core.bootstrap.BootstrapHelper; import com.orange.iot3mobility.IoT3Mobility; import com.orange.iot3mobility.IoT3MobilityCallback; -import com.orange.iot3mobility.TrueTime; import com.orange.iot3mobility.Utils; -import com.orange.iot3mobility.its.EtsiUtils; import com.orange.iot3mobility.messages.cam.core.CamCodec; import com.orange.iot3mobility.messages.cam.core.CamVersion; import com.orange.iot3mobility.messages.cam.v113.model.CamEnvelope113; import com.orange.iot3mobility.messages.cam.v230.model.CamEnvelope230; +import com.orange.iot3mobility.messages.cpm.core.CpmCodec; +import com.orange.iot3mobility.messages.cpm.core.CpmVersion; +import com.orange.iot3mobility.messages.cpm.v121.model.CpmEnvelope121; +import com.orange.iot3mobility.messages.cpm.v211.model.CpmEnvelope211; import com.orange.iot3mobility.roadobjects.HazardType; import com.orange.iot3mobility.its.StationType; -import com.orange.iot3mobility.its.json.JsonValue; -import com.orange.iot3mobility.its.json.Position; -import com.orange.iot3mobility.its.json.PositionConfidence; -import com.orange.iot3mobility.its.json.PositionConfidenceEllipse; -import com.orange.iot3mobility.its.json.cpm.*; import com.orange.iot3mobility.its.json.denm.DENM; import com.orange.iot3mobility.managers.IoT3RoadHazardCallback; import com.orange.iot3mobility.managers.IoT3RoadSensorCallback; @@ -31,7 +28,6 @@ import java.io.IOException; import java.net.URI; -import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -190,8 +186,14 @@ public void sensorObjectExpired(SensorObject sensorObject) { } @Override - public void cpmArrived(CPM cpm) { - System.out.println("CPM received: " + cpm.getJson()); + public void cpmArrived(CpmCodec.CpmFrame cpmFrame) { + if(cpmFrame.version() == CpmVersion.V1_2_1) { + CpmEnvelope121 cpm121 = (CpmEnvelope121) cpmFrame.envelope(); + System.out.println("Raw CPM v1.2.1: " + cpm121); + } else if(cpmFrame.version() == CpmVersion.V2_1_1) { + CpmEnvelope211 cpm211 = (CpmEnvelope211) cpmFrame.envelope(); + System.out.println("Raw CPM v2.1.1: " + cpm211); + } } }); @@ -218,17 +220,17 @@ private static void setRegionOfInterest(LatLng roiPosition) { private static synchronized void startSendingMessages() { ScheduledExecutorService messageScheduler = Executors.newScheduledThreadPool(1); - messageScheduler.scheduleWithFixedDelay(Iot3MobilityBootstrapExample::sendTestCam, 1, 1, TimeUnit.SECONDS); + messageScheduler.scheduleWithFixedDelay(() -> sendTestCam(CamVersion.V1_1_3), 1, 1, TimeUnit.SECONDS); messageScheduler.scheduleWithFixedDelay(Iot3MobilityBootstrapExample::sendTestDenm, 1, 10, TimeUnit.SECONDS); - messageScheduler.scheduleWithFixedDelay(Iot3MobilityBootstrapExample::sendTestCpm, 1, 1, TimeUnit.SECONDS); + messageScheduler.scheduleWithFixedDelay(() -> sendTestCpm(CpmVersion.V1_2_1), 1, 1, TimeUnit.SECONDS); } - private static void sendTestCam() { + private static void sendTestCam(CamVersion camVersion) { LatLng position = new LatLng(48.625218, 2.243448); // center point of UTAC TEQMO try { - ioT3Mobility.sendPosition(StationType.PASSENGER_CAR, position, 0, 0, 0, 0, 0, CamVersion.V1_1_3); + ioT3Mobility.sendPosition(StationType.PASSENGER_CAR, position, 0, 0, 0, 0, 0, camVersion); } catch (IOException e) { - throw new RuntimeException(e); + System.out.println("CAM ERROR: " + e); } } @@ -237,81 +239,42 @@ private static void sendTestDenm() { ioT3Mobility.sendHazard(HazardType.ACCIDENT_NO_SUBCAUSE, position, 10, 7, StationType.PASSENGER_CAR); } - private static void sendTestCpm() { + private static void sendTestCpm(CpmVersion cpmVersion) { LatLng position = new LatLng(48.625152, 2.240349); // city area of UTAC TEQMO + int pedestrianX = -1800 + Utils.randomBetween(-10, 10); + int pedestrianY = 200 + Utils.randomBetween(-10, 10); + int bicycleX = 1500 + Utils.randomBetween(-10, 10); + int bicycleY = 100 + Utils.randomBetween(-10, 10); - PerceivedObject pedestrianPo = new PerceivedObject.PerceivedObjectBuilder( - 12, 0, 1500) - .distance(-1800 + Utils.randomBetween(-10, 10), - 200 + Utils.randomBetween(-10, 10)) - .speed(0, 0) - .objectDimension(10, 10, 20, 0) - .classification(List.of(new ClassificationItem( - new ObjectClassSingleVru( - new ObjectVruPedestrian(1)), - 100))) - .sensorIdList(List.of(123)) - .confidence( - new PerceivedObjectConfidence.PerceivedObjectConfidenceBuilder(15) - .distance(0, 0) - .speed(0, 0) - .build()) - .build(); + try { + if(cpmVersion == CpmVersion.V1_2_1) { + CpmEnvelope121 cpmEnvelope121 = CpmV121Factory.createTestCpmEnvelope( + position, + EXAMPLE_UUID, + pedestrianX, + pedestrianY, + bicycleX, + bicycleY); - PerceivedObject bicyclePo = new PerceivedObject.PerceivedObjectBuilder( - 34, 0, 1500) - .distance(1500 + Utils.randomBetween(-10, 10), - 100 + Utils.randomBetween(-10, 10)) - .speed(0, 0) - .objectDimension(20, 20, 15, 0) - .classification(List.of(new ClassificationItem( - new ObjectClassSingleVru( - new ObjectVruBicyclist(1)), - 100))) - .sensorIdList(List.of(123)) - .confidence( - new PerceivedObjectConfidence.PerceivedObjectConfidenceBuilder(15) - .distance(0, 0) - .speed(0, 0) - .build()) - .build(); + System.out.println("Sending CPM v1.2.1: " + cpmEnvelope121); - CPM cpm = new CPM.CPMBuilder() - .header(JsonValue.Origin.SELF.value(), - JsonValue.Version.CURRENT_CPM.value(), + ioT3Mobility.sendCpm(cpmEnvelope121); + } else if(cpmVersion == CpmVersion.V2_1_1) { + CpmEnvelope211 cpmEnvelope211 = CpmV211Factory.createTestCpmEnvelope( + position, EXAMPLE_UUID, - TrueTime.getAccurateTime()) - .pduHeader(2, - 123456, - (int) (TrueTime.getAccurateETSITime() % 65536)) - .managementContainer( - new ManagementContainer( - StationType.ROAD_SIDE_UNIT.getId(), - new Position( - (long) (position.getLatitude() * EtsiUtils.ETSI_COORDINATES_FACTOR), - (long) (position.getLongitude() * EtsiUtils.ETSI_COORDINATES_FACTOR), - 0), - new PositionConfidence( - new PositionConfidenceEllipse(0, 0, 0), - 0))) - .stationDataContainer( - new StationDataContainer( - new OriginatingRsuContainer( - 123, 123, 123))) - .sensorInformationContainer( - new SensorInformationContainer( - List.of(new SensorInformation(123, 4, - new DetectionArea( - new StationarySensorCircular( - new Offset(0, 0, 0), - 200)))))) - .perceivedObjectContainer( - new PerceivedObjectContainer( - List.of(pedestrianPo, - bicyclePo))) - .build(); + pedestrianX, + pedestrianY, + bicycleX, + bicycleY); + + System.out.println("Sending CPM v2.1.1: " + cpmEnvelope211); - ioT3Mobility.sendCpm(cpm); + ioT3Mobility.sendCpm(cpmEnvelope211); + } + } catch (Exception e) { + System.out.println("CPM ERROR: " + e); + } } } diff --git a/java/iot3/examples/src/main/java/com/orange/Iot3MobilityExample.java b/java/iot3/examples/src/main/java/com/orange/Iot3MobilityExample.java index a3b302d58..ae4352f5b 100644 --- a/java/iot3/examples/src/main/java/com/orange/Iot3MobilityExample.java +++ b/java/iot3/examples/src/main/java/com/orange/Iot3MobilityExample.java @@ -5,20 +5,17 @@ import com.orange.iot3core.clients.lwm2m.model.Lwm2mServer; import com.orange.iot3mobility.IoT3Mobility; import com.orange.iot3mobility.IoT3MobilityCallback; -import com.orange.iot3mobility.TrueTime; import com.orange.iot3mobility.Utils; -import com.orange.iot3mobility.its.EtsiUtils; import com.orange.iot3mobility.messages.cam.core.CamCodec; import com.orange.iot3mobility.messages.cam.core.CamVersion; import com.orange.iot3mobility.messages.cam.v113.model.CamEnvelope113; import com.orange.iot3mobility.messages.cam.v230.model.CamEnvelope230; +import com.orange.iot3mobility.messages.cpm.core.CpmCodec; +import com.orange.iot3mobility.messages.cpm.core.CpmVersion; +import com.orange.iot3mobility.messages.cpm.v121.model.CpmEnvelope121; +import com.orange.iot3mobility.messages.cpm.v211.model.CpmEnvelope211; import com.orange.iot3mobility.roadobjects.HazardType; import com.orange.iot3mobility.its.StationType; -import com.orange.iot3mobility.its.json.JsonValue; -import com.orange.iot3mobility.its.json.Position; -import com.orange.iot3mobility.its.json.PositionConfidence; -import com.orange.iot3mobility.its.json.PositionConfidenceEllipse; -import com.orange.iot3mobility.its.json.cpm.*; import com.orange.iot3mobility.its.json.denm.DENM; import com.orange.iot3mobility.managers.IoT3RoadHazardCallback; import com.orange.iot3mobility.managers.IoT3RoadSensorCallback; @@ -31,7 +28,6 @@ import com.orange.lwm2m.model.CustomLwm2mConnectivityStatisticsExample; import java.io.IOException; -import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -203,8 +199,14 @@ public void sensorObjectExpired(SensorObject sensorObject) { } @Override - public void cpmArrived(CPM cpm) { - System.out.println("CPM received: " + cpm.getJson()); + public void cpmArrived(CpmCodec.CpmFrame cpmFrame) { + if(cpmFrame.version() == CpmVersion.V1_2_1) { + CpmEnvelope121 cpm121 = (CpmEnvelope121) cpmFrame.envelope(); + System.out.println("Raw CPM v1.2.1: " + cpm121); + } else if(cpmFrame.version() == CpmVersion.V2_1_1) { + CpmEnvelope211 cpm211 = (CpmEnvelope211) cpmFrame.envelope(); + System.out.println("Raw CPM v2.1.1: " + cpm211); + } } }); @@ -231,18 +233,18 @@ private static void setRegionOfInterest(LatLng roiPosition) { private static synchronized void startSendingMessages() { ScheduledExecutorService messageScheduler = Executors.newScheduledThreadPool(1); - messageScheduler.scheduleWithFixedDelay(Iot3MobilityExample::sendTestCam, 1, 1, TimeUnit.SECONDS); + messageScheduler.scheduleWithFixedDelay(() -> sendTestCam(CamVersion.V1_1_3), 1, 1, TimeUnit.SECONDS); messageScheduler.scheduleWithFixedDelay(Iot3MobilityExample::sendTestDenm, 1, 10, TimeUnit.SECONDS); - messageScheduler.scheduleWithFixedDelay(Iot3MobilityExample::sendTestCpm, 1, 1, TimeUnit.SECONDS); + messageScheduler.scheduleWithFixedDelay(() -> sendTestCpm(CpmVersion.V1_2_1), 1, 1, TimeUnit.SECONDS); messageScheduler.scheduleWithFixedDelay(Iot3MobilityExample::sendTestConnStat, 1, 1, TimeUnit.SECONDS); } - private static void sendTestCam() { + private static void sendTestCam(CamVersion camVersion) { LatLng position = new LatLng(48.625218, 2.243448); // center point of UTAC TEQMO try { - ioT3Mobility.sendPosition(StationType.PASSENGER_CAR, position, 0, 0, 0, 0, 0, CamVersion.V1_1_3); + ioT3Mobility.sendPosition(StationType.PASSENGER_CAR, position, 0, 0, 0, 0, 0, camVersion); } catch (IOException e) { - throw new RuntimeException(e); + System.out.println("CAM ERROR: " + e); } } @@ -251,81 +253,42 @@ private static void sendTestDenm() { ioT3Mobility.sendHazard(HazardType.ACCIDENT_NO_SUBCAUSE, position, 10, 7, StationType.PASSENGER_CAR); } - private static void sendTestCpm() { + private static void sendTestCpm(CpmVersion cpmVersion) { LatLng position = new LatLng(48.625152, 2.240349); // city area of UTAC TEQMO + int pedestrianX = -1800 + Utils.randomBetween(-10, 10); + int pedestrianY = 200 + Utils.randomBetween(-10, 10); + int bicycleX = 1500 + Utils.randomBetween(-10, 10); + int bicycleY = 100 + Utils.randomBetween(-10, 10); - PerceivedObject pedestrianPo = new PerceivedObject.PerceivedObjectBuilder( - 12, 0, 1500) - .distance(-1800 + Utils.randomBetween(-10, 10), - 200 + Utils.randomBetween(-10, 10)) - .speed(0, 0) - .objectDimension(10, 10, 20, 0) - .classification(List.of(new ClassificationItem( - new ObjectClassSingleVru( - new ObjectVruPedestrian(1)), - 100))) - .sensorIdList(List.of(123)) - .confidence( - new PerceivedObjectConfidence.PerceivedObjectConfidenceBuilder(15) - .distance(0, 0) - .speed(0, 0) - .build()) - .build(); + try { + if(cpmVersion == CpmVersion.V1_2_1) { + CpmEnvelope121 cpmEnvelope121 = CpmV121Factory.createTestCpmEnvelope( + position, + EXAMPLE_UUID, + pedestrianX, + pedestrianY, + bicycleX, + bicycleY); - PerceivedObject bicyclePo = new PerceivedObject.PerceivedObjectBuilder( - 34, 0, 1500) - .distance(1500 + Utils.randomBetween(-10, 10), - 100 + Utils.randomBetween(-10, 10)) - .speed(0, 0) - .objectDimension(20, 20, 15, 0) - .classification(List.of(new ClassificationItem( - new ObjectClassSingleVru( - new ObjectVruBicyclist(1)), - 100))) - .sensorIdList(List.of(123)) - .confidence( - new PerceivedObjectConfidence.PerceivedObjectConfidenceBuilder(15) - .distance(0, 0) - .speed(0, 0) - .build()) - .build(); + System.out.println("Sending CPM v1.2.1: " + cpmEnvelope121); - CPM cpm = new CPM.CPMBuilder() - .header(JsonValue.Origin.SELF.value(), - JsonValue.Version.CURRENT_CPM.value(), + ioT3Mobility.sendCpm(cpmEnvelope121); + } else if(cpmVersion == CpmVersion.V2_1_1) { + CpmEnvelope211 cpmEnvelope211 = CpmV211Factory.createTestCpmEnvelope( + position, EXAMPLE_UUID, - TrueTime.getAccurateTime()) - .pduHeader(2, - 123456, - (int) (TrueTime.getAccurateETSITime() % 65536)) - .managementContainer( - new ManagementContainer( - StationType.ROAD_SIDE_UNIT.getId(), - new Position( - (long) (position.getLatitude() * EtsiUtils.ETSI_COORDINATES_FACTOR), - (long) (position.getLongitude() * EtsiUtils.ETSI_COORDINATES_FACTOR), - 0), - new PositionConfidence( - new PositionConfidenceEllipse(0, 0, 0), - 0))) - .stationDataContainer( - new StationDataContainer( - new OriginatingRsuContainer( - 123, 123, 123))) - .sensorInformationContainer( - new SensorInformationContainer( - List.of(new SensorInformation(123, 4, - new DetectionArea( - new StationarySensorCircular( - new Offset(0, 0, 0), - 200)))))) - .perceivedObjectContainer( - new PerceivedObjectContainer( - List.of(pedestrianPo, - bicyclePo))) - .build(); + pedestrianX, + pedestrianY, + bicycleX, + bicycleY); + + System.out.println("Sending CPM v2.1.1: " + cpmEnvelope211); - ioT3Mobility.sendCpm(cpm); + ioT3Mobility.sendCpm(cpmEnvelope211); + } + } catch (Exception e) { + System.out.println("CPM ERROR: " + e); + } } private static void sendTestConnStat() { diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/IoT3Mobility.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/IoT3Mobility.java index 0026af59a..1c13cc45e 100644 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/IoT3Mobility.java +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/IoT3Mobility.java @@ -25,11 +25,13 @@ import com.orange.iot3mobility.messages.cam.v230.model.MessageFormat; import com.orange.iot3mobility.messages.cam.v230.model.basiccontainer.Altitude; import com.orange.iot3mobility.messages.cam.v230.model.highfrequencycontainer.*; +import com.orange.iot3mobility.messages.cpm.CpmHelper; +import com.orange.iot3mobility.messages.cpm.v121.model.CpmEnvelope121; +import com.orange.iot3mobility.messages.cpm.v211.model.CpmEnvelope211; import com.orange.iot3mobility.roadobjects.HazardType; import com.orange.iot3mobility.its.StationType; import com.orange.iot3mobility.its.json.JsonValue; import com.orange.iot3mobility.its.json.Position; -import com.orange.iot3mobility.its.json.cpm.CPM; import com.orange.iot3mobility.its.json.denm.*; import com.orange.iot3mobility.managers.*; import com.orange.iot3mobility.quadkey.LatLng; @@ -61,6 +63,7 @@ public class IoT3Mobility { private final IoT3Core ioT3Core; private final RoIManager roIManager; private final CamHelper camHelper = new CamHelper(); + private final CpmHelper cpmHelper = new CpmHelper(); private final String uuid; private final String context; @@ -319,7 +322,7 @@ public void setRawMessageCallback(IoT3RawMessageCallback ioT3RawMessageCallback) private void processMessage(String topic, String message) { if(ioT3RawMessageCallback != null) ioT3RawMessageCallback.messageArrived(message); if(topic.contains("/cam/")) RoadUserManager.processCam(message, camHelper); - else if(topic.contains("/cpm/")) RoadSensorManager.processCpm(message); + else if(topic.contains("/cpm/")) RoadSensorManager.processCpm(message, cpmHelper); else if(topic.contains("/denm/")) RoadHazardManager.processDenm(message); } @@ -504,21 +507,39 @@ public void sendDenm(DENM denm) { } /** - * Send a CPM - Cooperative Perception Message + * Send a CPM - Cooperative Perception Message - v1.2.1 * - * @param cpm the CPM representing your sensors and their perceived objects + * @param cpmV121 the CPM representing sensors and their perceived objects */ - public void sendCpm(CPM cpm) { + public void sendCpm(CpmEnvelope121 cpmV121) throws IOException { // build the topic String quadkey = QuadTileHelper.latLngToQuadKey( - cpm.getManagementContainer().getReferencePosition().getLatitudeDegree(), - cpm.getManagementContainer().getReferencePosition().getLongitudeDegree(), + EtsiConverter.latitudeDegrees(cpmV121.message().managementContainer().referencePosition().latitude()), + EtsiConverter.longitudeDegrees(cpmV121.message().managementContainer().referencePosition().longitude()), 22); String geoExtension = QuadTileHelper.quadKeyToQuadTopic(quadkey); String topic = context + "/inQueue/v2x/cpm/" + uuid + geoExtension; // send the message only if the client is connected - if(isConnected()) ioT3Core.mqttPublish(topic, cpm.getJson().toString(), false, 0, 1); + if(isConnected()) ioT3Core.mqttPublish(topic, cpmHelper.toJson(cpmV121), false, 0, 1); + } + + /** + * Send a CPM - Cooperative Perception Message - v2.1.1 + * + * @param cpmV211 the CPM representing sensors and their perceived objects + */ + public void sendCpm(CpmEnvelope211 cpmV211) throws IOException { + // build the topic + String quadkey = QuadTileHelper.latLngToQuadKey( + EtsiConverter.latitudeDegrees(cpmV211.message().managementContainer().referencePosition().latitude()), + EtsiConverter.longitudeDegrees(cpmV211.message().managementContainer().referencePosition().longitude()), + 22); + String geoExtension = QuadTileHelper.quadKeyToQuadTopic(quadkey); + String topic = context + "/inQueue/v2x/cpm/" + uuid + geoExtension; + + // send the message only if the client is connected + if(isConnected()) ioT3Core.mqttPublish(topic, cpmHelper.toJson(cpmV211), false, 0, 1); } /** diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/CPM.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/CPM.java deleted file mode 100644 index 4b1b2cac4..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/CPM.java +++ /dev/null @@ -1,383 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonKey; -import com.orange.iot3mobility.its.json.JsonUtil; -import com.orange.iot3mobility.its.json.JsonValue; -import com.orange.iot3mobility.its.json.MessageBase; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Cooperative Perception Message. - *

- * Collective Perception Messages (CPMs) are transmitted by ITS-Ss in order to share information about perceived - * objects (such as vehicles, pedestrians, animals and other collision relevant objects) and perception regions - * (road regions that allow receiving ITS-Ss to determine unoccupied regions) in the local environment. - *

- * A CPM contains a set of perceived objects and regions, along with their observed status and attributes. - * The content may vary depending on the type of the road user or object and the detection capabilities of the - * originating ITS-S. - */ -public class CPM extends MessageBase { - - private static final Logger LOGGER = Logger.getLogger(CPM.class.getName()); - - private static boolean strictMode = true; - - private final JSONObject json = new JSONObject(); - - /** - * Version of the ITS message and/or communication protocol. - */ - private final int protocolVersion; - - /** - * ITS-station identifier - */ - private final long stationId; - - /** - * Time of the reference position in the CPM, considered as time of the CPM generation. - *

- * TimestampIts mod 65 536. TimestampIts represents an integer value in milliseconds since - * 2004-01-01T00:00:00:000Z. - *

- * oneMilliSec(1). - */ - private final int generationDeltaTime; - - /** - * Contains the type and reference position of the emitting ITS-station. - */ - private final ManagementContainer managementContainer; - - /** - * Contains a sub-container describing the emitting ITS-station. - */ - private final StationDataContainer stationDataContainer; - - /** - * Contains all the sensor information of the emitting ITS-S. - */ - private final SensorInformationContainer sensorInformationContainer; - - /** - * Contains all the objects that have been perceived by the sensors of the emitting ITS-S. - */ - private final PerceivedObjectContainer perceivedObjectContainer; - - private CPM( - final String type, - final String origin, - final String version, - final String sourceUuid, - final long timestamp, - final int protocolVersion, - final long stationId, - final int generationDeltaTime, - final ManagementContainer managementContainer, - final StationDataContainer stationDataContainer, - final SensorInformationContainer sensorInformationContainer, - final PerceivedObjectContainer perceivedObjectContainer - ) throws IllegalArgumentException { - super(type, origin, version, sourceUuid, timestamp); - if(protocolVersion == UNKNOWN && isStrictMode()) { - throw new IllegalArgumentException("CPM ProtocolVersion is missing"); - } else if(isStrictMode() && (protocolVersion > 255 || protocolVersion < 0)) { - throw new IllegalArgumentException("CPM ProtocolVersion should be in the range of [0 - 255]." - + " Value: " + protocolVersion); - } - this.protocolVersion = protocolVersion; - if(stationId == UNKNOWN && isStrictMode()) { - throw new IllegalArgumentException("CPM StationID is missing"); - } else if(isStrictMode() && (stationId > 4294967295L || stationId < 0)) { - throw new IllegalArgumentException("CPM StationID should be in the range of [0 - 4294967295]." - + " Value: " + stationId); - } - this.stationId = stationId; - if(generationDeltaTime == UNKNOWN && isStrictMode()) { - throw new IllegalArgumentException("CPM GenerationDeltaTime is missing"); - } else if(isStrictMode() && (generationDeltaTime > 65535 || generationDeltaTime < 0)) { - throw new IllegalArgumentException("CPM GenerationDeltaTime should be in the range of [0 - 65535]." - + " Value: " + generationDeltaTime); - } - this.generationDeltaTime = generationDeltaTime; - if(managementContainer == null) { - throw new IllegalArgumentException("CPM ManagementContainer missing."); - } - this.managementContainer = managementContainer; - this.stationDataContainer = stationDataContainer; - this.sensorInformationContainer = sensorInformationContainer; - this.perceivedObjectContainer = perceivedObjectContainer; - - createJson(); - } - - private void createJson() { - try { - JSONObject message = new JSONObject(); - message.put(JsonCpmKey.Cpm.PROTOCOL_VERSION.key(), protocolVersion); - message.put(JsonCpmKey.Cpm.STATION_ID.key(), stationId); - message.put(JsonCpmKey.Cpm.GENERATION_DELTA_TIME.key(), generationDeltaTime); - message.put(JsonCpmKey.Cpm.MANAGEMENT_CONTAINER.key(), managementContainer.getJson()); - if(stationDataContainer != null) - message.put(JsonCpmKey.Cpm.STATION_DATA_CONTAINER.key(), stationDataContainer.getJson()); - if(sensorInformationContainer != null) - message.put(JsonCpmKey.Cpm.SENSOR_INFORMATION_CONTAINER.key(), sensorInformationContainer.getJson()); - if(perceivedObjectContainer != null) - message.put(JsonCpmKey.Cpm.PERCEIVED_OBJECT_CONTAINER.key(), perceivedObjectContainer.getJson()); - - json.put(JsonKey.Header.TYPE.key(), getType()); - json.put(JsonKey.Header.ORIGIN.key(), getOrigin()); - json.put(JsonKey.Header.VERSION.key(), getVersion()); - json.put(JsonKey.Header.SOURCE_UUID.key(), getSourceUuid()); - json.put(JsonKey.Header.TIMESTAMP.key(), getTimestamp()); - json.put(JsonKey.Header.MESSAGE.key(), message); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public int getProtocolVersion() { - return protocolVersion; - } - - public long getStationId() { - return stationId; - } - - public int getGenerationDeltaTime() { - return generationDeltaTime; - } - - public ManagementContainer getManagementContainer() { - return managementContainer; - } - - public StationDataContainer getStationDataContainer() { - return stationDataContainer; - } - - public SensorInformationContainer getSensorInformationContainer() { - return sensorInformationContainer; - } - - public PerceivedObjectContainer getPerceivedObjectContainer() { - return perceivedObjectContainer; - } - - public static class CPMBuilder { - private final String type; - private String origin; - private String version; - private String sourceUuid; - private long timestamp; - private int protocolVersion; - private long stationId; - private int generationDeltaTime; - private ManagementContainer managementContainer; - private StationDataContainer stationDataContainer; - private SensorInformationContainer sensorInformationContainer; - private PerceivedObjectContainer perceivedObjectContainer; - - /** - * Start building a CPM. - */ - public CPMBuilder() { - this.type = JsonValue.Type.CPM.value(); - } - - /** - * Sets the JSON header of the CPM. - *

- * These fields are mandatory. - * - * @param origin The entity responsible for emitting the message. - * @param version JSON message format version. - * @param sourceUuid The identifier of the entity responsible for emitting the message. - * @param timestamp The timestamp when the message was generated since Unix Epoch (1970/01/01), in milliseconds. - */ - public CPMBuilder header(String origin, - String version, - String sourceUuid, - long timestamp) { - this.origin = origin; - this.version = version; - this.sourceUuid = sourceUuid; - this.timestamp = timestamp; - return this; - } - - /** - * Sets the PDU header of the CPM. - *

- * These fields are mandatory. - * - * @param protocolVersion {@link CPM#protocolVersion} - * @param stationId {@link CPM#stationId} - * @param generationDeltaTime {@link CPM#generationDeltaTime} - */ - public CPMBuilder pduHeader(int protocolVersion, - long stationId, - int generationDeltaTime) { - this.protocolVersion = protocolVersion; - this.stationId = stationId; - this.generationDeltaTime = generationDeltaTime; - return this; - } - - /** - * Sets the management container of the CPM. - *

- * This field is mandatory. - * - * @param managementContainer {@link CPM#managementContainer} - */ - public CPMBuilder managementContainer(ManagementContainer managementContainer) { - this.managementContainer = managementContainer; - return this; - } - - /** - * Sets the station data container of the CPM. - *

- * This field is optional. - * - * @param stationDataContainer {@link CPM#stationDataContainer} - */ - public CPMBuilder stationDataContainer(StationDataContainer stationDataContainer) { - this.stationDataContainer = stationDataContainer; - return this; - } - - /** - * Sets the sensor information container of the CPM. - *

- * This field is optional. - * - * @param sensorInformationContainer {@link CPM#sensorInformationContainer} - */ - public CPMBuilder sensorInformationContainer(SensorInformationContainer sensorInformationContainer) { - this.sensorInformationContainer = sensorInformationContainer; - return this; - } - - /** - * Sets the perceived object container of the CPM. - *

- * This field is optional. - * - * @param perceivedObjectContainer {@link CPM#perceivedObjectContainer} - */ - public CPMBuilder perceivedObjectContainer(PerceivedObjectContainer perceivedObjectContainer) { - this.perceivedObjectContainer = perceivedObjectContainer; - return this; - } - - /** - * Build the CPM. - *

- * Call after setting all the mandatory fields. - * - * @return {@link #CPM} - */ - public CPM build() { - return new CPM( - type, - origin, - version, - sourceUuid, - timestamp, - protocolVersion, - stationId, - generationDeltaTime, - managementContainer, - stationDataContainer, - sensorInformationContainer, - perceivedObjectContainer - ); - } - } - - public static boolean isStrictMode() { - return strictMode; - } - - public static void setStrictMode(boolean strictMode) { - CPM.strictMode = strictMode; - } - - /** - * Parse a CPM in JSON format. - * - * @param jsonCPM The CPM in JSON format - * @return {@link CPM} - */ - public static CPM jsonParser(JSONObject jsonCPM) { - if(JsonUtil.isNullOrEmpty(jsonCPM)) return null; - try { - String type = jsonCPM.getString(JsonKey.Header.TYPE.key()); - - if(type.equals(JsonValue.Type.CPM.value())) { - JSONObject message = jsonCPM.getJSONObject(JsonKey.Header.MESSAGE.key()); - - String origin = jsonCPM.getString(JsonKey.Header.ORIGIN.key()); - String version = jsonCPM.getString(JsonKey.Header.VERSION.key()); - String sourceUuid = jsonCPM.getString(JsonKey.Header.SOURCE_UUID.key()); - long timestamp = jsonCPM.getLong(JsonKey.Header.TIMESTAMP.key()); - - int protocolVersion = message.getInt(JsonCpmKey.Cpm.PROTOCOL_VERSION.key()); - long stationId = message.getLong(JsonCpmKey.Cpm.STATION_ID.key()); - int generationDeltaTime = message.getInt(JsonCpmKey.Cpm.GENERATION_DELTA_TIME.key()); - - JSONObject jsonManagementContainer = message.getJSONObject(JsonCpmKey.Cpm.MANAGEMENT_CONTAINER.key()); - ManagementContainer managementContainer = ManagementContainer.jsonParser(jsonManagementContainer); - - JSONObject jsonStationDataContainer = message.optJSONObject(JsonCpmKey.Cpm.STATION_DATA_CONTAINER.key()); - StationDataContainer stationDataContainer = StationDataContainer.jsonParser(jsonStationDataContainer); - - JSONArray jsonSensorInformationContainer = message.optJSONArray(JsonCpmKey.Cpm.SENSOR_INFORMATION_CONTAINER.key()); - SensorInformationContainer sensorInformationContainer = SensorInformationContainer.jsonParser(jsonSensorInformationContainer); - - JSONArray jsonPerceivedObjectContainer = message.optJSONArray(JsonCpmKey.Cpm.PERCEIVED_OBJECT_CONTAINER.key()); - PerceivedObjectContainer perceivedObjectContainer = PerceivedObjectContainer.jsonParser(jsonPerceivedObjectContainer); - - return new CPMBuilder() - .header(origin, - version, - sourceUuid, - timestamp) - .pduHeader(protocolVersion, - stationId, - generationDeltaTime) - .managementContainer(managementContainer) - .stationDataContainer(stationDataContainer) - .sensorInformationContainer(sensorInformationContainer) - .perceivedObjectContainer(perceivedObjectContainer) - .build(); - } - } catch (JSONException | IllegalArgumentException e) { - LOGGER.log(Level.WARNING, "CPM JSON parsing error", "Error: " + e); - } - return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ClassificationItem.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ClassificationItem.java deleted file mode 100644 index fc606af0f..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ClassificationItem.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import com.orange.iot3mobility.its.StationType; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class ClassificationItem { - - private static final Logger LOGGER = Logger.getLogger(ClassificationItem.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * The class that best describes the detected object. - * - * The object can be classified into one of three main categories: vehicle, VRU and other. - */ - private final ObjectClassVehicle objectClassVehicle; - private final ObjectClassSingleVru objectClassSingleVru; - private final ObjectClassOther objectClassOther; - - /** - * Describes the confidence value for the type of a detected object. - * - * unknown(0) in case the confidence value is unknown but the reported classification - * is still valid, onePercent(1), oneHundredPercent(100), unavailable(101) - * in case the class confidence value computation is not available for this object, - * indicates that the class assignment is invalid - */ - private final int confidence; - - public ClassificationItem(final ObjectClassVehicle objectClassVehicle, - final int confidence) throws IllegalArgumentException { - if(objectClassVehicle == null && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM PerceivedObject objectClass is missing"); - } - this.objectClassVehicle = objectClassVehicle; - this.objectClassSingleVru = null; - this.objectClassOther = null; - if(CPM.isStrictMode() && (confidence > 101 || confidence < 0)) { - throw new IllegalArgumentException("CPM PerceivedObject confidence should be in the range of [0 - 101]." - + " Value: " + confidence); - } - this.confidence = confidence; - - createJson(); - } - - public ClassificationItem(final ObjectClassSingleVru objectClassSingleVru, - final int confidence) throws IllegalArgumentException { - if(objectClassSingleVru == null && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM PerceivedObject objectClass is missing"); - } - this.objectClassVehicle = null; - this.objectClassSingleVru = objectClassSingleVru; - this.objectClassOther = null; - if(CPM.isStrictMode() && (confidence > 101 || confidence < 0)) { - throw new IllegalArgumentException("CPM PerceivedObject confidence should be in the range of [0 - 101]." - + " Value: " + confidence); - } - this.confidence = confidence; - - createJson(); - } - - public ClassificationItem(final ObjectClassOther objectClassOther, - final int confidence) throws IllegalArgumentException { - if(objectClassOther == null && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM PerceivedObject objectClass is missing"); - } - this.objectClassSingleVru = null; - this.objectClassVehicle = null; - this.objectClassOther = objectClassOther; - if(CPM.isStrictMode() && (confidence > 101 || confidence < 0)) { - throw new IllegalArgumentException("CPM PerceivedObject confidence should be in the range of [0 - 101]." - + " Value: " + confidence); - } - this.confidence = confidence; - - createJson(); - } - - private void createJson() { - try { - if(objectClassVehicle != null) - json.put(JsonCpmKey.Classification.OBJECT_CLASS.key(), objectClassVehicle.getJson()); - else if(objectClassSingleVru != null) - json.put(JsonCpmKey.Classification.OBJECT_CLASS.key(), objectClassSingleVru.getJson()); - else if(objectClassOther != null) - json.put(JsonCpmKey.Classification.OBJECT_CLASS.key(), objectClassOther.getJson()); - json.put(JsonCpmKey.Classification.CONFIDENCE.key(), confidence); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM ClassificationItem JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public Object getObjectClass() { - if(objectClassVehicle != null) return objectClassVehicle; - if(objectClassSingleVru != null) return objectClassSingleVru; - return objectClassOther; - } - - public boolean isObjectPedestrian() { - if(objectClassSingleVru != null) { - Object singleVru = objectClassSingleVru.getVruObject(); - return singleVru instanceof ObjectVruPedestrian; - } - return false; - } - - public int getObjectClassId() { - if(objectClassVehicle != null) { - // raw ID - return objectClassVehicle.getSubclass(); - } else if(objectClassSingleVru != null) { - Object singleVru = objectClassSingleVru.getVruObject(); - if(singleVru instanceof ObjectVruPedestrian vruPedestrian) { - // raw ID + 10 - return vruPedestrian.getSubclass() + 10; - } else if(singleVru instanceof ObjectVruBicyclist vruBicyclist) { - // raw ID + 20 - return vruBicyclist.getSubclass() + 20; - } else if(singleVru instanceof ObjectVruMotorcyclist vruMotorcyclist) { - // raw ID + 30 - return vruMotorcyclist.getSubclass() + 30; - } else if(singleVru instanceof ObjectVruAnimal vruAnimal) { - // raw ID + 40 - return vruAnimal.getSubclass() + 40; - } else return -1; - } else if (objectClassOther != null) { - if(objectClassOther.getSubclass() == 1) - return 51; // raw ID + 50 - else - return -1; - } - return -1; - } - - public String getObjectClassStr() { - if(objectClassVehicle != null) { - return switch (objectClassVehicle.getSubclass()) { - case 1 -> "passenger-car"; - case 2 -> "bus"; - case 3 -> "light-truck"; - case 4 -> "heavy-truck"; - case 5 -> "trailer-truck"; - case 6 -> "special-vehicle"; - case 7 -> "tram"; - case 8 -> "emergency-vehicle"; - default -> "unknown"; - }; - } else if(objectClassSingleVru != null) { - Object singleVru = objectClassSingleVru.getVruObject(); - if(singleVru instanceof ObjectVruPedestrian vruPedestrian) { - return switch (vruPedestrian.getSubclass()) { - case 2 -> "pedestrian-road-worker"; - case 3 -> "pedestrian-first-responder"; - default -> "pedestrian"; - }; - } else if(singleVru instanceof ObjectVruBicyclist vruBicyclist) { - return switch (vruBicyclist.getSubclass()) { - case 2 -> "wheelchair-user_bicycle"; - case 3 -> "horseRider_bicycle"; - case 4 -> "rollerskater_bicycle"; - case 5 -> "e-scooter_bicycle"; - case 6 -> "personnal-transporter_bicycle"; - case 7 -> "pedelec_bicycle"; - case 8 -> "speed-pedelec_bicycle"; - default -> "bicycle"; - }; - } else if(singleVru instanceof ObjectVruMotorcyclist vruMotorcyclist) { - return switch (vruMotorcyclist.getSubclass()) { - case 1 -> "moped"; - case 3 -> "sidecar-right"; - case 4 -> "sidecar-left"; - default -> "motorcycle"; - }; - } else if(singleVru instanceof ObjectVruAnimal vruAnimal) { - return switch (vruAnimal.getSubclass()) { - case 1 -> "wild-animal"; - case 2 -> "farm-animal"; - case 3 -> "service-animal"; - default -> "animal"; - }; - } else return "unknown"; - } else if (objectClassOther != null) { - if(objectClassOther.getSubclass() == 1) - return "roadSideUnit"; - else - return "other"; - } - return "unknown"; - } - - public int getObjectStationType() { - if(objectClassVehicle != null) { - return switch (objectClassVehicle.getSubclass()) { - case 1 -> StationType.PASSENGER_CAR.getId(); - case 2 -> StationType.BUS.getId(); - case 3 -> StationType.LIGHT_TRUCK.getId(); - case 4 -> StationType.HEAVY_TRUCK.getId(); - case 5 -> StationType.TRAILER.getId(); - case 6, 8 -> StationType.SPECIAL_VEHICLES.getId(); - case 7 -> StationType.TRAM.getId(); - default -> StationType.UNKNOWN.getId(); - }; - } else if(objectClassSingleVru != null) { - Object singleVru = objectClassSingleVru.getVruObject(); - if(singleVru instanceof ObjectVruPedestrian) { - return StationType.PEDESTRIAN.getId(); - } else if(singleVru instanceof ObjectVruBicyclist) { - return StationType.CYCLIST.getId(); - } else if(singleVru instanceof ObjectVruMotorcyclist vruMotorcyclist) { - if(vruMotorcyclist.getSubclass() == 1) return StationType.MOPED.getId(); - else return StationType.MOTORCYCLE.getId(); - } else return StationType.UNKNOWN.getId(); - } - return StationType.UNKNOWN.getId(); - } - - public int getConfidence() { - return confidence; - } - - public static ClassificationItem jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - try { - JSONObject jsonObjectClass = json.getJSONObject(JsonCpmKey.Classification.OBJECT_CLASS.key()); - ObjectClassVehicle objectClassVehicle = ObjectClassVehicle.jsonParser(jsonObjectClass); - ObjectClassSingleVru objectClassSingleVru = ObjectClassSingleVru.jsonParser(jsonObjectClass); - ObjectClassOther objectClassOther = ObjectClassOther.jsonParser(jsonObjectClass); - int confidence = json.optInt(JsonCpmKey.Classification.CONFIDENCE.key(), 0); - - if(objectClassVehicle != null) return new ClassificationItem(objectClassVehicle, confidence); - if(objectClassSingleVru != null) return new ClassificationItem(objectClassSingleVru, confidence); - if(objectClassOther != null) return new ClassificationItem(objectClassOther, confidence); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM ClassificationItem JSON parsing error", "Error: " + e); - } - return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/DetectionArea.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/DetectionArea.java deleted file mode 100644 index 3e68df624..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/DetectionArea.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class DetectionArea { - - private static final Logger LOGGER = Logger.getLogger(DetectionArea.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * If the emitting ITS-station is a vehicle. - */ - private final VehicleSensor vehicleSensor; - - /** - * If the emitting ITS-station is a RSU, and the sensor's detection area is radial. - */ - private final StationarySensorRadial stationarySensorRadial; - - /** - * If the emitting ITS-station is a RSU, and the sensor's detection area is polygonal. - */ - private final StationarySensorPolygon stationarySensorPolygon; - - /** - * If the emitting ITS-station is a RSU, and the sensor's detection area is circular. - */ - private final StationarySensorCircular stationarySensorCircular; - - /** - * If the emitting ITS-station is a RSU, and the sensor's detection area is ellipsoidal. - */ - private final StationarySensorEllipse stationarySensorEllipse; - - /** - * If the emitting ITS-station is a RSU, and the sensor's detection area is rectangular. - */ - private final StationarySensorRectangle stationarySensorRectangle; - - public DetectionArea( - final VehicleSensor vehicleSensor - ) throws IllegalArgumentException { - this(vehicleSensor, - null, - null, - null, - null, - null); - } - - public DetectionArea( - final StationarySensorRadial stationarySensorRadial - ) throws IllegalArgumentException { - this(null, - stationarySensorRadial, - null, - null, - null, - null); - } - - public DetectionArea( - final StationarySensorPolygon stationarySensorPolygon - ) throws IllegalArgumentException { - this(null, - null, - stationarySensorPolygon, - null, - null, - null); - } - - public DetectionArea( - final StationarySensorCircular stationarySensorCircular - ) throws IllegalArgumentException { - this(null, - null, - null, - stationarySensorCircular, - null, - null); - } - - public DetectionArea( - final StationarySensorEllipse stationarySensorEllipse - ) throws IllegalArgumentException { - this(null, - null, - null, - null, - stationarySensorEllipse, - null); - } - - public DetectionArea( - final StationarySensorRectangle stationarySensorRectangle - ) throws IllegalArgumentException { - this(null, - null, - null, - null, - null, - stationarySensorRectangle); - } - - public DetectionArea( - final VehicleSensor vehicleSensor, - final StationarySensorRadial stationarySensorRadial, - final StationarySensorPolygon stationarySensorPolygon, - final StationarySensorCircular stationarySensorCircular, - final StationarySensorEllipse stationarySensorEllipse, - final StationarySensorRectangle stationarySensorRectangle - ) throws IllegalArgumentException { - this.vehicleSensor = vehicleSensor; - this.stationarySensorRadial = stationarySensorRadial; - this.stationarySensorPolygon = stationarySensorPolygon; - this.stationarySensorCircular = stationarySensorCircular; - this.stationarySensorEllipse = stationarySensorEllipse; - this.stationarySensorRectangle = stationarySensorRectangle; - - createJson(); - } - - private void createJson() { - try { - if(vehicleSensor != null) - json.put(JsonCpmKey.DetectionArea.VEHICLE_SENSOR.key(), vehicleSensor.getJson()); - else if(stationarySensorRadial != null) - json.put(JsonCpmKey.DetectionArea.STATIONARY_SENSOR_RADIAL.key(), stationarySensorRadial.getJson()); - else if(stationarySensorPolygon != null) - json.put(JsonCpmKey.DetectionArea.STATIONARY_SENSOR_POLYGON.key(), stationarySensorPolygon.getJson()); - else if(stationarySensorCircular != null) - json.put(JsonCpmKey.DetectionArea.STATIONARY_SENSOR_CIRCULAR.key(), stationarySensorCircular.getJson()); - else if(stationarySensorEllipse != null) - json.put(JsonCpmKey.DetectionArea.STATIONARY_SENSOR_ELLIPSE.key(), stationarySensorEllipse.getJson()); - else if(stationarySensorRectangle != null) - json.put(JsonCpmKey.DetectionArea.STATIONARY_SENSOR_RECTANGLE.key(), stationarySensorRectangle.getJson()); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM DetectionArea JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public VehicleSensor getVehicleSensor() { - return vehicleSensor; - } - - public StationarySensorRadial getStationarySensorRadial() { - return stationarySensorRadial; - } - - public StationarySensorPolygon getStationarySensorPolygon() { - return stationarySensorPolygon; - } - - public StationarySensorCircular getStationarySensorCircular() { - return stationarySensorCircular; - } - - public StationarySensorEllipse getStationarySensorEllipse() { - return stationarySensorEllipse; - } - - public StationarySensorRectangle getStationarySensorRectangle() { - return stationarySensorRectangle; - } - - public static DetectionArea jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - JSONObject jsonVehicleSensor = json.optJSONObject(JsonCpmKey.DetectionArea.VEHICLE_SENSOR.key()); - VehicleSensor vehicleSensor = VehicleSensor.jsonParser(jsonVehicleSensor); - JSONObject jsonStationarySensorRadial = json.optJSONObject(JsonCpmKey.DetectionArea.STATIONARY_SENSOR_RADIAL.key()); - StationarySensorRadial stationarySensorRadial = StationarySensorRadial.jsonParser(jsonStationarySensorRadial); - JSONArray jsonStationarySensorPolygon = json.optJSONArray(JsonCpmKey.DetectionArea.STATIONARY_SENSOR_POLYGON.key()); - StationarySensorPolygon stationarySensorPolygon = StationarySensorPolygon.jsonParser(jsonStationarySensorPolygon); - JSONObject jsonStationarySensorCircular = json.optJSONObject(JsonCpmKey.DetectionArea.STATIONARY_SENSOR_CIRCULAR.key()); - StationarySensorCircular stationarySensorCircular = StationarySensorCircular.jsonParser(jsonStationarySensorCircular); - JSONObject jsonStationarySensorEllipse = json.optJSONObject(JsonCpmKey.DetectionArea.STATIONARY_SENSOR_ELLIPSE.key()); - StationarySensorEllipse stationarySensorEllipse = StationarySensorEllipse.jsonParser(jsonStationarySensorEllipse); - JSONObject jsonStationarySensorRectangle = json.optJSONObject(JsonCpmKey.DetectionArea.STATIONARY_SENSOR_RECTANGLE.key()); - StationarySensorRectangle stationarySensorRectangle = StationarySensorRectangle.jsonParser(jsonStationarySensorRectangle); - - return new DetectionArea( - vehicleSensor, - stationarySensorRadial, - stationarySensorPolygon, - stationarySensorCircular, - stationarySensorEllipse, - stationarySensorRectangle); - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/JsonCpmKey.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/JsonCpmKey.java deleted file mode 100644 index 5662910f5..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/JsonCpmKey.java +++ /dev/null @@ -1,444 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -public class JsonCpmKey { - - public enum Cpm { - PROTOCOL_VERSION("protocol_version"), - STATION_ID("station_id"), - MESSAGE_ID("message_id"), - GENERATION_DELTA_TIME("generation_delta_time"), - MANAGEMENT_CONTAINER("management_container"), - STATION_DATA_CONTAINER("station_data_container"), - ORIGINATING_RSU_CONTAINER("originating_rsu_container"), - SENSOR_INFORMATION_CONTAINER("sensor_information_container"), - PERCEIVED_OBJECT_CONTAINER("perceived_object_container"), - NUMBER_OF_PERCEIVED_OBJECTS("number_of_perceived_objects"); - - private String key; - - Cpm(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum ManagementContainer { - STATION_TYPE("station_type"), - REFERENCE_POSITION("reference_position"), - CONFIDENCE("confidence"); - - private String key; - - ManagementContainer(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum StationDataContainer { - - ORIGINATING_VEHICLE_CONTAINER("originating_vehicle_container"), - ORIGINATING_RSU_CONTAINER("originating_rsu_container"); - - private String key; - - StationDataContainer(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum OriginatingVehicleContainer { - - HEADING("heading"), - SPEED("speed"), - DRIVE_DIRECTION("drive_direction"), - VEHICLE_LENGTH("vehicle_length"), - VEHICLE_WIDTH("vehicle_width"), - LONGITUDINAL_ACCELERATION("longitudinal_acceleration"), - YAW_RATE("yaw_rate"), - LATERAL_ACCELERATION("lateral_acceleration"), - VERTICAL_ACCELERATION("vertical_acceleration"), - CONFIDENCE("confidence"); - - private String key; - - OriginatingVehicleContainer(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum OriginatingRsuContainer { - - REGION("region"), - INTERSECTION_REFERENCE_ID("intersection_reference_id"), - ROAD_SEGMENT_REFERENCE_ID("road_segment_reference_id"); - - private String key; - - OriginatingRsuContainer(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum IntersectionReferenceId { - - ROAD_REGULATOR_ID("road_regulator_id"), - INTERSECTION_ID("intersection_id"); - - private String key; - - IntersectionReferenceId(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum SensorInformationContainer { - - SENSOR_ID("sensor_id"), - TYPE("type"), - DETECTION_AREA("detection_area"); - - private String key; - - SensorInformationContainer(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum DetectionArea { - - VEHICLE_SENSOR("vehicle_sensor"), - STATIONARY_SENSOR_RADIAL("stationary_sensor_radial"), - STATIONARY_SENSOR_POLYGON("stationary_sensor_polygon"), - STATIONARY_SENSOR_CIRCULAR("stationary_sensor_circular"), - STATIONARY_SENSOR_ELLIPSE("stationary_sensor_ellipse"), - STATIONARY_SENSOR_RECTANGLE("stationary_sensor_rectangle"); - - private String key; - - DetectionArea(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum VehicleSensor { - - REF_POINT_ID("ref_point_id"), - X_SENSOR_OFFSET("x_sensor_offset"), - Y_SENSOR_OFFSET("y_sensor_offset"), - Z_SENSOR_OFFSET("z_sensor_offset"), - VEHICLE_SENSOR_PROPERTY_LIST("vehicle_sensor_property_list"); - - private String key; - - VehicleSensor(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum VehicleSensorProperty { - - RANGE("range"), - HORIZONTAL_OPENING_ANGLE_START("horizontal_opening_angle_start"), - HORIZONTAL_OPENING_ANGLE_END("horizontal_opening_angle_end"), - VERTICAL_OPENING_ANGLE_START("vertical_opening_angle_start"), - VERTICAL_OPENING_ANGLE_END("vertical_opening_angle_end"); - - private String key; - - VehicleSensorProperty(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum StationarySensorRadial { - - RANGE("range"), - HORIZONTAL_OPENING_ANGLE_START("horizontal_opening_angle_start"), - HORIZONTAL_OPENING_ANGLE_END("horizontal_opening_angle_end"), - VERTICAL_OPENING_ANGLE_START("vertical_opening_angle_start"), - VERTICAL_OPENING_ANGLE_END("vertical_opening_angle_end"), - SENSOR_POSITION_OFFSET("sensor_position_offset"), - SENSOR_HEIGHT("sensor_height"); - - private String key; - - StationarySensorRadial(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum StationarySensorCircular { - - NODE_CENTER_POINT("node_center_point"), - RADIUS("radius"); - - private String key; - - StationarySensorCircular(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum StationarySensorEllipseRect { - - NODE_CENTER_POINT("node_center_point"), - SEMI_MAJOR_RANGE_LENGTH("semi_major_range_length"), - SEMI_MINOR_RANGE_LENGTH("semi_minor_range_length"), - SEMI_MAJOR_RANGE_ORIENTATION("semi_major_range_orientation"), - SEMI_HEIGHT("semi_height"); - - private String key; - - StationarySensorEllipseRect(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum PerceivedObjectContainer { - - OBJECT("object"), - OBJECT_ID("object_id"), - TIME_OF_MEASUREMENT("time_of_measurement"), - OBJECT_CONFIDENCE("object_confidence"), - CONFIDENCE("confidence"), - DISTANCE("distance"), - DISTANCE_CONFIDENCE("distance_confidence"), - SPEED("speed"), - SPEED_CONFIDENCE("speed_confidence"), - OBJECT_REF_POINT("object_ref_point"), - OBJECT_AGE("object_age"), - SENSOR_ID_LIST("sensor_id_list"), - DYNAMIC_STATUS("dynamic_status"), - CLASSIFICATION("classification"); - - private String key; - - PerceivedObjectContainer(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum ObjectDistance { - - X_DISTANCE("x_distance"), - Y_DISTANCE("y_distance"), - Z_DISTANCE("z_distance"); - - private String key; - - ObjectDistance(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum ObjectAngle { - - ROLL_ANGLE("roll_angle"), - PITCH_ANGLE("pitch_angle"), - YAW_ANGLE("yaw_angle"); - - private String key; - - ObjectAngle(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum ObjectSpeed { - - X_SPEED("x_speed"), - Y_SPEED("y_speed"), - Z_SPEED("z_speed"), - ROLL_RATE("roll_rate"), - PITCH_RATE("pitch_rate"), - YAW_RATE("yaw_rate"); - - private String key; - - ObjectSpeed(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum ObjectAcceleration { - - X_ACCELERATION("x_acceleration"), - Y_ACCELERATION("y_acceleration"), - Z_ACCELERATION("z_acceleration"), - ROLL_ACCELERATION("roll_acceleration"), - PITCH_ACCELERATION("pitch_acceleration"), - YAW_ACCELERATION("yaw_acceleration"); - - private String key; - - ObjectAcceleration(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum ObjectDimension { - - PLANAR_DIMENSION_1("planar_object_dimension_1"), - PLANAR_DIMENSION_2("planar_object_dimension_2"), - VERTICAL_DIMENSION("vertical_object_dimension"); - - private String key; - - ObjectDimension(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum ObjectLanePosition { - - LONGITUDINAL_LANE_POSITION("longitudinal_lane_position"); - - private String key; - - ObjectLanePosition(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum Classification { - - OBJECT_CLASS("object_class"), - CONFIDENCE("confidence"); - - private String key; - - Classification(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum ObjectClass { - - VEHICLE("vehicle"), - SINGLE_VRU("single_vru"), - PEDESTRIAN("pedestrian"), - BICYCLIST("bicyclist"), - MOTORCYLCIST("motorcyclist"), - ANIMAL("animal"), - VRU_GROUP("vru_group"), - OTHER("other"); - - private String key; - - ObjectClass(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - - public enum Offset { - - X("x"), - Y("y"), - Z("z"); - - private String key; - - Offset(String key) { - this.key = key; - } - - public String key() { - return key; - } - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ManagementContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ManagementContainer.java deleted file mode 100644 index a3acf68d7..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ManagementContainer.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import com.orange.iot3mobility.its.json.JsonUtil; -import com.orange.iot3mobility.its.json.Position; -import com.orange.iot3mobility.its.json.PositionConfidence; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class ManagementContainer { - - private static final Logger LOGGER = Logger.getLogger(ManagementContainer.class.getName()); - - private final JSONObject jsonManagementContainer = new JSONObject(); - - /** - * Type of the emitting ITS-station. - * - * unknown(0), pedestrian(1), cyclist(2), moped(3), motorcycle(4), passengerCar(5), bus(6), - * lightTruck(7), heavyTruck(8), trailer(9), specialVehicles(10), tram(11), roadSideUnit(15). - */ - private final int stationType; - - /** - * Position of the emitting ITS-station. - */ - private final Position referencePosition; - - /** - * Confidence of the emitting ITS-station position. - */ - private final PositionConfidence confidence; - - public ManagementContainer( - final int stationType, - final Position referencePosition, - final PositionConfidence confidence - ) throws IllegalArgumentException { - if(stationType > 255 || stationType < 0) { - throw new IllegalArgumentException("CPM ManagementContainer StationType should be in the range of [0 - 255]." - + " Value: " + stationType); - } - this.stationType = stationType; - if(referencePosition == null) { - throw new IllegalArgumentException("CPM ManagementContainer ReferencePosition missing."); - } - this.referencePosition = referencePosition; - if(confidence == null) { - throw new IllegalArgumentException("CPM ManagementContainer Confidence missing."); - } - this.confidence = confidence; - - createJson(); - } - - private void createJson() { - try { - jsonManagementContainer.put(JsonCpmKey.ManagementContainer.STATION_TYPE.key(), stationType); - jsonManagementContainer.put(JsonCpmKey.ManagementContainer.REFERENCE_POSITION.key(), referencePosition.getJson()); - jsonManagementContainer.put(JsonCpmKey.ManagementContainer.CONFIDENCE.key(), confidence.getJson()); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM ManagementContainer JSON build error", "Error: " + e); - } - } - - public int getStationType() { - return stationType; - } - - public Position getReferencePosition() { - return referencePosition; - } - - public PositionConfidence getConfidence() { - return confidence; - } - - public JSONObject getJson() { - return jsonManagementContainer; - } - - public static ManagementContainer jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - try { - int stationType = json.getInt(JsonCpmKey.ManagementContainer.STATION_TYPE.key()); - JSONObject jsonReferencePosition = json.getJSONObject(JsonCpmKey.ManagementContainer.REFERENCE_POSITION.key()); - Position referencePosition = Position.jsonParser(jsonReferencePosition); - JSONObject jsonConfidence = json.getJSONObject(JsonCpmKey.ManagementContainer.CONFIDENCE.key()); - PositionConfidence confidence = PositionConfidence.jsonParser(jsonConfidence); - - return new ManagementContainer(stationType, referencePosition, confidence); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM ManagementContainer JSON parsing error", "Error: " + e); - } - return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectClassOther.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectClassOther.java deleted file mode 100644 index ebaf1401a..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectClassOther.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class ObjectClassOther { - - private static final Logger LOGGER = Logger.getLogger(ObjectClassOther.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Detected object for class other. unknown(0), roadSideUnit(1). - */ - private final int subclass; - - public ObjectClassOther(final int subclass) throws IllegalArgumentException { - if(CPM.isStrictMode() && (subclass > 255 || subclass < 0)) { - throw new IllegalArgumentException("CPM Other Object subclass should be in the range of [0 - 255]." - + " Value: " + subclass); - } - this.subclass = subclass; - - createJson(); - } - - private void createJson() { - try { - json.put(JsonCpmKey.ObjectClass.OTHER.key(), subclass); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM ObjectClassOther JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public int getSubclass() { - return subclass; - } - - public static ObjectClassOther jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - int subclass = json.optInt(JsonCpmKey.ObjectClass.OTHER.key(), UNKNOWN); - if(subclass != UNKNOWN) return new ObjectClassOther(subclass); - else return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectClassSingleVru.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectClassSingleVru.java deleted file mode 100644 index ec6fb2ccf..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectClassSingleVru.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class ObjectClassSingleVru { - - private static final Logger LOGGER = Logger.getLogger(ObjectClassSingleVru.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * If the single VRU is a pedestrian. - */ - private final ObjectVruPedestrian objectVruPedestrian; - - /** - * If the single VRU is a bicyclist. - */ - private final ObjectVruBicyclist objectVruBicyclist; - - /** - * If the single VRU is a motorcyclist. - */ - private final ObjectVruMotorcyclist objectVruMotorcyclist; - - /** - * If the single VRU is an animal. - */ - private final ObjectVruAnimal objectVruAnimal; - - public ObjectClassSingleVru(final ObjectVruPedestrian objectVruPedestrian) - throws IllegalArgumentException { - if(objectVruPedestrian == null && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM objectClass VRU is missing"); - } - this.objectVruPedestrian = objectVruPedestrian; - this.objectVruBicyclist = null; - this.objectVruMotorcyclist = null; - this.objectVruAnimal = null; - - createJson(); - } - - public ObjectClassSingleVru(final ObjectVruBicyclist objectVruBicyclist) { - if(objectVruBicyclist == null && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM objectClass VRU is missing"); - } - this.objectVruPedestrian = null; - this.objectVruBicyclist = objectVruBicyclist; - this.objectVruMotorcyclist = null; - this.objectVruAnimal = null; - - createJson(); - } - - public ObjectClassSingleVru(final ObjectVruMotorcyclist objectVruMotorcyclist) { - if(objectVruMotorcyclist == null && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM objectClass VRU is missing"); - } - this.objectVruPedestrian = null; - this.objectVruBicyclist = null; - this.objectVruMotorcyclist = objectVruMotorcyclist; - this.objectVruAnimal = null; - - createJson(); - } - - public ObjectClassSingleVru(final ObjectVruAnimal objectVruAnimal) { - if(objectVruAnimal == null && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM objectClass VRU is missing"); - } - this.objectVruPedestrian = null; - this.objectVruBicyclist = null; - this.objectVruMotorcyclist = null; - this.objectVruAnimal = objectVruAnimal; - - createJson(); - } - - private void createJson() { - try { - if(objectVruPedestrian != null) - json.put(JsonCpmKey.ObjectClass.SINGLE_VRU.key(), objectVruPedestrian.getJson()); - if(objectVruBicyclist != null) - json.put(JsonCpmKey.ObjectClass.SINGLE_VRU.key(), objectVruBicyclist.getJson()); - if(objectVruMotorcyclist != null) - json.put(JsonCpmKey.ObjectClass.SINGLE_VRU.key(), objectVruMotorcyclist.getJson()); - if(objectVruAnimal != null) - json.put(JsonCpmKey.ObjectClass.SINGLE_VRU.key(), objectVruAnimal.getJson()); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM ObjectClassSingleVru JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public Object getVruObject() { - if(objectVruPedestrian != null) return objectVruPedestrian; - if(objectVruBicyclist != null) return objectVruBicyclist; - if(objectVruMotorcyclist != null) return objectVruMotorcyclist; - return objectVruAnimal; - } - - public static ObjectClassSingleVru jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - JSONObject jsonSingleVru = json.optJSONObject(JsonCpmKey.ObjectClass.SINGLE_VRU.key()); - if(jsonSingleVru != null) { - ObjectVruPedestrian objectVruPedestrian = ObjectVruPedestrian.jsonParser(jsonSingleVru); - ObjectVruBicyclist objectVruBicyclist = ObjectVruBicyclist.jsonParser(jsonSingleVru); - ObjectVruMotorcyclist objectVruMotorcyclist = ObjectVruMotorcyclist.jsonParser(jsonSingleVru); - ObjectVruAnimal objectVruAnimal = ObjectVruAnimal.jsonParser(jsonSingleVru); - - if(objectVruPedestrian != null) return new ObjectClassSingleVru(objectVruPedestrian); - if(objectVruBicyclist != null) return new ObjectClassSingleVru(objectVruBicyclist); - if(objectVruMotorcyclist != null) return new ObjectClassSingleVru(objectVruMotorcyclist); - if(objectVruAnimal != null) return new ObjectClassSingleVru(objectVruAnimal); - } - return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectClassVehicle.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectClassVehicle.java deleted file mode 100644 index c7cc5d844..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectClassVehicle.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class ObjectClassVehicle { - - private static final Logger LOGGER = Logger.getLogger(ObjectClassVehicle.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Describes the subclass of a detected object for class vehicle. - * - * unknown(0) the type of vehicle is unknown, - * passengerCar(1) the detected object is a small passenger car, - * bus(2) the detected object is a large passenger vehicle, - * lightTruck(3) the detected object is a light goods vehicle, - * heavyTruck(4) the detected object is a heavy goods vehicle, - * trailer(5) the detected object is an unpowered vehicle that is intended to be towed by - * a powered vehicle, - * specialVehicles(6) the detected object is a vehicle which has a special purpose other than - * the above (e.g. moving road works vehicle), - * tram(7) the detected object is a vehicle running on tracks along public streets, - * emergencyVehicle(8) the detected object is a vehicle used in an emergency situation, - * such as an ambulance, police car or fire engine, - * agricultural(9) the detected object is a vehicle used for agricultural purposes. - */ - private final int subclass; - - public ObjectClassVehicle(final int subclass) throws IllegalArgumentException { - if(CPM.isStrictMode() && (subclass > 255 || subclass < 0)) { - throw new IllegalArgumentException("CPM Vehicle Object subclass should be in the range of [0 - 255]." - + " Value: " + subclass); - } - this.subclass = subclass; - - createJson(); - } - - private void createJson() { - try { - json.put(JsonCpmKey.ObjectClass.VEHICLE.key(), subclass); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM ObjectClassVehicle JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public int getSubclass() { - return subclass; - } - - public static ObjectClassVehicle jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - int subclass = json.optInt(JsonCpmKey.ObjectClass.VEHICLE.key(), UNKNOWN); - if(subclass != UNKNOWN) return new ObjectClassVehicle(subclass); - else return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectVruAnimal.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectVruAnimal.java deleted file mode 100644 index a2dd54f4d..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectVruAnimal.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class ObjectVruAnimal { - - private static final Logger LOGGER = Logger.getLogger(ObjectVruAnimal.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Describes the subclass of a detected object for single VRU class animal. - * - * unavailable(0), wild-animal(1), farm-animal(2), service-animal(3), max(15). - */ - private final int subclass; - - public ObjectVruAnimal(final int subclass) throws IllegalArgumentException { - if(CPM.isStrictMode() && (subclass > 15 || subclass < 0)) { - throw new IllegalArgumentException("CPM Animal Object subclass should be in the range of [0 - 15]." - + " Value: " + subclass); - } - this.subclass = subclass; - - createJson(); - } - - private void createJson() { - try { - json.put(JsonCpmKey.ObjectClass.ANIMAL.key(), subclass); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM ObjectVruAnimal JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public int getSubclass() { - return subclass; - } - - public static ObjectVruAnimal jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - int subclass = json.optInt(JsonCpmKey.ObjectClass.ANIMAL.key(), UNKNOWN); - if(subclass != UNKNOWN) return new ObjectVruAnimal(subclass); - else return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectVruBicyclist.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectVruBicyclist.java deleted file mode 100644 index 92dd7a7eb..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectVruBicyclist.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class ObjectVruBicyclist { - - private static final Logger LOGGER = Logger.getLogger(ObjectVruBicyclist.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Describes the subclass of a detected object for single VRU class bicyclist. - * - * unavailable(0), bicyclist(1), wheelchair-user(2), horse-and-rider(3), rollerskater(4), - * e-scooter(5), personal-transporter(6), pedelec(7), speed-pedelec(8), max(15). - */ - private final int subclass; - - public ObjectVruBicyclist(final int subclass) throws IllegalArgumentException { - if(CPM.isStrictMode() && (subclass > 15 || subclass < 0)) { - throw new IllegalArgumentException("CPM Bicyclist Object subclass should be in the range of [0 - 15]." - + " Value: " + subclass); - } - this.subclass = subclass; - - createJson(); - } - - private void createJson() { - try { - json.put(JsonCpmKey.ObjectClass.BICYCLIST.key(), subclass); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM ObjectVruBicyclist JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public int getSubclass() { - return subclass; - } - - public static ObjectVruBicyclist jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - int subclass = json.optInt(JsonCpmKey.ObjectClass.BICYCLIST.key(), UNKNOWN); - if(subclass != UNKNOWN) return new ObjectVruBicyclist(subclass); - else return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectVruMotorcyclist.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectVruMotorcyclist.java deleted file mode 100644 index 9d55c0380..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectVruMotorcyclist.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class ObjectVruMotorcyclist { - - private static final Logger LOGGER = Logger.getLogger(ObjectVruMotorcyclist.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Describes the subclass of a detected object for single VRU class motorcyclist. - * - * unavailable(0), moped(1), motorcycle(2), motorcycle-and-sidecar-right(3), - * motorcycle-and-sidecar-left(4), max(15). - */ - private final int subclass; - - public ObjectVruMotorcyclist(final int subclass) throws IllegalArgumentException { - if(CPM.isStrictMode() && (subclass > 15 || subclass < 0)) { - throw new IllegalArgumentException("CPM Motorcyclist Object subclass should be in the range of [0 - 15]." - + " Value: " + subclass); - } - this.subclass = subclass; - - createJson(); - } - - private void createJson() { - try { - json.put(JsonCpmKey.ObjectClass.MOTORCYLCIST.key(), subclass); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM ObjectVruMotorcyclist JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public int getSubclass() { - return subclass; - } - - public static ObjectVruMotorcyclist jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - int subclass = json.optInt(JsonCpmKey.ObjectClass.MOTORCYLCIST.key(), UNKNOWN); - if(subclass != UNKNOWN) return new ObjectVruMotorcyclist(subclass); - else return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectVruPedestrian.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectVruPedestrian.java deleted file mode 100644 index 3f34abe72..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/ObjectVruPedestrian.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class ObjectVruPedestrian { - - private static final Logger LOGGER = Logger.getLogger(ObjectVruPedestrian.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Describes the subclass of a detected object for single VRU class pedestrian. - * - * unavailable(0), ordinary-pedestrian(1), road-worker(2), first-responder(3), max(15). - */ - private final int subclass; - - public ObjectVruPedestrian(final int subclass) throws IllegalArgumentException { - if(CPM.isStrictMode() && (subclass > 15 || subclass < 0)) { - throw new IllegalArgumentException("CPM Pedestrian Object subclass should be in the range of [0 - 15]." - + " Value: " + subclass); - } - this.subclass = subclass; - - createJson(); - } - - private void createJson() { - try { - json.put(JsonCpmKey.ObjectClass.PEDESTRIAN.key(), subclass); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM ObjectVruPedestrian JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public int getSubclass() { - return subclass; - } - - public static ObjectVruPedestrian jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - int subclass = json.optInt(JsonCpmKey.ObjectClass.PEDESTRIAN.key(), UNKNOWN); - if(subclass != UNKNOWN) return new ObjectVruPedestrian(subclass); - else return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/Offset.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/Offset.java deleted file mode 100644 index d185479fb..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/Offset.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class Offset { - - private static final Logger LOGGER = Logger.getLogger(Offset.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Unit: 0.01 m. - * - * Offset on the x-axis. - */ - private final int x; - - /** - * Unit: 0.01 m. - * - * Offset on the y-axis. - */ - private final int y; - - /** - * Unit: 0.01 m. - * - * Offset on the z-axis. - */ - private final int z; - - public Offset( - final int x, - final int y - ) throws IllegalArgumentException { - this(x, y, UNKNOWN); - } - - public Offset( - final int x, - final int y, - final int z - ) throws IllegalArgumentException { - if(x == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM Offset X is missing"); - } else if(CPM.isStrictMode() && (x > 32767 || x < -32768)) { - throw new IllegalArgumentException("CPM Offset X should be in the range of [-32768 - 32767]." - + " Value: " + x); - } - this.x = x; - if(y == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM Offset Y is missing"); - } else if(CPM.isStrictMode() && (y > 32767 || y < -32768)) { - throw new IllegalArgumentException("CPM Offset Y should be in the range of [-32768 - 32767]." - + " Value: " + y); - } - this.y = y; - if(z != UNKNOWN && CPM.isStrictMode() - && (z > 32767 || z < -32768)) { - throw new IllegalArgumentException("CPM Offset Z should be in the range of [-32768 - 32767]." - + " Value: " + z); - } - this.z = z; - - createJson(); - } - - private void createJson() { - try { - json.put(JsonCpmKey.Offset.X.key(), x); - json.put(JsonCpmKey.Offset.Y.key(), y); - if(z != UNKNOWN) - json.put(JsonCpmKey.Offset.Z.key(), z); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM Offset JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public int getX() { - return x; - } - - public int getY() { - return y; - } - - public int getZ() { - return z; - } - - public static Offset jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - try { - int x = json.getInt(JsonCpmKey.Offset.X.key()); - int y = json.getInt(JsonCpmKey.Offset.Y.key()); - int z = json.optInt(JsonCpmKey.Offset.Z.key(), UNKNOWN); - - return new Offset(x, y, z); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM Offset JSON parsing error", "Error: " + e); - } - return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/OriginatingRsuContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/OriginatingRsuContainer.java deleted file mode 100644 index 57077060d..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/OriginatingRsuContainer.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class OriginatingRsuContainer { - - private static final Logger LOGGER = Logger.getLogger(OriginatingRsuContainer.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Road regulator id. When is present the intersection or road segment reference id is - * guaranteed to be globally unique. - */ - private final int region; - - /** - * Intersection id. Unique within that region. - */ - private final int intersectionReferenceId; - - /** - * Road segment id. Unique within that region. - */ - private final int roadSegmentReferenceId; - - public OriginatingRsuContainer( - final int region, - final int intersectionReferenceId, - final int roadSegmentReferenceId - ) throws IllegalArgumentException { - if(region != UNKNOWN && CPM.isStrictMode() - && (region > 65535 || region < 0)) { - throw new IllegalArgumentException("CPM OriginatingRsuContainer region should be in the range of [0 - 65535]." - + " Value: " + region); - } - this.region = region; - if(intersectionReferenceId != UNKNOWN && CPM.isStrictMode() - && (intersectionReferenceId > 65535 || intersectionReferenceId < 0)) { - throw new IllegalArgumentException("CPM OriginatingRsuContainer intersectionReferenceId should be in the range of [0 - 65535]." - + " Value: " + intersectionReferenceId); - } - this.intersectionReferenceId = intersectionReferenceId; - if(roadSegmentReferenceId != UNKNOWN && CPM.isStrictMode() - && (roadSegmentReferenceId > 65535 || roadSegmentReferenceId < 0)) { - throw new IllegalArgumentException("CPM OriginatingRsuContainer roadSegmentReferenceId should be in the range of [0 - 65535]." - + " Value: " + roadSegmentReferenceId); - } - this.roadSegmentReferenceId = roadSegmentReferenceId; - - createJson(); - } - - private void createJson() { - try { - if(region != UNKNOWN) - json.put(JsonCpmKey.OriginatingRsuContainer.REGION.key(), region); - if(intersectionReferenceId != UNKNOWN) - json.put(JsonCpmKey.OriginatingRsuContainer.INTERSECTION_REFERENCE_ID.key(), intersectionReferenceId); - if(roadSegmentReferenceId != UNKNOWN) - json.put(JsonCpmKey.OriginatingRsuContainer.ROAD_SEGMENT_REFERENCE_ID.key(), roadSegmentReferenceId); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM OriginatingRsuContainer JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public int getRegion() { - return region; - } - - public int getIntersectionReferenceId() { - return intersectionReferenceId; - } - - public int getRoadSegmentReferenceId() { - return roadSegmentReferenceId; - } - - public static OriginatingRsuContainer jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - int region = json.optInt(JsonCpmKey.OriginatingRsuContainer.REGION.key(), UNKNOWN); - int intersectionReferenceId = json.optInt(JsonCpmKey.OriginatingRsuContainer.INTERSECTION_REFERENCE_ID.key(), UNKNOWN); - int roadSegmentReferenceId = json.optInt(JsonCpmKey.OriginatingRsuContainer.ROAD_SEGMENT_REFERENCE_ID.key(), UNKNOWN); - - return new OriginatingRsuContainer(region, intersectionReferenceId, roadSegmentReferenceId); - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/OriginatingVehicleConfidence.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/OriginatingVehicleConfidence.java deleted file mode 100644 index 9fcf96bfe..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/OriginatingVehicleConfidence.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class OriginatingVehicleConfidence { - - private static final Logger LOGGER = Logger.getLogger(OriginatingVehicleConfidence.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Unit: 0.1 degree. Heading accuracy of the vehicle movement with regards to the true north. - * - * equalOrWithinZeroPointOneDegree (1), equalOrWithinOneDegree (10), outOfRange(126), - * unavailable(127). - */ - private final int heading; - - /** - * Unit: 0.01 m/s. Speed accuracy. - * - * equalOrWithinOneCentimeterPerSec(1), equalOrWithinOneMeterPerSec(100), outOfRange(126), unavailable(127). - */ - private final int speed; - - /** - * noTrailerPresent(0), trailerPresentWithKnownLength(1), trailerPresentWithUnknownLength(2), - * trailerPresenceIsUnknown(3), unavailable(4). - */ - private final int vehicleLength; - - /** - * not specified... can be removed - */ - private final int vehicleWidth; - - /** - * degSec-000-01 (0), degSec-000-05 (1), degSec-000-10 (2), degSec-001-00 (3), - * degSec-005-00 (4), degSec-010-00 (5), degSec-100-00 (6), outOfRange (7), unavailable (8). - */ - private final int yawRate; - - /** - * Unit: 0.1 m/s2. Longitudinal acceleration accuracy. - * - * pointOneMeterPerSecSquared(1), outOfRange(101), unavailable(102). - */ - private final int longitudinalAcceleration; - - /** - * Unit: 0.1 m/s2. Lateral acceleration accuracy. - * - * pointOneMeterPerSecSquared(1), outOfRange(101), unavailable(102). - */ - private final int lateralAcceleration; - - /** - * Unit: 0.1 m/s2. Vertical acceleration accuracy. - * - * pointOneMeterPerSecSquared(1), outOfRange(101), unavailable(102). - */ - private final int verticalAcceleration; - - public OriginatingVehicleConfidence( - final int heading, - final int speed, - final int vehicleLength, - final int vehicleWidth, - final int yawRate, - final int longitudinalAcceleration, - final int lateralAcceleration, - final int verticalAcceleration - ) throws IllegalArgumentException { - if(heading != UNKNOWN && CPM.isStrictMode() && (heading > 127 || heading < 1)) { - throw new IllegalArgumentException("CPM OriginatingVehicleConfidence heading should be in the range of [1 - 127]." - + " Value: " + heading); - } - this.heading = heading; - if(speed != UNKNOWN && CPM.isStrictMode() && (speed > 127 || speed < 1)) { - throw new IllegalArgumentException("CPM OriginatingVehicleConfidence speed should be in the range of [1 - 127]." - + " Value: " + speed); - } - this.speed = speed; - if(vehicleLength != UNKNOWN && CPM.isStrictMode() && (vehicleLength > 4 || vehicleLength < 0)) { - throw new IllegalArgumentException("CPM OriginatingVehicleConfidence vehicleLength should be in the range of [0 - 4]." - + " Value: " + vehicleLength); - } - this.vehicleLength = vehicleLength; - if(vehicleWidth != UNKNOWN && CPM.isStrictMode() && (vehicleWidth > 4 || vehicleWidth < 0)) { - throw new IllegalArgumentException("CPM OriginatingVehicleConfidence vehicleWidth should be in the range of [0 - 4]." - + " Value: " + vehicleWidth); - } - this.vehicleWidth = vehicleWidth; - if(yawRate != UNKNOWN && CPM.isStrictMode() && (yawRate > 8 || yawRate < 0)) { - throw new IllegalArgumentException("CPM OriginatingVehicleConfidence yawRate should be in the range of [0 - 8]." - + " Value: " + yawRate); - } - this.yawRate = yawRate; - if(longitudinalAcceleration != UNKNOWN && CPM.isStrictMode() && (longitudinalAcceleration > 102 || longitudinalAcceleration < 0)) { - throw new IllegalArgumentException("CPM OriginatingVehicleConfidence longitudinalAcceleration should be in the range of [0 - 102]." - + " Value: " + longitudinalAcceleration); - } - this.longitudinalAcceleration = longitudinalAcceleration; - if(lateralAcceleration != UNKNOWN && CPM.isStrictMode() && (lateralAcceleration > 102 || lateralAcceleration < 0)) { - throw new IllegalArgumentException("CPM OriginatingVehicleConfidence lateralAcceleration should be in the range of [0 - 102]." - + " Value: " + lateralAcceleration); - } - this.lateralAcceleration = lateralAcceleration; - if(verticalAcceleration != UNKNOWN && CPM.isStrictMode() && (verticalAcceleration > 102 || verticalAcceleration < 0)) { - throw new IllegalArgumentException("CPM OriginatingVehicleConfidence verticalAcceleration should be in the range of [0 - 102]." - + " Value: " + verticalAcceleration); - } - this.verticalAcceleration = verticalAcceleration; - - createJson(); - } - - private void createJson() { - try { - if(heading != UNKNOWN) - json.put(JsonCpmKey.OriginatingVehicleContainer.HEADING.key(), heading); - if(speed != UNKNOWN) - json.put(JsonCpmKey.OriginatingVehicleContainer.SPEED.key(), speed); - if(vehicleLength != UNKNOWN) - json.put(JsonCpmKey.OriginatingVehicleContainer.VEHICLE_LENGTH.key(), vehicleLength); - if(vehicleWidth != UNKNOWN) - json.put(JsonCpmKey.OriginatingVehicleContainer.VEHICLE_WIDTH.key(), vehicleWidth); - if(yawRate != UNKNOWN) - json.put(JsonCpmKey.OriginatingVehicleContainer.YAW_RATE.key(), yawRate); - if(longitudinalAcceleration != UNKNOWN) - json.put(JsonCpmKey.OriginatingVehicleContainer.LONGITUDINAL_ACCELERATION.key(), longitudinalAcceleration); - if(lateralAcceleration != UNKNOWN) - json.put(JsonCpmKey.OriginatingVehicleContainer.LATERAL_ACCELERATION.key(), lateralAcceleration); - if(verticalAcceleration != UNKNOWN) - json.put(JsonCpmKey.OriginatingVehicleContainer.VERTICAL_ACCELERATION.key(), verticalAcceleration); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM OriginatingVehicleConfidence JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public int getHeading() { - return heading; - } - - public int getSpeed() { - return speed; - } - - public int getVehicleWidth() { - return vehicleWidth; - } - - public int getYawRate() { - return yawRate; - } - - public int getLongitudinalAcceleration() { - return longitudinalAcceleration; - } - - public int getLateralAcceleration() { - return lateralAcceleration; - } - - public int getVerticalAcceleration() { - return verticalAcceleration; - } - - public static OriginatingVehicleConfidence jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - int heading = json.optInt(JsonCpmKey.OriginatingVehicleContainer.HEADING.key(), UNKNOWN); - int speed = json.optInt(JsonCpmKey.OriginatingVehicleContainer.SPEED.key(), UNKNOWN); - int vehicleLength = json.optInt(JsonCpmKey.OriginatingVehicleContainer.VEHICLE_LENGTH.key(), UNKNOWN); - int vehicleWidth = json.optInt(JsonCpmKey.OriginatingVehicleContainer.VEHICLE_WIDTH.key(), UNKNOWN); - int yawRate = json.optInt(JsonCpmKey.OriginatingVehicleContainer.YAW_RATE.key(), UNKNOWN); - int longitudinalAcceleration = json.optInt(JsonCpmKey.OriginatingVehicleContainer.LONGITUDINAL_ACCELERATION.key(), UNKNOWN); - int lateralAcceleration = json.optInt(JsonCpmKey.OriginatingVehicleContainer.LATERAL_ACCELERATION.key(), UNKNOWN); - int verticalAcceleration = json.optInt(JsonCpmKey.OriginatingVehicleContainer.VERTICAL_ACCELERATION.key(), UNKNOWN); - - return new OriginatingVehicleConfidence( - heading, - speed, - vehicleLength, - vehicleWidth, - yawRate, - longitudinalAcceleration, - lateralAcceleration, - verticalAcceleration); - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/OriginatingVehicleContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/OriginatingVehicleContainer.java deleted file mode 100644 index ff5a0e0a6..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/OriginatingVehicleContainer.java +++ /dev/null @@ -1,261 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class OriginatingVehicleContainer { - - private static final Logger LOGGER = Logger.getLogger(OriginatingVehicleContainer.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Unit: 0.1 degree. Heading of the vehicle movement with regards to the true north; - * - * wgs84North(0), wgs84East(900), wgs84South(1800), wgs84West(2700), unavailable(3601). - */ - private final int heading; - - /** - * Unit: 0.01 m/s. Driving speed. - * - * standstill(0), oneCentimeterPerSec(1), unavailable(16383). - */ - private final int speed; - - /** - * Drive direction. - * - * forward (0), backward (1), unavailable (2). - */ - private final int driveDirection; - - /** - * Unit: 10 cm. Vehicle length. - * - * tenCentimeters(1), outOfRange(1022), unavailable(1023). - */ - private final int vehicleLength; - - /** - * Unit: 10 cm. Vehicle width. - * - * tenCentimeters(1), outOfRange(61), unavailable(62). - */ - private final int vehicleWidth; - - /** - * Unit: 0.01 degree/s. Yaw rate - * - * straight(0), degSec-000-01ToRight(-1), degSec-000-01ToLeft(1), unavailable(32767). - */ - private final int yawRate; - - /** - * Unit: 0.1 m/s2. Longitudinal acceleration. - * - * pointOneMeterPerSecSquaredForward(1), pointOneMeterPerSecSquaredBackward(-1), unavailable(161). - */ - private final int longitudinalAcceleration; - - /** - * Unit: 0.1 m/s2. Lateral acceleration. - * - * pointOneMeterPerSecSquaredToRight(-1), pointOneMeterPerSecSquaredToLeft(1), unavailable(161). - */ - private final int lateralAcceleration; - - /** - * Unit: 0.1 m/s2. Vertical acceleration. - * - * pointOneMeterPerSecSquaredUp(1), pointOneMeterPerSecSquaredDown(-1), unavailable(161). - */ - private final int verticalAcceleration; - - /** - * Confidence for all the above fields. - */ - private final OriginatingVehicleConfidence confidence; - - public OriginatingVehicleContainer( - final int heading, - final int speed, - final int driveDirection, - final int vehicleLength, - final int vehicleWidth, - final int yawRate, - final int longitudinalAcceleration, - final int lateralAcceleration, - final int verticalAcceleration, - final OriginatingVehicleConfidence confidence - ) throws IllegalArgumentException { - if(heading == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM OriginatingVehicleContainer heading missing"); - } else if(CPM.isStrictMode() && (heading > 3601 || heading < 0)) { - throw new IllegalArgumentException("CPM OriginatingVehicleContainer heading should be in the range of [0 - 3601]." - + " Value: " + heading); - } - this.heading = heading; - if(speed == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM OriginatingVehicleContainer speed missing"); - } else if(CPM.isStrictMode() && (speed > 16383 || speed < 0)) { - throw new IllegalArgumentException("CPM OriginatingVehicleContainer speed should be in the range of [0 - 16383]." - + " Value: " + speed); - } - this.speed = speed; - if(driveDirection != UNKNOWN && CPM.isStrictMode() && (driveDirection > 2 || driveDirection < 0)) { - throw new IllegalArgumentException("CPM OriginatingVehicleContainer driveDirection should be in the range of [0 - 2]." - + " Value: " + driveDirection); - } - this.driveDirection = driveDirection; - if(vehicleLength != UNKNOWN && CPM.isStrictMode() && (vehicleLength > 1023 || vehicleLength < 1)) { - throw new IllegalArgumentException("CPM OriginatingVehicleContainer vehicleLength should be in the range of [1 - 1023]." - + " Value: " + vehicleLength); - } - this.vehicleLength = vehicleLength; - if(vehicleWidth != UNKNOWN && CPM.isStrictMode() && (vehicleWidth > 62 || vehicleWidth < 1)) { - throw new IllegalArgumentException("CPM OriginatingVehicleContainer vehicleWidth should be in the range of [1 - 62]." - + " Value: " + vehicleWidth); - } - this.vehicleWidth = vehicleWidth; - if(yawRate != UNKNOWN && CPM.isStrictMode() && (yawRate > 32767 || yawRate < -32766)) { - throw new IllegalArgumentException("CPM OriginatingVehicleContainer yawRate should be in the range of [-32766 - 32767]." - + " Value: " + yawRate); - } - this.yawRate = yawRate; - if(longitudinalAcceleration != UNKNOWN && CPM.isStrictMode() && (longitudinalAcceleration > 161 || longitudinalAcceleration < -160)) { - throw new IllegalArgumentException("CPM OriginatingVehicleContainer longitudinalAcceleration should be in the range of [-160 - 161]." - + " Value: " + longitudinalAcceleration); - } - this.longitudinalAcceleration = longitudinalAcceleration; - if(lateralAcceleration != UNKNOWN && CPM.isStrictMode() && (lateralAcceleration > 161 || lateralAcceleration < -160)) { - throw new IllegalArgumentException("CPM OriginatingVehicleContainer lateralAcceleration should be in the range of [-160 - 161]." - + " Value: " + lateralAcceleration); - } - this.lateralAcceleration = lateralAcceleration; - if(verticalAcceleration != UNKNOWN && CPM.isStrictMode() && (verticalAcceleration > 161 || verticalAcceleration < -160)) { - throw new IllegalArgumentException("CPM OriginatingVehicleContainer verticalAcceleration should be in the range of [-160 - 161]." - + " Value: " + verticalAcceleration); - } - this.verticalAcceleration = verticalAcceleration; - if(confidence == null && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM OriginatingVehicleContainer confidence missing"); - } - this.confidence = confidence; - - createJson(); - } - - private void createJson() { - try { - if(heading != UNKNOWN) - json.put(JsonCpmKey.OriginatingVehicleContainer.HEADING.key(), heading); - if(speed != UNKNOWN) - json.put(JsonCpmKey.OriginatingVehicleContainer.SPEED.key(), speed); - if(driveDirection != UNKNOWN) - json.put(JsonCpmKey.OriginatingVehicleContainer.DRIVE_DIRECTION.key(), driveDirection); - if(vehicleLength != UNKNOWN) - json.put(JsonCpmKey.OriginatingVehicleContainer.VEHICLE_LENGTH.key(), vehicleLength); - if(vehicleWidth != UNKNOWN) - json.put(JsonCpmKey.OriginatingVehicleContainer.VEHICLE_WIDTH.key(), vehicleWidth); - if(yawRate != UNKNOWN) - json.put(JsonCpmKey.OriginatingVehicleContainer.YAW_RATE.key(), yawRate); - if(longitudinalAcceleration != UNKNOWN) - json.put(JsonCpmKey.OriginatingVehicleContainer.LONGITUDINAL_ACCELERATION.key(), longitudinalAcceleration); - if(lateralAcceleration != UNKNOWN) - json.put(JsonCpmKey.OriginatingVehicleContainer.LATERAL_ACCELERATION.key(), lateralAcceleration); - if(verticalAcceleration != UNKNOWN) - json.put(JsonCpmKey.OriginatingVehicleContainer.VERTICAL_ACCELERATION.key(), verticalAcceleration); - if(confidence != null) - json.put(JsonCpmKey.OriginatingVehicleContainer.CONFIDENCE.key(), confidence.getJson()); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM OriginatingVehicleContainer JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public int getHeading() { - return heading; - } - - public int getSpeed() { - return speed; - } - - public int getDriveDirection() { - return driveDirection; - } - - public int getVehicleLength() { - return vehicleLength; - } - - public int getVehicleWidth() { - return vehicleWidth; - } - - public int getYawRate() { - return yawRate; - } - - public int getLongitudinalAcceleration() { - return longitudinalAcceleration; - } - - public int getLateralAcceleration() { - return lateralAcceleration; - } - - public int getVerticalAcceleration() { - return verticalAcceleration; - } - - public static OriginatingVehicleContainer jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - try { - int heading = json.getInt(JsonCpmKey.OriginatingVehicleContainer.HEADING.key()); - int speed = json.getInt(JsonCpmKey.OriginatingVehicleContainer.SPEED.key()); - int driveDirection = json.optInt(JsonCpmKey.OriginatingVehicleContainer.DRIVE_DIRECTION.key(), UNKNOWN); - int vehicleLength = json.optInt(JsonCpmKey.OriginatingVehicleContainer.VEHICLE_LENGTH.key(), UNKNOWN); - int vehicleWidth = json.optInt(JsonCpmKey.OriginatingVehicleContainer.VEHICLE_WIDTH.key(), UNKNOWN); - int yawRate = json.optInt(JsonCpmKey.OriginatingVehicleContainer.YAW_RATE.key(), UNKNOWN); - int longitudinalAcceleration = json.optInt(JsonCpmKey.OriginatingVehicleContainer.LONGITUDINAL_ACCELERATION.key(), UNKNOWN); - int lateralAcceleration = json.optInt(JsonCpmKey.OriginatingVehicleContainer.LATERAL_ACCELERATION.key(), UNKNOWN); - int verticalAcceleration = json.optInt(JsonCpmKey.OriginatingVehicleContainer.VERTICAL_ACCELERATION.key(), UNKNOWN); - JSONObject jsonConfidence = json.getJSONObject(JsonCpmKey.OriginatingVehicleContainer.CONFIDENCE.key()); - OriginatingVehicleConfidence confidence = OriginatingVehicleConfidence.jsonParser(jsonConfidence); - - return new OriginatingVehicleContainer( - heading, - speed, - driveDirection, - vehicleLength, - vehicleWidth, - yawRate, - longitudinalAcceleration, - lateralAcceleration, - verticalAcceleration, - confidence); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM OriginatingVehicleContainer JSON build error", "Error: " + e); - } - return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/PerceivedObject.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/PerceivedObject.java deleted file mode 100644 index 661f26a10..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/PerceivedObject.java +++ /dev/null @@ -1,1248 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class PerceivedObject { - - private static final Logger LOGGER = Logger.getLogger(PerceivedObject.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Identifier assigned to a detected object which remains constant as long as the object is - * perceived. - *

- * Numbers are assigned in an increasing round-robin fashion. When the last identifier in the - * allowed range has been used, the first counter for the identifier starts from the beginning - * of the range again. - *

- * minimumValue(0), maximumValue(255). - */ - private final int objectId; - - /** - * Unit: 1 millisecond. Time difference from the message’s generation delta time to the time of - * the measurement of the object. - */ - private final int timeOfMeasurement; - - /** - * Unit: 0.01 meter. Distance to detected object from the reference point in x-direction for - * the time of measurement. - *

- * For a vehicle, the distance is reported in a body-fixed coordinate system as provided by - * ISO 8855. - *

- * For a RSU, the distance is reported in a coordinate system in which the y-axis corresponds - * to the North direction, the x-axis to the East direction, and the z-axis to the vertical - * direction. - *

- * zeroPointZeroOneMeter(1), oneMeter(100). - */ - private final int xDistance; - - /** - * Unit: 0.01 meter. Distance to detected object from the reference point in y-direction for - * the time of measurement. - *

- * For a vehicle, the distance is reported in a body-fixed coordinate system as provided by - * ISO 8855. - *

- * For a RSU, the distance is reported in a coordinate system in which the y-axis corresponds - * to the North direction, the x-axis to the East direction, and the z-axis to the vertical - * direction. - *

- * zeroPointZeroOneMeter(1), oneMeter(100). - */ - private final int yDistance; - - /** - * Unit: 0.01 meter. Distance to detected object from the reference point in z-direction for - * the time of measurement. - *

- * For a vehicle, the distance is reported in a body-fixed coordinate system as provided by - * ISO 8855. - *

- * For a RSU, the distance is reported in a coordinate system in which the y-axis corresponds - * to the North direction, the x-axis to the East direction, and the z-axis to the vertical - * direction. - *

- * zeroPointZeroOneMeter(1), oneMeter(100). - */ - private final int zDistance; - - /** - * Unit: 0.01 m/s. Speed of the detected object in the detecting reference system in x-direction - * for the time of measurement (i.e. speed of the object relative to the origin of the station’s - * reference system). - *

- * For a vehicle, the speed is reported in a body-fixed coordinate system as provided by - * ISO 8855 originating at the station’s reference point. - *

- * For a RSU, the speed is reported in a coordinate system in which the y-axis corresponds to - * the North direction, the x-axis to the East direction, and the z-axis to the vertical - * direction. - *

- * negativeSpeedMaximum(-16383), standstill(0), oneCentimeterPerSec(1), speedMaximum(16382), - * unavailable(16383). - */ - private final int xSpeed; - - /** - * Unit: 0.01 m/s. Speed of the detected object in the detecting reference system in y-direction - * for the time of measurement (i.e. speed of the object relative to the origin of the station’s - * reference system). - *

- * For a vehicle, the speed is reported in a body-fixed coordinate system as provided by - * ISO 8855 originating at the station’s reference point. - *

- * For a RSU, the speed is reported in a coordinate system in which the y-axis corresponds to - * the North direction, the x-axis to the East direction, and the z-axis to the vertical - * direction. - *

- * negativeSpeedMaximum(-16383), standstill(0), oneCentimeterPerSec(1), speedMaximum(16382), - * unavailable(16383). - */ - private final int ySpeed; - - /** - * Unit: 0.01 m/s. Speed of the detected object in the detecting reference system in z-direction - * for the time of measurement (i.e. speed of the object relative to the origin of the station’s - * reference system). - *

- * For a vehicle, the speed is reported in a body-fixed coordinate system as provided by - * ISO 8855 originating at the station’s reference point. - *

- * For a RSU, the speed is reported in a coordinate system in which the y-axis corresponds to - * the North direction, the x-axis to the East direction, and the z-axis to the vertical - * direction. - *

- * negativeSpeedMaximum(-16383), standstill(0), oneCentimeterPerSec(1), speedMaximum(16382), - * unavailable(16383). - */ - private final int zSpeed; - - /** - * Unit: 0.1 m/s2. Acceleration of the detected object from the reference point in x-direction - * for the time of measurement. - *

- * For a vehicle, the acceleration is reported in a body-fixed coordinate system as provided by - * ISO 8855 originating at the station’s reference point. - *

- * For a RSU, the acceleration is reported in a coordinate system in which the y-axis - * corresponds to the North direction, the x-axis to the East direction, and the z-axis to the - * vertical direct. - *

- * pointOneMeterPerSecSquared(1), minusPointOneMeterPerSecSquared(-1), unavailable(161). - */ - private final int xAcceleration; - - /** - * Unit: 0.1 m/s2. Acceleration of the detected object from the reference point in y-direction - * for the time of measurement. - *

- * For a vehicle, the acceleration is reported in a body-fixed coordinate system as provided by - * ISO 8855 originating at the station’s reference point. - *

- * For a RSU, the acceleration is reported in a coordinate system in which the y-axis - * corresponds to the North direction, the x-axis to the East direction, and the z-axis to the - * vertical direct. - *

- * pointOneMeterPerSecSquared(1), minusPointOneMeterPerSecSquared(-1), unavailable(161). - */ - private final int yAcceleration; - - /** - * Unit: 0.1 m/s2. Acceleration of the detected object from the reference point in z-direction - * for the time of measurement. - *

- * For a vehicle, the acceleration is reported in a body-fixed coordinate system as provided by - * ISO 8855 originating at the station’s reference point. - *

- * For a RSU, the acceleration is reported in a coordinate system in which the y-axis - * corresponds to the North direction, the x-axis to the East direction, and the z-axis to - * the vertical direct. - *

- * pointOneMeterPerSecSquared(1), minusPointOneMeterPerSecSquared(-1), unavailable(161). - */ - private final int zAcceleration; - - /** - * Unit: 0.1 degrees. Roll angle of object from the reference point. - *

- * For a vehicle, the angle is reported in a body-fixed coordinate system as provided by - * ISO 8855 originating at the station’s reference point. - *

- * For a RSU, the angle is reported in a coordinate system in which the y-axis corresponds to - * the North direction, the x-axis to the East direction, and the z- axis to the vertical - * direction. - *

- * The angle is measured with positive values considering the object orientation turning - * counter-clockwise around the x-axis. - *

- * zeroPointOneDegree(1), oneDegree(10), unavailable(3601). - */ - private final int rollAngle; - - /** - * Unit: 0.1 degrees. Pitch angle of object from the reference point. - *

- * For a vehicle, the angle is reported in a body-fixed coordinate system as provided by - * ISO 8855 originating at the station’s reference point. - *

- * For a RSU, the angle is reported in a coordinate system in which the y-axis corresponds to - * the North direction, the x-axis to the East direction, and the z- axis to the vertical - * direction. - *

- * The angle is measured with positive values considering the object orientation turning - * counter-clockwise around the y-axis. - *

- * zeroPointOneDegree(1), oneDegree(10), unavailable(3601). - */ - private final int pitchAngle; - - /** - * Unit: 0.1 degrees. Yaw angle of object from the reference point. - *

- * For a vehicle, the angle is reported in a body-fixed coordinate system as provided by - * ISO 8855 originating at the station’s reference point. - *

- * For a RSU, the angle is reported in a coordinate system in which the y-axis corresponds to - * the North direction, the x-axis to the East direction, and the z- axis to the vertical - * direction. - *

- * The angle is measured with positive values considering the object orientation turning - * counter-clockwise around the z-axis. - *

- * zeroPointOneDegree(1), oneDegree(10), unavailable(3601). - */ - private final int yawAngle; - - /** - * Unit: 0.01 degrees/s. Roll rate of object from the reference point. - *

- * For a vehicle, the angular rate is reported in a body-fixed coordinate system as provided - * by ISO 8855 originating at the station’s reference point. - *

- * For a RSU, the angular rate is reported in a coordinate system in which the y-axis - * corresponds to the North direction, the x-axis to the East direction, and the z- axis to - * the vertical direction. - *

- * The angular rate is measured with positive values considering the object orientation - * turning counter-clockwise around the x-axis. An angular speed value described in a local - * Cartesian coordinate system, counted positive in a right-hand local coordinate system from - * the abscissa. - *

- * noSpeed(0), oneDegreePerSecondAntiClockwise(100), oneDegreePerSecondClockwise(-100). - */ - private final int rollRate; - - /** - * Unit: 0.01 degrees/s. Pitch rate of object from the reference point. - *

- * For a vehicle, the angular rate is reported in a body-fixed coordinate system as provided - * by ISO 8855 originating at the station’s reference point. - *

- * For a RSU, the angular rate is reported in a coordinate system in which the y-axis - * corresponds to the North direction, the x-axis to the East direction, and the z- axis to - * the vertical direction. - *

- * The angular rate is measured with positive values considering the object orientation turning - * counter-clockwise around the x-axis. An angular speed value described in a local Cartesian - * coordinate system, counted positive in a right-hand local coordinate system from the - * abscissa. - *

- * noSpeed(0), oneDegreePerSecondAntiClockwise(100), oneDegreePerSecondClockwise(-100). - */ - private final int pitchRate; - - /** - * Unit: 0.01 degrees/s. Yaw rate of object from the reference point. - *

- * For a vehicle, the angular rate is reported in a body-fixed coordinate system as provided - * by ISO 8855 originating at the station’s reference point. - *

- * For a RSU, the angular rate is reported in a coordinate system in which the y-axis - * corresponds to the North direction, the x-axis to the East direction, and the z- axis to - * the vertical direction. - *

- * The angular rate is measured with positive values considering the object orientation turning - * counter-clockwise around the x-axis. An angular speed value described in a local Cartesian - * coordinate system, counted positive in a right-hand local coordinate system from the - * abscissa. - *

- * noSpeed(0), oneDegreePerSecondAntiClockwise(100), oneDegreePerSecondClockwise(-100). - */ - private final int yawRate; - - /** - * Unit: 0.01 degrees/s^2 (degrees per second squared). - * Roll acceleration of object from the reference point. - *

- * For a vehicle, the angular acceleration is reported in a body-fixed coordinate system as - * provided by ISO 8855 originating at the station’s reference point. - *

- * For a RSU, the angular acceleration is reported in a coordinate system in which the y-axis - * corresponds to the North direction, the x-axis to the East direction, and the z- axis to - * the vertical direction. - *

- * The angular acceleration is measured with positive values considering the object orientation - * turning counter-clockwise around the x-axis. An angular acceleration value described in a - * local Cartesian coordinate system, counted positive in a right-hand local coordinate system - * from the abscissa. - *

- * noAcceleration(0), oneDegreePerSecondSquaredAntiClockwise(100), - * oneDegreePerSecondSquaredClockwise(-100). - */ - private final int rollAcceleration; - - /** - * Unit: 0.01 degrees/s^2 (degrees per second squared). - * Pitch acceleration of object from the reference point. - *

- * For a vehicle, the angular acceleration is reported in a body-fixed coordinate system as - * provided by ISO 8855 originating at the station’s reference point. - *

- * For a RSU, the angular acceleration is reported in a coordinate system in which the y-axis - * corresponds to the North direction, the x-axis to the East direction, and the z- axis to the - * vertical direction. - *

- * The angular acceleration is measured with positive values considering the object orientation - * turning counter-clockwise around the x-axis. An angular acceleration value described in a - * local Cartesian coordinate system, counted positive in a right-hand local coordinate system - * from the abscissa. - *

- * noAcceleration(0), oneDegreePerSecondSquaredAntiClockwise(100), - * oneDegreePerSecondSquaredClockwise(-100). - */ - private final int pitchAcceleration; - - /** - * Unit: 0.01 degrees/s^2 (degrees per second squared). - * Yaw acceleration of object from the reference point. - *

- * For a vehicle, the angular acceleration is reported in a body-fixed coordinate system as - * provided by ISO 8855 originating at the station’s reference point. - *

- * For a RSU, the angular acceleration is reported in a coordinate system in which the y-axis - * corresponds to the North direction, the x-axis to the East direction, and the z- axis to the - * vertical direction. - *

- * The angular acceleration is measured with positive values considering the object orientation - * turning counter-clockwise around the x-axis. An angular acceleration value described in a - * local Cartesian coordinate system, counted positive in a right-hand local coordinate system - * from the abscissa. - *

- * noAcceleration(0), oneDegreePerSecondSquaredAntiClockwise(100), - * oneDegreePerSecondSquaredClockwise(-100). - */ - private final int yawAcceleration; - - //lowerTriangularCorrelationMatrixColumns not implemented - - /** - * Unit: 0.1 m. First dimension of object as provided by the sensor or environment model. - *

- * This dimension is always contained in the plane which is oriented perpendicular to the - * direction of the angle indicated by the yawAngle and which contains the object's reference - * point. A dimension for an object. - *

- * zeroPointOneMeter(1), oneMeter(10).", - */ - private final int planarObjectDimension1; - - /** - * Unit: 0.1 m. Second dimension of the object as provided by the sensor environment model. - *

- * This dimension is contained in the plane oriented in the direction of the angle indicated - * by the yawAngle and the object's reference point. A dimension for an object. - *

- * zeroPointOneMeter(1), oneMeter(10). - */ - private final int planarObjectDimension2; - - /** - * Unit: 0.1 m. Vertical dimension of object as provided by the sensor or object model. - *

- * A dimension for an object. - *

- * zeroPointOneMeter(1), oneMeter(10). - */ - private final int verticalObjectDimension; - - /** - * The reference point on the perceived object. - *

- * The kinematic attitude and state data provided for this object are valid for this reference - * point of the object. In case no object reference point can be determined, it is assumed to - * be the center point of the detected object. - *

- * {mid(0), bottomLeft(1), midLeft(2), topLeft(3), bottomMid(4), topMid(5), bottomRight(6), - * midRight(7), topRight(8). - */ - private final int objectRefPoint; - - /** - * Unit: 1 ms. Provides the age of the detected and described object. - *

- * Age of object in milliseconds, i.e. for how long the object has been observed by the - * disseminating station. - *

- * oneMiliSec(1), moreThan1Point5Second(1500). - */ - private final int objectAge; - - /** - * List of sensor-IDs which provided the measurement data. - */ - private final List sensorIdList; - - /** - * Indicated the dynamic capabilities of a detected object. - *

- * Indication whether the detected object is classified as a dynamic (i.e. moving) object. - * This value indicates whether an object has the general capability to move, i.e. change - * its position. - *

- * dynamic(0) the object is moving, hasBeenDynamic(1) indicates whether an object - * has been dynamic before, e.g., a car stopping at a traffic light, static(2) shall be used - * in case an object is identified to be not moving throughout any previous observation - */ - private final int dynamicStatus; - - /** - * Provides the classification of the described object. - * Multi-dimensional classification may be provided. - */ - private final List classification; - - //matchedPosition not implemented - - /** - * The confidence associated with each field of the perceived object - */ - private final PerceivedObjectConfidence confidence; - - /** - * Object which is perceived by one or several sensors. - */ - private PerceivedObject( - final int objectId, - final int timeOfMeasurement, - final int xDistance, - final int yDistance, - final int zDistance, - final int xSpeed, - final int ySpeed, - final int zSpeed, - final int xAcceleration, - final int yAcceleration, - final int zAcceleration, - final int rollAngle, - final int pitchAngle, - final int yawAngle, - final int rollRate, - final int pitchRate, - final int yawRate, - final int rollAcceleration, - final int pitchAcceleration, - final int yawAcceleration, - final int planarObjectDimension1, - final int planarObjectDimension2, - final int verticalObjectDimension, - final int objectRefPoint, - final int objectAge, - final List sensorIdList, - final int dynamicStatus, - final List classification, - final PerceivedObjectConfidence confidence - ) throws IllegalArgumentException { - if(objectId == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM PerceivedObject objectId is missing"); - } else if(CPM.isStrictMode() && (objectId > 255 || objectId < 0)) { - throw new IllegalArgumentException("CPM PerceivedObject objectId should be in the range of [0 - 255]." - + " Value: " + objectId); - } - this.objectId = objectId; - if(timeOfMeasurement == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM PerceivedObject timeOfMeasurement is missing"); - } /*else if(CPM.isStrictMode() && (timeOfMeasurement > 1500 || timeOfMeasurement < -1500)) { - throw new IllegalArgumentException("CPM PerceivedObject timeOfMeasurement should be in the range of [-1500 - 1500]." - + " Value: " + timeOfMeasurement); - }*/ - this.timeOfMeasurement = timeOfMeasurement; - //********* DISTANCE *********** - if(xDistance == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM PerceivedObject xDistance is missing"); - } else if(CPM.isStrictMode() && (xDistance > 132767 || xDistance < -132768)) { - throw new IllegalArgumentException("CPM PerceivedObject xDistance should be in the range of [-132768 - 132767]." - + " Value: " + xDistance); - } - this.xDistance = xDistance; - if(yDistance == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM PerceivedObject yDistance is missing"); - } else if(CPM.isStrictMode() && (yDistance > 132767 || yDistance < -132768)) { - throw new IllegalArgumentException("CPM PerceivedObject yDistance should be in the range of [-132768 - 132767]." - + " Value: " + yDistance); - } - this.yDistance = yDistance; - if(zDistance != UNKNOWN && CPM.isStrictMode() - && (zDistance > 132767 || zDistance < -132768)) { - throw new IllegalArgumentException("CPM PerceivedObject zDistance should be in the range of [-132768 - 132767]." - + " Value: " + zDistance); - } - this.zDistance = zDistance; - //********* SPEED *********** - if(xSpeed == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM PerceivedObject xSpeed is missing"); - } else if(CPM.isStrictMode() && (xSpeed > 16383 || xSpeed < -16383)) { - throw new IllegalArgumentException("CPM PerceivedObject xSpeed should be in the range of [-16383 - 16383]." - + " Value: " + xSpeed); - } - this.xSpeed = xSpeed; - if(ySpeed == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM PerceivedObject ySpeed is missing"); - } else if(CPM.isStrictMode() && (ySpeed > 16383 || ySpeed < -16383)) { - throw new IllegalArgumentException("CPM PerceivedObject ySpeed should be in the range of [-16383 - 16383]." - + " Value: " + ySpeed); - } - this.ySpeed = ySpeed; - if(zSpeed != UNKNOWN && CPM.isStrictMode() - && (zSpeed > 16383 || zSpeed < -16383)) { - throw new IllegalArgumentException("CPM PerceivedObject zSpeed should be in the range of [-16383 - 16383]." - + " Value: " + zSpeed); - } - this.zSpeed = zSpeed; - //********* ACCELERATION *********** - if(xAcceleration != UNKNOWN && CPM.isStrictMode() - && (xAcceleration > 161 || xAcceleration < -160)) { - throw new IllegalArgumentException("CPM PerceivedObject xAcceleration should be in the range of [-160 - 161]." - + " Value: " + xAcceleration); - } - this.xAcceleration = xAcceleration; - if(yAcceleration != UNKNOWN && CPM.isStrictMode() - && (yAcceleration > 161 || yAcceleration < -160)) { - throw new IllegalArgumentException("CPM PerceivedObject yAcceleration should be in the range of [-160 - 161]." - + " Value: " + yAcceleration); - } - this.yAcceleration = yAcceleration; - if(zAcceleration != UNKNOWN && CPM.isStrictMode() - && (zAcceleration > 161 || zAcceleration < -160)) { - throw new IllegalArgumentException("CPM PerceivedObject zAcceleration should be in the range of [-160 - 161]." - + " Value: " + zAcceleration); - } - this.zAcceleration = zAcceleration; - //********* ROTATION *********** - if(rollAngle != UNKNOWN && CPM.isStrictMode() - && (rollAngle > 3601 || rollAngle < 0)) { - throw new IllegalArgumentException("CPM PerceivedObject rollAngle should be in the range of [0 - 3601]." - + " Value: " + rollAngle); - } - this.rollAngle = rollAngle; - if(pitchAngle != UNKNOWN && CPM.isStrictMode() - && (pitchAngle > 3601 || pitchAngle < 0)) { - throw new IllegalArgumentException("CPM PerceivedObject pitchAngle should be in the range of [0 - 3601]." - + " Value: " + pitchAngle); - } - this.pitchAngle = pitchAngle; - if(yawAngle != UNKNOWN && CPM.isStrictMode() - && (yawAngle > 3601 || yawAngle < 0)) { - throw new IllegalArgumentException("CPM PerceivedObject yawAngle should be in the range of [0 - 3601]." - + " Value: " + yawAngle); - } - this.yawAngle = yawAngle; - //********* ROTATION RATE *********** - if(rollRate != UNKNOWN && CPM.isStrictMode() - && (rollRate > 32767 || rollRate < -32766)) { - throw new IllegalArgumentException("CPM PerceivedObject rollRate should be in the range of [-32766 - 32767]." - + " Value: " + rollRate); - } - this.rollRate = rollRate; - if(pitchRate != UNKNOWN && CPM.isStrictMode() - && (pitchRate > 32767 || pitchRate < -32766)) { - throw new IllegalArgumentException("CPM PerceivedObject pitchRate should be in the range of [-32766 - 32767]." - + " Value: " + pitchRate); - } - this.pitchRate = pitchRate; - if(yawRate != UNKNOWN && CPM.isStrictMode() - && (yawRate > 32767 || yawRate < -32766)) { - throw new IllegalArgumentException("CPM PerceivedObject yawRate should be in the range of [-32766 - 32767]." - + " Value: " + yawRate); - } - this.yawRate = yawRate; - //********* ROTATION ACCELERATION *********** - if(rollAcceleration != UNKNOWN && CPM.isStrictMode() - && (rollAcceleration > 32767 || rollAcceleration < -32766)) { - throw new IllegalArgumentException("CPM PerceivedObject rollAcceleration should be in the range of [-32766 - 32767]." - + " Value: " + rollAcceleration); - } - this.rollAcceleration = rollAcceleration; - if(pitchAcceleration != UNKNOWN && CPM.isStrictMode() - && (pitchAcceleration > 32767 || pitchAcceleration < -32766)) { - throw new IllegalArgumentException("CPM PerceivedObject pitchAcceleration should be in the range of [-32766 - 32767]." - + " Value: " + pitchAcceleration); - } - this.pitchAcceleration = pitchAcceleration; - if(yawAcceleration != UNKNOWN && CPM.isStrictMode() - && (yawAcceleration > 32767 || yawAcceleration < -32766)) { - throw new IllegalArgumentException("CPM PerceivedObject yawAcceleration should be in the range of [-32766 - 32767]." - + " Value: " + yawAcceleration); - } - this.yawAcceleration = yawAcceleration; - //********* OBJECT DIMENSION *********** - if(planarObjectDimension1 != UNKNOWN && CPM.isStrictMode() - && (planarObjectDimension1 > 1023 || planarObjectDimension1 < 0)) { - throw new IllegalArgumentException("CPM PerceivedObject planarObjectDimension1 should be in the range of [0 - 1023]." - + " Value: " + planarObjectDimension1); - } - this.planarObjectDimension1 = planarObjectDimension1; - if(planarObjectDimension2 != UNKNOWN && CPM.isStrictMode() - && (planarObjectDimension2 > 1023 || planarObjectDimension2 < 0)) { - throw new IllegalArgumentException("CPM PerceivedObject planarObjectDimension2 should be in the range of [0 - 1023]." - + " Value: " + planarObjectDimension2); - } - this.planarObjectDimension2 = planarObjectDimension2; - if(verticalObjectDimension != UNKNOWN && CPM.isStrictMode() - && (verticalObjectDimension > 1023 || verticalObjectDimension < 0)) { - throw new IllegalArgumentException("CPM PerceivedObject verticalObjectDimension should be in the range of [0 - 1023]." - + " Value: " + verticalObjectDimension); - } - this.verticalObjectDimension = verticalObjectDimension; - if(objectRefPoint != UNKNOWN && CPM.isStrictMode() - && (objectRefPoint > 8 || objectRefPoint < 0)) { - throw new IllegalArgumentException("CPM PerceivedObject objectRefPoint should be in the range of [0 - 8]." - + " Value: " + objectRefPoint); - } - this.objectRefPoint = objectRefPoint; - if(objectAge == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM PerceivedObject objectAge is missing"); - } else if(CPM.isStrictMode() && (objectAge > 1500 || objectAge < 0)) { - throw new IllegalArgumentException("CPM PerceivedObject objectAge should be in the range of [0 - 1500]." - + " Value: " + objectAge); - } - this.objectAge = objectAge; - this.sensorIdList = sensorIdList; - if(dynamicStatus != UNKNOWN && CPM.isStrictMode() - && (dynamicStatus > 2 || dynamicStatus < 0)) { - throw new IllegalArgumentException("CPM PerceivedObject dynamicStatus should be in the range of [0 - 2]." - + " Value: " + dynamicStatus); - } - this.dynamicStatus = dynamicStatus; - this.classification = classification; - if(confidence == null && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM PerceivedObject confidence is missing"); - } - this.confidence = confidence; - - createJson(); - } - - private void createJson() { - try { - json.put(JsonCpmKey.PerceivedObjectContainer.OBJECT_ID.key(), objectId); - json.put(JsonCpmKey.PerceivedObjectContainer.TIME_OF_MEASUREMENT.key(), timeOfMeasurement); - json.put(JsonCpmKey.ObjectDistance.X_DISTANCE.key(), xDistance); - json.put(JsonCpmKey.ObjectDistance.Y_DISTANCE.key(), yDistance); - if(zDistance != UNKNOWN) - json.put(JsonCpmKey.ObjectDistance.Z_DISTANCE.key(), zDistance); - json.put(JsonCpmKey.ObjectSpeed.X_SPEED.key(), xSpeed); - json.put(JsonCpmKey.ObjectSpeed.Y_SPEED.key(), ySpeed); - if(zSpeed != UNKNOWN) - json.put(JsonCpmKey.ObjectSpeed.Z_SPEED.key(), zSpeed); - if(xAcceleration != UNKNOWN) - json.put(JsonCpmKey.ObjectAcceleration.X_ACCELERATION.key(), xAcceleration); - if(yAcceleration != UNKNOWN) - json.put(JsonCpmKey.ObjectAcceleration.Y_ACCELERATION.key(), yAcceleration); - if(zAcceleration != UNKNOWN) - json.put(JsonCpmKey.ObjectAcceleration.Z_ACCELERATION.key(), zAcceleration); - if(rollAngle != UNKNOWN) - json.put(JsonCpmKey.ObjectAngle.ROLL_ANGLE.key(), rollAngle); - if(pitchAngle != UNKNOWN) - json.put(JsonCpmKey.ObjectAngle.PITCH_ANGLE.key(), pitchAngle); - if(yawAngle != UNKNOWN) - json.put(JsonCpmKey.ObjectAngle.YAW_ANGLE.key(), yawAngle); - if(rollRate != UNKNOWN) - json.put(JsonCpmKey.ObjectSpeed.ROLL_RATE.key(), rollRate); - if(pitchRate != UNKNOWN) - json.put(JsonCpmKey.ObjectSpeed.PITCH_RATE.key(), pitchRate); - if(yawRate != UNKNOWN) - json.put(JsonCpmKey.ObjectSpeed.YAW_RATE.key(), yawRate); - if(rollAcceleration != UNKNOWN) - json.put(JsonCpmKey.ObjectAcceleration.ROLL_ACCELERATION.key(), rollAcceleration); - if(pitchAcceleration != UNKNOWN) - json.put(JsonCpmKey.ObjectAcceleration.PITCH_ACCELERATION.key(), pitchAcceleration); - if(yawAcceleration != UNKNOWN) - json.put(JsonCpmKey.ObjectAcceleration.YAW_ACCELERATION.key(), yawAcceleration); - if(planarObjectDimension1 != UNKNOWN) - json.put(JsonCpmKey.ObjectDimension.PLANAR_DIMENSION_1.key(), planarObjectDimension1); - if(planarObjectDimension2 != UNKNOWN) - json.put(JsonCpmKey.ObjectDimension.PLANAR_DIMENSION_2.key(), planarObjectDimension2); - if(verticalObjectDimension != UNKNOWN) - json.put(JsonCpmKey.ObjectDimension.VERTICAL_DIMENSION.key(), verticalObjectDimension); - if(objectRefPoint != UNKNOWN) - json.put(JsonCpmKey.PerceivedObjectContainer.OBJECT_REF_POINT.key(), objectRefPoint); - json.put(JsonCpmKey.PerceivedObjectContainer.OBJECT_AGE.key(), objectAge); - if(sensorIdList != null && !sensorIdList.isEmpty()) { - JSONArray jsonSensorIdList = new JSONArray(); - for(int sensorId: sensorIdList) { - jsonSensorIdList.put(sensorId); - } - json.put(JsonCpmKey.PerceivedObjectContainer.SENSOR_ID_LIST.key(), jsonSensorIdList); - } - if(dynamicStatus != UNKNOWN) - json.put(JsonCpmKey.PerceivedObjectContainer.DYNAMIC_STATUS.key(), dynamicStatus); - if(classification != null && !classification.isEmpty()) { - JSONArray jsonClassification = new JSONArray(); - for(ClassificationItem classificationItem: classification) { - if(classificationItem != null) jsonClassification.put(classificationItem.getJson()); - } - if(!JsonUtil.isNullOrEmpty(jsonClassification)) - json.put(JsonCpmKey.PerceivedObjectContainer.CLASSIFICATION.key(), jsonClassification); - } - json.put(JsonCpmKey.PerceivedObjectContainer.CONFIDENCE.key(), confidence.getJson()); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM PerceivedObject JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public int getObjectId() { - return objectId; - } - - public int getTimeOfMeasurement() { - return timeOfMeasurement; - } - - public int getDistanceX() { - return xDistance; - } - - public int getDistanceY() { - return yDistance; - } - - public int getDistanceZ() { - return zDistance; - } - - public int getSpeedX() { - return xSpeed; - } - - public int getSpeedY() { - return ySpeed; - } - - public int getSpeedZ() { - return zSpeed; - } - - public float getSpeedMs() { - return (float) Math.sqrt(Math.abs(xSpeed * xSpeed) + Math.abs(ySpeed * ySpeed)) / 100f; - } - - public float getHeadingDegree() { - return (float) (90 - (180 / Math.PI) * Math.atan2(ySpeed, xSpeed)) % 360; - } - - public int getSpeedKmh() { - return (int)(getSpeedMs() * 3.6); - } - - public int getAccelerationX() { - return xAcceleration; - } - - public int getAccelerationY() { - return yAcceleration; - } - - public int getAccelerationZ() { - return zAcceleration; - } - - public int getRollAngle() { - return rollAngle; - } - - public int getPitchAngle() { - return pitchAngle; - } - - public int getYawAngle() { - return yawAngle; - } - - public int getRollRate() { - return rollRate; - } - - public int getPitchRate() { - return pitchRate; - } - - public int getYawRate() { - return yawRate; - } - - public int getRollAcceleration() { - return rollAcceleration; - } - - public int getPitchAcceleration() { - return pitchAcceleration; - } - - public int getYawAcceleration() { - return yawAcceleration; - } - - public boolean hasPlanarObjectDimensions() { - return planarObjectDimension1 != UNKNOWN && planarObjectDimension2 != UNKNOWN; - } - - public int getPlanarObjectDimension1() { - return planarObjectDimension1; - } - - public int getPlanarObjectDimension2() { - return planarObjectDimension2; - } - - public int getVerticalObjectDimension() { - return verticalObjectDimension; - } - - public int getObjectRefPoint() { - return objectRefPoint; - } - - public int getObjectAge() { - return objectAge; - } - - public List getSensorIdList() { - return sensorIdList; - } - - public int getDynamicStatus() { - return dynamicStatus; - } - - public List getClassification() { - return classification; - } - - public PerceivedObjectConfidence getConfidence() { - return confidence; - } - - public static class PerceivedObjectBuilder { - private final int objectId; - private final int timeOfMeasurement; - private int xDistance; - private int yDistance; - private int zDistance = UNKNOWN; - private int xSpeed; - private int ySpeed; - private int zSpeed = UNKNOWN; - private int xAcceleration = UNKNOWN; - private int yAcceleration = UNKNOWN; - private int zAcceleration = UNKNOWN; - private int rollAngle = UNKNOWN; - private int pitchAngle = UNKNOWN; - private int yawAngle = UNKNOWN; - private int rollRate = UNKNOWN; - private int pitchRate = UNKNOWN; - private int yawRate = UNKNOWN; - private int rollAcceleration = UNKNOWN; - private int pitchAcceleration = UNKNOWN; - private int yawAcceleration = UNKNOWN; - private int planarObjectDimension1 = UNKNOWN; - private int planarObjectDimension2 = UNKNOWN; - private int verticalObjectDimension = UNKNOWN; - private int objectRefPoint = UNKNOWN; - private final int objectAge; - private List sensorIdList; - private int dynamicStatus = UNKNOWN; - private List classification; - private PerceivedObjectConfidence confidence; - - /** - * Start building a PerceivedObject. - * - * @param objectId {@link PerceivedObject#objectId} - * @param timeOfMeasurement {@link PerceivedObject#timeOfMeasurement} - * @param objectAge {@link PerceivedObject#objectAge} - */ - public PerceivedObjectBuilder(int objectId, - int timeOfMeasurement, - int objectAge) { - this.objectId = objectId; - this.timeOfMeasurement = timeOfMeasurement; - this.objectAge = objectAge; - } - - /** - * Sets the distance between the sensor and the perceived object. - *

- * These fields are mandatory. - * - * @param xDistance {@link PerceivedObject#xDistance} - * @param yDistance {@link PerceivedObject#yDistance} - */ - public PerceivedObjectBuilder distance(int xDistance, - int yDistance) { - this.xDistance = xDistance; - this.yDistance = yDistance; - return this; - } - - /** - * Sets the distance between the sensor and the perceived object. - *

- * These fields are mandatory, except zDistance - use {@link #distance(int, int)} if not known. - * - * @param xDistance {@link PerceivedObject#xDistance} - * @param yDistance {@link PerceivedObject#yDistance} - * @param zDistance {@link PerceivedObject#zDistance} - */ - public PerceivedObjectBuilder distance(int xDistance, - int yDistance, - int zDistance) { - this.xDistance = xDistance; - this.yDistance = yDistance; - this.zDistance = zDistance; - return this; - } - - /** - * Sets the speed of the perceived object. - *

- * These fields are mandatory. - * - * @param xSpeed {@link PerceivedObject#xSpeed} - * @param ySpeed {@link PerceivedObject#ySpeed} - */ - public PerceivedObjectBuilder speed(int xSpeed, - int ySpeed) { - this.xSpeed = xSpeed; - this.ySpeed = ySpeed; - return this; - } - - /** - * Sets the speed of the perceived object. - *

- * These fields are mandatory, except zSpeed - use {@link #speed(int, int)} if not known. - * - * @param xSpeed {@link PerceivedObject#xSpeed} - * @param ySpeed {@link PerceivedObject#ySpeed} - * @param zSpeed {@link PerceivedObject#zSpeed} - */ - public PerceivedObjectBuilder speed(int xSpeed, - int ySpeed, - int zSpeed) { - this.xSpeed = xSpeed; - this.ySpeed = ySpeed; - this.zSpeed = zSpeed; - return this; - } - - /** - * Sets the acceleration of the perceived object. - *

- * These fields are optional. - * - * @param xAcceleration {@link PerceivedObject#xAcceleration} - * @param yAcceleration {@link PerceivedObject#yAcceleration} - * @param zAcceleration {@link PerceivedObject#zAcceleration} - */ - public PerceivedObjectBuilder acceleration(int xAcceleration, - int yAcceleration, - int zAcceleration) { - this.xAcceleration = xAcceleration; - this.yAcceleration = yAcceleration; - this.zAcceleration = zAcceleration; - return this; - } - - /** - * Sets the angle of the perceived object. - *

- * These fields are optional. - * - * @param rollAngle {@link PerceivedObject#rollAngle} - * @param pitchAngle {@link PerceivedObject#pitchAngle} - * @param yawAngle {@link PerceivedObject#yawAngle} - */ - public PerceivedObjectBuilder angle(int rollAngle, - int pitchAngle, - int yawAngle) { - this.rollAngle = rollAngle; - this.pitchAngle = pitchAngle; - this.yawAngle = yawAngle; - return this; - } - - /** - * Sets the angular rate of the perceived object. - *

- * These fields are optional. - * - * @param rollRate {@link PerceivedObject#rollRate} - * @param pitchRate {@link PerceivedObject#pitchRate} - * @param yawRate {@link PerceivedObject#yawRate} - */ - public PerceivedObjectBuilder angleRate(int rollRate, - int pitchRate, - int yawRate) { - this.rollRate = rollRate; - this.pitchRate = pitchRate; - this.yawRate = yawRate; - return this; - } - - /** - * Sets the angular acceleration of the perceived object. - *

- * These fields are optional. - * - * @param rollAcceleration {@link PerceivedObject#rollAcceleration} - * @param pitchAcceleration {@link PerceivedObject#pitchAcceleration} - * @param yawAcceleration {@link PerceivedObject#yawAcceleration} - */ - public PerceivedObjectBuilder angleAcceleration(int rollAcceleration, - int pitchAcceleration, - int yawAcceleration) { - this.rollAcceleration = rollAcceleration; - this.pitchAcceleration = pitchAcceleration; - this.yawAcceleration = yawAcceleration; - return this; - } - - /** - * Sets the dimensions of the perceived object. - *

- * These fields are optional. - * - * @param planarObjectDimension1 {@link PerceivedObject#planarObjectDimension1} - * @param planarObjectDimension2 {@link PerceivedObject#planarObjectDimension2} - * @param verticalObjectDimension {@link PerceivedObject#verticalObjectDimension} - * @param objectRefPoint {@link PerceivedObject#objectRefPoint} - */ - public PerceivedObjectBuilder objectDimension(int planarObjectDimension1, - int planarObjectDimension2, - int verticalObjectDimension, - int objectRefPoint) { - this.planarObjectDimension1 = planarObjectDimension1; - this.planarObjectDimension2 = planarObjectDimension2; - this.verticalObjectDimension = verticalObjectDimension; - this.objectRefPoint = objectRefPoint; - return this; - } - - /** - * Sets the list of sensors which have detected the perceived object. - *

- * This list is optional. - * - * @param sensorIdList {@link PerceivedObject#sensorIdList} - */ - public PerceivedObjectBuilder sensorIdList(List sensorIdList) { - this.sensorIdList = sensorIdList; - return this; - } - - /** - * Sets the dynamic status of the perceived object. - *

- * This field is optional. - * - * @param dynamicStatus {@link PerceivedObject#dynamicStatus} - */ - public PerceivedObjectBuilder dynamicStatus(int dynamicStatus) { - this.dynamicStatus = dynamicStatus; - return this; - } - - /** - * Sets the list of classification of the perceived object. - *

- * This list is optional. - * - * @param classification {@link PerceivedObject#classification} - */ - public PerceivedObjectBuilder classification(List classification) { - this.classification = classification; - return this; - } - - /** - * Sets the confidence of the perceived object. - *

- * This field is mandatory. - * - * @param confidence {@link PerceivedObject#confidence} - */ - public PerceivedObjectBuilder confidence(PerceivedObjectConfidence confidence) { - this.confidence = confidence; - return this; - } - - /** - * Build the perceived object. - *

- * Call after setting all the mandatory fields. - * - * @return {@link #PerceivedObject} - */ - public PerceivedObject build() { - return new PerceivedObject( - objectId, - timeOfMeasurement, - xDistance, - yDistance, - zDistance, - xSpeed, - ySpeed, - zSpeed, - xAcceleration, - yAcceleration, - zAcceleration, - rollAngle, - pitchAngle, - yawAngle, - rollRate, - pitchRate, - yawRate, - rollAcceleration, - pitchAcceleration, - yawAcceleration, - planarObjectDimension1, - planarObjectDimension2, - verticalObjectDimension, - objectRefPoint, - objectAge, - sensorIdList, - dynamicStatus, - classification, - confidence); - } - - } - - public static PerceivedObject jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - try { - int objectId = json.getInt(JsonCpmKey.PerceivedObjectContainer.OBJECT_ID.key()); - int timeOfMeasurement = json.getInt(JsonCpmKey.PerceivedObjectContainer.TIME_OF_MEASUREMENT.key()); - int xDistance = json.getInt(JsonCpmKey.ObjectDistance.X_DISTANCE.key()); - int yDistance = json.getInt(JsonCpmKey.ObjectDistance.Y_DISTANCE.key()); - int zDistance = json.optInt(JsonCpmKey.ObjectDistance.Z_DISTANCE.key(), UNKNOWN); - int xSpeed = json.getInt(JsonCpmKey.ObjectSpeed.X_SPEED.key()); - int ySpeed = json.getInt(JsonCpmKey.ObjectSpeed.Y_SPEED.key()); - int zSpeed = json.optInt(JsonCpmKey.ObjectSpeed.Z_SPEED.key(), UNKNOWN); - int xAcceleration = json.optInt(JsonCpmKey.ObjectAcceleration.X_ACCELERATION.key(), UNKNOWN); - int yAcceleration = json.optInt(JsonCpmKey.ObjectAcceleration.Y_ACCELERATION.key(), UNKNOWN); - int zAcceleration = json.optInt(JsonCpmKey.ObjectAcceleration.Z_ACCELERATION.key(), UNKNOWN); - int rollAngle = json.optInt(JsonCpmKey.ObjectAngle.ROLL_ANGLE.key(), UNKNOWN); - int pitchAngle = json.optInt(JsonCpmKey.ObjectAngle.PITCH_ANGLE.key(), UNKNOWN); - int yawAngle = json.optInt(JsonCpmKey.ObjectAngle.YAW_ANGLE.key(), UNKNOWN); - int rollRate = json.optInt(JsonCpmKey.ObjectSpeed.ROLL_RATE.key(), UNKNOWN); - int pitchRate = json.optInt(JsonCpmKey.ObjectSpeed.PITCH_RATE.key(), UNKNOWN); - int yawRate = json.optInt(JsonCpmKey.ObjectSpeed.YAW_RATE.key(), UNKNOWN); - int rollAcceleration = json.optInt(JsonCpmKey.ObjectAcceleration.ROLL_ACCELERATION.key(), UNKNOWN); - int pitchAcceleration = json.optInt(JsonCpmKey.ObjectAcceleration.PITCH_ACCELERATION.key(), UNKNOWN); - int yawAcceleration = json.optInt(JsonCpmKey.ObjectAcceleration.YAW_ACCELERATION.key(), UNKNOWN); - int planarObjectDimension1 = json.optInt(JsonCpmKey.ObjectDimension.PLANAR_DIMENSION_1.key(), UNKNOWN); - int planarObjectDimension2 = json.optInt(JsonCpmKey.ObjectDimension.PLANAR_DIMENSION_2.key(), UNKNOWN); - int verticalObjectDimension = json.optInt(JsonCpmKey.ObjectDimension.VERTICAL_DIMENSION.key(), UNKNOWN); - int objectRefPoint = json.optInt(JsonCpmKey.PerceivedObjectContainer.OBJECT_REF_POINT.key(), UNKNOWN); - int objectAge = json.getInt(JsonCpmKey.PerceivedObjectContainer.OBJECT_AGE.key()); - JSONArray jsonSensorIdList = json.optJSONArray(JsonCpmKey.PerceivedObjectContainer.SENSOR_ID_LIST.key()); - ArrayList sensorIdList = null; - if(jsonSensorIdList != null) { - sensorIdList = new ArrayList<>(); - for(int i = 0; i < jsonSensorIdList.length(); i++) { - sensorIdList.add(jsonSensorIdList.getInt(i)); - } - } - int dynamicStatus = json.optInt(JsonCpmKey.PerceivedObjectContainer.DYNAMIC_STATUS.key(), UNKNOWN); - JSONArray jsonClassification = json.optJSONArray(JsonCpmKey.PerceivedObjectContainer.CLASSIFICATION.key()); - ArrayList classification = null; - if(jsonClassification != null) { - classification = new ArrayList<>(); - for(int i = 0; i < jsonClassification.length(); i++) { - classification.add(ClassificationItem.jsonParser(jsonClassification.getJSONObject(i))); - } - } - JSONObject jsonConfidence = json.getJSONObject(JsonCpmKey.PerceivedObjectContainer.CONFIDENCE.key()); - PerceivedObjectConfidence confidence = PerceivedObjectConfidence.jsonParser(jsonConfidence); - - return new PerceivedObjectBuilder( - objectId, - timeOfMeasurement, - objectAge) - .distance( - xDistance, - yDistance, - zDistance) - .speed( - xSpeed, - ySpeed, - zSpeed) - .acceleration( - xAcceleration, - yAcceleration, - zAcceleration) - .angle( - rollAngle, - pitchAngle, - yawAngle) - .angleRate( - rollRate, - pitchRate, - yawRate) - .angleAcceleration( - rollAcceleration, - pitchAcceleration, - yawAcceleration) - .objectDimension( - planarObjectDimension1, - planarObjectDimension2, - verticalObjectDimension, - objectRefPoint) - .sensorIdList(sensorIdList) - .dynamicStatus(dynamicStatus) - .classification(classification) - .confidence(confidence) - .build(); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM PerceivedObject JSON parsing error", "Error: " + e); - } - return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/PerceivedObjectConfidence.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/PerceivedObjectConfidence.java deleted file mode 100644 index 3e129e027..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/PerceivedObjectConfidence.java +++ /dev/null @@ -1,952 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class PerceivedObjectConfidence { - - private static final Logger LOGGER = Logger.getLogger(PerceivedObjectConfidence.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Unit: 0.01 meter. Distance confidence to detected object from the reference point in - * x-direction at the time of measurement. - *

- * Absolute accuracy of measurement to a confidence level of 95%. - *

- * zeroPointZeroOneMeter(1), oneMeter(100), outOfRange(4094), unavailable(4095). - */ - private final int xDistance; - - /** - * Unit: 0.01 meter. Distance confidence to detected object from the reference point in - * y-direction at the time of measurement. - *

- * Absolute accuracy of measurement to a confidence level of 95%. - *

- * zeroPointZeroOneMeter(1), oneMeter(100), outOfRange(4094), unavailable(4095). - */ - private final int yDistance; - - /** - * Unit: 0.01 meter. Distance confidence to detected object from the reference point in - * z-direction at the time of measurement. - *

- * Absolute accuracy of measurement to a confidence level of 95%. - *

- * zeroPointZeroOneMeter(1), oneMeter(100), outOfRange(4094), unavailable(4095). - */ - private final int zDistance; - - /** - * Speed confidence of detected object from the reference point in x-direction at the time - * of measurement. - *

- * unavailable(0) Not Equipped or unavailable, - * prec100ms(1) 100 meters / sec, - * prec10ms(2) 10 meters / sec, - * prec5ms(3) 5 meters / sec, - * prec1ms(4) 1 meters / sec, - * prec0-1ms(5) 0.1 meters / sec, - * prec0-05ms(6) 0.05 meters / sec, - * prec0-01ms(7) 0.01 meters / sec - */ - private final int xSpeed; - - /** - * Speed confidence of detected object from the reference point in y-direction at the time - * of measurement. - *

- * unavailable(0) Not Equipped or unavailable, - * prec100ms(1) 100 meters / sec, - * prec10ms(2) 10 meters / sec, - * prec5ms(3) 5 meters / sec, - * prec1ms(4) 1 meters / sec, - * prec0-1ms(5) 0.1 meters / sec, - * prec0-05ms(6) 0.05 meters / sec, - * prec0-01ms(7) 0.01 meters / sec - */ - private final int ySpeed; - - /** - * Speed confidence of detected object from the reference point in z-direction at the time - * of measurement. - *

- * unavailable(0) Not Equipped or unavailable, - * prec100ms(1) 100 meters / sec, - * prec10ms(2) 10 meters / sec, - * prec5ms(3) 5 meters / sec, - * prec1ms(4) 1 meters / sec, - * prec0-1ms(5) 0.1 meters / sec, - * prec0-05ms(6) 0.05 meters / sec, - * prec0-01ms(7) 0.01 meters / sec - */ - private final int zSpeed; - - /** - * Acceleration confidence of detected object from the reference point in x-direction at the - * time of measurement. - *

- * pointOneMeterPerSecSquared(1), outOfRange(101), unavailable(102). - */ - private final int xAcceleration; - - /** - * Acceleration confidence of detected object from the reference point in y-direction at the - * time of measurement. - *

- * pointOneMeterPerSecSquared(1), outOfRange(101), unavailable(102). - */ - private final int yAcceleration; - - /** - * Acceleration confidence of detected object from the reference point in z-direction at the - * time of measurement. - *

- * pointOneMeterPerSecSquared(1), outOfRange(101), unavailable(102). - */ - private final int zAcceleration; - - /** - * Roll angle confidence. The absolute accuracy of a reported angle value for a predefined - * confidence level (e.g. 95 %). - *

- * The required confidence level is defined by the corresponding standards. - *

- * zeroPointOneDegree(1), oneDegree(10), outOfRange(126), unavailable(127). - */ - private final int rollAngle; - - /** - * Pitch angle confidence. The absolute accuracy of a reported angle value for a predefined - * confidence level (e.g. 95 %). - *

- * The required confidence level is defined by the corresponding standards. - *

- * zeroPointOneDegree(1), oneDegree(10), outOfRange(126), unavailable(127). - */ - private final int pitchAngle; - - /** - * Yaw angle confidence. The absolute accuracy of a reported angle value for a predefined - * confidence level (e.g. 95 %). - *

- * The required confidence level is defined by the corresponding standards. - *

- * zeroPointOneDegree(1), oneDegree(10), outOfRange(126), unavailable(127). - */ - private final int yawAngle; - - /** - * Roll rate confidence. The absolute accuracy of a reported angular speed value for a - * predefined confidence level (e.g. 95 %). - *

- * The required confidence level is defined by the corresponding standards. - * For correlation computation, maximum interval levels shall be assumed. - *

- * degSec-000-01(0) if the accuracy is equal to or less than 0.01 degree/second, - * degSec-000-05(1) if the accuracy is equal to or less than 0.05 degrees/second, - * degSec-000-10(2) if the accuracy is equal to or less than 0.1 degree/second, - * degSec-001-00(3) if the accuracy is equal to or less than 1 degree/second, - * degSec-005-00(4) if the accuracy is equal to or less than 5 degrees/second, - * degSec-010-00(5) if the accuracy is equal to or less than 10 degrees/second, - * degSec-100-00(6) if the accuracy is equal to or less than 100 degrees/second, - * outOfRange(7) if the accuracy is out of range, i.e. greater than 100 degrees/second, - * unavailable(8) if the accuracy information is unavailable - */ - private final int rollRate; - - /** - * Pitch rate confidence. The absolute accuracy of a reported angular speed value for a - * predefined confidence level (e.g. 95 %). - *

- * The required confidence level is defined by the corresponding standards. - * For correlation computation, maximum interval levels shall be assumed. - *

- * degSec-000-01(0) if the accuracy is equal to or less than 0.01 degree/second, - * degSec-000-05(1) if the accuracy is equal to or less than 0.05 degrees/second, - * degSec-000-10(2) if the accuracy is equal to or less than 0.1 degree/second, - * degSec-001-00(3) if the accuracy is equal to or less than 1 degree/second, - * degSec-005-00(4) if the accuracy is equal to or less than 5 degrees/second, - * degSec-010-00(5) if the accuracy is equal to or less than 10 degrees/second, - * degSec-100-00(6) if the accuracy is equal to or less than 100 degrees/second, - * outOfRange(7) if the accuracy is out of range, i.e. greater than 100 degrees/second, - * unavailable(8) if the accuracy information is unavailable - */ - private final int pitchRate; - - /** - * Yaw rate confidence. The absolute accuracy of a reported angular speed value for a - * predefined confidence level (e.g. 95 %). - *

- * The required confidence level is defined by the corresponding standards. - * For correlation computation, maximum interval levels shall be assumed. - *

- * degSec-000-01(0) if the accuracy is equal to or less than 0.01 degree/second, - * degSec-000-05(1) if the accuracy is equal to or less than 0.05 degrees/second, - * degSec-000-10(2) if the accuracy is equal to or less than 0.1 degree/second, - * degSec-001-00(3) if the accuracy is equal to or less than 1 degree/second, - * degSec-005-00(4) if the accuracy is equal to or less than 5 degrees/second, - * degSec-010-00(5) if the accuracy is equal to or less than 10 degrees/second, - * degSec-100-00(6) if the accuracy is equal to or less than 100 degrees/second, - * outOfRange(7) if the accuracy is out of range, i.e. greater than 100 degrees/second, - * unavailable(8) if the accuracy information is unavailable - */ - private final int yawRate; - - /** - * Roll acceleration confidence.The absolute accuracy of a reported angular acceleration value - * for a predefined confidence level (e.g. 95 %). - *

- * The required confidence level is defined by the corresponding standards. - * For correlation computation, maximum interval levels shall be assumed. - *

- * degSecSquared-000-01(0) if the accuracy is equal to or less than 0.01 degree/second^2, - * degSecSquared-000-05(1) if the accuracy is equal to or less than 0.05 degrees/second^2, - * degSecSquared-000-10(2) if the accuracy is equal to or less than 0.1 degree/second^2, - * degSecSquared-001-00(3) if the accuracy is equal to or less than 1 degree/second^2, - * degSecSquared-005-00(4) if the accuracy is equal to or less than 5 degrees/second^2, - * degSecSquared-010-00(5) if the accuracy is equal to or less than 10 degrees/second^2, - * degSecSquared-100-00(6) if the accuracy is equal to or less than 100 degrees/second^2, - * outOfRange(7) if the accuracy is out of range, i.e. greater than 100 degrees/second^2, - * unavailable(8) if the accuracy information is unavailable - */ - private final int rollAcceleration; - - /** - * Pitch acceleration confidence.The absolute accuracy of a reported angular acceleration value - * for a predefined confidence level (e.g. 95 %). - *

- * The required confidence level is defined by the corresponding standards. - * For correlation computation, maximum interval levels shall be assumed. - *

- * degSecSquared-000-01(0) if the accuracy is equal to or less than 0.01 degree/second^2, - * degSecSquared-000-05(1) if the accuracy is equal to or less than 0.05 degrees/second^2, - * degSecSquared-000-10(2) if the accuracy is equal to or less than 0.1 degree/second^2, - * degSecSquared-001-00(3) if the accuracy is equal to or less than 1 degree/second^2, - * degSecSquared-005-00(4) if the accuracy is equal to or less than 5 degrees/second^2, - * degSecSquared-010-00(5) if the accuracy is equal to or less than 10 degrees/second^2, - * degSecSquared-100-00(6) if the accuracy is equal to or less than 100 degrees/second^2, - * outOfRange(7) if the accuracy is out of range, i.e. greater than 100 degrees/second^2, - * unavailable(8) if the accuracy information is unavailable - */ - private final int pitchAcceleration; - - /** - * Yaw acceleration confidence.The absolute accuracy of a reported angular acceleration value - * for a predefined confidence level (e.g. 95 %). - *

- * The required confidence level is defined by the corresponding standards. - * For correlation computation, maximum interval levels shall be assumed. - *

- * degSecSquared-000-01(0) if the accuracy is equal to or less than 0.01 degree/second^2, - * degSecSquared-000-05(1) if the accuracy is equal to or less than 0.05 degrees/second^2, - * degSecSquared-000-10(2) if the accuracy is equal to or less than 0.1 degree/second^2, - * degSecSquared-001-00(3) if the accuracy is equal to or less than 1 degree/second^2, - * degSecSquared-005-00(4) if the accuracy is equal to or less than 5 degrees/second^2, - * degSecSquared-010-00(5) if the accuracy is equal to or less than 10 degrees/second^2, - * degSecSquared-100-00(6) if the accuracy is equal to or less than 100 degrees/second^2, - * outOfRange(7) if the accuracy is out of range, i.e. greater than 100 degrees/second^2, - * unavailable(8) if the accuracy information is unavailable - */ - private final int yawAcceleration; - - /** - * Unit: 0.01 m. Accuracy of first provided dimension value with a predefined confidence - * level (e.g. 95%). - *

- * zeroPointZeroOneMeter(1), oneMeter(100), outOfRange(101), unavailable(102). - */ - private final int planarObjectDimension1; - - /** - * Unit: 0.01 m. Accuracy of second provided dimension value with a predefined confidence - * level (e.g. 95%). - *

- * zeroPointZeroOneMeter(1), oneMeter(100), outOfRange(101), unavailable(102). - */ - private final int planarObjectDimension2; - - /** - * Unit: 0.01 m. Accuracy of vertical provided dimension value with a predefined confidence - * level (e.g. 95%). - *

- * zeroPointZeroOneMeter(1), oneMeter(100), outOfRange(101), unavailable(102). - */ - private final int verticalObjectDimension; - - /** - * Unit: 0.01 m. Absolute accuracy of longitudinal lane position measurement to a confidence - * level of 95%. - *

- * zeroPointZeroOneMeter(1), oneMeter(100), outOfRange(101) shall be set if the accuracy - * is out of range, unavailable(102) shall be set if the accuracy data is unavailable - */ - private final int longitudinalLanePosition; - - /** - * The confidence associated to the object. - *

- * The computation of the object confidence is based on a sensor's or, fusion system's specific - * detection confidence, the binary detection success that is, if an object has been - * successfully detected by the last measurement and the object age. - *

- * A single-value indication about the overall information quality of a perceived object. - * Its computation is based on several scaling factors and moving averages. - *

- * noConfidence(0) no confidence in detected object, e.g. for ghost-objects or if confidence - * could not be computed, fullConfidence(15) full confidence in detected object - */ - private final int object; - - private PerceivedObjectConfidence( - final int xDistance, - final int yDistance, - final int zDistance, - final int xSpeed, - final int ySpeed, - final int zSpeed, - final int xAcceleration, - final int yAcceleration, - final int zAcceleration, - final int rollAngle, - final int pitchAngle, - final int yawAngle, - final int rollRate, - final int pitchRate, - final int yawRate, - final int rollAcceleration, - final int pitchAcceleration, - final int yawAcceleration, - final int planarObjectDimension1, - final int planarObjectDimension2, - final int verticalObjectDimension, - final int longitudinalLanePosition, - final int object - ) throws IllegalArgumentException { - //********* DISTANCE *********** - if(xDistance == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence xDistance is missing"); - } else if(CPM.isStrictMode() && (xDistance > 4095 || xDistance < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence xDistance should be in the range of [0 - 4095]." - + " Value: " + xDistance); - } - this.xDistance = xDistance; - if(yDistance == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence yDistance is missing"); - } else if(CPM.isStrictMode() && (yDistance > 4095 || yDistance < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence yDistance should be in the range of [0 - 4095]." - + " Value: " + yDistance); - } - this.yDistance = yDistance; - if(zDistance != UNKNOWN && CPM.isStrictMode() - && (zDistance > 4095 || zDistance < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence zDistance should be in the range of [0 - 4095]." - + " Value: " + zDistance); - } - this.zDistance = zDistance; - //********* SPEED *********** - if(xSpeed == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence xSpeed is missing"); - } else if(CPM.isStrictMode() && (xSpeed > 7 || xSpeed < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence xSpeed should be in the range of [0 - 7]." - + " Value: " + xSpeed); - } - this.xSpeed = xSpeed; - if(ySpeed == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence ySpeed is missing"); - } else if(CPM.isStrictMode() && (ySpeed > 7 || ySpeed < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence ySpeed should be in the range of [0 - 7]." - + " Value: " + ySpeed); - } - this.ySpeed = ySpeed; - if(zSpeed != UNKNOWN && CPM.isStrictMode() - && (zSpeed > 7 || zSpeed < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence zSpeed should be in the range of [0 - 7]." - + " Value: " + zSpeed); - } - this.zSpeed = zSpeed; - //********* ACCELERATION *********** - if(xAcceleration != UNKNOWN && CPM.isStrictMode() - && (xAcceleration > 102 || xAcceleration < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence xAcceleration should be in the range of [0 - 102]." - + " Value: " + xAcceleration); - } - this.xAcceleration = xAcceleration; - if(yAcceleration != UNKNOWN && CPM.isStrictMode() - && (yAcceleration > 102 || yAcceleration < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence yAcceleration should be in the range of [0 - 102]." - + " Value: " + yAcceleration); - } - this.yAcceleration = yAcceleration; - if(zAcceleration != UNKNOWN && CPM.isStrictMode() - && (zAcceleration > 102 || zAcceleration < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence zAcceleration should be in the range of [0 - 102]." - + " Value: " + zAcceleration); - } - this.zAcceleration = zAcceleration; - //********* ROTATION *********** - if(rollAngle != UNKNOWN && CPM.isStrictMode() - && (rollAngle > 127 || rollAngle < 1)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence rollAngle should be in the range of [1 - 127]." - + " Value: " + rollAngle); - } - this.rollAngle = rollAngle; - if(pitchAngle != UNKNOWN && CPM.isStrictMode() - && (pitchAngle > 127 || pitchAngle < 1)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence pitchAngle should be in the range of [1 - 127]." - + " Value: " + pitchAngle); - } - this.pitchAngle = pitchAngle; - if(yawAngle != UNKNOWN && CPM.isStrictMode() - && (yawAngle > 127 || yawAngle < 1)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence yawAngle should be in the range of [1 - 127]." - + " Value: " + yawAngle); - } - this.yawAngle = yawAngle; - //********* ROTATION RATE *********** - if(rollRate != UNKNOWN && CPM.isStrictMode() - && (rollRate > 8 || rollRate < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence rollRate should be in the range of [0 - 8]." - + " Value: " + rollRate); - } - this.rollRate = rollRate; - if(pitchRate != UNKNOWN && CPM.isStrictMode() - && (pitchRate > 8 || pitchRate < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence pitchRate should be in the range of [0 - 8]." - + " Value: " + pitchRate); - } - this.pitchRate = pitchRate; - if(yawRate != UNKNOWN && CPM.isStrictMode() - && (yawRate > 8 || yawRate < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence yawRate should be in the range of [0 - 8]." - + " Value: " + yawRate); - } - this.yawRate = yawRate; - //********* ROTATION ACCELERATION *********** - if(rollAcceleration != UNKNOWN && CPM.isStrictMode() - && (rollAcceleration > 8 || rollAcceleration < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence rollAcceleration should be in the range of [0 - 8]." - + " Value: " + rollAcceleration); - } - this.rollAcceleration = rollAcceleration; - if(pitchAcceleration != UNKNOWN && CPM.isStrictMode() - && (pitchAcceleration > 8 || pitchAcceleration < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence pitchAcceleration should be in the range of [0 - 8]." - + " Value: " + pitchAcceleration); - } - this.pitchAcceleration = pitchAcceleration; - if(yawAcceleration != UNKNOWN && CPM.isStrictMode() - && (yawAcceleration > 8 || yawAcceleration < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence yawAcceleration should be in the range of [0 - 8]." - + " Value: " + yawAcceleration); - } - this.yawAcceleration = yawAcceleration; - //********* OBJECT DIMENSION *********** - if(planarObjectDimension1 != UNKNOWN && CPM.isStrictMode() - && (planarObjectDimension1 > 102 || planarObjectDimension1 < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence planarObjectDimension1 should be in the range of [0 - 102]." - + " Value: " + planarObjectDimension1); - } - this.planarObjectDimension1 = planarObjectDimension1; - if(planarObjectDimension2 != UNKNOWN && CPM.isStrictMode() - && (planarObjectDimension2 > 102 || planarObjectDimension2 < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence planarObjectDimension2 should be in the range of [0 - 102]." - + " Value: " + planarObjectDimension2); - } - this.planarObjectDimension2 = planarObjectDimension2; - if(verticalObjectDimension != UNKNOWN && CPM.isStrictMode() - && (verticalObjectDimension > 102 || verticalObjectDimension < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence verticalObjectDimension should be in the range of [0 - 102]." - + " Value: " + verticalObjectDimension); - } - this.verticalObjectDimension = verticalObjectDimension; - - if(longitudinalLanePosition != UNKNOWN && CPM.isStrictMode() - && (longitudinalLanePosition > 102 || longitudinalLanePosition < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence longitudinalLanePosition should be in the range of [0 - 102]." - + " Value: " + verticalObjectDimension); - } - this.longitudinalLanePosition = longitudinalLanePosition; - - if(object == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence object is missing"); - } else if(CPM.isStrictMode() && (object > 15 || object < 0)) { - throw new IllegalArgumentException("CPM PerceivedObjectConfidence object should be in the range of [0 - 15]." - + " Value: " + object); - } - this.object = object; - - createJson(); - } - - private void createJson() { - try { - json.put(JsonCpmKey.ObjectDistance.X_DISTANCE.key(), xDistance); - json.put(JsonCpmKey.ObjectDistance.Y_DISTANCE.key(), yDistance); - if(zDistance != UNKNOWN) - json.put(JsonCpmKey.ObjectDistance.Z_DISTANCE.key(), zDistance); - json.put(JsonCpmKey.ObjectSpeed.X_SPEED.key(), xSpeed); - json.put(JsonCpmKey.ObjectSpeed.Y_SPEED.key(), ySpeed); - if(zSpeed != UNKNOWN) - json.put(JsonCpmKey.ObjectSpeed.Z_SPEED.key(), zSpeed); - if(xAcceleration != UNKNOWN) - json.put(JsonCpmKey.ObjectAcceleration.X_ACCELERATION.key(), xAcceleration); - if(yAcceleration != UNKNOWN) - json.put(JsonCpmKey.ObjectAcceleration.Y_ACCELERATION.key(), yAcceleration); - if(zAcceleration != UNKNOWN) - json.put(JsonCpmKey.ObjectAcceleration.Z_ACCELERATION.key(), zAcceleration); - if(rollAngle != UNKNOWN) - json.put(JsonCpmKey.ObjectAngle.ROLL_ANGLE.key(), rollAngle); - if(pitchAngle != UNKNOWN) - json.put(JsonCpmKey.ObjectAngle.PITCH_ANGLE.key(), pitchAngle); - if(yawAngle != UNKNOWN) - json.put(JsonCpmKey.ObjectAngle.YAW_ANGLE.key(), yawAngle); - if(rollRate != UNKNOWN) - json.put(JsonCpmKey.ObjectSpeed.ROLL_RATE.key(), rollRate); - if(pitchRate != UNKNOWN) - json.put(JsonCpmKey.ObjectSpeed.PITCH_RATE.key(), pitchRate); - if(yawRate != UNKNOWN) - json.put(JsonCpmKey.ObjectSpeed.YAW_RATE.key(), yawRate); - if(rollAcceleration != UNKNOWN) - json.put(JsonCpmKey.ObjectAcceleration.ROLL_ACCELERATION.key(), rollAcceleration); - if(pitchAcceleration != UNKNOWN) - json.put(JsonCpmKey.ObjectAcceleration.PITCH_ACCELERATION.key(), pitchAcceleration); - if(yawAcceleration != UNKNOWN) - json.put(JsonCpmKey.ObjectAcceleration.YAW_ACCELERATION.key(), yawAcceleration); - if(planarObjectDimension1 != UNKNOWN) - json.put(JsonCpmKey.ObjectDimension.PLANAR_DIMENSION_1.key(), planarObjectDimension1); - if(planarObjectDimension2 != UNKNOWN) - json.put(JsonCpmKey.ObjectDimension.PLANAR_DIMENSION_2.key(), planarObjectDimension2); - if(verticalObjectDimension != UNKNOWN) - json.put(JsonCpmKey.ObjectDimension.VERTICAL_DIMENSION.key(), verticalObjectDimension); - if(longitudinalLanePosition != UNKNOWN) - json.put(JsonCpmKey.ObjectLanePosition.LONGITUDINAL_LANE_POSITION.key(), longitudinalLanePosition); - json.put(JsonCpmKey.PerceivedObjectContainer.OBJECT.key(), object); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM PerceivedObjectConfidence JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public int getDistanceX() { - return xDistance; - } - - public int getDistanceY() { - return yDistance; - } - - public int getDistanceZ() { - return zDistance; - } - - public int getSpeedX() { - return xSpeed; - } - - public int getSpeedY() { - return ySpeed; - } - - public int getSpeedZ() { - return zSpeed; - } - - public float getSpeedMs() { - double speed = Math.sqrt(Math.abs(xSpeed*xSpeed) + Math.abs(ySpeed*ySpeed)); - return (float)(speed / 100f); - } - - public int getSpeedKmh() { - return (int)(getSpeedMs() * 3.6); - } - - public int getAccelerationX() { - return xAcceleration; - } - - public int getAccelerationY() { - return yAcceleration; - } - - public int getAccelerationZ() { - return zAcceleration; - } - - public int getRollAngle() { - return rollAngle; - } - - public int getPitchAngle() { - return pitchAngle; - } - - public int getYawAngle() { - return yawAngle; - } - - public int getRollRate() { - return rollRate; - } - - public int getPitchRate() { - return pitchRate; - } - - public int getYawRate() { - return yawRate; - } - - public int getRollAcceleration() { - return rollAcceleration; - } - - public int getPitchAcceleration() { - return pitchAcceleration; - } - - public int getYawAcceleration() { - return yawAcceleration; - } - - public int getPlanarObjectDimension1() { - return planarObjectDimension1; - } - - public int getPlanarObjectDimension2() { - return planarObjectDimension2; - } - - public int getVerticalObjectDimension() { - return verticalObjectDimension; - } - - public int getLongitudinalLanePosition() { - return longitudinalLanePosition; - } - - public int getObject() { - return object; - } - - public static class PerceivedObjectConfidenceBuilder { - private int xDistance; - private int yDistance; - private int zDistance = UNKNOWN; - private int xSpeed; - private int ySpeed; - private int zSpeed = UNKNOWN; - private int xAcceleration = UNKNOWN; - private int yAcceleration = UNKNOWN; - private int zAcceleration = UNKNOWN; - private int rollAngle = UNKNOWN; - private int pitchAngle = UNKNOWN; - private int yawAngle = UNKNOWN; - private int rollRate = UNKNOWN; - private int pitchRate = UNKNOWN; - private int yawRate = UNKNOWN; - private int rollAcceleration = UNKNOWN; - private int pitchAcceleration = UNKNOWN; - private int yawAcceleration = UNKNOWN; - private int planarObjectDimension1 = UNKNOWN; - private int planarObjectDimension2 = UNKNOWN; - private int verticalObjectDimension = UNKNOWN; - private int longitudinalLanePosition = UNKNOWN; - private final int object; - - /** - * Start building a PerceivedObjectConfidence. - * - * @param object {@link PerceivedObjectConfidence#object} - */ - public PerceivedObjectConfidenceBuilder(int object) { - this.object = object; - } - - /** - * Sets the confidence of the distance between the sensor and the perceived object. - *

- * These fields are mandatory. - * - * @param xDistance {@link PerceivedObjectConfidence#xDistance} - * @param yDistance {@link PerceivedObjectConfidence#yDistance} - */ - public PerceivedObjectConfidenceBuilder distance(int xDistance, - int yDistance) { - this.xDistance = xDistance; - this.yDistance = yDistance; - return this; - } - - /** - * Sets the confidence of the distance between the sensor and the perceived object. - *

- * These fields are mandatory, except zDistance - use {@link #distance(int, int)} if not known. - * - * @param xDistance {@link PerceivedObjectConfidence#xDistance} - * @param yDistance {@link PerceivedObjectConfidence#yDistance} - * @param zDistance {@link PerceivedObjectConfidence#zDistance} - */ - public PerceivedObjectConfidenceBuilder distance(int xDistance, - int yDistance, - int zDistance) { - this.xDistance = xDistance; - this.yDistance = yDistance; - this.zDistance = zDistance; - return this; - } - - /** - * Sets the confidence of the speed of the perceived object. - *

- * These fields are mandatory. - * - * @param xSpeed {@link PerceivedObjectConfidence#xSpeed} - * @param ySpeed {@link PerceivedObjectConfidence#ySpeed} - */ - public PerceivedObjectConfidenceBuilder speed(int xSpeed, - int ySpeed) { - this.xSpeed = xSpeed; - this.ySpeed = ySpeed; - return this; - } - - /** - * Sets the confidence of the speed of the perceived object. - *

- * These fields are mandatory, except zSpeed - use {@link #speed(int, int)} if not known. - * - * @param xSpeed {@link PerceivedObjectConfidence#xSpeed} - * @param ySpeed {@link PerceivedObjectConfidence#ySpeed} - * @param zSpeed {@link PerceivedObjectConfidence#zSpeed} - */ - public PerceivedObjectConfidenceBuilder speed(int xSpeed, - int ySpeed, - int zSpeed) { - this.xSpeed = xSpeed; - this.ySpeed = ySpeed; - this.zSpeed = zSpeed; - return this; - } - - /** - * Sets the confidence of the acceleration of the perceived object. - *

- * These fields are optional. - * - * @param xAcceleration {@link PerceivedObjectConfidence#xAcceleration} - * @param yAcceleration {@link PerceivedObjectConfidence#yAcceleration} - * @param zAcceleration {@link PerceivedObjectConfidence#zAcceleration} - */ - public PerceivedObjectConfidenceBuilder acceleration(int xAcceleration, - int yAcceleration, - int zAcceleration) { - this.xAcceleration = xAcceleration; - this.yAcceleration = yAcceleration; - this.zAcceleration = zAcceleration; - return this; - } - - /** - * Sets the confidence of the angle of the perceived object. - *

- * These fields are optional. - * - * @param rollAngle {@link PerceivedObjectConfidence#rollAngle} - * @param pitchAngle {@link PerceivedObjectConfidence#pitchAngle} - * @param yawAngle {@link PerceivedObjectConfidence#yawAngle} - */ - public PerceivedObjectConfidenceBuilder angle(int rollAngle, - int pitchAngle, - int yawAngle) { - this.rollAngle = rollAngle; - this.pitchAngle = pitchAngle; - this.yawAngle = yawAngle; - return this; - } - - /** - * Sets the confidence of the angular rate of the perceived object. - *

- * These fields are optional. - * - * @param rollRate {@link PerceivedObjectConfidence#rollRate} - * @param pitchRate {@link PerceivedObjectConfidence#pitchRate} - * @param yawRate {@link PerceivedObjectConfidence#yawRate} - */ - public PerceivedObjectConfidenceBuilder angleRate(int rollRate, - int pitchRate, - int yawRate) { - this.rollRate = rollRate; - this.pitchRate = pitchRate; - this.yawRate = yawRate; - return this; - } - - /** - * Sets the confidence of the angular acceleration of the perceived object. - *

- * These fields are optional. - * - * @param rollAcceleration {@link PerceivedObjectConfidence#rollAcceleration} - * @param pitchAcceleration {@link PerceivedObjectConfidence#pitchAcceleration} - * @param yawAcceleration {@link PerceivedObjectConfidence#yawAcceleration} - */ - public PerceivedObjectConfidenceBuilder angleAcceleration(int rollAcceleration, - int pitchAcceleration, - int yawAcceleration) { - this.rollAcceleration = rollAcceleration; - this.pitchAcceleration = pitchAcceleration; - this.yawAcceleration = yawAcceleration; - return this; - } - - /** - * Sets the confidence of the dimensions of the perceived object. - *

- * These fields are optional. - * - * @param planarObjectDimension1 {@link PerceivedObjectConfidence#planarObjectDimension1} - * @param planarObjectDimension2 {@link PerceivedObjectConfidence#planarObjectDimension2} - * @param verticalObjectDimension {@link PerceivedObjectConfidence#verticalObjectDimension} - */ - public PerceivedObjectConfidenceBuilder objectDimension(int planarObjectDimension1, - int planarObjectDimension2, - int verticalObjectDimension) { - this.planarObjectDimension1 = planarObjectDimension1; - this.planarObjectDimension2 = planarObjectDimension2; - this.verticalObjectDimension = verticalObjectDimension; - return this; - } - - /** - * Sets the confidence of the lane position of the perceived object. - *

- * This field is optional. - * - * @param longitudinalLanePosition {@link PerceivedObjectConfidence#longitudinalLanePosition} - */ - public PerceivedObjectConfidenceBuilder longitudinalLanePosition(int longitudinalLanePosition) { - this.longitudinalLanePosition = longitudinalLanePosition; - return this; - } - - /** - * Build the perceived object confidence. - *

- * Call after setting all the mandatory fields. - * - * @return {@link #PerceivedObjectConfidence} - */ - public PerceivedObjectConfidence build() { - return new PerceivedObjectConfidence( - xDistance, - yDistance, - zDistance, - xSpeed, - ySpeed, - zSpeed, - xAcceleration, - yAcceleration, - zAcceleration, - rollAngle, - pitchAngle, - yawAngle, - rollRate, - pitchRate, - yawRate, - rollAcceleration, - pitchAcceleration, - yawAcceleration, - planarObjectDimension1, - planarObjectDimension2, - verticalObjectDimension, - longitudinalLanePosition, - object); - } - - } - - public static PerceivedObjectConfidence jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - try { - int xDistance = json.getInt(JsonCpmKey.ObjectDistance.X_DISTANCE.key()); - int yDistance = json.getInt(JsonCpmKey.ObjectDistance.Y_DISTANCE.key()); - int zDistance = json.optInt(JsonCpmKey.ObjectDistance.Z_DISTANCE.key(), UNKNOWN); - int xSpeed = json.getInt(JsonCpmKey.ObjectSpeed.X_SPEED.key()); - int ySpeed = json.getInt(JsonCpmKey.ObjectSpeed.Y_SPEED.key()); - int zSpeed = json.optInt(JsonCpmKey.ObjectSpeed.Z_SPEED.key(), UNKNOWN); - int xAcceleration = json.optInt(JsonCpmKey.ObjectAcceleration.X_ACCELERATION.key(), UNKNOWN); - int yAcceleration = json.optInt(JsonCpmKey.ObjectAcceleration.Y_ACCELERATION.key(), UNKNOWN); - int zAcceleration = json.optInt(JsonCpmKey.ObjectAcceleration.Z_ACCELERATION.key(), UNKNOWN); - int rollAngle = json.optInt(JsonCpmKey.ObjectAngle.ROLL_ANGLE.key(), UNKNOWN); - int pitchAngle = json.optInt(JsonCpmKey.ObjectAngle.PITCH_ANGLE.key(), UNKNOWN); - int yawAngle = json.optInt(JsonCpmKey.ObjectAngle.YAW_ANGLE.key(), UNKNOWN); - int rollRate = json.optInt(JsonCpmKey.ObjectSpeed.ROLL_RATE.key(), UNKNOWN); - int pitchRate = json.optInt(JsonCpmKey.ObjectSpeed.PITCH_RATE.key(), UNKNOWN); - int yawRate = json.optInt(JsonCpmKey.ObjectSpeed.YAW_RATE.key(), UNKNOWN); - int rollAcceleration = json.optInt(JsonCpmKey.ObjectAcceleration.ROLL_ACCELERATION.key(), UNKNOWN); - int pitchAcceleration = json.optInt(JsonCpmKey.ObjectAcceleration.PITCH_ACCELERATION.key(), UNKNOWN); - int yawAcceleration = json.optInt(JsonCpmKey.ObjectAcceleration.YAW_ACCELERATION.key(), UNKNOWN); - int planarObjectDimension1 = json.optInt(JsonCpmKey.ObjectDimension.PLANAR_DIMENSION_1.key(), UNKNOWN); - int planarObjectDimension2 = json.optInt(JsonCpmKey.ObjectDimension.PLANAR_DIMENSION_2.key(), UNKNOWN); - int verticalObjectDimension = json.optInt(JsonCpmKey.ObjectDimension.VERTICAL_DIMENSION.key(), UNKNOWN); - int longitudinalLanePosition = json.optInt(JsonCpmKey.ObjectLanePosition.LONGITUDINAL_LANE_POSITION.key(), UNKNOWN); - int object = json.getInt(JsonCpmKey.PerceivedObjectContainer.OBJECT.key()); - - return new PerceivedObjectConfidenceBuilder(object) - .distance( - xDistance, - yDistance, - zDistance) - .speed( - xSpeed, - ySpeed, - zSpeed) - .acceleration( - xAcceleration, - yAcceleration, - zAcceleration) - .angle( - rollAngle, - pitchAngle, - yawAngle) - .angleRate( - rollRate, - pitchRate, - yawRate) - .angleAcceleration( - rollAcceleration, - pitchAcceleration, - yawAcceleration) - .objectDimension( - planarObjectDimension1, - planarObjectDimension2, - verticalObjectDimension) - .longitudinalLanePosition(longitudinalLanePosition) - .build(); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM PerceivedObjectConfidence JSON parsing error", "Error: " + e); - } - return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/PerceivedObjectContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/PerceivedObjectContainer.java deleted file mode 100644 index 81a4e6891..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/PerceivedObjectContainer.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONArray; -import org.json.JSONException; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class PerceivedObjectContainer { - - private static final Logger LOGGER = Logger.getLogger(PerceivedObjectContainer.class.getName()); - - private final JSONArray jsonArray = new JSONArray(); - - /** - * List of perceived objects. - */ - private final List perceivedObjects; - - public PerceivedObjectContainer( - final List perceivedObjects - ) { - this.perceivedObjects = perceivedObjects; - - createJson(); - } - - private void createJson() { - for(PerceivedObject perceivedObject: perceivedObjects) { - jsonArray.put(perceivedObject.getJson()); - } - } - - public JSONArray getJson() { - return jsonArray; - } - - public List getPerceivedObjects() { - return perceivedObjects; - } - - public static PerceivedObjectContainer jsonParser(JSONArray jsonArray) { - if(JsonUtil.isNullOrEmpty(jsonArray)) return null; - try { - ArrayList perceivedObjects = new ArrayList<>(); - for (int i = 0; i < jsonArray.length(); i++) { - PerceivedObject perceivedObject = PerceivedObject.jsonParser(jsonArray.getJSONObject(i)); - perceivedObjects.add(perceivedObject); - } - - return new PerceivedObjectContainer(perceivedObjects); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM PerceivedObjectContainer JSON parsing error", "Error: " + e); - } - return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/SensorInformation.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/SensorInformation.java deleted file mode 100644 index 66bec36d9..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/SensorInformation.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class SensorInformation { - - private static final Logger LOGGER = Logger.getLogger(SensorInformation.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Sensor identifier. - */ - private final int sensorId; - - /** - * Type of attached sensor. - * - * undefined(0), radar(1), lidar(2), monovideo(3), stereovision(4), nightvision(5), - * ultrasonic(6), pmd(7), fusion(8), inductionloop(9), sphericalCamera(10), - * itssaggregation(11). - */ - private final int type; - - /** - * Area covered by the sensor. - */ - private final DetectionArea detectionArea; - - public SensorInformation( - final int sensorId, - final int type, - final DetectionArea detectionArea - ) throws IllegalArgumentException { - if(sensorId == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM SensorInformation sensorId is missing"); - } else if(CPM.isStrictMode() && (sensorId > 255 || sensorId < 0)) { - throw new IllegalArgumentException("CPM SensorInformation sensorId should be in the range of [0 - 255]." - + " Value: " + sensorId); - } - this.sensorId = sensorId; - if(type == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM SensorInformation type is missing"); - } else if(CPM.isStrictMode() && (type > 15 || type < 0)) { - throw new IllegalArgumentException("CPM SensorInformation type should be in the range of [0 - 15]." - + " Value: " + type); - } - this.type = type; - if(detectionArea == null && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM SensorInformation detectionArea is missing"); - } - this.detectionArea = detectionArea; - - createJson(); - } - - private void createJson() { - try { - json.put(JsonCpmKey.SensorInformationContainer.SENSOR_ID.key(), sensorId); - json.put(JsonCpmKey.SensorInformationContainer.TYPE.key(), type); - json.put(JsonCpmKey.SensorInformationContainer.DETECTION_AREA.key(), detectionArea.getJson()); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM SensorInformation JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public int getSensorId() { - return sensorId; - } - - public int getType() { - return type; - } - - public String getSensorType() { - return switch (type) { - case 1 -> "radar"; - case 2 -> "lidar"; - case 3 -> "mono video"; - case 4 -> "stereo vision"; - case 5 -> "night vision"; - case 6 -> "ultrasonic"; - case 7 -> "pmd"; - case 8 -> "fusion"; - case 9 -> "induction loop"; - case 10 -> "spherical camera"; - case 11 -> "its aggregation"; - default -> "undefined"; - }; - } - - public DetectionArea getDetectionArea() { - return detectionArea; - } - - public static SensorInformation jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - try { - int sensorId = json.getInt(JsonCpmKey.SensorInformationContainer.SENSOR_ID.key()); - int type = json.getInt(JsonCpmKey.SensorInformationContainer.TYPE.key()); - JSONObject jsonDetectionArea = json.getJSONObject(JsonCpmKey.SensorInformationContainer.DETECTION_AREA.key()); - DetectionArea detectionArea = DetectionArea.jsonParser(jsonDetectionArea); - - return new SensorInformation( - sensorId, - type, - detectionArea); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM SensorInformation JSON parsing error", "Error: " + e); - } - return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/SensorInformationContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/SensorInformationContainer.java deleted file mode 100644 index c4eba4474..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/SensorInformationContainer.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONArray; -import org.json.JSONException; - -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class SensorInformationContainer { - - private static final Logger LOGGER = Logger.getLogger(SensorInformationContainer.class.getName()); - - private final JSONArray jsonArray = new JSONArray(); - - /** - * List of sensors and their information. - */ - private final List sensorInformationList; - - public SensorInformationContainer( - final List sensorInformationList - ) { - this.sensorInformationList = sensorInformationList; - - createJson(); - } - - private void createJson() { - for(SensorInformation sensorInformation: sensorInformationList) { - jsonArray.put(sensorInformation.getJson()); - } - } - - public JSONArray getJson() { - return jsonArray; - } - - public List getSensorInformationList() { - return sensorInformationList; - } - - public List getSensorTypeList() { - ArrayList sensorTypeList = new ArrayList<>(); - for(SensorInformation sensorInformation: sensorInformationList) { - sensorTypeList.add(sensorInformation.getSensorType()); - } - return sensorTypeList; - } - - public static SensorInformationContainer jsonParser(JSONArray jsonArray) { - if(JsonUtil.isNullOrEmpty(jsonArray)) return null; - try { - ArrayList sensorInformationList = new ArrayList<>(); - for (int i = 0; i < jsonArray.length(); i++) { - SensorInformation sensorInformation = SensorInformation.jsonParser(jsonArray.getJSONObject(i)); - sensorInformationList.add(sensorInformation); - } - - return new SensorInformationContainer(sensorInformationList); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM SensorInformationContainer JSON parsing error", "Error: " + e); - } - return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationDataContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationDataContainer.java deleted file mode 100644 index eea9b22cd..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationDataContainer.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class StationDataContainer { - - private static final Logger LOGGER = Logger.getLogger(StationDataContainer.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Contains the description of the emitting ITS-station if it is a vehicle - */ - private final OriginatingVehicleContainer originatingVehicleContainer; - - /** - * Contains the description of the emitting ITS-station if it is a Road-Side Unit - */ - private final OriginatingRsuContainer originatingRsuContainer; - - public StationDataContainer( - final OriginatingVehicleContainer originatingVehicleContainer - ) { - this(originatingVehicleContainer, null); - } - - public StationDataContainer( - final OriginatingRsuContainer originatingRsuContainer - ) { - this(null, originatingRsuContainer); - } - - private StationDataContainer( - final OriginatingVehicleContainer originatingVehicleContainer, - final OriginatingRsuContainer originatingRsuContainer - ) { - this.originatingVehicleContainer = originatingVehicleContainer; - this.originatingRsuContainer = originatingRsuContainer; - - createJson(); - } - - public void createJson() { - try { - if(originatingVehicleContainer != null) - json.put(JsonCpmKey.StationDataContainer.ORIGINATING_VEHICLE_CONTAINER.key(), - originatingVehicleContainer.getJson()); - if(originatingRsuContainer != null) - json.put(JsonCpmKey.StationDataContainer.ORIGINATING_RSU_CONTAINER.key(), - originatingRsuContainer.getJson()); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM StationDataContainer JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public OriginatingVehicleContainer getOriginatingVehicleContainer() { - return originatingVehicleContainer; - } - - public OriginatingRsuContainer getOriginatingRsuContainer() { - return originatingRsuContainer; - } - - public static StationDataContainer jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - JSONObject jsonOriginatingVehicleContainer = json.optJSONObject(JsonCpmKey.StationDataContainer.ORIGINATING_VEHICLE_CONTAINER.key()); - OriginatingVehicleContainer originatingVehicleContainer = OriginatingVehicleContainer.jsonParser(jsonOriginatingVehicleContainer); - JSONObject jsonOriginatingRsuContainer = json.optJSONObject(JsonCpmKey.StationDataContainer.ORIGINATING_RSU_CONTAINER.key()); - OriginatingRsuContainer originatingRsuContainer = OriginatingRsuContainer.jsonParser(jsonOriginatingRsuContainer); - - return new StationDataContainer(originatingVehicleContainer, originatingRsuContainer); - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationarySensorCircular.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationarySensorCircular.java deleted file mode 100644 index 27bed23bd..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationarySensorCircular.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class StationarySensorCircular { - - private static final Logger LOGGER = Logger.getLogger(StationarySensorCircular.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Offset point about which the circle is centred with respect to the reference position. - */ - private final Offset nodeCenterPoint; - - /** - * Unit: 0.1 meter. The radius of the circular area. - * - * zeroPointZeroOneMeter(1), oneMeter(10). - */ - private final int radius; - - public StationarySensorCircular( - final int radius - ) throws IllegalArgumentException { - this(null, radius); - } - - public StationarySensorCircular( - final Offset nodeCenterPoint, - final int radius - ) throws IllegalArgumentException { - this.nodeCenterPoint = nodeCenterPoint; - if(radius == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM StationarySensorCircular radius is missing"); - } else if(CPM.isStrictMode() && (radius > 10000 || radius < 0)) { - throw new IllegalArgumentException("CPM StationarySensorRadial radius should be in the range of [0 - 10000]." - + " Value: " + radius); - } - this.radius = radius; - - createJson(); - } - - private void createJson() { - try { - if(nodeCenterPoint != null) - json.put(JsonCpmKey.StationarySensorCircular.NODE_CENTER_POINT.key(), nodeCenterPoint.getJson()); - json.put(JsonCpmKey.StationarySensorCircular.RADIUS.key(), radius); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM StationarySensorCircular JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public Offset getNodeCenterPoint() { - return nodeCenterPoint; - } - - public int getRadius() { - return radius; - } - - public static StationarySensorCircular jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - try { - JSONObject jsonNodeCenterPoint = json.optJSONObject(JsonCpmKey.StationarySensorCircular.NODE_CENTER_POINT.key()); - Offset nodeCenterPoint = Offset.jsonParser(jsonNodeCenterPoint); - int radius = json.getInt(JsonCpmKey.StationarySensorCircular.RADIUS.key()); - - return new StationarySensorCircular(nodeCenterPoint, radius); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM StationarySensorCircular JSON parsing error", "Error: " + e); - } - return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationarySensorEllipse.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationarySensorEllipse.java deleted file mode 100644 index c9de2b615..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationarySensorEllipse.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class StationarySensorEllipse { - - private static final Logger LOGGER = Logger.getLogger(StationarySensorEllipse.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Offset point about which the ellipse is centred with respect to the reference position. - */ - private final Offset nodeCenterPoint; - - /** - * Unit: 0.1 meter. Major radius of the ellipse. - * - * zeroPointOneMeter(1), oneMeter(10). - */ - private final int semiMajorRangeLength; - - /** - * Unit: 0.1 meter. Minor radius of the ellipse. - * - * zeroPointOneMeter(1), oneMeter(10). - */ - private final int semiMinorRangeLength; - - /** - * Unit: 0.1 degrees. Orientation of the semi major range length of the ellipse in the WGS84 - * coordinate system. - * - * wgs84North(0), wgs84East(900), wgs84South(1800), wgs84West(2700), unavailable(3601). - */ - private final int semiMajorRangeOrientation; - - /** - * Unit: 0.1 meter. - * - * zeroPointOneMeter(1), oneMeter(10). - */ - private final int semiHeight; - - public StationarySensorEllipse( - final Offset nodeCenterPoint, - final int semiMajorRangeLength, - final int semiMinorRangeLength, - final int semiMajorRangeOrientation, - final int semiHeight - ) throws IllegalArgumentException { - this.nodeCenterPoint = nodeCenterPoint; - if(semiMajorRangeLength == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM StationarySensorEllipse semiMajorRangeLength is missing"); - } else if(CPM.isStrictMode() && (semiMajorRangeLength > 10000 || semiMajorRangeLength < 0)) { - throw new IllegalArgumentException("CPM StationarySensorEllipse semiMajorRangeLength should be in the range of [0 - 10000]." - + " Value: " + semiMajorRangeLength); - } - this.semiMajorRangeLength = semiMajorRangeLength; - if(semiMinorRangeLength == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM StationarySensorEllipse semiMinorRangeLength is missing"); - } else if(CPM.isStrictMode() && (semiMinorRangeLength > 10000 || semiMinorRangeLength < 0)) { - throw new IllegalArgumentException("CPM StationarySensorEllipse semiMinorRangeLength should be in the range of [0 - 10000]." - + " Value: " + semiMinorRangeLength); - } - this.semiMinorRangeLength = semiMinorRangeLength; - if(semiMajorRangeOrientation == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM StationarySensorEllipse semiMajorRangeOrientation is missing"); - } else if(CPM.isStrictMode() && (semiMajorRangeOrientation > 3601 || semiMajorRangeOrientation < 0)) { - throw new IllegalArgumentException("CPM StationarySensorEllipse semiMajorRangeOrientation should be in the range of [0 - 3601]." - + " Value: " + semiMajorRangeOrientation); - } - this.semiMajorRangeOrientation = semiMajorRangeOrientation; - if(semiHeight != UNKNOWN && CPM.isStrictMode() - && (semiHeight > 10000 || semiHeight < 0)) { - throw new IllegalArgumentException("CPM StationarySensorEllipse semiHeight should be in the range of [0 - 10000]." - + " Value: " + semiHeight); - } - this.semiHeight = semiHeight; - - createJson(); - } - - private void createJson() { - try { - if(nodeCenterPoint != null) - json.put(JsonCpmKey.StationarySensorEllipseRect.NODE_CENTER_POINT.key(), nodeCenterPoint.getJson()); - json.put(JsonCpmKey.StationarySensorEllipseRect.SEMI_MAJOR_RANGE_LENGTH.key(), semiMajorRangeLength); - json.put(JsonCpmKey.StationarySensorEllipseRect.SEMI_MINOR_RANGE_LENGTH.key(), semiMinorRangeLength); - json.put(JsonCpmKey.StationarySensorEllipseRect.SEMI_MAJOR_RANGE_ORIENTATION.key(), semiMajorRangeOrientation); - if(semiHeight != UNKNOWN) - json.put(JsonCpmKey.StationarySensorEllipseRect.SEMI_HEIGHT.key(), semiHeight); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM StationarySensorEllipse JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public Offset getNodeCenterPoint() { - return nodeCenterPoint; - } - - public int getSemiMajorRangeLength() { - return semiMajorRangeLength; - } - - public int getSemiMinorRangeLength() { - return semiMinorRangeLength; - } - - public int getSemiMajorRangeOrientation() { - return semiMajorRangeOrientation; - } - - public int getSemiHeight() { - return semiHeight; - } - - public static StationarySensorEllipse jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - try { - JSONObject jsonNodeCenterPoint = json.optJSONObject(JsonCpmKey.StationarySensorEllipseRect.NODE_CENTER_POINT.key()); - Offset nodeCenterPoint = Offset.jsonParser(jsonNodeCenterPoint); - int semiMajorRangeLength = json.getInt(JsonCpmKey.StationarySensorEllipseRect.SEMI_MAJOR_RANGE_LENGTH.key()); - int semiMinorRangeLength = json.getInt(JsonCpmKey.StationarySensorEllipseRect.SEMI_MINOR_RANGE_LENGTH.key()); - int semiMajorRangeOrientation = json.getInt(JsonCpmKey.StationarySensorEllipseRect.SEMI_MAJOR_RANGE_ORIENTATION.key()); - int semiHeight = json.optInt(JsonCpmKey.StationarySensorEllipseRect.NODE_CENTER_POINT.key(), UNKNOWN); - - return new StationarySensorEllipse( - nodeCenterPoint, - semiMajorRangeLength, - semiMinorRangeLength, - semiMajorRangeOrientation, - semiHeight); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM StationarySensorEllipse JSON parsing error", "Error: " + e); - } - return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationarySensorPolygon.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationarySensorPolygon.java deleted file mode 100644 index 3ddc30c32..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationarySensorPolygon.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONArray; - -import java.util.ArrayList; -import java.util.logging.Logger; - -public class StationarySensorPolygon { - - private final JSONArray jsonArray = new JSONArray(); - - /** - * List of offset points forming the polygon, with respect to the reference position. - */ - private final ArrayList offsetPoints; - - public StationarySensorPolygon( - final ArrayList offsetPoints - ) { - this.offsetPoints = offsetPoints; - - createJson(); - } - - private void createJson() { - for(Offset offsetPoint: offsetPoints) { - jsonArray.put(offsetPoint.getJson()); - } - } - - public JSONArray getJson() { - return jsonArray; - } - - public ArrayList getOffsetPoints() { - return offsetPoints; - } - - public static StationarySensorPolygon jsonParser(JSONArray jsonArray) { - if(JsonUtil.isNullOrEmpty(jsonArray)) return null; - ArrayList offsetPoints = new ArrayList<>(); - for (int i = 0; i < jsonArray.length(); i++) { - Offset offsetPoint = Offset.jsonParser(jsonArray.optJSONObject(i)); - offsetPoints.add(offsetPoint); - } - - return new StationarySensorPolygon(offsetPoints); - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationarySensorRadial.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationarySensorRadial.java deleted file mode 100644 index e15a2d4b2..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationarySensorRadial.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class StationarySensorRadial { - - private static final Logger LOGGER = Logger.getLogger(StationarySensorRadial.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Unit: 0.1 meter. The radial range of the sensor from the reference point or sensor point - * offset. - * - * zeroPointOneMeter(1), oneMeter(10). - */ - private final int range; - - /** - * Unit: 0.1 degrees. The orientation indicating the start of the stationary sensor’s - * horizontal opening angle in positive angular direction with respect to the WGS84 coordinate - * system. - * - * wgs84North(0), wgs84East(900), wgs84South(1800), wgs84West(2700), unavailable (3601). - */ - private final int horizontalOpeningAngleStart; - - /** - * Unit: 0.1 degrees. The orientation indicating the end of the stationary sensor’s - * horizontal opening angle in positive angular direction with respect to the WGS84 coordinate - * system. - * - * wgs84North(0), wgs84East(900), wgs84South(1800), wgs84West(2700), unavailable (3601). - */ - private final int horizontalOpeningAngleEnd; - - /** - * Unit: 0.1 degrees. The orientation indicating the start of the stationary sensor’s - * vertical opening angle in positive angular direction of a Cartesian coordinate system - * with its x-axis located in the north-east plane of the WGS84 coordinate system. - * - * zeroPointOneDegree(1), oneDegree(10), unavailable(3601). - */ - private final int verticalOpeningAngleStart; - - /** - * Unit: 0.1 degrees. The orientation indicating the end of the stationary sensor’s - * vertical opening angle in positive angular direction of a Cartesian coordinate system - * with its x-axis located in the north-east plane of the WGS84 coordinate system. - * - * zeroPointOneDegree(1), oneDegree(10), unavailable(3601). - */ - private final int verticalOpeningAngleEnd; - - /** - * The offset of the mounting point of this sensor from the station's reference position. - */ - private final Offset sensorPositionOffset; - - public StationarySensorRadial( - final int range, - final int horizontalOpeningAngleStart, - final int horizontalOpeningAngleEnd, - final int verticalOpeningAngleStart, - final int verticalOpeningAngleEnd, - final Offset sensorPositionOffset - ) throws IllegalArgumentException { - if(range == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM StationarySensorRadial range is missing"); - } else if(CPM.isStrictMode() && (range > 10000 || range < 0)) { - throw new IllegalArgumentException("CPM StationarySensorRadial range should be in the range of [0 - 10000]." - + " Value: " + range); - } - this.range = range; - if(horizontalOpeningAngleStart == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM StationarySensorRadial horizontalOpeningAngleStart is missing"); - } else if(CPM.isStrictMode() && (horizontalOpeningAngleStart > 3601 || horizontalOpeningAngleStart < 0)) { - throw new IllegalArgumentException("CPM StationarySensorRadial horizontalOpeningAngleStart should be in the range of [0 - 3601]." - + " Value: " + horizontalOpeningAngleStart); - } - this.horizontalOpeningAngleStart = horizontalOpeningAngleStart; - if(horizontalOpeningAngleEnd == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM StationarySensorRadial horizontalOpeningAngleEnd is missing"); - } else if(CPM.isStrictMode() && (horizontalOpeningAngleEnd > 3601 || horizontalOpeningAngleEnd < 0)) { - throw new IllegalArgumentException("CPM StationarySensorRadial horizontalOpeningAngleEnd should be in the range of [0 - 3601]." - + " Value: " + horizontalOpeningAngleEnd); - } - this.horizontalOpeningAngleEnd = horizontalOpeningAngleEnd; - if(verticalOpeningAngleStart != UNKNOWN && CPM.isStrictMode() - && (verticalOpeningAngleStart > 3601 || verticalOpeningAngleStart < 0)) { - throw new IllegalArgumentException("CPM StationarySensorRadial verticalOpeningAngleStart should be in the range of [0 - 3601]." - + " Value: " + verticalOpeningAngleStart); - } - this.verticalOpeningAngleStart = verticalOpeningAngleStart; - if(verticalOpeningAngleEnd != UNKNOWN && CPM.isStrictMode() - && (verticalOpeningAngleEnd > 3601 || verticalOpeningAngleEnd < 0)) { - throw new IllegalArgumentException("CPM StationarySensorRadial verticalOpeningAngleEnd should be in the range of [0 - 3601]." - + " Value: " + verticalOpeningAngleEnd); - } - this.verticalOpeningAngleEnd = verticalOpeningAngleEnd; - this.sensorPositionOffset = sensorPositionOffset; - - createJson(); - } - - private void createJson() { - try { - json.put(JsonCpmKey.StationarySensorRadial.RANGE.key(), range); - json.put(JsonCpmKey.StationarySensorRadial.HORIZONTAL_OPENING_ANGLE_START.key(), horizontalOpeningAngleStart); - json.put(JsonCpmKey.StationarySensorRadial.HORIZONTAL_OPENING_ANGLE_END.key(), horizontalOpeningAngleEnd); - if(verticalOpeningAngleStart != UNKNOWN) - json.put(JsonCpmKey.StationarySensorRadial.VERTICAL_OPENING_ANGLE_START.key(), verticalOpeningAngleStart); - if(verticalOpeningAngleEnd != UNKNOWN) - json.put(JsonCpmKey.StationarySensorRadial.VERTICAL_OPENING_ANGLE_END.key(), verticalOpeningAngleEnd); - if(sensorPositionOffset != null) - json.put(JsonCpmKey.StationarySensorRadial.SENSOR_POSITION_OFFSET.key(), sensorPositionOffset.getJson()); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM StationarySensorRadial JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public int getRange() { - return range; - } - - public int getHorizontalOpeningAngleStart() { - return horizontalOpeningAngleStart; - } - - public int getHorizontalOpeningAngleEnd() { - return horizontalOpeningAngleEnd; - } - - public int getVerticalOpeningAngleStart() { - return verticalOpeningAngleStart; - } - - public int getVerticalOpeningAngleEnd() { - return verticalOpeningAngleEnd; - } - - public Offset getSensorPositionOffset() { - return sensorPositionOffset; - } - - public static StationarySensorRadial jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - try { - int range = json.getInt(JsonCpmKey.StationarySensorRadial.RANGE.key()); - int horizontalOpeningAngleStart = json.getInt(JsonCpmKey.StationarySensorRadial.HORIZONTAL_OPENING_ANGLE_START.key()); - int horizontalOpeningAngleEnd = json.getInt(JsonCpmKey.StationarySensorRadial.HORIZONTAL_OPENING_ANGLE_END.key()); - int verticalOpeningAngleStart = json.optInt(JsonCpmKey.StationarySensorRadial.VERTICAL_OPENING_ANGLE_START.key(), UNKNOWN); - int verticalOpeningAngleEnd = json.optInt(JsonCpmKey.StationarySensorRadial.VERTICAL_OPENING_ANGLE_END.key(), UNKNOWN); - JSONObject jsonSensorPositionOffset = json.optJSONObject(JsonCpmKey.StationarySensorRadial.SENSOR_POSITION_OFFSET.key()); - Offset offset = Offset.jsonParser(jsonSensorPositionOffset); - - return new StationarySensorRadial( - range, - horizontalOpeningAngleStart, - horizontalOpeningAngleEnd, - verticalOpeningAngleStart, - verticalOpeningAngleEnd, - offset); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM StationarySensorRadial JSON parsing error", "Error: " + e); - } - return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationarySensorRectangle.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationarySensorRectangle.java deleted file mode 100644 index a1579cf40..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/StationarySensorRectangle.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class StationarySensorRectangle { - - private static final Logger LOGGER = Logger.getLogger(StationarySensorRectangle.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Offset point about which the rectangle is centred with respect to the reference position. - */ - private final Offset nodeCenterPoint; - - /** - * Unit: 0.1 meter. Half length of the rectangle. - * - * zeroPointOneMeter(1), oneMeter(10). - */ - private final int semiMajorRangeLength; - - /** - * Unit: 0.1 meter. Half width of the rectangle. - * - * zeroPointOneMeter(1), oneMeter(10). - */ - private final int semiMinorRangeLength; - - /** - * Unit: 0.1 degrees.Orientation of the semi major range length of the rectangle in the WGS84 - * coordinate system. - * - * wgs84North(0), wgs84East(900), wgs84South(1800), wgs84West(2700), unavailable(3601). - */ - private final int semiMajorRangeOrientation; - - /** - * Unit: 0.1 meter. - * - * zeroPointOneMeter(1), oneMeter(10). - */ - private final int semiHeight; - - public StationarySensorRectangle( - final Offset nodeCenterPoint, - final int semiMajorRangeLength, - final int semiMinorRangeLength, - final int semiMajorRangeOrientation, - final int semiHeight - ) throws IllegalArgumentException { - this.nodeCenterPoint = nodeCenterPoint; - if(semiMajorRangeLength == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM StationarySensorEllipse semiMajorRangeLength is missing"); - } else if(CPM.isStrictMode() && (semiMajorRangeLength > 10000 || semiMajorRangeLength < 0)) { - throw new IllegalArgumentException("CPM StationarySensorEllipse semiMajorRangeLength should be in the range of [0 - 10000]." - + " Value: " + semiMajorRangeLength); - } - this.semiMajorRangeLength = semiMajorRangeLength; - if(semiMinorRangeLength == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM StationarySensorEllipse semiMinorRangeLength is missing"); - } else if(CPM.isStrictMode() && (semiMinorRangeLength > 10000 || semiMinorRangeLength < 0)) { - throw new IllegalArgumentException("CPM StationarySensorEllipse semiMinorRangeLength should be in the range of [0 - 10000]." - + " Value: " + semiMinorRangeLength); - } - this.semiMinorRangeLength = semiMinorRangeLength; - if(semiMajorRangeOrientation == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM StationarySensorEllipse semiMajorRangeOrientation is missing"); - } else if(CPM.isStrictMode() && (semiMajorRangeOrientation > 3601 || semiMajorRangeOrientation < 0)) { - throw new IllegalArgumentException("CPM StationarySensorEllipse semiMajorRangeOrientation should be in the range of [0 - 3601]." - + " Value: " + semiMajorRangeOrientation); - } - this.semiMajorRangeOrientation = semiMajorRangeOrientation; - if(semiHeight != UNKNOWN && CPM.isStrictMode() - && (semiHeight > 10000 || semiHeight < 0)) { - throw new IllegalArgumentException("CPM StationarySensorEllipse semiHeight should be in the range of [0 - 10000]." - + " Value: " + semiHeight); - } - this.semiHeight = semiHeight; - - createJson(); - } - - private void createJson() { - try { - if(nodeCenterPoint != null) - json.put(JsonCpmKey.StationarySensorEllipseRect.NODE_CENTER_POINT.key(), nodeCenterPoint.getJson()); - json.put(JsonCpmKey.StationarySensorEllipseRect.SEMI_MAJOR_RANGE_LENGTH.key(), semiMajorRangeLength); - json.put(JsonCpmKey.StationarySensorEllipseRect.SEMI_MINOR_RANGE_LENGTH.key(), semiMinorRangeLength); - json.put(JsonCpmKey.StationarySensorEllipseRect.SEMI_MAJOR_RANGE_ORIENTATION.key(), semiMajorRangeOrientation); - if(semiHeight != UNKNOWN) - json.put(JsonCpmKey.StationarySensorEllipseRect.SEMI_HEIGHT.key(), semiHeight); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM StationarySensorRectangle JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public Offset getNodeCenterPoint() { - return nodeCenterPoint; - } - - public int getSemiMajorRangeLength() { - return semiMajorRangeLength; - } - - public int getSemiMinorRangeLength() { - return semiMinorRangeLength; - } - - public int getSemiMajorRangeOrientation() { - return semiMajorRangeOrientation; - } - - public int getSemiHeight() { - return semiHeight; - } - - public static StationarySensorRectangle jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - try { - JSONObject jsonNodeCenterPoint = json.optJSONObject(JsonCpmKey.StationarySensorEllipseRect.NODE_CENTER_POINT.key()); - Offset nodeCenterPoint = Offset.jsonParser(jsonNodeCenterPoint); - int semiMajorRangeLength = json.getInt(JsonCpmKey.StationarySensorEllipseRect.SEMI_MAJOR_RANGE_LENGTH.key()); - int semiMinorRangeLength = json.getInt(JsonCpmKey.StationarySensorEllipseRect.SEMI_MINOR_RANGE_LENGTH.key()); - int semiMajorRangeOrientation = json.getInt(JsonCpmKey.StationarySensorEllipseRect.SEMI_MAJOR_RANGE_ORIENTATION.key()); - int semiHeight = json.optInt(JsonCpmKey.StationarySensorEllipseRect.NODE_CENTER_POINT.key(), UNKNOWN); - - return new StationarySensorRectangle( - nodeCenterPoint, - semiMajorRangeLength, - semiMinorRangeLength, - semiMajorRangeOrientation, - semiHeight); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM StationarySensorRectangle JSON parsing error", "Error: " + e); - } - return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/VehicleSensor.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/VehicleSensor.java deleted file mode 100644 index c64de29f6..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/VehicleSensor.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class VehicleSensor { - - private static final Logger LOGGER = Logger.getLogger(VehicleSensor.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Increasing counter of the trailer reference point (corresponding to the hitch point). - */ - private final int refPointId; - - /** - * Unit: 0.01 meter. Mounting position of sensor in negative x-direction from Reference Point - * indicated by the ref point id. - * - * negativeZeroPointZeroOneMeter(-1), negativeOneMeter(-100), negativeOutOfRange(-3094), - * positiveOneMeter(100),positiveOutOfRange(1001). - */ - private final int xSensorOffset; - - /** - * Unit: 0.01 meter. Mounting position of sensor in y-direction from Reference Point - * indicated by the ref point id. - * - * zeroPointZeroOneMeter(1), oneMeter(100). - */ - private final int ySensorOffset; - - /** - * Unit: 0.01 meter. Mounting position of sensor in z-direction from Reference Point - * indicated by the ref point id. - * - * zeroPointZeroOneMeter(1), oneMeter(100). - */ - private final int zSensorOffset; - - /** - * List of information for individual sensor(s) which are mounted to a vehicle. - */ - private final ArrayList vehicleSensorPropertyList; - - public VehicleSensor( - final int refPointId, - final int xSensorOffset, - final int ySensorOffset, - final int zSensorOffset, - final ArrayList vehicleSensorPropertyList - ) throws IllegalArgumentException { - if(refPointId == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM VehicleSensor refPointId is missing"); - } else if(CPM.isStrictMode() && (refPointId > 255 || refPointId < 0)) { - throw new IllegalArgumentException("CPM VehicleSensor refPointId should be in the range of [0 - 255]." - + " Value: " + refPointId); - } - this.refPointId = refPointId; - if(xSensorOffset == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM VehicleSensor xSensorOffset is missing"); - } else if(CPM.isStrictMode() && (xSensorOffset > 0 || xSensorOffset < -5000)) { - throw new IllegalArgumentException("CPM VehicleSensor xSensorOffset should be in the range of [-5000 - 0]." - + " Value: " + xSensorOffset); - } - this.xSensorOffset = xSensorOffset; - if(ySensorOffset == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM VehicleSensor ySensorOffset is missing"); - } else if(CPM.isStrictMode() && (ySensorOffset > 1000 || ySensorOffset < -1000)) { - throw new IllegalArgumentException("CPM VehicleSensor ySensorOffset should be in the range of [-1000 - 1000]." - + " Value: " + ySensorOffset); - } - this.ySensorOffset = ySensorOffset; - if(zSensorOffset != UNKNOWN && CPM.isStrictMode() && (zSensorOffset > 1000 || zSensorOffset < 0)) { - throw new IllegalArgumentException("CPM VehicleSensor zSensorOffset should be in the range of [0 - 1000]." - + " Value: " + zSensorOffset); - } - this.zSensorOffset = zSensorOffset; - if(vehicleSensorPropertyList == null && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM VehicleSensor vehicleSensorPropertyList is missing"); - } - this.vehicleSensorPropertyList = vehicleSensorPropertyList; - - createJson(); - } - - private void createJson() { - try { - json.put(JsonCpmKey.VehicleSensor.REF_POINT_ID.key(), refPointId); - json.put(JsonCpmKey.VehicleSensor.X_SENSOR_OFFSET.key(), xSensorOffset); - json.put(JsonCpmKey.VehicleSensor.Y_SENSOR_OFFSET.key(), ySensorOffset); - if(zSensorOffset != UNKNOWN) - json.put(JsonCpmKey.VehicleSensor.Z_SENSOR_OFFSET.key(), zSensorOffset); - JSONArray jsonVehicleSensorPropertyArray = new JSONArray(); - for(VehicleSensorProperty vehicleSensorProperty: vehicleSensorPropertyList) { - jsonVehicleSensorPropertyArray.put(vehicleSensorProperty.getJson()); - } - json.put(JsonCpmKey.VehicleSensor.VEHICLE_SENSOR_PROPERTY_LIST.key(), jsonVehicleSensorPropertyArray); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM VehicleSensor JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public int getRefPointId() { - return refPointId; - } - - public int getxSensorOffset() { - return xSensorOffset; - } - - public int getySensorOffset() { - return ySensorOffset; - } - - public int getzSensorOffset() { - return zSensorOffset; - } - - public ArrayList getVehicleSensorPropertyList() { - return vehicleSensorPropertyList; - } - - public static VehicleSensor jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - try { - int refPointId = json.getInt(JsonCpmKey.VehicleSensor.REF_POINT_ID.key()); - int xSensorOffset = json.getInt(JsonCpmKey.VehicleSensor.X_SENSOR_OFFSET.key()); - int ySensorOffset = json.getInt(JsonCpmKey.VehicleSensor.Y_SENSOR_OFFSET.key()); - int zSensorOffset = json.optInt(JsonCpmKey.VehicleSensor.Z_SENSOR_OFFSET.key(), UNKNOWN); - ArrayList vehicleSensorPropertyList = new ArrayList<>(); - JSONArray jsonVehicleSensorPropertyArray = json.getJSONArray(JsonCpmKey.VehicleSensor.VEHICLE_SENSOR_PROPERTY_LIST.key()); - for(int i = 0; i < jsonVehicleSensorPropertyArray.length(); i++) { - VehicleSensorProperty vehicleSensorProperty = VehicleSensorProperty.jsonParser(jsonVehicleSensorPropertyArray.getJSONObject(i)); - vehicleSensorPropertyList.add(vehicleSensorProperty); - } - - return new VehicleSensor(refPointId, - xSensorOffset, - ySensorOffset, - zSensorOffset, - vehicleSensorPropertyList); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM VehicleSensor JSON parsing error", "Error: " + e); - } - return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/VehicleSensorProperty.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/VehicleSensorProperty.java deleted file mode 100644 index b10732483..000000000 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/its/json/cpm/VehicleSensorProperty.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - Copyright 2016-2024 Orange - - This software is distributed under the MIT license, see LICENSE.txt file for more details. - - @author Mathieu LEFEBVRE - */ -package com.orange.iot3mobility.its.json.cpm; - -import static com.orange.iot3mobility.its.json.JsonUtil.UNKNOWN; - -import com.orange.iot3mobility.its.json.JsonUtil; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class VehicleSensorProperty { - - private static final Logger LOGGER = Logger.getLogger(VehicleSensorProperty.class.getName()); - - private final JSONObject json = new JSONObject(); - - /** - * Unit: 0.1 meter. Range of sensor within the indicated azimuth angle defined by the start - * and end opening angle. - * - * zeroPointOneMeter(1), oneMeter(10). - */ - private final int range; - - /** - * Unit: 0.1 degrees. Start of the sensor's horizontal opening angle extension relative to the - * body of the vehicle. - * - * The value is provided with respect to a body-fixed coordinate system according to the - * ISO 8855 [i.2] specification with angles counted positive in the counter-clockwise direction - * starting from the X-axis. - * - * The opening angle always extends from the horizontal opening angle start to horizontal - * opening angle end in counter-clockwise direction. - * - * zeroPointOneDegree(1), oneDegree(10), unavailable(3601). - */ - private final int horizontalOpeningAngleStart; - - /** - * Unit: 0.1 degrees. End of the sensor's horizontal opening angle extension relative to the - * body of the vehicle. - * - * The value is provided with respect to a body-fixed coordinate system according to the - * ISO 8855 [i.2] specification with angles counted positive in the counter-clockwise direction - * starting from the X-axis. - * - * The opening angle always extends from the horizontal opening angle start to horizontal - * opening angle end in counter-clockwise direction. - * - * zeroPointOneDegree(1), oneDegree(10), unavailable(3601). - */ - private final int horizontalOpeningAngleEnd; - - /** - * Unit: 0.1 degrees. Start of the sensor's vertical opening angle extension. - * - * The angle refers to a rotation about the y-axis of a sensor-specific coordinate system with - * its origin located at the location defined by the offset. - * - * The x-axis of the sensor's coordinate system points in the direction of half of the - * horizontal opening angle. - * - * zeroPointOneDegree(1), oneDegree(10), unavailable(3601). - */ - private final int verticalOpeningAngleStart; - - /** - * Unit: 0.1 degrees. End of the sensor's vertical opening angle extension. - * - * The angle refers to a rotation about the y-axis of a sensor-specific coordinate system with - * its origin located at the location defined by the offset. - * - * The X-axis of the sensor's coordinate system points in the direction of half of the - * horizontal opening angle. - * - * zeroPointOneDegree(1), oneDegree(10), unavailable(3601). - */ - private final int verticalOpeningAngleEnd; - - public VehicleSensorProperty( - final int range, - final int horizontalOpeningAngleStart, - final int horizontalOpeningAngleEnd, - final int verticalOpeningAngleStart, - final int verticalOpeningAngleEnd - ) throws IllegalArgumentException { - if(range == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM VehicleSensorProperty range is missing"); - } else if(CPM.isStrictMode() && (range > 10000 || range < 0)) { - throw new IllegalArgumentException("CPM VehicleSensorProperty range should be in the range of [0 - 10000]." - + " Value: " + range); - } - this.range = range; - if(horizontalOpeningAngleStart == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM VehicleSensorProperty horizontalOpeningAngleStart is missing"); - } else if(CPM.isStrictMode() && (horizontalOpeningAngleStart > 3601 || horizontalOpeningAngleStart < 0)) { - throw new IllegalArgumentException("CPM VehicleSensorProperty horizontalOpeningAngleStart should be in the range of [0 - 3601]." - + " Value: " + horizontalOpeningAngleStart); - } - this.horizontalOpeningAngleStart = horizontalOpeningAngleStart; - if(horizontalOpeningAngleEnd == UNKNOWN && CPM.isStrictMode()) { - throw new IllegalArgumentException("CPM VehicleSensorProperty horizontalOpeningAngleEnd is missing"); - } else if(CPM.isStrictMode() && (horizontalOpeningAngleEnd > 3601 || horizontalOpeningAngleEnd < 0)) { - throw new IllegalArgumentException("CPM VehicleSensorProperty horizontalOpeningAngleEnd should be in the range of [0 - 3601]." - + " Value: " + horizontalOpeningAngleEnd); - } - this.horizontalOpeningAngleEnd = horizontalOpeningAngleEnd; - if(verticalOpeningAngleStart != UNKNOWN && CPM.isStrictMode() - && (verticalOpeningAngleStart > 3601 || verticalOpeningAngleStart < 0)) { - throw new IllegalArgumentException("CPM VehicleSensorProperty verticalOpeningAngleStart should be in the range of [0 - 3601]." - + " Value: " + verticalOpeningAngleStart); - } - this.verticalOpeningAngleStart = verticalOpeningAngleStart; - if(verticalOpeningAngleEnd != UNKNOWN && CPM.isStrictMode() - && (verticalOpeningAngleEnd > 3601 || verticalOpeningAngleEnd < 0)) { - throw new IllegalArgumentException("CPM VehicleSensorProperty verticalOpeningAngleEnd should be in the range of [0 - 3601]." - + " Value: " + verticalOpeningAngleEnd); - } - this.verticalOpeningAngleEnd = verticalOpeningAngleEnd; - - createJson(); - } - - private void createJson() { - try { - json.put(JsonCpmKey.VehicleSensorProperty.RANGE.key(), range); - json.put(JsonCpmKey.VehicleSensorProperty.HORIZONTAL_OPENING_ANGLE_START.key(), horizontalOpeningAngleStart); - json.put(JsonCpmKey.VehicleSensorProperty.HORIZONTAL_OPENING_ANGLE_END.key(), horizontalOpeningAngleEnd); - if(verticalOpeningAngleStart != UNKNOWN) - json.put(JsonCpmKey.VehicleSensorProperty.VERTICAL_OPENING_ANGLE_START.key(), verticalOpeningAngleStart); - if(verticalOpeningAngleEnd != UNKNOWN) - json.put(JsonCpmKey.VehicleSensorProperty.VERTICAL_OPENING_ANGLE_END.key(), verticalOpeningAngleEnd); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM VehicleSensorProperty JSON build error", "Error: " + e); - } - } - - public JSONObject getJson() { - return json; - } - - public int getRange() { - return range; - } - - public int getHorizontalOpeningAngleStart() { - return horizontalOpeningAngleStart; - } - - public int getHorizontalOpeningAngleEnd() { - return horizontalOpeningAngleEnd; - } - - public int getVerticalOpeningAngleStart() { - return verticalOpeningAngleStart; - } - - public int getVerticalOpeningAngleEnd() { - return verticalOpeningAngleEnd; - } - - public static VehicleSensorProperty jsonParser(JSONObject json) { - if(JsonUtil.isNullOrEmpty(json)) return null; - try { - int range = json.getInt(JsonCpmKey.VehicleSensorProperty.RANGE.key()); - int horizontalOpeningAngleStart = json.getInt(JsonCpmKey.VehicleSensorProperty.HORIZONTAL_OPENING_ANGLE_START.key()); - int horizontalOpeningAngleEnd = json.getInt(JsonCpmKey.VehicleSensorProperty.HORIZONTAL_OPENING_ANGLE_END.key()); - int verticalOpeningAngleStart = json.optInt(JsonCpmKey.VehicleSensorProperty.VERTICAL_OPENING_ANGLE_START.key(), UNKNOWN); - int verticalOpeningAngleEnd = json.optInt(JsonCpmKey.VehicleSensorProperty.VERTICAL_OPENING_ANGLE_END.key(), UNKNOWN); - - return new VehicleSensorProperty( - range, - horizontalOpeningAngleStart, - horizontalOpeningAngleEnd, - verticalOpeningAngleStart, - verticalOpeningAngleEnd); - } catch (JSONException e) { - LOGGER.log(Level.WARNING, "CPM VehicleSensorProperty JSON parsing error", "Error: " + e); - } - return null; - } - -} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/managers/IoT3RoadSensorCallback.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/managers/IoT3RoadSensorCallback.java index 7a3b90078..f4e2e2e31 100644 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/managers/IoT3RoadSensorCallback.java +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/managers/IoT3RoadSensorCallback.java @@ -7,7 +7,7 @@ */ package com.orange.iot3mobility.managers; -import com.orange.iot3mobility.its.json.cpm.CPM; +import com.orange.iot3mobility.messages.cpm.core.CpmCodec; import com.orange.iot3mobility.roadobjects.RoadSensor; import com.orange.iot3mobility.roadobjects.SensorObject; @@ -25,6 +25,6 @@ public interface IoT3RoadSensorCallback { void sensorObjectExpired(SensorObject sensorObject); - void cpmArrived(CPM cpm); + void cpmArrived(CpmCodec.CpmFrame cpmFrame); } diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/managers/RoadSensorManager.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/managers/RoadSensorManager.java index 58914ca17..cc20b991e 100644 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/managers/RoadSensorManager.java +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/managers/RoadSensorManager.java @@ -7,13 +7,18 @@ */ package com.orange.iot3mobility.managers; -import com.orange.iot3mobility.its.json.cpm.CPM; +import com.orange.iot3mobility.messages.EtsiConverter; +import com.orange.iot3mobility.messages.cpm.CpmHelper; +import com.orange.iot3mobility.messages.cpm.core.CpmCodec; +import com.orange.iot3mobility.messages.cpm.core.CpmVersion; +import com.orange.iot3mobility.messages.cpm.v121.model.CpmEnvelope121; +import com.orange.iot3mobility.messages.cpm.v121.model.CpmMessage121; +import com.orange.iot3mobility.messages.cpm.v211.model.CpmEnvelope211; +import com.orange.iot3mobility.messages.cpm.v211.model.CpmMessage211; import com.orange.iot3mobility.quadkey.LatLng; import com.orange.iot3mobility.roadobjects.RoadSensor; -import org.json.JSONException; -import org.json.JSONObject; - +import java.io.IOException; import java.util.*; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -38,44 +43,55 @@ public static void init(IoT3RoadSensorCallback ioT3RoadSensorCallback) { startExpirationCheck(); } - public static void processCpm(String message) { + public static void processCpm(String message, CpmHelper cpmHelper) { if(ioT3RoadSensorCallback != null) { try { - CPM cpm = CPM.jsonParser(new JSONObject(message)); - if(cpm == null) { - LOGGER.log(Level.WARNING, TAG, "CPM parsing returned null"); - } else { - ioT3RoadSensorCallback.cpmArrived(cpm); - //associate the received CPM to a RoadSensor object - String uuid = cpm.getSourceUuid() + "_" + cpm.getStationId(); - LatLng position = new LatLng( - cpm.getManagementContainer().getReferencePosition().getLatitudeDegree(), - cpm.getManagementContainer().getReferencePosition().getLongitudeDegree()); - if(ROAD_SENSOR_MAP.containsKey(uuid)) { - synchronized (ROAD_SENSOR_MAP) { - RoadSensor roadSensor = ROAD_SENSOR_MAP.get(uuid); - if(roadSensor != null) { - roadSensor.updateTimestamp(); - roadSensor.setCpm(cpm); - if(cpm.getPerceivedObjectContainer() != null) - roadSensor.updateSensorObjects(cpm.getPerceivedObjectContainer().getPerceivedObjects()); - ioT3RoadSensorCallback.roadSensorUpdate(roadSensor); - } - } - } else { - RoadSensor roadSensor = new RoadSensor(uuid, position, cpm, ioT3RoadSensorCallback); - if(cpm.getPerceivedObjectContainer() != null) - roadSensor.updateSensorObjects(cpm.getPerceivedObjectContainer().getPerceivedObjects()); - addRoadSensor(uuid, roadSensor); - ioT3RoadSensorCallback.newRoadSensor(roadSensor); - } + CpmCodec.CpmFrame cpmFrame = cpmHelper.parse(message); + ioT3RoadSensorCallback.cpmArrived(cpmFrame); + + String uuid; + LatLng position; + + if(cpmFrame.version() == CpmVersion.V1_2_1) { + CpmEnvelope121 cpmEnvelope121 = (CpmEnvelope121) cpmFrame.envelope(); + CpmMessage121 cpm121 = cpmEnvelope121.message(); + uuid = cpmEnvelope121.sourceUuid() + "_" + cpm121.stationId(); + position = new LatLng( + EtsiConverter.latitudeDegrees(cpm121.managementContainer().referencePosition().latitude()), + EtsiConverter.longitudeDegrees(cpm121.managementContainer().referencePosition().longitude())); + updateRoadSensor(uuid, position, cpmFrame); + } else if(cpmFrame.version() == CpmVersion.V2_1_1) { + CpmEnvelope211 cpmEnvelope211 = (CpmEnvelope211) cpmFrame.envelope(); + CpmMessage211 cpm211 = cpmEnvelope211.message(); + uuid = cpmEnvelope211.sourceUuid() + "_" + cpm211.stationId(); + position = new LatLng( + EtsiConverter.latitudeDegrees(cpm211.managementContainer().referencePosition().latitude()), + EtsiConverter.longitudeDegrees(cpm211.managementContainer().referencePosition().longitude())); + updateRoadSensor(uuid, position, cpmFrame); } - } catch (JSONException e) { + } catch (IOException e) { LOGGER.log(Level.WARNING, TAG, "CPM parsing error: " + e); } } } + private static void updateRoadSensor(String uuid, LatLng position, CpmCodec.CpmFrame cpmFrame) { + if(ROAD_SENSOR_MAP.containsKey(uuid)) { + synchronized (ROAD_SENSOR_MAP) { + RoadSensor roadSensor = ROAD_SENSOR_MAP.get(uuid); + if(roadSensor != null) { + roadSensor.updateTimestamp(); + roadSensor.setCpmFrame(cpmFrame); + ioT3RoadSensorCallback.roadSensorUpdate(roadSensor); + } + } + } else { + RoadSensor roadSensor = new RoadSensor(uuid, position, cpmFrame, ioT3RoadSensorCallback); + addRoadSensor(uuid, roadSensor); + ioT3RoadSensorCallback.newRoadSensor(roadSensor); + } + } + private static void addRoadSensor(String key, RoadSensor roadSensor) { synchronized (ROAD_SENSORS) { ROAD_SENSORS.add(roadSensor); @@ -113,7 +129,7 @@ private static synchronized void startExpirationCheck() { /** * Retrieve a read-only list of the Road Sensors in the vicinity. * - * @return the read-only list of {@link com.orange.iot3mobility.roadobjects.RoadSensor} objects + * @return the read-only list of {@link RoadSensor} objects */ public static List getRoadSensors() { return Collections.unmodifiableList(ROAD_SENSORS); diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/EtsiConverter.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/EtsiConverter.java index 17076f5f2..6ce766837 100644 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/EtsiConverter.java +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/EtsiConverter.java @@ -375,4 +375,274 @@ public static double metersPerSecondToKilometersPerHour(double mps) { public static double kilometersPerHourToMetersPerSecond(double kmh) { return kmh / 3.6; } -} \ No newline at end of file + + // ------------------------------------------------------------------------- + // CPM-specific helpers + // ------------------------------------------------------------------------- + + /** + * CPM distance component in 0.01 m steps, signed (e.g. x_distance, y_distance, z_distance). + *

+ * ETSI -> SI (meters) + */ + public static double cpmDistanceMeters(int distanceEtsi) { + return distanceEtsi * 0.01; + } + + /** + * SI -> ETSI for CPM distance components (0.01 m steps, signed). + */ + public static int cpmDistanceEtsi(double distanceMeters) { + long etsi = Math.round(distanceMeters / 0.01); + if (etsi < -132768L) etsi = -132768L; + if (etsi > 132767L) etsi = 132767L; + return (int) etsi; + } + + /** + * CPM speed component in 0.01 m/s steps, signed (e.g. x_speed, y_speed, z_speed). + *

+ * ETSI -> SI (m/s) + */ + public static double cpmSpeedMetersPerSecond(int speedEtsi) { + return speedEtsi * 0.01; + } + + /** + * CPM X and Y speed components in 0.01 m/s steps. + *

+ * ETSI -> SI (m/s) + */ + public static double cpmDerivedSpeedMetersPerSecond(int xSpeed, int ySpeed) { + return Math.sqrt(Math.abs(xSpeed * xSpeed) + Math.abs(ySpeed * ySpeed)) * 0.01; + } + + /** + * SI -> ETSI for CPM speed components (0.01 m/s steps, signed). + */ + public static int cpmSpeedEtsiFromMetersPerSecond(double speedMps) { + long etsi = Math.round(speedMps / 0.01); + if (etsi < -16383L) etsi = -16383L; + if (etsi > 16383L) etsi = 16383L; + return (int) etsi; + } + + /** + * CPM X and Y speed components in 0.01 m/s steps. + */ + public static double cpmDerivedHeadingDegrees(int xSpeed, int ySpeed) { + return (90 - (180 / Math.PI) * Math.atan2(ySpeed, xSpeed)) % 360; + } + + /** + * CPM attitude angle in 0.1 degree steps (roll/pitch/yaw). Sentinel: 3601 => unavailable. + *

+ * ETSI -> SI (degrees). Returns NaN if sentinel. + */ + public static double cpmAngleDegrees(int angleEtsi) { + if (angleEtsi >= 3601) { + return Double.NaN; + } + return angleEtsi * 0.1; + } + + /** + * SI -> ETSI for CPM attitude angles (0.1 degree steps). If NaN, returns sentinel 3601. + */ + public static int cpmAngleEtsiFromDegrees(double angleDegrees) { + if (Double.isNaN(angleDegrees)) { + return 3601; + } + double normalized = ((angleDegrees % 360.0) + 360.0) % 360.0; + long etsi = Math.round(normalized / 0.1); + if (etsi < 0L) etsi = 0L; + if (etsi > 3601L) etsi = 3601L; + return (int) etsi; + } + + /** + * CPM angular rate in 0.01 deg/s steps, signed (roll/pitch/yaw rate). + *

+ * ETSI -> SI (deg/s) + */ + public static double cpmAngularRateDegreesPerSecond(int rateEtsi) { + return rateEtsi * 0.01; + } + + /** + * ETSI -> SI (rad/s) for CPM angular rate. + */ + public static double cpmAngularRateRadiansPerSecond(int rateEtsi) { + return Math.toRadians(cpmAngularRateDegreesPerSecond(rateEtsi)); + } + + /** + * SI -> ETSI for CPM angular rate (0.01 deg/s steps, signed). + */ + public static int cpmAngularRateEtsiFromDegreesPerSecond(double rateDegPerSec) { + long etsi = Math.round(rateDegPerSec / 0.01); + if (etsi < -32766L) etsi = -32766L; + if (etsi > 32767L) etsi = 32767L; + return (int) etsi; + } + + /** + * SI -> ETSI for CPM angular rate (rad/s). + */ + public static int cpmAngularRateEtsiFromRadiansPerSecond(double rateRadPerSec) { + return cpmAngularRateEtsiFromDegreesPerSecond(Math.toDegrees(rateRadPerSec)); + } + + /** + * CPM angular acceleration in 0.01 deg/s^2 steps, signed (roll/pitch/yaw acceleration). + *

+ * ETSI -> SI (deg/s^2) + */ + public static double cpmAngularAccelerationDegreesPerSecondSquared(int accelEtsi) { + return accelEtsi * 0.01; + } + + /** + * ETSI -> SI (rad/s^2) for CPM angular acceleration. + */ + public static double cpmAngularAccelerationRadiansPerSecondSquared(int accelEtsi) { + return Math.toRadians(cpmAngularAccelerationDegreesPerSecondSquared(accelEtsi)); + } + + /** + * SI -> ETSI for CPM angular acceleration (0.01 deg/s^2 steps, signed). + */ + public static int cpmAngularAccelerationEtsiFromDegreesPerSecondSquared(double accelDegPerSec2) { + long etsi = Math.round(accelDegPerSec2 / 0.01); + if (etsi < -32766L) etsi = -32766L; + if (etsi > 32767L) etsi = 32767L; + return (int) etsi; + } + + /** + * SI -> ETSI for CPM angular acceleration (rad/s^2). + */ + public static int cpmAngularAccelerationEtsiFromRadiansPerSecondSquared(double accelRadPerSec2) { + return cpmAngularAccelerationEtsiFromDegreesPerSecondSquared(Math.toDegrees(accelRadPerSec2)); + } + + /** + * CPM object dimensions in 0.1 m steps (planar and vertical dimensions). + *

+ * ETSI -> SI (meters) + */ + public static double cpmObjectDimensionMeters(int dimensionEtsi) { + return dimensionEtsi * 0.1; + } + + /** + * SI -> ETSI for CPM object dimensions (0.1 m steps). + */ + public static int cpmObjectDimensionEtsi(double dimensionMeters) { + long etsi = Math.round(dimensionMeters / 0.1); + if (etsi < 0L) etsi = 0L; + if (etsi > 1023L) etsi = 1023L; + return (int) etsi; + } + + /** + * CPM longitudinal lane position in 0.1 m steps. + *

+ * ETSI -> SI (meters) + */ + public static double cpmLanePositionMeters(int lanePositionEtsi) { + return lanePositionEtsi * 0.1; + } + + /** + * SI -> ETSI for CPM longitudinal lane position (0.1 m steps). + */ + public static int cpmLanePositionEtsi(double lanePositionMeters) { + long etsi = Math.round(lanePositionMeters / 0.1); + if (etsi < 0L) etsi = 0L; + if (etsi > 32767L) etsi = 32767L; + return (int) etsi; + } + + /** + * CPM offsets and sensor positions in 0.01 m steps, signed (Offset.x/y/z, sensor offsets). + *

+ * ETSI -> SI (meters) + */ + public static double cpmOffsetMeters(int offsetEtsi) { + return offsetEtsi * 0.01; + } + + /** + * SI -> ETSI for CPM offsets (0.01 m steps, signed). + */ + public static int cpmOffsetEtsi(double offsetMeters) { + long etsi = Math.round(offsetMeters / 0.01); + if (etsi < -32768L) etsi = -32768L; + if (etsi > 32767L) etsi = 32767L; + return (int) etsi; + } + + /** + * CPM range and radius in 0.1 m steps (sensor ranges and area dimensions). + *

+ * ETSI -> SI (meters) + */ + public static double cpmRangeMeters(int rangeEtsi) { + return rangeEtsi * 0.1; + } + + /** + * SI -> ETSI for CPM ranges and radii (0.1 m steps). + */ + public static int cpmRangeEtsi(double rangeMeters) { + long etsi = Math.round(rangeMeters / 0.1); + if (etsi < 0L) etsi = 0L; + if (etsi > 10000L) etsi = 10000L; + return (int) etsi; + } + + /** + * CPM opening angles in 0.1 degree steps. Sentinel: 3601 => unavailable. + *

+ * ETSI -> SI (degrees). Returns NaN if sentinel. + */ + public static double cpmOpeningAngleDegrees(int angleEtsi) { + if (angleEtsi >= 3601) { + return Double.NaN; + } + return angleEtsi * 0.1; + } + + /** + * SI -> ETSI for CPM opening angles (0.1 degree steps). If NaN, returns sentinel 3601. + */ + public static int cpmOpeningAngleEtsiFromDegrees(double angleDegrees) { + if (Double.isNaN(angleDegrees)) { + return 3601; + } + long etsi = Math.round(angleDegrees / 0.1); + if (etsi < 0L) etsi = 0L; + if (etsi > 3601L) etsi = 3601L; + return (int) etsi; + } + + /** + * CPM correlation coefficient scaled by 100 (range -100..100). + *

+ * ETSI -> SI (coefficient in [-1.0..1.0]) + */ + public static double cpmCorrelationCoefficient(int correlationEtsi) { + return correlationEtsi / 100.0; + } + + /** + * SI -> ETSI for CPM correlation coefficient scaled by 100. + */ + public static int cpmCorrelationEtsiFromCoefficient(double coefficient) { + long etsi = Math.round(coefficient * 100.0); + if (etsi < -100L) etsi = -100L; + if (etsi > 100L) etsi = 100L; + return (int) etsi; + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/CpmHelper.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/CpmHelper.java new file mode 100644 index 000000000..0e730ebe2 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/CpmHelper.java @@ -0,0 +1,132 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm; + +import com.fasterxml.jackson.core.JsonFactory; +import com.orange.iot3mobility.messages.cpm.core.CpmCodec; +import com.orange.iot3mobility.messages.cpm.core.CpmException; +import com.orange.iot3mobility.messages.cpm.core.CpmVersion; +import com.orange.iot3mobility.messages.cpm.v121.model.CpmEnvelope121; +import com.orange.iot3mobility.messages.cpm.v211.model.CpmEnvelope211; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +/** + * High-level helper around CpmCodec. + *

+ * - Manages a shared JsonFactory and CpmCodec instance. + * - Provides String-based APIs (convenient for MQTT payloads). + * - Thread-safe: stateless, all shared components are immutable. + */ +public final class CpmHelper { + + private final JsonFactory jsonFactory; + private final CpmCodec cpmCodec; + + /** + * Default constructor: creates its own JsonFactory. + * Recommended in most cases. + */ + public CpmHelper() { + this(new JsonFactory()); + } + + /** + * Constructor with externally-provided JsonFactory (for advanced usage, + * e.g. custom Jackson configuration). + */ + public CpmHelper(JsonFactory jsonFactory) { + this.jsonFactory = Objects.requireNonNull(jsonFactory, "jsonFactory"); + this.cpmCodec = new CpmCodec(this.jsonFactory); + } + + // --------------------------------------------------------------------- + // Reading / parsing from String (e.g. MQTT payload) + // --------------------------------------------------------------------- + + /** + * Parse a CPM JSON payload (string) and let CpmCodec detect the version. + * + * @param jsonPayload JSON string containing a CPM envelope + * @return a CpmFrame with detected version and typed envelope + * @throws IOException if JSON is malformed or I/O error in parser + * @throws CpmException (or subclasses) if the CPM structure/fields are invalid + */ + public CpmCodec.CpmFrame parse(String jsonPayload) throws IOException { + Objects.requireNonNull(jsonPayload, "jsonPayload"); + + return cpmCodec.read(jsonPayload); + } + + /** + * Parse a CPM JSON payload and cast it to a v1.2.1 envelope. + * Throws if the version is not 1.2.1. + */ + public CpmEnvelope121 parse121(String jsonPayload) throws IOException { + CpmCodec.CpmFrame frame = parse(jsonPayload); + if (frame.version() != CpmVersion.V1_2_1) { + throw new CpmException("Expected CPM version 1.2.1 but got " + frame.version()); + } + return (CpmEnvelope121) frame.envelope(); + } + + /** + * Parse a CPM JSON payload and cast it to a v2.1.1 envelope. + * Throws if the version is not 2.1.1. + */ + public CpmEnvelope211 parse211(String jsonPayload) throws IOException { + CpmCodec.CpmFrame frame = parse(jsonPayload); + if (frame.version() != CpmVersion.V2_1_1) { + throw new CpmException("Expected CPM version 2.1.1 but got " + frame.version()); + } + return (CpmEnvelope211) frame.envelope(); + } + + // --------------------------------------------------------------------- + // Writing / serializing to String + // --------------------------------------------------------------------- + + /** + * Serialize a v1.2.1 CPM envelope to a JSON string. + */ + public String toJson(CpmEnvelope121 envelope121) throws IOException { + Objects.requireNonNull(envelope121, "envelope121"); + return writeToString(CpmVersion.V1_2_1, envelope121); + } + + /** + * Serialize a v2.1.1 CPM envelope to a JSON string. + */ + public String toJson(CpmEnvelope211 envelope211) throws IOException { + Objects.requireNonNull(envelope211, "envelope211"); + return writeToString(CpmVersion.V2_1_1, envelope211); + } + + /** + * Generic entry point for CPM serialization to a JSON string. + */ + public String toJson(CpmVersion version, Object envelope) throws IOException { + Objects.requireNonNull(version, "version"); + Objects.requireNonNull(envelope, "envelope"); + return writeToString(version, envelope); + } + + // --------------------------------------------------------------------- + // Internal helper + // --------------------------------------------------------------------- + + private String writeToString(CpmVersion version, Object envelope) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(1024); + cpmCodec.write(version, envelope, out); + return out.toString(StandardCharsets.UTF_8); + } +} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/core/CpmCodec.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/core/CpmCodec.java new file mode 100644 index 000000000..797a42754 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/core/CpmCodec.java @@ -0,0 +1,128 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.core; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.orange.iot3mobility.messages.cpm.v121.codec.CpmReader121; +import com.orange.iot3mobility.messages.cpm.v121.codec.CpmWriter121; +import com.orange.iot3mobility.messages.cpm.v121.model.CpmEnvelope121; +import com.orange.iot3mobility.messages.cpm.v211.codec.CpmReader211; +import com.orange.iot3mobility.messages.cpm.v211.codec.CpmWriter211; +import com.orange.iot3mobility.messages.cpm.v211.model.CpmEnvelope211; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +/** + * Unified entry point to decode/encode CPM envelopes across all supported versions. + */ +public final class CpmCodec { + + /** + * Wrapper exposing both the detected CPM version and the decoded envelope. + */ + public record CpmFrame(CpmVersion version, T envelope) {} + + private final JsonFactory jsonFactory; + private final CpmReader121 reader121; + private final CpmWriter121 writer121; + private final CpmReader211 reader211; + private final CpmWriter211 writer211; + + public CpmCodec(JsonFactory jsonFactory) { + this.jsonFactory = Objects.requireNonNull(jsonFactory, "jsonFactory"); + this.reader121 = new CpmReader121(jsonFactory); + this.writer121 = new CpmWriter121(jsonFactory); + this.reader211 = new CpmReader211(jsonFactory); + this.writer211 = new CpmWriter211(jsonFactory); + } + + /** + * Reads the full payload, detects the CPM version from the top-level "version" field, + * then delegates to the appropriate reader. The returned {@link CpmFrame} exposes both the version and the + * strongly-typed envelope instance. + */ + public CpmFrame read(InputStream in) throws IOException { + byte[] payload = in.readAllBytes(); + CpmVersion version = detectVersion(payload); + + return switch (version) { + case V1_2_1 -> new CpmFrame<>(version, + reader121.read(new ByteArrayInputStream(payload))); + case V2_1_1 -> new CpmFrame<>(version, + reader211.read(new ByteArrayInputStream(payload))); + }; + } + + /** + * Same as {@link #read(InputStream)} but takes a JSON String directly. + * Convenient for MQTT payloads that are received as String. + */ + public CpmFrame read(String json) throws IOException { + Objects.requireNonNull(json, "json"); + byte[] payload = json.getBytes(StandardCharsets.UTF_8); + + CpmVersion version = detectVersion(payload); + + return switch (version) { + case V1_2_1 -> new CpmFrame<>(version, reader121.read(new ByteArrayInputStream(payload))); + case V2_1_1 -> new CpmFrame<>(version, reader211.read(new ByteArrayInputStream(payload))); + }; + } + + /** + * Writes an envelope using the writer that matches the provided version. + */ + public void write(CpmVersion version, Object envelope, OutputStream out) throws IOException { + switch (version) { + case V1_2_1 -> writer121.write(cast(envelope, CpmEnvelope121.class), out); + case V2_1_1 -> writer211.write(cast(envelope, CpmEnvelope211.class), out); + default -> throw new CpmException("Unsupported version: " + version); + } + } + + /** + * Parses only the top-level "version" field without building any tree (streaming mode). + */ + private CpmVersion detectVersion(byte[] payload) throws IOException { + try (JsonParser parser = jsonFactory.createParser(payload)) { + expect(parser.nextToken(), JsonToken.START_OBJECT); + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.getCurrentName(); + parser.nextToken(); + if ("version".equals(field)) { + return CpmVersion.fromJsonValue(parser.getValueAsString()); + } else { + parser.skipChildren(); + } + } + } + throw new CpmException("Missing 'version' field in CPM payload"); + } + + private static void expect(JsonToken actual, JsonToken expected) { + if (actual != expected) { + throw new CpmException("Expected token " + expected + " but got " + actual); + } + } + + @SuppressWarnings("unchecked") + private static T cast(Object value, Class type) { + if (!type.isInstance(value)) { + throw new CpmException("Expected envelope of type " + type.getName() + + " but got " + (value == null ? "null" : value.getClass().getName())); + } + return (T) value; + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/core/CpmException.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/core/CpmException.java new file mode 100644 index 000000000..848dbea20 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/core/CpmException.java @@ -0,0 +1,26 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.core; + +/** + * Base runtime exception for all CPM codec operations. + */ +public class CpmException extends RuntimeException { + + public CpmException(String message) { + super(message); + } + + public CpmException(String message, Throwable cause) { + super(message, cause); + } + + public CpmException(Throwable cause) { + super(cause); + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/core/CpmVersion.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/core/CpmVersion.java new file mode 100644 index 000000000..a5ca10ce3 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/core/CpmVersion.java @@ -0,0 +1,35 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.core; + +/** + * Supported CPM JSON envelope versions. + */ +public enum CpmVersion { + V1_2_1("1.2.1"), + V2_1_1("2.1.1"); + + private final String jsonValue; + + CpmVersion(String jsonValue) { + this.jsonValue = jsonValue; + } + + public String jsonValue() { + return jsonValue; + } + + public static CpmVersion fromJsonValue(String value) { + for (CpmVersion version : values()) { + if (version.jsonValue.equals(value)) { + return version; + } + } + throw new CpmException("Unsupported CPM version: " + value); + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/codec/CpmReader121.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/codec/CpmReader121.java new file mode 100644 index 000000000..4dae20168 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/codec/CpmReader121.java @@ -0,0 +1,1050 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.codec; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.orange.iot3mobility.messages.cpm.v121.model.CpmEnvelope121; +import com.orange.iot3mobility.messages.cpm.v121.model.CpmMessage121; +import com.orange.iot3mobility.messages.cpm.v121.model.defs.*; +import com.orange.iot3mobility.messages.cpm.v121.model.freespacecontainer.FreeSpaceAddendum; +import com.orange.iot3mobility.messages.cpm.v121.model.freespacecontainer.FreeSpaceAddendumContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.freespacecontainer.FreeSpaceArea; +import com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer.ManagementConfidence; +import com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer.ManagementContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer.PositionConfidenceEllipse; +import com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer.ReferencePosition; +import com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer.*; +import com.orange.iot3mobility.messages.cpm.v121.model.sensorinformationcontainer.*; +import com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer.OriginatingRsuContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer.OriginatingVehicleContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer.StationDataContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer.VehicleConfidence; +import com.orange.iot3mobility.messages.cpm.v121.validation.CpmValidationException; +import com.orange.iot3mobility.messages.cpm.v121.validation.CpmValidator121; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public final class CpmReader121 { + + private final JsonFactory jsonFactory; + + public CpmReader121(JsonFactory jsonFactory) { + this.jsonFactory = jsonFactory; + } + + public CpmEnvelope121 read(InputStream in) throws IOException { + try (JsonParser parser = jsonFactory.createParser(in)) { + expect(parser.nextToken(), JsonToken.START_OBJECT); + + String type = null; + String origin = null; + String version = null; + String sourceUuid = null; + Long timestamp = null; + CpmMessage121 message = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "type" -> type = parser.getValueAsString(); + case "origin" -> origin = parser.getValueAsString(); + case "version" -> version = parser.getValueAsString(); + case "source_uuid" -> sourceUuid = parser.getValueAsString(); + case "timestamp" -> timestamp = parser.getLongValue(); + case "message" -> message = readMessage(parser); + default -> parser.skipChildren(); + } + } + + CpmEnvelope121 envelope = new CpmEnvelope121( + requireField(type, "type"), + requireField(origin, "origin"), + requireField(version, "version"), + requireField(sourceUuid, "source_uuid"), + requireField(timestamp, "timestamp"), + requireField(message, "message")); + + CpmValidator121.validateEnvelope(envelope); + return envelope; + } + } + + private CpmMessage121 readMessage(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + + Integer protocolVersion = null; + Long stationId = null; + Integer generationDeltaTime = null; + ManagementContainer managementContainer = null; + StationDataContainer stationDataContainer = null; + SensorInformationContainer sensorInformationContainer = null; + PerceivedObjectContainer perceivedObjectContainer = null; + FreeSpaceAddendumContainer freeSpaceAddendumContainer = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "protocol_version" -> protocolVersion = parser.getIntValue(); + case "station_id" -> stationId = parser.getLongValue(); + case "generation_delta_time" -> generationDeltaTime = parser.getIntValue(); + case "management_container" -> managementContainer = readManagementContainer(parser); + case "station_data_container" -> stationDataContainer = readStationDataContainer(parser); + case "sensor_information_container" -> sensorInformationContainer = readSensorInformationContainer(parser); + case "perceived_object_container" -> perceivedObjectContainer = readPerceivedObjectContainer(parser); + case "free_space_addendum_container" -> freeSpaceAddendumContainer = readFreeSpaceAddendumContainer(parser); + default -> parser.skipChildren(); + } + } + + return new CpmMessage121( + requireField(protocolVersion, "protocol_version"), + requireField(stationId, "station_id"), + requireField(generationDeltaTime, "generation_delta_time"), + requireField(managementContainer, "management_container"), + stationDataContainer, + sensorInformationContainer, + perceivedObjectContainer, + freeSpaceAddendumContainer); + } + + /* --------------------------------------------------------------------- */ + /* Management container */ + /* --------------------------------------------------------------------- */ + + private ManagementContainer readManagementContainer(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer stationType = null; + ReferencePosition referencePosition = null; + ManagementConfidence confidence = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "station_type" -> stationType = parser.getIntValue(); + case "reference_position" -> referencePosition = readReferencePosition(parser); + case "confidence" -> confidence = readManagementConfidence(parser); + default -> parser.skipChildren(); + } + } + + return new ManagementContainer( + requireField(stationType, "station_type"), + requireField(referencePosition, "reference_position"), + requireField(confidence, "confidence")); + } + + private ReferencePosition readReferencePosition(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer latitude = null; + Integer longitude = null; + Integer altitude = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "latitude" -> latitude = parser.getIntValue(); + case "longitude" -> longitude = parser.getIntValue(); + case "altitude" -> altitude = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new ReferencePosition( + requireField(latitude, "latitude"), + requireField(longitude, "longitude"), + requireField(altitude, "altitude")); + } + + private ManagementConfidence readManagementConfidence(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + PositionConfidenceEllipse ellipse = null; + Integer altitude = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "position_confidence_ellipse" -> ellipse = readPositionConfidenceEllipse(parser); + case "altitude" -> altitude = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new ManagementConfidence( + requireField(ellipse, "position_confidence_ellipse"), + requireField(altitude, "altitude")); + } + + private PositionConfidenceEllipse readPositionConfidenceEllipse(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer semiMajor = null; + Integer semiMinor = null; + Integer orientation = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "semi_major_confidence" -> semiMajor = parser.getIntValue(); + case "semi_minor_confidence" -> semiMinor = parser.getIntValue(); + case "semi_major_orientation" -> orientation = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new PositionConfidenceEllipse( + requireField(semiMajor, "semi_major_confidence"), + requireField(semiMinor, "semi_minor_confidence"), + requireField(orientation, "semi_major_orientation")); + } + + /* --------------------------------------------------------------------- */ + /* Station data container */ + /* --------------------------------------------------------------------- */ + + private StationDataContainer readStationDataContainer(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + OriginatingVehicleContainer vehicleContainer = null; + OriginatingRsuContainer rsuContainer = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "originating_vehicle_container" -> vehicleContainer = readOriginatingVehicleContainer(parser); + case "originating_rsu_container" -> rsuContainer = readOriginatingRsuContainer(parser); + default -> parser.skipChildren(); + } + } + + return new StationDataContainer(vehicleContainer, rsuContainer); + } + + private OriginatingVehicleContainer readOriginatingVehicleContainer(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer heading = null; + Integer speed = null; + VehicleConfidence confidence = null; + Integer driveDirection = null; + Integer vehicleLength = null; + Integer vehicleWidth = null; + Integer longitudinalAcceleration = null; + Integer yawRate = null; + Integer lateralAcceleration = null; + Integer verticalAcceleration = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "heading" -> heading = parser.getIntValue(); + case "speed" -> speed = parser.getIntValue(); + case "confidence" -> confidence = readVehicleConfidence(parser); + case "drive_direction" -> driveDirection = parser.getIntValue(); + case "vehicle_length" -> vehicleLength = parser.getIntValue(); + case "vehicle_width" -> vehicleWidth = parser.getIntValue(); + case "longitudinal_acceleration" -> longitudinalAcceleration = parser.getIntValue(); + case "yaw_rate" -> yawRate = parser.getIntValue(); + case "lateral_acceleration" -> lateralAcceleration = parser.getIntValue(); + case "vertical_acceleration" -> verticalAcceleration = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new OriginatingVehicleContainer( + requireField(heading, "heading"), + requireField(speed, "speed"), + requireField(confidence, "confidence"), + driveDirection, + vehicleLength, + vehicleWidth, + longitudinalAcceleration, + yawRate, + lateralAcceleration, + verticalAcceleration); + } + + private VehicleConfidence readVehicleConfidence(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer heading = null; + Integer speed = null; + Integer vehicleLength = null; + Integer yawRate = null; + Integer longitudinalAcceleration = null; + Integer lateralAcceleration = null; + Integer verticalAcceleration = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "heading" -> heading = parser.getIntValue(); + case "speed" -> speed = parser.getIntValue(); + case "vehicle_length" -> vehicleLength = parser.getIntValue(); + case "yaw_rate" -> yawRate = parser.getIntValue(); + case "longitudinal_acceleration" -> longitudinalAcceleration = parser.getIntValue(); + case "lateral_acceleration" -> lateralAcceleration = parser.getIntValue(); + case "vertical_acceleration" -> verticalAcceleration = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new VehicleConfidence( + requireField(heading, "heading"), + requireField(speed, "speed"), + vehicleLength, + yawRate, + longitudinalAcceleration, + lateralAcceleration, + verticalAcceleration); + } + + private OriginatingRsuContainer readOriginatingRsuContainer(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer region = null; + Integer intersectionReferenceId = null; + Integer roadSegmentReferenceId = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "region" -> region = parser.getIntValue(); + case "intersection_reference_id" -> intersectionReferenceId = parser.getIntValue(); + case "road_segment_reference_id" -> roadSegmentReferenceId = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new OriginatingRsuContainer(region, intersectionReferenceId, roadSegmentReferenceId); + } + + /* --------------------------------------------------------------------- */ + /* Sensor information container */ + /* --------------------------------------------------------------------- */ + + private SensorInformationContainer readSensorInformationContainer(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List list = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + list.add(readSensorInformation(parser)); + } + return new SensorInformationContainer(list); + } + + private SensorInformation readSensorInformation(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer sensorId = null; + Integer type = null; + DetectionArea detectionArea = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "sensor_id" -> sensorId = parser.getIntValue(); + case "type" -> type = parser.getIntValue(); + case "detection_area" -> detectionArea = readDetectionArea(parser); + default -> parser.skipChildren(); + } + } + + return new SensorInformation( + requireField(sensorId, "sensor_id"), + requireField(type, "type"), + requireField(detectionArea, "detection_area")); + } + + private DetectionArea readDetectionArea(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + VehicleSensor vehicleSensor = null; + StationarySensorRadial stationarySensorRadial = null; + AreaPolygon stationarySensorPolygon = null; + AreaCircular stationarySensorCircular = null; + AreaEllipse stationarySensorEllipse = null; + AreaRectangle stationarySensorRectangle = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "vehicle_sensor" -> vehicleSensor = readVehicleSensor(parser); + case "stationary_sensor_radial" -> stationarySensorRadial = readStationarySensorRadial(parser); + case "stationary_sensor_polygon" -> stationarySensorPolygon = readAreaPolygon(parser); + case "stationary_sensor_circular" -> stationarySensorCircular = readAreaCircular(parser); + case "stationary_sensor_ellipse" -> stationarySensorEllipse = readAreaEllipse(parser); + case "stationary_sensor_rectangle" -> stationarySensorRectangle = readAreaRectangle(parser); + default -> parser.skipChildren(); + } + } + + return new DetectionArea( + vehicleSensor, + stationarySensorRadial, + stationarySensorPolygon, + stationarySensorCircular, + stationarySensorEllipse, + stationarySensorRectangle); + } + + private VehicleSensor readVehicleSensor(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer refPointId = null; + Integer xSensorOffset = null; + Integer ySensorOffset = null; + Integer zSensorOffset = null; + List properties = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "ref_point_id" -> refPointId = parser.getIntValue(); + case "x_sensor_offset" -> xSensorOffset = parser.getIntValue(); + case "y_sensor_offset" -> ySensorOffset = parser.getIntValue(); + case "z_sensor_offset" -> zSensorOffset = parser.getIntValue(); + case "vehicle_sensor_property_list" -> properties = readVehicleSensorPropertyList(parser); + default -> parser.skipChildren(); + } + } + + return new VehicleSensor( + refPointId, + requireField(xSensorOffset, "x_sensor_offset"), + requireField(ySensorOffset, "y_sensor_offset"), + zSensorOffset, + requireField(properties, "vehicle_sensor_property_list")); + } + + private List readVehicleSensorPropertyList(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List list = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + list.add(readVehicleSensorProperty(parser)); + } + return list; + } + + private VehicleSensorProperty readVehicleSensorProperty(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer range = null; + Integer horizontalStart = null; + Integer horizontalEnd = null; + Integer verticalStart = null; + Integer verticalEnd = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "range" -> range = parser.getIntValue(); + case "horizontal_opening_angle_start" -> horizontalStart = parser.getIntValue(); + case "horizontal_opening_angle_end" -> horizontalEnd = parser.getIntValue(); + case "vertical_opening_angle_start" -> verticalStart = parser.getIntValue(); + case "vertical_opening_angle_end" -> verticalEnd = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new VehicleSensorProperty( + requireField(range, "range"), + requireField(horizontalStart, "horizontal_opening_angle_start"), + requireField(horizontalEnd, "horizontal_opening_angle_end"), + verticalStart, + verticalEnd); + } + + private StationarySensorRadial readStationarySensorRadial(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer range = null; + Integer horizontalStart = null; + Integer horizontalEnd = null; + Integer verticalStart = null; + Integer verticalEnd = null; + Offset offset = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "range" -> range = parser.getIntValue(); + case "horizontal_opening_angle_start" -> horizontalStart = parser.getIntValue(); + case "horizontal_opening_angle_end" -> horizontalEnd = parser.getIntValue(); + case "vertical_opening_angle_start" -> verticalStart = parser.getIntValue(); + case "vertical_opening_angle_end" -> verticalEnd = parser.getIntValue(); + case "sensor_position_offset" -> offset = readOffset(parser); + default -> parser.skipChildren(); + } + } + + return new StationarySensorRadial( + requireField(range, "range"), + requireField(horizontalStart, "horizontal_opening_angle_start"), + requireField(horizontalEnd, "horizontal_opening_angle_end"), + verticalStart, + verticalEnd, + offset); + } + + /* --------------------------------------------------------------------- */ + /* Perceived object container */ + /* --------------------------------------------------------------------- */ + + private PerceivedObjectContainer readPerceivedObjectContainer(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List list = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + list.add(readPerceivedObject(parser)); + } + return new PerceivedObjectContainer(list); + } + + private PerceivedObject readPerceivedObject(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer objectId = null; + Integer timeOfMeasurement = null; + Integer xDistance = null; + Integer yDistance = null; + Integer zDistance = null; + Integer xSpeed = null; + Integer ySpeed = null; + Integer zSpeed = null; + Integer xAcceleration = null; + Integer yAcceleration = null; + Integer zAcceleration = null; + Integer rollAngle = null; + Integer pitchAngle = null; + Integer yawAngle = null; + Integer rollRate = null; + Integer pitchRate = null; + Integer yawRate = null; + Integer rollAcceleration = null; + Integer pitchAcceleration = null; + Integer yawAcceleration = null; + LowerTriangularCorrelationMatrix correlationMatrix = null; + Integer planarObjectDimension1 = null; + Integer planarObjectDimension2 = null; + Integer verticalObjectDimension = null; + Integer objectRefPoint = null; + Integer objectAge = null; + List sensorIdList = null; + Integer dynamicStatus = null; + List classification = null; + MapPosition matchedPosition = null; + PerceivedObjectConfidence confidence = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "object_id" -> objectId = parser.getIntValue(); + case "time_of_measurement" -> timeOfMeasurement = parser.getIntValue(); + case "x_distance" -> xDistance = parser.getIntValue(); + case "y_distance" -> yDistance = parser.getIntValue(); + case "z_distance" -> zDistance = parser.getIntValue(); + case "x_speed" -> xSpeed = parser.getIntValue(); + case "y_speed" -> ySpeed = parser.getIntValue(); + case "z_speed" -> zSpeed = parser.getIntValue(); + case "x_acceleration" -> xAcceleration = parser.getIntValue(); + case "y_acceleration" -> yAcceleration = parser.getIntValue(); + case "z_acceleration" -> zAcceleration = parser.getIntValue(); + case "roll_angle" -> rollAngle = parser.getIntValue(); + case "pitch_angle" -> pitchAngle = parser.getIntValue(); + case "yaw_angle" -> yawAngle = parser.getIntValue(); + case "roll_rate" -> rollRate = parser.getIntValue(); + case "pitch_rate" -> pitchRate = parser.getIntValue(); + case "yaw_rate" -> yawRate = parser.getIntValue(); + case "roll_acceleration" -> rollAcceleration = parser.getIntValue(); + case "pitch_acceleration" -> pitchAcceleration = parser.getIntValue(); + case "yaw_acceleration" -> yawAcceleration = parser.getIntValue(); + case "lower_triangular_correlation_matrix_columns" -> correlationMatrix = readLowerTriangularCorrelationMatrix(parser); + case "planar_object_dimension_1" -> planarObjectDimension1 = parser.getIntValue(); + case "planar_object_dimension_2" -> planarObjectDimension2 = parser.getIntValue(); + case "vertical_object_dimension" -> verticalObjectDimension = parser.getIntValue(); + case "object_ref_point" -> objectRefPoint = parser.getIntValue(); + case "object_age" -> objectAge = parser.getIntValue(); + case "sensor_id_list" -> sensorIdList = readIntegerList(parser); + case "dynamic_status" -> dynamicStatus = parser.getIntValue(); + case "classification" -> classification = readObjectClassificationList(parser); + case "matched_position" -> matchedPosition = readMapPosition(parser); + case "confidence" -> confidence = readPerceivedObjectConfidence(parser); + default -> parser.skipChildren(); + } + } + + return new PerceivedObject( + requireField(objectId, "object_id"), + requireField(timeOfMeasurement, "time_of_measurement"), + requireField(xDistance, "x_distance"), + requireField(yDistance, "y_distance"), + requireField(xSpeed, "x_speed"), + requireField(ySpeed, "y_speed"), + requireField(objectAge, "object_age"), + requireField(confidence, "confidence"), + zDistance, + zSpeed, + xAcceleration, + yAcceleration, + zAcceleration, + rollAngle, + pitchAngle, + yawAngle, + rollRate, + pitchRate, + yawRate, + rollAcceleration, + pitchAcceleration, + yawAcceleration, + correlationMatrix, + planarObjectDimension1, + planarObjectDimension2, + verticalObjectDimension, + objectRefPoint, + sensorIdList, + dynamicStatus, + classification, + matchedPosition); + } + + private PerceivedObjectConfidence readPerceivedObjectConfidence(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer xDistance = null; + Integer yDistance = null; + Integer zDistance = null; + Integer xSpeed = null; + Integer ySpeed = null; + Integer zSpeed = null; + Integer xAcceleration = null; + Integer yAcceleration = null; + Integer zAcceleration = null; + Integer rollAngle = null; + Integer pitchAngle = null; + Integer yawAngle = null; + Integer rollRate = null; + Integer pitchRate = null; + Integer yawRate = null; + Integer rollAcceleration = null; + Integer pitchAcceleration = null; + Integer yawAcceleration = null; + Integer planarObjectDimension1 = null; + Integer planarObjectDimension2 = null; + Integer verticalObjectDimension = null; + Integer longitudinalLanePosition = null; + Integer object = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "x_distance" -> xDistance = parser.getIntValue(); + case "y_distance" -> yDistance = parser.getIntValue(); + case "z_distance" -> zDistance = parser.getIntValue(); + case "x_speed" -> xSpeed = parser.getIntValue(); + case "y_speed" -> ySpeed = parser.getIntValue(); + case "z_speed" -> zSpeed = parser.getIntValue(); + case "x_acceleration" -> xAcceleration = parser.getIntValue(); + case "y_acceleration" -> yAcceleration = parser.getIntValue(); + case "z_acceleration" -> zAcceleration = parser.getIntValue(); + case "roll_angle" -> rollAngle = parser.getIntValue(); + case "pitch_angle" -> pitchAngle = parser.getIntValue(); + case "yaw_angle" -> yawAngle = parser.getIntValue(); + case "roll_rate" -> rollRate = parser.getIntValue(); + case "pitch_rate" -> pitchRate = parser.getIntValue(); + case "yaw_rate" -> yawRate = parser.getIntValue(); + case "roll_acceleration" -> rollAcceleration = parser.getIntValue(); + case "pitch_acceleration" -> pitchAcceleration = parser.getIntValue(); + case "yaw_acceleration" -> yawAcceleration = parser.getIntValue(); + case "planar_object_dimension_1" -> planarObjectDimension1 = parser.getIntValue(); + case "planar_object_dimension_2" -> planarObjectDimension2 = parser.getIntValue(); + case "vertical_object_dimension" -> verticalObjectDimension = parser.getIntValue(); + case "longitudinal_lane_position" -> longitudinalLanePosition = parser.getIntValue(); + case "object" -> object = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new PerceivedObjectConfidence( + requireField(xDistance, "x_distance"), + requireField(yDistance, "y_distance"), + requireField(xSpeed, "x_speed"), + requireField(ySpeed, "y_speed"), + requireField(object, "object"), + zDistance, + zSpeed, + xAcceleration, + yAcceleration, + zAcceleration, + rollAngle, + pitchAngle, + yawAngle, + rollRate, + pitchRate, + yawRate, + rollAcceleration, + pitchAcceleration, + yawAcceleration, + planarObjectDimension1, + planarObjectDimension2, + verticalObjectDimension, + longitudinalLanePosition); + } + + private LowerTriangularCorrelationMatrix readLowerTriangularCorrelationMatrix(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List> columns = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + columns.add(readIntegerList(parser)); + } + return new LowerTriangularCorrelationMatrix(columns); + } + + private List readObjectClassificationList(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List list = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + list.add(readObjectClassification(parser)); + } + return list; + } + + private ObjectClassification readObjectClassification(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + ObjectClass objectClass = null; + Integer confidence = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "object_class" -> objectClass = readObjectClass(parser); + case "confidence" -> confidence = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new ObjectClassification( + requireField(objectClass, "object_class"), + requireField(confidence, "confidence")); + } + + private ObjectClass readObjectClass(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer vehicle = null; + ObjectClassVru singleVru = null; + ObjectClassGroup vruGroup = null; + Integer other = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "vehicle" -> vehicle = parser.getIntValue(); + case "single_vru" -> singleVru = readObjectClassVru(parser); + case "vru_group" -> vruGroup = readObjectClassGroup(parser); + case "other" -> other = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new ObjectClass(vehicle, singleVru, vruGroup, other); + } + + private ObjectClassVru readObjectClassVru(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer pedestrian = null; + Integer bicyclist = null; + Integer motorcylist = null; + Integer animal = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "pedestrian" -> pedestrian = parser.getIntValue(); + case "bicyclist" -> bicyclist = parser.getIntValue(); + case "motorcylist" -> motorcylist = parser.getIntValue(); + case "animal" -> animal = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new ObjectClassVru(pedestrian, bicyclist, motorcylist, animal); + } + + private ObjectClassGroup readObjectClassGroup(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + GroupType groupType = null; + Integer groupSize = null; + Integer clusterId = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "group_type" -> groupType = readGroupType(parser); + case "group_size" -> groupSize = parser.getIntValue(); + case "cluster_id" -> clusterId = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new ObjectClassGroup( + requireField(groupType, "group_type"), + requireField(groupSize, "group_size"), + clusterId); + } + + private GroupType readGroupType(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Boolean pedestrian = null; + Boolean bicyclist = null; + Boolean motorcyclist = null; + Boolean animal = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "pedestrian" -> pedestrian = parser.getBooleanValue(); + case "bicyclist" -> bicyclist = parser.getBooleanValue(); + case "motorcyclist" -> motorcyclist = parser.getBooleanValue(); + case "animal" -> animal = parser.getBooleanValue(); + default -> parser.skipChildren(); + } + } + + return new GroupType( + requireField(pedestrian, "pedestrian"), + requireField(bicyclist, "bicyclist"), + requireField(motorcyclist, "motorcyclist"), + requireField(animal, "animal")); + } + + private MapPosition readMapPosition(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer laneId = null; + Integer longitudinalLanePosition = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "lane_id" -> laneId = parser.getIntValue(); + case "longitudinal_lane_position" -> longitudinalLanePosition = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new MapPosition(laneId, longitudinalLanePosition); + } + + /* --------------------------------------------------------------------- */ + /* Free space addendum container */ + /* --------------------------------------------------------------------- */ + + private FreeSpaceAddendumContainer readFreeSpaceAddendumContainer(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List list = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + list.add(readFreeSpaceAddendum(parser)); + } + return new FreeSpaceAddendumContainer(list); + } + + private FreeSpaceAddendum readFreeSpaceAddendum(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + FreeSpaceArea area = null; + Integer confidence = null; + List sensorIdList = null; + Boolean shadowingApplies = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "free_space_area" -> area = readFreeSpaceArea(parser); + case "free_space_confidence" -> confidence = parser.getIntValue(); + case "sensor_id_list" -> sensorIdList = readIntegerList(parser); + case "shadowing_applies" -> shadowingApplies = parser.getBooleanValue(); + default -> parser.skipChildren(); + } + } + + return new FreeSpaceAddendum( + requireField(area, "free_space_area"), + requireField(confidence, "free_space_confidence"), + sensorIdList, + shadowingApplies); + } + + private FreeSpaceArea readFreeSpaceArea(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + AreaPolygon polygon = null; + AreaCircular circular = null; + AreaEllipse ellipse = null; + AreaRectangle rectangle = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "free_space_polygon" -> polygon = readAreaPolygon(parser); + case "free_space_circular" -> circular = readAreaCircular(parser); + case "free_space_ellipse" -> ellipse = readAreaEllipse(parser); + case "free_space_rectangle" -> rectangle = readAreaRectangle(parser); + default -> parser.skipChildren(); + } + } + + return new FreeSpaceArea(polygon, circular, ellipse, rectangle); + } + + /* --------------------------------------------------------------------- */ + /* Shared helpers */ + /* --------------------------------------------------------------------- */ + + private Offset readOffset(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer x = null; + Integer y = null; + Integer z = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "x" -> x = parser.getIntValue(); + case "y" -> y = parser.getIntValue(); + case "z" -> z = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new Offset( + requireField(x, "x"), + requireField(y, "y"), + z); + } + + private AreaPolygon readAreaPolygon(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List offsets = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + offsets.add(readOffset(parser)); + } + return new AreaPolygon(offsets); + } + + private AreaCircular readAreaCircular(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Offset center = null; + Integer radius = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "node_center_point" -> center = readOffset(parser); + case "radius" -> radius = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new AreaCircular(center, requireField(radius, "radius")); + } + + private AreaEllipse readAreaEllipse(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Offset center = null; + Integer semiMajor = null; + Integer semiMinor = null; + Integer orientation = null; + Integer semiHeight = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "node_center_point" -> center = readOffset(parser); + case "semi_major_range_length" -> semiMajor = parser.getIntValue(); + case "semi_minor_range_length" -> semiMinor = parser.getIntValue(); + case "semi_major_range_orientation" -> orientation = parser.getIntValue(); + case "semi_height" -> semiHeight = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new AreaEllipse( + center, + requireField(semiMajor, "semi_major_range_length"), + requireField(semiMinor, "semi_minor_range_length"), + requireField(orientation, "semi_major_range_orientation"), + semiHeight); + } + + private AreaRectangle readAreaRectangle(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Offset center = null; + Integer semiMajor = null; + Integer semiMinor = null; + Integer orientation = null; + Integer semiHeight = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "node_center_point" -> center = readOffset(parser); + case "semi_major_range_length" -> semiMajor = parser.getIntValue(); + case "semi_minor_range_length" -> semiMinor = parser.getIntValue(); + case "semi_major_range_orientation" -> orientation = parser.getIntValue(); + case "semi_height" -> semiHeight = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new AreaRectangle( + center, + requireField(semiMajor, "semi_major_range_length"), + requireField(semiMinor, "semi_minor_range_length"), + requireField(orientation, "semi_major_range_orientation"), + semiHeight); + } + + private List readIntegerList(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List list = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + list.add(parser.getIntValue()); + } + return list; + } + + private static T requireField(T value, String field) { + if (value == null) { + throw new CpmValidationException("Missing mandatory field: " + field); + } + return value; + } + + private static void expect(JsonToken actual, JsonToken expected) throws JsonParseException { + if (actual != expected) { + throw new JsonParseException(null, "Expected " + expected + " but got " + actual); + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/codec/CpmWriter121.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/codec/CpmWriter121.java new file mode 100644 index 000000000..0024ff719 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/codec/CpmWriter121.java @@ -0,0 +1,662 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.codec; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import com.orange.iot3mobility.messages.cpm.v121.model.CpmEnvelope121; +import com.orange.iot3mobility.messages.cpm.v121.model.CpmMessage121; +import com.orange.iot3mobility.messages.cpm.v121.model.defs.*; +import com.orange.iot3mobility.messages.cpm.v121.model.freespacecontainer.FreeSpaceAddendum; +import com.orange.iot3mobility.messages.cpm.v121.model.freespacecontainer.FreeSpaceAddendumContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.freespacecontainer.FreeSpaceArea; +import com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer.ManagementConfidence; +import com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer.ManagementContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer.PositionConfidenceEllipse; +import com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer.ReferencePosition; +import com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer.*; +import com.orange.iot3mobility.messages.cpm.v121.model.sensorinformationcontainer.*; +import com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer.OriginatingRsuContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer.OriginatingVehicleContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer.StationDataContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer.VehicleConfidence; +import com.orange.iot3mobility.messages.cpm.v121.validation.CpmValidator121; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; + +public final class CpmWriter121 { + + private final JsonFactory jsonFactory; + + public CpmWriter121(JsonFactory jsonFactory) { + this.jsonFactory = jsonFactory; + } + + public void write(CpmEnvelope121 envelope, OutputStream out) throws IOException { + CpmValidator121.validateEnvelope(envelope); + + try (JsonGenerator gen = jsonFactory.createGenerator(out)) { + gen.writeStartObject(); + gen.writeStringField("type", envelope.type()); + gen.writeStringField("origin", envelope.origin()); + gen.writeStringField("version", envelope.version()); + gen.writeStringField("source_uuid", envelope.sourceUuid()); + gen.writeNumberField("timestamp", envelope.timestamp()); + gen.writeFieldName("message"); + writeMessage(gen, envelope.message()); + gen.writeEndObject(); + } + } + + private void writeMessage(JsonGenerator gen, CpmMessage121 msg) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("protocol_version", msg.protocolVersion()); + gen.writeNumberField("station_id", msg.stationId()); + gen.writeNumberField("generation_delta_time", msg.generationDeltaTime()); + + gen.writeFieldName("management_container"); + writeManagementContainer(gen, msg.managementContainer()); + + if (msg.stationDataContainer() != null) { + gen.writeFieldName("station_data_container"); + writeStationDataContainer(gen, msg.stationDataContainer()); + } + if (msg.sensorInformationContainer() != null) { + SensorInformationContainer container = msg.sensorInformationContainer(); + gen.writeArrayFieldStart("sensor_information_container"); + for (SensorInformation info : container.sensorInformation()) { + writeSensorInformation(gen, info); + } + gen.writeEndArray(); + } + if (msg.perceivedObjectContainer() != null) { + PerceivedObjectContainer container = msg.perceivedObjectContainer(); + gen.writeArrayFieldStart("perceived_object_container"); + for (PerceivedObject obj : container.perceivedObjects()) { + writePerceivedObject(gen, obj); + } + gen.writeEndArray(); + } + if (msg.freeSpaceAddendumContainer() != null) { + FreeSpaceAddendumContainer container = msg.freeSpaceAddendumContainer(); + gen.writeArrayFieldStart("free_space_addendum_container"); + for (FreeSpaceAddendum addendum : container.freeSpaceAddenda()) { + writeFreeSpaceAddendum(gen, addendum); + } + gen.writeEndArray(); + } + gen.writeEndObject(); + } + + /* --------------------------------------------------------------------- */ + /* Management container */ + /* --------------------------------------------------------------------- */ + + private void writeManagementContainer(JsonGenerator gen, ManagementContainer container) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("station_type", container.stationType()); + gen.writeFieldName("reference_position"); + writeReferencePosition(gen, container.referencePosition()); + gen.writeFieldName("confidence"); + writeManagementConfidence(gen, container.confidence()); + gen.writeEndObject(); + } + + private void writeReferencePosition(JsonGenerator gen, ReferencePosition reference) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("latitude", reference.latitude()); + gen.writeNumberField("longitude", reference.longitude()); + gen.writeNumberField("altitude", reference.altitude()); + gen.writeEndObject(); + } + + private void writeManagementConfidence(JsonGenerator gen, ManagementConfidence confidence) throws IOException { + gen.writeStartObject(); + gen.writeFieldName("position_confidence_ellipse"); + writePositionConfidenceEllipse(gen, confidence.positionConfidenceEllipse()); + gen.writeNumberField("altitude", confidence.altitude()); + gen.writeEndObject(); + } + + private void writePositionConfidenceEllipse(JsonGenerator gen, PositionConfidenceEllipse ellipse) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("semi_major_confidence", ellipse.semiMajorConfidence()); + gen.writeNumberField("semi_minor_confidence", ellipse.semiMinorConfidence()); + gen.writeNumberField("semi_major_orientation", ellipse.semiMajorOrientation()); + gen.writeEndObject(); + } + + /* --------------------------------------------------------------------- */ + /* Station data container */ + /* --------------------------------------------------------------------- */ + + private void writeStationDataContainer(JsonGenerator gen, StationDataContainer container) throws IOException { + gen.writeStartObject(); + if (container.originatingVehicleContainer() != null) { + gen.writeFieldName("originating_vehicle_container"); + writeOriginatingVehicleContainer(gen, container.originatingVehicleContainer()); + } + if (container.originatingRsuContainer() != null) { + gen.writeFieldName("originating_rsu_container"); + writeOriginatingRsuContainer(gen, container.originatingRsuContainer()); + } + gen.writeEndObject(); + } + + private void writeOriginatingVehicleContainer(JsonGenerator gen, OriginatingVehicleContainer container) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("heading", container.heading()); + gen.writeNumberField("speed", container.speed()); + gen.writeFieldName("confidence"); + writeVehicleConfidence(gen, container.confidence()); + if (container.driveDirection() != null) { + gen.writeNumberField("drive_direction", container.driveDirection()); + } + if (container.vehicleLength() != null) { + gen.writeNumberField("vehicle_length", container.vehicleLength()); + } + if (container.vehicleWidth() != null) { + gen.writeNumberField("vehicle_width", container.vehicleWidth()); + } + if (container.longitudinalAcceleration() != null) { + gen.writeNumberField("longitudinal_acceleration", container.longitudinalAcceleration()); + } + if (container.yawRate() != null) { + gen.writeNumberField("yaw_rate", container.yawRate()); + } + if (container.lateralAcceleration() != null) { + gen.writeNumberField("lateral_acceleration", container.lateralAcceleration()); + } + if (container.verticalAcceleration() != null) { + gen.writeNumberField("vertical_acceleration", container.verticalAcceleration()); + } + gen.writeEndObject(); + } + + private void writeVehicleConfidence(JsonGenerator gen, VehicleConfidence confidence) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("heading", confidence.heading()); + gen.writeNumberField("speed", confidence.speed()); + if (confidence.vehicleLength() != null) { + gen.writeNumberField("vehicle_length", confidence.vehicleLength()); + } + if (confidence.yawRate() != null) { + gen.writeNumberField("yaw_rate", confidence.yawRate()); + } + if (confidence.longitudinalAcceleration() != null) { + gen.writeNumberField("longitudinal_acceleration", confidence.longitudinalAcceleration()); + } + if (confidence.lateralAcceleration() != null) { + gen.writeNumberField("lateral_acceleration", confidence.lateralAcceleration()); + } + if (confidence.verticalAcceleration() != null) { + gen.writeNumberField("vertical_acceleration", confidence.verticalAcceleration()); + } + gen.writeEndObject(); + } + + private void writeOriginatingRsuContainer(JsonGenerator gen, OriginatingRsuContainer container) throws IOException { + gen.writeStartObject(); + if (container.region() != null) { + gen.writeNumberField("region", container.region()); + } + if (container.intersectionReferenceId() != null) { + gen.writeNumberField("intersection_reference_id", container.intersectionReferenceId()); + } + if (container.roadSegmentReferenceId() != null) { + gen.writeNumberField("road_segment_reference_id", container.roadSegmentReferenceId()); + } + gen.writeEndObject(); + } + + /* --------------------------------------------------------------------- */ + /* Sensor information container */ + /* --------------------------------------------------------------------- */ + + private void writeSensorInformation(JsonGenerator gen, SensorInformation info) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("sensor_id", info.sensorId()); + gen.writeNumberField("type", info.type()); + gen.writeFieldName("detection_area"); + writeDetectionArea(gen, info.detectionArea()); + gen.writeEndObject(); + } + + private void writeDetectionArea(JsonGenerator gen, DetectionArea area) throws IOException { + gen.writeStartObject(); + if (area.vehicleSensor() != null) { + gen.writeFieldName("vehicle_sensor"); + writeVehicleSensor(gen, area.vehicleSensor()); + } + if (area.stationarySensorRadial() != null) { + gen.writeFieldName("stationary_sensor_radial"); + writeStationarySensorRadial(gen, area.stationarySensorRadial()); + } + if (area.stationarySensorPolygon() != null) { + gen.writeFieldName("stationary_sensor_polygon"); + writeAreaPolygon(gen, area.stationarySensorPolygon()); + } + if (area.stationarySensorCircular() != null) { + gen.writeFieldName("stationary_sensor_circular"); + writeAreaCircular(gen, area.stationarySensorCircular()); + } + if (area.stationarySensorEllipse() != null) { + gen.writeFieldName("stationary_sensor_ellipse"); + writeAreaEllipse(gen, area.stationarySensorEllipse()); + } + if (area.stationarySensorRectangle() != null) { + gen.writeFieldName("stationary_sensor_rectangle"); + writeAreaRectangle(gen, area.stationarySensorRectangle()); + } + gen.writeEndObject(); + } + + private void writeVehicleSensor(JsonGenerator gen, VehicleSensor sensor) throws IOException { + gen.writeStartObject(); + if (sensor.refPointId() != null) { + gen.writeNumberField("ref_point_id", sensor.refPointId()); + } + gen.writeNumberField("x_sensor_offset", sensor.xSensorOffset()); + gen.writeNumberField("y_sensor_offset", sensor.ySensorOffset()); + if (sensor.zSensorOffset() != null) { + gen.writeNumberField("z_sensor_offset", sensor.zSensorOffset()); + } + gen.writeArrayFieldStart("vehicle_sensor_property_list"); + for (VehicleSensorProperty property : sensor.vehicleSensorPropertyList()) { + writeVehicleSensorProperty(gen, property); + } + gen.writeEndArray(); + gen.writeEndObject(); + } + + private void writeVehicleSensorProperty(JsonGenerator gen, VehicleSensorProperty property) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("range", property.range()); + gen.writeNumberField("horizontal_opening_angle_start", property.horizontalOpeningAngleStart()); + gen.writeNumberField("horizontal_opening_angle_end", property.horizontalOpeningAngleEnd()); + if (property.verticalOpeningAngleStart() != null) { + gen.writeNumberField("vertical_opening_angle_start", property.verticalOpeningAngleStart()); + } + if (property.verticalOpeningAngleEnd() != null) { + gen.writeNumberField("vertical_opening_angle_end", property.verticalOpeningAngleEnd()); + } + gen.writeEndObject(); + } + + private void writeStationarySensorRadial(JsonGenerator gen, StationarySensorRadial radial) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("range", radial.range()); + gen.writeNumberField("horizontal_opening_angle_start", radial.horizontalOpeningAngleStart()); + gen.writeNumberField("horizontal_opening_angle_end", radial.horizontalOpeningAngleEnd()); + if (radial.verticalOpeningAngleStart() != null) { + gen.writeNumberField("vertical_opening_angle_start", radial.verticalOpeningAngleStart()); + } + if (radial.verticalOpeningAngleEnd() != null) { + gen.writeNumberField("vertical_opening_angle_end", radial.verticalOpeningAngleEnd()); + } + if (radial.sensorPositionOffset() != null) { + gen.writeFieldName("sensor_position_offset"); + writeOffset(gen, radial.sensorPositionOffset()); + } + gen.writeEndObject(); + } + + /* --------------------------------------------------------------------- */ + /* Perceived object container */ + /* --------------------------------------------------------------------- */ + + private void writePerceivedObject(JsonGenerator gen, PerceivedObject object) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("object_id", object.objectId()); + gen.writeNumberField("time_of_measurement", object.timeOfMeasurement()); + gen.writeNumberField("x_distance", object.xDistance()); + gen.writeNumberField("y_distance", object.yDistance()); + if (object.zDistance() != null) { + gen.writeNumberField("z_distance", object.zDistance()); + } + gen.writeNumberField("x_speed", object.xSpeed()); + gen.writeNumberField("y_speed", object.ySpeed()); + if (object.zSpeed() != null) { + gen.writeNumberField("z_speed", object.zSpeed()); + } + if (object.xAcceleration() != null) { + gen.writeNumberField("x_acceleration", object.xAcceleration()); + } + if (object.yAcceleration() != null) { + gen.writeNumberField("y_acceleration", object.yAcceleration()); + } + if (object.zAcceleration() != null) { + gen.writeNumberField("z_acceleration", object.zAcceleration()); + } + if (object.rollAngle() != null) { + gen.writeNumberField("roll_angle", object.rollAngle()); + } + if (object.pitchAngle() != null) { + gen.writeNumberField("pitch_angle", object.pitchAngle()); + } + if (object.yawAngle() != null) { + gen.writeNumberField("yaw_angle", object.yawAngle()); + } + if (object.rollRate() != null) { + gen.writeNumberField("roll_rate", object.rollRate()); + } + if (object.pitchRate() != null) { + gen.writeNumberField("pitch_rate", object.pitchRate()); + } + if (object.yawRate() != null) { + gen.writeNumberField("yaw_rate", object.yawRate()); + } + if (object.rollAcceleration() != null) { + gen.writeNumberField("roll_acceleration", object.rollAcceleration()); + } + if (object.pitchAcceleration() != null) { + gen.writeNumberField("pitch_acceleration", object.pitchAcceleration()); + } + if (object.yawAcceleration() != null) { + gen.writeNumberField("yaw_acceleration", object.yawAcceleration()); + } + if (object.lowerTriangularCorrelationMatrixColumns() != null) { + gen.writeArrayFieldStart("lower_triangular_correlation_matrix_columns"); + for (List column : object.lowerTriangularCorrelationMatrixColumns().columns()) { + writeIntegerArray(gen, column); + } + gen.writeEndArray(); + } + if (object.planarObjectDimension1() != null) { + gen.writeNumberField("planar_object_dimension_1", object.planarObjectDimension1()); + } + if (object.planarObjectDimension2() != null) { + gen.writeNumberField("planar_object_dimension_2", object.planarObjectDimension2()); + } + if (object.verticalObjectDimension() != null) { + gen.writeNumberField("vertical_object_dimension", object.verticalObjectDimension()); + } + if (object.objectRefPoint() != null) { + gen.writeNumberField("object_ref_point", object.objectRefPoint()); + } + gen.writeNumberField("object_age", object.objectAge()); + if (object.sensorIdList() != null) { + gen.writeArrayFieldStart("sensor_id_list"); + for (Integer id : object.sensorIdList()) { + gen.writeNumber(id); + } + gen.writeEndArray(); + } + if (object.dynamicStatus() != null) { + gen.writeNumberField("dynamic_status", object.dynamicStatus()); + } + if (object.classification() != null) { + gen.writeArrayFieldStart("classification"); + for (ObjectClassification classification : object.classification()) { + writeObjectClassification(gen, classification); + } + gen.writeEndArray(); + } + if (object.matchedPosition() != null) { + gen.writeFieldName("matched_position"); + writeMapPosition(gen, object.matchedPosition()); + } + gen.writeFieldName("confidence"); + writePerceivedObjectConfidence(gen, object.confidence()); + gen.writeEndObject(); + } + + private void writePerceivedObjectConfidence(JsonGenerator gen, PerceivedObjectConfidence confidence) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("x_distance", confidence.xDistance()); + gen.writeNumberField("y_distance", confidence.yDistance()); + if (confidence.zDistance() != null) { + gen.writeNumberField("z_distance", confidence.zDistance()); + } + gen.writeNumberField("x_speed", confidence.xSpeed()); + gen.writeNumberField("y_speed", confidence.ySpeed()); + if (confidence.zSpeed() != null) { + gen.writeNumberField("z_speed", confidence.zSpeed()); + } + if (confidence.xAcceleration() != null) { + gen.writeNumberField("x_acceleration", confidence.xAcceleration()); + } + if (confidence.yAcceleration() != null) { + gen.writeNumberField("y_acceleration", confidence.yAcceleration()); + } + if (confidence.zAcceleration() != null) { + gen.writeNumberField("z_acceleration", confidence.zAcceleration()); + } + if (confidence.rollAngle() != null) { + gen.writeNumberField("roll_angle", confidence.rollAngle()); + } + if (confidence.pitchAngle() != null) { + gen.writeNumberField("pitch_angle", confidence.pitchAngle()); + } + if (confidence.yawAngle() != null) { + gen.writeNumberField("yaw_angle", confidence.yawAngle()); + } + if (confidence.rollRate() != null) { + gen.writeNumberField("roll_rate", confidence.rollRate()); + } + if (confidence.pitchRate() != null) { + gen.writeNumberField("pitch_rate", confidence.pitchRate()); + } + if (confidence.yawRate() != null) { + gen.writeNumberField("yaw_rate", confidence.yawRate()); + } + if (confidence.rollAcceleration() != null) { + gen.writeNumberField("roll_acceleration", confidence.rollAcceleration()); + } + if (confidence.pitchAcceleration() != null) { + gen.writeNumberField("pitch_acceleration", confidence.pitchAcceleration()); + } + if (confidence.yawAcceleration() != null) { + gen.writeNumberField("yaw_acceleration", confidence.yawAcceleration()); + } + if (confidence.planarObjectDimension1() != null) { + gen.writeNumberField("planar_object_dimension_1", confidence.planarObjectDimension1()); + } + if (confidence.planarObjectDimension2() != null) { + gen.writeNumberField("planar_object_dimension_2", confidence.planarObjectDimension2()); + } + if (confidence.verticalObjectDimension() != null) { + gen.writeNumberField("vertical_object_dimension", confidence.verticalObjectDimension()); + } + if (confidence.longitudinalLanePosition() != null) { + gen.writeNumberField("longitudinal_lane_position", confidence.longitudinalLanePosition()); + } + gen.writeNumberField("object", confidence.object()); + gen.writeEndObject(); + } + + private void writeObjectClassification(JsonGenerator gen, ObjectClassification classification) throws IOException { + gen.writeStartObject(); + gen.writeFieldName("object_class"); + writeObjectClass(gen, classification.objectClass()); + gen.writeNumberField("confidence", classification.confidence()); + gen.writeEndObject(); + } + + private void writeObjectClass(JsonGenerator gen, ObjectClass objectClass) throws IOException { + gen.writeStartObject(); + if (objectClass.vehicle() != null) { + gen.writeNumberField("vehicle", objectClass.vehicle()); + } + if (objectClass.singleVru() != null) { + gen.writeFieldName("single_vru"); + writeObjectClassVru(gen, objectClass.singleVru()); + } + if (objectClass.vruGroup() != null) { + gen.writeFieldName("vru_group"); + writeObjectClassGroup(gen, objectClass.vruGroup()); + } + if (objectClass.other() != null) { + gen.writeNumberField("other", objectClass.other()); + } + gen.writeEndObject(); + } + + private void writeObjectClassVru(JsonGenerator gen, ObjectClassVru vru) throws IOException { + gen.writeStartObject(); + if (vru.pedestrian() != null) { + gen.writeNumberField("pedestrian", vru.pedestrian()); + } + if (vru.bicyclist() != null) { + gen.writeNumberField("bicyclist", vru.bicyclist()); + } + if (vru.motorcylist() != null) { + gen.writeNumberField("motorcylist", vru.motorcylist()); + } + if (vru.animal() != null) { + gen.writeNumberField("animal", vru.animal()); + } + gen.writeEndObject(); + } + + private void writeObjectClassGroup(JsonGenerator gen, ObjectClassGroup group) throws IOException { + gen.writeStartObject(); + gen.writeFieldName("group_type"); + writeGroupType(gen, group.groupType()); + gen.writeNumberField("group_size", group.groupSize()); + if (group.clusterId() != null) { + gen.writeNumberField("cluster_id", group.clusterId()); + } + gen.writeEndObject(); + } + + private void writeGroupType(JsonGenerator gen, GroupType type) throws IOException { + gen.writeStartObject(); + gen.writeBooleanField("pedestrian", type.pedestrian()); + gen.writeBooleanField("bicyclist", type.bicyclist()); + gen.writeBooleanField("motorcyclist", type.motorcyclist()); + gen.writeBooleanField("animal", type.animal()); + gen.writeEndObject(); + } + + private void writeMapPosition(JsonGenerator gen, MapPosition position) throws IOException { + gen.writeStartObject(); + if (position.laneId() != null) { + gen.writeNumberField("lane_id", position.laneId()); + } + if (position.longitudinalLanePosition() != null) { + gen.writeNumberField("longitudinal_lane_position", position.longitudinalLanePosition()); + } + gen.writeEndObject(); + } + + /* --------------------------------------------------------------------- */ + /* Free space addendum container */ + /* --------------------------------------------------------------------- */ + + private void writeFreeSpaceAddendum(JsonGenerator gen, FreeSpaceAddendum addendum) throws IOException { + gen.writeStartObject(); + gen.writeFieldName("free_space_area"); + writeFreeSpaceArea(gen, addendum.freeSpaceArea()); + gen.writeNumberField("free_space_confidence", addendum.freeSpaceConfidence()); + if (addendum.sensorIdList() != null) { + gen.writeArrayFieldStart("sensor_id_list"); + for (Integer id : addendum.sensorIdList()) { + gen.writeNumber(id); + } + gen.writeEndArray(); + } + if (addendum.shadowingApplies() != null) { + gen.writeBooleanField("shadowing_applies", addendum.shadowingApplies()); + } + gen.writeEndObject(); + } + + private void writeFreeSpaceArea(JsonGenerator gen, FreeSpaceArea area) throws IOException { + gen.writeStartObject(); + if (area.freeSpacePolygon() != null) { + gen.writeFieldName("free_space_polygon"); + writeAreaPolygon(gen, area.freeSpacePolygon()); + } + if (area.freeSpaceCircular() != null) { + gen.writeFieldName("free_space_circular"); + writeAreaCircular(gen, area.freeSpaceCircular()); + } + if (area.freeSpaceEllipse() != null) { + gen.writeFieldName("free_space_ellipse"); + writeAreaEllipse(gen, area.freeSpaceEllipse()); + } + if (area.freeSpaceRectangle() != null) { + gen.writeFieldName("free_space_rectangle"); + writeAreaRectangle(gen, area.freeSpaceRectangle()); + } + gen.writeEndObject(); + } + + /* --------------------------------------------------------------------- */ + /* Shared helpers */ + /* --------------------------------------------------------------------- */ + + private void writeOffset(JsonGenerator gen, Offset offset) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("x", offset.x()); + gen.writeNumberField("y", offset.y()); + if (offset.z() != null) { + gen.writeNumberField("z", offset.z()); + } + gen.writeEndObject(); + } + + private void writeAreaPolygon(JsonGenerator gen, AreaPolygon polygon) throws IOException { + gen.writeStartArray(); + for (Offset offset : polygon.offsets()) { + writeOffset(gen, offset); + } + gen.writeEndArray(); + } + + private void writeAreaCircular(JsonGenerator gen, AreaCircular circular) throws IOException { + gen.writeStartObject(); + if (circular.nodeCenterPoint() != null) { + gen.writeFieldName("node_center_point"); + writeOffset(gen, circular.nodeCenterPoint()); + } + gen.writeNumberField("radius", circular.radius()); + gen.writeEndObject(); + } + + private void writeAreaEllipse(JsonGenerator gen, AreaEllipse ellipse) throws IOException { + gen.writeStartObject(); + if (ellipse.nodeCenterPoint() != null) { + gen.writeFieldName("node_center_point"); + writeOffset(gen, ellipse.nodeCenterPoint()); + } + gen.writeNumberField("semi_major_range_length", ellipse.semiMajorRangeLength()); + gen.writeNumberField("semi_minor_range_length", ellipse.semiMinorRangeLength()); + gen.writeNumberField("semi_major_range_orientation", ellipse.semiMajorRangeOrientation()); + if (ellipse.semiHeight() != null) { + gen.writeNumberField("semi_height", ellipse.semiHeight()); + } + gen.writeEndObject(); + } + + private void writeAreaRectangle(JsonGenerator gen, AreaRectangle rectangle) throws IOException { + gen.writeStartObject(); + if (rectangle.nodeCenterPoint() != null) { + gen.writeFieldName("node_center_point"); + writeOffset(gen, rectangle.nodeCenterPoint()); + } + gen.writeNumberField("semi_major_range_length", rectangle.semiMajorRangeLength()); + gen.writeNumberField("semi_minor_range_length", rectangle.semiMinorRangeLength()); + gen.writeNumberField("semi_major_range_orientation", rectangle.semiMajorRangeOrientation()); + if (rectangle.semiHeight() != null) { + gen.writeNumberField("semi_height", rectangle.semiHeight()); + } + gen.writeEndObject(); + } + + private void writeIntegerArray(JsonGenerator gen, List values) throws IOException { + gen.writeStartArray(); + for (Integer value : values) { + gen.writeNumber(value); + } + gen.writeEndArray(); + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/CpmEnvelope121.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/CpmEnvelope121.java new file mode 100644 index 000000000..19e81859c --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/CpmEnvelope121.java @@ -0,0 +1,95 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model; + +/** + * CpmEnvelope121 - base class to build a CPM v1.2.1 with its header + * + * @param type message type (cpm). Value: cpm. + * @param origin {@link Origin}. Value: self, global_application, mec_application, on_board_application. + * @param version json message format version (1.2.1). Value: 1.2.1. + * @param sourceUuid identifier. + * @param timestamp Unit: millisecond. The timestamp when the message was generated since Unix Epoch (1970/01/01). + * @param message {@link CpmMessage121} + */ +public record CpmEnvelope121( + String type, + String origin, + String version, + String sourceUuid, + long timestamp, + CpmMessage121 message) { + + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for CpmEnvelope121. + *

+ * Mandatory fields: + *

+ */ + public static final class Builder { + private final String type; + private String origin; + private final String version; + private String sourceUuid; + private Long timestamp; + private CpmMessage121 message; + + private Builder() { + this.type = "cpm"; + this.version = "1.2.1"; + } + + public Builder origin(String origin) { + this.origin = origin; + return this; + } + + public Builder sourceUuid(String sourceUuid) { + this.sourceUuid = sourceUuid; + return this; + } + + public Builder timestamp(long timestamp) { + this.timestamp = timestamp; + return this; + } + + public Builder message(CpmMessage121 message) { + this.message = message; + return this; + } + + public CpmEnvelope121 build() { + return new CpmEnvelope121( + requireNonNull(type, "type"), + requireNonNull(origin, "origin"), + requireNonNull(version, "version"), + requireNonNull(sourceUuid, "source_uuid"), + requireNonNull(timestamp, "timestamp"), + requireNonNull(message, "message")); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/CpmMessage121.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/CpmMessage121.java new file mode 100644 index 000000000..b19203f8d --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/CpmMessage121.java @@ -0,0 +1,111 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model; + +import com.orange.iot3mobility.messages.cpm.v121.model.freespacecontainer.FreeSpaceAddendumContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer.ManagementContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer.PerceivedObjectContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.sensorinformationcontainer.SensorInformationContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer.StationDataContainer; + +/** + * CPM v1.2.1 message body. + * + * @param protocolVersion Version of the message and/or communication protocol. Value: [0..255]. + * @param stationId Identifier. Value: [0..4294967295]. + * @param generationDeltaTime Generation delta time. Unit: millisecond (TimestampIts mod 65536). Value: [0..65535]. + * @param managementContainer {@link ManagementContainer} + * @param stationDataContainer Optional {@link StationDataContainer} + * @param sensorInformationContainer Optional {@link SensorInformationContainer} + * @param perceivedObjectContainer Optional {@link PerceivedObjectContainer} + * @param freeSpaceAddendumContainer Optional {@link FreeSpaceAddendumContainer} + */ +public record CpmMessage121( + int protocolVersion, + long stationId, + int generationDeltaTime, + ManagementContainer managementContainer, + StationDataContainer stationDataContainer, + SensorInformationContainer sensorInformationContainer, + PerceivedObjectContainer perceivedObjectContainer, + FreeSpaceAddendumContainer freeSpaceAddendumContainer) { + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private Integer protocolVersion; + private Long stationId; + private Integer generationDeltaTime; + private ManagementContainer managementContainer; + private StationDataContainer stationDataContainer; + private SensorInformationContainer sensorInformationContainer; + private PerceivedObjectContainer perceivedObjectContainer; + private FreeSpaceAddendumContainer freeSpaceAddendumContainer; + + public Builder protocolVersion(int protocolVersion) { + this.protocolVersion = protocolVersion; + return this; + } + + public Builder stationId(long stationId) { + this.stationId = stationId; + return this; + } + + public Builder generationDeltaTime(int generationDeltaTime) { + this.generationDeltaTime = generationDeltaTime; + return this; + } + + public Builder managementContainer(ManagementContainer managementContainer) { + this.managementContainer = managementContainer; + return this; + } + + public Builder stationDataContainer(StationDataContainer stationDataContainer) { + this.stationDataContainer = stationDataContainer; + return this; + } + + public Builder sensorInformationContainer(SensorInformationContainer sensorInformationContainer) { + this.sensorInformationContainer = sensorInformationContainer; + return this; + } + + public Builder perceivedObjectContainer(PerceivedObjectContainer perceivedObjectContainer) { + this.perceivedObjectContainer = perceivedObjectContainer; + return this; + } + + public Builder freeSpaceAddendumContainer(FreeSpaceAddendumContainer freeSpaceAddendumContainer) { + this.freeSpaceAddendumContainer = freeSpaceAddendumContainer; + return this; + } + + public CpmMessage121 build() { + return new CpmMessage121( + requireNonNull(protocolVersion, "protocol_version"), + requireNonNull(stationId, "station_id"), + requireNonNull(generationDeltaTime, "generation_delta_time"), + requireNonNull(managementContainer, "management_container"), + stationDataContainer, + sensorInformationContainer, + perceivedObjectContainer, + freeSpaceAddendumContainer); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/Origin.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/Origin.java new file mode 100644 index 000000000..9863bc9ee --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/Origin.java @@ -0,0 +1,27 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model; + +/** + * Origin v1.2.1 + *

+ * The entity responsible for this message. + * Value: self, global_application, mec_application, on_board_application. + */ +public enum Origin { + SELF("self"), + GLOBAL_APPLICATION("global_application"), + MEC_APPLICATION("mec_application"), + ON_BOARD_APPLICATION("on_board_application"); + + public final String value; + + Origin(String value) { + this.value = value; + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/defs/AreaCircular.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/defs/AreaCircular.java new file mode 100644 index 000000000..ce1d6710d --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/defs/AreaCircular.java @@ -0,0 +1,19 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.defs; + +/** + * Circular area. + * + * @param nodeCenterPoint Optional center offset. + * @param radius Radius. Unit: 0.1 meter. Value: zeroPointOneMeter(1), oneMeter(10). + */ +public record AreaCircular( + Offset nodeCenterPoint, + int radius) {} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/defs/AreaEllipse.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/defs/AreaEllipse.java new file mode 100644 index 000000000..61636b06b --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/defs/AreaEllipse.java @@ -0,0 +1,25 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.defs; + +/** + * Elliptical area. + * + * @param nodeCenterPoint Optional center offset. + * @param semiMajorRangeLength Major radius of the ellipse. Unit: 0.1 meter. Value: zeroPointOneMeter(1), oneMeter(10). + * @param semiMinorRangeLength Minor radius of the ellipse. Unit: 0.1 meter. Value: zeroPointOneMeter(1), oneMeter(10). + * @param semiMajorRangeOrientation Orientation of the semi major range length. Unit: 0.1 degree. Value: wgs84North(0), + * wgs84East(900), wgs84South(1800), wgs84West(2700), unavailable(3601). + * @param semiHeight Optional semi height. Unit: 0.1 meter. Value: zeroPointOneMeter(1), oneMeter(10). + */ +public record AreaEllipse( + Offset nodeCenterPoint, + int semiMajorRangeLength, + int semiMinorRangeLength, + int semiMajorRangeOrientation, + Integer semiHeight) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/defs/AreaPolygon.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/defs/AreaPolygon.java new file mode 100644 index 000000000..1f07a0a0a --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/defs/AreaPolygon.java @@ -0,0 +1,18 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.defs; + +import java.util.List; + +/** + * Polygonal area. + * + * @param offsets Polygon as a list of {@link Offset} points. Size: [3..16]. + */ +public record AreaPolygon(List offsets) {} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/defs/AreaRectangle.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/defs/AreaRectangle.java new file mode 100644 index 000000000..e93e5eea9 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/defs/AreaRectangle.java @@ -0,0 +1,25 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.defs; + +/** + * Rectangular area. + * + * @param nodeCenterPoint Optional center offset. + * @param semiMajorRangeLength Half length of the rectangle. Unit: 0.1 meter. Value: zeroPointOneMeter(1), oneMeter(10). + * @param semiMinorRangeLength Half width of the rectangle. Unit: 0.1 meter. Value: zeroPointOneMeter(1), oneMeter(10). + * @param semiMajorRangeOrientation Orientation of the semi major range length. Unit: 0.1 degree. Value: wgs84North(0), + * wgs84East(900), wgs84South(1800), wgs84West(2700), unavailable(3601). + * @param semiHeight Optional semi height. Unit: 0.1 meter. Value: zeroPointOneMeter(1), oneMeter(10). + */ +public record AreaRectangle( + Offset nodeCenterPoint, + int semiMajorRangeLength, + int semiMinorRangeLength, + int semiMajorRangeOrientation, + Integer semiHeight) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/defs/Offset.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/defs/Offset.java new file mode 100644 index 000000000..8cd9453e6 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/defs/Offset.java @@ -0,0 +1,21 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.defs; + +/** + * Offset position. + * + * @param x X offset. Value: [-32768..32767]. + * @param y Y offset. Value: [-32768..32767]. + * @param z Optional Z offset. Value: [-32768..32767]. + */ +public record Offset( + int x, + int y, + Integer z) {} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/freespacecontainer/FreeSpaceAddendum.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/freespacecontainer/FreeSpaceAddendum.java new file mode 100644 index 000000000..a4b19333e --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/freespacecontainer/FreeSpaceAddendum.java @@ -0,0 +1,25 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.freespacecontainer; + +import java.util.List; + +/** + * Free space addendum. + * + * @param freeSpaceArea Free space area for which the confidence applies. {@link FreeSpaceArea} + * @param freeSpaceConfidence Isotropic free space confidence for the area. Value: unknown(0), onePercent(1), + * oneHundredPercent(100), unavailable(101). + * @param sensorIdList List of sensor IDs which performed the measurement. Value: [0..255]. + * @param shadowingApplies Optional. True if simple shadowing mechanism applies within the area. + */ +public record FreeSpaceAddendum( + FreeSpaceArea freeSpaceArea, + int freeSpaceConfidence, + List sensorIdList, + Boolean shadowingApplies) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/freespacecontainer/FreeSpaceAddendumContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/freespacecontainer/FreeSpaceAddendumContainer.java new file mode 100644 index 000000000..95620eadd --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/freespacecontainer/FreeSpaceAddendumContainer.java @@ -0,0 +1,17 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.freespacecontainer; + +import java.util.List; + +/** + * Free space addendum container. + * + * @param freeSpaceAddenda List of {@link FreeSpaceAddendum}. Size: [1..128]. + */ +public record FreeSpaceAddendumContainer(List freeSpaceAddenda) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/freespacecontainer/FreeSpaceArea.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/freespacecontainer/FreeSpaceArea.java new file mode 100644 index 000000000..fabd417f9 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/freespacecontainer/FreeSpaceArea.java @@ -0,0 +1,69 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.freespacecontainer; + +import com.orange.iot3mobility.messages.cpm.v121.model.defs.AreaCircular; +import com.orange.iot3mobility.messages.cpm.v121.model.defs.AreaEllipse; +import com.orange.iot3mobility.messages.cpm.v121.model.defs.AreaPolygon; +import com.orange.iot3mobility.messages.cpm.v121.model.defs.AreaRectangle; + +/** + * Free space area definition. + *

+ * Exactly one of the free space area options should be provided. + * + * @param freeSpacePolygon {@link AreaPolygon} + * @param freeSpaceCircular {@link AreaCircular} + * @param freeSpaceEllipse {@link AreaEllipse} + * @param freeSpaceRectangle {@link AreaRectangle} + */ +public record FreeSpaceArea( + AreaPolygon freeSpacePolygon, + AreaCircular freeSpaceCircular, + AreaEllipse freeSpaceEllipse, + AreaRectangle freeSpaceRectangle) { + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private AreaPolygon freeSpacePolygon; + private AreaCircular freeSpaceCircular; + private AreaEllipse freeSpaceEllipse; + private AreaRectangle freeSpaceRectangle; + + public Builder freeSpacePolygon(AreaPolygon freeSpacePolygon) { + this.freeSpacePolygon = freeSpacePolygon; + return this; + } + + public Builder freeSpaceCircular(AreaCircular freeSpaceCircular) { + this.freeSpaceCircular = freeSpaceCircular; + return this; + } + + public Builder freeSpaceEllipse(AreaEllipse freeSpaceEllipse) { + this.freeSpaceEllipse = freeSpaceEllipse; + return this; + } + + public Builder freeSpaceRectangle(AreaRectangle freeSpaceRectangle) { + this.freeSpaceRectangle = freeSpaceRectangle; + return this; + } + + public FreeSpaceArea build() { + return new FreeSpaceArea( + freeSpacePolygon, + freeSpaceCircular, + freeSpaceEllipse, + freeSpaceRectangle); + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/managementcontainer/ManagementConfidence.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/managementcontainer/ManagementConfidence.java new file mode 100644 index 000000000..3b4f43522 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/managementcontainer/ManagementConfidence.java @@ -0,0 +1,20 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer; + +/** + * Confidence information for management container. + * + * @param positionConfidenceEllipse {@link PositionConfidenceEllipse} + * @param altitude Altitude confidence. Value: alt-000-01(0), alt-000-02(1), alt-000-05(2), alt-000-10(3), + * alt-000-20(4), alt-000-50(5), alt-001-00(6), alt-002-00(7), alt-005-00(8), alt-010-00(9), + * alt-020-00(10), alt-050-00(11), alt-100-00(12), alt-200-00(13), outOfRange(14), unavailable(15). + */ +public record ManagementConfidence( + PositionConfidenceEllipse positionConfidenceEllipse, + int altitude) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/managementcontainer/ManagementContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/managementcontainer/ManagementContainer.java new file mode 100644 index 000000000..1e0037a32 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/managementcontainer/ManagementContainer.java @@ -0,0 +1,62 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer; + +/** + * Management container. + * + * @param stationType Station type. Value: unknown(0), pedestrian(1), cyclist(2), moped(3), motorcycle(4), + * passengerCar(5), bus(6), lightTruck(7), heavyTruck(8), trailer(9), specialVehicles(10), tram(11), + * roadSideUnit(15). + * @param referencePosition {@link ReferencePosition} + * @param confidence {@link ManagementConfidence} + */ +public record ManagementContainer( + int stationType, + ReferencePosition referencePosition, + ManagementConfidence confidence) { + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private Integer stationType; + private ReferencePosition referencePosition; + private ManagementConfidence confidence; + + public Builder stationType(int stationType) { + this.stationType = stationType; + return this; + } + + public Builder referencePosition(ReferencePosition referencePosition) { + this.referencePosition = referencePosition; + return this; + } + + public Builder confidence(ManagementConfidence confidence) { + this.confidence = confidence; + return this; + } + + public ManagementContainer build() { + return new ManagementContainer( + requireNonNull(stationType, "station_type"), + requireNonNull(referencePosition, "reference_position"), + requireNonNull(confidence, "confidence")); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/managementcontainer/PositionConfidenceEllipse.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/managementcontainer/PositionConfidenceEllipse.java new file mode 100644 index 000000000..59ace22f6 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/managementcontainer/PositionConfidenceEllipse.java @@ -0,0 +1,21 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer; + +/** + * Position confidence ellipse. + * + * @param semiMajorConfidence Semi-major confidence. Value: oneCentimeter(1), outOfRange(4094), unavailable(4095). + * @param semiMinorConfidence Semi-minor confidence. Value: oneCentimeter(1), outOfRange(4094), unavailable(4095). + * @param semiMajorOrientation Semi-major orientation. Unit: 0.1 degree. Value: wgs84North(0), wgs84East(900), + * wgs84South(1800), wgs84West(2700), unavailable(3601). + */ +public record PositionConfidenceEllipse( + int semiMajorConfidence, + int semiMinorConfidence, + int semiMajorOrientation) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/managementcontainer/ReferencePosition.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/managementcontainer/ReferencePosition.java new file mode 100644 index 000000000..dc6cd9587 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/managementcontainer/ReferencePosition.java @@ -0,0 +1,23 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer; + +/** + * Reference position. + * + * @param latitude Latitude. Unit: 0.1 microdegree. Value: oneMicrodegreeNorth(10), oneMicrodegreeSouth(-10), + * unavailable(900000001). + * @param longitude Longitude. Unit: 0.1 microdegree. Value: oneMicrodegreeEast(10), oneMicrodegreeWest(-10), + * unavailable(1800000001). + * @param altitude Altitude. Unit: 0.01 meter. Value: referenceEllipsoidSurface(0), oneCentimeter(1), + * unavailable(800001). + */ +public record ReferencePosition( + int latitude, + int longitude, + int altitude) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/GroupType.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/GroupType.java new file mode 100644 index 000000000..1a9563f44 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/GroupType.java @@ -0,0 +1,22 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer; + +/** + * Group type flags for VRU groups. + * + * @param pedestrian True if pedestrian group. + * @param bicyclist True if bicyclist group. + * @param motorcyclist True if motorcyclist group. + * @param animal True if animal group. + */ +public record GroupType( + boolean pedestrian, + boolean bicyclist, + boolean motorcyclist, + boolean animal) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/LowerTriangularCorrelationMatrix.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/LowerTriangularCorrelationMatrix.java new file mode 100644 index 000000000..94091d9f3 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/LowerTriangularCorrelationMatrix.java @@ -0,0 +1,18 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer; + +import java.util.List; + +/** + * Lower triangular correlation matrix components. + * + * @param columns List of columns. Size: [1..17]. Each value is scaled by 100. Value: full-negative-correlation(-100), + * no-correlation(0), full-positive-correlation(100). + */ +public record LowerTriangularCorrelationMatrix(List> columns) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/MapPosition.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/MapPosition.java new file mode 100644 index 000000000..274c321c0 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/MapPosition.java @@ -0,0 +1,19 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer; + +/** + * Map-matched position. + * + * @param laneId Assigned index unique within the intersection. Value: [0..255]. + * @param longitudinalLanePosition Longitudinal offset along the matched lane. Unit: 0.1 meter. + * Value: zeroPointOneMeter(1). + */ +public record MapPosition( + Integer laneId, + Integer longitudinalLanePosition) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/ObjectClass.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/ObjectClass.java new file mode 100644 index 000000000..b9c614a1d --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/ObjectClass.java @@ -0,0 +1,26 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer; + +/** + * Object class. + *

+ * Exactly one of the class options should be provided. + * + * @param vehicle Describes the subclass of a detected object for class vehicle. Value: unknown(0), passengerCar(1), + * bus(2), lightTruck(3), heavyTruck(4), trailer(5), specialVehicles(6), tram(7), emergencyVehicle(8), + * agricultural(9). + * @param singleVru {@link ObjectClassVru} + * @param vruGroup {@link ObjectClassGroup} + * @param other Detected object for class other. Value: unknown(0), roadSideUnit(1). + */ +public record ObjectClass( + Integer vehicle, + ObjectClassVru singleVru, + ObjectClassGroup vruGroup, + Integer other) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/ObjectClassGroup.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/ObjectClassGroup.java new file mode 100644 index 000000000..c8ae98d92 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/ObjectClassGroup.java @@ -0,0 +1,20 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer; + +/** + * VRU group class. + * + * @param groupType {@link GroupType} + * @param groupSize Estimation of the number of VRUs in the group. Value: unavailable(0), onlyLeader(1). + * @param clusterId Optional ID of the associated cluster. Value: [0..255]. + */ +public record ObjectClassGroup( + GroupType groupType, + int groupSize, + Integer clusterId) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/ObjectClassVru.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/ObjectClassVru.java new file mode 100644 index 000000000..35e551eb5 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/ObjectClassVru.java @@ -0,0 +1,63 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer; + +/** + * VRU class. + *

+ * Exactly one of the VRU options should be provided. + * + * @param pedestrian Pedestrian type. Value: unavailable(0), ordinary-pedestrian(1), road-worker(2), first-responder(3), + * max(15). + * @param bicyclist Bicyclist type. Value: unavailable(0), bicyclist(1), wheelchair-user(2), horse-and-rider(3), + * rollerskater(4), e-scooter(5), personal-transporter(6), pedelec(7), speed-pedelec(8), max(15). + * @param motorcylist Motorcyclist type. Value: unavailable(0), moped(1), motorcycle(2), + * motorcycle-and-sidecar-right(3), motorcycle-and-sidecar-left(4), max(15). + * @param animal Animal type. Value: unavailable(0), wild-animal(1), farm-animal(2), service-animal(3), max(15). + */ +public record ObjectClassVru( + Integer pedestrian, + Integer bicyclist, + Integer motorcylist, + Integer animal) { + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private Integer pedestrian; + private Integer bicyclist; + private Integer motorcylist; + private Integer animal; + + public Builder pedestrian(Integer pedestrian) { + this.pedestrian = pedestrian; + return this; + } + + public Builder bicyclist(Integer bicyclist) { + this.bicyclist = bicyclist; + return this; + } + + public Builder motorcylist(Integer motorcylist) { + this.motorcylist = motorcylist; + return this; + } + + public Builder animal(Integer animal) { + this.animal = animal; + return this; + } + + public ObjectClassVru build() { + return new ObjectClassVru(pedestrian, bicyclist, motorcylist, animal); + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/ObjectClassification.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/ObjectClassification.java new file mode 100644 index 000000000..9375b7d80 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/ObjectClassification.java @@ -0,0 +1,18 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer; + +/** + * Object classification entry. + * + * @param objectClass {@link ObjectClass} + * @param confidence Confidence value for the type. Value: unknown(0), onePercent(1), oneHundredPercent(100), unavailable(101). + */ +public record ObjectClassification( + ObjectClass objectClass, + int confidence) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/PerceivedObject.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/PerceivedObject.java new file mode 100644 index 000000000..5d1c7c06f --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/PerceivedObject.java @@ -0,0 +1,320 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer; + +import java.util.List; + +/** + * Perceived object. + * + * @param objectId Identifier assigned to a detected object. Value: [0..255]. + * @param timeOfMeasurement Time difference from generation delta time. Unit: 1 millisecond. Value: [-1500..1500]. + * @param xDistance Distance to detected object in x-direction. Unit: 0.01 meter. Value: zeroPointZeroOneMeter(1), + * oneMeter(100). + * @param yDistance Distance to detected object in y-direction. Unit: 0.01 meter. Value: zeroPointZeroOneMeter(1), + * oneMeter(100). + * @param xSpeed Speed in x-direction. Unit: 0.01 m/s. Value: negativeSpeedMaximum(-16383), standstill(0), + * oneCentimeterPerSec(1), speedMaximum(16382), unavailable(16383). + * @param ySpeed Speed in y-direction. Unit: 0.01 m/s. Value: negativeSpeedMaximum(-16383), standstill(0), + * oneCentimeterPerSec(1), speedMaximum(16382), unavailable(16383). + * @param objectAge Age of the detected object. Unit: 1 ms. Value: oneMiliSec(1), moreThan1Point5Second(1500). + * @param confidence {@link PerceivedObjectConfidence} + * @param zDistance Optional. Distance to detected object in z-direction. Unit: 0.01 meter. + * Value: zeroPointZeroOneMeter(1), oneMeter(100). + * @param zSpeed Optional. Speed in z-direction. Unit: 0.01 m/s. Value: negativeSpeedMaximum(-16383), standstill(0), + * oneCentimeterPerSec(1), speedMaximum(16382), unavailable(16383). + * @param xAcceleration Optional. Acceleration in x-direction. Unit: 0.1 m/s2. Value: pointOneMeterPerSecSquared(1), + * minusPointOneMeterPerSecSquared(-1), unavailable(161). + * @param yAcceleration Optional. Acceleration in y-direction. Unit: 0.1 m/s2. Value: pointOneMeterPerSecSquared(1), + * minusPointOneMeterPerSecSquared(-1), unavailable(161). + * @param zAcceleration Optional. Acceleration in z-direction. Unit: 0.1 m/s2. Value: pointOneMeterPerSecSquared(1), + * minusPointOneMeterPerSecSquared(-1), unavailable(161). + * @param rollAngle Optional. Roll angle of object. Unit: 0.1 degree. Value: zeroPointOneDegree(1), oneDegree(10), + * unavailable(3601). + * @param pitchAngle Optional. Pitch angle of object. Unit: 0.1 degree. Value: zeroPointOneDegree(1), oneDegree(10), + * unavailable(3601). + * @param yawAngle Optional. Yaw angle of object. Unit: 0.1 degree. Value: zeroPointOneDegree(1), oneDegree(10), + * unavailable(3601). + * @param rollRate Optional. Roll rate of object. Unit: 0.01 degree/s. Value: noSpeed(0), + * oneDegreePerSecondAntiClockwise(100), oneDegreePerSecondClockwise(-100). + * @param pitchRate Optional. Pitch rate of object. Unit: 0.01 degree/s. Value: noSpeed(0), + * oneDegreePerSecondAntiClockwise(100), oneDegreePerSecondClockwise(-100). + * @param yawRate Optional. Yaw rate of object. Unit: 0.01 degree/s. Value: noSpeed(0), + * oneDegreePerSecondAntiClockwise(100), oneDegreePerSecondClockwise(-100). + * @param rollAcceleration Optional. Roll acceleration of object. Unit: 0.01 degree/s^2. Value: noAcceleration(0), + * oneDegreePerSecondSquaredAntiClockwise(100), oneDegreePerSecondSquaredClockwise(-100). + * @param pitchAcceleration Optional. Pitch acceleration of object. Unit: 0.01 degree/s^2. Value: noAcceleration(0), + * oneDegreePerSecondSquaredAntiClockwise(100), oneDegreePerSecondSquaredClockwise(-100). + * @param yawAcceleration Optional. Yaw acceleration of object. Unit: 0.01 degree/s^2. Value: noAcceleration(0), + * oneDegreePerSecondSquaredAntiClockwise(100), oneDegreePerSecondSquaredClockwise(-100). + * @param lowerTriangularCorrelationMatrixColumns Optional. Lower triangular correlation matrix columns. + * Value: [-100..100] scaled by 100. + * @param planarObjectDimension1 Optional. First planar dimension. Unit: 0.1 meter. Value: zeroPointOneMeter(1), + * oneMeter(10). + * @param planarObjectDimension2 Optional. Second planar dimension. Unit: 0.1 meter. Value: zeroPointOneMeter(1), + * oneMeter(10). + * @param verticalObjectDimension Optional. Vertical dimension. Unit: 0.1 meter. Value: zeroPointOneMeter(1), + * oneMeter(10). + * @param objectRefPoint Optional. Object reference point. Value: mid(0), bottomLeft(1), midLeft(2), topLeft(3), + * bottomMid(4), topMid(5), bottomRight(6), midRight(7), topRight(8). + * @param sensorIdList Optional. List of sensor IDs providing measurement data. Value: [0..255]. + * @param dynamicStatus Optional. Dynamic status of detected object. Value: dynamic(0), hasBeenDynamic(1), static(2). + * @param classification Optional. {@link ObjectClassification} + * @param matchedPosition Optional. Map-matched position {@link MapPosition} + */ +public record PerceivedObject( + int objectId, + int timeOfMeasurement, + int xDistance, + int yDistance, + int xSpeed, + int ySpeed, + int objectAge, + PerceivedObjectConfidence confidence, + Integer zDistance, + Integer zSpeed, + Integer xAcceleration, + Integer yAcceleration, + Integer zAcceleration, + Integer rollAngle, + Integer pitchAngle, + Integer yawAngle, + Integer rollRate, + Integer pitchRate, + Integer yawRate, + Integer rollAcceleration, + Integer pitchAcceleration, + Integer yawAcceleration, + LowerTriangularCorrelationMatrix lowerTriangularCorrelationMatrixColumns, + Integer planarObjectDimension1, + Integer planarObjectDimension2, + Integer verticalObjectDimension, + Integer objectRefPoint, + List sensorIdList, + Integer dynamicStatus, + List classification, + MapPosition matchedPosition) { + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private Integer objectId; + private Integer timeOfMeasurement; + private Integer xDistance; + private Integer yDistance; + private Integer xSpeed; + private Integer ySpeed; + private Integer objectAge; + private PerceivedObjectConfidence confidence; + private Integer zDistance; + private Integer zSpeed; + private Integer xAcceleration; + private Integer yAcceleration; + private Integer zAcceleration; + private Integer rollAngle; + private Integer pitchAngle; + private Integer yawAngle; + private Integer rollRate; + private Integer pitchRate; + private Integer yawRate; + private Integer rollAcceleration; + private Integer pitchAcceleration; + private Integer yawAcceleration; + private LowerTriangularCorrelationMatrix lowerTriangularCorrelationMatrixColumns; + private Integer planarObjectDimension1; + private Integer planarObjectDimension2; + private Integer verticalObjectDimension; + private Integer objectRefPoint; + private List sensorIdList; + private Integer dynamicStatus; + private List classification; + private MapPosition matchedPosition; + + public Builder objectId(int objectId) { + this.objectId = objectId; + return this; + } + + public Builder timeOfMeasurement(int timeOfMeasurement) { + this.timeOfMeasurement = timeOfMeasurement; + return this; + } + + public Builder distance(int xDistance, int yDistance) { + this.xDistance = xDistance; + this.yDistance = yDistance; + return this; + } + + public Builder speed(int xSpeed, int ySpeed) { + this.xSpeed = xSpeed; + this.ySpeed = ySpeed; + return this; + } + + public Builder objectAge(int objectAge) { + this.objectAge = objectAge; + return this; + } + + public Builder confidence(PerceivedObjectConfidence confidence) { + this.confidence = confidence; + return this; + } + + public Builder zDistance(Integer zDistance) { + this.zDistance = zDistance; + return this; + } + + public Builder zSpeed(Integer zSpeed) { + this.zSpeed = zSpeed; + return this; + } + + public Builder acceleration(Integer xAcceleration, Integer yAcceleration) { + this.xAcceleration = xAcceleration; + return this; + } + + public Builder zAcceleration(Integer zAcceleration) { + this.zAcceleration = zAcceleration; + return this; + } + + public Builder rollAngle(Integer rollAngle) { + this.rollAngle = rollAngle; + return this; + } + + public Builder pitchAngle(Integer pitchAngle) { + this.pitchAngle = pitchAngle; + return this; + } + + public Builder yawAngle(Integer yawAngle) { + this.yawAngle = yawAngle; + return this; + } + + public Builder rollRate(Integer rollRate) { + this.rollRate = rollRate; + return this; + } + + public Builder pitchRate(Integer pitchRate) { + this.pitchRate = pitchRate; + return this; + } + + public Builder yawRate(Integer yawRate) { + this.yawRate = yawRate; + return this; + } + + public Builder rollAcceleration(Integer rollAcceleration) { + this.rollAcceleration = rollAcceleration; + return this; + } + + public Builder pitchAcceleration(Integer pitchAcceleration) { + this.pitchAcceleration = pitchAcceleration; + return this; + } + + public Builder yawAcceleration(Integer yawAcceleration) { + this.yawAcceleration = yawAcceleration; + return this; + } + + public Builder lowerTriangularCorrelationMatrixColumns(LowerTriangularCorrelationMatrix columns) { + this.lowerTriangularCorrelationMatrixColumns = columns; + return this; + } + + public Builder planarObjectDimension(Integer planarObjectDimension1, Integer planarObjectDimension2) { + this.planarObjectDimension1 = planarObjectDimension1; + this.planarObjectDimension2 = planarObjectDimension2; + return this; + } + + public Builder verticalObjectDimension(Integer verticalObjectDimension) { + this.verticalObjectDimension = verticalObjectDimension; + return this; + } + + public Builder objectRefPoint(Integer objectRefPoint) { + this.objectRefPoint = objectRefPoint; + return this; + } + + public Builder sensorIdList(List sensorIdList) { + this.sensorIdList = sensorIdList; + return this; + } + + public Builder dynamicStatus(Integer dynamicStatus) { + this.dynamicStatus = dynamicStatus; + return this; + } + + public Builder classification(List classification) { + this.classification = classification; + return this; + } + + public Builder matchedPosition(MapPosition matchedPosition) { + this.matchedPosition = matchedPosition; + return this; + } + + public PerceivedObject build() { + return new PerceivedObject( + requireNonNull(objectId, "object_id"), + requireNonNull(timeOfMeasurement, "time_of_measurement"), + requireNonNull(xDistance, "x_distance"), + requireNonNull(yDistance, "y_distance"), + requireNonNull(xSpeed, "x_speed"), + requireNonNull(ySpeed, "y_speed"), + requireNonNull(objectAge, "object_age"), + requireNonNull(confidence, "confidence"), + zDistance, + zSpeed, + xAcceleration, + yAcceleration, + zAcceleration, + rollAngle, + pitchAngle, + yawAngle, + rollRate, + pitchRate, + yawRate, + rollAcceleration, + pitchAcceleration, + yawAcceleration, + lowerTriangularCorrelationMatrixColumns, + planarObjectDimension1, + planarObjectDimension2, + verticalObjectDimension, + objectRefPoint, + sensorIdList, + dynamicStatus, + classification, + matchedPosition); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/PerceivedObjectConfidence.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/PerceivedObjectConfidence.java new file mode 100644 index 000000000..36963f910 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/PerceivedObjectConfidence.java @@ -0,0 +1,255 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer; + +/** + * Confidence values for a perceived object. + * + * @param xDistance Distance confidence in x-direction. Unit: 0.01 meter. Value: zeroPointZeroOneMeter(1), + * oneMeter(100), outOfRange(4094), unavailable(4095). + * @param yDistance Distance confidence in y-direction. Unit: 0.01 meter. Value: zeroPointZeroOneMeter(1), + * oneMeter(100), outOfRange(4094), unavailable(4095). + * @param xSpeed Speed confidence in x-direction. Value: unavailable(0), prec100ms(1), prec10ms(2), prec5ms(3), + * prec1ms(4), prec0-1ms(5), prec0-05ms(6), prec0-01ms(7). + * @param ySpeed Speed confidence in y-direction. Value: unavailable(0), prec100ms(1), prec10ms(2), prec5ms(3), + * prec1ms(4), prec0-1ms(5), prec0-05ms(6), prec0-01ms(7). + * @param object Object confidence. Value: noConfidence(0), fullConfidence(15). + * @param zDistance Optional. Distance confidence in z-direction. Unit: 0.01 meter. Value: zeroPointZeroOneMeter(1), + * oneMeter(100), outOfRange(4094), unavailable(4095). + * @param zSpeed Optional. Speed confidence in z-direction. Value: unavailable(0), prec100ms(1), prec10ms(2), + * prec5ms(3), prec1ms(4), prec0-1ms(5), prec0-05ms(6), prec0-01ms(7). + * @param xAcceleration Optional. Acceleration confidence in x-direction. Value: pointOneMeterPerSecSquared(1), + * outOfRange(101), unavailable(102). + * @param yAcceleration Optional. Acceleration confidence in y-direction. Value: pointOneMeterPerSecSquared(1), + * outOfRange(101), unavailable(102). + * @param zAcceleration Optional. Acceleration confidence in z-direction. Value: pointOneMeterPerSecSquared(1), + * outOfRange(101), unavailable(102). + * @param rollAngle Optional. Roll angle confidence. Value: zeroPointOneDegree(1), oneDegree(10), outOfRange(126), + * unavailable(127). + * @param pitchAngle Optional. Pitch angle confidence. Value: zeroPointOneDegree(1), oneDegree(10), outOfRange(126), + * unavailable(127). + * @param yawAngle Optional. Yaw angle confidence. Value: zeroPointOneDegree(1), oneDegree(10), outOfRange(126), + * unavailable(127). + * @param rollRate Optional. Roll rate confidence. Value: degSec-000-01(0), degSec-000-05(1), degSec-000-10(2), + * degSec-001-00(3), degSec-005-00(4), degSec-010-00(5), degSec-100-00(6), outOfRange(7), + * unavailable(8). + * @param pitchRate Optional. Pitch rate confidence. Value: degSec-000-01(0), degSec-000-05(1), degSec-000-10(2), + * degSec-001-00(3), degSec-005-00(4), degSec-010-00(5), degSec-100-00(6), outOfRange(7), + * unavailable(8). + * @param yawRate Optional. Yaw rate confidence. Value: degSec-000-01(0), degSec-000-05(1), degSec-000-10(2), + * degSec-001-00(3), degSec-005-00(4), degSec-010-00(5), degSec-100-00(6), outOfRange(7), + * unavailable(8). + * @param rollAcceleration Optional. Roll acceleration confidence. Value: degSecSquared-000-01(0), + * degSecSquared-000-05(1), degSecSquared-000-10(2), degSecSquared-001-00(3), + * degSecSquared-005-00(4), degSecSquared-010-00(5), degSecSquared-100-00(6), + * outOfRange(7), unavailable(8). + * @param pitchAcceleration Optional. Pitch acceleration confidence. Value: degSecSquared-000-01(0), + * degSecSquared-000-05(1), degSecSquared-000-10(2), degSecSquared-001-00(3), + * degSecSquared-005-00(4), degSecSquared-010-00(5), degSecSquared-100-00(6), outOfRange(7), + * unavailable(8). + * @param yawAcceleration Optional. Yaw acceleration confidence. Value: degSecSquared-000-01(0), + * degSecSquared-000-05(1), degSecSquared-000-10(2), degSecSquared-001-00(3), + * degSecSquared-005-00(4), degSecSquared-010-00(5), degSecSquared-100-00(6), outOfRange(7), + * unavailable(8). + * @param planarObjectDimension1 Optional. Accuracy of first dimension. Unit: 0.01 meter. + * Value: zeroPointZeroOneMeter(1), oneMeter(100), outOfRange(101), unavailable(102). + * @param planarObjectDimension2 Optional. Accuracy of second dimension. Unit: 0.01 meter. + * Value: zeroPointZeroOneMeter(1), oneMeter(100), outOfRange(101), unavailable(102). + * @param verticalObjectDimension Optional. Accuracy of vertical dimension. Unit: 0.01 meter. + * Value: zeroPointZeroOneMeter(1), oneMeter(100), outOfRange(101), unavailable(102). + * @param longitudinalLanePosition Optional. Accuracy of longitudinal lane position. Unit: 0.01 meter. + * Value: zeroPointZeroOneMeter(1), oneMeter(100), outOfRange(101), unavailable(102). + */ +public record PerceivedObjectConfidence( + int xDistance, + int yDistance, + int xSpeed, + int ySpeed, + int object, + Integer zDistance, + Integer zSpeed, + Integer xAcceleration, + Integer yAcceleration, + Integer zAcceleration, + Integer rollAngle, + Integer pitchAngle, + Integer yawAngle, + Integer rollRate, + Integer pitchRate, + Integer yawRate, + Integer rollAcceleration, + Integer pitchAcceleration, + Integer yawAcceleration, + Integer planarObjectDimension1, + Integer planarObjectDimension2, + Integer verticalObjectDimension, + Integer longitudinalLanePosition) { + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private Integer xDistance; + private Integer yDistance; + private Integer xSpeed; + private Integer ySpeed; + private Integer object; + private Integer zDistance; + private Integer zSpeed; + private Integer xAcceleration; + private Integer yAcceleration; + private Integer zAcceleration; + private Integer rollAngle; + private Integer pitchAngle; + private Integer yawAngle; + private Integer rollRate; + private Integer pitchRate; + private Integer yawRate; + private Integer rollAcceleration; + private Integer pitchAcceleration; + private Integer yawAcceleration; + private Integer planarObjectDimension1; + private Integer planarObjectDimension2; + private Integer verticalObjectDimension; + private Integer longitudinalLanePosition; + + public Builder distance(int xDistance, int yDistance) { + this.xDistance = xDistance; + this.yDistance = yDistance; + return this; + } + + public Builder speed(int xSpeed, int ySpeed) { + this.xSpeed = xSpeed; + this.ySpeed = ySpeed; + return this; + } + + public Builder object(int object) { + this.object = object; + return this; + } + + public Builder zDistance(Integer zDistance) { + this.zDistance = zDistance; + return this; + } + + public Builder zSpeed(Integer zSpeed) { + this.zSpeed = zSpeed; + return this; + } + + public Builder acceleration(Integer xAcceleration, Integer yAcceleration) { + this.xAcceleration = xAcceleration; + this.yAcceleration = yAcceleration; + return this; + } + + public Builder zAcceleration(Integer zAcceleration) { + this.zAcceleration = zAcceleration; + return this; + } + + public Builder rollAngle(Integer rollAngle) { + this.rollAngle = rollAngle; + return this; + } + + public Builder pitchAngle(Integer pitchAngle) { + this.pitchAngle = pitchAngle; + return this; + } + + public Builder yawAngle(Integer yawAngle) { + this.yawAngle = yawAngle; + return this; + } + + public Builder rollRate(Integer rollRate) { + this.rollRate = rollRate; + return this; + } + + public Builder pitchRate(Integer pitchRate) { + this.pitchRate = pitchRate; + return this; + } + + public Builder yawRate(Integer yawRate) { + this.yawRate = yawRate; + return this; + } + + public Builder rollAcceleration(Integer rollAcceleration) { + this.rollAcceleration = rollAcceleration; + return this; + } + + public Builder pitchAcceleration(Integer pitchAcceleration) { + this.pitchAcceleration = pitchAcceleration; + return this; + } + + public Builder yawAcceleration(Integer yawAcceleration) { + this.yawAcceleration = yawAcceleration; + return this; + } + + public Builder planarObjectDimension(Integer planarObjectDimension1, Integer planarObjectDimension2) { + this.planarObjectDimension1 = planarObjectDimension1; + this.planarObjectDimension2 = planarObjectDimension2; + return this; + } + + public Builder verticalObjectDimension(Integer verticalObjectDimension) { + this.verticalObjectDimension = verticalObjectDimension; + return this; + } + + public Builder longitudinalLanePosition(Integer longitudinalLanePosition) { + this.longitudinalLanePosition = longitudinalLanePosition; + return this; + } + + public PerceivedObjectConfidence build() { + return new PerceivedObjectConfidence( + requireNonNull(xDistance, "x_distance"), + requireNonNull(yDistance, "y_distance"), + requireNonNull(xSpeed, "x_speed"), + requireNonNull(ySpeed, "y_speed"), + requireNonNull(object, "object"), + zDistance, + zSpeed, + xAcceleration, + yAcceleration, + zAcceleration, + rollAngle, + pitchAngle, + yawAngle, + rollRate, + pitchRate, + yawRate, + rollAcceleration, + pitchAcceleration, + yawAcceleration, + planarObjectDimension1, + planarObjectDimension2, + verticalObjectDimension, + longitudinalLanePosition); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/PerceivedObjectContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/PerceivedObjectContainer.java new file mode 100644 index 000000000..a46310041 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/perceivedobjectcontainer/PerceivedObjectContainer.java @@ -0,0 +1,18 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer; + +import java.util.List; + +/** + * Perceived object container. + * + * @param perceivedObjects List of {@link PerceivedObject}. Size: [1..128]. + */ +public record PerceivedObjectContainer(List perceivedObjects) {} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/DetectionArea.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/DetectionArea.java new file mode 100644 index 000000000..5dd407f94 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/DetectionArea.java @@ -0,0 +1,87 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.sensorinformationcontainer; + +import com.orange.iot3mobility.messages.cpm.v121.model.defs.AreaCircular; +import com.orange.iot3mobility.messages.cpm.v121.model.defs.AreaEllipse; +import com.orange.iot3mobility.messages.cpm.v121.model.defs.AreaPolygon; +import com.orange.iot3mobility.messages.cpm.v121.model.defs.AreaRectangle; + +/** + * Detection area for a sensor. + * + * Exactly one of the detection area options should be provided. + * + * @param vehicleSensor {@link VehicleSensor} + * @param stationarySensorRadial {@link StationarySensorRadial} + * @param stationarySensorPolygon {@link AreaPolygon} + * @param stationarySensorCircular {@link AreaCircular} + * @param stationarySensorEllipse {@link AreaEllipse} + * @param stationarySensorRectangle {@link AreaRectangle} + */ +public record DetectionArea( + VehicleSensor vehicleSensor, + StationarySensorRadial stationarySensorRadial, + AreaPolygon stationarySensorPolygon, + AreaCircular stationarySensorCircular, + AreaEllipse stationarySensorEllipse, + AreaRectangle stationarySensorRectangle) { + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private VehicleSensor vehicleSensor; + private StationarySensorRadial stationarySensorRadial; + private AreaPolygon stationarySensorPolygon; + private AreaCircular stationarySensorCircular; + private AreaEllipse stationarySensorEllipse; + private AreaRectangle stationarySensorRectangle; + + public Builder vehicleSensor(VehicleSensor vehicleSensor) { + this.vehicleSensor = vehicleSensor; + return this; + } + + public Builder stationarySensorRadial(StationarySensorRadial stationarySensorRadial) { + this.stationarySensorRadial = stationarySensorRadial; + return this; + } + + public Builder stationarySensorPolygon(AreaPolygon stationarySensorPolygon) { + this.stationarySensorPolygon = stationarySensorPolygon; + return this; + } + + public Builder stationarySensorCircular(AreaCircular stationarySensorCircular) { + this.stationarySensorCircular = stationarySensorCircular; + return this; + } + + public Builder stationarySensorEllipse(AreaEllipse stationarySensorEllipse) { + this.stationarySensorEllipse = stationarySensorEllipse; + return this; + } + + public Builder stationarySensorRectangle(AreaRectangle stationarySensorRectangle) { + this.stationarySensorRectangle = stationarySensorRectangle; + return this; + } + + public DetectionArea build() { + return new DetectionArea( + vehicleSensor, + stationarySensorRadial, + stationarySensorPolygon, + stationarySensorCircular, + stationarySensorEllipse, + stationarySensorRectangle); + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/SensorInformation.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/SensorInformation.java new file mode 100644 index 000000000..243bb6976 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/SensorInformation.java @@ -0,0 +1,21 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.sensorinformationcontainer; + +/** + * Sensor information item. + * + * @param sensorId Sensor identifier. Value: [0..255]. + * @param type Type of attached sensor. Value: undefined(0), radar(1), lidar(2), monovideo(3), stereovision(4), + * nightvision(5), ultrasonic(6), pmd(7), fusion(8), inductionloop(9), sphericalCamera(10), itssaggregation(11). + * @param detectionArea {@link DetectionArea} + */ +public record SensorInformation( + int sensorId, + int type, + DetectionArea detectionArea) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/SensorInformationContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/SensorInformationContainer.java new file mode 100644 index 000000000..661497627 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/SensorInformationContainer.java @@ -0,0 +1,18 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.sensorinformationcontainer; + +import java.util.List; + +/** + * Sensor information container. + * + * @param sensorInformation List of {@link SensorInformation}. Size: [1..128]. + */ +public record SensorInformationContainer(List sensorInformation) {} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/StationarySensorRadial.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/StationarySensorRadial.java new file mode 100644 index 000000000..a7b70098f --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/StationarySensorRadial.java @@ -0,0 +1,96 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.sensorinformationcontainer; + +import com.orange.iot3mobility.messages.cpm.v121.model.defs.Offset; + +/** + * Stationary radial sensor detection area. + * + * @param range Radial range of the sensor from the reference point. Unit: 0.1 meter. Value: zeroPointOneMeter(1), + * oneMeter(10). + * @param horizontalOpeningAngleStart Start of the stationary sensor's horizontal opening angle in WGS84. + * Unit: 0.1 degree. Value: wgs84North(0), wgs84East(900), wgs84South(1800), + * wgs84West(2700), unavailable(3601). + * @param horizontalOpeningAngleEnd End of the stationary sensor's horizontal opening angle in WGS84. + * Unit: 0.1 degree. Value: wgs84North(0), wgs84East(900), wgs84South(1800), + * wgs84West(2700), unavailable(3601). + * @param verticalOpeningAngleStart Optional. Start of the stationary sensor's vertical opening angle. Unit: 0.1 degree. + * Value: zeroPointOneDegree(1), oneDegree(10), unavailable(3601). + * @param verticalOpeningAngleEnd Optional. End of the stationary sensor's vertical opening angle. Unit: 0.1 degree. + * Value: zeroPointOneDegree(1), oneDegree(10), unavailable(3601). + * @param sensorPositionOffset Optional. {@link Offset} of the mounting point from the station's reference position. + */ +public record StationarySensorRadial( + int range, + int horizontalOpeningAngleStart, + int horizontalOpeningAngleEnd, + Integer verticalOpeningAngleStart, + Integer verticalOpeningAngleEnd, + Offset sensorPositionOffset) { + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private Integer range; + private Integer horizontalOpeningAngleStart; + private Integer horizontalOpeningAngleEnd; + private Integer verticalOpeningAngleStart; + private Integer verticalOpeningAngleEnd; + private Offset sensorPositionOffset; + + public Builder range(Integer range) { + this.range = range; + return this; + } + + public Builder horizontalOpeningAngleStart(Integer horizontalOpeningAngleStart) { + this.horizontalOpeningAngleStart = horizontalOpeningAngleStart; + return this; + } + + public Builder horizontalOpeningAngleEnd(Integer horizontalOpeningAngleEnd) { + this.horizontalOpeningAngleEnd = horizontalOpeningAngleEnd; + return this; + } + + public Builder verticalOpeningAngleStart(Integer verticalOpeningAngleStart) { + this.verticalOpeningAngleStart = verticalOpeningAngleStart; + return this; + } + + public Builder verticalOpeningAngleEnd(Integer verticalOpeningAngleEnd) { + this.verticalOpeningAngleEnd = verticalOpeningAngleEnd; + return this; + } + + public Builder sensorPositionOffset(Offset sensorPositionOffset) { + this.sensorPositionOffset = sensorPositionOffset; + return this; + } + + public StationarySensorRadial build() { + return new StationarySensorRadial( + requireNonNull(range, "range"), + requireNonNull(horizontalOpeningAngleStart, "horizontal_opening_angle_start"), + requireNonNull(horizontalOpeningAngleEnd, "horizontal_opening_angle_end"), + verticalOpeningAngleStart, + verticalOpeningAngleEnd, + sensorPositionOffset); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/VehicleSensor.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/VehicleSensor.java new file mode 100644 index 000000000..7b9eb9378 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/VehicleSensor.java @@ -0,0 +1,76 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.sensorinformationcontainer; + +import java.util.List; + +/** + * Vehicle sensor detection area. + * + * @param refPointId Optional. Increasing counter of the trailer reference point (hitch point). Value: [0..255]. + * @param xSensorOffset Mounting position of sensor in x-direction from reference point. Unit: 0.01 meter. + * Value: negativeZeroPointZeroOneMeter(-1), negativeOneMeter(-100), negativeOutOfRange(-3094), + * positiveOneMeter(100), positiveOutOfRange(1001). + * @param ySensorOffset Mounting position of sensor in y-direction from reference point. Unit: 0.01 meter. + * Value: zeroPointZeroOneMeter(1), oneMeter(100). + * @param zSensorOffset Optional. Mounting position of sensor in z-direction from reference point. Unit: 0.01 meter. + * Value: zeroPointZeroOneMeter(1), oneMeter(100). + * @param vehicleSensorPropertyList {@link VehicleSensorProperty} + */ +public record VehicleSensor( + Integer refPointId, + int xSensorOffset, + int ySensorOffset, + Integer zSensorOffset, + List vehicleSensorPropertyList) { + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private Integer refPointId; + private Integer xSensorOffset; + private Integer ySensorOffset; + private Integer zSensorOffset; + private List vehicleSensorPropertyList; + + public Builder refPointId(Integer refPointId) { + this.refPointId = refPointId; + return this; + } + + public Builder sensorOffset(int xSensorOffset, int ySensorOffset, Integer zSensorOffset) { + this.xSensorOffset = xSensorOffset; + this.ySensorOffset = ySensorOffset; + this.zSensorOffset = zSensorOffset; + return this; + } + + public Builder vehicleSensorPropertyList(List vehicleSensorPropertyList) { + this.vehicleSensorPropertyList = vehicleSensorPropertyList; + return this; + } + + public VehicleSensor build() { + return new VehicleSensor( + refPointId, + requireNonNull(xSensorOffset, "x_sensor_offset"), + requireNonNull(ySensorOffset, "y_sensor_offset"), + zSensorOffset, + requireNonNull(vehicleSensorPropertyList, "vehicle_sensor_property_list")); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/VehicleSensorProperty.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/VehicleSensorProperty.java new file mode 100644 index 000000000..e07b81c6f --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/sensorinformationcontainer/VehicleSensorProperty.java @@ -0,0 +1,75 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.sensorinformationcontainer; + +/** + * Vehicle sensor property item. + * + * @param range Range of sensor within the indicated azimuth angle. Unit: 0.1 meter. Value: zeroPointOneMeter(1), + * oneMeter(10). + * @param horizontalOpeningAngleStart Start of the sensor's horizontal opening angle extension. Unit: 0.1 degree. + * Value: zeroPointOneDegree(1), oneDegree(10), unavailable(3601). + * @param horizontalOpeningAngleEnd End of the sensor's horizontal opening angle extension. Unit: 0.1 degree. + * Value: zeroPointOneDegree(1), oneDegree(10), unavailable(3601). + * @param verticalOpeningAngleStart Optional. Start of the sensor's vertical opening angle extension. Unit: 0.1 degree. + * Value: zeroPointOneDegree(1), oneDegree(10), unavailable(3601). + * @param verticalOpeningAngleEnd Optional. End of the sensor's vertical opening angle extension. Unit: 0.1 degree. + * Value: zeroPointOneDegree(1), oneDegree(10), unavailable(3601). + */ +public record VehicleSensorProperty( + int range, + int horizontalOpeningAngleStart, + int horizontalOpeningAngleEnd, + Integer verticalOpeningAngleStart, + Integer verticalOpeningAngleEnd) { + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private Integer range; + private Integer horizontalOpeningAngleStart; + private Integer horizontalOpeningAngleEnd; + private Integer verticalOpeningAngleStart; + private Integer verticalOpeningAngleEnd; + + public Builder range(int range) { + this.range = range; + return this; + } + + public Builder horizontalOpeningAngle(int horizontalOpeningAngleStart, int horizontalOpeningAngleEnd) { + this.horizontalOpeningAngleStart = horizontalOpeningAngleStart; + this.horizontalOpeningAngleEnd = horizontalOpeningAngleEnd; + return this; + } + + public Builder verticalOpeningAngle(Integer verticalOpeningAngleStart, Integer verticalOpeningAngleEnd) { + this.verticalOpeningAngleStart = verticalOpeningAngleStart; + this.verticalOpeningAngleEnd = verticalOpeningAngleEnd; + return this; + } + + public VehicleSensorProperty build() { + return new VehicleSensorProperty( + requireNonNull(range, "range"), + requireNonNull(horizontalOpeningAngleStart, "horizontal_opening_angle_start"), + requireNonNull(horizontalOpeningAngleEnd, "horizontal_opening_angle_end"), + verticalOpeningAngleStart, + verticalOpeningAngleEnd); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/stationdatacontainer/OriginatingRsuContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/stationdatacontainer/OriginatingRsuContainer.java new file mode 100644 index 000000000..5ca64b01c --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/stationdatacontainer/OriginatingRsuContainer.java @@ -0,0 +1,20 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer; + +/** + * Originating RSU container. + * + * @param region Road regulator id. Value: [0..65535]. + * @param intersectionReferenceId Intersection id. Value: [0..65535]. + * @param roadSegmentReferenceId Road segment id. Value: [0..65535]. + */ +public record OriginatingRsuContainer( + Integer region, + Integer intersectionReferenceId, + Integer roadSegmentReferenceId) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/stationdatacontainer/OriginatingVehicleContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/stationdatacontainer/OriginatingVehicleContainer.java new file mode 100644 index 000000000..a3228c2db --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/stationdatacontainer/OriginatingVehicleContainer.java @@ -0,0 +1,133 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer; + +/** + * Originating vehicle container. + * + * @param heading Heading of the vehicle movement with regards to the true north. Unit: 0.1 degree. + * Value: wgs84North(0), wgs84East(900), wgs84South(1800), wgs84West(2700), unavailable(3601). + * @param speed Driving speed. Unit: 0.01 m/s. Value: standstill(0), oneCentimeterPerSec(1), unavailable(16383). + * @param confidence {@link VehicleConfidence} + * @param driveDirection Optional. Drive direction. Value: forward(0), backward(1), unavailable(2). + * @param vehicleLength Optional. Vehicle length. Unit: 0.1 meter. Value: tenCentimeters(1), outOfRange(1022), + * unavailable(1023). + * @param vehicleWidth Optional. Vehicle width. Unit: 0.1 meter. Value: tenCentimeters(1), outOfRange(61), + * unavailable(62). + * @param longitudinalAcceleration Optional. Longitudinal acceleration. Unit: 0.1 m/s2. + * Value: pointOneMeterPerSecSquaredForward(1), pointOneMeterPerSecSquaredBackward(-1), + * unavailable(161). + * @param yawRate Optional. Yaw rate. Unit: 0.01 degree/s. Value: straight(0), degSec-000-01ToRight(-1), + * degSec-000-01ToLeft(1), unavailable(32767). + * @param lateralAcceleration Optional. Lateral acceleration. Unit: 0.1 m/s2. + * Value: pointOneMeterPerSecSquaredToRight(-1), pointOneMeterPerSecSquaredToLeft(1), + * unavailable(161). + * @param verticalAcceleration Optional. Vertical acceleration. Unit: 0.1 m/s2. + * Value: pointOneMeterPerSecSquaredUp(1), pointOneMeterPerSecSquaredDown(-1), + * unavailable(161). + */ +public record OriginatingVehicleContainer( + int heading, + int speed, + VehicleConfidence confidence, + Integer driveDirection, + Integer vehicleLength, + Integer vehicleWidth, + Integer longitudinalAcceleration, + Integer yawRate, + Integer lateralAcceleration, + Integer verticalAcceleration) { + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private Integer heading; + private Integer speed; + private VehicleConfidence confidence; + private Integer driveDirection; + private Integer vehicleLength; + private Integer vehicleWidth; + private Integer longitudinalAcceleration; + private Integer yawRate; + private Integer lateralAcceleration; + private Integer verticalAcceleration; + + public Builder heading(int heading) { + this.heading = heading; + return this; + } + + public Builder speed(int speed) { + this.speed = speed; + return this; + } + + public Builder confidence(VehicleConfidence confidence) { + this.confidence = confidence; + return this; + } + + public Builder driveDirection(Integer driveDirection) { + this.driveDirection = driveDirection; + return this; + } + + public Builder vehicleLength(Integer vehicleLength) { + this.vehicleLength = vehicleLength; + return this; + } + + public Builder vehicleWidth(Integer vehicleWidth) { + this.vehicleWidth = vehicleWidth; + return this; + } + + public Builder longitudinalAcceleration(Integer longitudinalAcceleration) { + this.longitudinalAcceleration = longitudinalAcceleration; + return this; + } + + public Builder yawRate(Integer yawRate) { + this.yawRate = yawRate; + return this; + } + + public Builder lateralAcceleration(Integer lateralAcceleration) { + this.lateralAcceleration = lateralAcceleration; + return this; + } + + public Builder verticalAcceleration(Integer verticalAcceleration) { + this.verticalAcceleration = verticalAcceleration; + return this; + } + + public OriginatingVehicleContainer build() { + return new OriginatingVehicleContainer( + requireNonNull(heading, "heading"), + requireNonNull(speed, "speed"), + requireNonNull(confidence, "confidence"), + driveDirection, + vehicleLength, + vehicleWidth, + longitudinalAcceleration, + yawRate, + lateralAcceleration, + verticalAcceleration); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/stationdatacontainer/StationDataContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/stationdatacontainer/StationDataContainer.java new file mode 100644 index 000000000..58f7daa65 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/stationdatacontainer/StationDataContainer.java @@ -0,0 +1,46 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer; + +/** + * Station data container. + *

+ * Exactly one of originating_vehicle_container or originating_rsu_container must be provided. + * + * @param originatingVehicleContainer Optional {@link OriginatingVehicleContainer} + * @param originatingRsuContainer Optional {@link OriginatingRsuContainer} + */ +public record StationDataContainer( + OriginatingVehicleContainer originatingVehicleContainer, + OriginatingRsuContainer originatingRsuContainer) { + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private OriginatingVehicleContainer originatingVehicleContainer; + private OriginatingRsuContainer originatingRsuContainer; + + public Builder originatingVehicleContainer(OriginatingVehicleContainer originatingVehicleContainer) { + this.originatingVehicleContainer = originatingVehicleContainer; + return this; + } + + public Builder originatingRsuContainer(OriginatingRsuContainer originatingRsuContainer) { + this.originatingRsuContainer = originatingRsuContainer; + return this; + } + + public StationDataContainer build() { + return new StationDataContainer(originatingVehicleContainer, originatingRsuContainer); + } + + } +} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/stationdatacontainer/VehicleConfidence.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/stationdatacontainer/VehicleConfidence.java new file mode 100644 index 000000000..6e47256ec --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/model/stationdatacontainer/VehicleConfidence.java @@ -0,0 +1,105 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer; + +/** + * Confidence values for originating vehicle container. + * + * @param heading Heading accuracy of the vehicle movement with regards to the true north. Unit: 0.1 degree. + * Value: equalOrWithinZeroPointOneDegree(1), equalOrWithinOneDegree(10), outOfRange(126), + * unavailable(127). + * @param speed Speed accuracy. Unit: 0.01 m/s. Value: equalOrWithinOneCentimeterPerSec(1), + * equalOrWithinOneMeterPerSec(100), outOfRange(126), unavailable(127). + * @param vehicleLength Optional. Trailer presence/length confidence. Value: noTrailerPresent(0), + * trailerPresentWithKnownLength(1), trailerPresentWithUnknownLength(2), + * trailerPresenceIsUnknown(3), unavailable(4). + * @param yawRate Optional. Yaw rate confidence. Value: degSec-000-01(0), degSec-000-05(1), degSec-000-10(2), + * degSec-001-00(3), degSec-005-00(4), degSec-010-00(5), degSec-100-00(6), outOfRange(7), unavailable(8). + * @param longitudinalAcceleration Optional. Longitudinal acceleration confidence. + * Value: pointOneMeterPerSecSquared(1), outOfRange(101), unavailable(102). + * @param lateralAcceleration Optional. Lateral acceleration confidence. Unit: 0.1 m/s2. + * Value: pointOneMeterPerSecSquared(1), outOfRange(101), unavailable(102). + * @param verticalAcceleration Optional. Vertical acceleration confidence. Unit: 0.1 m/s2. + * Value: pointOneMeterPerSecSquared(1), outOfRange(101), unavailable(102). + */ +public record VehicleConfidence( + int heading, + int speed, + Integer vehicleLength, + Integer yawRate, + Integer longitudinalAcceleration, + Integer lateralAcceleration, + Integer verticalAcceleration) { + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private Integer heading; + private Integer speed; + private Integer vehicleLength; + private Integer yawRate; + private Integer longitudinalAcceleration; + private Integer lateralAcceleration; + private Integer verticalAcceleration; + + public Builder heading(int heading) { + this.heading = heading; + return this; + } + + public Builder speed(int speed) { + this.speed = speed; + return this; + } + + public Builder vehicleLength(Integer vehicleLength) { + this.vehicleLength = vehicleLength; + return this; + } + + public Builder yawRate(Integer yawRate) { + this.yawRate = yawRate; + return this; + } + + public Builder longitudinalAcceleration(Integer longitudinalAcceleration) { + this.longitudinalAcceleration = longitudinalAcceleration; + return this; + } + + public Builder lateralAcceleration(Integer lateralAcceleration) { + this.lateralAcceleration = lateralAcceleration; + return this; + } + + public Builder verticalAcceleration(Integer verticalAcceleration) { + this.verticalAcceleration = verticalAcceleration; + return this; + } + + public VehicleConfidence build() { + return new VehicleConfidence( + requireNonNull(heading, "heading"), + requireNonNull(speed, "speed"), + vehicleLength, + yawRate, + longitudinalAcceleration, + lateralAcceleration, + verticalAcceleration); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/validation/CpmValidationException.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/validation/CpmValidationException.java new file mode 100644 index 000000000..169d33364 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/validation/CpmValidationException.java @@ -0,0 +1,14 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.validation; + +public final class CpmValidationException extends RuntimeException { + public CpmValidationException(String message) { + super(message); + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/validation/CpmValidator121.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/validation/CpmValidator121.java new file mode 100644 index 000000000..8a4755375 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v121/validation/CpmValidator121.java @@ -0,0 +1,698 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v121.validation; + +import com.orange.iot3mobility.messages.cpm.v121.model.CpmEnvelope121; +import com.orange.iot3mobility.messages.cpm.v121.model.CpmMessage121; +import com.orange.iot3mobility.messages.cpm.v121.model.defs.*; +import com.orange.iot3mobility.messages.cpm.v121.model.freespacecontainer.FreeSpaceAddendum; +import com.orange.iot3mobility.messages.cpm.v121.model.freespacecontainer.FreeSpaceAddendumContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.freespacecontainer.FreeSpaceArea; +import com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer.ManagementConfidence; +import com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer.ManagementContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer.PositionConfidenceEllipse; +import com.orange.iot3mobility.messages.cpm.v121.model.managementcontainer.ReferencePosition; +import com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer.*; +import com.orange.iot3mobility.messages.cpm.v121.model.sensorinformationcontainer.*; +import com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer.OriginatingRsuContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer.OriginatingVehicleContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer.StationDataContainer; +import com.orange.iot3mobility.messages.cpm.v121.model.stationdatacontainer.VehicleConfidence; + +import java.util.List; +import java.util.Objects; + +public final class CpmValidator121 { + + private CpmValidator121() {} + + public static void validateEnvelope(CpmEnvelope121 env) { + requireNonNull("envelope", env); + requireEquals("type", env.type(), "cpm"); + requireEnum("origin", env.origin(), + List.of("self", "global_application", "mec_application", "on_board_application")); + requireEquals("version", env.version(), "1.2.1"); + requireNotBlank("source_uuid", env.sourceUuid()); + checkRange("timestamp", env.timestamp(), 1514764800000L, 1830297600000L); + validateMessage(env.message()); + } + + public static void validateMessage(CpmMessage121 msg) { + requireNonNull("message", msg); + checkRange("protocol_version", msg.protocolVersion(), 0, 255); + checkRange("station_id", msg.stationId(), 0, 4294967295L); + checkRange("generation_delta_time", msg.generationDeltaTime(), 0, 65535); + validateManagementContainer(msg.managementContainer()); + if (msg.stationDataContainer() != null) { + validateStationDataContainer(msg.stationDataContainer()); + } + if (msg.sensorInformationContainer() != null) { + validateSensorInformationContainer(msg.sensorInformationContainer()); + } + if (msg.perceivedObjectContainer() != null) { + validatePerceivedObjectContainer(msg.perceivedObjectContainer()); + } + if (msg.freeSpaceAddendumContainer() != null) { + validateFreeSpaceAddendumContainer(msg.freeSpaceAddendumContainer()); + } + } + + /* --------------------------------------------------------------------- */ + /* Management container */ + /* --------------------------------------------------------------------- */ + + private static void validateManagementContainer(ManagementContainer container) { + requireNonNull("management_container", container); + checkRange("management_container.station_type", container.stationType(), 0, 254); + validateReferencePosition(container.referencePosition()); + validateManagementConfidence(container.confidence()); + } + + private static void validateReferencePosition(ReferencePosition reference) { + requireNonNull("reference_position", reference); + checkRange("reference_position.latitude", reference.latitude(), -900000000, 900000001); + checkRange("reference_position.longitude", reference.longitude(), -1800000000, 1800000001); + checkRange("reference_position.altitude", reference.altitude(), -100000, 800001); + } + + private static void validateManagementConfidence(ManagementConfidence confidence) { + requireNonNull("management_container.confidence", confidence); + validatePositionConfidenceEllipse("management_container.confidence.position_confidence_ellipse", + confidence.positionConfidenceEllipse()); + checkRange("management_container.confidence.altitude", confidence.altitude(), 0, 15); + } + + private static void validatePositionConfidenceEllipse(String prefix, PositionConfidenceEllipse ellipse) { + requireNonNull(prefix, ellipse); + checkRange(prefix + ".semi_major_confidence", ellipse.semiMajorConfidence(), 0, 4095); + checkRange(prefix + ".semi_minor_confidence", ellipse.semiMinorConfidence(), 0, 4095); + checkRange(prefix + ".semi_major_orientation", ellipse.semiMajorOrientation(), 0, 3601); + } + + /* --------------------------------------------------------------------- */ + /* Station data container */ + /* --------------------------------------------------------------------- */ + + private static void validateStationDataContainer(StationDataContainer container) { + requireNonNull("station_data_container", container); + boolean hasVehicle = container.originatingVehicleContainer() != null; + boolean hasRsu = container.originatingRsuContainer() != null; + if (hasVehicle == hasRsu) { + throw new CpmValidationException("station_data_container must contain exactly one of originating_vehicle_container or originating_rsu_container"); + } + if (hasVehicle) { + validateOriginatingVehicleContainer(container.originatingVehicleContainer()); + } + if (hasRsu) { + validateOriginatingRsuContainer(container.originatingRsuContainer()); + } + } + + private static void validateOriginatingVehicleContainer(OriginatingVehicleContainer container) { + requireNonNull("originating_vehicle_container", container); + checkRange("originating_vehicle_container.heading", container.heading(), 0, 3601); + checkRange("originating_vehicle_container.speed", container.speed(), 0, 16383); + validateVehicleConfidence(container.confidence()); + if (container.driveDirection() != null) { + checkRange("originating_vehicle_container.drive_direction", container.driveDirection(), 0, 2); + } + if (container.vehicleLength() != null) { + checkRange("originating_vehicle_container.vehicle_length", container.vehicleLength(), 1, 1023); + } + if (container.vehicleWidth() != null) { + checkRange("originating_vehicle_container.vehicle_width", container.vehicleWidth(), 1, 62); + } + if (container.longitudinalAcceleration() != null) { + checkRange("originating_vehicle_container.longitudinal_acceleration", container.longitudinalAcceleration(), -160, 161); + } + if (container.yawRate() != null) { + checkRange("originating_vehicle_container.yaw_rate", container.yawRate(), -32766, 32767); + } + if (container.lateralAcceleration() != null) { + checkRange("originating_vehicle_container.lateral_acceleration", container.lateralAcceleration(), -160, 161); + } + if (container.verticalAcceleration() != null) { + checkRange("originating_vehicle_container.vertical_acceleration", container.verticalAcceleration(), -160, 161); + } + } + + private static void validateVehicleConfidence(VehicleConfidence confidence) { + requireNonNull("originating_vehicle_container.confidence", confidence); + checkRange("originating_vehicle_container.confidence.heading", confidence.heading(), 1, 127); + checkRange("originating_vehicle_container.confidence.speed", confidence.speed(), 1, 127); + if (confidence.vehicleLength() != null) { + checkRange("originating_vehicle_container.confidence.vehicle_length", confidence.vehicleLength(), 0, 4); + } + if (confidence.yawRate() != null) { + checkRange("originating_vehicle_container.confidence.yaw_rate", confidence.yawRate(), 0, 8); + } + if (confidence.longitudinalAcceleration() != null) { + checkRange("originating_vehicle_container.confidence.longitudinal_acceleration", confidence.longitudinalAcceleration(), 0, 102); + } + if (confidence.lateralAcceleration() != null) { + checkRange("originating_vehicle_container.confidence.lateral_acceleration", confidence.lateralAcceleration(), 0, 102); + } + if (confidence.verticalAcceleration() != null) { + checkRange("originating_vehicle_container.confidence.vertical_acceleration", confidence.verticalAcceleration(), 0, 102); + } + } + + private static void validateOriginatingRsuContainer(OriginatingRsuContainer container) { + requireNonNull("originating_rsu_container", container); + if (container.region() != null) { + checkRange("originating_rsu_container.region", container.region(), 0, 65535); + } + if (container.intersectionReferenceId() != null) { + checkRange("originating_rsu_container.intersection_reference_id", container.intersectionReferenceId(), 0, 65535); + } + if (container.roadSegmentReferenceId() != null) { + checkRange("originating_rsu_container.road_segment_reference_id", container.roadSegmentReferenceId(), 0, 65535); + } + } + + /* --------------------------------------------------------------------- */ + /* Sensor information container */ + /* --------------------------------------------------------------------- */ + + private static void validateSensorInformationContainer(SensorInformationContainer container) { + requireNonNull("sensor_information_container", container); + List sensors = requireNonNull("sensor_information_container", container.sensorInformation()); + int size = sensors.size(); + if (size < 1 || size > 128) { + throw new CpmValidationException("sensor_information_container size out of range [1,128]: " + size); + } + for (int i = 0; i < size; i++) { + validateSensorInformation("sensor_information_container[" + i + "]", sensors.get(i)); + } + } + + private static void validateSensorInformation(String prefix, SensorInformation info) { + requireNonNull(prefix, info); + checkRange(prefix + ".sensor_id", info.sensorId(), 0, 255); + checkRange(prefix + ".type", info.type(), 0, 15); + validateDetectionArea(prefix + ".detection_area", info.detectionArea()); + } + + private static void validateDetectionArea(String prefix, DetectionArea area) { + requireNonNull(prefix, area); + if (area.vehicleSensor() != null) { + validateVehicleSensor(prefix + ".vehicle_sensor", area.vehicleSensor()); + } + if (area.stationarySensorRadial() != null) { + validateStationarySensorRadial(prefix + ".stationary_sensor_radial", area.stationarySensorRadial()); + } + if (area.stationarySensorPolygon() != null) { + validateAreaPolygon(prefix + ".stationary_sensor_polygon", area.stationarySensorPolygon()); + } + if (area.stationarySensorCircular() != null) { + validateAreaCircular(prefix + ".stationary_sensor_circular", area.stationarySensorCircular()); + } + if (area.stationarySensorEllipse() != null) { + validateAreaEllipse(prefix + ".stationary_sensor_ellipse", area.stationarySensorEllipse()); + } + if (area.stationarySensorRectangle() != null) { + validateAreaRectangle(prefix + ".stationary_sensor_rectangle", area.stationarySensorRectangle()); + } + } + + private static void validateVehicleSensor(String prefix, VehicleSensor sensor) { + requireNonNull(prefix, sensor); + if (sensor.refPointId() != null) { + checkRange(prefix + ".ref_point_id", sensor.refPointId(), 0, 255); + } + checkRange(prefix + ".x_sensor_offset", sensor.xSensorOffset(), -3094, 1001); + checkRange(prefix + ".y_sensor_offset", sensor.ySensorOffset(), -1000, 1000); + if (sensor.zSensorOffset() != null) { + checkRange(prefix + ".z_sensor_offset", sensor.zSensorOffset(), 0, 1000); + } + List properties = requireNonNull(prefix + ".vehicle_sensor_property_list", + sensor.vehicleSensorPropertyList()); + int size = properties.size(); + if (size < 1 || size > 10) { + throw new CpmValidationException(prefix + ".vehicle_sensor_property_list size out of range [1,10]: " + size); + } + for (int i = 0; i < size; i++) { + validateVehicleSensorProperty(prefix + ".vehicle_sensor_property_list[" + i + "]", properties.get(i)); + } + } + + private static void validateVehicleSensorProperty(String prefix, VehicleSensorProperty property) { + requireNonNull(prefix, property); + checkRange(prefix + ".range", property.range(), 0, 10000); + checkRange(prefix + ".horizontal_opening_angle_start", property.horizontalOpeningAngleStart(), 0, 3601); + checkRange(prefix + ".horizontal_opening_angle_end", property.horizontalOpeningAngleEnd(), 0, 3601); + if (property.verticalOpeningAngleStart() != null) { + checkRange(prefix + ".vertical_opening_angle_start", property.verticalOpeningAngleStart(), 0, 3601); + } + if (property.verticalOpeningAngleEnd() != null) { + checkRange(prefix + ".vertical_opening_angle_end", property.verticalOpeningAngleEnd(), 0, 3601); + } + } + + private static void validateStationarySensorRadial(String prefix, StationarySensorRadial radial) { + requireNonNull(prefix, radial); + checkRange(prefix + ".range", radial.range(), 0, 10000); + checkRange(prefix + ".horizontal_opening_angle_start", radial.horizontalOpeningAngleStart(), 0, 3601); + checkRange(prefix + ".horizontal_opening_angle_end", radial.horizontalOpeningAngleEnd(), 0, 3601); + if (radial.verticalOpeningAngleStart() != null) { + checkRange(prefix + ".vertical_opening_angle_start", radial.verticalOpeningAngleStart(), 0, 3601); + } + if (radial.verticalOpeningAngleEnd() != null) { + checkRange(prefix + ".vertical_opening_angle_end", radial.verticalOpeningAngleEnd(), 0, 3601); + } + if (radial.sensorPositionOffset() != null) { + validateOffset(prefix + ".sensor_position_offset", radial.sensorPositionOffset()); + } + } + + /* --------------------------------------------------------------------- */ + /* Perceived object container */ + /* --------------------------------------------------------------------- */ + + private static void validatePerceivedObjectContainer(PerceivedObjectContainer container) { + requireNonNull("perceived_object_container", container); + List objects = requireNonNull("perceived_object_container", container.perceivedObjects()); + int size = objects.size(); + if (size < 1 || size > 128) { + throw new CpmValidationException("perceived_object_container size out of range [1,128]: " + size); + } + for (int i = 0; i < size; i++) { + validatePerceivedObject("perceived_object_container[" + i + "]", objects.get(i)); + } + } + + private static void validatePerceivedObject(String prefix, PerceivedObject object) { + requireNonNull(prefix, object); + checkRange(prefix + ".object_id", object.objectId(), 0, 255); + checkRange(prefix + ".time_of_measurement", object.timeOfMeasurement(), -1500, 1500); + checkRange(prefix + ".x_distance", object.xDistance(), -132768, 132767); + checkRange(prefix + ".y_distance", object.yDistance(), -132768, 132767); + if (object.zDistance() != null) { + checkRange(prefix + ".z_distance", object.zDistance(), -132768, 132767); + } + checkRange(prefix + ".x_speed", object.xSpeed(), -16383, 16383); + checkRange(prefix + ".y_speed", object.ySpeed(), -16383, 16383); + if (object.zSpeed() != null) { + checkRange(prefix + ".z_speed", object.zSpeed(), -16383, 16383); + } + if (object.xAcceleration() != null) { + checkRange(prefix + ".x_acceleration", object.xAcceleration(), -160, 161); + } + if (object.yAcceleration() != null) { + checkRange(prefix + ".y_acceleration", object.yAcceleration(), -160, 161); + } + if (object.zAcceleration() != null) { + checkRange(prefix + ".z_acceleration", object.zAcceleration(), -160, 161); + } + if (object.rollAngle() != null) { + checkRange(prefix + ".roll_angle", object.rollAngle(), 0, 3601); + } + if (object.pitchAngle() != null) { + checkRange(prefix + ".pitch_angle", object.pitchAngle(), 0, 3601); + } + if (object.yawAngle() != null) { + checkRange(prefix + ".yaw_angle", object.yawAngle(), 0, 3601); + } + if (object.rollRate() != null) { + checkRange(prefix + ".roll_rate", object.rollRate(), -32766, 32767); + } + if (object.pitchRate() != null) { + checkRange(prefix + ".pitch_rate", object.pitchRate(), -32766, 32767); + } + if (object.yawRate() != null) { + checkRange(prefix + ".yaw_rate", object.yawRate(), -32766, 32767); + } + if (object.rollAcceleration() != null) { + checkRange(prefix + ".roll_acceleration", object.rollAcceleration(), -32766, 32767); + } + if (object.pitchAcceleration() != null) { + checkRange(prefix + ".pitch_acceleration", object.pitchAcceleration(), -32766, 32767); + } + if (object.yawAcceleration() != null) { + checkRange(prefix + ".yaw_acceleration", object.yawAcceleration(), -32766, 32767); + } + if (object.lowerTriangularCorrelationMatrixColumns() != null) { + validateLowerTriangularCorrelationMatrix(prefix + ".lower_triangular_correlation_matrix_columns", + object.lowerTriangularCorrelationMatrixColumns()); + } + if (object.planarObjectDimension1() != null) { + checkRange(prefix + ".planar_object_dimension_1", object.planarObjectDimension1(), 0, 1023); + } + if (object.planarObjectDimension2() != null) { + checkRange(prefix + ".planar_object_dimension_2", object.planarObjectDimension2(), 0, 1023); + } + if (object.verticalObjectDimension() != null) { + checkRange(prefix + ".vertical_object_dimension", object.verticalObjectDimension(), 0, 1023); + } + if (object.objectRefPoint() != null) { + checkRange(prefix + ".object_ref_point", object.objectRefPoint(), 0, 8); + } + checkRange(prefix + ".object_age", object.objectAge(), 0, 1500); + if (object.sensorIdList() != null) { + int size = object.sensorIdList().size(); + if (size < 1 || size > 128) { + throw new CpmValidationException(prefix + ".sensor_id_list size out of range [1,128]: " + size); + } + for (int i = 0; i < size; i++) { + checkRange(prefix + ".sensor_id_list[" + i + "]", object.sensorIdList().get(i), 0, 255); + } + } + if (object.dynamicStatus() != null) { + checkRange(prefix + ".dynamic_status", object.dynamicStatus(), 0, 2); + } + if (object.classification() != null) { + validateClassification(prefix + ".classification", object.classification()); + } + if (object.matchedPosition() != null) { + validateMapPosition(prefix + ".matched_position", object.matchedPosition()); + } + validatePerceivedObjectConfidence(prefix + ".confidence", object.confidence()); + } + + private static void validateLowerTriangularCorrelationMatrix(String prefix, LowerTriangularCorrelationMatrix matrix) { + requireNonNull(prefix, matrix); + List> columns = requireNonNull(prefix, matrix.columns()); + int size = columns.size(); + if (size < 1 || size > 17) { + throw new CpmValidationException(prefix + " size out of range [1,17]: " + size); + } + for (int i = 0; i < size; i++) { + List column = requireNonNull(prefix + "[" + i + "]", columns.get(i)); + int columnSize = column.size(); + if (columnSize < 1 || columnSize > 17) { + throw new CpmValidationException(prefix + "[" + i + "] size out of range [1,17]: " + columnSize); + } + for (int j = 0; j < columnSize; j++) { + checkRange(prefix + "[" + i + "][" + j + "]", column.get(j), -100, 100); + } + } + } + + private static void validatePerceivedObjectConfidence(String prefix, PerceivedObjectConfidence confidence) { + requireNonNull(prefix, confidence); + checkRange(prefix + ".x_distance", confidence.xDistance(), 0, 4095); + checkRange(prefix + ".y_distance", confidence.yDistance(), 0, 4095); + if (confidence.zDistance() != null) { + checkRange(prefix + ".z_distance", confidence.zDistance(), 0, 4095); + } + checkRange(prefix + ".x_speed", confidence.xSpeed(), 0, 7); + checkRange(prefix + ".y_speed", confidence.ySpeed(), 0, 7); + if (confidence.zSpeed() != null) { + checkRange(prefix + ".z_speed", confidence.zSpeed(), 0, 7); + } + if (confidence.xAcceleration() != null) { + checkRange(prefix + ".x_acceleration", confidence.xAcceleration(), 0, 102); + } + if (confidence.yAcceleration() != null) { + checkRange(prefix + ".y_acceleration", confidence.yAcceleration(), 0, 102); + } + if (confidence.zAcceleration() != null) { + checkRange(prefix + ".z_acceleration", confidence.zAcceleration(), 0, 102); + } + if (confidence.rollAngle() != null) { + checkRange(prefix + ".roll_angle", confidence.rollAngle(), 1, 127); + } + if (confidence.pitchAngle() != null) { + checkRange(prefix + ".pitch_angle", confidence.pitchAngle(), 1, 127); + } + if (confidence.yawAngle() != null) { + checkRange(prefix + ".yaw_angle", confidence.yawAngle(), 1, 127); + } + if (confidence.rollRate() != null) { + checkRange(prefix + ".roll_rate", confidence.rollRate(), 0, 8); + } + if (confidence.pitchRate() != null) { + checkRange(prefix + ".pitch_rate", confidence.pitchRate(), 0, 8); + } + if (confidence.yawRate() != null) { + checkRange(prefix + ".yaw_rate", confidence.yawRate(), 0, 8); + } + if (confidence.rollAcceleration() != null) { + checkRange(prefix + ".roll_acceleration", confidence.rollAcceleration(), 0, 8); + } + if (confidence.pitchAcceleration() != null) { + checkRange(prefix + ".pitch_acceleration", confidence.pitchAcceleration(), 0, 8); + } + if (confidence.yawAcceleration() != null) { + checkRange(prefix + ".yaw_acceleration", confidence.yawAcceleration(), 0, 8); + } + if (confidence.planarObjectDimension1() != null) { + checkRange(prefix + ".planar_object_dimension_1", confidence.planarObjectDimension1(), 0, 102); + } + if (confidence.planarObjectDimension2() != null) { + checkRange(prefix + ".planar_object_dimension_2", confidence.planarObjectDimension2(), 0, 102); + } + if (confidence.verticalObjectDimension() != null) { + checkRange(prefix + ".vertical_object_dimension", confidence.verticalObjectDimension(), 0, 102); + } + if (confidence.longitudinalLanePosition() != null) { + checkRange(prefix + ".longitudinal_lane_position", confidence.longitudinalLanePosition(), 0, 102); + } + checkRange(prefix + ".object", confidence.object(), 0, 15); + } + + private static void validateClassification(String prefix, List list) { + int size = list.size(); + if (size < 1 || size > 8) { + throw new CpmValidationException(prefix + " size out of range [1,8]: " + size); + } + for (int i = 0; i < size; i++) { + validateObjectClassification(prefix + "[" + i + "]", list.get(i)); + } + } + + private static void validateObjectClassification(String prefix, ObjectClassification classification) { + requireNonNull(prefix, classification); + validateObjectClass(prefix + ".object_class", classification.objectClass()); + checkRange(prefix + ".confidence", classification.confidence(), 0, 101); + } + + private static void validateObjectClass(String prefix, ObjectClass objectClass) { + requireNonNull(prefix, objectClass); + int count = 0; + if (objectClass.vehicle() != null) { + count++; + checkRange(prefix + ".vehicle", objectClass.vehicle(), 0, 255); + } + if (objectClass.singleVru() != null) { + count++; + validateObjectClassVru(prefix + ".single_vru", objectClass.singleVru()); + } + if (objectClass.vruGroup() != null) { + count++; + validateObjectClassGroup(prefix + ".vru_group", objectClass.vruGroup()); + } + if (objectClass.other() != null) { + count++; + checkRange(prefix + ".other", objectClass.other(), 0, 255); + } + if (count != 1) { + throw new CpmValidationException(prefix + " must contain exactly one class option"); + } + } + + private static void validateObjectClassVru(String prefix, ObjectClassVru vru) { + requireNonNull(prefix, vru); + int count = 0; + if (vru.pedestrian() != null) { + count++; + checkRange(prefix + ".pedestrian", vru.pedestrian(), 0, 15); + } + if (vru.bicyclist() != null) { + count++; + checkRange(prefix + ".bicyclist", vru.bicyclist(), 0, 15); + } + if (vru.motorcylist() != null) { + count++; + checkRange(prefix + ".motorcylist", vru.motorcylist(), 0, 15); + } + if (vru.animal() != null) { + count++; + checkRange(prefix + ".animal", vru.animal(), 0, 15); + } + if (count != 1) { + throw new CpmValidationException(prefix + " must contain exactly one VRU option"); + } + } + + private static void validateObjectClassGroup(String prefix, ObjectClassGroup group) { + requireNonNull(prefix, group); + validateGroupType(prefix + ".group_type", group.groupType()); + checkRange(prefix + ".group_size", group.groupSize(), 0, 255); + if (group.clusterId() != null) { + checkRange(prefix + ".cluster_id", group.clusterId(), 0, 255); + } + } + + private static void validateGroupType(String prefix, GroupType type) { + requireNonNull(prefix, type); + } + + private static void validateMapPosition(String prefix, MapPosition position) { + requireNonNull(prefix, position); + if (position.laneId() != null) { + checkRange(prefix + ".lane_id", position.laneId(), 0, 255); + } + if (position.longitudinalLanePosition() != null) { + checkRange(prefix + ".longitudinal_lane_position", position.longitudinalLanePosition(), 0, 32767); + } + } + + /* --------------------------------------------------------------------- */ + /* Free space addendum container */ + /* --------------------------------------------------------------------- */ + + private static void validateFreeSpaceAddendumContainer(FreeSpaceAddendumContainer container) { + requireNonNull("free_space_addendum_container", container); + List addenda = requireNonNull("free_space_addendum_container", container.freeSpaceAddenda()); + int size = addenda.size(); + if (size < 1 || size > 128) { + throw new CpmValidationException("free_space_addendum_container size out of range [1,128]: " + size); + } + for (int i = 0; i < size; i++) { + validateFreeSpaceAddendum("free_space_addendum_container[" + i + "]", addenda.get(i)); + } + } + + private static void validateFreeSpaceAddendum(String prefix, FreeSpaceAddendum addendum) { + requireNonNull(prefix, addendum); + validateFreeSpaceArea(prefix + ".free_space_area", addendum.freeSpaceArea()); + checkRange(prefix + ".free_space_confidence", addendum.freeSpaceConfidence(), 0, 101); + if (addendum.sensorIdList() != null) { + int size = addendum.sensorIdList().size(); + if (size < 1 || size > 128) { + throw new CpmValidationException(prefix + ".sensor_id_list size out of range [1,128]: " + size); + } + for (int i = 0; i < size; i++) { + checkRange(prefix + ".sensor_id_list[" + i + "]", addendum.sensorIdList().get(i), 0, 255); + } + } + } + + private static void validateFreeSpaceArea(String prefix, FreeSpaceArea area) { + requireNonNull(prefix, area); + int count = 0; + if (area.freeSpacePolygon() != null) { + count++; + validateAreaPolygon(prefix + ".free_space_polygon", area.freeSpacePolygon()); + } + if (area.freeSpaceCircular() != null) { + count++; + validateAreaCircular(prefix + ".free_space_circular", area.freeSpaceCircular()); + } + if (area.freeSpaceEllipse() != null) { + count++; + validateAreaEllipse(prefix + ".free_space_ellipse", area.freeSpaceEllipse()); + } + if (area.freeSpaceRectangle() != null) { + count++; + validateAreaRectangle(prefix + ".free_space_rectangle", area.freeSpaceRectangle()); + } + if (count != 1) { + throw new CpmValidationException(prefix + " must contain exactly one area option"); + } + } + + /* --------------------------------------------------------------------- */ + /* Shared helpers */ + /* --------------------------------------------------------------------- */ + + private static void validateOffset(String prefix, Offset offset) { + requireNonNull(prefix, offset); + checkRange(prefix + ".x", offset.x(), -32768, 32767); + checkRange(prefix + ".y", offset.y(), -32768, 32767); + if (offset.z() != null) { + checkRange(prefix + ".z", offset.z(), -32768, 32767); + } + } + + private static void validateAreaPolygon(String prefix, AreaPolygon polygon) { + requireNonNull(prefix, polygon); + List offsets = requireNonNull(prefix, polygon.offsets()); + int size = offsets.size(); + if (size < 3 || size > 16) { + throw new CpmValidationException(prefix + " size out of range [3,16]: " + size); + } + for (int i = 0; i < size; i++) { + validateOffset(prefix + "[" + i + "]", offsets.get(i)); + } + } + + private static void validateAreaCircular(String prefix, AreaCircular circular) { + requireNonNull(prefix, circular); + if (circular.nodeCenterPoint() != null) { + validateOffset(prefix + ".node_center_point", circular.nodeCenterPoint()); + } + checkRange(prefix + ".radius", circular.radius(), 0, 10000); + } + + private static void validateAreaEllipse(String prefix, AreaEllipse ellipse) { + requireNonNull(prefix, ellipse); + if (ellipse.nodeCenterPoint() != null) { + validateOffset(prefix + ".node_center_point", ellipse.nodeCenterPoint()); + } + checkRange(prefix + ".semi_major_range_length", ellipse.semiMajorRangeLength(), 0, 10000); + checkRange(prefix + ".semi_minor_range_length", ellipse.semiMinorRangeLength(), 0, 10000); + checkRange(prefix + ".semi_major_range_orientation", ellipse.semiMajorRangeOrientation(), 0, 3601); + if (ellipse.semiHeight() != null) { + checkRange(prefix + ".semi_height", ellipse.semiHeight(), 0, 10000); + } + } + + private static void validateAreaRectangle(String prefix, AreaRectangle rectangle) { + requireNonNull(prefix, rectangle); + if (rectangle.nodeCenterPoint() != null) { + validateOffset(prefix + ".node_center_point", rectangle.nodeCenterPoint()); + } + checkRange(prefix + ".semi_major_range_length", rectangle.semiMajorRangeLength(), 0, 10000); + checkRange(prefix + ".semi_minor_range_length", rectangle.semiMinorRangeLength(), 0, 10000); + checkRange(prefix + ".semi_major_range_orientation", rectangle.semiMajorRangeOrientation(), 0, 3601); + if (rectangle.semiHeight() != null) { + checkRange(prefix + ".semi_height", rectangle.semiHeight(), 0, 10000); + } + } + + private static T requireNonNull(String field, T value) { + if (value == null) { + throw new CpmValidationException("Missing mandatory field: " + field); + } + return value; + } + + private static void requireNotBlank(String field, String value) { + if (value == null || value.isBlank()) { + throw new CpmValidationException("Missing mandatory field: " + field); + } + } + + private static void requireEquals(String field, String actual, String expected) { + if (!Objects.equals(actual, expected)) { + throw new CpmValidationException(field + " must equal '" + expected + "'"); + } + } + + private static void requireEnum(String field, String actual, List allowed) { + if (!allowed.contains(actual)) { + throw new CpmValidationException(field + " must be one of " + allowed); + } + } + + private static void checkRange(String field, Integer value, long min, long max) { + if (value != null && (value < min || value > max)) { + throw new CpmValidationException( + field + " out of range [" + min + ", " + max + "] (actual=" + value + ")"); + } + } + + private static void checkRange(String field, Long value, long min, long max) { + if (value != null && (value < min || value > max)) { + throw new CpmValidationException( + field + " out of range [" + min + ", " + max + "] (actual=" + value + ")"); + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/codec/CpmReader211.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/codec/CpmReader211.java new file mode 100644 index 000000000..887018d91 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/codec/CpmReader211.java @@ -0,0 +1,1458 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.codec; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.orange.iot3mobility.messages.cpm.v211.model.CpmEnvelope211; +import com.orange.iot3mobility.messages.cpm.v211.model.CpmMessage211; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.Angle; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.*; +import com.orange.iot3mobility.messages.cpm.v211.model.managementcontainer.ManagementContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.managementcontainer.MessageRateRange; +import com.orange.iot3mobility.messages.cpm.v211.model.managementcontainer.SegmentationInfo; +import com.orange.iot3mobility.messages.cpm.v211.model.originatingrsucontainer.OriginatingRsuContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.originatingvehiclecontainer.OriginatingVehicleContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.originatingvehiclecontainer.TrailerData; +import com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer.*; +import com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer.PerceivedObjectContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.perceptionregioncontainer.PerceptionRegion; +import com.orange.iot3mobility.messages.cpm.v211.model.perceptionregioncontainer.PerceptionRegionContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.sensorinformationcontainer.SensorInformation; +import com.orange.iot3mobility.messages.cpm.v211.model.sensorinformationcontainer.SensorInformationContainer; +import com.orange.iot3mobility.messages.cpm.v211.validation.CpmValidationException; +import com.orange.iot3mobility.messages.cpm.v211.validation.CpmValidator211; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Streaming JSON reader for CPM 2.1.1 + */ +public final class CpmReader211 { + + private final JsonFactory jsonFactory; + + public CpmReader211(JsonFactory jsonFactory) { + this.jsonFactory = Objects.requireNonNull(jsonFactory, "jsonFactory"); + } + + public CpmEnvelope211 read(InputStream in) throws IOException { + try (JsonParser parser = jsonFactory.createParser(in)) { + expect(parser.nextToken(), JsonToken.START_OBJECT); + + String messageType = null; + String sourceUuid = null; + Long timestamp = null; + String version = null; + Integer objectIdRotationCount = null; + CpmMessage211 message = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "message_type" -> messageType = parser.getValueAsString(); + case "source_uuid" -> sourceUuid = parser.getValueAsString(); + case "timestamp" -> timestamp = parser.getLongValue(); + case "version" -> version = parser.getValueAsString(); + case "object_id_rotation_count" -> objectIdRotationCount = parser.getIntValue(); + case "message" -> message = readMessage(parser); + default -> parser.skipChildren(); + } + } + + CpmEnvelope211 envelope = new CpmEnvelope211( + messageType, + sourceUuid, + requireField(timestamp, "timestamp"), + requireField(version, "version"), + objectIdRotationCount, + requireField(message, "message")); + + CpmValidator211.validateEnvelope(envelope); + return envelope; + } + } + + private CpmMessage211 readMessage(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + + Integer protocolVersion = null; + Long stationId = null; + ManagementContainer managementContainer = null; + OriginatingVehicleContainer originatingVehicleContainer = null; + OriginatingRsuContainer originatingRsuContainer = null; + SensorInformationContainer sensorInformationContainer = null; + PerceptionRegionContainer perceptionRegionContainer = null; + PerceivedObjectContainer perceivedObjectContainer = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "protocol_version" -> protocolVersion = parser.getIntValue(); + case "station_id" -> stationId = parser.getLongValue(); + case "management_container" -> managementContainer = readManagementContainer(parser); + case "originating_vehicle_container" -> originatingVehicleContainer = readOriginatingVehicleContainer(parser); + case "originating_rsu_container" -> originatingRsuContainer = readOriginatingRsuContainer(parser); + case "sensor_information_container" -> sensorInformationContainer = readSensorInformationContainer(parser); + case "perception_region_container" -> perceptionRegionContainer = readPerceptionRegionContainer(parser); + case "perceived_object_container" -> perceivedObjectContainer = readPerceivedObjectContainer(parser); + default -> parser.skipChildren(); + } + } + + return new CpmMessage211( + requireField(protocolVersion, "protocol_version"), + requireField(stationId, "station_id"), + requireField(managementContainer, "management_container"), + originatingVehicleContainer, + originatingRsuContainer, + sensorInformationContainer, + perceptionRegionContainer, + perceivedObjectContainer); + } + + /* --------------------------------------------------------------------- */ + /* Management container */ + /* --------------------------------------------------------------------- */ + + private ManagementContainer readManagementContainer(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Long referenceTime = null; + ReferencePosition referencePosition = null; + SegmentationInfo segmentationInfo = null; + MessageRateRange messageRateRange = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "reference_time" -> referenceTime = parser.getLongValue(); + case "reference_position" -> referencePosition = readReferencePosition(parser); + case "segmentation_info" -> segmentationInfo = readSegmentationInfo(parser); + case "message_rate_range" -> messageRateRange = readMessageRateRange(parser); + default -> parser.skipChildren(); + } + } + + return new ManagementContainer( + requireField(referenceTime, "reference_time"), + requireField(referencePosition, "reference_position"), + segmentationInfo, + messageRateRange); + } + + private ReferencePosition readReferencePosition(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer latitude = null; + Integer longitude = null; + PositionConfidenceEllipse ellipse = null; + Altitude altitude = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "latitude" -> latitude = parser.getIntValue(); + case "longitude" -> longitude = parser.getIntValue(); + case "position_confidence_ellipse" -> ellipse = readPositionConfidenceEllipse(parser); + case "altitude" -> altitude = readAltitude(parser); + default -> parser.skipChildren(); + } + } + + return new ReferencePosition( + requireField(latitude, "latitude"), + requireField(longitude, "longitude"), + requireField(ellipse, "position_confidence_ellipse"), + requireField(altitude, "altitude")); + } + + private PositionConfidenceEllipse readPositionConfidenceEllipse(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer semiMajor = null; + Integer semiMinor = null; + Integer semiMajorOrientation = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "semi_major" -> semiMajor = parser.getIntValue(); + case "semi_minor" -> semiMinor = parser.getIntValue(); + case "semi_major_orientation" -> semiMajorOrientation = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new PositionConfidenceEllipse( + requireField(semiMajor, "semi_major"), + requireField(semiMinor, "semi_minor"), + requireField(semiMajorOrientation, "semi_major_orientation")); + } + + private Altitude readAltitude(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer value = null; + Integer confidence = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "value" -> value = parser.getIntValue(); + case "confidence" -> confidence = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new Altitude( + requireField(value, "value"), + requireField(confidence, "confidence")); + } + + private SegmentationInfo readSegmentationInfo(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer totalMsgNo = null; + Integer thisMsgNo = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "total_msg_no" -> totalMsgNo = parser.getIntValue(); + case "this_msg_no" -> thisMsgNo = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new SegmentationInfo( + requireField(totalMsgNo, "total_msg_no"), + requireField(thisMsgNo, "this_msg_no")); + } + + private MessageRateRange readMessageRateRange(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + MessageRateHz min = null; + MessageRateHz max = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "message_rate_min" -> min = readMessageRateHz(parser); + case "message_rate_max" -> max = readMessageRateHz(parser); + default -> parser.skipChildren(); + } + } + + return new MessageRateRange( + requireField(min, "message_rate_min"), + requireField(max, "message_rate_max")); + } + + private MessageRateHz readMessageRateHz(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer mantissa = null; + Integer exponent = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "mantissa" -> mantissa = parser.getIntValue(); + case "exponent" -> exponent = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new MessageRateHz( + requireField(mantissa, "mantissa"), + requireField(exponent, "exponent")); + } + + /* --------------------------------------------------------------------- */ + /* Originating vehicle container */ + /* --------------------------------------------------------------------- */ + + private OriginatingVehicleContainer readOriginatingVehicleContainer(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Angle orientationAngle = null; + Angle pitchAngle = null; + Angle rollAngle = null; + List trailerDataSet = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "orientation_angle" -> orientationAngle = readAngle(parser); + case "pitch_angle" -> pitchAngle = readAngle(parser); + case "roll_angle" -> rollAngle = readAngle(parser); + case "trailer_data_set" -> trailerDataSet = readTrailerDataSet(parser); + default -> parser.skipChildren(); + } + } + + return new OriginatingVehicleContainer( + requireField(orientationAngle, "orientation_angle"), + pitchAngle, + rollAngle, + trailerDataSet); + } + + private List readTrailerDataSet(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List list = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + list.add(readTrailerData(parser)); + } + return list; + } + + private TrailerData readTrailerData(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer refPointId = null; + Integer hitchPointOffset = null; + Angle hitchAngle = null; + Integer frontOverhang = null; + Integer rearOverhang = null; + Integer trailerWidth = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "ref_point_id" -> refPointId = parser.getIntValue(); + case "hitch_point_offset" -> hitchPointOffset = parser.getIntValue(); + case "hitch_angle" -> hitchAngle = readAngle(parser); + case "front_overhang" -> frontOverhang = parser.getIntValue(); + case "rear_overhang" -> rearOverhang = parser.getIntValue(); + case "trailer_width" -> trailerWidth = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new TrailerData( + requireField(refPointId, "ref_point_id"), + requireField(hitchPointOffset, "hitch_point_offset"), + requireField(hitchAngle, "hitch_angle"), + frontOverhang, + rearOverhang, + trailerWidth); + } + + /* --------------------------------------------------------------------- */ + /* Originating RSU container */ + /* --------------------------------------------------------------------- */ + + private OriginatingRsuContainer readOriginatingRsuContainer(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List list = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + list.add(readMapReference(parser)); + } + return new OriginatingRsuContainer(list); + } + + /* --------------------------------------------------------------------- */ + /* Sensor information container */ + /* --------------------------------------------------------------------- */ + + private SensorInformationContainer readSensorInformationContainer(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List list = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + list.add(readSensorInformation(parser)); + } + return new SensorInformationContainer(list); + } + + private SensorInformation readSensorInformation(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer sensorId = null; + Integer sensorType = null; + Shape perceptionRegionShape = null; + Integer perceptionRegionConfidence = null; + Boolean shadowingApplies = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "sensor_id" -> sensorId = parser.getIntValue(); + case "sensor_type" -> sensorType = parser.getIntValue(); + case "perception_region_shape" -> perceptionRegionShape = readShape(parser); + case "perception_region_confidence" -> perceptionRegionConfidence = parser.getIntValue(); + case "shadowing_applies" -> shadowingApplies = parser.getBooleanValue(); + default -> parser.skipChildren(); + } + } + + return new SensorInformation( + requireField(sensorId, "sensor_id"), + requireField(sensorType, "sensor_type"), + perceptionRegionShape, + perceptionRegionConfidence, + requireField(shadowingApplies, "shadowing_applies")); + } + + /* --------------------------------------------------------------------- */ + /* Perception region container */ + /* --------------------------------------------------------------------- */ + + private PerceptionRegionContainer readPerceptionRegionContainer(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List list = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + list.add(readPerceptionRegion(parser)); + } + return new PerceptionRegionContainer(list); + } + + private PerceptionRegion readPerceptionRegion(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer measurementDeltaTime = null; + Integer perceptionRegionConfidence = null; + Shape perceptionRegionShape = null; + Boolean shadowingApplies = null; + List sensorIdList = null; + List perceivedObjectIds = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "measurement_delta_time" -> measurementDeltaTime = parser.getIntValue(); + case "perception_region_confidence" -> perceptionRegionConfidence = parser.getIntValue(); + case "perception_region_shape" -> perceptionRegionShape = readShape(parser); + case "shadowing_applies" -> shadowingApplies = parser.getBooleanValue(); + case "sensor_id_list" -> sensorIdList = readIntegerArray(parser); + case "perceived_object_ids" -> perceivedObjectIds = readIntegerArray(parser); + default -> parser.skipChildren(); + } + } + + return new PerceptionRegion( + requireField(measurementDeltaTime, "measurement_delta_time"), + requireField(perceptionRegionConfidence, "perception_region_confidence"), + requireField(perceptionRegionShape, "perception_region_shape"), + requireField(shadowingApplies, "shadowing_applies"), + sensorIdList, + perceivedObjectIds); + } + + private List readIntegerArray(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List values = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + values.add(parser.getIntValue()); + } + return values; + } + + /* --------------------------------------------------------------------- */ + /* Perceived object container */ + /* --------------------------------------------------------------------- */ + + private PerceivedObjectContainer readPerceivedObjectContainer(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List list = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + list.add(readPerceivedObject(parser)); + } + return new PerceivedObjectContainer(list); + } + + private PerceivedObject readPerceivedObject(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer measurementDeltaTime = null; + CartesianPosition3dWithConfidence position = null; + Integer objectId = null; + Velocity velocity = null; + Acceleration acceleration = null; + EulerAngles angles = null; + CartesianAngularVelocityComponent zAngularVelocity = null; + List matrices = null; + ObjectDimension objectDimensionZ = null; + ObjectDimension objectDimensionY = null; + ObjectDimension objectDimensionX = null; + Integer objectAge = null; + Integer objectPerceptionQuality = null; + List sensorIdList = null; + List classification = null; + MapPosition mapPosition = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "measurement_delta_time" -> measurementDeltaTime = parser.getIntValue(); + case "position" -> position = readCartesianPosition3dWithConfidence(parser); + case "object_id" -> objectId = parser.getIntValue(); + case "velocity" -> velocity = readVelocity(parser); + case "acceleration" -> acceleration = readAcceleration(parser); + case "angles" -> angles = readEulerAngles(parser); + case "z_angular_velocity" -> zAngularVelocity = readCartesianAngularVelocityComponent(parser); + case "lower_triangular_correlation_matrices" -> matrices = readLowerTriangularCorrelationMatrices(parser); + case "object_dimension_z" -> objectDimensionZ = readObjectDimension(parser); + case "object_dimension_y" -> objectDimensionY = readObjectDimension(parser); + case "object_dimension_x" -> objectDimensionX = readObjectDimension(parser); + case "object_age" -> objectAge = parser.getIntValue(); + case "object_perception_quality" -> objectPerceptionQuality = parser.getIntValue(); + case "sensor_id_list" -> sensorIdList = readIntegerArray(parser); + case "classification" -> classification = readClassification(parser); + case "map_position" -> mapPosition = readMapPosition(parser); + default -> parser.skipChildren(); + } + } + + return new PerceivedObject( + requireField(measurementDeltaTime, "measurement_delta_time"), + requireField(position, "position"), + objectId, + velocity, + acceleration, + angles, + zAngularVelocity, + matrices, + objectDimensionZ, + objectDimensionY, + objectDimensionX, + objectAge, + objectPerceptionQuality, + sensorIdList, + classification, + mapPosition); + } + + private CartesianPosition3dWithConfidence readCartesianPosition3dWithConfidence(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + CartesianCoordinateWithConfidence x = null; + CartesianCoordinateWithConfidence y = null; + CartesianCoordinateWithConfidence z = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "x_coordinate" -> x = readCartesianCoordinateWithConfidence(parser); + case "y_coordinate" -> y = readCartesianCoordinateWithConfidence(parser); + case "z_coordinate" -> z = readCartesianCoordinateWithConfidence(parser); + default -> parser.skipChildren(); + } + } + + return new CartesianPosition3dWithConfidence( + requireField(x, "x_coordinate"), + requireField(y, "y_coordinate"), + z); + } + + private CartesianCoordinateWithConfidence readCartesianCoordinateWithConfidence(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer value = null; + Integer confidence = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "value" -> value = parser.getIntValue(); + case "confidence" -> confidence = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new CartesianCoordinateWithConfidence( + requireField(value, "value"), + requireField(confidence, "confidence")); + } + + private Velocity readVelocity(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + PolarVelocity polar = null; + CartesianVelocity cartesian = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "polar_velocity" -> polar = readPolarVelocity(parser); + case "cartesian_velocity" -> cartesian = readCartesianVelocity(parser); + default -> parser.skipChildren(); + } + } + + return new Velocity(polar, cartesian); + } + + private PolarVelocity readPolarVelocity(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Speed velocityMagnitude = null; + Angle velocityDirection = null; + VelocityComponent zVelocity = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "velocity_magnitude" -> velocityMagnitude = readSpeed(parser); + case "velocity_direction" -> velocityDirection = readAngle(parser); + case "z_velocity" -> zVelocity = readVelocityComponent(parser); + default -> parser.skipChildren(); + } + } + + return new PolarVelocity( + requireField(velocityMagnitude, "velocity_magnitude"), + requireField(velocityDirection, "velocity_direction"), + zVelocity); + } + + private CartesianVelocity readCartesianVelocity(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + VelocityComponent x = null; + VelocityComponent y = null; + VelocityComponent z = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "x_velocity" -> x = readVelocityComponent(parser); + case "y_velocity" -> y = readVelocityComponent(parser); + case "z_velocity" -> z = readVelocityComponent(parser); + default -> parser.skipChildren(); + } + } + + return new CartesianVelocity( + requireField(x, "x_velocity"), + requireField(y, "y_velocity"), + z); + } + + private Acceleration readAcceleration(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + PolarAcceleration polar = null; + CartesianAcceleration cartesian = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "polar_acceleration" -> polar = readPolarAcceleration(parser); + case "cartesian_acceleration" -> cartesian = readCartesianAcceleration(parser); + default -> parser.skipChildren(); + } + } + + return new Acceleration(polar, cartesian); + } + + private PolarAcceleration readPolarAcceleration(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + AccelerationMagnitude magnitude = null; + Angle direction = null; + AccelerationComponent zAcceleration = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "acceleration_magnitude" -> magnitude = readAccelerationMagnitude(parser); + case "acceleration_direction" -> direction = readAngle(parser); + case "z_acceleration" -> zAcceleration = readAccelerationComponent(parser); + default -> parser.skipChildren(); + } + } + + return new PolarAcceleration( + requireField(magnitude, "acceleration_magnitude"), + requireField(direction, "acceleration_direction"), + zAcceleration); + } + + private AccelerationMagnitude readAccelerationMagnitude(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer value = null; + Integer confidence = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "value" -> value = parser.getIntValue(); + case "confidence" -> confidence = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new AccelerationMagnitude( + requireField(value, "value"), + requireField(confidence, "confidence")); + } + + private CartesianAcceleration readCartesianAcceleration(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + AccelerationComponent x = null; + AccelerationComponent y = null; + AccelerationComponent z = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "x_acceleration" -> x = readAccelerationComponent(parser); + case "y_acceleration" -> y = readAccelerationComponent(parser); + case "z_acceleration" -> z = readAccelerationComponent(parser); + default -> parser.skipChildren(); + } + } + + return new CartesianAcceleration( + requireField(x, "x_acceleration"), + requireField(y, "y_acceleration"), + z); + } + + private AccelerationComponent readAccelerationComponent(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer value = null; + Integer confidence = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "value" -> value = parser.getIntValue(); + case "confidence" -> confidence = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new AccelerationComponent( + requireField(value, "value"), + requireField(confidence, "confidence")); + } + + private EulerAngles readEulerAngles(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Angle zAngle = null; + Angle yAngle = null; + Angle xAngle = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "z_angle" -> zAngle = readAngle(parser); + case "y_angle" -> yAngle = readAngle(parser); + case "x_angle" -> xAngle = readAngle(parser); + default -> parser.skipChildren(); + } + } + + return new EulerAngles( + requireField(zAngle, "z_angle"), + yAngle, + xAngle); + } + + private CartesianAngularVelocityComponent readCartesianAngularVelocityComponent(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer value = null; + Integer confidence = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "value" -> value = parser.getIntValue(); + case "confidence" -> confidence = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new CartesianAngularVelocityComponent( + requireField(value, "value"), + requireField(confidence, "confidence")); + } + + private List readLowerTriangularCorrelationMatrices(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List list = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + list.add(readLowerTriangularCorrelationMatrix(parser)); + } + return list; + } + + private LowerTriangularCorrelationMatrix readLowerTriangularCorrelationMatrix(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + LowerTriangularMatrixComponents components = null; + List>> matrix = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "components_included_in_the_matrix" -> components = readLowerTriangularMatrixComponents(parser); + case "matrix" -> matrix = readMatrix(parser); + default -> parser.skipChildren(); + } + } + + return new LowerTriangularCorrelationMatrix( + requireField(components, "components_included_in_the_matrix"), + requireField(matrix, "matrix")); + } + + private LowerTriangularMatrixComponents readLowerTriangularMatrixComponents(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Boolean xPosition = null; + Boolean yPosition = null; + Boolean zPosition = null; + Boolean xVelocityOrVelocityMagnitude = null; + Boolean yVelocityOrVelocityDirection = null; + Boolean zSpeed = null; + Boolean xAccelOrAccelMagnitude = null; + Boolean yAccelOrAccelDirection = null; + Boolean zAcceleration = null; + Boolean zAngle = null; + Boolean yAngle = null; + Boolean xAngle = null; + Boolean zAngularVelocity = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "x_position" -> xPosition = parser.getBooleanValue(); + case "y_position" -> yPosition = parser.getBooleanValue(); + case "z_position" -> zPosition = parser.getBooleanValue(); + case "x_velocity_or_velocity_magnitude" -> xVelocityOrVelocityMagnitude = parser.getBooleanValue(); + case "y_velocity_or_velocity_direction" -> yVelocityOrVelocityDirection = parser.getBooleanValue(); + case "z_speed" -> zSpeed = parser.getBooleanValue(); + case "x_accel_or_accel_magnitude" -> xAccelOrAccelMagnitude = parser.getBooleanValue(); + case "y_accel_or_accel_direction" -> yAccelOrAccelDirection = parser.getBooleanValue(); + case "z_acceleration" -> zAcceleration = parser.getBooleanValue(); + case "z_angle" -> zAngle = parser.getBooleanValue(); + case "y_angle" -> yAngle = parser.getBooleanValue(); + case "x_angle" -> xAngle = parser.getBooleanValue(); + case "z_angular_velocity" -> zAngularVelocity = parser.getBooleanValue(); + default -> parser.skipChildren(); + } + } + + return new LowerTriangularMatrixComponents( + requireField(xPosition, "x_position"), + requireField(yPosition, "y_position"), + requireField(zPosition, "z_position"), + requireField(xVelocityOrVelocityMagnitude, "x_velocity_or_velocity_magnitude"), + requireField(yVelocityOrVelocityDirection, "y_velocity_or_velocity_direction"), + requireField(zSpeed, "z_speed"), + requireField(xAccelOrAccelMagnitude, "x_accel_or_accel_magnitude"), + requireField(yAccelOrAccelDirection, "y_accel_or_accel_direction"), + requireField(zAcceleration, "z_acceleration"), + requireField(zAngle, "z_angle"), + requireField(yAngle, "y_angle"), + requireField(xAngle, "x_angle"), + requireField(zAngularVelocity, "z_angular_velocity")); + } + + private List>> readMatrix(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List>> matrix = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + matrix.add(readMatrixColumn(parser)); + } + return matrix; + } + + private List> readMatrixColumn(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List> column = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + column.add(readMatrixRow(parser)); + } + return column; + } + + private List readMatrixRow(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List row = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + row.add(parser.getIntValue()); + } + return row; + } + + private ObjectDimension readObjectDimension(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer value = null; + Integer confidence = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "value" -> value = parser.getIntValue(); + case "confidence" -> confidence = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new ObjectDimension( + requireField(value, "value"), + requireField(confidence, "confidence")); + } + + private List readClassification(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List list = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + list.add(readObjectClassification(parser)); + } + return list; + } + + private ObjectClassification readObjectClassification(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + ObjectClass objectClass = null; + Integer confidence = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "object_class" -> objectClass = readObjectClass(parser); + case "confidence" -> confidence = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new ObjectClassification( + requireField(objectClass, "object_class"), + requireField(confidence, "confidence")); + } + + private ObjectClass readObjectClass(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer vehicle = null; + ObjectClassVru vru = null; + ObjectClassGroup group = null; + Integer other = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "vehicle" -> vehicle = parser.getIntValue(); + case "vru" -> vru = readObjectClassVru(parser); + case "group" -> group = readObjectClassGroup(parser); + case "other" -> other = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new ObjectClass(vehicle, vru, group, other); + } + + private ObjectClassVru readObjectClassVru(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer pedestrian = null; + Integer bicyclistAndLightVruVehicle = null; + Integer motorcylist = null; + Integer animal = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "pedestrian" -> pedestrian = parser.getIntValue(); + case "bicyclist_and_light_vru_vehicle" -> bicyclistAndLightVruVehicle = parser.getIntValue(); + case "motorcylist" -> motorcylist = parser.getIntValue(); + case "animal" -> animal = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new ObjectClassVru(pedestrian, bicyclistAndLightVruVehicle, motorcylist, animal); + } + + private ObjectClassGroup readObjectClassGroup(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Shape clusterBoundingBoxShape = null; + Integer clusterCardinalitySize = null; + Integer clusterId = null; + ClusterProfiles clusterProfiles = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "cluster_bounding_box_shape" -> clusterBoundingBoxShape = readShape(parser); + case "cluster_cardinality_size" -> clusterCardinalitySize = parser.getIntValue(); + case "cluster_id" -> clusterId = parser.getIntValue(); + case "cluster_profiles" -> clusterProfiles = readClusterProfiles(parser); + default -> parser.skipChildren(); + } + } + + return new ObjectClassGroup( + requireField(clusterBoundingBoxShape, "cluster_bounding_box_shape"), + requireField(clusterCardinalitySize, "cluster_cardinality_size"), + clusterId, + clusterProfiles); + } + + private ClusterProfiles readClusterProfiles(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Boolean pedestrian = null; + Boolean bicyclist = null; + Boolean motorcyclist = null; + Boolean animal = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "pedestrian" -> pedestrian = parser.getBooleanValue(); + case "bicyclist" -> bicyclist = parser.getBooleanValue(); + case "motorcyclist" -> motorcyclist = parser.getBooleanValue(); + case "animal" -> animal = parser.getBooleanValue(); + default -> parser.skipChildren(); + } + } + + return new ClusterProfiles( + requireField(pedestrian, "pedestrian"), + requireField(bicyclist, "bicyclist"), + requireField(motorcyclist, "motorcyclist"), + requireField(animal, "animal")); + } + + private MapPosition readMapPosition(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + MapReference mapReference = null; + Integer laneId = null; + Integer connectionId = null; + LongitudinalLanePosition longitudinalLanePosition = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "map_reference" -> mapReference = readMapReference(parser); + case "lane_id" -> laneId = parser.getIntValue(); + case "connection_id" -> connectionId = parser.getIntValue(); + case "longitudinal_lane_position" -> longitudinalLanePosition = readLongitudinalLanePosition(parser); + default -> parser.skipChildren(); + } + } + + return new MapPosition(mapReference, laneId, connectionId, longitudinalLanePosition); + } + + private LongitudinalLanePosition readLongitudinalLanePosition(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer value = null; + Integer confidence = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "value" -> value = parser.getIntValue(); + case "confidence" -> confidence = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new LongitudinalLanePosition( + requireField(value, "value"), + requireField(confidence, "confidence")); + } + + /* --------------------------------------------------------------------- */ + /* CDD helpers */ + /* --------------------------------------------------------------------- */ + + private Angle readAngle(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer value = null; + Integer confidence = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "value" -> value = parser.getIntValue(); + case "confidence" -> confidence = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new Angle( + requireField(value, "value"), + requireField(confidence, "confidence")); + } + + private Speed readSpeed(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer value = null; + Integer confidence = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "value" -> value = parser.getIntValue(); + case "confidence" -> confidence = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new Speed( + requireField(value, "value"), + requireField(confidence, "confidence")); + } + + private VelocityComponent readVelocityComponent(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer value = null; + Integer confidence = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "value" -> value = parser.getIntValue(); + case "confidence" -> confidence = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new VelocityComponent( + requireField(value, "value"), + requireField(confidence, "confidence")); + } + + private MapReference readMapReference(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + RoadSegment roadSegment = null; + Intersection intersection = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "road_segment" -> roadSegment = readRoadSegment(parser); + case "intersection" -> intersection = readIntersection(parser); + default -> parser.skipChildren(); + } + } + + return new MapReference(roadSegment, intersection); + } + + private RoadSegment readRoadSegment(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer id = null; + Integer region = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "id" -> id = parser.getIntValue(); + case "region" -> region = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new RoadSegment( + requireField(id, "id"), + region); + } + + private Intersection readIntersection(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer id = null; + Integer region = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "id" -> id = parser.getIntValue(); + case "region" -> region = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new Intersection( + requireField(id, "id"), + region); + } + + private Shape readShape(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Rectangular rectangular = null; + Circular circular = null; + Polygonal polygonal = null; + Elliptical elliptical = null; + Radial radial = null; + RadialShapes radialShapes = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "rectangular" -> rectangular = readRectangular(parser); + case "circular" -> circular = readCircular(parser); + case "polygonal" -> polygonal = readPolygonal(parser); + case "elliptical" -> elliptical = readElliptical(parser); + case "radial" -> radial = readRadial(parser); + case "radial_shapes" -> radialShapes = readRadialShapes(parser); + default -> parser.skipChildren(); + } + } + + return new Shape(rectangular, circular, polygonal, elliptical, radial, radialShapes); + } + + private Rectangular readRectangular(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + CartesianPosition3d centerPoint = null; + Integer semiLength = null; + Integer semiBreadth = null; + Integer orientation = null; + Integer height = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "center_point" -> centerPoint = readCartesianPosition3d(parser); + case "semi_length" -> semiLength = parser.getIntValue(); + case "semi_breadth" -> semiBreadth = parser.getIntValue(); + case "orientation" -> orientation = parser.getIntValue(); + case "height" -> height = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new Rectangular( + centerPoint, + requireField(semiLength, "semi_length"), + requireField(semiBreadth, "semi_breadth"), + orientation, + height); + } + + private Circular readCircular(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer radius = null; + CartesianPosition3d shapeReferencePoint = null; + Integer height = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "radius" -> radius = parser.getIntValue(); + case "shape_reference_point" -> shapeReferencePoint = readCartesianPosition3d(parser); + case "height" -> height = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new Circular( + requireField(radius, "radius"), + shapeReferencePoint, + height); + } + + private Polygonal readPolygonal(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + List polygon = null; + CartesianPosition3d shapeReferencePoint = null; + Integer height = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "polygon" -> polygon = readCartesianPosition3dArray(parser); + case "shape_reference_point" -> shapeReferencePoint = readCartesianPosition3d(parser); + case "height" -> height = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new Polygonal( + requireField(polygon, "polygon"), + shapeReferencePoint, + height); + } + + private Elliptical readElliptical(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer semiMajorAxisLength = null; + Integer semiMinorAxisLength = null; + CartesianPosition3d shapeReferencePoint = null; + Integer orientation = null; + Integer height = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "semi_major_axis_length" -> semiMajorAxisLength = parser.getIntValue(); + case "semi_minor_axis_length" -> semiMinorAxisLength = parser.getIntValue(); + case "shape_reference_point" -> shapeReferencePoint = readCartesianPosition3d(parser); + case "orientation" -> orientation = parser.getIntValue(); + case "height" -> height = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new Elliptical( + requireField(semiMajorAxisLength, "semi_major_axis_length"), + requireField(semiMinorAxisLength, "semi_minor_axis_length"), + shapeReferencePoint, + orientation, + height); + } + + private Radial readRadial(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer range = null; + Integer stationaryHorizontalOpeningAngleStart = null; + Integer stationaryHorizontalOpeningAngleEnd = null; + CartesianPosition3d shapeReferencePoint = null; + Integer verticalOpeningAngleStart = null; + Integer verticalOpeningAngleEnd = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "range" -> range = parser.getIntValue(); + case "stationary_horizontal_opening_angle_start" -> stationaryHorizontalOpeningAngleStart = parser.getIntValue(); + case "stationary_horizontal_opening_angle_end" -> stationaryHorizontalOpeningAngleEnd = parser.getIntValue(); + case "shape_reference_point" -> shapeReferencePoint = readCartesianPosition3d(parser); + case "vertical_opening_angle_start" -> verticalOpeningAngleStart = parser.getIntValue(); + case "vertical_opening_angle_end" -> verticalOpeningAngleEnd = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new Radial( + requireField(range, "range"), + requireField(stationaryHorizontalOpeningAngleStart, "stationary_horizontal_opening_angle_start"), + requireField(stationaryHorizontalOpeningAngleEnd, "stationary_horizontal_opening_angle_end"), + shapeReferencePoint, + verticalOpeningAngleStart, + verticalOpeningAngleEnd); + } + + private RadialShapes readRadialShapes(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer refPointId = null; + Integer xCoordinate = null; + Integer yCoordinate = null; + Integer zCoordinate = null; + List radialShapesList = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "ref_point_id" -> refPointId = parser.getIntValue(); + case "x_coordinate" -> xCoordinate = parser.getIntValue(); + case "y_coordinate" -> yCoordinate = parser.getIntValue(); + case "z_coordinate" -> zCoordinate = parser.getIntValue(); + case "radial_shapes_list" -> radialShapesList = readRadialList(parser); + default -> parser.skipChildren(); + } + } + + return new RadialShapes( + requireField(refPointId, "ref_point_id"), + requireField(xCoordinate, "x_coordinate"), + requireField(yCoordinate, "y_coordinate"), + zCoordinate, + requireField(radialShapesList, "radial_shapes_list")); + } + + private List readRadialList(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List list = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + list.add(readRadial(parser)); + } + return list; + } + + private List readCartesianPosition3dArray(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_ARRAY); + List list = new ArrayList<>(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + list.add(readCartesianPosition3d(parser)); + } + return list; + } + + private CartesianPosition3d readCartesianPosition3d(JsonParser parser) throws IOException { + expect(parser.getCurrentToken(), JsonToken.START_OBJECT); + Integer xCoordinate = null; + Integer yCoordinate = null; + Integer zCoordinate = null; + + while (parser.nextToken() != JsonToken.END_OBJECT) { + String field = parser.currentName(); + parser.nextToken(); + switch (field) { + case "x_coordinate" -> xCoordinate = parser.getIntValue(); + case "y_coordinate" -> yCoordinate = parser.getIntValue(); + case "z_coordinate" -> zCoordinate = parser.getIntValue(); + default -> parser.skipChildren(); + } + } + + return new CartesianPosition3d( + requireField(xCoordinate, "x_coordinate"), + requireField(yCoordinate, "y_coordinate"), + zCoordinate); + } + + private static T requireField(T value, String field) { + if (value == null) { + throw new CpmValidationException("Missing mandatory field: " + field); + } + return value; + } + + private static void expect(JsonToken actual, JsonToken expected) throws JsonParseException { + if (actual != expected) { + throw new JsonParseException(null, "Expected token " + expected + " but got " + actual); + } + } +} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/codec/CpmWriter211.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/codec/CpmWriter211.java new file mode 100644 index 000000000..db34a29b7 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/codec/CpmWriter211.java @@ -0,0 +1,800 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.codec; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import com.orange.iot3mobility.messages.cpm.v211.model.CpmEnvelope211; +import com.orange.iot3mobility.messages.cpm.v211.model.CpmMessage211; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.Angle; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.*; +import com.orange.iot3mobility.messages.cpm.v211.model.managementcontainer.ManagementContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.managementcontainer.MessageRateRange; +import com.orange.iot3mobility.messages.cpm.v211.model.managementcontainer.SegmentationInfo; +import com.orange.iot3mobility.messages.cpm.v211.model.originatingvehiclecontainer.OriginatingVehicleContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.originatingvehiclecontainer.TrailerData; +import com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer.*; +import com.orange.iot3mobility.messages.cpm.v211.model.perceptionregioncontainer.PerceptionRegion; +import com.orange.iot3mobility.messages.cpm.v211.model.perceptionregioncontainer.PerceptionRegionContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.sensorinformationcontainer.SensorInformation; +import com.orange.iot3mobility.messages.cpm.v211.model.sensorinformationcontainer.SensorInformationContainer; +import com.orange.iot3mobility.messages.cpm.v211.validation.CpmValidator211; +import com.orange.iot3mobility.messages.cpm.v211.model.originatingrsucontainer.OriginatingRsuContainer; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.Objects; + +/** + * Streaming JSON writer for CPM 2.1.1 + */ +public final class CpmWriter211 { + + private final JsonFactory jsonFactory; + + public CpmWriter211(JsonFactory jsonFactory) { + this.jsonFactory = Objects.requireNonNull(jsonFactory, "jsonFactory"); + } + + public void write(CpmEnvelope211 envelope, OutputStream out) throws IOException { + CpmValidator211.validateEnvelope(envelope); + + try (JsonGenerator gen = jsonFactory.createGenerator(out)) { + gen.writeStartObject(); + gen.writeStringField("message_type", envelope.messageType()); + gen.writeStringField("source_uuid", envelope.sourceUuid()); + gen.writeNumberField("timestamp", envelope.timestamp()); + gen.writeStringField("version", envelope.version()); + if (envelope.objectIdRotationCount() != null) { + gen.writeNumberField("object_id_rotation_count", envelope.objectIdRotationCount()); + } + gen.writeFieldName("message"); + writeMessage(gen, envelope.message()); + gen.writeEndObject(); + } + } + + private void writeMessage(JsonGenerator gen, CpmMessage211 message) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("protocol_version", message.protocolVersion()); + gen.writeNumberField("station_id", message.stationId()); + + gen.writeFieldName("management_container"); + writeManagementContainer(gen, message.managementContainer()); + + if (message.originatingVehicleContainer() != null) { + gen.writeFieldName("originating_vehicle_container"); + writeOriginatingVehicleContainer(gen, message.originatingVehicleContainer()); + } + if (message.originatingRsuContainer() != null) { + OriginatingRsuContainer rsuContainer = message.originatingRsuContainer(); + gen.writeArrayFieldStart("originating_rsu_container"); + for (MapReference reference : rsuContainer.mapReferences()) { + writeMapReference(gen, reference); + } + gen.writeEndArray(); + } + if (message.sensorInformationContainer() != null) { + SensorInformationContainer sensorContainer = message.sensorInformationContainer(); + gen.writeArrayFieldStart("sensor_information_container"); + for (SensorInformation info : sensorContainer.sensorInformation()) { + writeSensorInformation(gen, info); + } + gen.writeEndArray(); + } + if (message.perceptionRegionContainer() != null) { + PerceptionRegionContainer regionContainer = message.perceptionRegionContainer(); + gen.writeArrayFieldStart("perception_region_container"); + for (PerceptionRegion region : regionContainer.perceptionRegions()) { + writePerceptionRegion(gen, region); + } + gen.writeEndArray(); + } + if (message.perceivedObjectContainer() != null) { + PerceivedObjectContainer objectContainer = message.perceivedObjectContainer(); + gen.writeArrayFieldStart("perceived_object_container"); + for (PerceivedObject obj : objectContainer.perceivedObjects()) { + writePerceivedObject(gen, obj); + } + gen.writeEndArray(); + } + gen.writeEndObject(); + } + + /* --------------------------------------------------------------------- */ + /* Management container */ + /* --------------------------------------------------------------------- */ + + private void writeManagementContainer(JsonGenerator gen, ManagementContainer container) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("reference_time", container.referenceTime()); + gen.writeFieldName("reference_position"); + writeReferencePosition(gen, container.referencePosition()); + if (container.segmentationInfo() != null) { + gen.writeFieldName("segmentation_info"); + writeSegmentationInfo(gen, container.segmentationInfo()); + } + if (container.messageRateRange() != null) { + gen.writeFieldName("message_rate_range"); + writeMessageRateRange(gen, container.messageRateRange()); + } + gen.writeEndObject(); + } + + private void writeReferencePosition(JsonGenerator gen, ReferencePosition reference) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("latitude", reference.latitude()); + gen.writeNumberField("longitude", reference.longitude()); + gen.writeFieldName("position_confidence_ellipse"); + writePositionConfidenceEllipse(gen, reference.positionConfidenceEllipse()); + gen.writeFieldName("altitude"); + writeAltitude(gen, reference.altitude()); + gen.writeEndObject(); + } + + private void writePositionConfidenceEllipse(JsonGenerator gen, PositionConfidenceEllipse ellipse) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("semi_major", ellipse.semiMajor()); + gen.writeNumberField("semi_minor", ellipse.semiMinor()); + gen.writeNumberField("semi_major_orientation", ellipse.semiMajorOrientation()); + gen.writeEndObject(); + } + + private void writeAltitude(JsonGenerator gen, Altitude altitude) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("value", altitude.value()); + gen.writeNumberField("confidence", altitude.confidence()); + gen.writeEndObject(); + } + + private void writeSegmentationInfo(JsonGenerator gen, SegmentationInfo info) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("total_msg_no", info.totalMsgNo()); + gen.writeNumberField("this_msg_no", info.thisMsgNo()); + gen.writeEndObject(); + } + + private void writeMessageRateRange(JsonGenerator gen, MessageRateRange range) throws IOException { + gen.writeStartObject(); + gen.writeFieldName("message_rate_min"); + writeMessageRateHz(gen, range.messageRateMin()); + gen.writeFieldName("message_rate_max"); + writeMessageRateHz(gen, range.messageRateMax()); + gen.writeEndObject(); + } + + private void writeMessageRateHz(JsonGenerator gen, MessageRateHz rate) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("mantissa", rate.mantissa()); + gen.writeNumberField("exponent", rate.exponent()); + gen.writeEndObject(); + } + + /* --------------------------------------------------------------------- */ + /* Originating vehicle container */ + /* --------------------------------------------------------------------- */ + + private void writeOriginatingVehicleContainer(JsonGenerator gen, OriginatingVehicleContainer container) throws IOException { + gen.writeStartObject(); + gen.writeFieldName("orientation_angle"); + writeAngle(gen, container.orientationAngle()); + if (container.pitchAngle() != null) { + gen.writeFieldName("pitch_angle"); + writeAngle(gen, container.pitchAngle()); + } + if (container.rollAngle() != null) { + gen.writeFieldName("roll_angle"); + writeAngle(gen, container.rollAngle()); + } + if (container.trailerDataSet() != null) { + gen.writeArrayFieldStart("trailer_data_set"); + for (TrailerData trailer : container.trailerDataSet()) { + writeTrailerData(gen, trailer); + } + gen.writeEndArray(); + } + gen.writeEndObject(); + } + + private void writeTrailerData(JsonGenerator gen, TrailerData trailer) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("ref_point_id", trailer.refPointId()); + gen.writeNumberField("hitch_point_offset", trailer.hitchPointOffset()); + gen.writeFieldName("hitch_angle"); + writeAngle(gen, trailer.hitchAngle()); + if (trailer.frontOverhang() != null) { + gen.writeNumberField("front_overhang", trailer.frontOverhang()); + } + if (trailer.rearOverhang() != null) { + gen.writeNumberField("rear_overhang", trailer.rearOverhang()); + } + if (trailer.trailerWidth() != null) { + gen.writeNumberField("trailer_width", trailer.trailerWidth()); + } + gen.writeEndObject(); + } + + /* --------------------------------------------------------------------- */ + /* Sensor information container */ + /* --------------------------------------------------------------------- */ + + private void writeSensorInformation(JsonGenerator gen, SensorInformation info) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("sensor_id", info.sensorId()); + gen.writeNumberField("sensor_type", info.sensorType()); + if (info.perceptionRegionShape() != null) { + gen.writeFieldName("perception_region_shape"); + writeShape(gen, info.perceptionRegionShape()); + } + if (info.perceptionRegionConfidence() != null) { + gen.writeNumberField("perception_region_confidence", info.perceptionRegionConfidence()); + } + gen.writeBooleanField("shadowing_applies", info.shadowingApplies()); + gen.writeEndObject(); + } + + /* --------------------------------------------------------------------- */ + /* Perception region container */ + /* --------------------------------------------------------------------- */ + + private void writePerceptionRegion(JsonGenerator gen, PerceptionRegion region) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("measurement_delta_time", region.measurementDeltaTime()); + gen.writeNumberField("perception_region_confidence", region.perceptionRegionConfidence()); + gen.writeFieldName("perception_region_shape"); + writeShape(gen, region.perceptionRegionShape()); + gen.writeBooleanField("shadowing_applies", region.shadowingApplies()); + if (region.sensorIdList() != null) { + gen.writeArrayFieldStart("sensor_id_list"); + for (Integer id : region.sensorIdList()) { + gen.writeNumber(id); + } + gen.writeEndArray(); + } + if (region.perceivedObjectIds() != null) { + gen.writeArrayFieldStart("perceived_object_ids"); + for (Integer id : region.perceivedObjectIds()) { + gen.writeNumber(id); + } + gen.writeEndArray(); + } + gen.writeEndObject(); + } + + /* --------------------------------------------------------------------- */ + /* Perceived object container */ + /* --------------------------------------------------------------------- */ + + private void writePerceivedObject(JsonGenerator gen, PerceivedObject object) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("measurement_delta_time", object.measurementDeltaTime()); + gen.writeFieldName("position"); + writeCartesianPosition3dWithConfidence(gen, object.position()); + if (object.objectId() != null) { + gen.writeNumberField("object_id", object.objectId()); + } + if (object.velocity() != null) { + gen.writeFieldName("velocity"); + writeVelocity(gen, object.velocity()); + } + if (object.acceleration() != null) { + gen.writeFieldName("acceleration"); + writeAcceleration(gen, object.acceleration()); + } + if (object.angles() != null) { + gen.writeFieldName("angles"); + writeEulerAngles(gen, object.angles()); + } + if (object.zAngularVelocity() != null) { + gen.writeFieldName("z_angular_velocity"); + writeCartesianAngularVelocityComponent(gen, object.zAngularVelocity()); + } + if (object.lowerTriangularCorrelationMatrices() != null) { + gen.writeArrayFieldStart("lower_triangular_correlation_matrices"); + for (LowerTriangularCorrelationMatrix matrix : object.lowerTriangularCorrelationMatrices()) { + writeLowerTriangularCorrelationMatrix(gen, matrix); + } + gen.writeEndArray(); + } + if (object.objectDimensionZ() != null) { + gen.writeFieldName("object_dimension_z"); + writeObjectDimension(gen, object.objectDimensionZ()); + } + if (object.objectDimensionY() != null) { + gen.writeFieldName("object_dimension_y"); + writeObjectDimension(gen, object.objectDimensionY()); + } + if (object.objectDimensionX() != null) { + gen.writeFieldName("object_dimension_x"); + writeObjectDimension(gen, object.objectDimensionX()); + } + if (object.objectAge() != null) { + gen.writeNumberField("object_age", object.objectAge()); + } + if (object.objectPerceptionQuality() != null) { + gen.writeNumberField("object_perception_quality", object.objectPerceptionQuality()); + } + if (object.sensorIdList() != null) { + gen.writeArrayFieldStart("sensor_id_list"); + for (Integer id : object.sensorIdList()) { + gen.writeNumber(id); + } + gen.writeEndArray(); + } + if (object.classification() != null) { + gen.writeArrayFieldStart("classification"); + for (ObjectClassification classification : object.classification()) { + writeObjectClassification(gen, classification); + } + gen.writeEndArray(); + } + if (object.mapPosition() != null) { + gen.writeFieldName("map_position"); + writeMapPosition(gen, object.mapPosition()); + } + gen.writeEndObject(); + } + + private void writeCartesianPosition3dWithConfidence(JsonGenerator gen, CartesianPosition3dWithConfidence position) throws IOException { + gen.writeStartObject(); + gen.writeFieldName("x_coordinate"); + writeCartesianCoordinateWithConfidence(gen, position.xCoordinate()); + gen.writeFieldName("y_coordinate"); + writeCartesianCoordinateWithConfidence(gen, position.yCoordinate()); + if (position.zCoordinate() != null) { + gen.writeFieldName("z_coordinate"); + writeCartesianCoordinateWithConfidence(gen, position.zCoordinate()); + } + gen.writeEndObject(); + } + + private void writeCartesianCoordinateWithConfidence(JsonGenerator gen, CartesianCoordinateWithConfidence coordinate) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("value", coordinate.value()); + gen.writeNumberField("confidence", coordinate.confidence()); + gen.writeEndObject(); + } + + private void writeVelocity(JsonGenerator gen, Velocity velocity) throws IOException { + gen.writeStartObject(); + if (velocity.polarVelocity() != null) { + gen.writeFieldName("polar_velocity"); + writePolarVelocity(gen, velocity.polarVelocity()); + } else if (velocity.cartesianVelocity() != null) { + gen.writeFieldName("cartesian_velocity"); + writeCartesianVelocity(gen, velocity.cartesianVelocity()); + } + gen.writeEndObject(); + } + + private void writePolarVelocity(JsonGenerator gen, PolarVelocity velocity) throws IOException { + gen.writeStartObject(); + gen.writeFieldName("velocity_magnitude"); + writeSpeed(gen, velocity.velocityMagnitude()); + gen.writeFieldName("velocity_direction"); + writeAngle(gen, velocity.velocityDirection()); + if (velocity.zVelocity() != null) { + gen.writeFieldName("z_velocity"); + writeVelocityComponent(gen, velocity.zVelocity()); + } + gen.writeEndObject(); + } + + private void writeCartesianVelocity(JsonGenerator gen, CartesianVelocity velocity) throws IOException { + gen.writeStartObject(); + gen.writeFieldName("x_velocity"); + writeVelocityComponent(gen, velocity.xVelocity()); + gen.writeFieldName("y_velocity"); + writeVelocityComponent(gen, velocity.yVelocity()); + if (velocity.zVelocity() != null) { + gen.writeFieldName("z_velocity"); + writeVelocityComponent(gen, velocity.zVelocity()); + } + gen.writeEndObject(); + } + + private void writeAcceleration(JsonGenerator gen, Acceleration acceleration) throws IOException { + gen.writeStartObject(); + if (acceleration.polarAcceleration() != null) { + gen.writeFieldName("polar_acceleration"); + writePolarAcceleration(gen, acceleration.polarAcceleration()); + } else if (acceleration.cartesianAcceleration() != null) { + gen.writeFieldName("cartesian_acceleration"); + writeCartesianAcceleration(gen, acceleration.cartesianAcceleration()); + } + gen.writeEndObject(); + } + + private void writePolarAcceleration(JsonGenerator gen, PolarAcceleration acceleration) throws IOException { + gen.writeStartObject(); + gen.writeFieldName("acceleration_magnitude"); + writeAccelerationMagnitude(gen, acceleration.accelerationMagnitude()); + gen.writeFieldName("acceleration_direction"); + writeAngle(gen, acceleration.accelerationDirection()); + if (acceleration.zAcceleration() != null) { + gen.writeFieldName("z_acceleration"); + writeAccelerationComponent(gen, acceleration.zAcceleration()); + } + gen.writeEndObject(); + } + + private void writeAccelerationMagnitude(JsonGenerator gen, AccelerationMagnitude magnitude) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("value", magnitude.value()); + gen.writeNumberField("confidence", magnitude.confidence()); + gen.writeEndObject(); + } + + private void writeCartesianAcceleration(JsonGenerator gen, CartesianAcceleration acceleration) throws IOException { + gen.writeStartObject(); + gen.writeFieldName("x_acceleration"); + writeAccelerationComponent(gen, acceleration.xAcceleration()); + gen.writeFieldName("y_acceleration"); + writeAccelerationComponent(gen, acceleration.yAcceleration()); + if (acceleration.zAcceleration() != null) { + gen.writeFieldName("z_acceleration"); + writeAccelerationComponent(gen, acceleration.zAcceleration()); + } + gen.writeEndObject(); + } + + private void writeAccelerationComponent(JsonGenerator gen, AccelerationComponent component) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("value", component.value()); + gen.writeNumberField("confidence", component.confidence()); + gen.writeEndObject(); + } + + private void writeEulerAngles(JsonGenerator gen, EulerAngles angles) throws IOException { + gen.writeStartObject(); + gen.writeFieldName("z_angle"); + writeAngle(gen, angles.zAngle()); + if (angles.yAngle() != null) { + gen.writeFieldName("y_angle"); + writeAngle(gen, angles.yAngle()); + } + if (angles.xAngle() != null) { + gen.writeFieldName("x_angle"); + writeAngle(gen, angles.xAngle()); + } + gen.writeEndObject(); + } + + private void writeCartesianAngularVelocityComponent(JsonGenerator gen, CartesianAngularVelocityComponent component) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("value", component.value()); + gen.writeNumberField("confidence", component.confidence()); + gen.writeEndObject(); + } + + private void writeLowerTriangularCorrelationMatrix(JsonGenerator gen, LowerTriangularCorrelationMatrix matrix) throws IOException { + gen.writeStartObject(); + gen.writeFieldName("components_included_in_the_matrix"); + writeLowerTriangularMatrixComponents(gen, matrix.componentsIncludedInTheMatrix()); + gen.writeFieldName("matrix"); + writeMatrix(gen, matrix.matrix()); + gen.writeEndObject(); + } + + private void writeLowerTriangularMatrixComponents(JsonGenerator gen, LowerTriangularMatrixComponents components) throws IOException { + gen.writeStartObject(); + gen.writeBooleanField("x_position", components.xPosition()); + gen.writeBooleanField("y_position", components.yPosition()); + gen.writeBooleanField("z_position", components.zPosition()); + gen.writeBooleanField("x_velocity_or_velocity_magnitude", components.xVelocityOrVelocityMagnitude()); + gen.writeBooleanField("y_velocity_or_velocity_direction", components.yVelocityOrVelocityDirection()); + gen.writeBooleanField("z_speed", components.zSpeed()); + gen.writeBooleanField("x_accel_or_accel_magnitude", components.xAccelOrAccelMagnitude()); + gen.writeBooleanField("y_accel_or_accel_direction", components.yAccelOrAccelDirection()); + gen.writeBooleanField("z_acceleration", components.zAcceleration()); + gen.writeBooleanField("z_angle", components.zAngle()); + gen.writeBooleanField("y_angle", components.yAngle()); + gen.writeBooleanField("x_angle", components.xAngle()); + gen.writeBooleanField("z_angular_velocity", components.zAngularVelocity()); + gen.writeEndObject(); + } + + private void writeMatrix(JsonGenerator gen, List>> matrix) throws IOException { + gen.writeStartArray(); + for (List> column : matrix) { + gen.writeStartArray(); + for (List row : column) { + gen.writeStartArray(); + for (Integer value : row) { + gen.writeNumber(value); + } + gen.writeEndArray(); + } + gen.writeEndArray(); + } + gen.writeEndArray(); + } + + private void writeObjectDimension(JsonGenerator gen, ObjectDimension dimension) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("value", dimension.value()); + gen.writeNumberField("confidence", dimension.confidence()); + gen.writeEndObject(); + } + + private void writeObjectClassification(JsonGenerator gen, ObjectClassification classification) throws IOException { + gen.writeStartObject(); + gen.writeFieldName("object_class"); + writeObjectClass(gen, classification.objectClass()); + gen.writeNumberField("confidence", classification.confidence()); + gen.writeEndObject(); + } + + private void writeObjectClass(JsonGenerator gen, ObjectClass objectClass) throws IOException { + gen.writeStartObject(); + if (objectClass.vehicle() != null) { + gen.writeNumberField("vehicle", objectClass.vehicle()); + } else if (objectClass.vru() != null) { + gen.writeFieldName("vru"); + writeObjectClassVru(gen, objectClass.vru()); + } else if (objectClass.group() != null) { + gen.writeFieldName("group"); + writeObjectClassGroup(gen, objectClass.group()); + } else if (objectClass.other() != null) { + gen.writeNumberField("other", objectClass.other()); + } + gen.writeEndObject(); + } + + private void writeObjectClassVru(JsonGenerator gen, ObjectClassVru vru) throws IOException { + gen.writeStartObject(); + if (vru.pedestrian() != null) { + gen.writeNumberField("pedestrian", vru.pedestrian()); + } else if (vru.bicyclistAndLightVruVehicle() != null) { + gen.writeNumberField("bicyclist_and_light_vru_vehicle", vru.bicyclistAndLightVruVehicle()); + } else if (vru.motorcylist() != null) { + gen.writeNumberField("motorcylist", vru.motorcylist()); + } else if (vru.animal() != null) { + gen.writeNumberField("animal", vru.animal()); + } + gen.writeEndObject(); + } + + private void writeObjectClassGroup(JsonGenerator gen, ObjectClassGroup group) throws IOException { + gen.writeStartObject(); + gen.writeFieldName("cluster_bounding_box_shape"); + writeShape(gen, group.clusterBoundingBoxShape()); + gen.writeNumberField("cluster_cardinality_size", group.clusterCardinalitySize()); + if (group.clusterId() != null) { + gen.writeNumberField("cluster_id", group.clusterId()); + } + if (group.clusterProfiles() != null) { + gen.writeFieldName("cluster_profiles"); + writeClusterProfiles(gen, group.clusterProfiles()); + } + gen.writeEndObject(); + } + + private void writeClusterProfiles(JsonGenerator gen, ClusterProfiles profiles) throws IOException { + gen.writeStartObject(); + gen.writeBooleanField("pedestrian", profiles.pedestrian()); + gen.writeBooleanField("bicyclist", profiles.bicyclist()); + gen.writeBooleanField("motorcyclist", profiles.motorcyclist()); + gen.writeBooleanField("animal", profiles.animal()); + gen.writeEndObject(); + } + + private void writeMapPosition(JsonGenerator gen, MapPosition mapPosition) throws IOException { + gen.writeStartObject(); + if (mapPosition.mapReference() != null) { + gen.writeFieldName("map_reference"); + writeMapReference(gen, mapPosition.mapReference()); + } + if (mapPosition.laneId() != null) { + gen.writeNumberField("lane_id", mapPosition.laneId()); + } + if (mapPosition.connectionId() != null) { + gen.writeNumberField("connection_id", mapPosition.connectionId()); + } + if (mapPosition.longitudinalLanePosition() != null) { + gen.writeFieldName("longitudinal_lane_position"); + writeLongitudinalLanePosition(gen, mapPosition.longitudinalLanePosition()); + } + gen.writeEndObject(); + } + + private void writeLongitudinalLanePosition(JsonGenerator gen, LongitudinalLanePosition position) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("value", position.value()); + gen.writeNumberField("confidence", position.confidence()); + gen.writeEndObject(); + } + + /* --------------------------------------------------------------------- */ + /* CDD helpers */ + /* --------------------------------------------------------------------- */ + + private void writeAngle(JsonGenerator gen, Angle angle) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("value", angle.value()); + gen.writeNumberField("confidence", angle.confidence()); + gen.writeEndObject(); + } + + private void writeSpeed(JsonGenerator gen, Speed speed) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("value", speed.value()); + gen.writeNumberField("confidence", speed.confidence()); + gen.writeEndObject(); + } + + private void writeVelocityComponent(JsonGenerator gen, VelocityComponent component) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("value", component.value()); + gen.writeNumberField("confidence", component.confidence()); + gen.writeEndObject(); + } + + private void writeMapReference(JsonGenerator gen, MapReference reference) throws IOException { + gen.writeStartObject(); + if (reference.roadSegment() != null) { + gen.writeFieldName("road_segment"); + writeRoadSegment(gen, reference.roadSegment()); + } else if (reference.intersection() != null) { + gen.writeFieldName("intersection"); + writeIntersection(gen, reference.intersection()); + } + gen.writeEndObject(); + } + + private void writeRoadSegment(JsonGenerator gen, RoadSegment segment) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("id", segment.id()); + if (segment.region() != null) { + gen.writeNumberField("region", segment.region()); + } + gen.writeEndObject(); + } + + private void writeIntersection(JsonGenerator gen, Intersection intersection) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("id", intersection.id()); + if (intersection.region() != null) { + gen.writeNumberField("region", intersection.region()); + } + gen.writeEndObject(); + } + + private void writeShape(JsonGenerator gen, Shape shape) throws IOException { + gen.writeStartObject(); + if (shape.rectangular() != null) { + gen.writeFieldName("rectangular"); + writeRectangular(gen, shape.rectangular()); + } else if (shape.circular() != null) { + gen.writeFieldName("circular"); + writeCircular(gen, shape.circular()); + } else if (shape.polygonal() != null) { + gen.writeFieldName("polygonal"); + writePolygonal(gen, shape.polygonal()); + } else if (shape.elliptical() != null) { + gen.writeFieldName("elliptical"); + writeElliptical(gen, shape.elliptical()); + } else if (shape.radial() != null) { + gen.writeFieldName("radial"); + writeRadial(gen, shape.radial()); + } else if (shape.radialShapes() != null) { + gen.writeFieldName("radial_shapes"); + writeRadialShapes(gen, shape.radialShapes()); + } + gen.writeEndObject(); + } + + private void writeRectangular(JsonGenerator gen, Rectangular rectangular) throws IOException { + gen.writeStartObject(); + if (rectangular.centerPoint() != null) { + gen.writeFieldName("center_point"); + writeCartesianPosition3d(gen, rectangular.centerPoint()); + } + gen.writeNumberField("semi_length", rectangular.semiLength()); + gen.writeNumberField("semi_breadth", rectangular.semiBreadth()); + if (rectangular.orientation() != null) { + gen.writeNumberField("orientation", rectangular.orientation()); + } + if (rectangular.height() != null) { + gen.writeNumberField("height", rectangular.height()); + } + gen.writeEndObject(); + } + + private void writeCircular(JsonGenerator gen, Circular circular) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("radius", circular.radius()); + if (circular.shapeReferencePoint() != null) { + gen.writeFieldName("shape_reference_point"); + writeCartesianPosition3d(gen, circular.shapeReferencePoint()); + } + if (circular.height() != null) { + gen.writeNumberField("height", circular.height()); + } + gen.writeEndObject(); + } + + private void writePolygonal(JsonGenerator gen, Polygonal polygonal) throws IOException { + gen.writeStartObject(); + gen.writeArrayFieldStart("polygon"); + for (CartesianPosition3d position : polygonal.polygon()) { + writeCartesianPosition3d(gen, position); + } + gen.writeEndArray(); + if (polygonal.shapeReferencePoint() != null) { + gen.writeFieldName("shape_reference_point"); + writeCartesianPosition3d(gen, polygonal.shapeReferencePoint()); + } + if (polygonal.height() != null) { + gen.writeNumberField("height", polygonal.height()); + } + gen.writeEndObject(); + } + + private void writeElliptical(JsonGenerator gen, Elliptical elliptical) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("semi_major_axis_length", elliptical.semiMajorAxisLength()); + gen.writeNumberField("semi_minor_axis_length", elliptical.semiMinorAxisLength()); + if (elliptical.shapeReferencePoint() != null) { + gen.writeFieldName("shape_reference_point"); + writeCartesianPosition3d(gen, elliptical.shapeReferencePoint()); + } + if (elliptical.orientation() != null) { + gen.writeNumberField("orientation", elliptical.orientation()); + } + if (elliptical.height() != null) { + gen.writeNumberField("height", elliptical.height()); + } + gen.writeEndObject(); + } + + private void writeRadial(JsonGenerator gen, Radial radial) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("range", radial.range()); + gen.writeNumberField("stationary_horizontal_opening_angle_start", radial.stationaryHorizontalOpeningAngleStart()); + gen.writeNumberField("stationary_horizontal_opening_angle_end", radial.stationaryHorizontalOpeningAngleEnd()); + if (radial.shapeReferencePoint() != null) { + gen.writeFieldName("shape_reference_point"); + writeCartesianPosition3d(gen, radial.shapeReferencePoint()); + } + if (radial.verticalOpeningAngleStart() != null) { + gen.writeNumberField("vertical_opening_angle_start", radial.verticalOpeningAngleStart()); + } + if (radial.verticalOpeningAngleEnd() != null) { + gen.writeNumberField("vertical_opening_angle_end", radial.verticalOpeningAngleEnd()); + } + gen.writeEndObject(); + } + + private void writeRadialShapes(JsonGenerator gen, RadialShapes radialShapes) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("ref_point_id", radialShapes.refPointId()); + gen.writeNumberField("x_coordinate", radialShapes.xCoordinate()); + gen.writeNumberField("y_coordinate", radialShapes.yCoordinate()); + if (radialShapes.zCoordinate() != null) { + gen.writeNumberField("z_coordinate", radialShapes.zCoordinate()); + } + gen.writeArrayFieldStart("radial_shapes_list"); + for (Radial radial : radialShapes.radialShapesList()) { + writeRadial(gen, radial); + } + gen.writeEndArray(); + gen.writeEndObject(); + } + + private void writeCartesianPosition3d(JsonGenerator gen, CartesianPosition3d position) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("x_coordinate", position.xCoordinate()); + gen.writeNumberField("y_coordinate", position.yCoordinate()); + if (position.zCoordinate() != null) { + gen.writeNumberField("z_coordinate", position.zCoordinate()); + } + gen.writeEndObject(); + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/CpmEnvelope211.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/CpmEnvelope211.java new file mode 100644 index 000000000..d430c964b --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/CpmEnvelope211.java @@ -0,0 +1,101 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model; + +/** + * CpmEnvelope211 - base class to build a JSON CPM v2.1.1 with its header + *

+ * This 2.1.1 version corresponds to the following ETSI references: + *

    + *
  • CPM TS 103 324 - version 2.1.1
  • + *
  • CDD TS 102 894-2 - version 2.1.1
  • + *
+ * + * @param messageType Type of the message carried in message property (cpm). + * @param sourceUuid Unique id for the message sender. + * @param timestamp Timestamp when the message was generated since Unix Epoch (millisecond). Range: + * 1514764800000..1830297600000. + * @param version JSON message format version (2.1.1). + * @param objectIdRotationCount Optional object ID rotation count (0..255). + * @param message {@link CpmMessage211} + */ +public record CpmEnvelope211( + String messageType, + String sourceUuid, + long timestamp, + String version, + Integer objectIdRotationCount, + CpmMessage211 message) { + + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for CpmEnvelope211. + *

+ * Mandatory fields: + *

    + *
  • messageType - hardcoded (cpm)
  • + *
  • sourceUuid
  • + *
  • timestamp
  • + *
  • version - hardcoded (2.1.1)
  • + *
  • message
  • + *
+ */ + public static final class Builder { + private final String messageType; + private String sourceUuid; + private Long timestamp; + private final String version; + private Integer objectIdRotationCount; + private CpmMessage211 message; + + private Builder() { + this.messageType = "cpm"; + this.version = "2.1.1"; + } + + public Builder sourceUuid(String sourceUuid) { + this.sourceUuid = sourceUuid; + return this; + } + + public Builder timestamp(long timestamp) { + this.timestamp = timestamp; + return this; + } + + public Builder message(CpmMessage211 message) { + this.message = message; + return this; + } + + public Builder objectIdRotationCount(Integer objectIdRotationCount) { + this.objectIdRotationCount = objectIdRotationCount; + return this; + } + + public CpmEnvelope211 build() { + return new CpmEnvelope211( + requireNonNull(messageType, "message_type"), + requireNonNull(sourceUuid, "source_uuid"), + requireNonNull(timestamp, "timestamp"), + requireNonNull(version, "version"), + objectIdRotationCount, + requireNonNull(message, "message")); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/CpmMessage211.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/CpmMessage211.java new file mode 100644 index 000000000..3abd98cbe --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/CpmMessage211.java @@ -0,0 +1,108 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model; + +import com.orange.iot3mobility.messages.cpm.v211.model.managementcontainer.ManagementContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.originatingvehiclecontainer.OriginatingVehicleContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.originatingrsucontainer.OriginatingRsuContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer.PerceivedObjectContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.perceptionregioncontainer.PerceptionRegionContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.sensorinformationcontainer.SensorInformationContainer; + +/** + * CPM v2.1.1 + * + * @param protocolVersion Version of the ITS message (0..255). + * @param stationId Identifier for an ITS-S (0..4294967295). + * @param managementContainer {@link ManagementContainer} + * @param originatingVehicleContainer Optional {@link OriginatingVehicleContainer} + * @param originatingRsuContainer Optional {@link OriginatingRsuContainer} + * @param sensorInformationContainer Optional {@link SensorInformationContainer} + * @param perceptionRegionContainer Optional {@link PerceptionRegionContainer} + * @param perceivedObjectContainer Optional {@link PerceivedObjectContainer} + */ +public record CpmMessage211( + int protocolVersion, + long stationId, + ManagementContainer managementContainer, + OriginatingVehicleContainer originatingVehicleContainer, + OriginatingRsuContainer originatingRsuContainer, + SensorInformationContainer sensorInformationContainer, + PerceptionRegionContainer perceptionRegionContainer, + PerceivedObjectContainer perceivedObjectContainer) { + + public static Builder builder() { return new Builder(); } + + public static final class Builder { + private Integer protocolVersion; + private Long stationId; + private ManagementContainer managementContainer; + private OriginatingVehicleContainer originatingVehicleContainer; + private OriginatingRsuContainer originatingRsuContainer; + private SensorInformationContainer sensorInformationContainer; + private PerceptionRegionContainer perceptionRegionContainer; + private PerceivedObjectContainer perceivedObjectContainer; + + public Builder protocolVersion(int protocolVersion) { + this.protocolVersion = protocolVersion; + return this; + } + + public Builder stationId(long stationId) { + this.stationId = stationId; + return this; + } + + public Builder managementContainer(ManagementContainer managementContainer) { + this.managementContainer = managementContainer; + return this; + } + + public Builder originatingVehicleContainer(OriginatingVehicleContainer originatingVehicleContainer) { + this.originatingVehicleContainer = originatingVehicleContainer; + return this; + } + + public Builder originatingRsuContainer(OriginatingRsuContainer originatingRsuContainer) { + this.originatingRsuContainer = originatingRsuContainer; + return this; + } + + public Builder sensorInformationContainer(SensorInformationContainer sensorInformationContainer) { + this.sensorInformationContainer = sensorInformationContainer; + return this; + } + + public Builder perceptionRegionContainer(PerceptionRegionContainer perceptionRegionContainer) { + this.perceptionRegionContainer = perceptionRegionContainer; + return this; + } + + public Builder perceivedObjectContainer(PerceivedObjectContainer perceivedObjectContainer) { + this.perceivedObjectContainer = perceivedObjectContainer; + return this; + } + + public CpmMessage211 build() { + return new CpmMessage211( + requireNonNull(protocolVersion, "protocol_version"), + requireNonNull(stationId, "station_id"), + requireNonNull(managementContainer, "management_container"), + originatingVehicleContainer, + originatingRsuContainer, + sensorInformationContainer, + perceptionRegionContainer, + perceivedObjectContainer); + } + + private static T requireNonNull(T value, String field) { + if (value == null) throw new IllegalStateException("Missing field: " + field); + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/AccelerationComponent.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/AccelerationComponent.java new file mode 100644 index 000000000..fe581de74 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/AccelerationComponent.java @@ -0,0 +1,17 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Acceleration component along with a confidence value. + * + * @param value Acceleration component value. Unit: 0,1 m/s2. negativeOutOfRange (-160), positiveOutOfRange (160), + * unavailable (161). + * @param confidence Acceleration confidence. Unit: 0,1 m/s2. outOfRange (101), unavailable (102). + */ +public record AccelerationComponent(int value, int confidence) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Altitude.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Altitude.java new file mode 100644 index 000000000..4c5dfe041 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Altitude.java @@ -0,0 +1,22 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Altitude + *

+ * Altitude and an altitude accuracy of the geographical point. + * + * @param value Altitude of a geographical point. Unit: 0,01 metre. negativeOutOfRange (-100000), + * positiveOutOfRange (800000), unavailable (800001) + * @param confidence Confidence level of the altitudeValue. alt-000-01 (0), alt-000-02 (1), alt-000-05 (2), + * alt-000-10 (3), alt-000-20 (4), alt-000-50 (5), alt-001-00 (6), alt-002-00 (7), alt-005-00 (8), + * alt-010-00 (9), alt-020-00 (10), alt-050-00 (11), alt-100-00 (12), alt-200-00 (13), + * outOfRange (14), unavailable (15) + */ +public record Altitude(int value, int confidence) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Angle.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Angle.java new file mode 100644 index 000000000..388f200f3 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Angle.java @@ -0,0 +1,19 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Angle with confidence. + *

+ * Angle value expressed in WGS84 with a confidence level. + * + * @param value Angle value in WGS84. Unit: 0,1 degrees. wgs84North (0), wgs84East (900), wgs84South (1800), + * wgs84West (2700), doNotUse (3600), unavailable (3601). + * @param confidence Angle confidence. Unit: 0,1 degrees. outOfRange (126), unavailable (127). + */ +public record Angle(int value, int confidence) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/CartesianAngularVelocityComponent.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/CartesianAngularVelocityComponent.java new file mode 100644 index 000000000..226bd26ec --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/CartesianAngularVelocityComponent.java @@ -0,0 +1,18 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Angular velocity component along with a confidence value in a cartesian coordinate system. + * + * @param value Angular velocity component. Unit: degree/s. negativeOutOfRange (-255), positiveOutOfRange (255), + * unavailable (256). + * @param confidence Angular speed confidence. degSec-01 (0), degSec-02 (1), degSec-05 (2), degSec-10 (3), + * degSec-20 (4), degSec-50 (5), outOfRange (6), unavailable (7). + */ +public record CartesianAngularVelocityComponent(int value, int confidence) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/CartesianCoordinateWithConfidence.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/CartesianCoordinateWithConfidence.java new file mode 100644 index 000000000..541be5a68 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/CartesianCoordinateWithConfidence.java @@ -0,0 +1,17 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Coordinate along with a confidence value in a cartesian reference system. + * + * @param value Coordinate value (mean of the distribution). Unit: 0,01 m. negativeOutOfRange (-131072), + * positiveOutOfRange (131071). + * @param confidence Coordinate confidence. Unit: 0,01 m. outOfRange (4095), unavailable (4096). + */ +public record CartesianCoordinateWithConfidence(int value, int confidence) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/CartesianPosition3d.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/CartesianPosition3d.java new file mode 100644 index 000000000..98d90ecf3 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/CartesianPosition3d.java @@ -0,0 +1,17 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Position in a two or three dimensional cartesian coordinate system. + * + * @param xCoordinate X coordinate. Unit: 0,01 m. negativeOutOfRange (-32768), positiveOutOfRange (32767). + * @param yCoordinate Y coordinate. Unit: 0,01 m. negativeOutOfRange (-32768), positiveOutOfRange (32767). + * @param zCoordinate Optional. Z coordinate. Unit: 0,01 m. negativeOutOfRange (-32768), positiveOutOfRange (32767). + */ +public record CartesianPosition3d(int xCoordinate, int yCoordinate, Integer zCoordinate) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/CartesianPosition3dWithConfidence.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/CartesianPosition3dWithConfidence.java new file mode 100644 index 000000000..259dbe414 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/CartesianPosition3dWithConfidence.java @@ -0,0 +1,20 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Position in a two or three-dimensional cartesian coordinate system with confidence. + * + * @param xCoordinate {@link CartesianCoordinateWithConfidence} for the x-axis. + * @param yCoordinate {@link CartesianCoordinateWithConfidence} for the y-axis. + * @param zCoordinate Optional. {@link CartesianCoordinateWithConfidence} for the z-axis. + */ +public record CartesianPosition3dWithConfidence( + CartesianCoordinateWithConfidence xCoordinate, + CartesianCoordinateWithConfidence yCoordinate, + CartesianCoordinateWithConfidence zCoordinate) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Circular.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Circular.java new file mode 100644 index 000000000..b8513d11a --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Circular.java @@ -0,0 +1,21 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Circular shape definition. + * + * @param radius Radius of the circular area (0..4095). + * @param shapeReferencePoint Optional {@link CartesianPosition3d} reference point. + * @param height Optional height for a right circular cylinder (0..4095). + */ +public record Circular( + int radius, + CartesianPosition3d shapeReferencePoint, + Integer height) {} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Elliptical.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Elliptical.java new file mode 100644 index 000000000..5dd371390 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Elliptical.java @@ -0,0 +1,24 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Elliptical shape definition. + * + * @param semiMajorAxisLength Half length of the major axis (0..4095). + * @param semiMinorAxisLength Half length of the minor axis (0..4095). + * @param shapeReferencePoint Optional {@link CartesianPosition3d} reference point. + * @param orientation Optional. Orientation in WGS84. Unit: 0,1 degrees (0..3601). + * @param height Optional height for a right elliptical cylinder (0..4095). + */ +public record Elliptical( + int semiMajorAxisLength, + int semiMinorAxisLength, + CartesianPosition3d shapeReferencePoint, + Integer orientation, + Integer height) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Intersection.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Intersection.java new file mode 100644 index 000000000..ad3a7363b --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Intersection.java @@ -0,0 +1,17 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Intersection reference in a MAPEM. + * + * @param id Intersection identifier. + * @param region Optional region identifier of the responsible entity. + */ +public record Intersection(int id, Integer region) {} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/LongitudinalLanePosition.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/LongitudinalLanePosition.java new file mode 100644 index 000000000..012ed04ea --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/LongitudinalLanePosition.java @@ -0,0 +1,17 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Longitudinal lane position. + * + * @param value Longitudinal offset along the matched lane. Unit: 0,1 m. outOfRange (32766), unavailable (32767). + * @param confidence Longitudinal position confidence. Unit: 0,1 m. outOfRange (1022), unavailable (1023). + */ +public record LongitudinalLanePosition(int value, int confidence) {} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/MapReference.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/MapReference.java new file mode 100644 index 000000000..4190efdde --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/MapReference.java @@ -0,0 +1,54 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Identifies the MAPEM containing the topology information reference. + *

+ * Exactly one of {@link RoadSegment} or {@link Intersection} shall be provided. + * + * @param roadSegment Road segment reference. + * @param intersection Intersection reference. + */ +public record MapReference(RoadSegment roadSegment, Intersection intersection) { + + public static MapReference roadSegment(RoadSegment roadSegment) { + return new MapReference(roadSegment, null); + } + + public static MapReference intersection(Intersection intersection) { + return new MapReference(null, intersection); + } + + public static Builder builder() { return new Builder(); } + + public static final class Builder { + private RoadSegment roadSegment; + private Intersection intersection; + + public Builder roadSegment(RoadSegment roadSegment) { + this.roadSegment = roadSegment; + return this; + } + + public Builder intersection(Intersection intersection) { + this.intersection = intersection; + return this; + } + + public MapReference build() { + int count = 0; + if (roadSegment != null) count++; + if (intersection != null) count++; + if (count != 1) { + throw new IllegalStateException("MapReference must contain exactly one non-null field"); + } + return new MapReference(roadSegment, intersection); + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/MessageRateHz.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/MessageRateHz.java new file mode 100644 index 000000000..dbacd21c9 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/MessageRateHz.java @@ -0,0 +1,16 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Message rate in Hz expressed as mantissa * 10^exponent. + * + * @param mantissa Mantissa of the rate (1..100). + * @param exponent Exponent of the rate (-5..2). + */ +public record MessageRateHz(int mantissa, int exponent) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/ObjectDimension.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/ObjectDimension.java new file mode 100644 index 000000000..357aa6ed6 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/ObjectDimension.java @@ -0,0 +1,16 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Dimension of an object together with a confidence value. + * + * @param value Object dimension value. Unit: 0,1 m. outOfRange (255), unavailable (256). + * @param confidence Dimension confidence. Unit: 0,1 m. outOfRange (31), unavailable (32). + */ +public record ObjectDimension(int value, int confidence) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Polygonal.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Polygonal.java new file mode 100644 index 000000000..e6c3775a3 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Polygonal.java @@ -0,0 +1,23 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +import java.util.List; + +/** + * Polygonal shape definition. + * + * @param polygon List of {@link CartesianPosition3d} relative positions defining the polygon. + * @param shapeReferencePoint Optional {@link CartesianPosition3d} reference point. + * @param height Optional height for a right prism (0..4095). + */ +public record Polygonal( + List polygon, + CartesianPosition3d shapeReferencePoint, + Integer height) {} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/PositionConfidenceEllipse.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/PositionConfidenceEllipse.java new file mode 100644 index 000000000..34aacae37 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/PositionConfidenceEllipse.java @@ -0,0 +1,24 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * PositionConfidenceEllipse + * + * @param semiMajor Half of length of the major axis (semi-major axis). Unit: 0,01 m. doNotUse (0), outOfRange (4094), + * unavailable (4095). + * @param semiMinor Half of length of the minor axis (semi-minor axis). Unit: 0,01 m. doNotUse (0), outOfRange (4094), + * unavailable (4095). + * @param semiMajorOrientation Orientation of the ellipse major axis with respect to WGS84 North. Unit: 0,1 degrees. + * wgs84North (0), wgs84East (900), wgs84South (1800), wgs84West (2700), doNotUse (3600), + * unavailable (3601). + */ +public record PositionConfidenceEllipse( + int semiMajor, + int semiMinor, + int semiMajorOrientation) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Radial.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Radial.java new file mode 100644 index 000000000..8b38121fe --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Radial.java @@ -0,0 +1,26 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Radial shape definition. + * + * @param range Radial range from the reference point (0..4095). + * @param stationaryHorizontalOpeningAngleStart Horizontal opening angle start. Unit: 0,1 degrees (0..3601). + * @param stationaryHorizontalOpeningAngleEnd Horizontal opening angle end. Unit: 0,1 degrees (0..3601). + * @param shapeReferencePoint Optional {@link CartesianPosition3d} reference point. + * @param verticalOpeningAngleStart Optional vertical opening angle start. Unit: 0,1 degrees (0..3601). + * @param verticalOpeningAngleEnd Optional vertical opening angle end. Unit: 0,1 degrees (0..3601). + */ +public record Radial( + int range, + int stationaryHorizontalOpeningAngleStart, + int stationaryHorizontalOpeningAngleEnd, + CartesianPosition3d shapeReferencePoint, + Integer verticalOpeningAngleStart, + Integer verticalOpeningAngleEnd) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/RadialShapes.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/RadialShapes.java new file mode 100644 index 000000000..4bb7a200c --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/RadialShapes.java @@ -0,0 +1,26 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +import java.util.List; + +/** + * Radial shapes list. + * + * @param refPointId Reference point identifier (0..255). + * @param xCoordinate X offset of the reference point (-3094..1001). + * @param yCoordinate Y offset of the reference point (-3094..1001). + * @param zCoordinate Optional Z offset of the reference point (-3094..1001). + * @param radialShapesList List of {@link Radial} shapes. + */ +public record RadialShapes( + int refPointId, + int xCoordinate, + int yCoordinate, + Integer zCoordinate, + List radialShapesList) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Rectangular.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Rectangular.java new file mode 100644 index 000000000..3bbc859d9 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Rectangular.java @@ -0,0 +1,24 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Rectangular shape definition. + * + * @param centerPoint Optional {@link CartesianPosition3d} center point. + * @param semiLength Half the length of the rectangle (0..102). + * @param semiBreadth Half the breadth of the rectangle (0..102). + * @param orientation Optional. Orientation in WGS84. Unit: 0,1 degrees (0..3601). + * @param height Optional height for a right rectangular prism (0..4095). + */ +public record Rectangular( + CartesianPosition3d centerPoint, + int semiLength, + int semiBreadth, + Integer orientation, + Integer height) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/ReferencePosition.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/ReferencePosition.java new file mode 100644 index 000000000..6dac14885 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/ReferencePosition.java @@ -0,0 +1,80 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * ReferencePosition + *

+ * Position within a geographic coordinate system together with a confidence ellipse. + * + * @param latitude Latitude of the geographical point. Unit: 0,1 microdegree. unavailable (900000001). + * @param longitude Longitude of the geographical point. Unit: 0,1 microdegree. unavailable (1800000001). + * @param positionConfidenceEllipse {@link PositionConfidenceEllipse} + * @param altitude {@link Altitude} + */ +public record ReferencePosition( + int latitude, + int longitude, + PositionConfidenceEllipse positionConfidenceEllipse, + Altitude altitude) { + + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for ReferencePosition. + *

+ * Mandatory fields: + *

    + *
  • latitude
  • + *
  • longitude
  • + *
  • positionConfidenceEllipse
  • + *
  • altitude
  • + *
+ */ + public static final class Builder { + private Integer latitude; + private Integer longitude; + private PositionConfidenceEllipse positionConfidenceEllipse; + private Altitude altitude; + + private Builder() {} + + public Builder latitudeLongitude(int latitude, int longitude) { + this.latitude = latitude; + this.longitude = longitude; + return this; + } + + public Builder positionConfidenceEllipse(PositionConfidenceEllipse positionConfidenceEllipse) { + this.positionConfidenceEllipse = positionConfidenceEllipse; + return this; + } + + public Builder altitude(Altitude altitude) { + this.altitude = altitude; + return this; + } + + public ReferencePosition build() { + return new ReferencePosition( + requireNonNull(latitude, "latitude"), + requireNonNull(longitude, "longitude"), + requireNonNull(positionConfidenceEllipse, "position_confidence_ellipse"), + requireNonNull(altitude, "altitude")); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/RoadSegment.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/RoadSegment.java new file mode 100644 index 000000000..a7bb7485b --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/RoadSegment.java @@ -0,0 +1,17 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Road segment reference in a MAPEM. + * + * @param id Road segment identifier. + * @param region Optional region identifier of the responsible entity. + */ +public record RoadSegment(int id, Integer region) {} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Shape.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Shape.java new file mode 100644 index 000000000..f431439d4 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Shape.java @@ -0,0 +1,111 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Definition of a geographical area or volume, based on different options. + *

+ * Exactly one of {@link Rectangular}, {@link Circular}, {@link Polygonal}, {@link Elliptical}, {@link Radial} or + * {@link RadialShapes} shall be provided. + * + * @param rectangular Rectangular shape definition. + * @param circular Circular shape definition. + * @param polygonal Polygonal shape definition. + * @param elliptical Elliptical shape definition. + * @param radial Radial shape definition. + * @param radialShapes Set of radial shapes with an offset reference point. + */ +public record Shape( + Rectangular rectangular, + Circular circular, + Polygonal polygonal, + Elliptical elliptical, + Radial radial, + RadialShapes radialShapes) { + + public static Shape rectangular(Rectangular rectangular) { + return new Shape(rectangular, null, null, null, null, null); + } + + public static Shape circular(Circular circular) { + return new Shape(null, circular, null, null, null, null); + } + + public static Shape polygonal(Polygonal polygonal) { + return new Shape(null, null, polygonal, null, null, null); + } + + public static Shape elliptical(Elliptical elliptical) { + return new Shape(null, null, null, elliptical, null, null); + } + + public static Shape radial(Radial radial) { + return new Shape(null, null, null, null, radial, null); + } + + public static Shape radialShapes(RadialShapes radialShapes) { + return new Shape(null, null, null, null, null, radialShapes); + } + + public static Builder builder() { return new Builder(); } + + public static final class Builder { + private Rectangular rectangular; + private Circular circular; + private Polygonal polygonal; + private Elliptical elliptical; + private Radial radial; + private RadialShapes radialShapes; + + public Builder rectangular(Rectangular value) { + this.rectangular = value; + return this; + } + + public Builder circular(Circular value) { + this.circular = value; + return this; + } + + public Builder polygonal(Polygonal value) { + this.polygonal = value; + return this; + } + + public Builder elliptical(Elliptical value) { + this.elliptical = value; + return this; + } + + public Builder radial(Radial value) { + this.radial = value; + return this; + } + + public Builder radialShapes(RadialShapes value) { + this.radialShapes = value; + return this; + } + + public Shape build() { + int count = 0; + if (rectangular != null) count++; + if (circular != null) count++; + if (polygonal != null) count++; + if (elliptical != null) count++; + if (radial != null) count++; + if (radialShapes != null) count++; + + if (count != 1) { + throw new IllegalStateException("Shape must contain exactly one non-null field"); + } + + return new Shape(rectangular, circular, polygonal, elliptical, radial, radialShapes); + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Speed.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Speed.java new file mode 100644 index 000000000..640c851d2 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/Speed.java @@ -0,0 +1,17 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Speed and associated confidence value. + * + * @param value Speed value (magnitude of velocity). Unit: 0,01 m/s. standstill (0), outOfRange (16382), + * unavailable (16383). + * @param confidence Speed confidence. Unit: 0,01 m/s. outOfRange (126), unavailable (127). + */ +public record Speed(int value, int confidence) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/VelocityComponent.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/VelocityComponent.java new file mode 100644 index 000000000..34f7c4003 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/defs/VelocityComponent.java @@ -0,0 +1,17 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.defs; + +/** + * Component of the velocity vector and the associated confidence value. + * + * @param value Velocity component value. Unit: 0,01 m/s. negativeOutOfRange (-16383), outOfRange (16382), + * unavailable (16383). + * @param confidence Speed confidence for this component. Unit: 0,01 m/s. outOfRange (126), unavailable (127). + */ +public record VelocityComponent(int value, int confidence) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/managementcontainer/ManagementContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/managementcontainer/ManagementContainer.java new file mode 100644 index 000000000..fe8b1933f --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/managementcontainer/ManagementContainer.java @@ -0,0 +1,67 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.managementcontainer; + +import com.orange.iot3mobility.messages.cpm.v211.model.defs.ReferencePosition; + +/** + * Management container + * + * @param referenceTime Reference time for all time related information in the CPM. Unit: ms (0..4398046511103). + * @param referencePosition {@link ReferencePosition} + * @param segmentationInfo Optional {@link SegmentationInfo} + * @param messageRateRange Optional {@link MessageRateRange} planned or expected range of CPM generation rate. + */ +public record ManagementContainer( + long referenceTime, + ReferencePosition referencePosition, + SegmentationInfo segmentationInfo, + MessageRateRange messageRateRange) { + + public static Builder builder() { return new Builder(); } + + public static final class Builder { + private Long referenceTime; + private ReferencePosition referencePosition; + private SegmentationInfo segmentationInfo; + private MessageRateRange messageRateRange; + + public Builder referenceTime(long referenceTime) { + this.referenceTime = referenceTime; + return this; + } + + public Builder referencePosition(ReferencePosition referencePosition) { + this.referencePosition = referencePosition; + return this; + } + + public Builder segmentationInfo(SegmentationInfo segmentationInfo) { + this.segmentationInfo = segmentationInfo; + return this; + } + + public Builder messageRateRange(MessageRateRange messageRateRange) { + this.messageRateRange = messageRateRange; + return this; + } + + public ManagementContainer build() { + return new ManagementContainer( + requireNonNull(referenceTime, "reference_time"), + requireNonNull(referencePosition, "reference_position"), + segmentationInfo, + messageRateRange); + } + + private static T requireNonNull(T value, String field) { + if (value == null) throw new IllegalStateException("Missing field: " + field); + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/managementcontainer/MessageRateRange.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/managementcontainer/MessageRateRange.java new file mode 100644 index 000000000..4c8745e74 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/managementcontainer/MessageRateRange.java @@ -0,0 +1,20 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.managementcontainer; + +import com.orange.iot3mobility.messages.cpm.v211.model.defs.MessageRateHz; + +/** + * Message rate range + *

+ * Planned or expected range of the CPM generation rate. + * + * @param messageRateMin Minimum {@link MessageRateHz} (mantissa * 10^exponent) in Hz. + * @param messageRateMax Maximum {@link MessageRateHz} (mantissa * 10^exponent) in Hz. + */ +public record MessageRateRange(MessageRateHz messageRateMin, MessageRateHz messageRateMax) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/managementcontainer/SegmentationInfo.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/managementcontainer/SegmentationInfo.java new file mode 100644 index 000000000..ef8135ef0 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/managementcontainer/SegmentationInfo.java @@ -0,0 +1,19 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.managementcontainer; + +/** + * Segmentation information + *

+ * Information regarding the message segmentation on the facility layer. + * + * @param totalMsgNo Indicates the total number of messages used to encode the information (1..8). + * @param thisMsgNo Indicates the position of the message within the total set (1..8). + */ +public record SegmentationInfo(int totalMsgNo, int thisMsgNo) {} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/originatingrsucontainer/OriginatingRsuContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/originatingrsucontainer/OriginatingRsuContainer.java new file mode 100644 index 000000000..d14521454 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/originatingrsucontainer/OriginatingRsuContainer.java @@ -0,0 +1,42 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.originatingrsucontainer; + +import com.orange.iot3mobility.messages.cpm.v211.model.defs.MapReference; + +import java.util.List; + +/** + * Originating RSU container + * + * @param mapReferences List of {@link MapReference} identifying MAPEM topology references for perceived objects. + */ +public record OriginatingRsuContainer(List mapReferences) { + + public static Builder builder() { return new Builder(); } + + public static final class Builder { + private List mapReferences; + + public Builder mapReferences(List mapReferences) { + this.mapReferences = mapReferences; + return this; + } + + public OriginatingRsuContainer build() { + return new OriginatingRsuContainer(requireNonNull(mapReferences, "originating_rsu_container")); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/originatingvehiclecontainer/OriginatingVehicleContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/originatingvehiclecontainer/OriginatingVehicleContainer.java new file mode 100644 index 000000000..ea05d5e49 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/originatingvehiclecontainer/OriginatingVehicleContainer.java @@ -0,0 +1,74 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.originatingvehiclecontainer; + +import com.orange.iot3mobility.messages.cpm.v211.model.defs.Angle; + +import java.util.List; + +/** + * Originating vehicle container + * + * @param orientationAngle {@link Angle} absolute orientation of the disseminating vehicle in the WGS84 coordinate + * system with respect to true North. Unit: 0,1 degrees. + * @param pitchAngle Optional. {@link Angle} between the ground plane and the current orientation of the vehicle's + * x-axis with respect to the ground plane about the y-axis according to ISO 8855. Unit: 0,1 degrees. + * @param rollAngle Optional. {@link Angle} between the ground plane and the current orientation of a vehicle's y-axis + * with respect to the ground plane about the x-axis according to ISO 8855. Unit: 0,1 degrees. + * @param trailerDataSet Optional. List of {@link TrailerData} for attached trailers. + */ +public record OriginatingVehicleContainer( + Angle orientationAngle, + Angle pitchAngle, + Angle rollAngle, + List trailerDataSet) { + + public static Builder builder() { return new Builder(); } + + public static final class Builder { + private Angle orientationAngle; + private Angle pitchAngle; + private Angle rollAngle; + private List trailerDataSet; + + public Builder orientationAngle(Angle orientationAngle) { + this.orientationAngle = orientationAngle; + return this; + } + + public Builder pitchAngle(Angle pitchAngle) { + this.pitchAngle = pitchAngle; + return this; + } + + public Builder rollAngle(Angle rollAngle) { + this.rollAngle = rollAngle; + return this; + } + + public Builder trailerDataSet(List trailerDataSet) { + this.trailerDataSet = trailerDataSet; + return this; + } + + public OriginatingVehicleContainer build() { + return new OriginatingVehicleContainer( + requireNonNull(orientationAngle, "orientation_angle"), + pitchAngle, + rollAngle, + trailerDataSet); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/originatingvehiclecontainer/TrailerData.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/originatingvehiclecontainer/TrailerData.java new file mode 100644 index 000000000..15d61a6a3 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/originatingvehiclecontainer/TrailerData.java @@ -0,0 +1,37 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.originatingvehiclecontainer; + +import com.orange.iot3mobility.messages.cpm.v211.model.defs.Angle; + +/** + * Trailer data entry + *

+ * Provides detailed information about an attached trailer. + * + * @param refPointId Identifier of the reference point of the trailer (0..255). + * @param hitchPointOffset Position of the hitch point in negative x-direction (according to ISO 8855) from the vehicle + * reference point (0..255). + * @param hitchAngle {@link Angle} between the trailer orientation (corresponding to the x direction of the ISO 8855 + * coordinate system centered on the trailer) and the direction of the segment having as + * end points the reference point of the trailer and the reference point of the pulling + * vehicle, which can be another trailer or a vehicle looking on the horizontal plane xy, + * described in the local Cartesian coordinate system of the trailer. Unit: 0,1 degrees. + * @param frontOverhang Optional. Length of the trailer overhang in the positive x direction (according to ISO 8855) + * from the trailer Reference Point indicated by the refPointID (0..255). + * @param rearOverhang Optional. Length of the trailer overhang in the negative x direction (according to ISO 8855) + * from the trailer Reference Point indicated by the refPointID (0..255). + * @param trailerWidth Optional. Width of the trailer. Unit: 0,1 m. outOfRange (61), unavailable (62). + */ +public record TrailerData( + int refPointId, + int hitchPointOffset, + Angle hitchAngle, + Integer frontOverhang, + Integer rearOverhang, + Integer trailerWidth) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/Acceleration.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/Acceleration.java new file mode 100644 index 000000000..fdd99d81e --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/Acceleration.java @@ -0,0 +1,54 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +/** + * Acceleration vector of an object. + *

+ * Exactly one of {@link PolarAcceleration} or {@link CartesianAcceleration} shall be provided. + * + * @param polarAcceleration Acceleration in a polar or cylindrical coordinate system. + * @param cartesianAcceleration Acceleration in a cartesian coordinate system. + */ +public record Acceleration(PolarAcceleration polarAcceleration, CartesianAcceleration cartesianAcceleration) { + + public static Acceleration polar(PolarAcceleration polarAcceleration) { + return new Acceleration(polarAcceleration, null); + } + + public static Acceleration cartesian(CartesianAcceleration cartesianAcceleration) { + return new Acceleration(null, cartesianAcceleration); + } + + public static Builder builder() { return new Builder(); } + + public static final class Builder { + private PolarAcceleration polarAcceleration; + private CartesianAcceleration cartesianAcceleration; + + public Builder polarAcceleration(PolarAcceleration polarAcceleration) { + this.polarAcceleration = polarAcceleration; + return this; + } + + public Builder cartesianAcceleration(CartesianAcceleration cartesianAcceleration) { + this.cartesianAcceleration = cartesianAcceleration; + return this; + } + + public Acceleration build() { + int count = 0; + if (polarAcceleration != null) count++; + if (cartesianAcceleration != null) count++; + if (count != 1) { + throw new IllegalStateException("Acceleration must contain exactly one non-null field"); + } + return new Acceleration(polarAcceleration, cartesianAcceleration); + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/AccelerationMagnitude.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/AccelerationMagnitude.java new file mode 100644 index 000000000..93aec601d --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/AccelerationMagnitude.java @@ -0,0 +1,17 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +/** + * Acceleration magnitude with confidence. + * + * @param value Magnitude of the acceleration vector. Unit: 0,1 m/s2. noAcceleration (0), positiveOutOfRange (160), + * unavailable (161). + * @param confidence Acceleration confidence. Unit: 0,1 m/s2. outOfRange (101), unavailable (102). + */ +public record AccelerationMagnitude(int value, int confidence) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/CartesianAcceleration.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/CartesianAcceleration.java new file mode 100644 index 000000000..7d63eb09b --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/CartesianAcceleration.java @@ -0,0 +1,22 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +import com.orange.iot3mobility.messages.cpm.v211.model.defs.AccelerationComponent; + +/** + * Acceleration vector in a cartesian coordinate system. + * + * @param xAcceleration {@link AccelerationComponent} along the x-axis. + * @param yAcceleration {@link AccelerationComponent} along the y-axis. + * @param zAcceleration Optional. {@link AccelerationComponent} along the z-axis. + */ +public record CartesianAcceleration( + AccelerationComponent xAcceleration, + AccelerationComponent yAcceleration, + AccelerationComponent zAcceleration) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/CartesianVelocity.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/CartesianVelocity.java new file mode 100644 index 000000000..2dd2520bc --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/CartesianVelocity.java @@ -0,0 +1,22 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +import com.orange.iot3mobility.messages.cpm.v211.model.defs.VelocityComponent; + +/** + * Velocity vector in a cartesian coordinate system. + * + * @param xVelocity {@link VelocityComponent} along the x-axis. + * @param yVelocity {@link VelocityComponent} along the y-axis. + * @param zVelocity Optional. {@link VelocityComponent} along the z-axis. + */ +public record CartesianVelocity( + VelocityComponent xVelocity, + VelocityComponent yVelocity, + VelocityComponent zVelocity) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/ClusterProfiles.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/ClusterProfiles.java new file mode 100644 index 000000000..66760847a --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/ClusterProfiles.java @@ -0,0 +1,23 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +/** + * VRU cluster profiles. + * + * @param pedestrian Indicates that the cluster contains at least one pedestrian VRU. + * @param bicyclist Indicates that the cluster contains at least one bicycle VRU. + * @param motorcyclist Indicates that the cluster contains at least one motorcycle VRU. + * @param animal Indicates that the cluster contains at least one animal VRU. + */ +public record ClusterProfiles( + boolean pedestrian, + boolean bicyclist, + boolean motorcyclist, + boolean animal) {} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/EulerAngles.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/EulerAngles.java new file mode 100644 index 000000000..7b1a645e1 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/EulerAngles.java @@ -0,0 +1,20 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +import com.orange.iot3mobility.messages.cpm.v211.model.defs.Angle; + +/** + * Euler angles of the object bounding box. + * + * @param zAngle {@link Angle} yaw (rotation around z-axis) at measurement time. + * @param yAngle Optional {@link Angle} pitch (rotation around y-axis) at measurement time. + * @param xAngle Optional {@link Angle} roll (rotation around x-axis) at measurement time. + */ +public record EulerAngles(Angle zAngle, Angle yAngle, Angle xAngle) {} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/LowerTriangularCorrelationMatrix.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/LowerTriangularCorrelationMatrix.java new file mode 100644 index 000000000..0e2c078fc --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/LowerTriangularCorrelationMatrix.java @@ -0,0 +1,22 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +import java.util.List; + +/** + * Lower triangular correlation matrix. + * + * @param componentsIncludedInTheMatrix {@link LowerTriangularMatrixComponents} indicating which components are included. + * @param matrix List of matrix cells ordered by columns and rows. Cell values are correlation values (-100..101), where + * 101 indicates unavailable. + */ +public record LowerTriangularCorrelationMatrix( + LowerTriangularMatrixComponents componentsIncludedInTheMatrix, + List>> matrix) {} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/LowerTriangularMatrixComponents.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/LowerTriangularMatrixComponents.java new file mode 100644 index 000000000..f6cf3c9ed --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/LowerTriangularMatrixComponents.java @@ -0,0 +1,41 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +/** + * Components included in the lower triangular correlation matrix. + * + * @param xPosition Include x coordinate of position. + * @param yPosition Include y coordinate of position. + * @param zPosition Include z coordinate of position. + * @param xVelocityOrVelocityMagnitude Include x velocity or velocity magnitude. + * @param yVelocityOrVelocityDirection Include y velocity or velocity direction. + * @param zSpeed Include z speed component. + * @param xAccelOrAccelMagnitude Include x acceleration or acceleration magnitude. + * @param yAccelOrAccelDirection Include y acceleration or acceleration direction. + * @param zAcceleration Include z acceleration component. + * @param zAngle Include yaw angle (z axis). + * @param yAngle Include pitch angle (y axis). + * @param xAngle Include roll angle (x axis). + * @param zAngularVelocity Include angular velocity around z axis. + */ +public record LowerTriangularMatrixComponents( + boolean xPosition, + boolean yPosition, + boolean zPosition, + boolean xVelocityOrVelocityMagnitude, + boolean yVelocityOrVelocityDirection, + boolean zSpeed, + boolean xAccelOrAccelMagnitude, + boolean yAccelOrAccelDirection, + boolean zAcceleration, + boolean zAngle, + boolean yAngle, + boolean xAngle, + boolean zAngularVelocity) {} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/MapPosition.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/MapPosition.java new file mode 100644 index 000000000..8e1722f46 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/MapPosition.java @@ -0,0 +1,25 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +import com.orange.iot3mobility.messages.cpm.v211.model.defs.LongitudinalLanePosition; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.MapReference; + +/** + * Map-matched position of an object. + * + * @param mapReference Optional {@link MapReference} referencing MAPEM topology. + * @param laneId Optional lane identifier (0..255). + * @param connectionId Optional connection identifier (0..255). + * @param longitudinalLanePosition Optional {@link LongitudinalLanePosition} along the lane. + */ +public record MapPosition( + MapReference mapReference, + Integer laneId, + Integer connectionId, + LongitudinalLanePosition longitudinalLanePosition) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/ObjectClass.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/ObjectClass.java new file mode 100644 index 000000000..ef449f4ed --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/ObjectClass.java @@ -0,0 +1,84 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +/** + * Object class, one of vehicle, vru, group or other. + *

+ * Exactly one option shall be provided. + * + * @param vehicle Vehicle subclass. Examples: unknown (0), pedestrian (1), cyclist (2), moped (3), motorcycle (4), + * passengerCar (5), bus (6), lightTruck (7), heavyTruck (8), trailer (9), specialVehicle (10), + * tram (11), lightVruVehicle (12), animal (13), agricultural (14), roadSideUnit (15). + * @param vru {@link ObjectClassVru} for VRU subclasses. + * @param group {@link ObjectClassGroup} for VRU group/cluster information. + * @param other Other subclasses. Examples: unknown (0), singleObject (1), multipleObjects (2), bulkMaterial (3). + */ +public record ObjectClass( + Integer vehicle, + ObjectClassVru vru, + ObjectClassGroup group, + Integer other) { + + public static ObjectClass vehicle(int vehicle) { + return new ObjectClass(vehicle, null, null, null); + } + + public static ObjectClass vru(ObjectClassVru vru) { + return new ObjectClass(null, vru, null, null); + } + + public static ObjectClass group(ObjectClassGroup group) { + return new ObjectClass(null, null, group, null); + } + + public static ObjectClass other(int other) { + return new ObjectClass(null, null, null, other); + } + + public static Builder builder() { return new Builder(); } + + public static final class Builder { + private Integer vehicle; + private ObjectClassVru vru; + private ObjectClassGroup group; + private Integer other; + + public Builder vehicle(Integer vehicle) { + this.vehicle = vehicle; + return this; + } + + public Builder vru(ObjectClassVru vru) { + this.vru = vru; + return this; + } + + public Builder group(ObjectClassGroup group) { + this.group = group; + return this; + } + + public Builder other(Integer other) { + this.other = other; + return this; + } + + public ObjectClass build() { + int count = 0; + if (vehicle != null) count++; + if (vru != null) count++; + if (group != null) count++; + if (other != null) count++; + if (count != 1) { + throw new IllegalStateException("ObjectClass must contain exactly one non-null field"); + } + return new ObjectClass(vehicle, vru, group, other); + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/ObjectClassGroup.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/ObjectClassGroup.java new file mode 100644 index 000000000..20636c221 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/ObjectClassGroup.java @@ -0,0 +1,24 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +import com.orange.iot3mobility.messages.cpm.v211.model.defs.Shape; + +/** + * Object class group or cluster. + * + * @param clusterBoundingBoxShape {@link Shape} defining the cluster bounding box. + * @param clusterCardinalitySize Estimated number of VRUs in the group. unavailable (0), onlyLeader (1). + * @param clusterId Optional identifier of a VRU cluster (0..255). + * @param clusterProfiles Optional {@link ClusterProfiles} describing VRU profile types in the cluster. + */ +public record ObjectClassGroup( + Shape clusterBoundingBoxShape, + int clusterCardinalitySize, + Integer clusterId, + ClusterProfiles clusterProfiles) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/ObjectClassVru.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/ObjectClassVru.java new file mode 100644 index 000000000..7d63d5dcd --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/ObjectClassVru.java @@ -0,0 +1,87 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +/** + * VRU object class. + *

+ * Exactly one option shall be provided. + * + * @param pedestrian Pedestrian subclasses. Examples: unavailable (0), ordinary-pedestrian (1), road-worker (2), + * first-responder (3), max (15). + * @param bicyclistAndLightVruVehicle Bicyclist/light VRU subclasses. Examples: unavailable (0), bicyclist (1), + * wheelchair-user (2), horse-and-rider (3), rollerskater (4), e-scooter (5), + * personal-transporter (6), pedelec (7), speed-pedelec (8), max (15). + * @param motorcylist Motorcyclist subclasses. Examples: unavailable (0), moped (1), motorcycle (2), + * motorcycle-and-sidecar-right (3), motorcycle-and-sidecar-left (4), max (15). + * @param animal Animal subclasses. Examples: unavailable (0), wild-animal (1), farm-animal (2), service-animal (3), + * max (15). + */ +public record ObjectClassVru( + Integer pedestrian, + Integer bicyclistAndLightVruVehicle, + Integer motorcylist, + Integer animal) { + + public static ObjectClassVru pedestrian(int pedestrian) { + return new ObjectClassVru(pedestrian, null, null, null); + } + + public static ObjectClassVru bicyclistAndLightVruVehicle(int value) { + return new ObjectClassVru(null, value, null, null); + } + + public static ObjectClassVru motorcylist(int motorcylist) { + return new ObjectClassVru(null, null, motorcylist, null); + } + + public static ObjectClassVru animal(int animal) { + return new ObjectClassVru(null, null, null, animal); + } + + public static Builder builder() { return new Builder(); } + + public static final class Builder { + private Integer pedestrian; + private Integer bicyclistAndLightVruVehicle; + private Integer motorcylist; + private Integer animal; + + public Builder pedestrian(Integer pedestrian) { + this.pedestrian = pedestrian; + return this; + } + + public Builder bicyclistAndLightVruVehicle(Integer bicyclistAndLightVruVehicle) { + this.bicyclistAndLightVruVehicle = bicyclistAndLightVruVehicle; + return this; + } + + public Builder motorcylist(Integer motorcylist) { + this.motorcylist = motorcylist; + return this; + } + + public Builder animal(Integer animal) { + this.animal = animal; + return this; + } + + public ObjectClassVru build() { + int count = 0; + if (pedestrian != null) count++; + if (bicyclistAndLightVruVehicle != null) count++; + if (motorcylist != null) count++; + if (animal != null) count++; + if (count != 1) { + throw new IllegalStateException("ObjectClassVru must contain exactly one non-null field"); + } + return new ObjectClassVru(pedestrian, bicyclistAndLightVruVehicle, motorcylist, animal); + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/ObjectClassification.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/ObjectClassification.java new file mode 100644 index 000000000..5d1887b55 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/ObjectClassification.java @@ -0,0 +1,17 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +/** + * Classification of the described object. + * + * @param objectClass {@link ObjectClass} describing the detected object. + * @param confidence Confidence value of the classification (1..101). 101 indicates unavailable. + */ +public record ObjectClassification(ObjectClass objectClass, int confidence) {} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/PerceivedObject.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/PerceivedObject.java new file mode 100644 index 000000000..c6c76f267 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/PerceivedObject.java @@ -0,0 +1,187 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +import com.orange.iot3mobility.messages.cpm.v211.model.defs.CartesianAngularVelocityComponent; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.CartesianPosition3dWithConfidence; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.ObjectDimension; + +import java.util.List; + +/** + * Perceived object + *

+ * Information for an individual perceived object. + * + * @param measurementDeltaTime Time difference from a reference time to the time of the measurement of the object. + * Unit: ms (-2048..2047). + * @param position {@link CartesianPosition3dWithConfidence} of the geometric centre of the object's bounding box + * within the pre-defined coordinate system. + * @param objectId Optional. Identifier assigned to a detected object (0..65535). + * @param velocity Optional. {@link Velocity} vector of the object within the pre-defined coordinate system. + * @param acceleration Optional. {@link Acceleration} vector of the object within the pre-defined coordinate system. + * @param angles Optional. {@link EulerAngles} of the object bounding box at the time of measurement. + * @param zAngularVelocity Optional. {@link CartesianAngularVelocityComponent} around the z-axis at the time of + * measurement. + * @param lowerTriangularCorrelationMatrices Optional. List of {@link LowerTriangularCorrelationMatrix} entries. + * @param objectDimensionZ Optional. {@link ObjectDimension} z-dimension of object bounding box. + * @param objectDimensionY Optional. {@link ObjectDimension} y-dimension of object bounding box. + * @param objectDimensionX Optional. {@link ObjectDimension} x-dimension of object bounding box. + * @param objectAge Optional. Age of the detected object since first detection. Unit: ms (-2048..2047). + * @param objectPerceptionQuality Optional. Overall perception quality (0..15). noConfidence (0), fullConfidence (15). + * @param sensorIdList Optional. List of sensor-IDs which provided the measurement data (1..128 items). + * @param classification Optional. List of {@link ObjectClassification} entries (1..8 items). + * @param mapPosition Optional. {@link MapPosition}. + */ +public record PerceivedObject( + int measurementDeltaTime, + CartesianPosition3dWithConfidence position, + Integer objectId, + Velocity velocity, + Acceleration acceleration, + EulerAngles angles, + CartesianAngularVelocityComponent zAngularVelocity, + List lowerTriangularCorrelationMatrices, + ObjectDimension objectDimensionZ, + ObjectDimension objectDimensionY, + ObjectDimension objectDimensionX, + Integer objectAge, + Integer objectPerceptionQuality, + List sensorIdList, + List classification, + MapPosition mapPosition) { + + public static Builder builder() { return new Builder(); } + + public static final class Builder { + private Integer measurementDeltaTime; + private CartesianPosition3dWithConfidence position; + private Integer objectId; + private Velocity velocity; + private Acceleration acceleration; + private EulerAngles angles; + private CartesianAngularVelocityComponent zAngularVelocity; + private List lowerTriangularCorrelationMatrices; + private ObjectDimension objectDimensionZ; + private ObjectDimension objectDimensionY; + private ObjectDimension objectDimensionX; + private Integer objectAge; + private Integer objectPerceptionQuality; + private List sensorIdList; + private List classification; + private MapPosition mapPosition; + + public Builder measurementDeltaTime(int measurementDeltaTime) { + this.measurementDeltaTime = measurementDeltaTime; + return this; + } + + public Builder position(CartesianPosition3dWithConfidence position) { + this.position = position; + return this; + } + + public Builder objectId(Integer objectId) { + this.objectId = objectId; + return this; + } + + public Builder velocity(Velocity velocity) { + this.velocity = velocity; + return this; + } + + public Builder acceleration(Acceleration acceleration) { + this.acceleration = acceleration; + return this; + } + + public Builder angles(EulerAngles angles) { + this.angles = angles; + return this; + } + + public Builder zAngularVelocity(CartesianAngularVelocityComponent zAngularVelocity) { + this.zAngularVelocity = zAngularVelocity; + return this; + } + + public Builder lowerTriangularCorrelationMatrices(List matrices) { + this.lowerTriangularCorrelationMatrices = matrices; + return this; + } + + public Builder objectDimensionZ(ObjectDimension objectDimensionZ) { + this.objectDimensionZ = objectDimensionZ; + return this; + } + + public Builder objectDimensionY(ObjectDimension objectDimensionY) { + this.objectDimensionY = objectDimensionY; + return this; + } + + public Builder objectDimensionX(ObjectDimension objectDimensionX) { + this.objectDimensionX = objectDimensionX; + return this; + } + + public Builder objectAge(Integer objectAge) { + this.objectAge = objectAge; + return this; + } + + public Builder objectPerceptionQuality(Integer objectPerceptionQuality) { + this.objectPerceptionQuality = objectPerceptionQuality; + return this; + } + + public Builder sensorIdList(List sensorIdList) { + this.sensorIdList = sensorIdList; + return this; + } + + public Builder classification(List classification) { + this.classification = classification; + return this; + } + + public Builder mapPosition(MapPosition mapPosition) { + this.mapPosition = mapPosition; + return this; + } + + public PerceivedObject build() { + return new PerceivedObject( + requireNonNull(measurementDeltaTime, "measurement_delta_time"), + requireNonNull(position, "position"), + objectId, + velocity, + acceleration, + angles, + zAngularVelocity, + lowerTriangularCorrelationMatrices, + objectDimensionZ, + objectDimensionY, + objectDimensionX, + objectAge, + objectPerceptionQuality, + sensorIdList, + classification, + mapPosition); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} + diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/PerceivedObjectContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/PerceivedObjectContainer.java new file mode 100644 index 000000000..d2faa4a1d --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/PerceivedObjectContainer.java @@ -0,0 +1,40 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +import java.util.List; + +/** + * Perceived object container. + * + * @param perceivedObjects List of {@link PerceivedObject} entries. + */ +public record PerceivedObjectContainer(List perceivedObjects) { + + public static Builder builder() { return new Builder(); } + + public static final class Builder { + private List perceivedObjects; + + public Builder perceivedObjects(List perceivedObjects) { + this.perceivedObjects = perceivedObjects; + return this; + } + + public PerceivedObjectContainer build() { + return new PerceivedObjectContainer(requireNonNull(perceivedObjects, "perceived_object_container")); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/PolarAcceleration.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/PolarAcceleration.java new file mode 100644 index 000000000..516de6b0a --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/PolarAcceleration.java @@ -0,0 +1,23 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +import com.orange.iot3mobility.messages.cpm.v211.model.defs.AccelerationComponent; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.Angle; + +/** + * Acceleration vector in a polar or cylindrical coordinate system. + * + * @param accelerationMagnitude {@link AccelerationMagnitude} projected onto the reference plane. + * @param accelerationDirection {@link Angle} of the projected acceleration vector. + * @param zAcceleration Optional. {@link AccelerationComponent} along the reference axis. + */ +public record PolarAcceleration( + AccelerationMagnitude accelerationMagnitude, + Angle accelerationDirection, + AccelerationComponent zAcceleration) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/PolarVelocity.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/PolarVelocity.java new file mode 100644 index 000000000..01d12247d --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/PolarVelocity.java @@ -0,0 +1,24 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +import com.orange.iot3mobility.messages.cpm.v211.model.defs.Angle; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.Speed; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.VelocityComponent; + +/** + * Velocity vector in a polar or cylindrical coordinate system. + * + * @param velocityMagnitude {@link Speed} of the velocity vector projected onto the reference plane. + * @param velocityDirection {@link Angle} of the velocity vector projected onto the reference plane. + * @param zVelocity Optional. {@link VelocityComponent} along the reference axis of the cylindrical system. + */ +public record PolarVelocity( + Speed velocityMagnitude, + Angle velocityDirection, + VelocityComponent zVelocity) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/Velocity.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/Velocity.java new file mode 100644 index 000000000..8856d23d0 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceivedobjectcontainer/Velocity.java @@ -0,0 +1,54 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer; + +/** + * Velocity vector of an object. + *

+ * Exactly one of {@link PolarVelocity} or {@link CartesianVelocity} shall be provided. + * + * @param polarVelocity Velocity in a polar or cylindrical coordinate system. + * @param cartesianVelocity Velocity in a cartesian coordinate system. + */ +public record Velocity(PolarVelocity polarVelocity, CartesianVelocity cartesianVelocity) { + + public static Velocity polar(PolarVelocity polarVelocity) { + return new Velocity(polarVelocity, null); + } + + public static Velocity cartesian(CartesianVelocity cartesianVelocity) { + return new Velocity(null, cartesianVelocity); + } + + public static Builder builder() { return new Builder(); } + + public static final class Builder { + private PolarVelocity polarVelocity; + private CartesianVelocity cartesianVelocity; + + public Builder polarVelocity(PolarVelocity polarVelocity) { + this.polarVelocity = polarVelocity; + return this; + } + + public Builder cartesianVelocity(CartesianVelocity cartesianVelocity) { + this.cartesianVelocity = cartesianVelocity; + return this; + } + + public Velocity build() { + int count = 0; + if (polarVelocity != null) count++; + if (cartesianVelocity != null) count++; + if (count != 1) { + throw new IllegalStateException("Velocity must contain exactly one non-null field"); + } + return new Velocity(polarVelocity, cartesianVelocity); + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceptionregioncontainer/PerceptionRegion.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceptionregioncontainer/PerceptionRegion.java new file mode 100644 index 000000000..d88e89bb8 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceptionregioncontainer/PerceptionRegion.java @@ -0,0 +1,31 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceptionregioncontainer; + +import com.orange.iot3mobility.messages.cpm.v211.model.defs.Shape; + +import java.util.List; + +/** + * Perception region information. + * + * @param measurementDeltaTime Difference between the time of estimation of the perception region and the reference + * time. Unit: ms (-2048..2047). + * @param perceptionRegionConfidence Perception confidence (1..101). 101 indicates unavailable. + * @param perceptionRegionShape {@link Shape} describing the region. + * @param shadowingApplies Indicates if the standard shadowing approach applies. + * @param sensorIdList Optional list of sensor identifiers involved (1..128 items). + * @param perceivedObjectIds Optional list of perceived object identifiers contained in the region (0..255 items). + */ +public record PerceptionRegion( + int measurementDeltaTime, + int perceptionRegionConfidence, + Shape perceptionRegionShape, + boolean shadowingApplies, + List sensorIdList, + List perceivedObjectIds) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceptionregioncontainer/PerceptionRegionContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceptionregioncontainer/PerceptionRegionContainer.java new file mode 100644 index 000000000..c4e35453b --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/perceptionregioncontainer/PerceptionRegionContainer.java @@ -0,0 +1,40 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.perceptionregioncontainer; + +import java.util.List; + +/** + * Perception region container. + * + * @param perceptionRegions List of {@link PerceptionRegion} entries. + */ +public record PerceptionRegionContainer(List perceptionRegions) { + + public static Builder builder() { return new Builder(); } + + public static final class Builder { + private List perceptionRegions; + + public Builder perceptionRegions(List perceptionRegions) { + this.perceptionRegions = perceptionRegions; + return this; + } + + public PerceptionRegionContainer build() { + return new PerceptionRegionContainer(requireNonNull(perceptionRegions, "perception_region_container")); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/sensorinformationcontainer/SensorInformation.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/sensorinformationcontainer/SensorInformation.java new file mode 100644 index 000000000..5802e9d72 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/sensorinformationcontainer/SensorInformation.java @@ -0,0 +1,29 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.sensorinformationcontainer; + +import com.orange.iot3mobility.messages.cpm.v211.model.defs.Shape; + +/** + * Information for an individual sensor. + * + * @param sensorId Sensor identifier (0..255). + * @param sensorType Type of attached sensor (0..31). Examples: radar (1), lidar (2), monovideo (3), stereovision (4), + * nightvision (5), ultrasonic (6), pmd (7), inductionloop (8), sphericalCamera (9), uwb (10), + * acoustic (11), localAggregation (12), itsAggregation (13). + * @param perceptionRegionShape Optional {@link Shape} describing the perception region. + * @param perceptionRegionConfidence Optional homogeneous confidence for the perception region (1..101). 101 indicates + * unavailable. + * @param shadowingApplies Indicates if the standard shadowing approach applies. + */ +public record SensorInformation( + int sensorId, + int sensorType, + Shape perceptionRegionShape, + Integer perceptionRegionConfidence, + boolean shadowingApplies) {} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/sensorinformationcontainer/SensorInformationContainer.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/sensorinformationcontainer/SensorInformationContainer.java new file mode 100644 index 000000000..82c23903f --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/model/sensorinformationcontainer/SensorInformationContainer.java @@ -0,0 +1,40 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.model.sensorinformationcontainer; + +import java.util.List; + +/** + * Sensor information container. + * + * @param sensorInformation List of {@link SensorInformation} entries. + */ +public record SensorInformationContainer(List sensorInformation) { + + public static Builder builder() { return new Builder(); } + + public static final class Builder { + private List sensorInformation; + + public Builder sensorInformation(List sensorInformation) { + this.sensorInformation = sensorInformation; + return this; + } + + public SensorInformationContainer build() { + return new SensorInformationContainer(requireNonNull(sensorInformation, "sensor_information_container")); + } + + private static T requireNonNull(T value, String field) { + if (value == null) { + throw new IllegalStateException("Missing field: " + field); + } + return value; + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/validation/CpmValidationException.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/validation/CpmValidationException.java new file mode 100644 index 000000000..ee7a33357 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/validation/CpmValidationException.java @@ -0,0 +1,25 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.validation; + +import com.orange.iot3mobility.messages.cpm.core.CpmException; + +/** + * Dedicated exception thrown whenever a CAM 2.3.0 payload is not compliant + * with the JSON schema or the project-specific rules. + */ +public final class CpmValidationException extends CpmException { + + public CpmValidationException(String message) { + super(message); + } + + public CpmValidationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/validation/CpmValidator211.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/validation/CpmValidator211.java new file mode 100644 index 000000000..157827c38 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cpm/v211/validation/CpmValidator211.java @@ -0,0 +1,798 @@ +/* + Copyright 2016-2026 Orange + + This software is distributed under the MIT license, see LICENSE.txt file for more details. + + @author Mathieu LEFEBVRE + */ +package com.orange.iot3mobility.messages.cpm.v211.validation; + +import com.orange.iot3mobility.messages.cpm.v211.model.CpmEnvelope211; +import com.orange.iot3mobility.messages.cpm.v211.model.CpmMessage211; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.Angle; +import com.orange.iot3mobility.messages.cpm.v211.model.defs.*; +import com.orange.iot3mobility.messages.cpm.v211.model.managementcontainer.ManagementContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.managementcontainer.MessageRateRange; +import com.orange.iot3mobility.messages.cpm.v211.model.managementcontainer.SegmentationInfo; +import com.orange.iot3mobility.messages.cpm.v211.model.originatingvehiclecontainer.OriginatingVehicleContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.originatingvehiclecontainer.TrailerData; +import com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer.*; +import com.orange.iot3mobility.messages.cpm.v211.model.perceptionregioncontainer.PerceptionRegion; +import com.orange.iot3mobility.messages.cpm.v211.model.perceptionregioncontainer.PerceptionRegionContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.sensorinformationcontainer.SensorInformation; +import com.orange.iot3mobility.messages.cpm.v211.model.sensorinformationcontainer.SensorInformationContainer; +import com.orange.iot3mobility.messages.cpm.v211.model.originatingrsucontainer.OriginatingRsuContainer; + +import java.util.List; +import java.util.Objects; + +/** + * Static validation utility for CPM 2.1.1 envelope + */ +public final class CpmValidator211 { + + private static final long MIN_TIMESTAMP = 1514764800000L; // 2018-01-01 + + private CpmValidator211() {} + + public static void validateEnvelope(CpmEnvelope211 envelope) { + requireNonNull("envelope", envelope); + requireEquals("message_type", envelope.messageType(), "cpm"); + requireNotBlank("source_uuid", envelope.sourceUuid()); + checkMin("timestamp", envelope.timestamp(), MIN_TIMESTAMP); + requireEquals("version", envelope.version(), "2.1.1"); + if (envelope.objectIdRotationCount() != null) { + checkRange("object_id_rotation_count", envelope.objectIdRotationCount(), 0, 255); + } + + validateMessage(requireNonNull("message", envelope.message())); + } + + private static void validateMessage(CpmMessage211 message) { + requireNonNull("message", message); + checkRange("protocol_version", message.protocolVersion(), 0, 255); + checkRange("station_id", message.stationId(), 0, 4294967295L); + + validateManagementContainer(message.managementContainer()); + + if (message.originatingVehicleContainer() != null) { + validateOriginatingVehicleContainer(message.originatingVehicleContainer()); + } + if (message.originatingRsuContainer() != null) { + validateOriginatingRsuContainer(message.originatingRsuContainer()); + } + if (message.sensorInformationContainer() != null) { + validateSensorInformationContainer(message.sensorInformationContainer()); + } + if (message.perceptionRegionContainer() != null) { + validatePerceptionRegionContainer(message.perceptionRegionContainer()); + } + if (message.perceivedObjectContainer() != null) { + validatePerceivedObjectContainer(message.perceivedObjectContainer()); + } + } + + /* --------------------------------------------------------------------- */ + /* Management container */ + /* --------------------------------------------------------------------- */ + + private static void validateManagementContainer(ManagementContainer container) { + requireNonNull("management_container", container); + checkRange("management_container.reference_time", container.referenceTime(), 0, 4398046511103L); + validateReferencePosition(container.referencePosition()); + + if (container.segmentationInfo() != null) { + validateSegmentationInfo(container.segmentationInfo()); + } + if (container.messageRateRange() != null) { + validateMessageRateRange(container.messageRateRange()); + } + } + + private static void validateReferencePosition(ReferencePosition reference) { + requireNonNull("reference_position", reference); + checkRange("reference_position.latitude", reference.latitude(), -900000000, 900000001); + checkRange("reference_position.longitude", reference.longitude(), -1800000000, 1800000001); + validatePositionConfidenceEllipse("reference_position.position_confidence_ellipse", reference.positionConfidenceEllipse()); + validateAltitude("reference_position.altitude", reference.altitude()); + } + + private static void validatePositionConfidenceEllipse(String prefix, PositionConfidenceEllipse ellipse) { + requireNonNull(prefix, ellipse); + checkRange(prefix + ".semi_major", ellipse.semiMajor(), 0, 4095); + checkRange(prefix + ".semi_minor", ellipse.semiMinor(), 0, 4095); + checkRange(prefix + ".semi_major_orientation", ellipse.semiMajorOrientation(), 0, 3601); + } + + private static void validateAltitude(String prefix, Altitude altitude) { + requireNonNull(prefix, altitude); + checkRange(prefix + ".value", altitude.value(), -100000, 800001); + checkRange(prefix + ".confidence", altitude.confidence(), 0, 15); + } + + private static void validateSegmentationInfo(SegmentationInfo info) { + requireNonNull("segmentation_info", info); + checkRange("segmentation_info.total_msg_no", info.totalMsgNo(), 1, 8); + checkRange("segmentation_info.this_msg_no", info.thisMsgNo(), 1, 8); + } + + private static void validateMessageRateRange(MessageRateRange range) { + requireNonNull("message_rate_range", range); + validateMessageRateHz("message_rate_range.message_rate_min", range.messageRateMin()); + validateMessageRateHz("message_rate_range.message_rate_max", range.messageRateMax()); + } + + private static void validateMessageRateHz(String prefix, MessageRateHz rate) { + requireNonNull(prefix, rate); + checkRange(prefix + ".mantissa", rate.mantissa(), 1, 100); + checkRange(prefix + ".exponent", rate.exponent(), -5, 2); + } + + /* --------------------------------------------------------------------- */ + /* Originating vehicle container */ + /* --------------------------------------------------------------------- */ + + private static void validateOriginatingVehicleContainer(OriginatingVehicleContainer container) { + requireNonNull("originating_vehicle_container", container); + validateAngle("originating_vehicle_container.orientation_angle", container.orientationAngle()); + if (container.pitchAngle() != null) { + validateAngle("originating_vehicle_container.pitch_angle", container.pitchAngle()); + } + if (container.rollAngle() != null) { + validateAngle("originating_vehicle_container.roll_angle", container.rollAngle()); + } + if (container.trailerDataSet() != null) { + for (int i = 0; i < container.trailerDataSet().size(); i++) { + validateTrailerData("originating_vehicle_container.trailer_data_set[" + i + "]", container.trailerDataSet().get(i)); + } + } + } + + private static void validateTrailerData(String prefix, TrailerData trailer) { + requireNonNull(prefix, trailer); + checkRange(prefix + ".ref_point_id", trailer.refPointId(), 0, 255); + checkRange(prefix + ".hitch_point_offset", trailer.hitchPointOffset(), 0, 255); + validateAngle(prefix + ".hitch_angle", trailer.hitchAngle()); + if (trailer.frontOverhang() != null) { + checkRange(prefix + ".front_overhang", trailer.frontOverhang(), 0, 255); + } + if (trailer.rearOverhang() != null) { + checkRange(prefix + ".rear_overhang", trailer.rearOverhang(), 0, 255); + } + if (trailer.trailerWidth() != null) { + checkRange(prefix + ".trailer_width", trailer.trailerWidth(), 1, 62); + } + } + + /* --------------------------------------------------------------------- */ + /* Originating RSU container */ + /* --------------------------------------------------------------------- */ + + private static void validateOriginatingRsuContainer(OriginatingRsuContainer container) { + requireNonNull("originating_rsu_container", container); + List references = requireNonNull("originating_rsu_container", container.mapReferences()); + for (int i = 0; i < references.size(); i++) { + validateMapReference("originating_rsu_container[" + i + "]", references.get(i)); + } + } + + /* --------------------------------------------------------------------- */ + /* Sensor information container */ + /* --------------------------------------------------------------------- */ + + private static void validateSensorInformationContainer(SensorInformationContainer container) { + requireNonNull("sensor_information_container", container); + List sensors = requireNonNull("sensor_information_container", container.sensorInformation()); + int size = sensors.size(); + if (size < 1 || size > 128) { + throw new CpmValidationException("sensor_information_container size out of range [1,128]: " + size); + } + for (int i = 0; i < size; i++) { + validateSensorInformation("sensor_information_container[" + i + "]", sensors.get(i)); + } + } + + private static void validateSensorInformation(String prefix, SensorInformation info) { + requireNonNull(prefix, info); + checkRange(prefix + ".sensor_id", info.sensorId(), 0, 255); + checkRange(prefix + ".sensor_type", info.sensorType(), 0, 31); + if (info.perceptionRegionShape() != null) { + validateShape(prefix + ".perception_region_shape", info.perceptionRegionShape()); + } + if (info.perceptionRegionConfidence() != null) { + checkRange(prefix + ".perception_region_confidence", info.perceptionRegionConfidence(), 1, 101); + } + } + + /* --------------------------------------------------------------------- */ + /* Perception region container */ + /* --------------------------------------------------------------------- */ + + private static void validatePerceptionRegionContainer(PerceptionRegionContainer container) { + requireNonNull("perception_region_container", container); + List regions = requireNonNull("perception_region_container", container.perceptionRegions()); + int size = regions.size(); + if (size < 1 || size > 256) { + throw new CpmValidationException("perception_region_container size out of range [1,256]: " + size); + } + for (int i = 0; i < size; i++) { + validatePerceptionRegion("perception_region_container[" + i + "]", regions.get(i)); + } + } + + private static void validatePerceptionRegion(String prefix, PerceptionRegion region) { + requireNonNull(prefix, region); + checkRange(prefix + ".measurement_delta_time", region.measurementDeltaTime(), -2048, 2047); + checkRange(prefix + ".perception_region_confidence", region.perceptionRegionConfidence(), 1, 101); + validateShape(prefix + ".perception_region_shape", region.perceptionRegionShape()); + if (region.sensorIdList() != null) { + int size = region.sensorIdList().size(); + if (size < 1 || size > 128) { + throw new CpmValidationException(prefix + ".sensor_id_list size out of range [1,128]: " + size); + } + for (int i = 0; i < size; i++) { + checkRange(prefix + ".sensor_id_list[" + i + "]", region.sensorIdList().get(i), 0, 255); + } + } + if (region.perceivedObjectIds() != null) { + int size = region.perceivedObjectIds().size(); + if (size > 255) { + throw new CpmValidationException(prefix + ".perceived_object_ids size out of range [0,255]: " + size); + } + for (int i = 0; i < size; i++) { + checkRange(prefix + ".perceived_object_ids[" + i + "]", region.perceivedObjectIds().get(i), 0, 65535); + } + } + } + + /* --------------------------------------------------------------------- */ + /* Perceived object container */ + /* --------------------------------------------------------------------- */ + + private static void validatePerceivedObjectContainer(PerceivedObjectContainer container) { + requireNonNull("perceived_object_container", container); + List objects = requireNonNull("perceived_object_container", container.perceivedObjects()); + int size = objects.size(); + if (size > 255) { + throw new CpmValidationException("perceived_object_container size out of range [0,255]: " + size); + } + for (int i = 0; i < size; i++) { + validatePerceivedObject("perceived_object_container[" + i + "]", objects.get(i)); + } + } + + private static void validatePerceivedObject(String prefix, PerceivedObject object) { + requireNonNull(prefix, object); + checkRange(prefix + ".measurement_delta_time", object.measurementDeltaTime(), -2048, 2047); + validateCartesianPosition3dWithConfidence(prefix + ".position", object.position()); + if (object.objectId() != null) { + checkRange(prefix + ".object_id", object.objectId(), 0, 65535); + } + if (object.velocity() != null) { + validateVelocity(prefix + ".velocity", object.velocity()); + } + if (object.acceleration() != null) { + validateAcceleration(prefix + ".acceleration", object.acceleration()); + } + if (object.angles() != null) { + validateEulerAngles(prefix + ".angles", object.angles()); + } + if (object.zAngularVelocity() != null) { + validateCartesianAngularVelocityComponent(prefix + ".z_angular_velocity", object.zAngularVelocity()); + } + if (object.lowerTriangularCorrelationMatrices() != null) { + validateLowerTriangularCorrelationMatrices(prefix + ".lower_triangular_correlation_matrices", object.lowerTriangularCorrelationMatrices()); + } + if (object.objectDimensionZ() != null) { + validateObjectDimension(prefix + ".object_dimension_z", object.objectDimensionZ()); + } + if (object.objectDimensionY() != null) { + validateObjectDimension(prefix + ".object_dimension_y", object.objectDimensionY()); + } + if (object.objectDimensionX() != null) { + validateObjectDimension(prefix + ".object_dimension_x", object.objectDimensionX()); + } + if (object.objectAge() != null) { + checkRange(prefix + ".object_age", object.objectAge(), -2048, 2047); + } + if (object.objectPerceptionQuality() != null) { + checkRange(prefix + ".object_perception_quality", object.objectPerceptionQuality(), 0, 15); + } + if (object.sensorIdList() != null) { + int size = object.sensorIdList().size(); + if (size < 1 || size > 128) { + throw new CpmValidationException(prefix + ".sensor_id_list size out of range [1,128]: " + size); + } + for (int i = 0; i < size; i++) { + checkRange(prefix + ".sensor_id_list[" + i + "]", object.sensorIdList().get(i), 0, 255); + } + } + if (object.classification() != null) { + validateClassification(prefix + ".classification", object.classification()); + } + if (object.mapPosition() != null) { + validateMapPosition(prefix + ".map_position", object.mapPosition()); + } + } + + private static void validateCartesianPosition3dWithConfidence(String prefix, CartesianPosition3dWithConfidence position) { + requireNonNull(prefix, position); + validateCartesianCoordinateWithConfidence(prefix + ".x_coordinate", position.xCoordinate()); + validateCartesianCoordinateWithConfidence(prefix + ".y_coordinate", position.yCoordinate()); + if (position.zCoordinate() != null) { + validateCartesianCoordinateWithConfidence(prefix + ".z_coordinate", position.zCoordinate()); + } + } + + private static void validateCartesianCoordinateWithConfidence(String prefix, CartesianCoordinateWithConfidence coordinate) { + requireNonNull(prefix, coordinate); + checkRange(prefix + ".value", coordinate.value(), -131072, 131071); + checkRange(prefix + ".confidence", coordinate.confidence(), 1, 4096); + } + + private static void validateVelocity(String prefix, Velocity velocity) { + requireNonNull(prefix, velocity); + if (velocity.polarVelocity() != null && velocity.cartesianVelocity() != null) { + throw new CpmValidationException(prefix + " must contain exactly one velocity option"); + } + if (velocity.polarVelocity() != null) { + validatePolarVelocity(prefix + ".polar_velocity", velocity.polarVelocity()); + } else if (velocity.cartesianVelocity() != null) { + validateCartesianVelocity(prefix + ".cartesian_velocity", velocity.cartesianVelocity()); + } else { + throw new CpmValidationException(prefix + " must contain a velocity option"); + } + } + + private static void validatePolarVelocity(String prefix, PolarVelocity velocity) { + requireNonNull(prefix, velocity); + validateSpeed(prefix + ".velocity_magnitude", velocity.velocityMagnitude()); + validateAngle(prefix + ".velocity_direction", velocity.velocityDirection()); + if (velocity.zVelocity() != null) { + validateVelocityComponent(prefix + ".z_velocity", velocity.zVelocity()); + } + } + + private static void validateCartesianVelocity(String prefix, CartesianVelocity velocity) { + requireNonNull(prefix, velocity); + validateVelocityComponent(prefix + ".x_velocity", velocity.xVelocity()); + validateVelocityComponent(prefix + ".y_velocity", velocity.yVelocity()); + if (velocity.zVelocity() != null) { + validateVelocityComponent(prefix + ".z_velocity", velocity.zVelocity()); + } + } + + private static void validateAcceleration(String prefix, Acceleration acceleration) { + requireNonNull(prefix, acceleration); + if (acceleration.polarAcceleration() != null && acceleration.cartesianAcceleration() != null) { + throw new CpmValidationException(prefix + " must contain exactly one acceleration option"); + } + if (acceleration.polarAcceleration() != null) { + validatePolarAcceleration(prefix + ".polar_acceleration", acceleration.polarAcceleration()); + } else if (acceleration.cartesianAcceleration() != null) { + validateCartesianAcceleration(prefix + ".cartesian_acceleration", acceleration.cartesianAcceleration()); + } else { + throw new CpmValidationException(prefix + " must contain an acceleration option"); + } + } + + private static void validatePolarAcceleration(String prefix, PolarAcceleration acceleration) { + requireNonNull(prefix, acceleration); + validateAccelerationMagnitude(prefix + ".acceleration_magnitude", acceleration.accelerationMagnitude()); + validateAngle(prefix + ".acceleration_direction", acceleration.accelerationDirection()); + if (acceleration.zAcceleration() != null) { + validateAccelerationComponent(prefix + ".z_acceleration", acceleration.zAcceleration()); + } + } + + private static void validateAccelerationMagnitude(String prefix, AccelerationMagnitude magnitude) { + requireNonNull(prefix, magnitude); + checkRange(prefix + ".value", magnitude.value(), 0, 161); + checkRange(prefix + ".confidence", magnitude.confidence(), 0, 102); + } + + private static void validateCartesianAcceleration(String prefix, CartesianAcceleration acceleration) { + requireNonNull(prefix, acceleration); + validateAccelerationComponent(prefix + ".x_acceleration", acceleration.xAcceleration()); + validateAccelerationComponent(prefix + ".y_acceleration", acceleration.yAcceleration()); + if (acceleration.zAcceleration() != null) { + validateAccelerationComponent(prefix + ".z_acceleration", acceleration.zAcceleration()); + } + } + + private static void validateAccelerationComponent(String prefix, AccelerationComponent component) { + requireNonNull(prefix, component); + checkRange(prefix + ".value", component.value(), -160, 161); + checkRange(prefix + ".confidence", component.confidence(), 0, 102); + } + + private static void validateEulerAngles(String prefix, EulerAngles angles) { + requireNonNull(prefix, angles); + validateAngle(prefix + ".z_angle", angles.zAngle()); + if (angles.yAngle() != null) { + validateAngle(prefix + ".y_angle", angles.yAngle()); + } + if (angles.xAngle() != null) { + validateAngle(prefix + ".x_angle", angles.xAngle()); + } + } + + private static void validateCartesianAngularVelocityComponent(String prefix, CartesianAngularVelocityComponent component) { + requireNonNull(prefix, component); + checkRange(prefix + ".value", component.value(), -255, 256); + checkRange(prefix + ".confidence", component.confidence(), 0, 7); + } + + private static void validateLowerTriangularCorrelationMatrices(String prefix, List matrices) { + requireNonNull(prefix, matrices); + int size = matrices.size(); + if (size < 1 || size > 4) { + throw new CpmValidationException(prefix + " size out of range [1,4]: " + size); + } + for (int i = 0; i < size; i++) { + validateLowerTriangularCorrelationMatrix(prefix + "[" + i + "]", matrices.get(i)); + } + } + + private static void validateLowerTriangularCorrelationMatrix(String prefix, LowerTriangularCorrelationMatrix matrix) { + requireNonNull(prefix, matrix); + validateLowerTriangularMatrixComponents(prefix + ".components_included_in_the_matrix", matrix.componentsIncludedInTheMatrix()); + validateMatrix(prefix + ".matrix", matrix.matrix()); + } + + private static void validateLowerTriangularMatrixComponents(String prefix, LowerTriangularMatrixComponents components) { + requireNonNull(prefix, components); + } + + private static void validateMatrix(String prefix, List>> matrix) { + requireNonNull(prefix, matrix); + int size = matrix.size(); + if (size < 1 || size > 13) { + throw new CpmValidationException(prefix + " size out of range [1,13]: " + size); + } + for (int i = 0; i < size; i++) { + List> column = matrix.get(i); + if (column == null || column.isEmpty() || column.size() > 13) { + throw new CpmValidationException(prefix + "[" + i + "] size out of range [1,13]"); + } + for (int j = 0; j < column.size(); j++) { + List row = column.get(j); + if (row == null || row.isEmpty() || row.size() > 13) { + throw new CpmValidationException(prefix + "[" + i + "][" + j + "] size out of range [1,13]"); + } + for (int k = 0; k < row.size(); k++) { + checkRange(prefix + "[" + i + "][" + j + "][" + k + "]", row.get(k), -100, 101); + } + } + } + } + + private static void validateObjectDimension(String prefix, ObjectDimension dimension) { + requireNonNull(prefix, dimension); + checkRange(prefix + ".value", dimension.value(), 1, 256); + checkRange(prefix + ".confidence", dimension.confidence(), 1, 32); + } + + private static void validateClassification(String prefix, List classification) { + requireNonNull(prefix, classification); + int size = classification.size(); + if (size < 1 || size > 8) { + throw new CpmValidationException(prefix + " size out of range [1,8]: " + size); + } + for (int i = 0; i < size; i++) { + validateObjectClassification(prefix + "[" + i + "]", classification.get(i)); + } + } + + private static void validateObjectClassification(String prefix, ObjectClassification classification) { + requireNonNull(prefix, classification); + validateObjectClass(prefix + ".object_class", classification.objectClass()); + checkRange(prefix + ".confidence", classification.confidence(), 1, 101); + } + + private static void validateObjectClass(String prefix, ObjectClass objectClass) { + requireNonNull(prefix, objectClass); + int count = 0; + if (objectClass.vehicle() != null) count++; + if (objectClass.vru() != null) count++; + if (objectClass.group() != null) count++; + if (objectClass.other() != null) count++; + if (count != 1) { + throw new CpmValidationException(prefix + " must contain exactly one class option"); + } + if (objectClass.vehicle() != null) { + checkRange(prefix + ".vehicle", objectClass.vehicle(), 0, 255); + } + if (objectClass.vru() != null) { + validateObjectClassVru(prefix + ".vru", objectClass.vru()); + } + if (objectClass.group() != null) { + validateObjectClassGroup(prefix + ".group", objectClass.group()); + } + if (objectClass.other() != null) { + checkRange(prefix + ".other", objectClass.other(), 0, 255); + } + } + + private static void validateObjectClassVru(String prefix, ObjectClassVru vru) { + requireNonNull(prefix, vru); + int count = 0; + if (vru.pedestrian() != null) count++; + if (vru.bicyclistAndLightVruVehicle() != null) count++; + if (vru.motorcylist() != null) count++; + if (vru.animal() != null) count++; + if (count != 1) { + throw new CpmValidationException(prefix + " must contain exactly one VRU option"); + } + if (vru.pedestrian() != null) { + checkRange(prefix + ".pedestrian", vru.pedestrian(), 0, 15); + } + if (vru.bicyclistAndLightVruVehicle() != null) { + checkRange(prefix + ".bicyclist_and_light_vru_vehicle", vru.bicyclistAndLightVruVehicle(), 0, 15); + } + if (vru.motorcylist() != null) { + checkRange(prefix + ".motorcylist", vru.motorcylist(), 0, 15); + } + if (vru.animal() != null) { + checkRange(prefix + ".animal", vru.animal(), 0, 15); + } + } + + private static void validateObjectClassGroup(String prefix, ObjectClassGroup group) { + requireNonNull(prefix, group); + validateShape(prefix + ".cluster_bounding_box_shape", group.clusterBoundingBoxShape()); + checkRange(prefix + ".cluster_cardinality_size", group.clusterCardinalitySize(), 0, 255); + if (group.clusterId() != null) { + checkRange(prefix + ".cluster_id", group.clusterId(), 0, 255); + } + if (group.clusterProfiles() != null) { + validateClusterProfiles(prefix + ".cluster_profiles", group.clusterProfiles()); + } + } + + private static void validateClusterProfiles(String prefix, ClusterProfiles profiles) { + requireNonNull(prefix, profiles); + } + + private static void validateMapPosition(String prefix, MapPosition mapPosition) { + requireNonNull(prefix, mapPosition); + if (mapPosition.mapReference() != null) { + validateMapReference(prefix + ".map_reference", mapPosition.mapReference()); + } + if (mapPosition.laneId() != null) { + checkRange(prefix + ".lane_id", mapPosition.laneId(), 0, 255); + } + if (mapPosition.connectionId() != null) { + checkRange(prefix + ".connection_id", mapPosition.connectionId(), 0, 255); + } + if (mapPosition.longitudinalLanePosition() != null) { + validateLongitudinalLanePosition(prefix + ".longitudinal_lane_position", mapPosition.longitudinalLanePosition()); + } + } + + private static void validateLongitudinalLanePosition(String prefix, LongitudinalLanePosition position) { + requireNonNull(prefix, position); + checkRange(prefix + ".value", position.value(), 0, 32767); + checkRange(prefix + ".confidence", position.confidence(), 0, 1023); + } + + /* --------------------------------------------------------------------- */ + /* CDD helpers */ + /* --------------------------------------------------------------------- */ + + private static void validateAngle(String prefix, Angle angle) { + requireNonNull(prefix, angle); + checkRange(prefix + ".value", angle.value(), 0, 3601); + checkRange(prefix + ".confidence", angle.confidence(), 1, 127); + } + + private static void validateSpeed(String prefix, Speed speed) { + requireNonNull(prefix, speed); + checkRange(prefix + ".value", speed.value(), 0, 16383); + checkRange(prefix + ".confidence", speed.confidence(), 1, 127); + } + + private static void validateVelocityComponent(String prefix, VelocityComponent component) { + requireNonNull(prefix, component); + checkRange(prefix + ".value", component.value(), -16383, 16383); + checkRange(prefix + ".confidence", component.confidence(), 1, 127); + } + + private static void validateMapReference(String prefix, MapReference reference) { + requireNonNull(prefix, reference); + int count = 0; + if (reference.roadSegment() != null) count++; + if (reference.intersection() != null) count++; + if (count != 1) { + throw new CpmValidationException(prefix + " must contain exactly one map reference option"); + } + if (reference.roadSegment() != null) { + validateRoadSegment(prefix + ".road_segment", reference.roadSegment()); + } + if (reference.intersection() != null) { + validateIntersection(prefix + ".intersection", reference.intersection()); + } + } + + private static void validateRoadSegment(String prefix, RoadSegment segment) { + requireNonNull(prefix, segment); + checkRange(prefix + ".id", segment.id(), 0, 65535); + if (segment.region() != null) { + checkRange(prefix + ".region", segment.region(), 0, 65535); + } + } + + private static void validateIntersection(String prefix, Intersection intersection) { + requireNonNull(prefix, intersection); + checkRange(prefix + ".id", intersection.id(), 0, 65535); + if (intersection.region() != null) { + checkRange(prefix + ".region", intersection.region(), 0, 65535); + } + } + + private static void validateShape(String prefix, Shape shape) { + requireNonNull(prefix, shape); + int count = 0; + if (shape.rectangular() != null) count++; + if (shape.circular() != null) count++; + if (shape.polygonal() != null) count++; + if (shape.elliptical() != null) count++; + if (shape.radial() != null) count++; + if (shape.radialShapes() != null) count++; + if (count != 1) { + throw new CpmValidationException(prefix + " must contain exactly one shape option"); + } + if (shape.rectangular() != null) { + validateRectangular(prefix + ".rectangular", shape.rectangular()); + } + if (shape.circular() != null) { + validateCircular(prefix + ".circular", shape.circular()); + } + if (shape.polygonal() != null) { + validatePolygonal(prefix + ".polygonal", shape.polygonal()); + } + if (shape.elliptical() != null) { + validateElliptical(prefix + ".elliptical", shape.elliptical()); + } + if (shape.radial() != null) { + validateRadial(prefix + ".radial", shape.radial()); + } + if (shape.radialShapes() != null) { + validateRadialShapes(prefix + ".radial_shapes", shape.radialShapes()); + } + } + + private static void validateRectangular(String prefix, Rectangular rectangular) { + requireNonNull(prefix, rectangular); + if (rectangular.centerPoint() != null) { + validateCartesianPosition3d(prefix + ".center_point", rectangular.centerPoint()); + } + checkRange(prefix + ".semi_length", rectangular.semiLength(), 0, 102); + checkRange(prefix + ".semi_breadth", rectangular.semiBreadth(), 0, 102); + if (rectangular.orientation() != null) { + checkRange(prefix + ".orientation", rectangular.orientation(), 0, 3601); + } + if (rectangular.height() != null) { + checkRange(prefix + ".height", rectangular.height(), 0, 4095); + } + } + + private static void validateCircular(String prefix, Circular circular) { + requireNonNull(prefix, circular); + checkRange(prefix + ".radius", circular.radius(), 0, 4095); + if (circular.shapeReferencePoint() != null) { + validateCartesianPosition3d(prefix + ".shape_reference_point", circular.shapeReferencePoint()); + } + if (circular.height() != null) { + checkRange(prefix + ".height", circular.height(), 0, 4095); + } + } + + private static void validatePolygonal(String prefix, Polygonal polygonal) { + requireNonNull(prefix, polygonal); + requireNonNull(prefix + ".polygon", polygonal.polygon()); + for (int i = 0; i < polygonal.polygon().size(); i++) { + validateCartesianPosition3d(prefix + ".polygon[" + i + "]", polygonal.polygon().get(i)); + } + if (polygonal.shapeReferencePoint() != null) { + validateCartesianPosition3d(prefix + ".shape_reference_point", polygonal.shapeReferencePoint()); + } + if (polygonal.height() != null) { + checkRange(prefix + ".height", polygonal.height(), 0, 4095); + } + } + + private static void validateElliptical(String prefix, Elliptical elliptical) { + requireNonNull(prefix, elliptical); + checkRange(prefix + ".semi_major_axis_length", elliptical.semiMajorAxisLength(), 0, 4095); + checkRange(prefix + ".semi_minor_axis_length", elliptical.semiMinorAxisLength(), 0, 4095); + if (elliptical.shapeReferencePoint() != null) { + validateCartesianPosition3d(prefix + ".shape_reference_point", elliptical.shapeReferencePoint()); + } + if (elliptical.orientation() != null) { + checkRange(prefix + ".orientation", elliptical.orientation(), 0, 3601); + } + if (elliptical.height() != null) { + checkRange(prefix + ".height", elliptical.height(), 0, 4095); + } + } + + private static void validateRadial(String prefix, Radial radial) { + requireNonNull(prefix, radial); + checkRange(prefix + ".range", radial.range(), 0, 4095); + checkRange(prefix + ".stationary_horizontal_opening_angle_start", radial.stationaryHorizontalOpeningAngleStart(), 0, 3601); + checkRange(prefix + ".stationary_horizontal_opening_angle_end", radial.stationaryHorizontalOpeningAngleEnd(), 0, 3601); + if (radial.shapeReferencePoint() != null) { + validateCartesianPosition3d(prefix + ".shape_reference_point", radial.shapeReferencePoint()); + } + if (radial.verticalOpeningAngleStart() != null) { + checkRange(prefix + ".vertical_opening_angle_start", radial.verticalOpeningAngleStart(), 0, 3601); + } + if (radial.verticalOpeningAngleEnd() != null) { + checkRange(prefix + ".vertical_opening_angle_end", radial.verticalOpeningAngleEnd(), 0, 3601); + } + } + + private static void validateRadialShapes(String prefix, RadialShapes radialShapes) { + requireNonNull(prefix, radialShapes); + checkRange(prefix + ".ref_point_id", radialShapes.refPointId(), 0, 255); + checkRange(prefix + ".x_coordinate", radialShapes.xCoordinate(), -3094, 1001); + checkRange(prefix + ".y_coordinate", radialShapes.yCoordinate(), -3094, 1001); + if (radialShapes.zCoordinate() != null) { + checkRange(prefix + ".z_coordinate", radialShapes.zCoordinate(), -3094, 1001); + } + requireNonNull(prefix + ".radial_shapes_list", radialShapes.radialShapesList()); + for (int i = 0; i < radialShapes.radialShapesList().size(); i++) { + validateRadial(prefix + ".radial_shapes_list[" + i + "]", radialShapes.radialShapesList().get(i)); + } + } + + private static void validateCartesianPosition3d(String prefix, CartesianPosition3d position) { + requireNonNull(prefix, position); + checkRange(prefix + ".x_coordinate", position.xCoordinate(), -32768, 32767); + checkRange(prefix + ".y_coordinate", position.yCoordinate(), -32768, 32767); + if (position.zCoordinate() != null) { + checkRange(prefix + ".z_coordinate", position.zCoordinate(), -32768, 32767); + } + } + + private static T requireNonNull(String field, T value) { + if (value == null) { + throw new CpmValidationException("Missing mandatory field: " + field); + } + return value; + } + + private static String requireNotBlank(String field, String value) { + if (value == null || value.isBlank()) { + throw new CpmValidationException("Missing mandatory field: " + field); + } + return value; + } + + private static void requireEquals(String field, String actual, String expected) { + if (!Objects.equals(actual, expected)) { + throw new CpmValidationException(field + " must equal '" + expected + "'"); + } + } + + private static void checkRange(String field, long value, long min, long max) { + if (value < min || value > max) { + throw new CpmValidationException(field + " out of range [" + min + ", " + max + "] (actual=" + value + ")"); + } + } + + private static void checkMin(String field, long value, long min) { + if (value < min) { + throw new CpmValidationException(field + " inferior to min [" + min + "] (actual=" + value + ")"); + } + } + + private static void checkStringLength(String field, String value, int min, int max) { + requireNotBlank(field, value); + int len = value.length(); + if (len < min || len > max) { + throw new CpmValidationException(field + " length out of range [" + min + ", " + max + "] (actual=" + len + ")"); + } + } +} diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/roadobjects/RoadSensor.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/roadobjects/RoadSensor.java index 4b1871b91..2ac3390ff 100644 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/roadobjects/RoadSensor.java +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/roadobjects/RoadSensor.java @@ -7,10 +7,14 @@ */ package com.orange.iot3mobility.roadobjects; -import com.orange.iot3mobility.its.json.cpm.CPM; -import com.orange.iot3mobility.its.json.cpm.ClassificationItem; -import com.orange.iot3mobility.its.json.cpm.PerceivedObject; import com.orange.iot3mobility.managers.IoT3RoadSensorCallback; +import com.orange.iot3mobility.messages.EtsiConverter; +import com.orange.iot3mobility.messages.cpm.core.CpmCodec; +import com.orange.iot3mobility.messages.cpm.core.CpmVersion; +import com.orange.iot3mobility.messages.cpm.v121.model.CpmEnvelope121; +import com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer.ObjectClassification; +import com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer.PerceivedObject; +import com.orange.iot3mobility.messages.cpm.v211.model.CpmEnvelope211; import com.orange.iot3mobility.quadkey.LatLng; import com.orange.iot3mobility.Utils; @@ -29,17 +33,18 @@ public class RoadSensor { private LatLng position; private long timestamp; private long zeroObjectsTimestamp; - private CPM cpm; + private CpmCodec.CpmFrame cpmFrame; private final IoT3RoadSensorCallback ioT3RoadSensorCallback; - public RoadSensor(String uuid, LatLng position, CPM cpm, IoT3RoadSensorCallback ioT3RoadSensorCallback) { + public RoadSensor(String uuid, LatLng position, CpmCodec.CpmFrame cpmFrame, IoT3RoadSensorCallback ioT3RoadSensorCallback) { this.uuid = uuid; this.position = position; - this.cpm = cpm; + this.cpmFrame = cpmFrame; this.sensorObjects = new ArrayList<>(); this.sensorObjectMap = new HashMap<>(); this.ioT3RoadSensorCallback = ioT3RoadSensorCallback; updateTimestamp(); + updateSensorObjects(); } public String getUuid() { @@ -54,80 +59,196 @@ public void setPosition(LatLng position) { this.position = position; } - public void setCpm(CPM cpm) { - this.cpm = cpm; + public CpmCodec.CpmFrame getCpmFrame() { + return cpmFrame; } - public CPM getCpm() { - return cpm; + public void setCpmFrame(CpmCodec.CpmFrame cpmFrame) { + this.cpmFrame = cpmFrame; + updateSensorObjects(); } - public void updateSensorObjects(List perceivedObjects) { - if(!perceivedObjects.isEmpty()) { - for(PerceivedObject perceivedObject: perceivedObjects) { - String objectId = uuid + "_" + perceivedObject.getObjectId(); + public void updateSensorObjects() { + if(cpmFrame != null && cpmFrame.version() == CpmVersion.V1_2_1) { + CpmEnvelope121 cpmEnvelope121 = (CpmEnvelope121) cpmFrame.envelope(); + List perceivedObjects = cpmEnvelope121.message().perceivedObjectContainer().perceivedObjects(); - float xOffsetMeters = perceivedObject.getDistanceX() / 100f; - float yOffsetMeters = perceivedObject.getDistanceY() / 100f; + if(!perceivedObjects.isEmpty()) { + for(PerceivedObject perceivedObject: perceivedObjects) { + String objectId = uuid + "_" + perceivedObject.objectId(); - LatLng objectPosition = Utils.pointFromPosition(position, 0, yOffsetMeters); - objectPosition = Utils.pointFromPosition(objectPosition, (90 + 360) % 360, xOffsetMeters); - int objectType = -1; - if(perceivedObject.getClassification() != null) { - for(ClassificationItem classificationItem: perceivedObject.getClassification()) { - if(classificationItem != null) { - objectType = classificationItem.getObjectStationType(); + double xOffsetMeters = EtsiConverter.cpmDistanceMeters(perceivedObject.xDistance()); + double yOffsetMeters = EtsiConverter.cpmDistanceMeters(perceivedObject.yDistance()); + + LatLng objectPosition = Utils.pointFromPosition(position, 0, yOffsetMeters); + objectPosition = Utils.pointFromPosition(objectPosition, (90 + 360) % 360, xOffsetMeters); + + SensorObjectType objectType = SensorObjectType.UNKNOWN; + if(perceivedObject.classification() != null) { + for(ObjectClassification classificationItem: perceivedObject.classification()) { + if(classificationItem != null && classificationItem.objectClass() != null) { + objectType = cpm121ObjectTypeFromObjectClass(classificationItem.objectClass()); + } } } - } - float objectSpeed = perceivedObject.getSpeedMs(); - float objectHeading = perceivedObject.getHeadingDegree(); - int infoQuality = 3; + double objectSpeed = EtsiConverter.cpmDerivedSpeedMetersPerSecond( + perceivedObject.xSpeed(), + perceivedObject.ySpeed()); + double objectHeading = EtsiConverter.cpmDerivedHeadingDegrees( + perceivedObject.xSpeed(), + perceivedObject.ySpeed()); + int infoQuality = 3; + + if(perceivedObject.classification() != null + && !perceivedObject.classification().isEmpty()) { + infoQuality = perceivedObject.classification().get(0).confidence(); + } + + SensorObject sensorObject; + if(sensorObjectMap.containsKey(objectId)) { + // update existing SensorObject + synchronized (sensorObjectMap) { + sensorObject = sensorObjectMap.get(objectId); + if(sensorObject != null) { + sensorObject.updateTimestamp(); + sensorObject.setPosition(objectPosition); + sensorObject.setType(objectType); + sensorObject.setSpeed(objectSpeed); + sensorObject.setHeading(objectHeading); + sensorObject.setInfoQuality(infoQuality); + ioT3RoadSensorCallback.sensorObjectUpdate(sensorObject); + } + } + } else { + // create new SensorObject + sensorObject = new SensorObject(objectId, objectType, objectPosition, objectSpeed, objectHeading, infoQuality); + synchronized (sensorObjects) { + sensorObjects.add(sensorObject); + } + synchronized (sensorObjectMap) { + sensorObjectMap.put(objectId, sensorObject); + } + ioT3RoadSensorCallback.newSensorObject(sensorObject); + } - if(perceivedObject.getClassification() != null - && !perceivedObject.getClassification().isEmpty()) { - infoQuality = perceivedObject.getClassification().get(0).getConfidence(); + if(perceivedObjects.size() != sensorObjects.size() + && !sensorObjects.isEmpty()) { + // remove objects that have not been tracked / detected again + checkAndRemoveExpiredObjects(false); + } + zeroObjectsTimestamp = 0; } + } else { // clear all objects if nothing is received for 1.5 seconds + if(zeroObjectsTimestamp == 0) zeroObjectsTimestamp = System.currentTimeMillis(); + else if(System.currentTimeMillis() - zeroObjectsTimestamp > LIFETIME) checkAndRemoveExpiredObjects(true); + } + } else if (cpmFrame != null && cpmFrame.version() == CpmVersion.V2_1_1) { + CpmEnvelope211 cpmEnvelope211 = (CpmEnvelope211) cpmFrame.envelope(); + List perceivedObjects = new ArrayList<>(); + if (cpmEnvelope211.message().perceivedObjectContainer() != null) { + perceivedObjects = cpmEnvelope211.message().perceivedObjectContainer().perceivedObjects(); + } - SensorObject sensorObject; - if(sensorObjectMap.containsKey(objectId)) { - // update existing SensorObject - synchronized (sensorObjectMap) { - sensorObject = sensorObjectMap.get(objectId); - if(sensorObject != null) { - sensorObject.updateTimestamp(); - sensorObject.setPosition(objectPosition); - sensorObject.setType(objectType); - sensorObject.setSpeed(objectSpeed); - sensorObject.setHeading(objectHeading); - sensorObject.setInfoQuality(infoQuality); - ioT3RoadSensorCallback.sensorObjectUpdate(sensorObject); + if(!perceivedObjects.isEmpty()) { + for (int index = 0; index < perceivedObjects.size(); index++) { + com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer.PerceivedObject perceivedObject = perceivedObjects.get(index); + Integer perceivedObjectId = perceivedObject.objectId(); + String objectId = uuid + "_" + (perceivedObjectId != null ? perceivedObjectId : index); + + if (perceivedObject.position() == null + || perceivedObject.position().xCoordinate() == null + || perceivedObject.position().yCoordinate() == null) { + continue; + } + + int xDistance = perceivedObject.position().xCoordinate().value(); + int yDistance = perceivedObject.position().yCoordinate().value(); + double xOffsetMeters = EtsiConverter.cpmDistanceMeters(xDistance); + double yOffsetMeters = EtsiConverter.cpmDistanceMeters(yDistance); + + LatLng objectPosition = Utils.pointFromPosition(position, 0, yOffsetMeters); + objectPosition = Utils.pointFromPosition(objectPosition, (90 + 360) % 360, xOffsetMeters); + + SensorObjectType objectType = SensorObjectType.UNKNOWN; + if(perceivedObject.classification() != null) { + for(com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer.ObjectClassification classificationItem: perceivedObject.classification()) { + if(classificationItem != null && classificationItem.objectClass() != null) { + objectType = cpm211ObjectTypeFromObjectClass(classificationItem.objectClass()); + } } } - } else { - // create new SensorObject - sensorObject = new SensorObject(objectId, objectType, objectPosition, objectSpeed, objectHeading, infoQuality); - synchronized (sensorObjects) { - sensorObjects.add(sensorObject); + + double objectSpeed = 0.0; + double objectHeading = 0.0; + if (perceivedObject.velocity() != null) { + if (perceivedObject.velocity().cartesianVelocity() != null + && perceivedObject.velocity().cartesianVelocity().xVelocity() != null + && perceivedObject.velocity().cartesianVelocity().yVelocity() != null) { + int xSpeed = perceivedObject.velocity().cartesianVelocity().xVelocity().value(); + int ySpeed = perceivedObject.velocity().cartesianVelocity().yVelocity().value(); + objectSpeed = EtsiConverter.cpmDerivedSpeedMetersPerSecond(xSpeed, ySpeed); + objectHeading = EtsiConverter.cpmDerivedHeadingDegrees(xSpeed, ySpeed); + } else if (perceivedObject.velocity().polarVelocity() != null) { + if (perceivedObject.velocity().polarVelocity().velocityMagnitude() != null) { + objectSpeed = EtsiConverter.cpmSpeedMetersPerSecond( + perceivedObject.velocity().polarVelocity().velocityMagnitude().value()); + } + if (perceivedObject.velocity().polarVelocity().velocityDirection() != null) { + objectHeading = EtsiConverter.cpmAngleDegrees( + perceivedObject.velocity().polarVelocity().velocityDirection().value()); + if (Double.isNaN(objectHeading)) { + objectHeading = 0.0; + } + } + } } - synchronized (sensorObjectMap) { - sensorObjectMap.put(objectId, sensorObject); + + int infoQuality = 3; + if(perceivedObject.classification() != null + && !perceivedObject.classification().isEmpty()) { + infoQuality = perceivedObject.classification().get(0).confidence(); + } + + SensorObject sensorObject; + if(sensorObjectMap.containsKey(objectId)) { + // update existing SensorObject + synchronized (sensorObjectMap) { + sensorObject = sensorObjectMap.get(objectId); + if(sensorObject != null) { + sensorObject.updateTimestamp(); + sensorObject.setPosition(objectPosition); + sensorObject.setType(objectType); + sensorObject.setSpeed(objectSpeed); + sensorObject.setHeading(objectHeading); + sensorObject.setInfoQuality(infoQuality); + ioT3RoadSensorCallback.sensorObjectUpdate(sensorObject); + } + } + } else { + // create new SensorObject + sensorObject = new SensorObject(objectId, objectType, objectPosition, objectSpeed, objectHeading, infoQuality); + synchronized (sensorObjects) { + sensorObjects.add(sensorObject); + } + synchronized (sensorObjectMap) { + sensorObjectMap.put(objectId, sensorObject); + } + ioT3RoadSensorCallback.newSensorObject(sensorObject); } - ioT3RoadSensorCallback.newSensorObject(sensorObject); - } - if(perceivedObjects.size() != sensorObjects.size() - && !sensorObjects.isEmpty()) { - // remove objects that have not been tracked / detected again - checkAndRemoveExpiredObjects(false); + if(perceivedObjects.size() != sensorObjects.size() + && !sensorObjects.isEmpty()) { + // remove objects that have not been tracked / detected again + checkAndRemoveExpiredObjects(false); + } + zeroObjectsTimestamp = 0; } - zeroObjectsTimestamp = 0; + } else { // clear all objects if nothing is received for 1.5 seconds + if(zeroObjectsTimestamp == 0) zeroObjectsTimestamp = System.currentTimeMillis(); + else if(System.currentTimeMillis() - zeroObjectsTimestamp > LIFETIME) checkAndRemoveExpiredObjects(true); } - } else { // clear all objects if nothing is received for 1.5 seconds - if(zeroObjectsTimestamp == 0) zeroObjectsTimestamp = System.currentTimeMillis(); - else if(System.currentTimeMillis() - zeroObjectsTimestamp > LIFETIME) checkAndRemoveExpiredObjects(true); } } @@ -164,4 +285,82 @@ public boolean stillLiving() { return System.currentTimeMillis() - timestamp < LIFETIME; } + /* --------------------------------------------------------------------- */ + /* Helper methods */ + /* --------------------------------------------------------------------- */ + + private SensorObjectType cpm121ObjectTypeFromObjectClass( + com.orange.iot3mobility.messages.cpm.v121.model.perceivedobjectcontainer.ObjectClass objectClass) { + if(objectClass.vehicle() != null) { + return switch (objectClass.vehicle()) { + case 1 -> SensorObjectType.PASSENGER_CAR; + case 2 -> SensorObjectType.BUS; + case 3 -> SensorObjectType.LIGHT_TRUCK; + case 4 -> SensorObjectType.HEAVY_TRUCK; + case 5 -> SensorObjectType.TRAILER; + case 6, 8 -> SensorObjectType.SPECIAL_VEHICLES; + case 7 -> SensorObjectType.TRAM; + default -> SensorObjectType.UNKNOWN; + }; + } else if(objectClass.singleVru() != null) { + if(objectClass.singleVru().pedestrian() != null) + return SensorObjectType.PEDESTRIAN; + else if(objectClass.singleVru().bicyclist() != null) + return SensorObjectType.CYCLIST; + else if(objectClass.singleVru().motorcylist() != null) + return SensorObjectType.MOTORCYCLE; + else if(objectClass.singleVru().animal() != null) + return SensorObjectType.ANIMAL; + else return SensorObjectType.UNKNOWN; + } else if(objectClass.vruGroup() != null) { + if(objectClass.vruGroup().groupType().pedestrian()) + return SensorObjectType.PEDESTRIAN_GROUP; + else if(objectClass.vruGroup().groupType().bicyclist()) + return SensorObjectType.CYCLIST_GROUP; + else if(objectClass.vruGroup().groupType().motorcyclist()) + return SensorObjectType.MOTORCYCLE_GROUP; + else if(objectClass.vruGroup().groupType().animal()) + return SensorObjectType.ANIMAL_GROUP; + else return SensorObjectType.UNKNOWN; + } + return SensorObjectType.UNKNOWN; + } + + private SensorObjectType cpm211ObjectTypeFromObjectClass( + com.orange.iot3mobility.messages.cpm.v211.model.perceivedobjectcontainer.ObjectClass objectClass) { + if(objectClass.vehicle() != null) { + return switch (objectClass.vehicle()) { + case 1 -> SensorObjectType.PASSENGER_CAR; + case 2 -> SensorObjectType.BUS; + case 3 -> SensorObjectType.LIGHT_TRUCK; + case 4 -> SensorObjectType.HEAVY_TRUCK; + case 5 -> SensorObjectType.TRAILER; + case 6, 8 -> SensorObjectType.SPECIAL_VEHICLES; + case 7 -> SensorObjectType.TRAM; + default -> SensorObjectType.UNKNOWN; + }; + } else if(objectClass.vru() != null) { + if(objectClass.vru().pedestrian() != null) + return SensorObjectType.PEDESTRIAN; + else if(objectClass.vru().bicyclistAndLightVruVehicle() != null) + return SensorObjectType.CYCLIST; + else if(objectClass.vru().motorcylist() != null) + return SensorObjectType.MOTORCYCLE; + else if(objectClass.vru().animal() != null) + return SensorObjectType.ANIMAL; + else return SensorObjectType.UNKNOWN; + } else if(objectClass.group() != null) { + if(objectClass.group().clusterProfiles().pedestrian()) + return SensorObjectType.PEDESTRIAN_GROUP; + else if(objectClass.group().clusterProfiles().bicyclist()) + return SensorObjectType.CYCLIST_GROUP; + else if(objectClass.group().clusterProfiles().motorcyclist()) + return SensorObjectType.MOTORCYCLE_GROUP; + else if(objectClass.group().clusterProfiles().animal()) + return SensorObjectType.ANIMAL_GROUP; + else return SensorObjectType.UNKNOWN; + } + return SensorObjectType.UNKNOWN; + } + } diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/roadobjects/SensorObject.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/roadobjects/SensorObject.java index 0393b40a2..a156207a9 100644 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/roadobjects/SensorObject.java +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/roadobjects/SensorObject.java @@ -14,18 +14,14 @@ public class SensorObject { private static final int LIFETIME = 1500; // 1.5 seconds private final String uuid; - private int type; + private SensorObjectType type; private LatLng position; - private float speed; // m/s - private float heading; // degree + private double speed; // m/s + private double heading; // degree private int infoQuality; private long timestamp; - public SensorObject(String uuid, int type, LatLng position, float speed, float heading) { - this(uuid, type, position, speed, heading, 3); - } - - public SensorObject(String uuid, int type, LatLng position, float speed, float heading, int infoQuality) { + public SensorObject(String uuid, SensorObjectType type, LatLng position, double speed, double heading, int infoQuality) { this.uuid = uuid; this.type = type; this.position = position; @@ -39,11 +35,11 @@ public String getUuid() { return uuid; } - public int getType() { + public SensorObjectType getType() { return type; } - public void setType(int type) { + public void setType(SensorObjectType type) { this.type = type; } @@ -55,23 +51,23 @@ public void setPosition(LatLng position) { this.position = position; } - public float getSpeed() { + public double getSpeed() { return speed; } - public float getSpeedKmh() { + public double getSpeedKmh() { return speed * 3.6f; } - public void setSpeed(float speed) { + public void setSpeed(double speed) { this.speed = speed; } - public float getHeading() { + public double getHeading() { return heading; } - public void setHeading(float heading) { + public void setHeading(double heading) { this.heading = heading; } diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/roadobjects/SensorObjectType.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/roadobjects/SensorObjectType.java new file mode 100644 index 000000000..c93e3b601 --- /dev/null +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/roadobjects/SensorObjectType.java @@ -0,0 +1,25 @@ +package com.orange.iot3mobility.roadobjects; + +/** + * Inspired from ETSI StationType and ETSI CPM ObjectClass + */ +public enum SensorObjectType { + UNKNOWN, + PEDESTRIAN, + CYCLIST, + MOPED, + MOTORCYCLE, + PASSENGER_CAR, + BUS, + LIGHT_TRUCK, + HEAVY_TRUCK, + TRAILER, + SPECIAL_VEHICLES, + TRAM, + PEDESTRIAN_GROUP, + CYCLIST_GROUP, + MOTORCYCLE_GROUP, + ANIMAL, + ANIMAL_GROUP, + ROAD_SIDE_UNIT +}