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 = "_dsrc_2_2_1")]
657angle_to_deg!(dsrc_2_2_1::etsi_its_dsrc::Angle, u16, 80., 28800); #[cfg(feature = "_cdd_2_2_1")]
659angle_to_deg!(cdd_2_2_1::etsi_its_cdd::HeadingValue, u16, 10., 3601); #[cfg(feature = "_cdd_1_3_1_1")]
661angle_to_deg!(cdd_1_3_1_1::its_container::HeadingValue, u16, 10., 3601); #[cfg(feature = "_cdd_2_2_1")]
664angle_to_deg!(
665 cdd_2_2_1::etsi_its_cdd::SteeringWheelAngleValue,
666 i16,
667 (1. / 1.5),
668 512
669); #[cfg(feature = "_cdd_1_3_1_1")]
671angle_to_deg!(
672 cdd_1_3_1_1::its_container::SteeringWheelAngleValue,
673 i16,
674 (1. / 1.5),
675 512
676); #[cfg(any(feature = "_cdd_2_2_1", feature = "_cdd_1_3_1_1"))]
680macro_rules! angle_to_degrate {
681 ($t:ty, $conv:expr, $unavailable:expr) => {
682 impl $t {
683 #[must_use]
685 pub fn as_deg_rate(&self) -> f32 {
686 f32::from(self.0) / $conv
687 }
688
689 #[must_use]
691 pub fn try_as_deg(&self) -> Option<f32> {
692 if self.is_unavailable() {
693 None
694 } else {
695 Some(self.as_deg_rate())
696 }
697 }
698
699 pub fn from_deg_rate(value: f32) -> Result<Self, alloc::string::String> {
704 use rasn::AsnType;
705
706 #[allow(clippy::cast_possible_truncation)]
707 let etsi_val = (value * $conv) as i16;
708
709 if let Some(constraints) = Self::CONSTRAINTS.value() {
710 if !constraints.constraint.in_bound(&etsi_val) {
711 return Err(alloc::format!("Value out of bounds"));
712 }
713 }
714
715 if etsi_val == $unavailable {
718 return Err(alloc::format!("Value out of bounds"));
719 }
720
721 Ok(Self(etsi_val))
722 }
723
724 pub fn unavailable() -> Self {
726 Self($unavailable)
727 }
728
729 pub fn is_unavailable(&self) -> bool {
731 self.0 == $unavailable
732 }
733 }
734
735 impl From<&$t> for f32 {
736 fn from(other: &$t) -> f32 {
737 other.as_deg_rate()
738 }
739 }
740 impl From<$t> for f32 {
741 fn from(other: $t) -> f32 {
742 other.as_deg_rate()
743 }
744 }
745
746 impl TryFrom<f32> for $t {
747 type Error = alloc::string::String;
748
749 fn try_from(value: f32) -> Result<Self, Self::Error> {
750 Self::from_deg_rate(value)
751 }
752 }
753 };
754}
755
756#[cfg(feature = "_cdd_2_2_1")]
757angle_to_degrate!(cdd_2_2_1::etsi_its_cdd::YawRateValue, 100., 32767); #[cfg(feature = "_cdd_1_3_1_1")]
759angle_to_degrate!(cdd_1_3_1_1::its_container::YawRateValue, 100., 32767); #[cfg(feature = "_dsrc_2_2_1")]
763impl dsrc_2_2_1::etsi_its_dsrc::DeltaTime {
764 #[must_use]
766 pub fn as_sec(&self) -> i16 {
767 i16::from(self.0) * 10
768 }
769
770 #[must_use]
772 pub fn try_as_sec(&self) -> Option<i16> {
773 if self.is_unavailable() {
774 None
775 } else {
776 Some(self.as_sec())
777 }
778 }
779
780 #[must_use]
782 pub fn from_sec(value: i16) -> Self {
783 #[allow(clippy::cast_possible_truncation)]
784 let etsi_val = (value / 10) as i8;
785
786 Self(etsi_val.clamp(-121, 120))
787 }
788
789 pub fn unavailable() -> Self {
791 Self(-122)
792 }
793
794 pub fn is_unavailable(&self) -> bool {
796 self.0 == -122
797 }
798}
799
800#[cfg(feature = "_dsrc_2_2_1")]
801impl From<&dsrc_2_2_1::etsi_its_dsrc::DeltaTime> for i16 {
802 fn from(other: &dsrc_2_2_1::etsi_its_dsrc::DeltaTime) -> i16 {
803 other.as_sec()
804 }
805}
806#[cfg(feature = "_dsrc_2_2_1")]
807impl From<dsrc_2_2_1::etsi_its_dsrc::DeltaTime> for i16 {
808 fn from(other: dsrc_2_2_1::etsi_its_dsrc::DeltaTime) -> i16 {
809 other.as_sec()
810 }
811}
812
813#[cfg(feature = "_dsrc_2_2_1")]
815impl dsrc_2_2_1::etsi_its_dsrc::DSecond {
816 #[must_use]
818 pub fn as_millis(&self) -> u16 {
819 self.0
820 }
821
822 #[must_use]
824 pub fn try_as_millis(&self) -> Option<u16> {
825 if self.is_unavailable() {
826 None
827 } else {
828 Some(self.as_millis())
829 }
830 }
831
832 pub fn from_millis(value: u16) -> Result<Self, alloc::string::String> {
837 if value > 60999 {
840 return Err(alloc::format!("Value out of bounds"));
841 }
842
843 Ok(Self(value))
844 }
845
846 pub fn unavailable() -> Self {
848 Self(65535)
849 }
850
851 pub fn is_unavailable(&self) -> bool {
853 self.0 == 65535
854 }
855}
856
857#[cfg(feature = "_dsrc_2_2_1")]
858impl From<&dsrc_2_2_1::etsi_its_dsrc::DSecond> for u16 {
859 fn from(other: &dsrc_2_2_1::etsi_its_dsrc::DSecond) -> u16 {
860 other.as_millis()
861 }
862}
863#[cfg(feature = "_dsrc_2_2_1")]
864impl From<dsrc_2_2_1::etsi_its_dsrc::DSecond> for u16 {
865 fn from(other: dsrc_2_2_1::etsi_its_dsrc::DSecond) -> u16 {
866 other.as_millis()
867 }
868}
869
870#[cfg(feature = "_dsrc_2_2_1")]
872impl dsrc_2_2_1::etsi_its_dsrc::MinuteOfTheYear {
873 pub fn invalid() -> Self {
875 Self(527040)
876 }
877
878 pub fn is_invalid(&self) -> bool {
880 self.0 == 527040
881 }
882}
883
884#[cfg(feature = "_dsrc_2_2_1")]
886impl crate::standards::dsrc_2_2_1::etsi_its_dsrc::MsgCount {
887 pub fn increment(&self) -> Self {
888 Self((self.0 + 1) % 128)
889 }
890}
891#[cfg(feature = "_dsrc_2_2_1")]
892impl From<u8> for dsrc_2_2_1::etsi_its_dsrc::MsgCount {
893 fn from(value: u8) -> Self {
894 Self(value % 128)
895 }
896}
897
898#[cfg(feature = "_dsrc_2_2_1")]
900impl crate::standards::dsrc_2_2_1::etsi_its_dsrc::RequestID {
901 pub fn increment(&self) -> Self {
902 Self(self.0.wrapping_add(1))
903 }
904}
905#[cfg(feature = "_dsrc_2_2_1")]
906impl From<u8> for dsrc_2_2_1::etsi_its_dsrc::RequestID {
907 fn from(value: u8) -> Self {
909 Self(value)
910 }
911}
912
913#[cfg(feature = "_dsrc_2_2_1")]
915impl dsrc_2_2_1::etsi_its_dsrc::SpeedLimitList {
916 pub fn get_speed_limit_mps(
918 &self,
919 limit_type: dsrc_2_2_1::etsi_its_dsrc::SpeedLimitType,
920 ) -> Option<f32> {
921 self.0.iter().find_map(|item| {
922 if item.r_type == limit_type {
923 Some(item.speed.as_mps())
924 } else {
925 None
926 }
927 })
928 }
929}