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
18macro_rules! latlon_to_deg {
20 ($t:ty, $unavailable:expr) => {
21 impl $t {
22 #[must_use]
24 pub fn as_deg(&self) -> f64 {
25 f64::from(self.0) / 10_000_000.
26 }
27
28 #[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 #[must_use]
40 pub fn from_deg(other: f64) -> Self {
41 Self((other * 10_000_000.) as i32)
42 }
43
44 pub fn unavailable() -> Self {
46 Self($unavailable)
47 }
48
49 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#[cfg(feature = "cpm_1")]
77macro_rules! etsi_to_meters {
78 ($t:ty, $tt:ty, $conv:expr) => {
79 impl $t {
80 #[must_use]
82 pub fn as_meters(&self) -> f32 {
83 self.0 as f32 / $conv
84 }
85
86 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#[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 #[must_use]
139 pub fn as_meters(&self) -> f32 {
140 self.0 as f32 / $conv
141 }
142
143 #[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 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 if etsi_val == $unavailable {
172 return Err(alloc::format!("Value out of bounds"));
173 }
174
175 Ok(Self(etsi_val))
176 }
177
178 pub fn unavailable() -> Self {
180 Self($unavailable)
181 }
182
183 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); #[cfg(feature = "_cdd_2_2_1")]
241etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::VehicleWidth, u8, 10., 62); #[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); #[cfg(feature = "_cdd_2_2_1")]
250etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::VehicleLengthValue, u16, 10., 1023); #[cfg(feature = "_dsrc_2_2_1")]
253etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::VehicleHeight, u8, 20., 127); #[cfg(feature = "_cdd_2_2_1")]
255etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::VehicleHeight, u8, 20., 127); #[cfg(feature = "_cdd_1_3_1_1")]
258etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::SemiAxisLength, u16, 100., 4095); #[cfg(feature = "_cdd_2_2_1")]
260etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::SemiAxisLength, u16, 100., 4095); #[cfg(feature = "_cdd_1_3_1_1")]
263etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::AltitudeValue, i32, 100., 800001); #[cfg(feature = "_cdd_2_2_1")]
265etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::AltitudeValue, i32, 100., 800001); #[cfg(feature = "_cdd_1_3_1_1")]
268etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::DeltaAltitude, i16, 100., 12800); #[cfg(feature = "_cdd_2_2_1")]
270etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::DeltaAltitude, i16, 100., 12800); #[cfg(feature = "_cdd_1_3_1_1")]
273etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::HeightLonCarr, u8, 100., 100); #[cfg(feature = "_cdd_2_2_1")]
275etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::HeightLonCarr, u8, 100., 100); #[cfg(feature = "_cdd_1_3_1_1")]
278etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::PosLonCarr, u8, 100., 127); #[cfg(feature = "_cdd_2_2_1")]
280etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::PosLonCarr, u8, 100., 127); #[cfg(feature = "_cdd_1_3_1_1")]
283etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::PosFrontAx, u8, 100., 20); #[cfg(feature = "_cdd_2_2_1")]
285etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::PosFrontAx, u8, 100., 20); #[cfg(feature = "_cdd_1_3_1_1")]
288etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::PosPillar, u8, 100., 30); #[cfg(feature = "_cdd_2_2_1")]
290etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::PosPillar, u8, 100., 30); #[cfg(feature = "_cdd_1_3_1_1")]
293etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::WheelBaseVehicle, u8, 100., 127); #[cfg(feature = "_cdd_2_2_1")]
295etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::WheelBaseVehicle, u8, 100., 127); #[cfg(feature = "_cdd_1_3_1_1")]
298etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::TurningRadius, u8, 2.5, 255); #[cfg(feature = "_cdd_2_2_1")]
300etsi_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!(
304 cdd_2_2_1::etsi_its_cdd::CoordinateConfidence,
305 u16,
306 100.,
307 4096
308); #[cfg(feature = "cpm_1")]
311etsi_to_meters!(
312 cpm_1::cpm_pdu_descriptions::LongitudinalLanePositionValue,
313 u16,
314 10.
315); #[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); #[cfg(feature = "cpm_1")]
325etsi_to_meters_unavailable!(
326 cpm_1::cpm_pdu_descriptions::LongitudinalLanePositionConfidence,
327 u8,
328 100.,
329 102
330); #[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); #[cfg(feature = "cpm_1")]
340etsi_to_meters_unavailable!(
341 cpm_1::cpm_pdu_descriptions::ObjectDimensionConfidence,
342 u8,
343 100.,
344 102
345); #[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); #[cfg(feature = "_cdd_2_2_1")] etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::Position1d, i16, 1., 8191); macro_rules! etsi_to_mps {
359 ($t:ty, $tt:ty, $conv:expr, $unavailable:expr) => {
360 impl $t {
361 #[must_use]
363 pub fn as_mps(&self) -> f32 {
364 f32::from(self.0) / $conv
365 }
366
367 #[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 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 if etsi_val == $unavailable {
396 return Err(alloc::format!("Value out of bounds"));
397 }
398
399 Ok(Self(etsi_val))
400 }
401
402 #[must_use]
404 pub fn as_kmh(&self) -> f32 {
405 self.as_mps() * MPS_TO_KMH_FACTOR
406 }
407
408 #[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 pub fn from_kmh(value: f32) -> Result<Self, alloc::string::String> {
423 Self::from_mps(value / MPS_TO_KMH_FACTOR)
424 }
425
426 pub fn unavailable() -> Self {
428 Self($unavailable)
429 }
430
431 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); #[cfg(feature = "_cdd_1_3_1_1")]
466etsi_to_mps!(cdd_1_3_1_1::its_container::SpeedValue, u16, 100., 16_383); #[cfg(feature = "_cdd_2_2_1")]
468etsi_to_mps!(cdd_2_2_1::etsi_its_cdd::SpeedValue, u16, 100., 16_383); #[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); #[cfg(feature = "_cdd_1_3_1_1")]
479etsi_to_mps!(cdd_1_3_1_1::its_container::SpeedConfidence, u8, 100., 127); #[cfg(feature = "_cdd_2_2_1")]
481etsi_to_mps!(cdd_2_2_1::etsi_its_cdd::SpeedConfidence, u8, 100., 127); #[cfg(feature = "_dsrc_2_2_1")]
484etsi_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"))]
488macro_rules! etsi_to_mpss {
489 ($t:ty, $tt:ty, $conv:expr, $unavailable:expr) => {
490 impl $t {
491 #[must_use]
493 pub fn as_mpss(&self) -> f32 {
494 f32::from(self.0) / $conv
495 }
496
497 #[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 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 if etsi_val == $unavailable {
526 return Err(alloc::format!("Value out of bounds"));
527 }
528
529 Ok(Self(etsi_val))
530 }
531
532 pub fn unavailable() -> Self {
534 Self($unavailable)
535 }
536
537 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); #[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); #[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); #[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); #[cfg(feature = "_cdd_2_2_1")]
593etsi_to_mpss!(cdd_2_2_1::etsi_its_cdd::AccelerationValue, i16, 10., 161); #[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); #[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); #[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); #[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); #[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); #[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 #[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 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 if value == $unavailable {
664 return Err(alloc::format!("Value out of bounds"));
665 }
666
667 Ok(Self(value))
668 }
669
670 pub fn unavailable() -> Self {
672 Self($unavailable)
673 }
674
675 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); #[cfg(feature = "_cdd_2_2_1")]
710etsi_raw_unavailable!(cdd_2_2_1::etsi_its_cdd::CorrelationCellValue, i8, 101); #[cfg(feature = "_cdd_2_2_1")]
712etsi_raw_unavailable!(cdd_2_2_1::etsi_its_cdd::NumberOfOccupants, u8, 127); #[cfg(feature = "_cdd_2_2_1")]
715etsi_raw_unavailable!(cdd_2_2_1::etsi_its_cdd::StabilityLossProbability, u8, 63); #[cfg(feature = "_cdd_2_2_1")]
717etsi_raw_unavailable!(
718 cdd_2_2_1::etsi_its_cdd::TrajectoryInterceptionProbability,
719 u8,
720 63
721); #[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 #[must_use]
735 pub fn as_deg(&self) -> f32 {
736 f32::from(self.0) / $conv
737 }
738
739 #[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 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 if etsi_val == $unavailable {
768 return Err(alloc::format!("Value out of bounds"));
769 }
770
771 Ok(Self(etsi_val))
772 }
773
774 pub fn unavailable() -> Self {
776 Self($unavailable)
777 }
778
779 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); #[cfg(feature = "cpm_1")]
809angle_to_deg!(
810 cpm_1::cpm_pdu_descriptions::CartesianAngleValue,
811 u16,
812 10.,
813 3601
814); #[cfg(feature = "cpm_1")]
816angle_to_deg!(cpm_1::cpm_pdu_descriptions::WGS84AngleValue, u16, 10., 3601); #[cfg(feature = "_cdd_2_2_1")]
818angle_to_deg!(cdd_2_2_1::etsi_its_cdd::Wgs84AngleValue, u16, 10., 3601); #[cfg(feature = "_dsrc_2_2_1")]
820angle_to_deg!(dsrc_2_2_1::etsi_its_dsrc::Angle, u16, 80., 28800); #[cfg(feature = "_cdd_2_2_1")]
822angle_to_deg!(cdd_2_2_1::etsi_its_cdd::HeadingValue, u16, 10., 3601); #[cfg(feature = "_cdd_1_3_1_1")]
824angle_to_deg!(cdd_1_3_1_1::its_container::HeadingValue, u16, 10., 3601); #[cfg(feature = "_cdd_2_2_1")]
826angle_to_deg!(cdd_2_2_1::etsi_its_cdd::HeadingConfidence, u8, 10., 127); #[cfg(feature = "_cdd_1_3_1_1")]
828angle_to_deg!(cdd_1_3_1_1::its_container::HeadingConfidence, u8, 10., 127); #[cfg(feature = "_cdd_2_2_1")]
831angle_to_deg!(cdd_2_2_1::etsi_its_cdd::AngleConfidence, u8, 10., 127); #[cfg(feature = "cpm_1")]
833angle_to_deg!(cpm_1::cpm_pdu_descriptions::AngleConfidence, u8, 10., 127); #[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); #[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); #[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); #[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); #[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 #[must_use]
872 pub fn as_deg_rate(&self) -> f32 {
873 f32::from(self.0) / $conv
874 }
875
876 #[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 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 if etsi_val == $unavailable {
905 return Err(alloc::format!("Value out of bounds"));
906 }
907
908 Ok(Self(etsi_val))
909 }
910
911 pub fn unavailable() -> Self {
913 Self($unavailable)
914 }
915
916 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); #[cfg(feature = "_cdd_1_3_1_1")]
946angle_to_degrate!(cdd_1_3_1_1::its_container::YawRateValue, 100., 32767); #[cfg(feature = "_cdd_2_2_1")]
949angle_to_degrate!(
950 cdd_2_2_1::etsi_its_cdd::CartesianAngularVelocityComponentValue,
951 1.,
952 256
953); #[cfg(feature = "_dsrc_2_2_1")]
957impl dsrc_2_2_1::etsi_its_dsrc::DeltaTime {
958 #[must_use]
960 pub fn as_sec(&self) -> i16 {
961 i16::from(self.0) * 10
962 }
963
964 #[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 #[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 pub fn unavailable() -> Self {
985 Self(-122)
986 }
987
988 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#[cfg(feature = "_dsrc_2_2_1")]
1009impl dsrc_2_2_1::etsi_its_dsrc::DSecond {
1010 #[must_use]
1012 pub fn as_millis(&self) -> u16 {
1013 self.0
1014 }
1015
1016 #[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 pub fn from_millis(value: u16) -> Result<Self, alloc::string::String> {
1031 if value > 60999 {
1034 return Err(alloc::format!("Value out of bounds"));
1035 }
1036
1037 Ok(Self(value))
1038 }
1039
1040 pub fn unavailable() -> Self {
1042 Self(65535)
1043 }
1044
1045 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#[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 #[must_use]
1073 pub fn as_millis(&self) -> u32 {
1074 self.0 as u32 * Self::CONV_FACTOR
1075 }
1076
1077 #[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 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 if etsi_val > 35999 {
1097 return Err(alloc::format!("Value out of bounds"));
1098 }
1099
1100 Ok(Self(etsi_val))
1101 }
1102
1103 pub fn unknown() -> Self {
1105 Self(Self::UNKNOWN)
1106 }
1107
1108 pub fn is_unknown(&self) -> bool {
1110 self.0 == Self::UNKNOWN
1111 }
1112
1113 pub fn out_of_range() -> Self {
1115 Self(Self::OUT_OF_RANGE)
1116 }
1117
1118 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#[cfg(feature = "_dsrc_2_2_1")]
1139impl dsrc_2_2_1::etsi_its_dsrc::MinuteOfTheYear {
1140 pub fn invalid() -> Self {
1142 Self(527040)
1143 }
1144
1145 pub fn is_invalid(&self) -> bool {
1147 self.0 == 527040
1148 }
1149}
1150
1151#[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#[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 fn from(value: u8) -> Self {
1176 Self(value)
1177 }
1178}
1179
1180#[cfg(feature = "_dsrc_2_2_1")]
1183impl dsrc_2_2_1::etsi_its_dsrc::SpeedLimitList {
1184 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 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}