1mod attitude;
6pub mod catalog;
7mod differential;
8mod dnu;
9mod extended;
10mod external;
11mod ins;
12mod meas3;
13mod meas3_decoder;
14mod measurement;
15mod navigation;
16mod position;
17mod sbas;
18mod status;
19mod time;
20
21pub use attitude::*;
22pub use differential::*;
23pub use extended::*;
24pub use external::*;
25pub use ins::*;
26pub use meas3::*;
27pub use meas3_decoder::*;
28pub use measurement::*;
29pub use navigation::*;
30pub use position::*;
31pub use sbas::*;
32pub use status::*;
33pub use time::*;
34
35pub use catalog::fallback_name;
36
37use crate::error::{SbfError, SbfResult};
38use crate::header::SbfHeader;
39
40pub mod block_ids {
46 pub const MEAS_EPOCH: u16 = 4027;
48 pub const MEAS_EXTRA: u16 = 4000;
49 pub const END_OF_MEAS: u16 = 5922;
50 pub const IQ_CORR: u16 = 4046;
51 pub const MEAS3_RANGES: u16 = 4109;
52 pub const MEAS3_CN0_HI_RES: u16 = 4110;
53 pub const MEAS3_DOPPLER: u16 = 4111;
54 pub const MEAS3_PP: u16 = 4112;
55 pub const MEAS3_MP: u16 = 4113;
56
57 pub const PVT_CARTESIAN: u16 = 4006;
59 pub const PVT_GEODETIC: u16 = 4007;
60 pub const DOP: u16 = 4001;
61 pub const DOP_LEGACY: u16 = 5909;
62 pub const POS_CART: u16 = 4044;
63 pub const PVT_SAT_CARTESIAN: u16 = 4008;
64 pub const PVT_RESIDUALS_V2: u16 = 4009;
65 pub const RAIM_STATISTICS_V2: u16 = 4011;
66 pub const BASE_VECTOR_CART: u16 = 4043;
67 pub const BASE_VECTOR_GEOD: u16 = 4028;
68 pub const POS_COV_CARTESIAN: u16 = 5905;
69 pub const POS_COV_GEODETIC: u16 = 5906;
70 pub const VEL_COV_CARTESIAN: u16 = 5907;
71 pub const VEL_COV_GEODETIC: u16 = 5908;
72 pub const GEO_CORRECTIONS: u16 = 5935;
73 pub const BASE_STATION: u16 = 5949;
74 pub const DIFF_CORR_IN: u16 = 5919;
75 pub const END_OF_PVT: u16 = 5921;
76 pub const PVT_SUPPORT: u16 = 4076;
77 pub const PVT_SUPPORT_A: u16 = 4079;
78 pub const FUGRO_DDS: u16 = 4211;
79 pub const POS_LOCAL: u16 = 4052;
80 pub const POS_PROJECTED: u16 = 4094;
81
82 pub const INT_PV_CART: u16 = 4060;
84 pub const INT_PV_GEOD: u16 = 4061;
85 pub const INT_PVA_AGEOD: u16 = 4045;
86 pub const INT_POS_COV_CART: u16 = 4062;
87 pub const INT_VEL_COV_CART: u16 = 4063;
88 pub const INT_POS_COV_GEOD: u16 = 4064;
89 pub const INT_VEL_COV_GEOD: u16 = 4065;
90 pub const INT_ATT_EULER: u16 = 4070;
91 pub const INT_ATT_COV_EULER: u16 = 4072;
92
93 pub const ATT_EULER: u16 = 5938;
97 pub const ATT_COV_EULER: u16 = 5939;
98 pub const AUX_ANT_POSITIONS: u16 = 5942;
99 pub const END_OF_ATT: u16 = 5943;
100
101 pub const GPS_NAV: u16 = 5891;
103 pub const GPS_ALM: u16 = 5892;
104 pub const GPS_ION: u16 = 5893;
105 pub const GPS_UTC: u16 = 5894;
106 pub const GPS_CNAV: u16 = 4042;
107 pub const GPS_RAW_CA: u16 = 4017;
108 pub const GPS_RAW_L2C: u16 = 4018;
109 pub const GPS_RAW_L5: u16 = 4019;
110 pub const GEO_RAW_L1: u16 = 4020;
111 pub const GAL_RAW_FNAV: u16 = 4022;
112 pub const GAL_RAW_INAV: u16 = 4023;
113 pub const GAL_RAW_CNAV: u16 = 4024;
114 pub const GLO_RAW_CA: u16 = 4026;
115 pub const CMP_RAW: u16 = 4047;
116 pub const QZS_RAW_L1CA: u16 = 4066;
117 pub const QZS_RAW_L2C: u16 = 4067;
118 pub const QZS_RAW_L5: u16 = 4068;
119 pub const GLO_NAV: u16 = 4004;
120 pub const GLO_ALM: u16 = 4005;
121 pub const GLO_TIME: u16 = 4036;
122 pub const GAL_NAV: u16 = 4002;
123 pub const GAL_ALM: u16 = 4003;
124 pub const GAL_ION: u16 = 4030;
125 pub const GAL_UTC: u16 = 4031;
126 pub const GAL_GST_GPS: u16 = 4032;
127 pub const GAL_SAR_RLM: u16 = 4034;
128 pub const BDS_ION: u16 = 4120;
129 pub const BDS_NAV: u16 = 4081;
130 pub const BDS_ALM: u16 = 4119;
131 pub const BDS_UTC: u16 = 4121;
132 pub const BDS_CNAV1: u16 = 4251;
133 pub const BDS_CNAV2: u16 = 4252;
134 pub const BDS_CNAV3: u16 = 4253;
135 pub const QZS_NAV: u16 = 4095;
136 pub const QZS_ALM: u16 = 4116;
137 pub const GEO_RAW_L5: u16 = 4021;
138 pub const BDS_RAW_B1C: u16 = 4218;
139 pub const BDS_RAW_B2A: u16 = 4219;
140 pub const BDS_RAW_B2B: u16 = 4242;
142 pub const GAL_AUTH_STATUS: u16 = 4245;
144 pub const NAVIC_RAW: u16 = 4093;
145 pub const GEO_IONO_DELAY: u16 = 5933;
146 pub const GEO_MT00: u16 = 5925;
147 pub const GEO_PRN_MASK: u16 = 5926;
148 pub const GEO_FAST_CORR: u16 = 5927;
149 pub const GEO_FAST_CORR_DEGR: u16 = 5929;
150 pub const GEO_DEGR_FACTORS: u16 = 5930;
151 pub const GEO_SERVICE_LEVEL: u16 = 5917;
152 pub const GEO_NAV: u16 = 5896;
153 pub const GEO_INTEGRITY: u16 = 5928;
154 pub const GEO_ALM: u16 = 5897;
155 pub const GEO_NETWORK_TIME: u16 = 5918;
156 pub const GEO_IGP_MASK: u16 = 5931;
157 pub const GEO_LONG_TERM_CORR: u16 = 5932;
158 pub const GEO_CLOCK_EPH_COV_MATRIX: u16 = 5934;
159
160 pub const RECEIVER_STATUS: u16 = 4014;
164 pub const TRACKING_STATUS: u16 = 5912;
165 pub const CHANNEL_STATUS: u16 = 4013;
166 pub const SAT_VISIBILITY: u16 = 4012;
167 pub const QUALITY_IND: u16 = 4082;
168 pub const INPUT_LINK: u16 = 4090;
169 pub const OUTPUT_LINK: u16 = 4091;
170 pub const IP_STATUS: u16 = 4058;
171 pub const LBAND_TRACKER_STATUS: u16 = 4201;
172 pub const LBAND_BEAMS: u16 = 4204;
173 pub const EXT_SENSOR_MEAS: u16 = 4050;
174 pub const EXT_SENSOR_STATUS: u16 = 4056;
175 pub const EXT_SENSOR_SETUP: u16 = 4057;
176 pub const COMMANDS: u16 = 4015;
177 pub const COMMENT: u16 = 5936;
178 pub const RECEIVER_SETUP: u16 = 5902;
179 pub const BB_SAMPLES: u16 = 4040;
180 pub const ASCII_IN: u16 = 4075;
181 pub const RX_MESSAGE: u16 = 4103;
182 pub const ENCAPSULATED_OUTPUT: u16 = 4097;
183 pub const GIS_ACTION: u16 = 4106;
184 pub const GIS_STATUS: u16 = 4107;
185 pub const DYN_DNS_STATUS: u16 = 4105;
186 pub const DISK_STATUS: u16 = 4059;
187 pub const NTRIP_CLIENT_STATUS: u16 = 4053;
188 pub const NTRIP_SERVER_STATUS: u16 = 4122;
189 pub const RF_STATUS: u16 = 4092;
190 pub const P2PP_STATUS: u16 = 4238;
191 pub const COSMOS_STATUS: u16 = 4243;
192 pub const RTCM_DATUM: u16 = 4049;
193
194 pub const RECEIVER_TIME: u16 = 5914;
196 pub const PPS_OFFSET: u16 = 5911;
197 pub const EXT_EVENT: u16 = 5924;
198 pub const EXT_EVENT_PVT_CARTESIAN: u16 = 4037;
199 pub const EXT_EVENT_PVT_GEODETIC: u16 = 4038;
200 pub const EXT_EVENT_BASE_VECT_GEOD: u16 = 4217;
201 pub const EXT_EVENT_ATT_EULER: u16 = 4237;
202}
203
204pub fn block_name(id: u16) -> &'static str {
210 match id {
211 block_ids::MEAS_EPOCH => "MeasEpoch",
212 block_ids::MEAS_EXTRA => "MeasExtra",
213 block_ids::IQ_CORR => "IQCorr",
214 block_ids::END_OF_MEAS => "EndOfMeas",
215 block_ids::MEAS3_RANGES => "Meas3Ranges",
216 block_ids::MEAS3_CN0_HI_RES => "Meas3CN0HiRes",
217 block_ids::MEAS3_DOPPLER => "Meas3Doppler",
218 block_ids::MEAS3_PP => "Meas3PP",
219 block_ids::MEAS3_MP => "Meas3MP",
220 block_ids::PVT_CARTESIAN => "PVTCartesian",
221 block_ids::PVT_GEODETIC => "PVTGeodetic",
222 block_ids::DOP => "DOP",
223 block_ids::DOP_LEGACY => "DOP",
224 block_ids::POS_CART => "PosCart",
225 block_ids::PVT_SAT_CARTESIAN => "PVTSatCartesian",
226 block_ids::PVT_RESIDUALS_V2 => "PVTResiduals_v2",
227 block_ids::RAIM_STATISTICS_V2 => "RAIMStatistics_v2",
228 block_ids::BASE_VECTOR_CART => "BaseVectorCart",
229 block_ids::BASE_VECTOR_GEOD => "BaseVectorGeod",
230 block_ids::POS_COV_CARTESIAN => "PosCovCartesian",
231 block_ids::POS_COV_GEODETIC => "PosCovGeodetic",
232 block_ids::VEL_COV_CARTESIAN => "VelCovCartesian",
233 block_ids::VEL_COV_GEODETIC => "VelCovGeodetic",
234 block_ids::GEO_CORRECTIONS => "GEOCorrections",
235 block_ids::BASE_STATION => "BaseStation",
236 block_ids::DIFF_CORR_IN => "DiffCorrIn",
237 block_ids::END_OF_PVT => "EndOfPVT",
238 block_ids::PVT_SUPPORT => "PVTSupport",
239 block_ids::PVT_SUPPORT_A => "PVTSupportA",
240 block_ids::FUGRO_DDS => "FugroDDS",
241 block_ids::INT_PV_CART => "IntPVCart",
242 block_ids::INT_PV_GEOD => "IntPVGeod",
243 block_ids::INT_PVA_AGEOD => "IntPVAAGeod",
244 block_ids::INT_ATT_EULER => "IntAttEuler",
245 block_ids::INT_POS_COV_CART => "IntPosCovCart",
246 block_ids::INT_VEL_COV_CART => "IntVelCovCart",
247 block_ids::INT_POS_COV_GEOD => "IntPosCovGeod",
248 block_ids::INT_VEL_COV_GEOD => "IntVelCovGeod",
249 block_ids::INT_ATT_COV_EULER => "IntAttCovEuler",
250 block_ids::IP_STATUS => "IPStatus",
251 block_ids::EXT_SENSOR_MEAS => "ExtSensorMeas",
252 block_ids::EXT_SENSOR_STATUS => "ExtSensorStatus",
253 block_ids::EXT_SENSOR_SETUP => "ExtSensorSetup",
254 block_ids::ATT_EULER => "AttEuler",
255 block_ids::ATT_COV_EULER => "AttCovEuler",
256 block_ids::AUX_ANT_POSITIONS => "AuxAntPositions",
257 block_ids::END_OF_ATT => "EndOfAtt",
258 block_ids::GPS_NAV => "GPSNav",
259 block_ids::GPS_ALM => "GPSAlm",
260 block_ids::GPS_ION => "GPSIon",
261 block_ids::GPS_UTC => "GPSUtc",
262 block_ids::GPS_CNAV => "GPSCNav",
263 block_ids::GLO_NAV => "GLONav",
264 block_ids::GLO_ALM => "GLOAlm",
265 block_ids::GLO_TIME => "GLOTime",
266 block_ids::GAL_NAV => "GALNav",
267 block_ids::GAL_ALM => "GALAlm",
268 block_ids::GAL_ION => "GALIon",
269 block_ids::GAL_UTC => "GALUtc",
270 block_ids::GAL_GST_GPS => "GALGstGps",
271 block_ids::GAL_SAR_RLM => "GALSARRLM",
272 block_ids::BDS_ION => "BDSIon",
273 block_ids::BDS_CNAV1 => "BDSCNav1",
274 block_ids::GEO_IONO_DELAY => "GEOIonoDelay",
275 block_ids::GEO_MT00 => "GEOMT00",
276 block_ids::GEO_PRN_MASK => "GEOPRNMask",
277 block_ids::GEO_FAST_CORR => "GEOFastCorr",
278 block_ids::GEO_FAST_CORR_DEGR => "GEOFastCorrDegr",
279 block_ids::GEO_DEGR_FACTORS => "GEODegrFactors",
280 block_ids::GEO_SERVICE_LEVEL => "GEOServiceLevel",
281 block_ids::GEO_NAV => "GEONav",
282 block_ids::GEO_INTEGRITY => "GEOIntegrity",
283 block_ids::GEO_ALM => "GEOAlm",
284 block_ids::GEO_NETWORK_TIME => "GEONetworkTime",
285 block_ids::GEO_IGP_MASK => "GEOIGPMask",
286 block_ids::GEO_LONG_TERM_CORR => "GEOLongTermCorr",
287 block_ids::GEO_CLOCK_EPH_COV_MATRIX => "GEOClockEphCovMatrix",
288 block_ids::GPS_RAW_CA => "GPSRawCA",
289 block_ids::GPS_RAW_L2C => "GPSRawL2C",
290 block_ids::GPS_RAW_L5 => "GPSRawL5",
291 block_ids::GAL_RAW_FNAV => "GALRawFNAV",
292 block_ids::GAL_RAW_INAV => "GALRawINAV",
293 block_ids::GAL_RAW_CNAV => "GALRawCNAV",
294 block_ids::GEO_RAW_L1 => "GEORawL1",
295 block_ids::GLO_RAW_CA => "GLORawCA",
296 block_ids::CMP_RAW => "CMPRaw",
297 block_ids::QZS_RAW_L1CA => "QZSRawL1CA",
298 block_ids::QZS_RAW_L2C => "QZSRawL2C",
299 block_ids::QZS_RAW_L5 => "QZSRawL5",
300 block_ids::RECEIVER_STATUS => "ReceiverStatus",
301 block_ids::TRACKING_STATUS => "TrackingStatus",
302 block_ids::CHANNEL_STATUS => "ChannelStatus",
303 block_ids::SAT_VISIBILITY => "SatVisibility",
304 block_ids::QUALITY_IND => "QualityInd",
305 block_ids::INPUT_LINK => "InputLink",
306 block_ids::OUTPUT_LINK => "OutputLink",
307 block_ids::LBAND_TRACKER_STATUS => "LBandTrackerStatus",
308 block_ids::COMMANDS => "Commands",
309 block_ids::COMMENT => "Comment",
310 block_ids::RECEIVER_SETUP => "ReceiverSetup",
311 block_ids::BB_SAMPLES => "BBSamples",
312 block_ids::ASCII_IN => "ASCIIIn",
313 block_ids::NTRIP_CLIENT_STATUS => "NTRIPClientStatus",
314 block_ids::NTRIP_SERVER_STATUS => "NTRIPServerStatus",
315 block_ids::RF_STATUS => "RFStatus",
316 block_ids::RECEIVER_TIME => "ReceiverTime",
317 block_ids::PPS_OFFSET => "xPPSOffset",
318 block_ids::EXT_EVENT => "ExtEvent",
319 block_ids::EXT_EVENT_PVT_CARTESIAN => "ExtEventPVTCartesian",
320 block_ids::EXT_EVENT_PVT_GEODETIC => "ExtEventPVTGeodetic",
321 block_ids::EXT_EVENT_BASE_VECT_GEOD => "ExtEventBaseVectGeod",
322 block_ids::EXT_EVENT_ATT_EULER => "ExtEventAttEuler",
323 block_ids::POS_LOCAL => "PosLocal",
324 block_ids::POS_PROJECTED => "PosProjected",
325 block_ids::BDS_NAV => "BDSNav",
326 block_ids::BDS_ALM => "BDSAlm",
327 block_ids::BDS_UTC => "BDSUtc",
328 block_ids::BDS_CNAV2 => "BDSCNav2",
329 block_ids::BDS_CNAV3 => "BDSCNav3",
330 block_ids::QZS_NAV => "QZSNav",
331 block_ids::QZS_ALM => "QZSAlm",
332 block_ids::GEO_RAW_L5 => "GEORawL5",
333 block_ids::BDS_RAW_B1C => "BDSRawB1C",
334 block_ids::BDS_RAW_B2A => "BDSRawB2a",
335 block_ids::BDS_RAW_B2B => "BDSRawB2b",
336 block_ids::GAL_AUTH_STATUS => "GALAuthStatus",
337 block_ids::NAVIC_RAW => "NAVICRaw",
338 block_ids::RTCM_DATUM => "RTCMDatum",
339 block_ids::LBAND_BEAMS => "LBandBeams",
340 block_ids::DYN_DNS_STATUS => "DynDNSStatus",
341 block_ids::DISK_STATUS => "DiskStatus",
342 block_ids::P2PP_STATUS => "P2PPStatus",
343 block_ids::COSMOS_STATUS => "CosmosStatus",
344 block_ids::RX_MESSAGE => "RxMessage",
345 block_ids::ENCAPSULATED_OUTPUT => "EncapsulatedOutput",
346 block_ids::GIS_ACTION => "GISAction",
347 block_ids::GIS_STATUS => "GISStatus",
348 _ => catalog::fallback_name(id),
349 }
350}
351
352pub fn is_known_opaque_id(id: u16) -> bool {
354 matches!(id, block_ids::FUGRO_DDS)
355}
356
357pub trait SbfBlockParse: Sized {
363 const BLOCK_ID: u16;
365
366 fn parse(header: &SbfHeader, data: &[u8]) -> SbfResult<Self>;
375}
376
377#[non_exhaustive]
387#[derive(Debug, Clone)]
388pub enum SbfBlock {
389 MeasEpoch(MeasEpochBlock),
391 MeasExtra(MeasExtraBlock),
392 EndOfMeas(EndOfMeasBlock),
393 Meas3Ranges(Meas3RangesBlock),
394 Meas3Cn0HiRes(Meas3Cn0HiResBlock),
395 Meas3Doppler(Meas3DopplerBlock),
396 Meas3Pp(Meas3PpBlock),
397 Meas3Mp(Meas3MpBlock),
398
399 PvtGeodetic(PvtGeodeticBlock),
401 PvtCartesian(PvtCartesianBlock),
402 Dop(DopBlock),
403 PosCart(PosCartBlock),
404 PvtSatCartesian(PvtSatCartesianBlock),
405 PvtResidualsV2(PvtResidualsV2Block),
406 RaimStatisticsV2(RaimStatisticsV2Block),
407 BaseVectorCart(BaseVectorCartBlock),
408 BaseVectorGeod(BaseVectorGeodBlock),
409 PosCovCartesian(PosCovCartesianBlock),
410 PosCovGeodetic(PosCovGeodeticBlock),
411 VelCovCartesian(VelCovCartesianBlock),
412 VelCovGeodetic(VelCovGeodeticBlock),
413 GeoCorrections(GeoCorrectionsBlock),
414 BaseStation(BaseStationBlock),
415 DiffCorrIn(DiffCorrInBlock),
416 PvtSupport(PvtSupportBlock),
417 PvtSupportA(PvtSupportABlock),
418 PosLocal(PosLocalBlock),
419 PosProjected(PosProjectedBlock),
420
421 AttEuler(AttEulerBlock),
423 AttCovEuler(AttCovEulerBlock),
424 AuxAntPositions(AuxAntPositionsBlock),
425 EndOfAtt(EndOfAttBlock),
426
427 GpsNav(GpsNavBlock),
429 GpsAlm(GpsAlmBlock),
430 GpsIon(GpsIonBlock),
431 GpsUtc(GpsUtcBlock),
432 GpsCNav(GpsCNavBlock),
433 GalNav(GalNavBlock),
434 GalAlm(GalAlmBlock),
435 GalIon(GalIonBlock),
436 GalUtc(GalUtcBlock),
437 GalGstGps(GalGstGpsBlock),
438 GalAuthStatus(GalAuthStatusBlock),
439 GalSarRlm(GalSarRlmBlock),
440 GloNav(GloNavBlock),
441 GloAlm(GloAlmBlock),
442 GloTime(GloTimeBlock),
443 BdsIon(BdsIonBlock),
444 BdsNav(BdsNavBlock),
445 BdsAlm(BdsAlmBlock),
446 BdsUtc(BdsUtcBlock),
447 BdsCNav1(BdsCNav1Block),
448 BdsCNav2(BdsCNav2Block),
449 BdsCNav3(BdsCNav3Block),
450 QzsNav(QzsNavBlock),
451 QzsAlm(QzsAlmBlock),
452 GeoIonoDelay(GeoIonoDelayBlock),
453 GpsRawCa(GpsRawCaBlock),
454 GpsRawL2C(GpsRawL2CBlock),
455 GpsRawL5(GpsRawL5Block),
456 GalRawFnav(GalRawFnavBlock),
457 GalRawInav(GalRawInavBlock),
458 GalRawCnav(GalRawCnavBlock),
459 GeoRawL1(GeoRawL1Block),
460 GeoRawL5(GeoRawL5Block),
461 GloRawCa(GloRawCaBlock),
462 CmpRaw(CmpRawBlock),
463 BdsRawB1c(BdsRawB1cBlock),
464 BdsRawB2a(BdsRawB2aBlock),
465 BdsRawB2b(BdsRawB2bBlock),
466 IrnssRaw(IrnssRawBlock),
467 QzsRawL1Ca(QzsRawL1CaBlock),
468 QzsRawL2C(QzsRawL2CBlock),
469 QzsRawL5(QzsRawL5Block),
470 GeoMt00(GeoMt00Block),
471 GeoPrnMask(GeoPrnMaskBlock),
472 GeoFastCorr(GeoFastCorrBlock),
473 GeoFastCorrDegr(GeoFastCorrDegrBlock),
474 GeoDegrFactors(GeoDegrFactorsBlock),
475 GeoServiceLevel(GeoServiceLevelBlock),
476 GeoNav(GeoNavBlock),
477 GeoIntegrity(GeoIntegrityBlock),
478 GeoAlm(GeoAlmBlock),
479 GeoNetworkTime(GeoNetworkTimeBlock),
480 GeoIgpMask(GeoIgpMaskBlock),
481 GeoLongTermCorr(GeoLongTermCorrBlock),
482 GeoClockEphCovMatrix(GeoClockEphCovMatrixBlock),
483
484 ReceiverStatus(ReceiverStatusBlock),
486 TrackingStatus(ChannelStatusBlock),
487 ChannelStatus(ChannelStatusBlock),
488 SatVisibility(SatVisibilityBlock),
489 QualityInd(QualityIndBlock),
490 InputLink(InputLinkBlock),
491 OutputLink(OutputLinkBlock),
492 LBandTrackerStatus(LBandTrackerStatusBlock),
493 Commands(CommandsBlock),
494 Comment(CommentBlock),
495 ReceiverSetup(ReceiverSetupBlock),
496 BBSamples(BBSamplesBlock),
497 ASCIIIn(ASCIIInBlock),
498 NtripClientStatus(NtripClientStatusBlock),
499 NtripServerStatus(NtripServerStatusBlock),
500 RfStatus(RfStatusBlock),
501 RtcmDatum(RtcmDatumBlock),
502 LBandBeams(LBandBeamsBlock),
503 DynDnsStatus(DynDnsStatusBlock),
504 DiskStatus(DiskStatusBlock),
505 P2ppStatus(P2ppStatusBlock),
506 CosmosStatus(CosmosStatusBlock),
507 RxMessage(RxMessageBlock),
508 EncapsulatedOutput(EncapsulatedOutputBlock),
509 GisAction(GisActionBlock),
510 GisStatus(GisStatusBlock),
511
512 ReceiverTime(ReceiverTimeBlock),
514 PpsOffset(PpsOffsetBlock),
515 ExtEvent(ExtEventBlock),
516 ExtEventPvtCartesian(ExtEventPvtCartesianBlock),
517 ExtEventPvtGeodetic(ExtEventPvtGeodeticBlock),
518 ExtEventBaseVectGeod(ExtEventBaseVectGeodBlock),
519 ExtEventAttEuler(ExtEventAttEulerBlock),
520 EndOfPvt(EndOfPvtBlock),
521 IntPvCart(IntPvCartBlock),
522 IntPvGeod(IntPvGeodBlock),
523 IntPvaaGeod(IntPvaaGeodBlock),
524 IntAttEuler(IntAttEulerBlock),
525 IntPosCovCart(IntPosCovCartBlock),
526 IntVelCovCart(IntVelCovCartBlock),
527 IntPosCovGeod(IntPosCovGeodBlock),
528 IntVelCovGeod(IntVelCovGeodBlock),
529 IntAttCovEuler(IntAttCovEulerBlock),
530 IpStatus(IpStatusBlock),
531 IqCorr(IqCorrBlock),
532 ExtSensorMeas(ExtSensorMeasBlock),
533 ExtSensorStatus(ExtSensorStatusBlock),
534 ExtSensorSetup(ExtSensorSetupBlock),
535
536 KnownOpaque {
538 id: u16,
539 rev: u8,
540 data: Vec<u8>,
542 },
543
544 Unknown {
546 id: u16,
547 rev: u8,
548 data: Vec<u8>,
550 },
551}
552
553impl SbfBlock {
554 pub fn parse(data: &[u8]) -> SbfResult<(Self, usize)> {
562 if data.len() < 2 || data[0] != 0x24 || data[1] != 0x40 {
564 return Err(SbfError::InvalidSync);
565 }
566
567 let header = SbfHeader::parse(&data[2..])?;
569 let total_len = header.length as usize;
570
571 if data.len() < total_len {
573 return Err(SbfError::IncompleteBlock {
574 needed: total_len,
575 have: data.len(),
576 });
577 }
578
579 let block_data = &data[2..];
581
582 let block = match header.block_id {
584 block_ids::MEAS_EPOCH => {
585 SbfBlock::MeasEpoch(MeasEpochBlock::parse(&header, block_data)?)
586 }
587 block_ids::MEAS_EXTRA => {
588 SbfBlock::MeasExtra(MeasExtraBlock::parse(&header, block_data)?)
589 }
590 block_ids::END_OF_MEAS => {
591 SbfBlock::EndOfMeas(EndOfMeasBlock::parse(&header, block_data)?)
592 }
593 block_ids::MEAS3_RANGES => {
594 SbfBlock::Meas3Ranges(Meas3RangesBlock::parse(&header, block_data)?)
595 }
596 block_ids::MEAS3_CN0_HI_RES => {
597 SbfBlock::Meas3Cn0HiRes(Meas3Cn0HiResBlock::parse(&header, block_data)?)
598 }
599 block_ids::MEAS3_DOPPLER => {
600 SbfBlock::Meas3Doppler(Meas3DopplerBlock::parse(&header, block_data)?)
601 }
602 block_ids::MEAS3_PP => SbfBlock::Meas3Pp(Meas3PpBlock::parse(&header, block_data)?),
603 block_ids::MEAS3_MP => SbfBlock::Meas3Mp(Meas3MpBlock::parse(&header, block_data)?),
604 block_ids::PVT_GEODETIC => {
605 SbfBlock::PvtGeodetic(PvtGeodeticBlock::parse(&header, block_data)?)
606 }
607 block_ids::PVT_CARTESIAN => {
608 SbfBlock::PvtCartesian(PvtCartesianBlock::parse(&header, block_data)?)
609 }
610 block_ids::DOP | block_ids::DOP_LEGACY => {
611 SbfBlock::Dop(DopBlock::parse(&header, block_data)?)
612 }
613 block_ids::POS_CART => SbfBlock::PosCart(PosCartBlock::parse(&header, block_data)?),
614 block_ids::PVT_SAT_CARTESIAN => {
615 SbfBlock::PvtSatCartesian(PvtSatCartesianBlock::parse(&header, block_data)?)
616 }
617 block_ids::PVT_RESIDUALS_V2 => {
618 SbfBlock::PvtResidualsV2(PvtResidualsV2Block::parse(&header, block_data)?)
619 }
620 block_ids::RAIM_STATISTICS_V2 => {
621 SbfBlock::RaimStatisticsV2(RaimStatisticsV2Block::parse(&header, block_data)?)
622 }
623 block_ids::BASE_VECTOR_CART => {
624 SbfBlock::BaseVectorCart(BaseVectorCartBlock::parse(&header, block_data)?)
625 }
626 block_ids::BASE_VECTOR_GEOD => {
627 SbfBlock::BaseVectorGeod(BaseVectorGeodBlock::parse(&header, block_data)?)
628 }
629 block_ids::POS_COV_CARTESIAN => {
630 SbfBlock::PosCovCartesian(PosCovCartesianBlock::parse(&header, block_data)?)
631 }
632 block_ids::POS_COV_GEODETIC => {
633 SbfBlock::PosCovGeodetic(PosCovGeodeticBlock::parse(&header, block_data)?)
634 }
635 block_ids::VEL_COV_CARTESIAN => {
636 SbfBlock::VelCovCartesian(VelCovCartesianBlock::parse(&header, block_data)?)
637 }
638 block_ids::VEL_COV_GEODETIC => {
639 SbfBlock::VelCovGeodetic(VelCovGeodeticBlock::parse(&header, block_data)?)
640 }
641 block_ids::GEO_CORRECTIONS => {
642 SbfBlock::GeoCorrections(GeoCorrectionsBlock::parse(&header, block_data)?)
643 }
644 block_ids::BASE_STATION => {
645 SbfBlock::BaseStation(BaseStationBlock::parse(&header, block_data)?)
646 }
647 block_ids::DIFF_CORR_IN => {
648 SbfBlock::DiffCorrIn(DiffCorrInBlock::parse(&header, block_data)?)
649 }
650 block_ids::PVT_SUPPORT => {
651 SbfBlock::PvtSupport(PvtSupportBlock::parse(&header, block_data)?)
652 }
653 block_ids::PVT_SUPPORT_A => {
654 SbfBlock::PvtSupportA(PvtSupportABlock::parse(&header, block_data)?)
655 }
656 block_ids::POS_LOCAL => SbfBlock::PosLocal(PosLocalBlock::parse(&header, block_data)?),
657 block_ids::POS_PROJECTED => {
658 SbfBlock::PosProjected(PosProjectedBlock::parse(&header, block_data)?)
659 }
660 block_ids::INT_PVA_AGEOD => {
661 SbfBlock::IntPvaaGeod(IntPvaaGeodBlock::parse(&header, block_data)?)
662 }
663 block_ids::ATT_EULER => SbfBlock::AttEuler(AttEulerBlock::parse(&header, block_data)?),
664 block_ids::ATT_COV_EULER => {
665 SbfBlock::AttCovEuler(AttCovEulerBlock::parse(&header, block_data)?)
666 }
667 block_ids::AUX_ANT_POSITIONS => {
668 SbfBlock::AuxAntPositions(AuxAntPositionsBlock::parse(&header, block_data)?)
669 }
670 block_ids::END_OF_ATT => SbfBlock::EndOfAtt(EndOfAttBlock::parse(&header, block_data)?),
671 block_ids::GPS_NAV => SbfBlock::GpsNav(GpsNavBlock::parse(&header, block_data)?),
672 block_ids::GPS_ALM => SbfBlock::GpsAlm(GpsAlmBlock::parse(&header, block_data)?),
673 block_ids::GPS_ION => SbfBlock::GpsIon(GpsIonBlock::parse(&header, block_data)?),
674 block_ids::GPS_UTC => SbfBlock::GpsUtc(GpsUtcBlock::parse(&header, block_data)?),
675 block_ids::GPS_CNAV => SbfBlock::GpsCNav(GpsCNavBlock::parse(&header, block_data)?),
676 block_ids::GAL_NAV => SbfBlock::GalNav(GalNavBlock::parse(&header, block_data)?),
677 block_ids::GAL_ALM => SbfBlock::GalAlm(GalAlmBlock::parse(&header, block_data)?),
678 block_ids::GAL_ION => SbfBlock::GalIon(GalIonBlock::parse(&header, block_data)?),
679 block_ids::GAL_UTC => SbfBlock::GalUtc(GalUtcBlock::parse(&header, block_data)?),
680 block_ids::GAL_GST_GPS => {
681 SbfBlock::GalGstGps(GalGstGpsBlock::parse(&header, block_data)?)
682 }
683 block_ids::GAL_AUTH_STATUS => {
684 SbfBlock::GalAuthStatus(GalAuthStatusBlock::parse(&header, block_data)?)
685 }
686 block_ids::GAL_SAR_RLM => {
687 SbfBlock::GalSarRlm(GalSarRlmBlock::parse(&header, block_data)?)
688 }
689 block_ids::GLO_NAV => SbfBlock::GloNav(GloNavBlock::parse(&header, block_data)?),
690 block_ids::GLO_ALM => SbfBlock::GloAlm(GloAlmBlock::parse(&header, block_data)?),
691 block_ids::GLO_TIME => SbfBlock::GloTime(GloTimeBlock::parse(&header, block_data)?),
692 block_ids::BDS_ION => SbfBlock::BdsIon(BdsIonBlock::parse(&header, block_data)?),
693 block_ids::BDS_NAV => SbfBlock::BdsNav(BdsNavBlock::parse(&header, block_data)?),
694 block_ids::BDS_ALM => SbfBlock::BdsAlm(BdsAlmBlock::parse(&header, block_data)?),
695 block_ids::BDS_UTC => SbfBlock::BdsUtc(BdsUtcBlock::parse(&header, block_data)?),
696 block_ids::BDS_CNAV1 => SbfBlock::BdsCNav1(BdsCNav1Block::parse(&header, block_data)?),
697 block_ids::BDS_CNAV2 => SbfBlock::BdsCNav2(BdsCNav2Block::parse(&header, block_data)?),
698 block_ids::BDS_CNAV3 => SbfBlock::BdsCNav3(BdsCNav3Block::parse(&header, block_data)?),
699 block_ids::QZS_NAV => SbfBlock::QzsNav(QzsNavBlock::parse(&header, block_data)?),
700 block_ids::QZS_ALM => SbfBlock::QzsAlm(QzsAlmBlock::parse(&header, block_data)?),
701 block_ids::GEO_IONO_DELAY => {
702 SbfBlock::GeoIonoDelay(GeoIonoDelayBlock::parse(&header, block_data)?)
703 }
704 block_ids::GPS_RAW_CA => SbfBlock::GpsRawCa(GpsRawCaBlock::parse(&header, block_data)?),
705 block_ids::GPS_RAW_L2C => {
706 SbfBlock::GpsRawL2C(GpsRawL2CBlock::parse(&header, block_data)?)
707 }
708 block_ids::GPS_RAW_L5 => SbfBlock::GpsRawL5(GpsRawL5Block::parse(&header, block_data)?),
709 block_ids::GAL_RAW_FNAV => {
710 SbfBlock::GalRawFnav(GalRawFnavBlock::parse(&header, block_data)?)
711 }
712 block_ids::GAL_RAW_INAV => {
713 SbfBlock::GalRawInav(GalRawInavBlock::parse(&header, block_data)?)
714 }
715 block_ids::GAL_RAW_CNAV => {
716 SbfBlock::GalRawCnav(GalRawCnavBlock::parse(&header, block_data)?)
717 }
718 block_ids::GEO_RAW_L1 => SbfBlock::GeoRawL1(GeoRawL1Block::parse(&header, block_data)?),
719 block_ids::GEO_RAW_L5 => SbfBlock::GeoRawL5(GeoRawL5Block::parse(&header, block_data)?),
720 block_ids::GLO_RAW_CA => SbfBlock::GloRawCa(GloRawCaBlock::parse(&header, block_data)?),
721 block_ids::CMP_RAW => SbfBlock::CmpRaw(CmpRawBlock::parse(&header, block_data)?),
722 block_ids::BDS_RAW_B1C => {
723 SbfBlock::BdsRawB1c(BdsRawB1cBlock::parse(&header, block_data)?)
724 }
725 block_ids::BDS_RAW_B2A => {
726 SbfBlock::BdsRawB2a(BdsRawB2aBlock::parse(&header, block_data)?)
727 }
728 block_ids::BDS_RAW_B2B => {
729 SbfBlock::BdsRawB2b(BdsRawB2bBlock::parse(&header, block_data)?)
730 }
731 block_ids::NAVIC_RAW => SbfBlock::IrnssRaw(IrnssRawBlock::parse(&header, block_data)?),
732 block_ids::QZS_RAW_L1CA => {
733 SbfBlock::QzsRawL1Ca(QzsRawL1CaBlock::parse(&header, block_data)?)
734 }
735 block_ids::QZS_RAW_L2C => {
736 SbfBlock::QzsRawL2C(QzsRawL2CBlock::parse(&header, block_data)?)
737 }
738 block_ids::QZS_RAW_L5 => SbfBlock::QzsRawL5(QzsRawL5Block::parse(&header, block_data)?),
739 block_ids::GEO_MT00 => SbfBlock::GeoMt00(GeoMt00Block::parse(&header, block_data)?),
740 block_ids::GEO_PRN_MASK => {
741 SbfBlock::GeoPrnMask(GeoPrnMaskBlock::parse(&header, block_data)?)
742 }
743 block_ids::GEO_FAST_CORR => {
744 SbfBlock::GeoFastCorr(GeoFastCorrBlock::parse(&header, block_data)?)
745 }
746 block_ids::GEO_FAST_CORR_DEGR => {
747 SbfBlock::GeoFastCorrDegr(GeoFastCorrDegrBlock::parse(&header, block_data)?)
748 }
749 block_ids::GEO_DEGR_FACTORS => {
750 SbfBlock::GeoDegrFactors(GeoDegrFactorsBlock::parse(&header, block_data)?)
751 }
752 block_ids::GEO_SERVICE_LEVEL => {
753 SbfBlock::GeoServiceLevel(GeoServiceLevelBlock::parse(&header, block_data)?)
754 }
755 block_ids::GEO_NAV => SbfBlock::GeoNav(GeoNavBlock::parse(&header, block_data)?),
756 block_ids::GEO_INTEGRITY => {
757 SbfBlock::GeoIntegrity(GeoIntegrityBlock::parse(&header, block_data)?)
758 }
759 block_ids::GEO_ALM => SbfBlock::GeoAlm(GeoAlmBlock::parse(&header, block_data)?),
760 block_ids::GEO_NETWORK_TIME => {
761 SbfBlock::GeoNetworkTime(GeoNetworkTimeBlock::parse(&header, block_data)?)
762 }
763 block_ids::GEO_IGP_MASK => {
764 SbfBlock::GeoIgpMask(GeoIgpMaskBlock::parse(&header, block_data)?)
765 }
766 block_ids::GEO_LONG_TERM_CORR => {
767 SbfBlock::GeoLongTermCorr(GeoLongTermCorrBlock::parse(&header, block_data)?)
768 }
769 block_ids::GEO_CLOCK_EPH_COV_MATRIX => SbfBlock::GeoClockEphCovMatrix(
770 GeoClockEphCovMatrixBlock::parse(&header, block_data)?,
771 ),
772 block_ids::RECEIVER_STATUS => {
773 SbfBlock::ReceiverStatus(ReceiverStatusBlock::parse(&header, block_data)?)
774 }
775 block_ids::TRACKING_STATUS => {
776 SbfBlock::TrackingStatus(ChannelStatusBlock::parse(&header, block_data)?)
777 }
778 block_ids::CHANNEL_STATUS => {
779 SbfBlock::ChannelStatus(ChannelStatusBlock::parse(&header, block_data)?)
780 }
781 block_ids::SAT_VISIBILITY => {
782 SbfBlock::SatVisibility(SatVisibilityBlock::parse(&header, block_data)?)
783 }
784 block_ids::QUALITY_IND => {
785 SbfBlock::QualityInd(QualityIndBlock::parse(&header, block_data)?)
786 }
787 block_ids::INPUT_LINK => {
788 SbfBlock::InputLink(InputLinkBlock::parse(&header, block_data)?)
789 }
790 block_ids::OUTPUT_LINK => {
791 SbfBlock::OutputLink(OutputLinkBlock::parse(&header, block_data)?)
792 }
793 block_ids::LBAND_TRACKER_STATUS => {
794 SbfBlock::LBandTrackerStatus(LBandTrackerStatusBlock::parse(&header, block_data)?)
795 }
796 block_ids::COMMANDS => SbfBlock::Commands(CommandsBlock::parse(&header, block_data)?),
797 block_ids::COMMENT => SbfBlock::Comment(CommentBlock::parse(&header, block_data)?),
798 block_ids::RECEIVER_SETUP => {
799 SbfBlock::ReceiverSetup(ReceiverSetupBlock::parse(&header, block_data)?)
800 }
801 block_ids::BB_SAMPLES => {
802 SbfBlock::BBSamples(BBSamplesBlock::parse(&header, block_data)?)
803 }
804 block_ids::ASCII_IN => SbfBlock::ASCIIIn(ASCIIInBlock::parse(&header, block_data)?),
805 block_ids::NTRIP_CLIENT_STATUS => {
806 SbfBlock::NtripClientStatus(NtripClientStatusBlock::parse(&header, block_data)?)
807 }
808 block_ids::NTRIP_SERVER_STATUS => {
809 SbfBlock::NtripServerStatus(NtripServerStatusBlock::parse(&header, block_data)?)
810 }
811 block_ids::RF_STATUS => SbfBlock::RfStatus(RfStatusBlock::parse(&header, block_data)?),
812 block_ids::RTCM_DATUM => {
813 SbfBlock::RtcmDatum(RtcmDatumBlock::parse(&header, block_data)?)
814 }
815 block_ids::LBAND_BEAMS => {
816 SbfBlock::LBandBeams(LBandBeamsBlock::parse(&header, block_data)?)
817 }
818 block_ids::DYN_DNS_STATUS => {
819 SbfBlock::DynDnsStatus(DynDnsStatusBlock::parse(&header, block_data)?)
820 }
821 block_ids::DISK_STATUS => {
822 SbfBlock::DiskStatus(DiskStatusBlock::parse(&header, block_data)?)
823 }
824 block_ids::P2PP_STATUS => {
825 SbfBlock::P2ppStatus(P2ppStatusBlock::parse(&header, block_data)?)
826 }
827 block_ids::COSMOS_STATUS => {
828 SbfBlock::CosmosStatus(CosmosStatusBlock::parse(&header, block_data)?)
829 }
830 block_ids::RX_MESSAGE => {
831 SbfBlock::RxMessage(RxMessageBlock::parse(&header, block_data)?)
832 }
833 block_ids::ENCAPSULATED_OUTPUT => {
834 SbfBlock::EncapsulatedOutput(EncapsulatedOutputBlock::parse(&header, block_data)?)
835 }
836 block_ids::GIS_ACTION => {
837 SbfBlock::GisAction(GisActionBlock::parse(&header, block_data)?)
838 }
839 block_ids::GIS_STATUS => {
840 SbfBlock::GisStatus(GisStatusBlock::parse(&header, block_data)?)
841 }
842 block_ids::RECEIVER_TIME => {
843 SbfBlock::ReceiverTime(ReceiverTimeBlock::parse(&header, block_data)?)
844 }
845 block_ids::PPS_OFFSET => {
846 SbfBlock::PpsOffset(PpsOffsetBlock::parse(&header, block_data)?)
847 }
848 block_ids::EXT_EVENT => SbfBlock::ExtEvent(ExtEventBlock::parse(&header, block_data)?),
849 block_ids::EXT_EVENT_PVT_CARTESIAN => SbfBlock::ExtEventPvtCartesian(
850 ExtEventPvtCartesianBlock::parse(&header, block_data)?,
851 ),
852 block_ids::EXT_EVENT_PVT_GEODETIC => {
853 SbfBlock::ExtEventPvtGeodetic(ExtEventPvtGeodeticBlock::parse(&header, block_data)?)
854 }
855 block_ids::EXT_EVENT_BASE_VECT_GEOD => SbfBlock::ExtEventBaseVectGeod(
856 ExtEventBaseVectGeodBlock::parse(&header, block_data)?,
857 ),
858 block_ids::EXT_EVENT_ATT_EULER => {
859 SbfBlock::ExtEventAttEuler(ExtEventAttEulerBlock::parse(&header, block_data)?)
860 }
861 block_ids::END_OF_PVT => SbfBlock::EndOfPvt(EndOfPvtBlock::parse(&header, block_data)?),
862 block_ids::INT_PV_CART => {
863 SbfBlock::IntPvCart(IntPvCartBlock::parse(&header, block_data)?)
864 }
865 block_ids::INT_PV_GEOD => {
866 SbfBlock::IntPvGeod(IntPvGeodBlock::parse(&header, block_data)?)
867 }
868 block_ids::INT_ATT_EULER => {
869 SbfBlock::IntAttEuler(IntAttEulerBlock::parse(&header, block_data)?)
870 }
871 block_ids::INT_POS_COV_CART => {
872 SbfBlock::IntPosCovCart(IntPosCovCartBlock::parse(&header, block_data)?)
873 }
874 block_ids::INT_VEL_COV_CART => {
875 SbfBlock::IntVelCovCart(IntVelCovCartBlock::parse(&header, block_data)?)
876 }
877 block_ids::INT_POS_COV_GEOD => {
878 SbfBlock::IntPosCovGeod(IntPosCovGeodBlock::parse(&header, block_data)?)
879 }
880 block_ids::INT_VEL_COV_GEOD => {
881 SbfBlock::IntVelCovGeod(IntVelCovGeodBlock::parse(&header, block_data)?)
882 }
883 block_ids::INT_ATT_COV_EULER => {
884 SbfBlock::IntAttCovEuler(IntAttCovEulerBlock::parse(&header, block_data)?)
885 }
886 block_ids::IP_STATUS => SbfBlock::IpStatus(IpStatusBlock::parse(&header, block_data)?),
887 block_ids::IQ_CORR => SbfBlock::IqCorr(IqCorrBlock::parse(&header, block_data)?),
888 block_ids::EXT_SENSOR_MEAS => {
889 SbfBlock::ExtSensorMeas(ExtSensorMeasBlock::parse(&header, block_data)?)
890 }
891 block_ids::EXT_SENSOR_STATUS => {
892 SbfBlock::ExtSensorStatus(ExtSensorStatusBlock::parse(&header, block_data)?)
893 }
894 block_ids::EXT_SENSOR_SETUP => {
895 SbfBlock::ExtSensorSetup(ExtSensorSetupBlock::parse(&header, block_data)?)
896 }
897 id if is_known_opaque_id(id) => SbfBlock::KnownOpaque {
898 id,
899 rev: header.block_rev,
900 data: data[8..total_len].to_vec(),
901 },
902 _ => SbfBlock::Unknown {
903 id: header.block_id,
904 rev: header.block_rev,
905 data: data[8..total_len].to_vec(),
906 },
907 };
908
909 Ok((block, total_len))
910 }
911
912 pub fn block_id(&self) -> u16 {
914 match self {
915 SbfBlock::MeasEpoch(_) => block_ids::MEAS_EPOCH,
916 SbfBlock::MeasExtra(_) => block_ids::MEAS_EXTRA,
917 SbfBlock::EndOfMeas(_) => block_ids::END_OF_MEAS,
918 SbfBlock::Meas3Ranges(_) => block_ids::MEAS3_RANGES,
919 SbfBlock::Meas3Cn0HiRes(_) => block_ids::MEAS3_CN0_HI_RES,
920 SbfBlock::Meas3Doppler(_) => block_ids::MEAS3_DOPPLER,
921 SbfBlock::Meas3Pp(_) => block_ids::MEAS3_PP,
922 SbfBlock::Meas3Mp(_) => block_ids::MEAS3_MP,
923 SbfBlock::PvtGeodetic(_) => block_ids::PVT_GEODETIC,
924 SbfBlock::PvtCartesian(_) => block_ids::PVT_CARTESIAN,
925 SbfBlock::Dop(_) => block_ids::DOP,
926 SbfBlock::PosCart(_) => block_ids::POS_CART,
927 SbfBlock::PvtSatCartesian(_) => block_ids::PVT_SAT_CARTESIAN,
928 SbfBlock::PvtResidualsV2(_) => block_ids::PVT_RESIDUALS_V2,
929 SbfBlock::RaimStatisticsV2(_) => block_ids::RAIM_STATISTICS_V2,
930 SbfBlock::BaseVectorCart(_) => block_ids::BASE_VECTOR_CART,
931 SbfBlock::BaseVectorGeod(_) => block_ids::BASE_VECTOR_GEOD,
932 SbfBlock::PosCovCartesian(_) => block_ids::POS_COV_CARTESIAN,
933 SbfBlock::PosCovGeodetic(_) => block_ids::POS_COV_GEODETIC,
934 SbfBlock::VelCovCartesian(_) => block_ids::VEL_COV_CARTESIAN,
935 SbfBlock::VelCovGeodetic(_) => block_ids::VEL_COV_GEODETIC,
936 SbfBlock::GeoCorrections(_) => block_ids::GEO_CORRECTIONS,
937 SbfBlock::BaseStation(_) => block_ids::BASE_STATION,
938 SbfBlock::DiffCorrIn(_) => block_ids::DIFF_CORR_IN,
939 SbfBlock::PvtSupport(_) => block_ids::PVT_SUPPORT,
940 SbfBlock::PvtSupportA(_) => block_ids::PVT_SUPPORT_A,
941 SbfBlock::PosLocal(_) => block_ids::POS_LOCAL,
942 SbfBlock::PosProjected(_) => block_ids::POS_PROJECTED,
943 SbfBlock::IntPvaaGeod(_) => block_ids::INT_PVA_AGEOD,
944 SbfBlock::AttEuler(_) => block_ids::ATT_EULER,
945 SbfBlock::AttCovEuler(_) => block_ids::ATT_COV_EULER,
946 SbfBlock::AuxAntPositions(_) => block_ids::AUX_ANT_POSITIONS,
947 SbfBlock::EndOfAtt(_) => block_ids::END_OF_ATT,
948 SbfBlock::GpsNav(_) => block_ids::GPS_NAV,
949 SbfBlock::GpsAlm(_) => block_ids::GPS_ALM,
950 SbfBlock::GpsIon(_) => block_ids::GPS_ION,
951 SbfBlock::GpsUtc(_) => block_ids::GPS_UTC,
952 SbfBlock::GpsCNav(_) => block_ids::GPS_CNAV,
953 SbfBlock::GalNav(_) => block_ids::GAL_NAV,
954 SbfBlock::GalAlm(_) => block_ids::GAL_ALM,
955 SbfBlock::GalIon(_) => block_ids::GAL_ION,
956 SbfBlock::GalUtc(_) => block_ids::GAL_UTC,
957 SbfBlock::GalGstGps(_) => block_ids::GAL_GST_GPS,
958 SbfBlock::GalAuthStatus(_) => block_ids::GAL_AUTH_STATUS,
959 SbfBlock::GalSarRlm(_) => block_ids::GAL_SAR_RLM,
960 SbfBlock::GloNav(_) => block_ids::GLO_NAV,
961 SbfBlock::GloAlm(_) => block_ids::GLO_ALM,
962 SbfBlock::GloTime(_) => block_ids::GLO_TIME,
963 SbfBlock::BdsIon(_) => block_ids::BDS_ION,
964 SbfBlock::BdsNav(_) => block_ids::BDS_NAV,
965 SbfBlock::BdsAlm(_) => block_ids::BDS_ALM,
966 SbfBlock::BdsUtc(_) => block_ids::BDS_UTC,
967 SbfBlock::BdsCNav1(_) => block_ids::BDS_CNAV1,
968 SbfBlock::BdsCNav2(_) => block_ids::BDS_CNAV2,
969 SbfBlock::BdsCNav3(_) => block_ids::BDS_CNAV3,
970 SbfBlock::QzsNav(_) => block_ids::QZS_NAV,
971 SbfBlock::QzsAlm(_) => block_ids::QZS_ALM,
972 SbfBlock::GeoIonoDelay(_) => block_ids::GEO_IONO_DELAY,
973 SbfBlock::GpsRawCa(_) => block_ids::GPS_RAW_CA,
974 SbfBlock::GpsRawL2C(_) => block_ids::GPS_RAW_L2C,
975 SbfBlock::GpsRawL5(_) => block_ids::GPS_RAW_L5,
976 SbfBlock::GalRawFnav(_) => block_ids::GAL_RAW_FNAV,
977 SbfBlock::GalRawInav(_) => block_ids::GAL_RAW_INAV,
978 SbfBlock::GalRawCnav(_) => block_ids::GAL_RAW_CNAV,
979 SbfBlock::GeoRawL1(_) => block_ids::GEO_RAW_L1,
980 SbfBlock::GeoRawL5(_) => block_ids::GEO_RAW_L5,
981 SbfBlock::GloRawCa(_) => block_ids::GLO_RAW_CA,
982 SbfBlock::CmpRaw(_) => block_ids::CMP_RAW,
983 SbfBlock::BdsRawB1c(_) => block_ids::BDS_RAW_B1C,
984 SbfBlock::BdsRawB2a(_) => block_ids::BDS_RAW_B2A,
985 SbfBlock::BdsRawB2b(_) => block_ids::BDS_RAW_B2B,
986 SbfBlock::IrnssRaw(_) => block_ids::NAVIC_RAW,
987 SbfBlock::QzsRawL1Ca(_) => block_ids::QZS_RAW_L1CA,
988 SbfBlock::QzsRawL2C(_) => block_ids::QZS_RAW_L2C,
989 SbfBlock::QzsRawL5(_) => block_ids::QZS_RAW_L5,
990 SbfBlock::GeoMt00(_) => block_ids::GEO_MT00,
991 SbfBlock::GeoPrnMask(_) => block_ids::GEO_PRN_MASK,
992 SbfBlock::GeoFastCorr(_) => block_ids::GEO_FAST_CORR,
993 SbfBlock::GeoFastCorrDegr(_) => block_ids::GEO_FAST_CORR_DEGR,
994 SbfBlock::GeoDegrFactors(_) => block_ids::GEO_DEGR_FACTORS,
995 SbfBlock::GeoServiceLevel(_) => block_ids::GEO_SERVICE_LEVEL,
996 SbfBlock::GeoNav(_) => block_ids::GEO_NAV,
997 SbfBlock::GeoIntegrity(_) => block_ids::GEO_INTEGRITY,
998 SbfBlock::GeoAlm(_) => block_ids::GEO_ALM,
999 SbfBlock::GeoNetworkTime(_) => block_ids::GEO_NETWORK_TIME,
1000 SbfBlock::GeoIgpMask(_) => block_ids::GEO_IGP_MASK,
1001 SbfBlock::GeoLongTermCorr(_) => block_ids::GEO_LONG_TERM_CORR,
1002 SbfBlock::GeoClockEphCovMatrix(_) => block_ids::GEO_CLOCK_EPH_COV_MATRIX,
1003 SbfBlock::ReceiverStatus(_) => block_ids::RECEIVER_STATUS,
1004 SbfBlock::TrackingStatus(_) => block_ids::TRACKING_STATUS,
1005 SbfBlock::ChannelStatus(_) => block_ids::CHANNEL_STATUS,
1006 SbfBlock::SatVisibility(_) => block_ids::SAT_VISIBILITY,
1007 SbfBlock::QualityInd(_) => block_ids::QUALITY_IND,
1008 SbfBlock::InputLink(_) => block_ids::INPUT_LINK,
1009 SbfBlock::OutputLink(_) => block_ids::OUTPUT_LINK,
1010 SbfBlock::LBandTrackerStatus(_) => block_ids::LBAND_TRACKER_STATUS,
1011 SbfBlock::Commands(_) => block_ids::COMMANDS,
1012 SbfBlock::Comment(_) => block_ids::COMMENT,
1013 SbfBlock::ReceiverSetup(_) => block_ids::RECEIVER_SETUP,
1014 SbfBlock::BBSamples(_) => block_ids::BB_SAMPLES,
1015 SbfBlock::ASCIIIn(_) => block_ids::ASCII_IN,
1016 SbfBlock::NtripClientStatus(_) => block_ids::NTRIP_CLIENT_STATUS,
1017 SbfBlock::NtripServerStatus(_) => block_ids::NTRIP_SERVER_STATUS,
1018 SbfBlock::RfStatus(_) => block_ids::RF_STATUS,
1019 SbfBlock::RtcmDatum(_) => block_ids::RTCM_DATUM,
1020 SbfBlock::LBandBeams(_) => block_ids::LBAND_BEAMS,
1021 SbfBlock::DynDnsStatus(_) => block_ids::DYN_DNS_STATUS,
1022 SbfBlock::DiskStatus(_) => block_ids::DISK_STATUS,
1023 SbfBlock::P2ppStatus(_) => block_ids::P2PP_STATUS,
1024 SbfBlock::CosmosStatus(_) => block_ids::COSMOS_STATUS,
1025 SbfBlock::RxMessage(_) => block_ids::RX_MESSAGE,
1026 SbfBlock::EncapsulatedOutput(_) => block_ids::ENCAPSULATED_OUTPUT,
1027 SbfBlock::GisAction(_) => block_ids::GIS_ACTION,
1028 SbfBlock::GisStatus(_) => block_ids::GIS_STATUS,
1029 SbfBlock::ReceiverTime(_) => block_ids::RECEIVER_TIME,
1030 SbfBlock::PpsOffset(_) => block_ids::PPS_OFFSET,
1031 SbfBlock::ExtEvent(_) => block_ids::EXT_EVENT,
1032 SbfBlock::ExtEventPvtCartesian(_) => block_ids::EXT_EVENT_PVT_CARTESIAN,
1033 SbfBlock::ExtEventPvtGeodetic(_) => block_ids::EXT_EVENT_PVT_GEODETIC,
1034 SbfBlock::ExtEventBaseVectGeod(_) => block_ids::EXT_EVENT_BASE_VECT_GEOD,
1035 SbfBlock::ExtEventAttEuler(_) => block_ids::EXT_EVENT_ATT_EULER,
1036 SbfBlock::EndOfPvt(_) => block_ids::END_OF_PVT,
1037 SbfBlock::IntPvCart(_) => block_ids::INT_PV_CART,
1038 SbfBlock::IntPvGeod(_) => block_ids::INT_PV_GEOD,
1039 SbfBlock::IntAttEuler(_) => block_ids::INT_ATT_EULER,
1040 SbfBlock::IntPosCovCart(_) => block_ids::INT_POS_COV_CART,
1041 SbfBlock::IntVelCovCart(_) => block_ids::INT_VEL_COV_CART,
1042 SbfBlock::IntPosCovGeod(_) => block_ids::INT_POS_COV_GEOD,
1043 SbfBlock::IntVelCovGeod(_) => block_ids::INT_VEL_COV_GEOD,
1044 SbfBlock::IntAttCovEuler(_) => block_ids::INT_ATT_COV_EULER,
1045 SbfBlock::IpStatus(_) => block_ids::IP_STATUS,
1046 SbfBlock::IqCorr(_) => block_ids::IQ_CORR,
1047 SbfBlock::ExtSensorMeas(_) => block_ids::EXT_SENSOR_MEAS,
1048 SbfBlock::ExtSensorStatus(_) => block_ids::EXT_SENSOR_STATUS,
1049 SbfBlock::ExtSensorSetup(_) => block_ids::EXT_SENSOR_SETUP,
1050 SbfBlock::KnownOpaque { id, .. } => *id,
1051 SbfBlock::Unknown { id, .. } => *id,
1052 }
1053 }
1054
1055 pub fn name(&self) -> &'static str {
1057 block_name(self.block_id())
1058 }
1059
1060 pub fn unsupported_payload(&self) -> Option<&[u8]> {
1064 match self {
1065 SbfBlock::KnownOpaque { data, .. } | SbfBlock::Unknown { data, .. } => Some(data),
1066 _ => None,
1067 }
1068 }
1069}
1070
1071#[cfg(test)]
1072mod tests {
1073 use super::*;
1074
1075 fn build_min_block(block_id: u16, block_rev: u8) -> Vec<u8> {
1076 let total_len = 16u16;
1077 let mut data = vec![0u8; total_len as usize];
1078 data[0] = 0x24;
1079 data[1] = 0x40;
1080
1081 let id_rev = block_id | ((block_rev as u16 & 0x07) << 13);
1082 data[4..6].copy_from_slice(&id_rev.to_le_bytes());
1083 data[6..8].copy_from_slice(&total_len.to_le_bytes());
1084 data[8..12].copy_from_slice(&123_456u32.to_le_bytes());
1085 data[12..14].copy_from_slice(&2150u16.to_le_bytes());
1086 data[14] = 0xAA;
1087 data[15] = 0x55;
1088 data
1089 }
1090
1091 #[test]
1092 fn test_meas3_ranges_min_block_parse() {
1093 let total_len = 20u16;
1095 let mut data = vec![0u8; total_len as usize];
1096 data[0] = 0x24;
1097 data[1] = 0x40;
1098 let id_rev = block_ids::MEAS3_RANGES | ((2u16 & 0x07) << 13);
1099 data[4..6].copy_from_slice(&id_rev.to_le_bytes());
1100 data[6..8].copy_from_slice(&total_len.to_le_bytes());
1101 data[8..12].copy_from_slice(&123_456u32.to_le_bytes());
1102 data[12..14].copy_from_slice(&2150u16.to_le_bytes());
1103
1104 let (block, consumed) = SbfBlock::parse(&data).unwrap();
1105 assert_eq!(consumed, usize::from(total_len));
1106 match block {
1107 SbfBlock::Meas3Ranges(m) => {
1108 assert_eq!(m.tow_ms(), 123_456);
1109 assert_eq!(m.wnc(), 2150);
1110 assert_eq!(m.data, Vec::<u8>::new());
1111 }
1112 other => panic!("expected Meas3Ranges, got {:?}", other),
1113 }
1114 }
1115
1116 #[test]
1117 fn test_pvt_support_parse() {
1118 let data = build_min_block(block_ids::PVT_SUPPORT, 1);
1119 let (block, consumed) = SbfBlock::parse(&data).unwrap();
1120
1121 assert_eq!(consumed, 16);
1122 assert_eq!(block.block_id(), block_ids::PVT_SUPPORT);
1123 match block {
1124 SbfBlock::PvtSupport(pvt) => {
1125 assert_eq!(pvt.tow_ms(), 123_456);
1126 assert_eq!(pvt.wnc(), 2150);
1127 assert!((pvt.tow_seconds() - 123.456).abs() < 1e-6);
1128 }
1129 other => panic!("expected PvtSupport, got {:?}", other),
1130 }
1131 }
1132
1133 #[test]
1134 fn test_bds_raw_b2b_min_block_parse() {
1135 let total_len: u16 = 144; let mut data = vec![0u8; total_len as usize];
1137 data[0] = 0x24;
1138 data[1] = 0x40;
1139 let id_rev = block_ids::BDS_RAW_B2B;
1140 data[4..6].copy_from_slice(&id_rev.to_le_bytes());
1141 data[6..8].copy_from_slice(&total_len.to_le_bytes());
1142 data[8..12].copy_from_slice(&1_000u32.to_le_bytes());
1143 data[12..14].copy_from_slice(&200u16.to_le_bytes());
1144 data[14] = 5;
1145 data[15] = 1;
1146
1147 let (block, consumed) = SbfBlock::parse(&data).unwrap();
1148 assert_eq!(consumed, total_len as usize);
1149 match block {
1150 SbfBlock::BdsRawB2b(b) => {
1151 assert_eq!(b.tow_ms(), 1_000);
1152 assert_eq!(b.wnc(), 200);
1153 assert_eq!(b.svid, 5);
1154 }
1155 other => panic!("expected BdsRawB2b, got {:?}", other),
1156 }
1157 }
1158
1159 #[test]
1160 fn test_gal_auth_status_min_block_parse() {
1161 let total_len: u16 = 52;
1162 let mut data = vec![0u8; total_len as usize];
1163 data[0] = 0x24;
1164 data[1] = 0x40;
1165 let id_rev = block_ids::GAL_AUTH_STATUS;
1166 data[4..6].copy_from_slice(&id_rev.to_le_bytes());
1167 data[6..8].copy_from_slice(&total_len.to_le_bytes());
1168 data[8..12].copy_from_slice(&2_000u32.to_le_bytes());
1169 data[12..14].copy_from_slice(&201u16.to_le_bytes());
1170 data[14..16].copy_from_slice(&0xABCDu16.to_le_bytes());
1171 data[16..20].copy_from_slice(&1.5f32.to_le_bytes());
1172 for i in 0..8usize {
1173 data[20 + i] = 0x10 + i as u8;
1174 data[28 + i] = 0x20 + i as u8;
1175 data[36 + i] = 0x30 + i as u8;
1176 data[44 + i] = 0x40 + i as u8;
1177 }
1178
1179 let (block, consumed) = SbfBlock::parse(&data).unwrap();
1180 assert_eq!(consumed, total_len as usize);
1181 match block {
1182 SbfBlock::GalAuthStatus(a) => {
1183 assert_eq!(a.tow_ms(), 2_000);
1184 assert_eq!(a.wnc(), 201);
1185 assert_eq!(a.osnma_status, 0xABCD);
1186 assert!((a.trusted_time_delta - 1.5).abs() < 1e-6);
1187 }
1188 other => panic!("expected GalAuthStatus, got {:?}", other),
1189 }
1190 }
1191
1192 #[test]
1193 fn test_unknown_id_stays_unknown() {
1194 let data = build_min_block(4999, 0);
1195 let (block, _) = SbfBlock::parse(&data).unwrap();
1196
1197 match block {
1198 SbfBlock::Unknown { id, rev, data } => {
1199 assert_eq!(id, 4999);
1200 assert_eq!(rev, 0);
1201 assert_eq!(data, vec![0x40, 0xE2, 0x01, 0x00, 0x66, 0x08, 0xAA, 0x55]);
1202 }
1203 other => panic!("expected Unknown, got {:?}", other),
1204 }
1205 }
1206
1207 #[test]
1208 fn test_unsupported_payload_helper() {
1209 let known_data = build_min_block(block_ids::FUGRO_DDS, 0);
1210 let (known, _) = SbfBlock::parse(&known_data).unwrap();
1211 assert_eq!(
1212 known.unsupported_payload(),
1213 Some([0x40, 0xE2, 0x01, 0x00, 0x66, 0x08, 0xAA, 0x55].as_slice())
1214 );
1215
1216 let unknown_data = build_min_block(4998, 1);
1217 let (unknown, _) = SbfBlock::parse(&unknown_data).unwrap();
1218 assert_eq!(
1219 unknown.unsupported_payload(),
1220 Some([0x40, 0xE2, 0x01, 0x00, 0x66, 0x08, 0xAA, 0x55].as_slice())
1221 );
1222 }
1223}