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")]
990impl dsrc_2_2_1::etsi_its_dsrc::SpeedLimitList {
991 pub fn get_speed_limit_mps(
993 &self,
994 limit_type: dsrc_2_2_1::etsi_its_dsrc::SpeedLimitType,
995 ) -> Option<f32> {
996 self.0.iter().find_map(|item| {
997 if item.r_type == limit_type {
998 Some(item.speed.as_mps())
999 } else {
1000 None
1001 }
1002 })
1003 }
1004}