Vanetza
 
Loading...
Searching...
No Matches
cam_functions.cpp
1#include <vanetza/asn1/cam.hpp>
2#include <vanetza/facilities/cam_functions.hpp>
3#include <vanetza/facilities/path_history.hpp>
4#include <vanetza/geonet/areas.hpp>
5#include <boost/algorithm/clamp.hpp>
6#include <boost/date_time/posix_time/posix_time.hpp>
7#include <boost/math/constants/constants.hpp>
8#include <boost/units/cmath.hpp>
9#include <boost/units/systems/si/prefixes.hpp>
10#include <boost/units/systems/angle/degrees.hpp>
11#include <algorithm>
12#include <limits>
13#undef min
14
15namespace vanetza
16{
17namespace facilities
18{
19
20using vanetza::units::Angle;
21
22static const auto microdegree = units::degree * units::si::micro;
23
24// TODO: C2C-CC BSP allows up to 500m history for CAMs, we provide just minimal required history
25void copy(const facilities::PathHistory& ph, BasicVehicleContainerLowFrequency& container)
26{
27 static const std::size_t scMaxPathPoints = 23;
28 static const boost::posix_time::time_duration scMaxDeltaTime = boost::posix_time::millisec(655350);
29 static const auto scMicrodegree = microdegree;
30
31 const auto& concise_points = ph.getConcisePoints();
32 const facilities::PathPoint& ref = ph.getReferencePoint();
33 std::size_t path_points = 0;
34
35 for (const PathPoint& point : concise_points) {
36 auto delta_time = ref.time - point.time; // positive: point is in past
37 auto delta_latitude = point.latitude - ref.latitude; // positive: point is north
38 auto delta_longitude = point.longitude - ref.longitude; // positive: point is east
39
40 while (!delta_time.is_negative() && path_points < scMaxPathPoints) {
41 ::PathPoint* path_point = asn1::allocate<::PathPoint>();
42 path_point->pathDeltaTime = asn1::allocate<PathDeltaTime_t>();
43 *(path_point->pathDeltaTime) = std::min(delta_time, scMaxDeltaTime).total_milliseconds() /
44 10 * PathDeltaTime::PathDeltaTime_tenMilliSecondsInPast;
45 path_point->pathPosition.deltaLatitude = (delta_latitude / scMicrodegree).value() *
46 DeltaLatitude::DeltaLatitude_oneMicrodegreeNorth;
47 path_point->pathPosition.deltaLongitude = (delta_longitude / scMicrodegree).value() *
48 DeltaLongitude::DeltaLongitude_oneMicrodegreeEast;
49 path_point->pathPosition.deltaAltitude = DeltaAltitude::DeltaAltitude_unavailable;
50
51 ASN_SEQUENCE_ADD(&container.pathHistory, path_point);
52
53 delta_time -= scMaxDeltaTime;
54 ++path_points;
55 }
56 }
57}
58
59bool similar_heading(const Heading& a, const Heading& b, Angle limit)
60{
61 // HeadingValues are tenth of degree (900 equals 90 degree east)
62 static_assert(HeadingValue_wgs84East == 900, "HeadingValue interpretation fails");
63
64 bool result = false;
65 if (is_available(a) && is_available(b)) {
66 using vanetza::units::degree;
67 const Angle angle_a { a.headingValue / 10.0 * degree };
68 const Angle angle_b { b.headingValue / 10.0 * degree };
69 result = similar_heading(angle_a, angle_b, limit);
70 }
71
72 return result;
73}
74
75bool similar_heading(const Heading& a, Angle b, Angle limit)
76{
77 bool result = false;
78 if (is_available(a)) {
79 using vanetza::units::degree;
80 result = similar_heading(Angle { a.headingValue / 10.0 * degree}, b, limit);
81 }
82 return result;
83}
84
85bool similar_heading(Angle a, Angle b, Angle limit)
86{
87 using namespace boost::units;
88 using boost::math::double_constants::pi;
89
90 static const Angle full_circle = 2.0 * pi * si::radian;
91 const Angle abs_diff = fmod(abs(a - b), full_circle);
92 return abs_diff <= limit || abs_diff >= full_circle - limit;
93}
94
95units::Length distance(const ReferencePosition_t& a, const ReferencePosition_t& b)
96{
97 using geonet::GeodeticPosition;
98 using units::GeoAngle;
99
100 auto length = units::Length::from_value(std::numeric_limits<double>::quiet_NaN());
101 if (is_available(a) && is_available(b)) {
102 GeodeticPosition geo_a {
103 GeoAngle { a.latitude / Latitude_oneMicrodegreeNorth * microdegree },
104 GeoAngle { a.longitude / Longitude_oneMicrodegreeEast * microdegree }
105 };
106 GeodeticPosition geo_b {
107 GeoAngle { b.latitude / Latitude_oneMicrodegreeNorth * microdegree },
108 GeoAngle { b.longitude / Longitude_oneMicrodegreeEast * microdegree }
109 };
110 length = geonet::distance(geo_a, geo_b);
111 }
112 return length;
113}
114
115units::Length distance(const ReferencePosition_t& a, units::GeoAngle lat, units::GeoAngle lon)
116{
117 using geonet::GeodeticPosition;
118 using units::GeoAngle;
119
120 auto length = units::Length::from_value(std::numeric_limits<double>::quiet_NaN());
121 if (is_available(a)) {
122 GeodeticPosition geo_a {
123 GeoAngle { a.latitude / Latitude_oneMicrodegreeNorth * microdegree },
124 GeoAngle { a.longitude / Longitude_oneMicrodegreeEast * microdegree }
125 };
126 GeodeticPosition geo_b { lat, lon };
127 length = geonet::distance(geo_a, geo_b);
128 }
129 return length;
130}
131
132bool is_available(const Heading& hd)
133{
134 return hd.headingValue != HeadingValue_unavailable;
135}
136
137bool is_available(const ReferencePosition& pos)
138{
139 return pos.latitude != Latitude_unavailable && pos.longitude != Longitude_unavailable;
140}
141
142
143template<typename T, typename U>
144long round(const boost::units::quantity<T>& q, const U& u)
145{
146 boost::units::quantity<U> v { q };
147 return std::round(v.value());
148}
149
150void copy(const PositionFix& position, ReferencePosition& reference_position) {
151 reference_position.longitude = round(position.longitude, microdegree) * Longitude_oneMicrodegreeEast;
152 reference_position.latitude = round(position.latitude, microdegree) * Latitude_oneMicrodegreeNorth;
153 reference_position.positionConfidenceEllipse.semiMajorOrientation = HeadingValue_unavailable;
154 reference_position.positionConfidenceEllipse.semiMajorConfidence = SemiAxisLength_unavailable;
155 reference_position.positionConfidenceEllipse.semiMinorConfidence = SemiAxisLength_unavailable;
156 if (position.altitude) {
157 reference_position.altitude.altitudeValue = to_altitude_value(position.altitude->value());
158 reference_position.altitude.altitudeConfidence = to_altitude_confidence(position.altitude->confidence());
159 } else {
160 reference_position.altitude.altitudeValue = AltitudeValue_unavailable;
161 reference_position.altitude.altitudeConfidence = AltitudeConfidence_unavailable;
162 }
163}
164
165AltitudeConfidence_t to_altitude_confidence(units::Length confidence)
166{
167 const double alt_con = confidence / units::si::meter;
168
169 if (alt_con < 0 || std::isnan(alt_con)) {
170 return AltitudeConfidence_unavailable;
171 } else if (alt_con <= 0.01) {
172 return AltitudeConfidence_alt_000_01;
173 } else if (alt_con <= 0.02) {
174 return AltitudeConfidence_alt_000_02;
175 } else if (alt_con <= 0.05) {
176 return AltitudeConfidence_alt_000_05;
177 } else if (alt_con <= 0.1) {
178 return AltitudeConfidence_alt_000_10;
179 } else if (alt_con <= 0.2) {
180 return AltitudeConfidence_alt_000_20;
181 } else if (alt_con <= 0.5) {
182 return AltitudeConfidence_alt_000_50;
183 } else if (alt_con <= 1.0) {
184 return AltitudeConfidence_alt_001_00;
185 } else if (alt_con <= 2.0) {
186 return AltitudeConfidence_alt_002_00;
187 } else if (alt_con <= 5.0) {
188 return AltitudeConfidence_alt_005_00;
189 } else if (alt_con <= 10.0) {
190 return AltitudeConfidence_alt_010_00;
191 } else if (alt_con <= 20.0) {
192 return AltitudeConfidence_alt_020_00;
193 } else if (alt_con <= 50.0) {
194 return AltitudeConfidence_alt_050_00;
195 } else if (alt_con <= 100.0) {
196 return AltitudeConfidence_alt_100_00;
197 } else if (alt_con <= 200.0) {
198 return AltitudeConfidence_alt_200_00;
199 } else {
200 return AltitudeConfidence_outOfRange;
201 }
202}
203
204AltitudeValue_t to_altitude_value(units::Length alt)
205{
206 using boost::units::isnan;
207
208 if (!isnan(alt)) {
209 alt = boost::algorithm::clamp(alt, -1000.0 * units::si::meter, 8000.0 * units::si::meter);
210 return AltitudeValue_oneCentimeter * 100.0 * (alt / units::si::meter);
211 } else {
212 return AltitudeValue_unavailable;
213 }
214}
215
216bool check_service_specific_permissions(const asn1::Cam& cam, security::CamPermissions ssp)
217{
218 using security::CamPermission;
219 using security::CamPermissions;
220
221 CamPermissions required_permissions;
222 const CamParameters_t& params = cam->cam.camParameters;
223
224 if (params.highFrequencyContainer.present == HighFrequencyContainer_PR_rsuContainerHighFrequency) {
225 const RSUContainerHighFrequency_t& rsu = params.highFrequencyContainer.choice.rsuContainerHighFrequency;
226 if (rsu.protectedCommunicationZonesRSU) {
227 required_permissions.add(CamPermission::CEN_DSRC_Tolling_Zone);
228 }
229 }
230
231 if (const SpecialVehicleContainer_t* special = params.specialVehicleContainer) {
232 const EmergencyContainer_t* emergency = nullptr;
233 const SafetyCarContainer_t* safety = nullptr;
234 const RoadWorksContainerBasic_t* roadworks = nullptr;
235
236 switch (special->present) {
237 case SpecialVehicleContainer_PR_publicTransportContainer:
238 required_permissions.add(CamPermission::Public_Transport);
239 break;
240 case SpecialVehicleContainer_PR_specialTransportContainer:
241 required_permissions.add(CamPermission::Special_Transport);
242 break;
243 case SpecialVehicleContainer_PR_dangerousGoodsContainer:
244 required_permissions.add(CamPermission::Dangerous_Goods);
245 break;
246 case SpecialVehicleContainer_PR_roadWorksContainerBasic:
247 required_permissions.add(CamPermission::Roadwork);
248 roadworks = &special->choice.roadWorksContainerBasic;
249 break;
250 case SpecialVehicleContainer_PR_rescueContainer:
251 required_permissions.add(CamPermission::Rescue);
252 break;
253 case SpecialVehicleContainer_PR_emergencyContainer:
254 required_permissions.add(CamPermission::Emergency);
255 emergency = &special->choice.emergencyContainer;
256 break;
257 case SpecialVehicleContainer_PR_safetyCarContainer:
258 required_permissions.add(CamPermission::Safety_Car);
259 safety = &special->choice.safetyCarContainer;
260 break;
261 case SpecialVehicleContainer_PR_NOTHING:
262 default:
263 break;
264 }
265
266 if (emergency && emergency->emergencyPriority && emergency->emergencyPriority->size == 1) {
267 // testing bit strings from asn1c is such a mess...
268 assert(emergency->emergencyPriority->buf);
269 uint8_t bits = *emergency->emergencyPriority->buf;
270 if (bits & (1 << (7 - EmergencyPriority_requestForRightOfWay))) {
271 required_permissions.add(CamPermission::Request_For_Right_Of_Way);
272 }
273 if (bits & (1 << (7 - EmergencyPriority_requestForFreeCrossingAtATrafficLight))) {
274 required_permissions.add(CamPermission::Request_For_Free_Crossing_At_Traffic_Light);
275 }
276 }
277
278 if (roadworks && roadworks->closedLanes) {
279 required_permissions.add(CamPermission::Closed_Lanes);
280 }
281
282 if (safety && safety->trafficRule) {
283 switch (*safety->trafficRule) {
284 case TrafficRule_noPassing:
285 required_permissions.add(CamPermission::No_Passing);
286 break;
287 case TrafficRule_noPassingForTrucks:
288 required_permissions.add(CamPermission::No_Passing_For_Trucks);
289 break;
290 default:
291 break;
292 }
293 }
294
295 if (safety && safety->speedLimit) {
296 required_permissions.add(CamPermission::Speed_Limit);
297 }
298 }
299
300 return ssp.has(required_permissions);
301}
302
303void print_indented(std::ostream& os, const asn1::Cam& message, const std::string& indent, unsigned level)
304{
305 auto prefix = [&](const char* field) -> std::ostream& {
306 for (unsigned i = 0; i < level; ++i) {
307 os << indent;
308 }
309 os << field << ": ";
310 return os;
311 };
312
313 const ItsPduHeader_t& header = message->header;
314 prefix("ITS PDU Header") << "\n";
315 ++level;
316 prefix("Protocol Version") << header.protocolVersion << "\n";
317 prefix("Message ID") << header.messageID << "\n";
318 prefix("Station ID") << header.stationID << "\n";
319 --level;
320
321 const CoopAwareness_t& cam = message->cam;
322 prefix("CoopAwarensess") << "\n";
323 ++level;
324 prefix("Generation Delta Time") << cam.generationDeltaTime << "\n";
325
326 prefix("Basic Container") << "\n";
327 ++level;
328 const BasicContainer_t& basic = cam.camParameters.basicContainer;
329 prefix("Station Type") << basic.stationType << "\n";
330 prefix("Reference Position") << "\n";
331 ++level;
332 prefix("Longitude") << basic.referencePosition.longitude << "\n";
333 prefix("Latitude") << basic.referencePosition.latitude << "\n";
334 prefix("Semi Major Orientation") << basic.referencePosition.positionConfidenceEllipse.semiMajorOrientation << "\n";
335 prefix("Semi Major Confidence") << basic.referencePosition.positionConfidenceEllipse.semiMajorConfidence << "\n";
336 prefix("Semi Minor Confidence") << basic.referencePosition.positionConfidenceEllipse.semiMinorConfidence << "\n";
337 prefix("Altitude [Confidence]") << basic.referencePosition.altitude.altitudeValue
338 << " [" << basic.referencePosition.altitude.altitudeConfidence << "]\n";
339 --level;
340 --level;
341
342 if (cam.camParameters.highFrequencyContainer.present == HighFrequencyContainer_PR_basicVehicleContainerHighFrequency) {
343 prefix("High Frequency Container [Basic Vehicle]") << "\n";
344 ++level;
345 const BasicVehicleContainerHighFrequency& bvc =
346 cam.camParameters.highFrequencyContainer.choice.basicVehicleContainerHighFrequency;
347 prefix("Heading [Confidence]") << bvc.heading.headingValue
348 << " [" << bvc.heading.headingConfidence << "]\n";
349 prefix("Speed [Confidence]") << bvc.speed.speedValue
350 << " [" << bvc.speed.speedConfidence << "]\n";
351 prefix("Drive Direction") << bvc.driveDirection << "\n";
352 prefix("Longitudinal Acceleration [Confidence]") << bvc.longitudinalAcceleration.longitudinalAccelerationValue
353 << " [" << bvc.longitudinalAcceleration.longitudinalAccelerationConfidence << "]\n";
354 prefix("Vehicle Length [Confidence Indication]") << bvc.vehicleLength.vehicleLengthValue
355 << " [" << bvc.vehicleLength.vehicleLengthConfidenceIndication << "]\n";
356 prefix("Vehicle Width") << bvc.vehicleWidth << "\n";
357 prefix("Curvature [Confidence]") << bvc.curvature.curvatureValue
358 << " [" << bvc.curvature.curvatureConfidence << "]\n";
359 prefix("Curvature Calculation Mode") << bvc.curvatureCalculationMode << "\n";
360 prefix("Yaw Rate [Confidence]") << bvc.yawRate.yawRateValue
361 << " [" << bvc.yawRate.yawRateConfidence << "]\n";
362 --level;
363 } else if (cam.camParameters.highFrequencyContainer.present == HighFrequencyContainer_PR_rsuContainerHighFrequency) {
364 prefix("High Frequency Container [RSU]") << "\n";
365 const RSUContainerHighFrequency_t& rsu = cam.camParameters.highFrequencyContainer.choice.rsuContainerHighFrequency;
366 if (nullptr != rsu.protectedCommunicationZonesRSU && nullptr != rsu.protectedCommunicationZonesRSU->list.array) {
367 ++level;
368 int size = rsu.protectedCommunicationZonesRSU->list.count;
369 for (int i = 0; i < size; i++)
370 {
371 prefix("Protected Zone") << "\n";
372 ++level;
373 prefix("Type") << rsu.protectedCommunicationZonesRSU->list.array[i]->protectedZoneType << "\n";
374 if (rsu.protectedCommunicationZonesRSU->list.array[i]->expiryTime
375 && nullptr != rsu.protectedCommunicationZonesRSU->list.array[i]->expiryTime->buf
376 && rsu.protectedCommunicationZonesRSU->list.array[i]->expiryTime->size > 0)
377 prefix("Expiry Time") << (unsigned) rsu.protectedCommunicationZonesRSU->list.array[i]->expiryTime->buf[0] << "\n";
378 prefix("Latitude") << rsu.protectedCommunicationZonesRSU->list.array[i]->protectedZoneLatitude << "\n";
379 prefix("Longitude") << rsu.protectedCommunicationZonesRSU->list.array[i]->protectedZoneLongitude << "\n";
380 if (nullptr != rsu.protectedCommunicationZonesRSU->list.array[i]->protectedZoneRadius)
381 prefix("Radius") << *(rsu.protectedCommunicationZonesRSU->list.array[i]->protectedZoneRadius) << "\n";
382 if (nullptr != rsu.protectedCommunicationZonesRSU->list.array[i]->protectedZoneRadius)
383 prefix("ID") << *(rsu.protectedCommunicationZonesRSU->list.array[i]->protectedZoneID) << "\n";
384 --level;
385 }
386 --level;
387 }
388 } else {
389 prefix("High Frequency Container") << "empty\n";
390 }
391
392 if (nullptr != cam.camParameters.lowFrequencyContainer) {
393 if (cam.camParameters.lowFrequencyContainer->present == LowFrequencyContainer_PR_basicVehicleContainerLowFrequency) {
394 prefix("Low Frequency Container") << "\n";
395 const BasicVehicleContainerLowFrequency_t& lfc =
396 cam.camParameters.lowFrequencyContainer->choice.basicVehicleContainerLowFrequency;
397 ++level;
398 prefix("Vehicle Role") << (lfc.vehicleRole) << "\n";
399
400 if (nullptr != lfc.exteriorLights.buf && lfc.exteriorLights.size > 0)
401 prefix("Exterior Lights") << unsigned(*(lfc.exteriorLights.buf)) << "\n";
402 if (nullptr != lfc.pathHistory.list.array) {
403 int size = lfc.pathHistory.list.count;
404 for (int i = 0; i < size; i++)
405 {
406 prefix("Path history point") << "\n";
407 ++level;
408 prefix("Latitude") << (lfc.pathHistory.list.array[i]->pathPosition.deltaLatitude) << "\n";
409 prefix("Longitude") << (lfc.pathHistory.list.array[i]->pathPosition.deltaLongitude) << "\n";
410 prefix("Altitude") << (lfc.pathHistory.list.array[i]->pathPosition.deltaAltitude) << "\n";
411 if (lfc.pathHistory.list.array[i]->pathDeltaTime)
412 prefix("Delta time") << *(lfc.pathHistory.list.array[i]->pathDeltaTime) << "\n";
413 --level;
414 }
415 }
416 --level;
417 }
418 else // LowFrequencyContainer_PR_NOTHING
419 prefix("Low Frequency Container") << "present but empty" << "\n";
420 }
421 else
422 prefix("Low Frequency Container") << "not present" << "\n";
423
424 if (nullptr != cam.camParameters.specialVehicleContainer) {
425 if (cam.camParameters.specialVehicleContainer->present == SpecialVehicleContainer_PR_publicTransportContainer) {
426 prefix("Special Vehicle Container [Public Transport]") << "\n";
427 PublicTransportContainer_t& ptc = cam.camParameters.specialVehicleContainer->choice.publicTransportContainer;
428 ++level;
429 prefix("Embarkation Status") << ptc.embarkationStatus << "\n";
430 if (ptc.ptActivation) {
431 prefix("PT Activation Type") << ptc.ptActivation->ptActivationType << "\n";
432 if (0 != ptc.ptActivation->ptActivationData.size) {
433 int size = ptc.ptActivation->ptActivationData.size;
434 for (int i = 0; i < ptc.ptActivation->ptActivationData.size; i++)
435 prefix("PT Activation Data") << (unsigned) ptc.ptActivation->ptActivationData.buf[i] << "\n";
436 }
437 }
438 --level;
439 } else if (cam.camParameters.specialVehicleContainer->present == SpecialVehicleContainer_PR_specialTransportContainer) {
440 prefix("Special Vehicle Container [Special Transport]") << "\n";
441 SpecialTransportContainer_t& stc = cam.camParameters.specialVehicleContainer->choice.specialTransportContainer;
442 ++level;
443 if (nullptr != stc.specialTransportType.buf && stc.specialTransportType.size > 0)
444 prefix("Type") << (unsigned) stc.specialTransportType.buf[0] << "\n";
445 if (nullptr != stc.lightBarSirenInUse.buf && stc.lightBarSirenInUse.size > 0)
446 prefix("Light Bar Siren in Use") << (unsigned) stc.lightBarSirenInUse.buf[0] << "\n";
447 --level;
448 } else if (cam.camParameters.specialVehicleContainer->present == SpecialVehicleContainer_PR_dangerousGoodsContainer) {
449 prefix("Special Vehicle Container [Dangerous Goods]") << "\n";
450 DangerousGoodsContainer_t& dgc = cam.camParameters.specialVehicleContainer->choice.dangerousGoodsContainer;
451 ++level;
452 prefix("Dangerous Goods Basic Type") << (unsigned)dgc.dangerousGoodsBasic << "\n";
453 --level;
454 } else if (cam.camParameters.specialVehicleContainer->present == SpecialVehicleContainer_PR_roadWorksContainerBasic) {
455 prefix("Special Vehicle Container [Road Works]") << "\n";
456 RoadWorksContainerBasic_t& rwc = cam.camParameters.specialVehicleContainer->choice.roadWorksContainerBasic;
457 ++level;
458 if (nullptr != rwc.roadworksSubCauseCode)
459 prefix("Sub Cause Code") << *(rwc.roadworksSubCauseCode) << "\n";
460 if (nullptr != rwc.lightBarSirenInUse.buf && rwc.lightBarSirenInUse.size > 0)
461 prefix("Light Bar Siren in Use") << (unsigned) rwc.lightBarSirenInUse.buf[0] << "\n";
462 if (nullptr != rwc.closedLanes) {
463 if (rwc.closedLanes->innerhardShoulderStatus)
464 prefix("Inner Hard Shoulder Status") << *(rwc.closedLanes->innerhardShoulderStatus) << "\n";
465 if (rwc.closedLanes->outerhardShoulderStatus)
466 prefix("Outer Hard Shoulder Status") << *(rwc.closedLanes->outerhardShoulderStatus) << "\n";
467 if (rwc.closedLanes->drivingLaneStatus && nullptr != rwc.closedLanes->drivingLaneStatus->buf
468 && rwc.closedLanes->drivingLaneStatus->size > 0)
469 prefix("Driving Lane Status") << (unsigned) rwc.closedLanes->drivingLaneStatus->buf[0] << "\n";
470 }
471 --level;
472 } else if (cam.camParameters.specialVehicleContainer->present == SpecialVehicleContainer_PR_rescueContainer) {
473 prefix("Special Vehicle Container [Rescue]") << "\n";
474 RescueContainer_t& rc = cam.camParameters.specialVehicleContainer->choice.rescueContainer;
475 ++level;
476 if (nullptr != rc.lightBarSirenInUse.buf && rc.lightBarSirenInUse.size > 0)
477 prefix("Light Bar Siren in Use") << (unsigned) rc.lightBarSirenInUse.buf[0] << "\n";
478 --level;
479 } else if (cam.camParameters.specialVehicleContainer->present == SpecialVehicleContainer_PR_emergencyContainer) {
480 prefix("Special Vehicle Container [Emergency]") << "\n";
481 EmergencyContainer_t& ec = cam.camParameters.specialVehicleContainer->choice.emergencyContainer;
482 ++level;
483 if (nullptr != ec.lightBarSirenInUse.buf && ec.lightBarSirenInUse.size > 0)
484 prefix("Light Bar Siren in Use") << (unsigned) ec.lightBarSirenInUse.buf[0] << "\n";
485 if (nullptr != ec.incidentIndication) {
486 prefix("Incident Indication Cause Code") << ec.incidentIndication->causeCode << "\n";
487 prefix("Incident Indication Sub Cause Code") << ec.incidentIndication->subCauseCode << "\n";
488 }
489 if (nullptr != ec.emergencyPriority && nullptr != ec.emergencyPriority->buf
490 && ec.emergencyPriority->size > 0) {
491 prefix("Emergency Priority") << (unsigned) ec.emergencyPriority->buf[0] << "\n";
492 }
493 --level;
494 } else if (cam.camParameters.specialVehicleContainer->present == SpecialVehicleContainer_PR_safetyCarContainer) {
495 prefix("Special Vehicle Container [Safety Car]") << "\n";
496 SafetyCarContainer_t& sc = cam.camParameters.specialVehicleContainer->choice.safetyCarContainer;
497 ++level;
498 if (nullptr != sc.lightBarSirenInUse.buf && sc.lightBarSirenInUse.size > 0)
499 prefix("Light Bar Siren in Use") << (unsigned) sc.lightBarSirenInUse.buf[0] << "\n";
500 if (nullptr != sc.incidentIndication) {
501 prefix("Incident Indication Cause Code") << sc.incidentIndication->causeCode << "\n";
502 prefix("Incident Indication Sub Cause Code") << sc.incidentIndication->subCauseCode << "\n";
503 }
504 if (nullptr != sc.trafficRule) {
505 prefix("Traffic Rule") << *(sc.trafficRule) << "\n";
506 }
507 if (nullptr != sc.speedLimit) {
508 prefix("Speed Limit") << *(sc.speedLimit) << "\n";
509 }
510 --level;
511 }
512 else // SpecialVehicleContainer_PR_NOTHING
513 prefix("Special Vehicle Container") << ("present but empty") << "\n";
514 }
515 else
516 prefix("Special Vehicle Container") << "not present" << "\n";
517
518 --level;
519}
520
521} // namespace facilities
522} // namespace vanetza