Skip to main content

c_its_parser/standards/
conversions.rs

1// Copyright (c) 2025 consider it GmbH
2
3//! Conversions between ETSI ASN.1 values and common (SI) units
4//!
5//! Take a look at the individual data types in [`crate::standards`] to discover available conversion methods and initialization functions.
6
7pub const MPS_TO_KMH_FACTOR: f32 = 3.6;
8
9#[cfg(feature = "_cdd_1_3_1_1")]
10use crate::standards::cdd_1_3_1_1;
11#[cfg(feature = "_cdd_2_2_1")]
12use crate::standards::cdd_2_2_1;
13#[cfg(feature = "cpm_1")]
14use crate::standards::cpm_1;
15#[cfg(feature = "_dsrc_2_2_1")]
16use crate::standards::dsrc_2_2_1;
17
18/// Create conversions for ETSI type `t` and some "unavailable" value
19macro_rules! latlon_to_deg {
20    ($t:ty, $unavailable:expr) => {
21        impl $t {
22            /// convert ETSI Latitude/ Longitude to degrees
23            #[must_use]
24            pub fn as_deg(&self) -> f64 {
25                f64::from(self.0) / 10_000_000.
26            }
27
28            /// convert ETSI Latitude/ Longitude to degrees or `None` if "unavailable"
29            #[must_use]
30            pub fn try_as_deg(&self) -> Option<f64> {
31                if self.is_unavailable() {
32                    None
33                } else {
34                    Some(self.as_deg())
35                }
36            }
37
38            /// convert ETSI Latitude/ Longitude to degrees
39            #[must_use]
40            pub fn from_deg(other: f64) -> Self {
41                Self((other * 10_000_000.) as i32)
42            }
43
44            /// create ETSI type with "unavailable" value
45            pub fn unavailable() -> Self {
46                Self($unavailable)
47            }
48
49            /// determines if the ETSI value is special "unavailable" value
50            pub fn is_unavailable(&self) -> bool {
51                self.0 == $unavailable
52            }
53        }
54    };
55}
56
57#[cfg(feature = "_cdd_1_3_1_1")]
58latlon_to_deg!(cdd_1_3_1_1::its_container::Longitude, 1_800_000_001);
59#[cfg(feature = "_cdd_1_3_1_1")]
60latlon_to_deg!(cdd_1_3_1_1::its_container::Latitude, 900_000_001);
61#[cfg(feature = "_cdd_2_2_1")]
62latlon_to_deg!(cdd_2_2_1::etsi_its_cdd::Longitude, 1_800_000_001);
63#[cfg(feature = "_cdd_2_2_1")]
64latlon_to_deg!(cdd_2_2_1::etsi_its_cdd::Latitude, 900_000_001);
65
66#[cfg(feature = "_cdd_1_3_1_1")]
67latlon_to_deg!(cdd_1_3_1_1::its_container::DeltaLongitude, 131_072);
68#[cfg(feature = "_cdd_1_3_1_1")]
69latlon_to_deg!(cdd_1_3_1_1::its_container::DeltaLatitude, 131_072);
70#[cfg(feature = "_cdd_2_2_1")]
71latlon_to_deg!(cdd_2_2_1::etsi_its_cdd::DeltaLongitude, 131_072);
72#[cfg(feature = "_cdd_2_2_1")]
73latlon_to_deg!(cdd_2_2_1::etsi_its_cdd::DeltaLatitude, 131_072);
74
75/// Create conversions for ETSI type `t` (which has underlying data type `tt`) with conversion factor `conv`
76#[cfg(feature = "cpm_1")]
77macro_rules! etsi_to_meters {
78    ($t:ty, $tt:ty, $conv:expr) => {
79        impl $t {
80            /// convert ETSI data to meters
81            #[must_use]
82            pub fn as_meters(&self) -> f32 {
83                self.0 as f32 / $conv
84            }
85
86            /// create ETSI data from meters
87            ///
88            /// # Errors
89            /// human-readable string when input value is out of bounds
90            pub fn from_meters(value: f32) -> Result<Self, alloc::string::String> {
91                use rasn::AsnType;
92
93                #[allow(clippy::cast_possible_truncation)]
94                let etsi_val = (value * $conv) as $tt;
95
96                if let Some(constraints) = Self::CONSTRAINTS.value() {
97                    if !constraints.constraint.in_bound(&etsi_val) {
98                        return Err(alloc::format!("Value out of bounds"));
99                    }
100                }
101
102                Ok(Self(etsi_val))
103            }
104        }
105
106        impl From<&$t> for f32 {
107            fn from(other: &$t) -> f32 {
108                other.as_meters()
109            }
110        }
111        impl From<$t> for f32 {
112            fn from(other: $t) -> f32 {
113                other.as_meters()
114            }
115        }
116
117        impl TryFrom<f32> for $t {
118            type Error = alloc::string::String;
119
120            fn try_from(value: f32) -> Result<Self, Self::Error> {
121                Self::from_meters(value)
122            }
123        }
124    };
125}
126
127/// Create conversions for ETSI type `t` (which has underlying data type `tt`) with conversion factor `conv` and some "unavailable" value
128#[cfg(any(
129    feature = "_cdd_1_3_1_1",
130    feature = "_cdd_2_2_1",
131    feature = "_dsrc_2_2_1",
132    feature = "cpm_1"
133))]
134macro_rules! etsi_to_meters_unavailable {
135    ($t:ty, $tt:ty, $conv:expr, $unavailable:expr) => {
136        impl $t {
137            /// convert ETSI data to meters
138            #[must_use]
139            pub fn as_meters(&self) -> f32 {
140                self.0 as f32 / $conv
141            }
142
143            /// convert ETSI data to meters or `None` if "unavailable"
144            #[must_use]
145            pub fn try_as_meters(&self) -> Option<f32> {
146                if self.is_unavailable() {
147                    None
148                } else {
149                    Some(self.as_meters())
150                }
151            }
152
153            /// create ETSI data from meters
154            ///
155            /// # Errors
156            /// human-readable string when input value is out of bounds
157            pub fn from_meters(value: f32) -> Result<Self, alloc::string::String> {
158                use rasn::AsnType;
159
160                #[allow(clippy::cast_possible_truncation)]
161                let etsi_val = (value * $conv) as $tt;
162
163                if let Some(constraints) = Self::CONSTRAINTS.value() {
164                    if !constraints.constraint.in_bound(&etsi_val) {
165                        return Err(alloc::format!("Value out of bounds"));
166                    }
167                }
168
169                // Not all "unavailable" values are positive, but always at the very edge of the allowed value range.
170                // So by checking for constraints first, we can use a strict equals condition.
171                if etsi_val == $unavailable {
172                    return Err(alloc::format!("Value out of bounds"));
173                }
174
175                Ok(Self(etsi_val))
176            }
177
178            /// create ETSI type with "unavailable" value
179            pub fn unavailable() -> Self {
180                Self($unavailable)
181            }
182
183            /// determines if the ETSI value is special "unavailable" value
184            pub fn is_unavailable(&self) -> bool {
185                self.0 == $unavailable
186            }
187        }
188
189        impl From<&$t> for f32 {
190            fn from(other: &$t) -> f32 {
191                other.as_meters()
192            }
193        }
194        impl From<$t> for f32 {
195            fn from(other: $t) -> f32 {
196                other.as_meters()
197            }
198        }
199
200        impl TryFrom<f32> for $t {
201            type Error = alloc::string::String;
202
203            fn try_from(value: f32) -> Result<Self, Self::Error> {
204                Self::from_meters(value)
205            }
206        }
207    };
208}
209
210#[cfg(feature = "_dsrc_2_2_1")]
211etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB09, i16, 100., -256);
212#[cfg(feature = "_dsrc_2_2_1")]
213etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB10, i16, 100., -512);
214#[cfg(feature = "_dsrc_2_2_1")]
215etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB11, i16, 100., -1024);
216#[cfg(feature = "_dsrc_2_2_1")]
217etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB12, i16, 100., -2048);
218#[cfg(feature = "_dsrc_2_2_1")]
219etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB13, i16, 100., -4096);
220#[cfg(feature = "_dsrc_2_2_1")]
221etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB14, i16, 100., -8192);
222#[cfg(feature = "_dsrc_2_2_1")]
223etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB16, i16, 100., -32768);
224
225#[cfg(feature = "cpm_1")]
226etsi_to_meters!(cpm_1::cpm_pdu_descriptions::DistanceValue, i32, 100.);
227#[cfg(feature = "_cdd_2_2_1")]
228etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::ObjectDimensionValue, u16, 10., 256);
229#[cfg(feature = "cpm_1")]
230etsi_to_meters!(cpm_1::cpm_pdu_descriptions::ObjectDimensionValue, u16, 10.);
231#[cfg(feature = "cpm_1")]
232etsi_to_meters!(cpm_1::cpm_pdu_descriptions::Radius, u16, 10.);
233#[cfg(feature = "cpm_1")]
234etsi_to_meters!(cpm_1::cpm_pdu_descriptions::Range, u16, 10.);
235#[cfg(feature = "cpm_1")]
236etsi_to_meters!(cpm_1::cpm_pdu_descriptions::SemiRangeLength, u16, 10.);
237
238#[cfg(feature = "_cdd_1_3_1_1")]
239etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::VehicleWidth, u8, 10., 62); // Unit: 0,1 metre
240#[cfg(feature = "_cdd_2_2_1")]
241etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::VehicleWidth, u8, 10., 62); // Unit: 0,1 metre
242#[cfg(feature = "_cdd_1_3_1_1")]
243etsi_to_meters_unavailable!(
244    cdd_1_3_1_1::its_container::VehicleLengthValue,
245    u16,
246    10.,
247    1023
248); // Unit: 0,1 metre
249#[cfg(feature = "_cdd_2_2_1")]
250etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::VehicleLengthValue, u16, 10., 1023); // Unit: 0,1 metre
251
252#[cfg(feature = "_dsrc_2_2_1")]
253etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::VehicleHeight, u8, 20., 127); // Unit: 0,05 metre
254#[cfg(feature = "_cdd_2_2_1")]
255etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::VehicleHeight, u8, 20., 127); // Unit: 0,05 metre
256
257#[cfg(feature = "_cdd_1_3_1_1")]
258etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::SemiAxisLength, u16, 100., 4095); // Unit: 0,01 metre
259#[cfg(feature = "_cdd_2_2_1")]
260etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::SemiAxisLength, u16, 100., 4095); // Unit: 0,01 metre
261
262#[cfg(feature = "_cdd_1_3_1_1")]
263etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::AltitudeValue, i32, 100., 800001); // Unit: 0,01 metre
264#[cfg(feature = "_cdd_2_2_1")]
265etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::AltitudeValue, i32, 100., 800001); // Unit: 0,01 metre
266
267#[cfg(feature = "_cdd_1_3_1_1")]
268etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::DeltaAltitude, i16, 100., 12800); // Unit: 0,01 metre
269#[cfg(feature = "_cdd_2_2_1")]
270etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::DeltaAltitude, i16, 100., 12800); // Unit: 0,01 metre
271
272#[cfg(feature = "_cdd_1_3_1_1")]
273etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::HeightLonCarr, u8, 100., 100); // Unit: 0,01 metre
274#[cfg(feature = "_cdd_2_2_1")]
275etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::HeightLonCarr, u8, 100., 100); // Unit: 0,01 metre
276
277#[cfg(feature = "_cdd_1_3_1_1")]
278etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::PosLonCarr, u8, 100., 127); // Unit: 0,01 metre
279#[cfg(feature = "_cdd_2_2_1")]
280etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::PosLonCarr, u8, 100., 127); // Unit: 0,01 metre
281
282#[cfg(feature = "_cdd_1_3_1_1")]
283etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::PosFrontAx, u8, 100., 20); // Unit: 0,01 metre
284#[cfg(feature = "_cdd_2_2_1")]
285etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::PosFrontAx, u8, 100., 20); // Unit: 0,01 metre
286
287#[cfg(feature = "_cdd_1_3_1_1")]
288etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::PosPillar, u8, 100., 30); // Unit: 0,01 metre
289#[cfg(feature = "_cdd_2_2_1")]
290etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::PosPillar, u8, 100., 30); // Unit: 0,01 metre
291
292#[cfg(feature = "_cdd_1_3_1_1")]
293etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::WheelBaseVehicle, u8, 100., 127); // Unit: 0,01 metre
294#[cfg(feature = "_cdd_2_2_1")]
295etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::WheelBaseVehicle, u8, 100., 127); // Unit: 0,01 metre
296
297#[cfg(feature = "_cdd_1_3_1_1")]
298etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::TurningRadius, u8, 2.5, 255); // Unit: 0,4 metre
299#[cfg(feature = "_cdd_2_2_1")]
300etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::TurningRadius, u8, 2.5, 255); // Unit: 0,4 metre
301
302#[cfg(feature = "_cdd_2_2_1")] // CPM v2
303etsi_to_meters_unavailable!(
304    cdd_2_2_1::etsi_its_cdd::CoordinateConfidence,
305    u16,
306    100.,
307    4096
308); // Unit: 0,01 metre
309
310#[cfg(feature = "cpm_1")]
311etsi_to_meters!(
312    cpm_1::cpm_pdu_descriptions::LongitudinalLanePositionValue,
313    u16,
314    10.
315); // Unit: 0,1 metre
316#[cfg(feature = "_cdd_2_2_1")]
317etsi_to_meters_unavailable!(
318    cdd_2_2_1::etsi_its_cdd::LongitudinalLanePositionValue,
319    u16,
320    10.,
321    32767
322); // Unit: 0,1 metre
323
324#[cfg(feature = "cpm_1")]
325etsi_to_meters_unavailable!(
326    cpm_1::cpm_pdu_descriptions::LongitudinalLanePositionConfidence,
327    u8,
328    100.,
329    102
330); // Unit: 0.01 metre
331#[cfg(feature = "_cdd_2_2_1")]
332etsi_to_meters_unavailable!(
333    cdd_2_2_1::etsi_its_cdd::LongitudinalLanePositionConfidence,
334    u16,
335    10.,
336    1023
337); // Unit: 0,1 metre
338
339#[cfg(feature = "cpm_1")]
340etsi_to_meters_unavailable!(
341    cpm_1::cpm_pdu_descriptions::ObjectDimensionConfidence,
342    u8,
343    100.,
344    102
345); // Unit: 0.01 metre
346#[cfg(feature = "_cdd_2_2_1")]
347etsi_to_meters_unavailable!(
348    cdd_2_2_1::etsi_its_cdd::ObjectDimensionConfidence,
349    u8,
350    10.,
351    32
352); // Unit: 0,1 metre
353
354#[cfg(feature = "_cdd_2_2_1")] // DENM v2
355etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::Position1d, i16, 1., 8191); // Unit: 1 metre
356
357/// Create conversions for ETSI type `t` (which has underlying data type `tt`) with conversion factor `conv` and some "unavailable" value
358macro_rules! etsi_to_mps {
359    ($t:ty, $tt:ty, $conv:expr, $unavailable:expr) => {
360        impl $t {
361            /// convert ETSI speed to m/s
362            #[must_use]
363            pub fn as_mps(&self) -> f32 {
364                f32::from(self.0) / $conv
365            }
366
367            /// convert ETSI speed to m/s or `None` if "unavailable"
368            #[must_use]
369            pub fn try_as_mps(&self) -> Option<f32> {
370                if self.is_unavailable() {
371                    None
372                } else {
373                    Some(self.as_mps())
374                }
375            }
376
377            /// create ETSI speed from m/s
378            ///
379            /// # Errors
380            /// human-readable string when input value is out of bounds
381            pub fn from_mps(value: f32) -> Result<Self, alloc::string::String> {
382                use rasn::AsnType;
383
384                #[allow(clippy::cast_possible_truncation)]
385                let etsi_val = (value * $conv) as $tt;
386
387                if let Some(constraints) = Self::CONSTRAINTS.value() {
388                    if !constraints.constraint.in_bound(&etsi_val) {
389                        return Err(alloc::format!("Value out of bounds"));
390                    }
391                }
392
393                // Not all "unavailable" values are positive, but always at the very edge of the allowed value range.
394                // So by checking for constraints first, we can use a strict equals condition.
395                if etsi_val == $unavailable {
396                    return Err(alloc::format!("Value out of bounds"));
397                }
398
399                Ok(Self(etsi_val))
400            }
401
402            /// convert ETSI speed to km/h
403            #[must_use]
404            pub fn as_kmh(&self) -> f32 {
405                self.as_mps() * MPS_TO_KMH_FACTOR
406            }
407
408            /// convert ETSI speed to km/h or `None` if "unavailable"
409            #[must_use]
410            pub fn try_as_kmh(&self) -> Option<f32> {
411                if self.is_unavailable() {
412                    None
413                } else {
414                    Some(self.as_kmh())
415                }
416            }
417
418            /// create ETSI speed from km/h
419            ///
420            /// # Errors
421            /// human-readable string when input value is out of bounds
422            pub fn from_kmh(value: f32) -> Result<Self, alloc::string::String> {
423                Self::from_mps(value / MPS_TO_KMH_FACTOR)
424            }
425
426            /// create ETSI type with "unavailable" value
427            pub fn unavailable() -> Self {
428                Self($unavailable)
429            }
430
431            /// determines if the ETSI value is special "unavailable" value
432            pub fn is_unavailable(&self) -> bool {
433                self.0 == $unavailable
434            }
435        }
436
437        impl From<&$t> for f32 {
438            fn from(other: &$t) -> f32 {
439                other.as_mps()
440            }
441        }
442        impl From<$t> for f32 {
443            fn from(other: $t) -> f32 {
444                other.as_mps()
445            }
446        }
447
448        impl TryFrom<f32> for $t {
449            type Error = alloc::string::String;
450
451            fn try_from(value: f32) -> Result<Self, Self::Error> {
452                Self::from_mps(value)
453            }
454        }
455    };
456}
457
458#[cfg(feature = "cpm_1")]
459etsi_to_mps!(
460    cpm_1::cpm_pdu_descriptions::SpeedValueExtended,
461    i16,
462    100.,
463    16_383
464); // Unit: 0,01 m/s
465#[cfg(feature = "_cdd_1_3_1_1")]
466etsi_to_mps!(cdd_1_3_1_1::its_container::SpeedValue, u16, 100., 16_383); // Unit: 0,01 m/s
467#[cfg(feature = "_cdd_2_2_1")]
468etsi_to_mps!(cdd_2_2_1::etsi_its_cdd::SpeedValue, u16, 100., 16_383); // Unit: 0,01 m/s
469
470#[cfg(feature = "_cdd_2_2_1")]
471etsi_to_mps!(
472    cdd_2_2_1::etsi_its_cdd::VelocityComponentValue,
473    i16,
474    100.,
475    16_383
476); // Unit: 0,01 m/s
477
478#[cfg(feature = "_cdd_1_3_1_1")]
479etsi_to_mps!(cdd_1_3_1_1::its_container::SpeedConfidence, u8, 100., 127); // Unit: 0,01 m/s
480#[cfg(feature = "_cdd_2_2_1")]
481etsi_to_mps!(cdd_2_2_1::etsi_its_cdd::SpeedConfidence, u8, 100., 127); // Unit: 0,01 m/s
482
483#[cfg(feature = "_dsrc_2_2_1")]
484etsi_to_mps!(dsrc_2_2_1::etsi_its_dsrc::Velocity, u16, 50., 8191); // Unit: 0.02 m/s
485
486/// Create conversions for ETSI type `t` (which has underlying data type `tt`) with conversion factor `conv` and some "unavailable" value
487#[cfg(any(feature = "_cdd_1_3_1_1", feature = "_cdd_2_2_1"))]
488macro_rules! etsi_to_mpss {
489    ($t:ty, $tt:ty, $conv:expr, $unavailable:expr) => {
490        impl $t {
491            /// convert ETSI acceleration to m/s/s
492            #[must_use]
493            pub fn as_mpss(&self) -> f32 {
494                f32::from(self.0) / $conv
495            }
496
497            /// convert ETSI acceleration to m/s/s or `None` if "unavailable"
498            #[must_use]
499            pub fn try_as_mpss(&self) -> Option<f32> {
500                if self.is_unavailable() {
501                    None
502                } else {
503                    Some(self.as_mpss())
504                }
505            }
506
507            /// create ETSI acceleration from m/s/s
508            ///
509            /// # Errors
510            /// human-readable string when input value is out of bounds
511            pub fn from_mpss(value: f32) -> Result<Self, alloc::string::String> {
512                use rasn::AsnType;
513
514                #[allow(clippy::cast_possible_truncation)]
515                let etsi_val = (value * $conv) as $tt;
516
517                if let Some(constraints) = Self::CONSTRAINTS.value() {
518                    if !constraints.constraint.in_bound(&etsi_val) {
519                        return Err(alloc::format!("Value out of bounds"));
520                    }
521                }
522
523                // Not all "unavailable" values are positive, but always at the very edge of the allowed value range.
524                // So by checking for constraints first, we can use a strict equals condition.
525                if etsi_val == $unavailable {
526                    return Err(alloc::format!("Value out of bounds"));
527                }
528
529                Ok(Self(etsi_val))
530            }
531
532            /// create ETSI type with "unavailable" value
533            pub fn unavailable() -> Self {
534                Self($unavailable)
535            }
536
537            /// determines if the ETSI value is special "unavailable" value
538            pub fn is_unavailable(&self) -> bool {
539                self.0 == $unavailable
540            }
541        }
542
543        impl From<&$t> for f32 {
544            fn from(other: &$t) -> f32 {
545                other.as_mpss()
546            }
547        }
548        impl From<$t> for f32 {
549            fn from(other: $t) -> f32 {
550                other.as_mpss()
551            }
552        }
553
554        impl TryFrom<f32> for $t {
555            type Error = alloc::string::String;
556
557            fn try_from(value: f32) -> Result<Self, Self::Error> {
558                Self::from_mpss(value)
559            }
560        }
561    };
562}
563
564#[cfg(feature = "_cdd_1_3_1_1")]
565etsi_to_mpss!(
566    cdd_1_3_1_1::its_container::LongitudinalAccelerationValue,
567    i16,
568    10.,
569    161
570); // Unit: 0,1 m/s^2
571#[cfg(feature = "_cdd_2_2_1")]
572etsi_to_mpss!(
573    cdd_2_2_1::etsi_its_cdd::LongitudinalAccelerationValue,
574    i16,
575    10.,
576    161
577); // Unit: 0,1 m/s^2
578#[cfg(feature = "_cdd_1_3_1_1")]
579etsi_to_mpss!(
580    cdd_1_3_1_1::its_container::LateralAccelerationValue,
581    i16,
582    10.,
583    161
584); // Unit: 0,1 m/s^2
585#[cfg(feature = "_cdd_2_2_1")]
586etsi_to_mpss!(
587    cdd_2_2_1::etsi_its_cdd::LateralAccelerationValue,
588    i16,
589    10.,
590    161
591); // Unit: 0,1 m/s^2
592#[cfg(feature = "_cdd_2_2_1")]
593etsi_to_mpss!(cdd_2_2_1::etsi_its_cdd::AccelerationValue, i16, 10., 161); // Unit: 0,1 m/s^2
594
595#[cfg(feature = "_cdd_1_3_1_1")]
596etsi_to_mpss!(
597    cdd_1_3_1_1::its_container::VerticalAccelerationValue,
598    i16,
599    10.,
600    161
601); // Unit: 0,1 m/s^2
602#[cfg(feature = "_cdd_2_2_1")]
603etsi_to_mpss!(
604    cdd_2_2_1::etsi_its_cdd::VerticalAccelerationValue,
605    i16,
606    10.,
607    161
608); // Unit: 0,1 m/s^2
609
610#[cfg(feature = "_cdd_2_2_1")]
611etsi_to_mpss!(
612    cdd_2_2_1::etsi_its_cdd::AccelerationMagnitudeValue,
613    u8,
614    10.,
615    161
616); // Unit: 0,1 m/s^2
617
618#[cfg(feature = "_cdd_1_3_1_1")]
619etsi_to_mpss!(
620    cdd_1_3_1_1::its_container::AccelerationConfidence,
621    u8,
622    10.,
623    102
624); // Unit: 0,1 m/s^2
625#[cfg(feature = "_cdd_2_2_1")]
626etsi_to_mpss!(
627    cdd_2_2_1::etsi_its_cdd::AccelerationConfidence,
628    u8,
629    10.,
630    102
631); // Unit: 0,1 m/s^2
632
633/// Check for unavailable data of ETSI type `t` (which has underlying data type `tt`)
634#[cfg(any(feature = "_cdd_1_3_1_1", feature = "_cdd_2_2_1"))]
635macro_rules! etsi_raw_unavailable {
636    ($t:ty, $tt:ty, $unavailable:expr) => {
637        impl $t {
638            /// convert ETSI acceleration to m/s/s or `None` if "unavailable"
639            #[must_use]
640            pub fn try_as_raw(&self) -> Option<$tt> {
641                if self.is_unavailable() {
642                    None
643                } else {
644                    Some(self.0)
645                }
646            }
647
648            /// create ETSI acceleration from raw value
649            ///
650            /// # Errors
651            /// human-readable string when input value is out of bounds
652            pub fn from_raw(value: $tt) -> Result<Self, alloc::string::String> {
653                use rasn::AsnType;
654
655                if let Some(constraints) = Self::CONSTRAINTS.value() {
656                    if !constraints.constraint.in_bound(&value) {
657                        return Err(alloc::format!("Value out of bounds"));
658                    }
659                }
660
661                // Not all "unavailable" values are positive, but always at the very edge of the allowed value range.
662                // So by checking for constraints first, we can use a strict equals condition.
663                if value == $unavailable {
664                    return Err(alloc::format!("Value out of bounds"));
665                }
666
667                Ok(Self(value))
668            }
669
670            /// create ETSI type with "unavailable" value
671            pub fn unavailable() -> Self {
672                Self($unavailable)
673            }
674
675            /// determines if the ETSI value is special "unavailable" value
676            pub fn is_unavailable(&self) -> bool {
677                self.0 == $unavailable
678            }
679        }
680
681        impl From<&$t> for $tt {
682            fn from(other: &$t) -> $tt {
683                other.0
684            }
685        }
686        impl From<$t> for $tt {
687            fn from(other: $t) -> $tt {
688                other.0
689            }
690        }
691
692        impl TryFrom<$tt> for $t {
693            type Error = alloc::string::String;
694
695            fn try_from(value: $tt) -> Result<Self, Self::Error> {
696                Self::from_raw(value)
697            }
698        }
699    };
700}
701
702#[cfg(feature = "_cdd_1_3_1_1")]
703etsi_raw_unavailable!(cdd_1_3_1_1::its_container::CurvatureValue, i16, 1023);
704#[cfg(feature = "_cdd_2_2_1")]
705etsi_raw_unavailable!(cdd_2_2_1::etsi_its_cdd::CurvatureValue, i16, 1023);
706
707#[cfg(feature = "_cdd_2_2_1")]
708etsi_raw_unavailable!(cdd_2_2_1::etsi_its_cdd::ConfidenceLevel, u8, 101); // Unit: percent
709#[cfg(feature = "_cdd_2_2_1")]
710etsi_raw_unavailable!(cdd_2_2_1::etsi_its_cdd::CorrelationCellValue, i8, 101); // Unit: the value is scaled by 100
711#[cfg(feature = "_cdd_2_2_1")]
712etsi_raw_unavailable!(cdd_2_2_1::etsi_its_cdd::NumberOfOccupants, u8, 127); // Unit: 1 person
713
714#[cfg(feature = "_cdd_2_2_1")]
715etsi_raw_unavailable!(cdd_2_2_1::etsi_its_cdd::StabilityLossProbability, u8, 63); // Unit: 2 %
716#[cfg(feature = "_cdd_2_2_1")]
717etsi_raw_unavailable!(
718    cdd_2_2_1::etsi_its_cdd::TrajectoryInterceptionProbability,
719    u8,
720    63
721); // Unit: 2 %
722
723/// Create conversions for ETSI type `t` with conversion factor `conv` and some "unavailable" value
724#[cfg(any(
725    feature = "cpm_1",
726    feature = "_cdd_1_3_1_1",
727    feature = "_cdd_2_2_1",
728    feature = "_dsrc_2_2_1"
729))]
730macro_rules! angle_to_deg {
731    ($t:ty, $tt:ty, $conv:expr, $unavailable:expr) => {
732        impl $t {
733            /// convert ETSI WGS84AngleValue/ CartesianAngleValue to degrees
734            #[must_use]
735            pub fn as_deg(&self) -> f32 {
736                f32::from(self.0) / $conv
737            }
738
739            /// convert ETSI WGS84AngleValue/ CartesianAngleValue to degrees or `None` if "unavailable"
740            #[must_use]
741            pub fn try_as_deg(&self) -> Option<f32> {
742                if self.is_unavailable() {
743                    None
744                } else {
745                    Some(self.as_deg())
746                }
747            }
748
749            /// create ETSI WGS84AngleValue/ CartesianAngleValue from degrees
750            ///
751            /// # Errors
752            /// human-readable string when input value is out of bounds
753            pub fn from_deg(value: f32) -> Result<Self, alloc::string::String> {
754                use rasn::AsnType;
755
756                #[allow(clippy::cast_possible_truncation)]
757                let etsi_val = (value * $conv) as $tt;
758
759                if let Some(constraints) = Self::CONSTRAINTS.value() {
760                    if !constraints.constraint.in_bound(&etsi_val) {
761                        return Err(alloc::format!("Value out of bounds"));
762                    }
763                }
764
765                // Not all "unavailable" values are positive, but always at the very edge of the allowed value range.
766                // So by checking for constraints first, we can use a strict equals condition.
767                if etsi_val == $unavailable {
768                    return Err(alloc::format!("Value out of bounds"));
769                }
770
771                Ok(Self(etsi_val))
772            }
773
774            /// create ETSI type with "unavailable" value
775            pub fn unavailable() -> Self {
776                Self($unavailable)
777            }
778
779            /// determines if the ETSI value is special "unavailable" value
780            pub fn is_unavailable(&self) -> bool {
781                self.0 == $unavailable
782            }
783        }
784
785        impl From<&$t> for f32 {
786            fn from(other: &$t) -> f32 {
787                other.as_deg()
788            }
789        }
790        impl From<$t> for f32 {
791            fn from(other: $t) -> f32 {
792                other.as_deg()
793            }
794        }
795
796        impl TryFrom<f32> for $t {
797            type Error = alloc::string::String;
798
799            fn try_from(value: f32) -> Result<Self, Self::Error> {
800                Self::from_deg(value)
801            }
802        }
803    };
804}
805
806#[cfg(feature = "_cdd_2_2_1")]
807angle_to_deg!(cdd_2_2_1::etsi_its_cdd::CartesianAngleValue, u16, 10., 3601); // Unit: 0,1 degrees
808#[cfg(feature = "cpm_1")]
809angle_to_deg!(
810    cpm_1::cpm_pdu_descriptions::CartesianAngleValue,
811    u16,
812    10.,
813    3601
814); // Unit: 0,1 degrees
815#[cfg(feature = "cpm_1")]
816angle_to_deg!(cpm_1::cpm_pdu_descriptions::WGS84AngleValue, u16, 10., 3601); // Unit: 0,1 degrees
817#[cfg(feature = "_cdd_2_2_1")]
818angle_to_deg!(cdd_2_2_1::etsi_its_cdd::Wgs84AngleValue, u16, 10., 3601); // Unit: 0,1 degrees
819#[cfg(feature = "_dsrc_2_2_1")]
820angle_to_deg!(dsrc_2_2_1::etsi_its_dsrc::Angle, u16, 80., 28800); // Unit: 0.0125 degrees
821#[cfg(feature = "_cdd_2_2_1")]
822angle_to_deg!(cdd_2_2_1::etsi_its_cdd::HeadingValue, u16, 10., 3601); // Unit: 0,1 degree
823#[cfg(feature = "_cdd_1_3_1_1")]
824angle_to_deg!(cdd_1_3_1_1::its_container::HeadingValue, u16, 10., 3601); // Unit: 0,1 degree
825#[cfg(feature = "_cdd_2_2_1")]
826angle_to_deg!(cdd_2_2_1::etsi_its_cdd::HeadingConfidence, u8, 10., 127); // Unit: 0,1 degree
827#[cfg(feature = "_cdd_1_3_1_1")]
828angle_to_deg!(cdd_1_3_1_1::its_container::HeadingConfidence, u8, 10., 127); // Unit: 0,1 degree
829
830#[cfg(feature = "_cdd_2_2_1")]
831angle_to_deg!(cdd_2_2_1::etsi_its_cdd::AngleConfidence, u8, 10., 127); // Unit: 0,1 degrees
832#[cfg(feature = "cpm_1")]
833angle_to_deg!(cpm_1::cpm_pdu_descriptions::AngleConfidence, u8, 10., 127); // Unit: 0,1 degrees
834
835#[cfg(feature = "_cdd_2_2_1")]
836angle_to_deg!(
837    cdd_2_2_1::etsi_its_cdd::SteeringWheelAngleValue,
838    i16,
839    (1. / 1.5),
840    512
841); // Unit: 1,5 degree
842#[cfg(feature = "_cdd_1_3_1_1")]
843angle_to_deg!(
844    cdd_1_3_1_1::its_container::SteeringWheelAngleValue,
845    i16,
846    (1. / 1.5),
847    512
848); // Unit: 1,5 degree
849
850#[cfg(feature = "_cdd_2_2_1")]
851angle_to_deg!(
852    cdd_2_2_1::etsi_its_cdd::SteeringWheelAngleConfidence,
853    u8,
854    (1. / 1.5),
855    127
856); // Unit: 1,5 degree
857#[cfg(feature = "_cdd_1_3_1_1")]
858angle_to_deg!(
859    cdd_1_3_1_1::its_container::SteeringWheelAngleConfidence,
860    u8,
861    (1. / 1.5),
862    127
863); // Unit: 1,5 degree
864
865/// Create conversions for ETSI type `t` with conversion factor `conv` and some "unavailable" value
866#[cfg(any(feature = "_cdd_2_2_1", feature = "_cdd_1_3_1_1"))]
867macro_rules! angle_to_degrate {
868    ($t:ty, $conv:expr, $unavailable:expr) => {
869        impl $t {
870            /// convert ETSI YawRateValue to degrees per second
871            #[must_use]
872            pub fn as_deg_rate(&self) -> f32 {
873                f32::from(self.0) / $conv
874            }
875
876            /// convert ETSI YawRateValue to degrees per second or `None` if "unavailable"
877            #[must_use]
878            pub fn try_as_deg_rate(&self) -> Option<f32> {
879                if self.is_unavailable() {
880                    None
881                } else {
882                    Some(self.as_deg_rate())
883                }
884            }
885
886            /// create ETSI YawRateValue from degrees per second
887            ///
888            /// # Errors
889            /// human-readable string when input value is out of bounds
890            pub fn from_deg_rate(value: f32) -> Result<Self, alloc::string::String> {
891                use rasn::AsnType;
892
893                #[allow(clippy::cast_possible_truncation)]
894                let etsi_val = (value * $conv) as i16;
895
896                if let Some(constraints) = Self::CONSTRAINTS.value() {
897                    if !constraints.constraint.in_bound(&etsi_val) {
898                        return Err(alloc::format!("Value out of bounds"));
899                    }
900                }
901
902                // Not all "unavailable" values are positive, but always at the very edge of the allowed value range.
903                // So by checking for constraints first, we can use a strict equals condition.
904                if etsi_val == $unavailable {
905                    return Err(alloc::format!("Value out of bounds"));
906                }
907
908                Ok(Self(etsi_val))
909            }
910
911            /// create ETSI type with "unavailable" value
912            pub fn unavailable() -> Self {
913                Self($unavailable)
914            }
915
916            /// determines if the ETSI value is special "unavailable" value
917            pub fn is_unavailable(&self) -> bool {
918                self.0 == $unavailable
919            }
920        }
921
922        impl From<&$t> for f32 {
923            fn from(other: &$t) -> f32 {
924                other.as_deg_rate()
925            }
926        }
927        impl From<$t> for f32 {
928            fn from(other: $t) -> f32 {
929                other.as_deg_rate()
930            }
931        }
932
933        impl TryFrom<f32> for $t {
934            type Error = alloc::string::String;
935
936            fn try_from(value: f32) -> Result<Self, Self::Error> {
937                Self::from_deg_rate(value)
938            }
939        }
940    };
941}
942
943#[cfg(feature = "_cdd_2_2_1")]
944angle_to_degrate!(cdd_2_2_1::etsi_its_cdd::YawRateValue, 100., 32767); // Unit: 0,01 degree per second
945#[cfg(feature = "_cdd_1_3_1_1")]
946angle_to_degrate!(cdd_1_3_1_1::its_container::YawRateValue, 100., 32767); // Unit: 0,01 degree per second
947
948#[cfg(feature = "_cdd_2_2_1")]
949angle_to_degrate!(
950    cdd_2_2_1::etsi_its_cdd::CartesianAngularVelocityComponentValue,
951    1.,
952    256
953); // Unit: degree/s
954
955// DeltaTime: unit 10 seconds, clamping to -121 for <-20 minutes and +120 for >+20 minutes, -122 for unavailable
956#[cfg(feature = "_dsrc_2_2_1")]
957impl dsrc_2_2_1::etsi_its_dsrc::DeltaTime {
958    /// convert ETSI DeltaTime to seconds
959    #[must_use]
960    pub fn as_sec(&self) -> i16 {
961        i16::from(self.0) * 10
962    }
963
964    /// convert ETSI DeltaTime to seconds or `None` if "unavailable"
965    #[must_use]
966    pub fn try_as_sec(&self) -> Option<i16> {
967        if self.is_unavailable() {
968            None
969        } else {
970            Some(self.as_sec())
971        }
972    }
973
974    /// create ETSI DeltaTime from seconds, clamping at min. and max. bounds
975    #[must_use]
976    pub fn from_sec(value: i16) -> Self {
977        #[allow(clippy::cast_possible_truncation)]
978        let etsi_val = (value / 10) as i8;
979
980        Self(etsi_val.clamp(-121, 120))
981    }
982
983    /// create ETSI type with "unavailable" value
984    pub fn unavailable() -> Self {
985        Self(-122)
986    }
987
988    /// determines if the ETSI value is special "unavailable" value
989    pub fn is_unavailable(&self) -> bool {
990        self.0 == -122
991    }
992}
993
994#[cfg(feature = "_dsrc_2_2_1")]
995impl From<&dsrc_2_2_1::etsi_its_dsrc::DeltaTime> for i16 {
996    fn from(other: &dsrc_2_2_1::etsi_its_dsrc::DeltaTime) -> i16 {
997        other.as_sec()
998    }
999}
1000#[cfg(feature = "_dsrc_2_2_1")]
1001impl From<dsrc_2_2_1::etsi_its_dsrc::DeltaTime> for i16 {
1002    fn from(other: dsrc_2_2_1::etsi_its_dsrc::DeltaTime) -> i16 {
1003        other.as_sec()
1004    }
1005}
1006
1007// DSecond: unit milliseconds, 65535 for unavailable
1008#[cfg(feature = "_dsrc_2_2_1")]
1009impl dsrc_2_2_1::etsi_its_dsrc::DSecond {
1010    /// convert ETSI DeltaTime to milliseconds
1011    #[must_use]
1012    pub fn as_millis(&self) -> u16 {
1013        self.0
1014    }
1015
1016    /// convert ETSI DeltaTime to milliseconds or `None` if "unavailable"
1017    #[must_use]
1018    pub fn try_as_millis(&self) -> Option<u16> {
1019        if self.is_unavailable() {
1020            None
1021        } else {
1022            Some(self.as_millis())
1023        }
1024    }
1025
1026    /// create ETSI DSecond from milliseconds
1027    ///
1028    /// # Errors
1029    /// human-readable string when input value is out of bounds
1030    pub fn from_millis(value: u16) -> Result<Self, alloc::string::String> {
1031        // ASN.1 bounds are bigger than allowed values (0..59999 for normal values, 60000..60999 for leap seconds)
1032
1033        if value > 60999 {
1034            return Err(alloc::format!("Value out of bounds"));
1035        }
1036
1037        Ok(Self(value))
1038    }
1039
1040    /// create ETSI type with "unavailable" value
1041    pub fn unavailable() -> Self {
1042        Self(65535)
1043    }
1044
1045    /// determines if the ETSI value is special "unavailable" value
1046    pub fn is_unavailable(&self) -> bool {
1047        self.0 == 65535
1048    }
1049}
1050
1051#[cfg(feature = "_dsrc_2_2_1")]
1052impl From<&dsrc_2_2_1::etsi_its_dsrc::DSecond> for u16 {
1053    fn from(other: &dsrc_2_2_1::etsi_its_dsrc::DSecond) -> u16 {
1054        other.as_millis()
1055    }
1056}
1057#[cfg(feature = "_dsrc_2_2_1")]
1058impl From<dsrc_2_2_1::etsi_its_dsrc::DSecond> for u16 {
1059    fn from(other: dsrc_2_2_1::etsi_its_dsrc::DSecond) -> u16 {
1060        other.as_millis()
1061    }
1062}
1063
1064// TimeMark: unit 1/10 of a second, 36001 for unknown, 36000 for out-of-range
1065#[cfg(feature = "_dsrc_2_2_1")]
1066impl dsrc_2_2_1::etsi_its_dsrc::TimeMark {
1067    const CONV_FACTOR: u32 = 100;
1068    const UNKNOWN: u16 = 36001;
1069    const OUT_OF_RANGE: u16 = 36000;
1070
1071    /// convert ETSI TimeMark to milliseconds
1072    #[must_use]
1073    pub fn as_millis(&self) -> u32 {
1074        self.0 as u32 * Self::CONV_FACTOR
1075    }
1076
1077    /// convert ETSI TimeMark to milliseconds or `None` if "unknown"
1078    #[must_use]
1079    pub fn try_as_millis(&self) -> Option<u32> {
1080        if self.is_unknown() {
1081            None
1082        } else {
1083            Some(self.as_millis())
1084        }
1085    }
1086
1087    /// create ETSI TimeMark from milliseconds
1088    ///
1089    /// # Errors
1090    /// human-readable string when input value is out of bounds
1091    pub fn from_millis(value: u32) -> Result<Self, alloc::string::String> {
1092        #[allow(clippy::cast_possible_truncation)]
1093        let etsi_val = (value / Self::CONV_FACTOR) as u16;
1094
1095        // ASN.1 bounds are bigger than allowed values (0..35990 for normal values, 35991..35999 for leap seconds)
1096        if etsi_val > 35999 {
1097            return Err(alloc::format!("Value out of bounds"));
1098        }
1099
1100        Ok(Self(etsi_val))
1101    }
1102
1103    /// create ETSI type with "unknown" value
1104    pub fn unknown() -> Self {
1105        Self(Self::UNKNOWN)
1106    }
1107
1108    /// determines if the ETSI value is special "unknown" value
1109    pub fn is_unknown(&self) -> bool {
1110        self.0 == Self::UNKNOWN
1111    }
1112
1113    /// create ETSI type with "out-of-range" value
1114    pub fn out_of_range() -> Self {
1115        Self(Self::OUT_OF_RANGE)
1116    }
1117
1118    /// determines if the ETSI value is special "out-of-range" value
1119    pub fn is_out_of_range(&self) -> bool {
1120        self.0 == Self::OUT_OF_RANGE
1121    }
1122}
1123
1124#[cfg(feature = "_dsrc_2_2_1")]
1125impl From<&dsrc_2_2_1::etsi_its_dsrc::TimeMark> for u32 {
1126    fn from(other: &dsrc_2_2_1::etsi_its_dsrc::TimeMark) -> u32 {
1127        other.as_millis()
1128    }
1129}
1130#[cfg(feature = "_dsrc_2_2_1")]
1131impl From<dsrc_2_2_1::etsi_its_dsrc::TimeMark> for u32 {
1132    fn from(other: dsrc_2_2_1::etsi_its_dsrc::TimeMark) -> u32 {
1133        other.as_millis()
1134    }
1135}
1136
1137// MinuteOfTheYear: unit minute, 527040 for invalid
1138#[cfg(feature = "_dsrc_2_2_1")]
1139impl dsrc_2_2_1::etsi_its_dsrc::MinuteOfTheYear {
1140    /// create ETSI MinuteOfTheYear with "invalid" value
1141    pub fn invalid() -> Self {
1142        Self(527040)
1143    }
1144
1145    /// determines if the ETSI value is special "invalid" value
1146    pub fn is_invalid(&self) -> bool {
1147        self.0 == 527040
1148    }
1149}
1150
1151// MsgCount 0..127
1152#[cfg(feature = "_dsrc_2_2_1")]
1153impl crate::standards::dsrc_2_2_1::etsi_its_dsrc::MsgCount {
1154    pub fn increment(&self) -> Self {
1155        Self((self.0 + 1) % 128)
1156    }
1157}
1158#[cfg(feature = "_dsrc_2_2_1")]
1159impl From<u8> for dsrc_2_2_1::etsi_its_dsrc::MsgCount {
1160    fn from(value: u8) -> Self {
1161        Self(value % 128)
1162    }
1163}
1164
1165// RequestID 0..255
1166#[cfg(feature = "_dsrc_2_2_1")]
1167impl crate::standards::dsrc_2_2_1::etsi_its_dsrc::RequestID {
1168    pub fn increment(&self) -> Self {
1169        Self(self.0.wrapping_add(1))
1170    }
1171}
1172#[cfg(feature = "_dsrc_2_2_1")]
1173impl From<u8> for dsrc_2_2_1::etsi_its_dsrc::RequestID {
1174    // for convenience and interface unification only
1175    fn from(value: u8) -> Self {
1176        Self(value)
1177    }
1178}
1179
1180// convenience getters
1181
1182#[cfg(feature = "_dsrc_2_2_1")]
1183impl dsrc_2_2_1::etsi_its_dsrc::SpeedLimitList {
1184    /// Extracts a certain speed limit in m/s, if existing
1185    pub fn get_speed_limit_mps(
1186        &self,
1187        limit_type: dsrc_2_2_1::etsi_its_dsrc::SpeedLimitType,
1188    ) -> Option<f32> {
1189        self.0.iter().find_map(|item| {
1190            if item.r_type == limit_type {
1191                Some(item.speed.as_mps())
1192            } else {
1193                None
1194            }
1195        })
1196    }
1197}
1198
1199#[cfg(feature = "_cdd_2_2_1")]
1200impl cdd_2_2_1::etsi_its_cdd::CauseCodeChoice {
1201    /// Converts a Cause Code to an integer tuple of cause code and sub cause code ID
1202    pub fn to_u8_tuple(&self) -> (u8, u8) {
1203        match self {
1204            Self::reserved0(scc) => (0, scc.0),
1205            Self::trafficCondition1(scc) => (1, scc.0),
1206            Self::accident2(scc) => (2, scc.0),
1207            Self::roadworks3(scc) => (3, scc.0),
1208            Self::reserved4(scc) => (4, scc.0),
1209            Self::impassability5(scc) => (5, scc.0),
1210            Self::adverseWeatherCondition_Adhesion6(scc) => (6, scc.0),
1211            Self::aquaplaning7(scc) => (7, scc.0),
1212            Self::reserved8(scc) => (8, scc.0),
1213            Self::hazardousLocation_SurfaceCondition9(scc) => (9, scc.0),
1214            Self::hazardousLocation_ObstacleOnTheRoad10(scc) => (10, scc.0),
1215            Self::hazardousLocation_AnimalOnTheRoad11(scc) => (11, scc.0),
1216            Self::humanPresenceOnTheRoad12(scc) => (12, scc.0),
1217            Self::reserved13(scc) => (13, scc.0),
1218            Self::wrongWayDriving14(scc) => (14, scc.0),
1219            Self::rescueAndRecoveryWorkInProgress15(scc) => (15, scc.0),
1220            Self::reserved16(scc) => (16, scc.0),
1221            Self::adverseWeatherCondition_ExtremeWeatherCondition17(scc) => (17, scc.0),
1222            Self::adverseWeatherCondition_Visibility18(scc) => (18, scc.0),
1223            Self::adverseWeatherCondition_Precipitation19(scc) => (19, scc.0),
1224            Self::violence20(scc) => (20, scc.0),
1225            Self::reserved21(scc) => (21, scc.0),
1226            Self::reserved22(scc) => (22, scc.0),
1227            Self::reserved23(scc) => (23, scc.0),
1228            Self::reserved24(scc) => (24, scc.0),
1229            Self::reserved25(scc) => (25, scc.0),
1230            Self::slowVehicle26(scc) => (26, scc.0),
1231            Self::dangerousEndOfQueue27(scc) => (27, scc.0),
1232            Self::publicTransportVehicleApproaching28(scc) => (28, scc.0),
1233            Self::reserved29(scc) => (29, scc.0),
1234            Self::reserved30(scc) => (30, scc.0),
1235            Self::reserved31(scc) => (31, scc.0),
1236            Self::reserved32(scc) => (32, scc.0),
1237            Self::reserved33(scc) => (33, scc.0),
1238            Self::reserved34(scc) => (34, scc.0),
1239            Self::reserved35(scc) => (35, scc.0),
1240            Self::reserved36(scc) => (36, scc.0),
1241            Self::reserved37(scc) => (37, scc.0),
1242            Self::reserved38(scc) => (38, scc.0),
1243            Self::reserved39(scc) => (39, scc.0),
1244            Self::reserved40(scc) => (40, scc.0),
1245            Self::reserved41(scc) => (41, scc.0),
1246            Self::reserved42(scc) => (42, scc.0),
1247            Self::reserved43(scc) => (43, scc.0),
1248            Self::reserved44(scc) => (44, scc.0),
1249            Self::reserved45(scc) => (45, scc.0),
1250            Self::reserved46(scc) => (46, scc.0),
1251            Self::reserved47(scc) => (47, scc.0),
1252            Self::reserved48(scc) => (48, scc.0),
1253            Self::reserved49(scc) => (49, scc.0),
1254            Self::reserved50(scc) => (50, scc.0),
1255            Self::reserved51(scc) => (51, scc.0),
1256            Self::reserved52(scc) => (52, scc.0),
1257            Self::reserved53(scc) => (53, scc.0),
1258            Self::reserved54(scc) => (54, scc.0),
1259            Self::reserved55(scc) => (55, scc.0),
1260            Self::reserved56(scc) => (56, scc.0),
1261            Self::reserved57(scc) => (57, scc.0),
1262            Self::reserved58(scc) => (58, scc.0),
1263            Self::reserved59(scc) => (59, scc.0),
1264            Self::reserved60(scc) => (60, scc.0),
1265            Self::reserved61(scc) => (61, scc.0),
1266            Self::reserved62(scc) => (62, scc.0),
1267            Self::reserved63(scc) => (63, scc.0),
1268            Self::reserved64(scc) => (64, scc.0),
1269            Self::reserved65(scc) => (65, scc.0),
1270            Self::reserved66(scc) => (66, scc.0),
1271            Self::reserved67(scc) => (67, scc.0),
1272            Self::reserved68(scc) => (68, scc.0),
1273            Self::reserved69(scc) => (69, scc.0),
1274            Self::reserved70(scc) => (70, scc.0),
1275            Self::reserved71(scc) => (71, scc.0),
1276            Self::reserved72(scc) => (72, scc.0),
1277            Self::reserved73(scc) => (73, scc.0),
1278            Self::reserved74(scc) => (74, scc.0),
1279            Self::reserved75(scc) => (75, scc.0),
1280            Self::reserved76(scc) => (76, scc.0),
1281            Self::reserved77(scc) => (77, scc.0),
1282            Self::reserved78(scc) => (78, scc.0),
1283            Self::reserved79(scc) => (79, scc.0),
1284            Self::reserved80(scc) => (80, scc.0),
1285            Self::reserved81(scc) => (81, scc.0),
1286            Self::reserved82(scc) => (82, scc.0),
1287            Self::reserved83(scc) => (83, scc.0),
1288            Self::reserved84(scc) => (84, scc.0),
1289            Self::reserved85(scc) => (85, scc.0),
1290            Self::reserved86(scc) => (86, scc.0),
1291            Self::reserved87(scc) => (87, scc.0),
1292            Self::reserved88(scc) => (88, scc.0),
1293            Self::reserved89(scc) => (89, scc.0),
1294            Self::reserved90(scc) => (90, scc.0),
1295            Self::vehicleBreakdown91(scc) => (91, scc.0),
1296            Self::postCrash92(scc) => (92, scc.0),
1297            Self::humanProblem93(scc) => (93, scc.0),
1298            Self::stationaryVehicle94(scc) => (94, scc.0),
1299            Self::emergencyVehicleApproaching95(scc) => (95, scc.0),
1300            Self::hazardousLocation_DangerousCurve96(scc) => (96, scc.0),
1301            Self::collisionRisk97(scc) => (97, scc.0),
1302            Self::signalViolation98(scc) => (98, scc.0),
1303            Self::dangerousSituation99(scc) => (99, scc.0),
1304            Self::railwayLevelCrossing100(scc) => (100, scc.0),
1305            Self::reserved101(scc) => (101, scc.0),
1306            Self::reserved102(scc) => (102, scc.0),
1307            Self::reserved103(scc) => (103, scc.0),
1308            Self::reserved104(scc) => (104, scc.0),
1309            Self::reserved105(scc) => (105, scc.0),
1310            Self::reserved106(scc) => (106, scc.0),
1311            Self::reserved107(scc) => (107, scc.0),
1312            Self::reserved108(scc) => (108, scc.0),
1313            Self::reserved109(scc) => (109, scc.0),
1314            Self::reserved110(scc) => (110, scc.0),
1315            Self::reserved111(scc) => (111, scc.0),
1316            Self::reserved112(scc) => (112, scc.0),
1317            Self::reserved113(scc) => (113, scc.0),
1318            Self::reserved114(scc) => (114, scc.0),
1319            Self::reserved115(scc) => (115, scc.0),
1320            Self::reserved116(scc) => (116, scc.0),
1321            Self::reserved117(scc) => (117, scc.0),
1322            Self::reserved118(scc) => (118, scc.0),
1323            Self::reserved119(scc) => (119, scc.0),
1324            Self::reserved120(scc) => (120, scc.0),
1325            Self::reserved121(scc) => (121, scc.0),
1326            Self::reserved122(scc) => (122, scc.0),
1327            Self::reserved123(scc) => (123, scc.0),
1328            Self::reserved124(scc) => (124, scc.0),
1329            Self::reserved125(scc) => (125, scc.0),
1330            Self::reserved126(scc) => (126, scc.0),
1331            Self::reserved127(scc) => (127, scc.0),
1332            Self::reserved128(scc) => (128, scc.0),
1333        }
1334    }
1335}