1pub 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#[cfg(any(feature = "_cdd_1_3_1_1", feature = "_cdd_2_2_1"))]
20macro_rules! latlon_to_deg {
21 ($t:ty, $unavailable:expr) => {
22 impl $t {
23 #[must_use]
25 pub fn as_deg(&self) -> f64 {
26 f64::from(self.0) / 10_000_000.
27 }
28
29 #[must_use]
31 pub fn try_as_deg(&self) -> Option<f64> {
32 if self.is_unavailable() {
33 None
34 } else {
35 Some(self.as_deg())
36 }
37 }
38
39 #[must_use]
41 pub fn from_deg(other: f64) -> Self {
42 Self((other * 10_000_000.) as i32)
43 }
44
45 pub fn unavailable() -> Self {
47 Self($unavailable)
48 }
49
50 pub fn is_unavailable(&self) -> bool {
52 self.0 == $unavailable
53 }
54 }
55 };
56}
57
58#[cfg(feature = "_cdd_1_3_1_1")]
59latlon_to_deg!(cdd_1_3_1_1::its_container::Longitude, 1_800_000_001);
60#[cfg(feature = "_cdd_1_3_1_1")]
61latlon_to_deg!(cdd_1_3_1_1::its_container::Latitude, 900_000_001);
62#[cfg(feature = "_cdd_2_2_1")]
63latlon_to_deg!(cdd_2_2_1::etsi_its_cdd::Longitude, 1_800_000_001);
64#[cfg(feature = "_cdd_2_2_1")]
65latlon_to_deg!(cdd_2_2_1::etsi_its_cdd::Latitude, 900_000_001);
66
67#[cfg(feature = "_cdd_1_3_1_1")]
68latlon_to_deg!(cdd_1_3_1_1::its_container::DeltaLongitude, 131_072);
69#[cfg(feature = "_cdd_1_3_1_1")]
70latlon_to_deg!(cdd_1_3_1_1::its_container::DeltaLatitude, 131_072);
71#[cfg(feature = "_cdd_2_2_1")]
72latlon_to_deg!(cdd_2_2_1::etsi_its_cdd::DeltaLongitude, 131_072);
73#[cfg(feature = "_cdd_2_2_1")]
74latlon_to_deg!(cdd_2_2_1::etsi_its_cdd::DeltaLatitude, 131_072);
75
76#[cfg(feature = "cpm_1")]
78macro_rules! etsi_to_meters {
79 ($t:ty, $tt:ty, $conv:expr) => {
80 impl $t {
81 #[must_use]
83 pub fn as_meters(&self) -> f32 {
84 self.0 as f32 / $conv
85 }
86
87 pub fn from_meters(value: f32) -> Result<Self, alloc::string::String> {
92 use rasn::AsnType;
93
94 #[allow(clippy::cast_possible_truncation)]
95 let etsi_val = (value * $conv) as $tt;
96
97 if let Some(constraints) = Self::CONSTRAINTS.value() {
98 if !constraints.constraint.in_bound(&etsi_val) {
99 return Err(alloc::format!("Value out of bounds"));
100 }
101 }
102
103 Ok(Self(etsi_val))
104 }
105 }
106
107 impl From<&$t> for f32 {
108 fn from(other: &$t) -> f32 {
109 other.as_meters()
110 }
111 }
112 impl From<$t> for f32 {
113 fn from(other: $t) -> f32 {
114 other.as_meters()
115 }
116 }
117
118 impl TryFrom<f32> for $t {
119 type Error = alloc::string::String;
120
121 fn try_from(value: f32) -> Result<Self, Self::Error> {
122 Self::from_meters(value)
123 }
124 }
125 };
126}
127
128#[cfg(any(
130 feature = "_cdd_1_3_1_1",
131 feature = "_cdd_2_2_1",
132 feature = "_dsrc_2_2_1",
133 feature = "cpm_1"
134))]
135macro_rules! etsi_to_meters_unavailable {
136 ($t:ty, $tt:ty, $conv:expr, $unavailable:expr) => {
137 impl $t {
138 #[must_use]
140 pub fn as_meters(&self) -> f32 {
141 self.0 as f32 / $conv
142 }
143
144 #[must_use]
146 pub fn try_as_meters(&self) -> Option<f32> {
147 if self.is_unavailable() {
148 None
149 } else {
150 Some(self.as_meters())
151 }
152 }
153
154 pub fn from_meters(value: f32) -> Result<Self, alloc::string::String> {
159 use rasn::AsnType;
160
161 #[allow(clippy::cast_possible_truncation)]
162 let etsi_val = (value * $conv) as $tt;
163
164 if let Some(constraints) = Self::CONSTRAINTS.value() {
165 if !constraints.constraint.in_bound(&etsi_val) {
166 return Err(alloc::format!("Value out of bounds"));
167 }
168 }
169
170 if etsi_val == $unavailable {
173 return Err(alloc::format!("Value out of bounds"));
174 }
175
176 Ok(Self(etsi_val))
177 }
178
179 pub fn unavailable() -> Self {
181 Self($unavailable)
182 }
183
184 pub fn is_unavailable(&self) -> bool {
186 self.0 == $unavailable
187 }
188 }
189
190 impl From<&$t> for f32 {
191 fn from(other: &$t) -> f32 {
192 other.as_meters()
193 }
194 }
195 impl From<$t> for f32 {
196 fn from(other: $t) -> f32 {
197 other.as_meters()
198 }
199 }
200
201 impl TryFrom<f32> for $t {
202 type Error = alloc::string::String;
203
204 fn try_from(value: f32) -> Result<Self, Self::Error> {
205 Self::from_meters(value)
206 }
207 }
208 };
209}
210
211#[cfg(feature = "_dsrc_2_2_1")]
212etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB09, i16, 100., -256);
213#[cfg(feature = "_dsrc_2_2_1")]
214etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB10, i16, 100., -512);
215#[cfg(feature = "_dsrc_2_2_1")]
216etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB11, i16, 100., -1024);
217#[cfg(feature = "_dsrc_2_2_1")]
218etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB12, i16, 100., -2048);
219#[cfg(feature = "_dsrc_2_2_1")]
220etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB13, i16, 100., -4096);
221#[cfg(feature = "_dsrc_2_2_1")]
222etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB14, i16, 100., -8192);
223#[cfg(feature = "_dsrc_2_2_1")]
224etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB16, i16, 100., -32768);
225
226#[cfg(feature = "cpm_1")]
227etsi_to_meters!(cpm_1::cpm_pdu_descriptions::DistanceValue, i32, 100.);
228#[cfg(feature = "_cdd_2_2_1")]
229etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::ObjectDimensionValue, u16, 10., 256);
230#[cfg(feature = "cpm_1")]
231etsi_to_meters!(cpm_1::cpm_pdu_descriptions::ObjectDimensionValue, u16, 10.);
232#[cfg(feature = "cpm_1")]
233etsi_to_meters!(cpm_1::cpm_pdu_descriptions::Radius, u16, 10.);
234#[cfg(feature = "cpm_1")]
235etsi_to_meters!(cpm_1::cpm_pdu_descriptions::Range, u16, 10.);
236#[cfg(feature = "cpm_1")]
237etsi_to_meters!(cpm_1::cpm_pdu_descriptions::SemiRangeLength, u16, 10.);
238
239#[cfg(feature = "_cdd_1_3_1_1")]
240etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::VehicleWidth, u8, 10., 62); #[cfg(feature = "_cdd_2_2_1")]
242etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::VehicleWidth, u8, 10., 62); #[cfg(feature = "_cdd_1_3_1_1")]
244etsi_to_meters_unavailable!(
245 cdd_1_3_1_1::its_container::VehicleLengthValue,
246 u16,
247 10.,
248 1023
249); #[cfg(feature = "_cdd_2_2_1")]
251etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::VehicleLengthValue, u16, 10., 1023); #[cfg(feature = "_dsrc_2_2_1")]
254etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::VehicleHeight, u8, 20., 127); #[cfg(feature = "_cdd_2_2_1")]
256etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::VehicleHeight, u8, 20., 127); #[cfg(feature = "_cdd_1_3_1_1")]
259etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::SemiAxisLength, u16, 100., 4095); #[cfg(feature = "_cdd_2_2_1")]
261etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::SemiAxisLength, u16, 100., 4095); #[cfg(feature = "_cdd_1_3_1_1")]
264etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::AltitudeValue, i32, 100., 800001); #[cfg(feature = "_cdd_2_2_1")]
266etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::AltitudeValue, i32, 100., 800001); #[cfg(feature = "_cdd_1_3_1_1")]
269etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::DeltaAltitude, i16, 100., 12800); #[cfg(feature = "_cdd_2_2_1")]
271etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::DeltaAltitude, i16, 100., 12800); #[cfg(feature = "_cdd_1_3_1_1")]
274etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::HeightLonCarr, u8, 100., 100); #[cfg(feature = "_cdd_2_2_1")]
276etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::HeightLonCarr, u8, 100., 100); #[cfg(feature = "_cdd_1_3_1_1")]
279etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::PosLonCarr, u8, 100., 127); #[cfg(feature = "_cdd_2_2_1")]
281etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::PosLonCarr, u8, 100., 127); #[cfg(feature = "_cdd_1_3_1_1")]
284etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::PosFrontAx, u8, 100., 20); #[cfg(feature = "_cdd_2_2_1")]
286etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::PosFrontAx, u8, 100., 20); #[cfg(feature = "_cdd_1_3_1_1")]
289etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::PosPillar, u8, 100., 30); #[cfg(feature = "_cdd_2_2_1")]
291etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::PosPillar, u8, 100., 30); #[cfg(feature = "_cdd_1_3_1_1")]
294etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::WheelBaseVehicle, u8, 100., 127); #[cfg(feature = "_cdd_2_2_1")]
296etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::WheelBaseVehicle, u8, 100., 127); #[cfg(feature = "_cdd_1_3_1_1")]
299etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::TurningRadius, u8, 2.5, 255); #[cfg(feature = "_cdd_2_2_1")]
301etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::TurningRadius, u8, 2.5, 255); #[cfg(feature = "_cdd_2_2_1")] etsi_to_meters_unavailable!(
305 cdd_2_2_1::etsi_its_cdd::CoordinateConfidence,
306 u16,
307 100.,
308 4096
309); #[cfg(feature = "cpm_1")]
312etsi_to_meters!(
313 cpm_1::cpm_pdu_descriptions::LongitudinalLanePositionValue,
314 u16,
315 10.
316); #[cfg(feature = "_cdd_2_2_1")]
318etsi_to_meters_unavailable!(
319 cdd_2_2_1::etsi_its_cdd::LongitudinalLanePositionValue,
320 u16,
321 10.,
322 32767
323); #[cfg(feature = "cpm_1")]
326etsi_to_meters_unavailable!(
327 cpm_1::cpm_pdu_descriptions::LongitudinalLanePositionConfidence,
328 u8,
329 100.,
330 102
331); #[cfg(feature = "_cdd_2_2_1")]
333etsi_to_meters_unavailable!(
334 cdd_2_2_1::etsi_its_cdd::LongitudinalLanePositionConfidence,
335 u16,
336 10.,
337 1023
338); #[cfg(feature = "cpm_1")]
341etsi_to_meters_unavailable!(
342 cpm_1::cpm_pdu_descriptions::ObjectDimensionConfidence,
343 u8,
344 100.,
345 102
346); #[cfg(feature = "_cdd_2_2_1")]
348etsi_to_meters_unavailable!(
349 cdd_2_2_1::etsi_its_cdd::ObjectDimensionConfidence,
350 u8,
351 10.,
352 32
353); #[cfg(feature = "_cdd_2_2_1")] etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::Position1d, i16, 1., 8191); #[cfg(any(feature = "cpm_1", feature = "_cdd_1_3_1_1", feature = "_cdd_2_2_1"))]
360macro_rules! etsi_to_mps {
361 ($t:ty, $tt:ty, $conv:expr, $unavailable:expr) => {
362 impl $t {
363 #[must_use]
365 pub fn as_mps(&self) -> f32 {
366 f32::from(self.0) / $conv
367 }
368
369 #[must_use]
371 pub fn try_as_mps(&self) -> Option<f32> {
372 if self.is_unavailable() {
373 None
374 } else {
375 Some(self.as_mps())
376 }
377 }
378
379 pub fn from_mps(value: f32) -> Result<Self, alloc::string::String> {
384 use rasn::AsnType;
385
386 #[allow(clippy::cast_possible_truncation)]
387 let etsi_val = (value * $conv) as $tt;
388
389 if let Some(constraints) = Self::CONSTRAINTS.value() {
390 if !constraints.constraint.in_bound(&etsi_val) {
391 return Err(alloc::format!("Value out of bounds"));
392 }
393 }
394
395 if etsi_val == $unavailable {
398 return Err(alloc::format!("Value out of bounds"));
399 }
400
401 Ok(Self(etsi_val))
402 }
403
404 #[must_use]
406 pub fn as_kmh(&self) -> f32 {
407 self.as_mps() * MPS_TO_KMH_FACTOR
408 }
409
410 #[must_use]
412 pub fn try_as_kmh(&self) -> Option<f32> {
413 if self.is_unavailable() {
414 None
415 } else {
416 Some(self.as_kmh())
417 }
418 }
419
420 pub fn from_kmh(value: f32) -> Result<Self, alloc::string::String> {
425 Self::from_mps(value / MPS_TO_KMH_FACTOR)
426 }
427
428 pub fn unavailable() -> Self {
430 Self($unavailable)
431 }
432
433 pub fn is_unavailable(&self) -> bool {
435 self.0 == $unavailable
436 }
437 }
438
439 impl From<&$t> for f32 {
440 fn from(other: &$t) -> f32 {
441 other.as_mps()
442 }
443 }
444 impl From<$t> for f32 {
445 fn from(other: $t) -> f32 {
446 other.as_mps()
447 }
448 }
449
450 impl TryFrom<f32> for $t {
451 type Error = alloc::string::String;
452
453 fn try_from(value: f32) -> Result<Self, Self::Error> {
454 Self::from_mps(value)
455 }
456 }
457 };
458}
459
460#[cfg(feature = "cpm_1")]
461etsi_to_mps!(
462 cpm_1::cpm_pdu_descriptions::SpeedValueExtended,
463 i16,
464 100.,
465 16_383
466); #[cfg(feature = "_cdd_1_3_1_1")]
468etsi_to_mps!(cdd_1_3_1_1::its_container::SpeedValue, u16, 100., 16_383); #[cfg(feature = "_cdd_2_2_1")]
470etsi_to_mps!(cdd_2_2_1::etsi_its_cdd::SpeedValue, u16, 100., 16_383); #[cfg(feature = "_cdd_2_2_1")]
473etsi_to_mps!(
474 cdd_2_2_1::etsi_its_cdd::VelocityComponentValue,
475 i16,
476 100.,
477 16_383
478); #[cfg(feature = "_cdd_1_3_1_1")]
481etsi_to_mps!(cdd_1_3_1_1::its_container::SpeedConfidence, u8, 100., 127); #[cfg(feature = "_cdd_2_2_1")]
483etsi_to_mps!(cdd_2_2_1::etsi_its_cdd::SpeedConfidence, u8, 100., 127); #[cfg(feature = "_dsrc_2_2_1")]
486etsi_to_mps!(dsrc_2_2_1::etsi_its_dsrc::Velocity, u16, 50., 8191); #[cfg(any(feature = "_cdd_1_3_1_1", feature = "_cdd_2_2_1"))]
490macro_rules! etsi_to_mpss {
491 ($t:ty, $tt:ty, $conv:expr, $unavailable:expr) => {
492 impl $t {
493 #[must_use]
495 pub fn as_mpss(&self) -> f32 {
496 f32::from(self.0) / $conv
497 }
498
499 #[must_use]
501 pub fn try_as_mpss(&self) -> Option<f32> {
502 if self.is_unavailable() {
503 None
504 } else {
505 Some(self.as_mpss())
506 }
507 }
508
509 pub fn from_mpss(value: f32) -> Result<Self, alloc::string::String> {
514 use rasn::AsnType;
515
516 #[allow(clippy::cast_possible_truncation)]
517 let etsi_val = (value * $conv) as $tt;
518
519 if let Some(constraints) = Self::CONSTRAINTS.value() {
520 if !constraints.constraint.in_bound(&etsi_val) {
521 return Err(alloc::format!("Value out of bounds"));
522 }
523 }
524
525 if etsi_val == $unavailable {
528 return Err(alloc::format!("Value out of bounds"));
529 }
530
531 Ok(Self(etsi_val))
532 }
533
534 pub fn unavailable() -> Self {
536 Self($unavailable)
537 }
538
539 pub fn is_unavailable(&self) -> bool {
541 self.0 == $unavailable
542 }
543 }
544
545 impl From<&$t> for f32 {
546 fn from(other: &$t) -> f32 {
547 other.as_mpss()
548 }
549 }
550 impl From<$t> for f32 {
551 fn from(other: $t) -> f32 {
552 other.as_mpss()
553 }
554 }
555
556 impl TryFrom<f32> for $t {
557 type Error = alloc::string::String;
558
559 fn try_from(value: f32) -> Result<Self, Self::Error> {
560 Self::from_mpss(value)
561 }
562 }
563 };
564}
565
566#[cfg(feature = "_cdd_1_3_1_1")]
567etsi_to_mpss!(
568 cdd_1_3_1_1::its_container::LongitudinalAccelerationValue,
569 i16,
570 10.,
571 161
572); #[cfg(feature = "_cdd_2_2_1")]
574etsi_to_mpss!(
575 cdd_2_2_1::etsi_its_cdd::LongitudinalAccelerationValue,
576 i16,
577 10.,
578 161
579); #[cfg(feature = "_cdd_1_3_1_1")]
581etsi_to_mpss!(
582 cdd_1_3_1_1::its_container::LateralAccelerationValue,
583 i16,
584 10.,
585 161
586); #[cfg(feature = "_cdd_2_2_1")]
588etsi_to_mpss!(
589 cdd_2_2_1::etsi_its_cdd::LateralAccelerationValue,
590 i16,
591 10.,
592 161
593); #[cfg(feature = "_cdd_2_2_1")]
595etsi_to_mpss!(cdd_2_2_1::etsi_its_cdd::AccelerationValue, i16, 10., 161); #[cfg(feature = "_cdd_1_3_1_1")]
598etsi_to_mpss!(
599 cdd_1_3_1_1::its_container::VerticalAccelerationValue,
600 i16,
601 10.,
602 161
603); #[cfg(feature = "_cdd_2_2_1")]
605etsi_to_mpss!(
606 cdd_2_2_1::etsi_its_cdd::VerticalAccelerationValue,
607 i16,
608 10.,
609 161
610); #[cfg(feature = "_cdd_2_2_1")]
613etsi_to_mpss!(
614 cdd_2_2_1::etsi_its_cdd::AccelerationMagnitudeValue,
615 u8,
616 10.,
617 161
618); #[cfg(feature = "_cdd_1_3_1_1")]
621etsi_to_mpss!(
622 cdd_1_3_1_1::its_container::AccelerationConfidence,
623 u8,
624 10.,
625 102
626); #[cfg(feature = "_cdd_2_2_1")]
628etsi_to_mpss!(
629 cdd_2_2_1::etsi_its_cdd::AccelerationConfidence,
630 u8,
631 10.,
632 102
633); #[cfg(any(feature = "_cdd_1_3_1_1", feature = "_cdd_2_2_1"))]
637macro_rules! etsi_raw_unavailable {
638 ($t:ty, $tt:ty, $unavailable:expr) => {
639 impl $t {
640 #[must_use]
642 pub fn try_as_raw(&self) -> Option<$tt> {
643 if self.is_unavailable() {
644 None
645 } else {
646 Some(self.0)
647 }
648 }
649
650 pub fn from_raw(value: $tt) -> Result<Self, alloc::string::String> {
655 use rasn::AsnType;
656
657 if let Some(constraints) = Self::CONSTRAINTS.value() {
658 if !constraints.constraint.in_bound(&value) {
659 return Err(alloc::format!("Value out of bounds"));
660 }
661 }
662
663 if value == $unavailable {
666 return Err(alloc::format!("Value out of bounds"));
667 }
668
669 Ok(Self(value))
670 }
671
672 pub fn unavailable() -> Self {
674 Self($unavailable)
675 }
676
677 pub fn is_unavailable(&self) -> bool {
679 self.0 == $unavailable
680 }
681 }
682
683 impl From<&$t> for $tt {
684 fn from(other: &$t) -> $tt {
685 other.0
686 }
687 }
688 impl From<$t> for $tt {
689 fn from(other: $t) -> $tt {
690 other.0
691 }
692 }
693
694 impl TryFrom<$tt> for $t {
695 type Error = alloc::string::String;
696
697 fn try_from(value: $tt) -> Result<Self, Self::Error> {
698 Self::from_raw(value)
699 }
700 }
701 };
702}
703
704#[cfg(feature = "_cdd_1_3_1_1")]
705etsi_raw_unavailable!(cdd_1_3_1_1::its_container::CurvatureValue, i16, 1023);
706#[cfg(feature = "_cdd_2_2_1")]
707etsi_raw_unavailable!(cdd_2_2_1::etsi_its_cdd::CurvatureValue, i16, 1023);
708
709#[cfg(feature = "_cdd_2_2_1")]
710etsi_raw_unavailable!(cdd_2_2_1::etsi_its_cdd::ConfidenceLevel, u8, 101); #[cfg(feature = "_cdd_2_2_1")]
712etsi_raw_unavailable!(cdd_2_2_1::etsi_its_cdd::CorrelationCellValue, i8, 101); #[cfg(feature = "_cdd_2_2_1")]
714etsi_raw_unavailable!(cdd_2_2_1::etsi_its_cdd::NumberOfOccupants, u8, 127); #[cfg(feature = "_cdd_2_2_1")]
717etsi_raw_unavailable!(cdd_2_2_1::etsi_its_cdd::StabilityLossProbability, u8, 63); #[cfg(feature = "_cdd_2_2_1")]
719etsi_raw_unavailable!(
720 cdd_2_2_1::etsi_its_cdd::TrajectoryInterceptionProbability,
721 u8,
722 63
723); #[cfg(any(
727 feature = "cpm_1",
728 feature = "_cdd_1_3_1_1",
729 feature = "_cdd_2_2_1",
730 feature = "_dsrc_2_2_1"
731))]
732macro_rules! angle_to_deg {
733 ($t:ty, $tt:ty, $conv:expr, $unavailable:expr) => {
734 impl $t {
735 #[must_use]
737 pub fn as_deg(&self) -> f32 {
738 f32::from(self.0) / $conv
739 }
740
741 #[must_use]
743 pub fn try_as_deg(&self) -> Option<f32> {
744 if self.is_unavailable() {
745 None
746 } else {
747 Some(self.as_deg())
748 }
749 }
750
751 pub fn from_deg(value: f32) -> Result<Self, alloc::string::String> {
756 use rasn::AsnType;
757
758 #[allow(clippy::cast_possible_truncation)]
759 let etsi_val = (value * $conv) as $tt;
760
761 if let Some(constraints) = Self::CONSTRAINTS.value() {
762 if !constraints.constraint.in_bound(&etsi_val) {
763 return Err(alloc::format!("Value out of bounds"));
764 }
765 }
766
767 if etsi_val == $unavailable {
770 return Err(alloc::format!("Value out of bounds"));
771 }
772
773 Ok(Self(etsi_val))
774 }
775
776 pub fn unavailable() -> Self {
778 Self($unavailable)
779 }
780
781 pub fn is_unavailable(&self) -> bool {
783 self.0 == $unavailable
784 }
785 }
786
787 impl From<&$t> for f32 {
788 fn from(other: &$t) -> f32 {
789 other.as_deg()
790 }
791 }
792 impl From<$t> for f32 {
793 fn from(other: $t) -> f32 {
794 other.as_deg()
795 }
796 }
797
798 impl TryFrom<f32> for $t {
799 type Error = alloc::string::String;
800
801 fn try_from(value: f32) -> Result<Self, Self::Error> {
802 Self::from_deg(value)
803 }
804 }
805 };
806}
807
808#[cfg(feature = "_cdd_2_2_1")]
809angle_to_deg!(cdd_2_2_1::etsi_its_cdd::CartesianAngleValue, u16, 10., 3601); #[cfg(feature = "cpm_1")]
811angle_to_deg!(
812 cpm_1::cpm_pdu_descriptions::CartesianAngleValue,
813 u16,
814 10.,
815 3601
816); #[cfg(feature = "cpm_1")]
818angle_to_deg!(cpm_1::cpm_pdu_descriptions::WGS84AngleValue, u16, 10., 3601); #[cfg(feature = "_cdd_2_2_1")]
820angle_to_deg!(cdd_2_2_1::etsi_its_cdd::Wgs84AngleValue, u16, 10., 3601); #[cfg(feature = "_dsrc_2_2_1")]
822angle_to_deg!(dsrc_2_2_1::etsi_its_dsrc::Angle, u16, 80., 28800); #[cfg(feature = "_cdd_2_2_1")]
824angle_to_deg!(cdd_2_2_1::etsi_its_cdd::HeadingValue, u16, 10., 3601); #[cfg(feature = "_cdd_1_3_1_1")]
826angle_to_deg!(cdd_1_3_1_1::its_container::HeadingValue, u16, 10., 3601); #[cfg(feature = "_cdd_2_2_1")]
828angle_to_deg!(cdd_2_2_1::etsi_its_cdd::HeadingConfidence, u8, 10., 127); #[cfg(feature = "_cdd_1_3_1_1")]
830angle_to_deg!(cdd_1_3_1_1::its_container::HeadingConfidence, u8, 10., 127); #[cfg(feature = "_cdd_2_2_1")]
833angle_to_deg!(cdd_2_2_1::etsi_its_cdd::AngleConfidence, u8, 10., 127); #[cfg(feature = "cpm_1")]
835angle_to_deg!(cpm_1::cpm_pdu_descriptions::AngleConfidence, u8, 10., 127); #[cfg(feature = "_cdd_2_2_1")]
838angle_to_deg!(
839 cdd_2_2_1::etsi_its_cdd::SteeringWheelAngleValue,
840 i16,
841 (1. / 1.5),
842 512
843); #[cfg(feature = "_cdd_1_3_1_1")]
845angle_to_deg!(
846 cdd_1_3_1_1::its_container::SteeringWheelAngleValue,
847 i16,
848 (1. / 1.5),
849 512
850); #[cfg(feature = "_cdd_2_2_1")]
853angle_to_deg!(
854 cdd_2_2_1::etsi_its_cdd::SteeringWheelAngleConfidence,
855 u8,
856 (1. / 1.5),
857 127
858); #[cfg(feature = "_cdd_1_3_1_1")]
860angle_to_deg!(
861 cdd_1_3_1_1::its_container::SteeringWheelAngleConfidence,
862 u8,
863 (1. / 1.5),
864 127
865); #[cfg(any(feature = "_cdd_2_2_1", feature = "_cdd_1_3_1_1"))]
869macro_rules! angle_to_degrate {
870 ($t:ty, $conv:expr, $unavailable:expr) => {
871 impl $t {
872 #[must_use]
874 pub fn as_deg_rate(&self) -> f32 {
875 f32::from(self.0) / $conv
876 }
877
878 #[must_use]
880 pub fn try_as_deg_rate(&self) -> Option<f32> {
881 if self.is_unavailable() {
882 None
883 } else {
884 Some(self.as_deg_rate())
885 }
886 }
887
888 pub fn from_deg_rate(value: f32) -> Result<Self, alloc::string::String> {
893 use rasn::AsnType;
894
895 #[allow(clippy::cast_possible_truncation)]
896 let etsi_val = (value * $conv) as i16;
897
898 if let Some(constraints) = Self::CONSTRAINTS.value() {
899 if !constraints.constraint.in_bound(&etsi_val) {
900 return Err(alloc::format!("Value out of bounds"));
901 }
902 }
903
904 if etsi_val == $unavailable {
907 return Err(alloc::format!("Value out of bounds"));
908 }
909
910 Ok(Self(etsi_val))
911 }
912
913 pub fn unavailable() -> Self {
915 Self($unavailable)
916 }
917
918 pub fn is_unavailable(&self) -> bool {
920 self.0 == $unavailable
921 }
922 }
923
924 impl From<&$t> for f32 {
925 fn from(other: &$t) -> f32 {
926 other.as_deg_rate()
927 }
928 }
929 impl From<$t> for f32 {
930 fn from(other: $t) -> f32 {
931 other.as_deg_rate()
932 }
933 }
934
935 impl TryFrom<f32> for $t {
936 type Error = alloc::string::String;
937
938 fn try_from(value: f32) -> Result<Self, Self::Error> {
939 Self::from_deg_rate(value)
940 }
941 }
942 };
943}
944
945#[cfg(feature = "_cdd_2_2_1")]
946angle_to_degrate!(cdd_2_2_1::etsi_its_cdd::YawRateValue, 100., 32767); #[cfg(feature = "_cdd_1_3_1_1")]
948angle_to_degrate!(cdd_1_3_1_1::its_container::YawRateValue, 100., 32767); #[cfg(feature = "_cdd_2_2_1")]
951angle_to_degrate!(
952 cdd_2_2_1::etsi_its_cdd::CartesianAngularVelocityComponentValue,
953 1.,
954 256
955); #[cfg(feature = "_dsrc_2_2_1")]
959impl dsrc_2_2_1::etsi_its_dsrc::DeltaTime {
960 #[must_use]
962 pub fn as_sec(&self) -> i16 {
963 i16::from(self.0) * 10
964 }
965
966 #[must_use]
968 pub fn try_as_sec(&self) -> Option<i16> {
969 if self.is_unavailable() {
970 None
971 } else {
972 Some(self.as_sec())
973 }
974 }
975
976 #[must_use]
978 pub fn from_sec(value: i16) -> Self {
979 #[allow(clippy::cast_possible_truncation)]
980 let etsi_val = (value / 10) as i8;
981
982 Self(etsi_val.clamp(-121, 120))
983 }
984
985 pub fn unavailable() -> Self {
987 Self(-122)
988 }
989
990 pub fn is_unavailable(&self) -> bool {
992 self.0 == -122
993 }
994}
995
996#[cfg(feature = "_dsrc_2_2_1")]
997impl From<&dsrc_2_2_1::etsi_its_dsrc::DeltaTime> for i16 {
998 fn from(other: &dsrc_2_2_1::etsi_its_dsrc::DeltaTime) -> i16 {
999 other.as_sec()
1000 }
1001}
1002#[cfg(feature = "_dsrc_2_2_1")]
1003impl From<dsrc_2_2_1::etsi_its_dsrc::DeltaTime> for i16 {
1004 fn from(other: dsrc_2_2_1::etsi_its_dsrc::DeltaTime) -> i16 {
1005 other.as_sec()
1006 }
1007}
1008
1009#[cfg(feature = "_dsrc_2_2_1")]
1011impl dsrc_2_2_1::etsi_its_dsrc::DSecond {
1012 #[must_use]
1014 pub fn as_millis(&self) -> u16 {
1015 self.0
1016 }
1017
1018 #[must_use]
1020 pub fn try_as_millis(&self) -> Option<u16> {
1021 if self.is_unavailable() {
1022 None
1023 } else {
1024 Some(self.as_millis())
1025 }
1026 }
1027
1028 pub fn from_millis(value: u16) -> Result<Self, alloc::string::String> {
1033 if value > 60999 {
1036 return Err(alloc::format!("Value out of bounds"));
1037 }
1038
1039 Ok(Self(value))
1040 }
1041
1042 pub fn unavailable() -> Self {
1044 Self(65535)
1045 }
1046
1047 pub fn is_unavailable(&self) -> bool {
1049 self.0 == 65535
1050 }
1051}
1052
1053#[cfg(feature = "_dsrc_2_2_1")]
1054impl From<&dsrc_2_2_1::etsi_its_dsrc::DSecond> for u16 {
1055 fn from(other: &dsrc_2_2_1::etsi_its_dsrc::DSecond) -> u16 {
1056 other.as_millis()
1057 }
1058}
1059#[cfg(feature = "_dsrc_2_2_1")]
1060impl From<dsrc_2_2_1::etsi_its_dsrc::DSecond> for u16 {
1061 fn from(other: dsrc_2_2_1::etsi_its_dsrc::DSecond) -> u16 {
1062 other.as_millis()
1063 }
1064}
1065
1066#[cfg(feature = "_dsrc_2_2_1")]
1068impl dsrc_2_2_1::etsi_its_dsrc::TimeMark {
1069 const CONV_FACTOR: u32 = 100;
1070 const UNKNOWN: u16 = 36001;
1071 const OUT_OF_RANGE: u16 = 36000;
1072
1073 #[must_use]
1075 pub fn as_millis(&self) -> u32 {
1076 self.0 as u32 * Self::CONV_FACTOR
1077 }
1078
1079 #[must_use]
1081 pub fn try_as_millis(&self) -> Option<u32> {
1082 if self.is_unknown() {
1083 None
1084 } else {
1085 Some(self.as_millis())
1086 }
1087 }
1088
1089 pub fn from_millis(value: u32) -> Result<Self, alloc::string::String> {
1094 #[allow(clippy::cast_possible_truncation)]
1095 let etsi_val = (value / Self::CONV_FACTOR) as u16;
1096
1097 if etsi_val > 35999 {
1099 return Err(alloc::format!("Value out of bounds"));
1100 }
1101
1102 Ok(Self(etsi_val))
1103 }
1104
1105 pub fn unknown() -> Self {
1107 Self(Self::UNKNOWN)
1108 }
1109
1110 pub fn is_unknown(&self) -> bool {
1112 self.0 == Self::UNKNOWN
1113 }
1114
1115 pub fn out_of_range() -> Self {
1117 Self(Self::OUT_OF_RANGE)
1118 }
1119
1120 pub fn is_out_of_range(&self) -> bool {
1122 self.0 == Self::OUT_OF_RANGE
1123 }
1124}
1125
1126#[cfg(feature = "_dsrc_2_2_1")]
1127impl From<&dsrc_2_2_1::etsi_its_dsrc::TimeMark> for u32 {
1128 fn from(other: &dsrc_2_2_1::etsi_its_dsrc::TimeMark) -> u32 {
1129 other.as_millis()
1130 }
1131}
1132#[cfg(feature = "_dsrc_2_2_1")]
1133impl From<dsrc_2_2_1::etsi_its_dsrc::TimeMark> for u32 {
1134 fn from(other: dsrc_2_2_1::etsi_its_dsrc::TimeMark) -> u32 {
1135 other.as_millis()
1136 }
1137}
1138
1139#[cfg(any(feature = "_cdd_2_2_1", feature = "_cdd_1_3_1_1"))]
1143macro_rules! path_delta_time_to_millis {
1144 ($t:ty) => {
1145 impl $t {
1146 const ETSI_TO_MS_FACTOR: u32 = 10; #[must_use]
1150 pub fn as_millis(&self) -> u32 {
1151 let etsi_val: i64 = (&self.0).try_into().unwrap_or_default();
1152
1153 #[allow(clippy::cast_possible_truncation)]
1154 let etsi_val = etsi_val as u32;
1155 etsi_val * Self::ETSI_TO_MS_FACTOR
1156 }
1157
1158 pub fn from_millis(value: u32) -> Result<Self, alloc::string::String> {
1163 use rasn::AsnType;
1164
1165 #[allow(clippy::cast_possible_truncation)]
1166 let etsi_val = value / Self::ETSI_TO_MS_FACTOR;
1167
1168 if let Some(constraints) = Self::CONSTRAINTS.value() {
1169 if !constraints.constraint.in_bound(&etsi_val) {
1170 return Err(alloc::format!("Value out of bounds"));
1171 }
1172 }
1173
1174 Ok(Self(etsi_val.into()))
1175 }
1176 }
1177 };
1178}
1179
1180#[cfg(feature = "_cdd_1_3_1_1")]
1181path_delta_time_to_millis!(cdd_1_3_1_1::its_container::PathDeltaTime);
1182#[cfg(feature = "_cdd_2_2_1")]
1183path_delta_time_to_millis!(cdd_2_2_1::etsi_its_cdd::PathDeltaTime);
1184
1185#[cfg(feature = "_dsrc_2_2_1")]
1187impl dsrc_2_2_1::etsi_its_dsrc::MinuteOfTheYear {
1188 pub fn invalid() -> Self {
1190 Self(527040)
1191 }
1192
1193 pub fn is_invalid(&self) -> bool {
1195 self.0 == 527040
1196 }
1197}
1198
1199#[cfg(feature = "_dsrc_2_2_1")]
1201impl crate::standards::dsrc_2_2_1::etsi_its_dsrc::MsgCount {
1202 pub fn increment(&self) -> Self {
1203 Self((self.0 + 1) % 128)
1204 }
1205}
1206#[cfg(feature = "_dsrc_2_2_1")]
1207impl From<u8> for dsrc_2_2_1::etsi_its_dsrc::MsgCount {
1208 fn from(value: u8) -> Self {
1209 Self(value % 128)
1210 }
1211}
1212
1213#[cfg(feature = "_dsrc_2_2_1")]
1215impl crate::standards::dsrc_2_2_1::etsi_its_dsrc::RequestID {
1216 pub fn increment(&self) -> Self {
1217 Self(self.0.wrapping_add(1))
1218 }
1219}
1220#[cfg(feature = "_dsrc_2_2_1")]
1221impl From<u8> for dsrc_2_2_1::etsi_its_dsrc::RequestID {
1222 fn from(value: u8) -> Self {
1224 Self(value)
1225 }
1226}
1227
1228#[cfg(feature = "_dsrc_2_2_1")]
1231impl dsrc_2_2_1::etsi_its_dsrc::SpeedLimitList {
1232 pub fn get_speed_limit_mps(
1234 &self,
1235 limit_type: dsrc_2_2_1::etsi_its_dsrc::SpeedLimitType,
1236 ) -> Option<f32> {
1237 self.0.iter().find_map(|item| {
1238 if item.r_type == limit_type {
1239 Some(item.speed.as_mps())
1240 } else {
1241 None
1242 }
1243 })
1244 }
1245}
1246
1247#[cfg(feature = "_cdd_2_2_1")]
1248impl cdd_2_2_1::etsi_its_cdd::CauseCodeChoice {
1249 pub fn to_u8_tuple(&self) -> (u8, u8) {
1251 match self {
1252 Self::reserved0(scc) => (0, scc.0),
1253 Self::trafficCondition1(scc) => (1, scc.0),
1254 Self::accident2(scc) => (2, scc.0),
1255 Self::roadworks3(scc) => (3, scc.0),
1256 Self::reserved4(scc) => (4, scc.0),
1257 Self::impassability5(scc) => (5, scc.0),
1258 Self::adverseWeatherCondition_Adhesion6(scc) => (6, scc.0),
1259 Self::aquaplaning7(scc) => (7, scc.0),
1260 Self::reserved8(scc) => (8, scc.0),
1261 Self::hazardousLocation_SurfaceCondition9(scc) => (9, scc.0),
1262 Self::hazardousLocation_ObstacleOnTheRoad10(scc) => (10, scc.0),
1263 Self::hazardousLocation_AnimalOnTheRoad11(scc) => (11, scc.0),
1264 Self::humanPresenceOnTheRoad12(scc) => (12, scc.0),
1265 Self::reserved13(scc) => (13, scc.0),
1266 Self::wrongWayDriving14(scc) => (14, scc.0),
1267 Self::rescueAndRecoveryWorkInProgress15(scc) => (15, scc.0),
1268 Self::reserved16(scc) => (16, scc.0),
1269 Self::adverseWeatherCondition_ExtremeWeatherCondition17(scc) => (17, scc.0),
1270 Self::adverseWeatherCondition_Visibility18(scc) => (18, scc.0),
1271 Self::adverseWeatherCondition_Precipitation19(scc) => (19, scc.0),
1272 Self::violence20(scc) => (20, scc.0),
1273 Self::reserved21(scc) => (21, scc.0),
1274 Self::reserved22(scc) => (22, scc.0),
1275 Self::reserved23(scc) => (23, scc.0),
1276 Self::reserved24(scc) => (24, scc.0),
1277 Self::reserved25(scc) => (25, scc.0),
1278 Self::slowVehicle26(scc) => (26, scc.0),
1279 Self::dangerousEndOfQueue27(scc) => (27, scc.0),
1280 Self::publicTransportVehicleApproaching28(scc) => (28, scc.0),
1281 Self::reserved29(scc) => (29, scc.0),
1282 Self::reserved30(scc) => (30, scc.0),
1283 Self::reserved31(scc) => (31, scc.0),
1284 Self::reserved32(scc) => (32, scc.0),
1285 Self::reserved33(scc) => (33, scc.0),
1286 Self::reserved34(scc) => (34, scc.0),
1287 Self::reserved35(scc) => (35, scc.0),
1288 Self::reserved36(scc) => (36, scc.0),
1289 Self::reserved37(scc) => (37, scc.0),
1290 Self::reserved38(scc) => (38, scc.0),
1291 Self::reserved39(scc) => (39, scc.0),
1292 Self::reserved40(scc) => (40, scc.0),
1293 Self::reserved41(scc) => (41, scc.0),
1294 Self::reserved42(scc) => (42, scc.0),
1295 Self::reserved43(scc) => (43, scc.0),
1296 Self::reserved44(scc) => (44, scc.0),
1297 Self::reserved45(scc) => (45, scc.0),
1298 Self::reserved46(scc) => (46, scc.0),
1299 Self::reserved47(scc) => (47, scc.0),
1300 Self::reserved48(scc) => (48, scc.0),
1301 Self::reserved49(scc) => (49, scc.0),
1302 Self::reserved50(scc) => (50, scc.0),
1303 Self::reserved51(scc) => (51, scc.0),
1304 Self::reserved52(scc) => (52, scc.0),
1305 Self::reserved53(scc) => (53, scc.0),
1306 Self::reserved54(scc) => (54, scc.0),
1307 Self::reserved55(scc) => (55, scc.0),
1308 Self::reserved56(scc) => (56, scc.0),
1309 Self::reserved57(scc) => (57, scc.0),
1310 Self::reserved58(scc) => (58, scc.0),
1311 Self::reserved59(scc) => (59, scc.0),
1312 Self::reserved60(scc) => (60, scc.0),
1313 Self::reserved61(scc) => (61, scc.0),
1314 Self::reserved62(scc) => (62, scc.0),
1315 Self::reserved63(scc) => (63, scc.0),
1316 Self::reserved64(scc) => (64, scc.0),
1317 Self::reserved65(scc) => (65, scc.0),
1318 Self::reserved66(scc) => (66, scc.0),
1319 Self::reserved67(scc) => (67, scc.0),
1320 Self::reserved68(scc) => (68, scc.0),
1321 Self::reserved69(scc) => (69, scc.0),
1322 Self::reserved70(scc) => (70, scc.0),
1323 Self::reserved71(scc) => (71, scc.0),
1324 Self::reserved72(scc) => (72, scc.0),
1325 Self::reserved73(scc) => (73, scc.0),
1326 Self::reserved74(scc) => (74, scc.0),
1327 Self::reserved75(scc) => (75, scc.0),
1328 Self::reserved76(scc) => (76, scc.0),
1329 Self::reserved77(scc) => (77, scc.0),
1330 Self::reserved78(scc) => (78, scc.0),
1331 Self::reserved79(scc) => (79, scc.0),
1332 Self::reserved80(scc) => (80, scc.0),
1333 Self::reserved81(scc) => (81, scc.0),
1334 Self::reserved82(scc) => (82, scc.0),
1335 Self::reserved83(scc) => (83, scc.0),
1336 Self::reserved84(scc) => (84, scc.0),
1337 Self::reserved85(scc) => (85, scc.0),
1338 Self::reserved86(scc) => (86, scc.0),
1339 Self::reserved87(scc) => (87, scc.0),
1340 Self::reserved88(scc) => (88, scc.0),
1341 Self::reserved89(scc) => (89, scc.0),
1342 Self::reserved90(scc) => (90, scc.0),
1343 Self::vehicleBreakdown91(scc) => (91, scc.0),
1344 Self::postCrash92(scc) => (92, scc.0),
1345 Self::humanProblem93(scc) => (93, scc.0),
1346 Self::stationaryVehicle94(scc) => (94, scc.0),
1347 Self::emergencyVehicleApproaching95(scc) => (95, scc.0),
1348 Self::hazardousLocation_DangerousCurve96(scc) => (96, scc.0),
1349 Self::collisionRisk97(scc) => (97, scc.0),
1350 Self::signalViolation98(scc) => (98, scc.0),
1351 Self::dangerousSituation99(scc) => (99, scc.0),
1352 Self::railwayLevelCrossing100(scc) => (100, scc.0),
1353 Self::reserved101(scc) => (101, scc.0),
1354 Self::reserved102(scc) => (102, scc.0),
1355 Self::reserved103(scc) => (103, scc.0),
1356 Self::reserved104(scc) => (104, scc.0),
1357 Self::reserved105(scc) => (105, scc.0),
1358 Self::reserved106(scc) => (106, scc.0),
1359 Self::reserved107(scc) => (107, scc.0),
1360 Self::reserved108(scc) => (108, scc.0),
1361 Self::reserved109(scc) => (109, scc.0),
1362 Self::reserved110(scc) => (110, scc.0),
1363 Self::reserved111(scc) => (111, scc.0),
1364 Self::reserved112(scc) => (112, scc.0),
1365 Self::reserved113(scc) => (113, scc.0),
1366 Self::reserved114(scc) => (114, scc.0),
1367 Self::reserved115(scc) => (115, scc.0),
1368 Self::reserved116(scc) => (116, scc.0),
1369 Self::reserved117(scc) => (117, scc.0),
1370 Self::reserved118(scc) => (118, scc.0),
1371 Self::reserved119(scc) => (119, scc.0),
1372 Self::reserved120(scc) => (120, scc.0),
1373 Self::reserved121(scc) => (121, scc.0),
1374 Self::reserved122(scc) => (122, scc.0),
1375 Self::reserved123(scc) => (123, scc.0),
1376 Self::reserved124(scc) => (124, scc.0),
1377 Self::reserved125(scc) => (125, scc.0),
1378 Self::reserved126(scc) => (126, scc.0),
1379 Self::reserved127(scc) => (127, scc.0),
1380 Self::reserved128(scc) => (128, scc.0),
1381 }
1382 }
1383}