1use super::descriptor_body;
29use crate::error::{Error, Result};
30use alloc::vec::Vec;
31use dvb_common::{Parse, Serialize};
32
33pub const TAG: u8 = 0x6C;
35pub const HEADER_LEN: usize = 2;
37pub const OUTER_FIXED_LEN: usize = 10;
40pub const SUBCELL_LEN: usize = 8;
42pub const EXTENT_MASK: u16 = 0x0FFF;
44
45const LAT_SCALE: f64 = 90.0 / 32768.0;
47const LONG_SCALE: f64 = 180.0 / 32768.0;
49
50fn u16_to_i16(raw: u16) -> i16 {
52 raw as i16
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57#[cfg_attr(feature = "serde", derive(serde::Serialize))]
58pub struct CellListSubcell {
59 pub cell_id_extension: u8,
61 pub subcell_latitude: u16,
63 pub subcell_longitude: u16,
65 pub subcell_extent_of_latitude: u16,
67 pub subcell_extent_of_longitude: u16,
69}
70
71impl CellListSubcell {
72 #[must_use]
74 pub fn subcell_latitude_deg(&self) -> f64 {
75 u16_to_i16(self.subcell_latitude) as f64 * LAT_SCALE
76 }
77
78 #[must_use]
80 pub fn subcell_longitude_deg(&self) -> f64 {
81 u16_to_i16(self.subcell_longitude) as f64 * LONG_SCALE
82 }
83
84 #[must_use]
86 pub fn subcell_extent_of_latitude_deg(&self) -> f64 {
87 (self.subcell_extent_of_latitude & EXTENT_MASK) as f64 * LAT_SCALE
88 }
89
90 #[must_use]
92 pub fn subcell_extent_of_longitude_deg(&self) -> f64 {
93 (self.subcell_extent_of_longitude & EXTENT_MASK) as f64 * LONG_SCALE
94 }
95}
96
97#[derive(Debug, Clone, PartialEq, Eq)]
99#[cfg_attr(feature = "serde", derive(serde::Serialize))]
100pub struct CellListEntry {
101 pub cell_id: u16,
103 pub cell_latitude: u16,
105 pub cell_longitude: u16,
107 pub cell_extent_of_latitude: u16,
109 pub cell_extent_of_longitude: u16,
111 pub subcells: Vec<CellListSubcell>,
113}
114
115impl CellListEntry {
116 #[must_use]
118 pub fn cell_latitude_deg(&self) -> f64 {
119 u16_to_i16(self.cell_latitude) as f64 * LAT_SCALE
120 }
121
122 #[must_use]
124 pub fn cell_longitude_deg(&self) -> f64 {
125 u16_to_i16(self.cell_longitude) as f64 * LONG_SCALE
126 }
127
128 #[must_use]
130 pub fn cell_extent_of_latitude_deg(&self) -> f64 {
131 (self.cell_extent_of_latitude & EXTENT_MASK) as f64 * LAT_SCALE
132 }
133
134 #[must_use]
136 pub fn cell_extent_of_longitude_deg(&self) -> f64 {
137 (self.cell_extent_of_longitude & EXTENT_MASK) as f64 * LONG_SCALE
138 }
139}
140
141#[derive(Debug, Clone, PartialEq, Eq)]
143#[cfg_attr(feature = "serde", derive(serde::Serialize))]
144pub struct CellListDescriptor {
145 pub entries: Vec<CellListEntry>,
147}
148
149fn read_extents(b: &[u8]) -> (u16, u16) {
151 let lat = (u16::from(b[0]) << 4) | (u16::from(b[1]) >> 4);
152 let long = ((u16::from(b[1]) & 0x0F) << 8) | u16::from(b[2]);
153 (lat, long)
154}
155
156fn write_extents(buf: &mut [u8], lat: u16, long: u16) {
158 let lat = lat & EXTENT_MASK;
159 let long = long & EXTENT_MASK;
160 buf[0] = (lat >> 4) as u8;
161 buf[1] = (((lat & 0x0F) << 4) | (long >> 8)) as u8;
162 buf[2] = long as u8;
163}
164
165impl<'a> Parse<'a> for CellListDescriptor {
166 type Error = crate::error::Error;
167 fn parse(bytes: &'a [u8]) -> Result<Self> {
168 let body = descriptor_body(
169 bytes,
170 TAG,
171 "CellListDescriptor",
172 "unexpected tag for cell_list_descriptor",
173 )?;
174 let mut entries = Vec::new();
175 let mut pos = 0;
176 while pos < body.len() {
177 if pos + OUTER_FIXED_LEN > body.len() {
178 return Err(Error::InvalidDescriptor {
179 tag: TAG,
180 reason: "cell_list outer entry truncated",
181 });
182 }
183 let cell_id = u16::from_be_bytes([body[pos], body[pos + 1]]);
184 let cell_latitude = u16::from_be_bytes([body[pos + 2], body[pos + 3]]);
185 let cell_longitude = u16::from_be_bytes([body[pos + 4], body[pos + 5]]);
186 let (cell_extent_of_latitude, cell_extent_of_longitude) =
187 read_extents(&body[pos + 6..pos + 9]);
188 let subcell_info_loop_length = body[pos + 9] as usize;
189 pos += OUTER_FIXED_LEN;
190 if subcell_info_loop_length % SUBCELL_LEN != 0 {
191 return Err(Error::InvalidDescriptor {
192 tag: TAG,
193 reason: "subcell_info_loop_length must be a multiple of 8",
194 });
195 }
196 if pos + subcell_info_loop_length > body.len() {
197 return Err(Error::InvalidDescriptor {
198 tag: TAG,
199 reason: "subcell_info_loop_length exceeds descriptor body",
200 });
201 }
202 let subcell_count = subcell_info_loop_length / SUBCELL_LEN;
203 let mut subcells = Vec::with_capacity(subcell_count);
204 for _ in 0..subcell_count {
205 let cell_id_extension = body[pos];
206 let subcell_latitude = u16::from_be_bytes([body[pos + 1], body[pos + 2]]);
207 let subcell_longitude = u16::from_be_bytes([body[pos + 3], body[pos + 4]]);
208 let (subcell_extent_of_latitude, subcell_extent_of_longitude) =
209 read_extents(&body[pos + 5..pos + 8]);
210 subcells.push(CellListSubcell {
211 cell_id_extension,
212 subcell_latitude,
213 subcell_longitude,
214 subcell_extent_of_latitude,
215 subcell_extent_of_longitude,
216 });
217 pos += SUBCELL_LEN;
218 }
219 entries.push(CellListEntry {
220 cell_id,
221 cell_latitude,
222 cell_longitude,
223 cell_extent_of_latitude,
224 cell_extent_of_longitude,
225 subcells,
226 });
227 }
228 Ok(Self { entries })
229 }
230}
231
232impl CellListDescriptor {
233 fn body_len(&self) -> usize {
234 self.entries
235 .iter()
236 .map(|e| OUTER_FIXED_LEN + e.subcells.len() * SUBCELL_LEN)
237 .sum()
238 }
239}
240
241impl Serialize for CellListDescriptor {
242 type Error = crate::error::Error;
243 fn serialized_len(&self) -> usize {
244 HEADER_LEN + self.body_len()
245 }
246
247 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
248 let body_len = self.body_len();
249 if body_len > u8::MAX as usize {
250 return Err(Error::InvalidDescriptor {
251 tag: TAG,
252 reason: "cell_list_descriptor body exceeds 255 bytes",
253 });
254 }
255 for e in &self.entries {
256 if e.subcells.len() * SUBCELL_LEN > u8::MAX as usize {
257 return Err(Error::InvalidDescriptor {
258 tag: TAG,
259 reason: "subcell_info_loop_length exceeds 255 bytes",
260 });
261 }
262 }
263 let len = self.serialized_len();
264 if buf.len() < len {
265 return Err(Error::OutputBufferTooSmall {
266 need: len,
267 have: buf.len(),
268 });
269 }
270 buf[0] = TAG;
271 buf[1] = body_len as u8;
272 let mut pos = HEADER_LEN;
273 for e in &self.entries {
274 buf[pos..pos + 2].copy_from_slice(&e.cell_id.to_be_bytes());
275 buf[pos + 2..pos + 4].copy_from_slice(&e.cell_latitude.to_be_bytes());
276 buf[pos + 4..pos + 6].copy_from_slice(&e.cell_longitude.to_be_bytes());
277 write_extents(
278 &mut buf[pos + 6..pos + 9],
279 e.cell_extent_of_latitude,
280 e.cell_extent_of_longitude,
281 );
282 buf[pos + 9] = (e.subcells.len() * SUBCELL_LEN) as u8;
283 pos += OUTER_FIXED_LEN;
284 for sc in &e.subcells {
285 buf[pos] = sc.cell_id_extension;
286 buf[pos + 1..pos + 3].copy_from_slice(&sc.subcell_latitude.to_be_bytes());
287 buf[pos + 3..pos + 5].copy_from_slice(&sc.subcell_longitude.to_be_bytes());
288 write_extents(
289 &mut buf[pos + 5..pos + 8],
290 sc.subcell_extent_of_latitude,
291 sc.subcell_extent_of_longitude,
292 );
293 pos += SUBCELL_LEN;
294 }
295 }
296 Ok(len)
297 }
298}
299impl<'a> crate::traits::DescriptorDef<'a> for CellListDescriptor {
300 const TAG: u8 = TAG;
301 const NAME: &'static str = "CELL_LIST";
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307
308 #[test]
309 fn extents_pack_round_trips() {
310 let mut b = [0u8; 3];
311 write_extents(&mut b, 0xABC, 0xDEF);
312 assert_eq!(read_extents(&b), (0xABC, 0xDEF));
313 }
314
315 #[test]
316 fn cell_latitude_deg_zero() {
317 let e = CellListEntry {
318 cell_id: 0,
319 cell_latitude: 0,
320 cell_longitude: 0,
321 cell_extent_of_latitude: 0,
322 cell_extent_of_longitude: 0,
323 subcells: vec![],
324 };
325 assert_eq!(e.cell_latitude_deg(), 0.0);
326 assert_eq!(e.cell_longitude_deg(), 0.0);
327 }
328
329 #[test]
330 fn cell_latitude_deg_max() {
331 let e = CellListEntry {
333 cell_id: 0,
334 cell_latitude: 0x7FFF,
335 cell_longitude: 0,
336 cell_extent_of_latitude: 0,
337 cell_extent_of_longitude: 0,
338 subcells: vec![],
339 };
340 let deg = e.cell_latitude_deg();
341 assert!(deg > 89.9 && deg <= 90.0, "got {deg}");
342 }
343
344 #[test]
345 fn cell_longitude_deg_max() {
346 let e = CellListEntry {
347 cell_id: 0,
348 cell_latitude: 0,
349 cell_longitude: 0x7FFF,
350 cell_extent_of_latitude: 0,
351 cell_extent_of_longitude: 0,
352 subcells: vec![],
353 };
354 let deg = e.cell_longitude_deg();
355 assert!(deg > 179.9 && deg <= 180.0, "got {deg}");
356 }
357
358 #[test]
359 fn cell_extent_deg() {
360 let e = CellListEntry {
362 cell_id: 0,
363 cell_latitude: 0,
364 cell_longitude: 0,
365 cell_extent_of_latitude: 4095,
366 cell_extent_of_longitude: 2048,
367 subcells: vec![],
368 };
369 assert!(e.cell_extent_of_latitude_deg() > 11.2);
370 assert!(e.cell_extent_of_longitude_deg() > 11.2);
371 }
372
373 #[test]
374 fn parse_entry_with_subcell() {
375 let bytes = [
376 TAG, 18, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 8,
379 0x07, 0x11, 0x11, 0x22, 0x22, 0x33, 0x34, 0x44,
381 ];
382 let d = CellListDescriptor::parse(&bytes).unwrap();
383 assert_eq!(d.entries.len(), 1);
384 let e = &d.entries[0];
385 assert_eq!(e.cell_id, 0x1234);
386 assert_eq!(e.cell_latitude, 0x5678);
387 assert_eq!(e.cell_longitude, 0x9ABC);
388 assert_eq!(e.cell_extent_of_latitude, 0xDEF);
389 assert_eq!(e.cell_extent_of_longitude, 0x012);
390 assert_eq!(e.subcells.len(), 1);
391 let sc = &e.subcells[0];
392 assert_eq!(sc.cell_id_extension, 0x07);
393 assert_eq!(sc.subcell_latitude, 0x1111);
394 assert_eq!(sc.subcell_longitude, 0x2222);
395 assert_eq!(sc.subcell_extent_of_latitude, 0x333);
396 assert_eq!(sc.subcell_extent_of_longitude, 0x444);
397
398 let lat_deg = e.cell_latitude_deg();
400 let long_deg = e.cell_longitude_deg();
401 assert!(lat_deg.abs() > 0.0);
402 assert!(long_deg.abs() > 0.0);
403 let sc_lat = sc.subcell_latitude_deg();
404 let sc_long = sc.subcell_longitude_deg();
405 assert!(sc_lat.abs() > 0.0);
406 assert!(sc_long.abs() > 0.0);
407 }
408
409 #[test]
410 fn parse_entry_no_subcells() {
411 let bytes = [
412 TAG, 10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
413 ];
414 let d = CellListDescriptor::parse(&bytes).unwrap();
415 assert_eq!(d.entries.len(), 1);
416 assert!(d.entries[0].subcells.is_empty());
417 }
418
419 #[test]
420 fn empty_body_is_valid() {
421 let d = CellListDescriptor::parse(&[TAG, 0]).unwrap();
422 assert!(d.entries.is_empty());
423 }
424
425 #[test]
426 fn parse_rejects_wrong_tag() {
427 assert!(matches!(
428 CellListDescriptor::parse(&[0x6D, 0]).unwrap_err(),
429 Error::InvalidDescriptor { tag: 0x6D, .. }
430 ));
431 }
432
433 #[test]
434 fn parse_rejects_truncated_outer() {
435 let bytes = [TAG, 5, 0, 0, 0, 0, 0];
436 assert!(matches!(
437 CellListDescriptor::parse(&bytes).unwrap_err(),
438 Error::InvalidDescriptor { tag: TAG, .. }
439 ));
440 }
441
442 #[test]
443 fn parse_rejects_subcell_loop_overrun() {
444 let bytes = [
446 TAG, 10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 8,
447 ];
448 assert!(matches!(
449 CellListDescriptor::parse(&bytes).unwrap_err(),
450 Error::InvalidDescriptor { tag: TAG, .. }
451 ));
452 }
453
454 #[test]
455 fn parse_rejects_buffer_shorter_than_length() {
456 let bytes = [TAG, 10, 0x00, 0x01, 0x00];
457 assert!(matches!(
458 CellListDescriptor::parse(&bytes).unwrap_err(),
459 Error::BufferTooShort { .. }
460 ));
461 }
462
463 #[test]
464 fn serialize_round_trip() {
465 let d = CellListDescriptor {
466 entries: vec![
467 CellListEntry {
468 cell_id: 0x1234,
469 cell_latitude: 0x5678,
470 cell_longitude: 0x9ABC,
471 cell_extent_of_latitude: 0xDEF,
472 cell_extent_of_longitude: 0x012,
473 subcells: vec![CellListSubcell {
474 cell_id_extension: 0x07,
475 subcell_latitude: 0x1111,
476 subcell_longitude: 0x2222,
477 subcell_extent_of_latitude: 0x333,
478 subcell_extent_of_longitude: 0x444,
479 }],
480 },
481 CellListEntry {
482 cell_id: 0xAAAA,
483 cell_latitude: 0xBBBB,
484 cell_longitude: 0xCCCC,
485 cell_extent_of_latitude: 0x111,
486 cell_extent_of_longitude: 0x222,
487 subcells: vec![],
488 },
489 ],
490 };
491 let mut buf = vec![0u8; d.serialized_len()];
492 d.serialize_into(&mut buf).unwrap();
493 assert_eq!(CellListDescriptor::parse(&buf).unwrap(), d);
494 }
495
496 #[test]
497 fn serialize_rejects_too_small_buffer() {
498 let d = CellListDescriptor {
499 entries: vec![CellListEntry {
500 cell_id: 0,
501 cell_latitude: 0,
502 cell_longitude: 0,
503 cell_extent_of_latitude: 0,
504 cell_extent_of_longitude: 0,
505 subcells: vec![],
506 }],
507 };
508 let mut buf = vec![0u8; 3];
509 assert!(matches!(
510 d.serialize_into(&mut buf).unwrap_err(),
511 Error::OutputBufferTooSmall { .. }
512 ));
513 }
514
515 #[test]
516 fn serialize_rejects_over_range_body() {
517 let d = CellListDescriptor {
519 entries: (0..26)
520 .map(|_| CellListEntry {
521 cell_id: 0,
522 cell_latitude: 0,
523 cell_longitude: 0,
524 cell_extent_of_latitude: 0,
525 cell_extent_of_longitude: 0,
526 subcells: vec![],
527 })
528 .collect(),
529 };
530 let mut buf = vec![0u8; d.serialized_len()];
531 assert!(matches!(
532 d.serialize_into(&mut buf).unwrap_err(),
533 Error::InvalidDescriptor { tag: TAG, .. }
534 ));
535 }
536
537 #[cfg(feature = "serde")]
538 #[test]
539 fn serde_round_trip() {
540 let d = CellListDescriptor {
541 entries: vec![CellListEntry {
542 cell_id: 0x1234,
543 cell_latitude: 0x5678,
544 cell_longitude: 0x9ABC,
545 cell_extent_of_latitude: 0xDEF,
546 cell_extent_of_longitude: 0x012,
547 subcells: vec![CellListSubcell {
548 cell_id_extension: 0x07,
549 subcell_latitude: 0x1111,
550 subcell_longitude: 0x2222,
551 subcell_extent_of_latitude: 0x333,
552 subcell_extent_of_longitude: 0x444,
553 }],
554 }],
555 };
556 let json = serde_json::to_string(&d).unwrap();
557 let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
559 }
560}