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))]
133macro_rules! etsi_to_meters_unavailable {
134 ($t:ty, $tt:ty, $conv:expr, $unavailable:expr) => {
135 impl $t {
136 #[must_use]
138 pub fn as_meters(&self) -> f32 {
139 self.0 as f32 / $conv
140 }
141
142 #[must_use]
144 pub fn try_as_meters(&self) -> Option<f32> {
145 if self.is_unavailable() {
146 None
147 } else {
148 Some(self.as_meters())
149 }
150 }
151
152 pub fn from_meters(value: f32) -> Result<Self, alloc::string::String> {
157 use rasn::AsnType;
158
159 #[allow(clippy::cast_possible_truncation)]
160 let etsi_val = (value * $conv) as $tt;
161
162 if let Some(constraints) = Self::CONSTRAINTS.value() {
163 if !constraints.constraint.in_bound(&etsi_val) {
164 return Err(alloc::format!("Value out of bounds"));
165 }
166 }
167
168 if etsi_val == $unavailable {
171 return Err(alloc::format!("Value out of bounds"));
172 }
173
174 Ok(Self(etsi_val))
175 }
176
177 pub fn unavailable() -> Self {
179 Self($unavailable)
180 }
181
182 pub fn is_unavailable(&self) -> bool {
184 self.0 == $unavailable
185 }
186 }
187
188 impl From<&$t> for f32 {
189 fn from(other: &$t) -> f32 {
190 other.as_meters()
191 }
192 }
193 impl From<$t> for f32 {
194 fn from(other: $t) -> f32 {
195 other.as_meters()
196 }
197 }
198
199 impl TryFrom<f32> for $t {
200 type Error = alloc::string::String;
201
202 fn try_from(value: f32) -> Result<Self, Self::Error> {
203 Self::from_meters(value)
204 }
205 }
206 };
207}
208
209#[cfg(feature = "_dsrc_2_2_1")]
210etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB09, i16, 100., -256);
211#[cfg(feature = "_dsrc_2_2_1")]
212etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB10, i16, 100., -512);
213#[cfg(feature = "_dsrc_2_2_1")]
214etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB11, i16, 100., -1024);
215#[cfg(feature = "_dsrc_2_2_1")]
216etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB12, i16, 100., -2048);
217#[cfg(feature = "_dsrc_2_2_1")]
218etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB13, i16, 100., -4096);
219#[cfg(feature = "_dsrc_2_2_1")]
220etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB14, i16, 100., -8192);
221#[cfg(feature = "_dsrc_2_2_1")]
222etsi_to_meters_unavailable!(dsrc_2_2_1::etsi_its_dsrc::OffsetB16, i16, 100., -32768);
223
224#[cfg(feature = "cpm_1")]
225etsi_to_meters!(cpm_1::cpm_pdu_descriptions::DistanceValue, i32, 100.);
226#[cfg(feature = "_cdd_2_2_1")]
227etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::ObjectDimensionValue, u16, 10., 256);
228#[cfg(feature = "cpm_1")]
229etsi_to_meters!(cpm_1::cpm_pdu_descriptions::ObjectDimensionValue, u16, 10.);
230#[cfg(feature = "cpm_1")]
231etsi_to_meters!(cpm_1::cpm_pdu_descriptions::Radius, u16, 10.);
232#[cfg(feature = "cpm_1")]
233etsi_to_meters!(cpm_1::cpm_pdu_descriptions::Range, u16, 10.);
234#[cfg(feature = "cpm_1")]
235etsi_to_meters!(cpm_1::cpm_pdu_descriptions::SemiRangeLength, u16, 10.);
236
237#[cfg(feature = "_cdd_1_3_1_1")]
238etsi_to_meters_unavailable!(cdd_1_3_1_1::its_container::VehicleWidth, u8, 10., 62);
239#[cfg(feature = "_cdd_2_2_1")]
240etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::VehicleWidth, u8, 10., 62);
241#[cfg(feature = "_cdd_1_3_1_1")]
242etsi_to_meters_unavailable!(
243 cdd_1_3_1_1::its_container::VehicleLengthValue,
244 u16,
245 10.,
246 1023
247);
248#[cfg(feature = "_cdd_2_2_1")]
249etsi_to_meters_unavailable!(cdd_2_2_1::etsi_its_cdd::VehicleLengthValue, u16, 10., 1023);
250
251macro_rules! etsi_to_mps {
253 ($t:ty, $tt:ty, $conv:expr, $unavailable:expr) => {
254 impl $t {
255 #[must_use]
257 pub fn as_mps(&self) -> f32 {
258 f32::from(self.0) / $conv
259 }
260
261 #[must_use]
263 pub fn try_as_mps(&self) -> Option<f32> {
264 if self.is_unavailable() {
265 None
266 } else {
267 Some(self.as_mps())
268 }
269 }
270
271 pub fn from_mps(value: f32) -> Result<Self, alloc::string::String> {
276 use rasn::AsnType;
277
278 #[allow(clippy::cast_possible_truncation)]
279 let etsi_val = (value * $conv) as $tt;
280
281 if let Some(constraints) = Self::CONSTRAINTS.value() {
282 if !constraints.constraint.in_bound(&etsi_val) {
283 return Err(alloc::format!("Value out of bounds"));
284 }
285 }
286
287 if etsi_val == $unavailable {
290 return Err(alloc::format!("Value out of bounds"));
291 }
292
293 Ok(Self(etsi_val))
294 }
295
296 #[must_use]
298 pub fn as_kmh(&self) -> f32 {
299 self.as_mps() * MPS_TO_KMH_FACTOR
300 }
301
302 #[must_use]
304 pub fn try_as_kmh(&self) -> Option<f32> {
305 if self.is_unavailable() {
306 None
307 } else {
308 Some(self.as_kmh())
309 }
310 }
311
312 pub fn from_kmh(value: f32) -> Result<Self, alloc::string::String> {
317 Self::from_mps(value / MPS_TO_KMH_FACTOR)
318 }
319
320 pub fn unavailable() -> Self {
322 Self($unavailable)
323 }
324
325 pub fn is_unavailable(&self) -> bool {
327 self.0 == $unavailable
328 }
329 }
330
331 impl From<&$t> for f32 {
332 fn from(other: &$t) -> f32 {
333 other.as_mps()
334 }
335 }
336 impl From<$t> for f32 {
337 fn from(other: $t) -> f32 {
338 other.as_mps()
339 }
340 }
341
342 impl TryFrom<f32> for $t {
343 type Error = alloc::string::String;
344
345 fn try_from(value: f32) -> Result<Self, Self::Error> {
346 Self::from_mps(value)
347 }
348 }
349 };
350}
351
352#[cfg(feature = "cpm_1")]
353etsi_to_mps!(
354 cpm_1::cpm_pdu_descriptions::SpeedValueExtended,
355 i16,
356 100.,
357 16_383
358); #[cfg(feature = "_cdd_1_3_1_1")]
360etsi_to_mps!(cdd_1_3_1_1::its_container::SpeedValue, u16, 100., 16_383); #[cfg(feature = "_cdd_2_2_1")]
362etsi_to_mps!(cdd_2_2_1::etsi_its_cdd::SpeedValue, u16, 100., 16_383); #[cfg(feature = "_dsrc_2_2_1")]
365etsi_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"))]
369macro_rules! etsi_to_mpss {
370 ($t:ty, $tt:ty, $conv:expr, $unavailable:expr) => {
371 impl $t {
372 #[must_use]
374 pub fn as_mpss(&self) -> f32 {
375 f32::from(self.0) / $conv
376 }
377
378 #[must_use]
380 pub fn try_as_mpss(&self) -> Option<f32> {
381 if self.is_unavailable() {
382 None
383 } else {
384 Some(self.as_mpss())
385 }
386 }
387
388 pub fn from_mpss(value: f32) -> Result<Self, alloc::string::String> {
393 use rasn::AsnType;
394
395 #[allow(clippy::cast_possible_truncation)]
396 let etsi_val = (value * $conv) as $tt;
397
398 if let Some(constraints) = Self::CONSTRAINTS.value() {
399 if !constraints.constraint.in_bound(&etsi_val) {
400 return Err(alloc::format!("Value out of bounds"));
401 }
402 }
403
404 if etsi_val == $unavailable {
407 return Err(alloc::format!("Value out of bounds"));
408 }
409
410 Ok(Self(etsi_val))
411 }
412
413 pub fn unavailable() -> Self {
415 Self($unavailable)
416 }
417
418 pub fn is_unavailable(&self) -> bool {
420 self.0 == $unavailable
421 }
422 }
423
424 impl From<&$t> for f32 {
425 fn from(other: &$t) -> f32 {
426 other.as_mpss()
427 }
428 }
429 impl From<$t> for f32 {
430 fn from(other: $t) -> f32 {
431 other.as_mpss()
432 }
433 }
434
435 impl TryFrom<f32> for $t {
436 type Error = alloc::string::String;
437
438 fn try_from(value: f32) -> Result<Self, Self::Error> {
439 Self::from_mpss(value)
440 }
441 }
442 };
443}
444
445#[cfg(feature = "_cdd_1_3_1_1")]
446etsi_to_mpss!(
447 cdd_1_3_1_1::its_container::LongitudinalAccelerationValue,
448 i16,
449 10.,
450 161
451); #[cfg(feature = "_cdd_2_2_1")]
453etsi_to_mpss!(
454 cdd_2_2_1::etsi_its_cdd::LongitudinalAccelerationValue,
455 i16,
456 10.,
457 161
458); #[cfg(feature = "_cdd_1_3_1_1")]
460etsi_to_mpss!(
461 cdd_1_3_1_1::its_container::LateralAccelerationValue,
462 i16,
463 10.,
464 161
465); #[cfg(feature = "_cdd_2_2_1")]
467etsi_to_mpss!(
468 cdd_2_2_1::etsi_its_cdd::LateralAccelerationValue,
469 i16,
470 10.,
471 161
472); #[cfg(feature = "_cdd_1_3_1_1")]
474etsi_to_mpss!(
475 cdd_1_3_1_1::its_container::VerticalAccelerationValue,
476 i16,
477 10.,
478 161
479); #[cfg(feature = "_cdd_2_2_1")]
481etsi_to_mpss!(
482 cdd_2_2_1::etsi_its_cdd::VerticalAccelerationValue,
483 i16,
484 10.,
485 161
486); #[cfg(any(feature = "_cdd_1_3_1_1", feature = "_cdd_2_2_1"))]
490macro_rules! etsi_raw_unavailable {
491 ($t:ty, $tt:ty, $unavailable:expr) => {
492 impl $t {
493 #[must_use]
495 pub fn try_as_raw(&self) -> Option<$tt> {
496 if self.is_unavailable() {
497 None
498 } else {
499 Some(self.0)
500 }
501 }
502
503 pub fn from_raw(value: $tt) -> Result<Self, alloc::string::String> {
508 use rasn::AsnType;
509
510 if let Some(constraints) = Self::CONSTRAINTS.value() {
511 if !constraints.constraint.in_bound(&value) {
512 return Err(alloc::format!("Value out of bounds"));
513 }
514 }
515
516 if value == $unavailable {
519 return Err(alloc::format!("Value out of bounds"));
520 }
521
522 Ok(Self(value))
523 }
524
525 pub fn unavailable() -> Self {
527 Self($unavailable)
528 }
529
530 pub fn is_unavailable(&self) -> bool {
532 self.0 == $unavailable
533 }
534 }
535
536 impl From<&$t> for $tt {
537 fn from(other: &$t) -> $tt {
538 other.0
539 }
540 }
541 impl From<$t> for $tt {
542 fn from(other: $t) -> $tt {
543 other.0
544 }
545 }
546
547 impl TryFrom<$tt> for $t {
548 type Error = alloc::string::String;
549
550 fn try_from(value: $tt) -> Result<Self, Self::Error> {
551 Self::from_raw(value)
552 }
553 }
554 };
555}
556
557#[cfg(feature = "_cdd_1_3_1_1")]
558etsi_raw_unavailable!(cdd_1_3_1_1::its_container::CurvatureValue, i16, 1023);
559#[cfg(feature = "_cdd_2_2_1")]
560etsi_raw_unavailable!(cdd_2_2_1::etsi_its_cdd::CurvatureValue, i16, 1023);
561
562#[cfg(any(
564 feature = "cpm_1",
565 feature = "_cdd_1_3_1_1",
566 feature = "_cdd_2_2_1",
567 feature = "_dsrc_2_2_1"
568))]
569macro_rules! angle_to_deg {
570 ($t:ty, $tt:ty, $conv:expr, $unavailable:expr) => {
571 impl $t {
572 #[must_use]
574 pub fn as_deg(&self) -> f32 {
575 f32::from(self.0) / $conv
576 }
577
578 #[must_use]
580 pub fn try_as_deg(&self) -> Option<f32> {
581 if self.is_unavailable() {
582 None
583 } else {
584 Some(self.as_deg())
585 }
586 }
587
588 pub fn from_deg(value: f32) -> Result<Self, alloc::string::String> {
593 use rasn::AsnType;
594
595 #[allow(clippy::cast_possible_truncation)]
596 let etsi_val = (value * $conv) as $tt;
597
598 if let Some(constraints) = Self::CONSTRAINTS.value() {
599 if !constraints.constraint.in_bound(&etsi_val) {
600 return Err(alloc::format!("Value out of bounds"));
601 }
602 }
603
604 if etsi_val == $unavailable {
607 return Err(alloc::format!("Value out of bounds"));
608 }
609
610 Ok(Self(etsi_val))
611 }
612
613 pub fn unavailable() -> Self {
615 Self($unavailable)
616 }
617
618 pub fn is_unavailable(&self) -> bool {
620 self.0 == $unavailable
621 }
622 }
623
624 impl From<&$t> for f32 {
625 fn from(other: &$t) -> f32 {
626 other.as_deg()
627 }
628 }
629 impl From<$t> for f32 {
630 fn from(other: $t) -> f32 {
631 other.as_deg()
632 }
633 }
634
635 impl TryFrom<f32> for $t {
636 type Error = alloc::string::String;
637
638 fn try_from(value: f32) -> Result<Self, Self::Error> {
639 Self::from_deg(value)
640 }
641 }
642 };
643}
644
645#[cfg(feature = "_cdd_2_2_1")]
646angle_to_deg!(cdd_2_2_1::etsi_its_cdd::CartesianAngleValue, u16, 10., 3601); #[cfg(feature = "cpm_1")]
648angle_to_deg!(
649 cpm_1::cpm_pdu_descriptions::CartesianAngleValue,
650 u16,
651 10.,
652 3601
653); #[cfg(feature = "cpm_1")]
655angle_to_deg!(cpm_1::cpm_pdu_descriptions::WGS84AngleValue, u16, 10., 3601); #[cfg(feature = "_cdd_2_2_1")]
657angle_to_deg!(cdd_2_2_1::etsi_its_cdd::Wgs84AngleValue, u16, 10., 3601); #[cfg(feature = "_dsrc_2_2_1")]
659angle_to_deg!(dsrc_2_2_1::etsi_its_dsrc::Angle, u16, 80., 28800); #[cfg(feature = "_cdd_2_2_1")]
661angle_to_deg!(cdd_2_2_1::etsi_its_cdd::HeadingValue, u16, 10., 3601); #[cfg(feature = "_cdd_1_3_1_1")]
663angle_to_deg!(cdd_1_3_1_1::its_container::HeadingValue, u16, 10., 3601); #[cfg(feature = "_cdd_2_2_1")]
666angle_to_deg!(
667 cdd_2_2_1::etsi_its_cdd::SteeringWheelAngleValue,
668 i16,
669 (1. / 1.5),
670 512
671); #[cfg(feature = "_cdd_1_3_1_1")]
673angle_to_deg!(
674 cdd_1_3_1_1::its_container::SteeringWheelAngleValue,
675 i16,
676 (1. / 1.5),
677 512
678); #[cfg(any(feature = "_cdd_2_2_1", feature = "_cdd_1_3_1_1"))]
682macro_rules! angle_to_degrate {
683 ($t:ty, $conv:expr, $unavailable:expr) => {
684 impl $t {
685 #[must_use]
687 pub fn as_deg_rate(&self) -> f32 {
688 f32::from(self.0) / $conv
689 }
690
691 #[must_use]
693 pub fn try_as_deg(&self) -> Option<f32> {
694 if self.is_unavailable() {
695 None
696 } else {
697 Some(self.as_deg_rate())
698 }
699 }
700
701 pub fn from_deg_rate(value: f32) -> Result<Self, alloc::string::String> {
706 use rasn::AsnType;
707
708 #[allow(clippy::cast_possible_truncation)]
709 let etsi_val = (value * $conv) as i16;
710
711 if let Some(constraints) = Self::CONSTRAINTS.value() {
712 if !constraints.constraint.in_bound(&etsi_val) {
713 return Err(alloc::format!("Value out of bounds"));
714 }
715 }
716
717 if etsi_val == $unavailable {
720 return Err(alloc::format!("Value out of bounds"));
721 }
722
723 Ok(Self(etsi_val))
724 }
725
726 pub fn unavailable() -> Self {
728 Self($unavailable)
729 }
730
731 pub fn is_unavailable(&self) -> bool {
733 self.0 == $unavailable
734 }
735 }
736
737 impl From<&$t> for f32 {
738 fn from(other: &$t) -> f32 {
739 other.as_deg_rate()
740 }
741 }
742 impl From<$t> for f32 {
743 fn from(other: $t) -> f32 {
744 other.as_deg_rate()
745 }
746 }
747
748 impl TryFrom<f32> for $t {
749 type Error = alloc::string::String;
750
751 fn try_from(value: f32) -> Result<Self, Self::Error> {
752 Self::from_deg_rate(value)
753 }
754 }
755 };
756}
757
758#[cfg(feature = "_cdd_2_2_1")]
759angle_to_degrate!(cdd_2_2_1::etsi_its_cdd::YawRateValue, 100., 32767); #[cfg(feature = "_cdd_1_3_1_1")]
761angle_to_degrate!(cdd_1_3_1_1::its_container::YawRateValue, 100., 32767); #[cfg(feature = "_dsrc_2_2_1")]
765impl dsrc_2_2_1::etsi_its_dsrc::DeltaTime {
766 #[must_use]
768 pub fn as_sec(&self) -> i16 {
769 i16::from(self.0) * 10
770 }
771
772 #[must_use]
774 pub fn try_as_sec(&self) -> Option<i16> {
775 if self.is_unavailable() {
776 None
777 } else {
778 Some(self.as_sec())
779 }
780 }
781
782 #[must_use]
784 pub fn from_sec(value: i16) -> Self {
785 #[allow(clippy::cast_possible_truncation)]
786 let etsi_val = (value / 10) as i8;
787
788 Self(etsi_val.clamp(-121, 120))
789 }
790
791 pub fn unavailable() -> Self {
793 Self(-122)
794 }
795
796 pub fn is_unavailable(&self) -> bool {
798 self.0 == -122
799 }
800}
801
802#[cfg(feature = "_dsrc_2_2_1")]
803impl From<&dsrc_2_2_1::etsi_its_dsrc::DeltaTime> for i16 {
804 fn from(other: &dsrc_2_2_1::etsi_its_dsrc::DeltaTime) -> i16 {
805 other.as_sec()
806 }
807}
808#[cfg(feature = "_dsrc_2_2_1")]
809impl From<dsrc_2_2_1::etsi_its_dsrc::DeltaTime> for i16 {
810 fn from(other: dsrc_2_2_1::etsi_its_dsrc::DeltaTime) -> i16 {
811 other.as_sec()
812 }
813}
814
815#[cfg(feature = "_dsrc_2_2_1")]
817impl dsrc_2_2_1::etsi_its_dsrc::DSecond {
818 #[must_use]
820 pub fn as_millis(&self) -> u16 {
821 self.0
822 }
823
824 #[must_use]
826 pub fn try_as_millis(&self) -> Option<u16> {
827 if self.is_unavailable() {
828 None
829 } else {
830 Some(self.as_millis())
831 }
832 }
833
834 pub fn from_millis(value: u16) -> Result<Self, alloc::string::String> {
839 if value > 60999 {
842 return Err(alloc::format!("Value out of bounds"));
843 }
844
845 Ok(Self(value))
846 }
847
848 pub fn unavailable() -> Self {
850 Self(65535)
851 }
852
853 pub fn is_unavailable(&self) -> bool {
855 self.0 == 65535
856 }
857}
858
859#[cfg(feature = "_dsrc_2_2_1")]
860impl From<&dsrc_2_2_1::etsi_its_dsrc::DSecond> for u16 {
861 fn from(other: &dsrc_2_2_1::etsi_its_dsrc::DSecond) -> u16 {
862 other.as_millis()
863 }
864}
865#[cfg(feature = "_dsrc_2_2_1")]
866impl From<dsrc_2_2_1::etsi_its_dsrc::DSecond> for u16 {
867 fn from(other: dsrc_2_2_1::etsi_its_dsrc::DSecond) -> u16 {
868 other.as_millis()
869 }
870}
871
872#[cfg(feature = "_dsrc_2_2_1")]
874impl dsrc_2_2_1::etsi_its_dsrc::TimeMark {
875 const CONV_FACTOR: u32 = 100;
876 const UNKNOWN: u16 = 36001;
877 const OUT_OF_RANGE: u16 = 36000;
878
879 #[must_use]
881 pub fn as_millis(&self) -> u32 {
882 self.0 as u32 * Self::CONV_FACTOR
883 }
884
885 #[must_use]
887 pub fn try_as_millis(&self) -> Option<u32> {
888 if self.is_unknown() {
889 None
890 } else {
891 Some(self.as_millis())
892 }
893 }
894
895 pub fn from_millis(value: u32) -> Result<Self, alloc::string::String> {
900 #[allow(clippy::cast_possible_truncation)]
901 let etsi_val = (value / Self::CONV_FACTOR) as u16;
902
903 if etsi_val > 35999 {
905 return Err(alloc::format!("Value out of bounds"));
906 }
907
908 Ok(Self(etsi_val))
909 }
910
911 pub fn unknown() -> Self {
913 Self(Self::UNKNOWN)
914 }
915
916 pub fn is_unknown(&self) -> bool {
918 self.0 == Self::UNKNOWN
919 }
920
921 pub fn out_of_range() -> Self {
923 Self(Self::OUT_OF_RANGE)
924 }
925
926 pub fn is_out_of_range(&self) -> bool {
928 self.0 == Self::OUT_OF_RANGE
929 }
930}
931
932#[cfg(feature = "_dsrc_2_2_1")]
933impl From<&dsrc_2_2_1::etsi_its_dsrc::TimeMark> for u32 {
934 fn from(other: &dsrc_2_2_1::etsi_its_dsrc::TimeMark) -> u32 {
935 other.as_millis()
936 }
937}
938#[cfg(feature = "_dsrc_2_2_1")]
939impl From<dsrc_2_2_1::etsi_its_dsrc::TimeMark> for u32 {
940 fn from(other: dsrc_2_2_1::etsi_its_dsrc::TimeMark) -> u32 {
941 other.as_millis()
942 }
943}
944
945#[cfg(feature = "_dsrc_2_2_1")]
947impl dsrc_2_2_1::etsi_its_dsrc::MinuteOfTheYear {
948 pub fn invalid() -> Self {
950 Self(527040)
951 }
952
953 pub fn is_invalid(&self) -> bool {
955 self.0 == 527040
956 }
957}
958
959#[cfg(feature = "_dsrc_2_2_1")]
961impl crate::standards::dsrc_2_2_1::etsi_its_dsrc::MsgCount {
962 pub fn increment(&self) -> Self {
963 Self((self.0 + 1) % 128)
964 }
965}
966#[cfg(feature = "_dsrc_2_2_1")]
967impl From<u8> for dsrc_2_2_1::etsi_its_dsrc::MsgCount {
968 fn from(value: u8) -> Self {
969 Self(value % 128)
970 }
971}
972
973#[cfg(feature = "_dsrc_2_2_1")]
975impl crate::standards::dsrc_2_2_1::etsi_its_dsrc::RequestID {
976 pub fn increment(&self) -> Self {
977 Self(self.0.wrapping_add(1))
978 }
979}
980#[cfg(feature = "_dsrc_2_2_1")]
981impl From<u8> for dsrc_2_2_1::etsi_its_dsrc::RequestID {
982 fn from(value: u8) -> Self {
984 Self(value)
985 }
986}
987
988#[cfg(feature = "_dsrc_2_2_1")]
991impl dsrc_2_2_1::etsi_its_dsrc::SpeedLimitList {
992 pub fn get_speed_limit_mps(
994 &self,
995 limit_type: dsrc_2_2_1::etsi_its_dsrc::SpeedLimitType,
996 ) -> Option<f32> {
997 self.0.iter().find_map(|item| {
998 if item.r_type == limit_type {
999 Some(item.speed.as_mps())
1000 } else {
1001 None
1002 }
1003 })
1004 }
1005}
1006
1007#[cfg(feature = "_cdd_2_2_1")]
1008impl cdd_2_2_1::etsi_its_cdd::CauseCodeChoice {
1009 pub fn to_u8_tuple(&self) -> (u8, u8) {
1011 match self {
1012 Self::reserved0(scc) => (0, scc.0),
1013 Self::trafficCondition1(scc) => (1, scc.0),
1014 Self::accident2(scc) => (2, scc.0),
1015 Self::roadworks3(scc) => (3, scc.0),
1016 Self::reserved4(scc) => (4, scc.0),
1017 Self::impassability5(scc) => (5, scc.0),
1018 Self::adverseWeatherCondition_Adhesion6(scc) => (6, scc.0),
1019 Self::aquaplaning7(scc) => (7, scc.0),
1020 Self::reserved8(scc) => (8, scc.0),
1021 Self::hazardousLocation_SurfaceCondition9(scc) => (9, scc.0),
1022 Self::hazardousLocation_ObstacleOnTheRoad10(scc) => (10, scc.0),
1023 Self::hazardousLocation_AnimalOnTheRoad11(scc) => (11, scc.0),
1024 Self::humanPresenceOnTheRoad12(scc) => (12, scc.0),
1025 Self::reserved13(scc) => (13, scc.0),
1026 Self::wrongWayDriving14(scc) => (14, scc.0),
1027 Self::rescueAndRecoveryWorkInProgress15(scc) => (15, scc.0),
1028 Self::reserved16(scc) => (16, scc.0),
1029 Self::adverseWeatherCondition_ExtremeWeatherCondition17(scc) => (17, scc.0),
1030 Self::adverseWeatherCondition_Visibility18(scc) => (18, scc.0),
1031 Self::adverseWeatherCondition_Precipitation19(scc) => (19, scc.0),
1032 Self::violence20(scc) => (20, scc.0),
1033 Self::reserved21(scc) => (21, scc.0),
1034 Self::reserved22(scc) => (22, scc.0),
1035 Self::reserved23(scc) => (23, scc.0),
1036 Self::reserved24(scc) => (24, scc.0),
1037 Self::reserved25(scc) => (25, scc.0),
1038 Self::slowVehicle26(scc) => (26, scc.0),
1039 Self::dangerousEndOfQueue27(scc) => (27, scc.0),
1040 Self::publicTransportVehicleApproaching28(scc) => (28, scc.0),
1041 Self::reserved29(scc) => (29, scc.0),
1042 Self::reserved30(scc) => (30, scc.0),
1043 Self::reserved31(scc) => (31, scc.0),
1044 Self::reserved32(scc) => (32, scc.0),
1045 Self::reserved33(scc) => (33, scc.0),
1046 Self::reserved34(scc) => (34, scc.0),
1047 Self::reserved35(scc) => (35, scc.0),
1048 Self::reserved36(scc) => (36, scc.0),
1049 Self::reserved37(scc) => (37, scc.0),
1050 Self::reserved38(scc) => (38, scc.0),
1051 Self::reserved39(scc) => (39, scc.0),
1052 Self::reserved40(scc) => (40, scc.0),
1053 Self::reserved41(scc) => (41, scc.0),
1054 Self::reserved42(scc) => (42, scc.0),
1055 Self::reserved43(scc) => (43, scc.0),
1056 Self::reserved44(scc) => (44, scc.0),
1057 Self::reserved45(scc) => (45, scc.0),
1058 Self::reserved46(scc) => (46, scc.0),
1059 Self::reserved47(scc) => (47, scc.0),
1060 Self::reserved48(scc) => (48, scc.0),
1061 Self::reserved49(scc) => (49, scc.0),
1062 Self::reserved50(scc) => (50, scc.0),
1063 Self::reserved51(scc) => (51, scc.0),
1064 Self::reserved52(scc) => (52, scc.0),
1065 Self::reserved53(scc) => (53, scc.0),
1066 Self::reserved54(scc) => (54, scc.0),
1067 Self::reserved55(scc) => (55, scc.0),
1068 Self::reserved56(scc) => (56, scc.0),
1069 Self::reserved57(scc) => (57, scc.0),
1070 Self::reserved58(scc) => (58, scc.0),
1071 Self::reserved59(scc) => (59, scc.0),
1072 Self::reserved60(scc) => (60, scc.0),
1073 Self::reserved61(scc) => (61, scc.0),
1074 Self::reserved62(scc) => (62, scc.0),
1075 Self::reserved63(scc) => (63, scc.0),
1076 Self::reserved64(scc) => (64, scc.0),
1077 Self::reserved65(scc) => (65, scc.0),
1078 Self::reserved66(scc) => (66, scc.0),
1079 Self::reserved67(scc) => (67, scc.0),
1080 Self::reserved68(scc) => (68, scc.0),
1081 Self::reserved69(scc) => (69, scc.0),
1082 Self::reserved70(scc) => (70, scc.0),
1083 Self::reserved71(scc) => (71, scc.0),
1084 Self::reserved72(scc) => (72, scc.0),
1085 Self::reserved73(scc) => (73, scc.0),
1086 Self::reserved74(scc) => (74, scc.0),
1087 Self::reserved75(scc) => (75, scc.0),
1088 Self::reserved76(scc) => (76, scc.0),
1089 Self::reserved77(scc) => (77, scc.0),
1090 Self::reserved78(scc) => (78, scc.0),
1091 Self::reserved79(scc) => (79, scc.0),
1092 Self::reserved80(scc) => (80, scc.0),
1093 Self::reserved81(scc) => (81, scc.0),
1094 Self::reserved82(scc) => (82, scc.0),
1095 Self::reserved83(scc) => (83, scc.0),
1096 Self::reserved84(scc) => (84, scc.0),
1097 Self::reserved85(scc) => (85, scc.0),
1098 Self::reserved86(scc) => (86, scc.0),
1099 Self::reserved87(scc) => (87, scc.0),
1100 Self::reserved88(scc) => (88, scc.0),
1101 Self::reserved89(scc) => (89, scc.0),
1102 Self::reserved90(scc) => (90, scc.0),
1103 Self::vehicleBreakdown91(scc) => (91, scc.0),
1104 Self::postCrash92(scc) => (92, scc.0),
1105 Self::humanProblem93(scc) => (93, scc.0),
1106 Self::stationaryVehicle94(scc) => (94, scc.0),
1107 Self::emergencyVehicleApproaching95(scc) => (95, scc.0),
1108 Self::hazardousLocation_DangerousCurve96(scc) => (96, scc.0),
1109 Self::collisionRisk97(scc) => (97, scc.0),
1110 Self::signalViolation98(scc) => (98, scc.0),
1111 Self::dangerousSituation99(scc) => (99, scc.0),
1112 Self::railwayLevelCrossing100(scc) => (100, scc.0),
1113 Self::reserved101(scc) => (101, scc.0),
1114 Self::reserved102(scc) => (102, scc.0),
1115 Self::reserved103(scc) => (103, scc.0),
1116 Self::reserved104(scc) => (104, scc.0),
1117 Self::reserved105(scc) => (105, scc.0),
1118 Self::reserved106(scc) => (106, scc.0),
1119 Self::reserved107(scc) => (107, scc.0),
1120 Self::reserved108(scc) => (108, scc.0),
1121 Self::reserved109(scc) => (109, scc.0),
1122 Self::reserved110(scc) => (110, scc.0),
1123 Self::reserved111(scc) => (111, scc.0),
1124 Self::reserved112(scc) => (112, scc.0),
1125 Self::reserved113(scc) => (113, scc.0),
1126 Self::reserved114(scc) => (114, scc.0),
1127 Self::reserved115(scc) => (115, scc.0),
1128 Self::reserved116(scc) => (116, scc.0),
1129 Self::reserved117(scc) => (117, scc.0),
1130 Self::reserved118(scc) => (118, scc.0),
1131 Self::reserved119(scc) => (119, scc.0),
1132 Self::reserved120(scc) => (120, scc.0),
1133 Self::reserved121(scc) => (121, scc.0),
1134 Self::reserved122(scc) => (122, scc.0),
1135 Self::reserved123(scc) => (123, scc.0),
1136 Self::reserved124(scc) => (124, scc.0),
1137 Self::reserved125(scc) => (125, scc.0),
1138 Self::reserved126(scc) => (126, scc.0),
1139 Self::reserved127(scc) => (127, scc.0),
1140 Self::reserved128(scc) => (128, scc.0),
1141 }
1142 }
1143}