dted2/
parsers.rs

1#![allow(unused_doc_comments)]
2//! Contains [nom] parsers for various components within a DTED file.
3
4// --------------------------------------------------
5// external
6// --------------------------------------------------
7use nom::{
8    branch::alt,
9    bytes::complete::{tag, take},
10    combinator::{map, map_res, opt},
11    multi::count,
12    number::complete::be_u16,
13    sequence::{preceded, tuple},
14    IResult,
15};
16use num_traits::{int::PrimInt, Unsigned};
17
18// --------------------------------------------------
19// local
20// --------------------------------------------------
21use crate::dted::*;
22use crate::primitives::{Angle, AxisElement};
23
24// --------------------------------------------------
25// general constants
26// --------------------------------------------------
27/// Unsigned 16-bit integer sign bit
28const U16_SIGN_BIT: u16 = 0x8000;
29const U16_DATA_MSK: u16 = 0x7FFF;
30
31/// Parses a byte slice into an unsigned integer
32/// - Max precision is 32 bits (4294967296)
33///
34/// # Arguments
35///
36/// * `input` - A byte slice
37///
38/// # Returns
39///
40/// An option containing an unsigned integer
41///
42/// # Examples
43///
44/// ```
45/// use dted2::parsers::to_uint;
46/// assert_eq!(to_uint::<u32>(b"123"), Some(123 as u32));
47/// ```
48pub fn to_uint<U>(input: &[u8]) -> Option<U>
49where
50    U: PrimInt + Unsigned,
51{
52    U::from(input.iter().fold(0_u32, |acc, b| {
53        // assert!(*b >= 0x30 && *b <= 0x39); // is a digit
54        (acc * 10) + (*b - 0x30) as u32
55    }))
56}
57
58/// Nom parser that parses `count` number of bytes and returns an unsigned integer
59///
60/// # Arguments
61///
62/// * `count` - The number of bytes to parse
63///
64/// # Returns
65///
66/// A result containing an unsigned integer of length `num`, or an error if
67/// the input is invalid
68///
69/// # Examples
70///
71/// ```
72/// use dted2::parsers::uint_parser;
73/// assert_eq!(uint_parser::<u32>(3)(b"123"), Ok((&b""[..], 123 as u32)));
74/// ```
75pub fn uint_parser<U>(count: usize) -> impl Fn(&[u8]) -> IResult<&[u8], U>
76where
77    U: PrimInt + Unsigned,
78{
79    move |input| {
80        map_res(take(count), |bytes: &[u8]| {
81            to_uint::<U>(bytes).ok_or(nom::error::Error::new(input, nom::error::ErrorKind::Digit))
82        })(input)
83    }
84}
85
86/// Nom parser that parses `count` number of bytes and returns an unsigned integer
87/// If `count` is 0, a default value `default` is returned
88///
89/// # Arguments
90///
91/// * `count` - The number of bytes to parse
92/// * `default` - The default value to return if `count` is 0
93///
94/// # Returns
95///
96/// A [std::result::Result] containing an unsigned integer of length `count`, or an error if
97/// the input is invalid. If `count` is 0, `default` is returned
98///
99/// # Examples
100///
101/// ```
102/// use dted2::parsers::uint_parser_with_default;
103/// assert_eq!(uint_parser_with_default::<u32>(3, 0)(b"123"), Ok((&b""[..], 123 as u32)));
104/// assert_eq!(uint_parser_with_default::<u32>(0, 0)(b"123"), Ok((&b"123"[..], 0 as u32)));
105/// ```
106pub fn uint_parser_with_default<U>(count: usize, default: U) -> impl Fn(&[u8]) -> IResult<&[u8], U>
107where
108    U: PrimInt + Unsigned,
109{
110    move |input| match count {
111        0 => Ok((input, default)),
112        _ => uint_parser(count)(input),
113    }
114}
115
116/// Parses a byte slice into a [crate::primitives::Angle]
117///
118/// # Arguments
119///
120/// * `input` - A byte slice
121/// * `num_deg` - The number of bytes to parse for degrees
122/// * `num_min` - The number of bytes to parse for minutes
123/// * `num_sec` - The number of bytes to parse for seconds
124///
125/// # Returns
126///
127/// An [Option] containing a [crate::primitives::Angle]
128///
129/// # Examples
130///
131/// ```
132/// use dted2::parsers::to_angle;
133/// use dted2::primitives::Angle;
134/// assert_eq!(to_angle(b"12345", 3, 1, 1), Ok((&b""[..], Angle::new(123, 4, 5.0, false))));
135/// assert_eq!(to_angle(b"12345W", 3, 1, 1), Ok((&b""[..], Angle::new(123, 4, 5.0, true))));
136/// ```
137pub fn to_angle(
138    input: &[u8],
139    num_deg: usize,
140    num_min: usize,
141    num_sec: usize,
142) -> IResult<&[u8], Angle> {
143    let (input, (deg, min, sec, sign)) = tuple((
144        uint_parser_with_default(num_deg, 0u32),
145        uint_parser_with_default(num_min, 0u32),
146        uint_parser_with_default(num_sec, 0u32),
147        opt(alt((
148            map(tag("N"), |_| false),
149            map(tag("S"), |_| true),
150            map(tag("E"), |_| false),
151            map(tag("W"), |_| true),
152        ))),
153    ))(input)?;
154    Ok((
155        input,
156        Angle::new(deg as u16, min as u8, sec as f64, sign.unwrap_or(false)),
157    ))
158}
159
160/// Nom parser that parses `num_deg`, `num_min`, and `num_sec` number of bytes and returns an angle
161///
162/// # Arguments
163///
164/// * `num_deg` - The number of bytes to parse for degrees
165/// * `num_min` - The number of bytes to parse for minutes
166/// * `num_sec` - The number of bytes to parse for seconds
167///
168/// # Examples
169///
170/// ```
171/// use dted2::primitives::Angle;
172/// use dted2::parsers::angle_parser;
173/// assert_eq!(angle_parser(3, 1, 1)(b"12345"), Ok((&b""[..], Angle::new(123, 4, 5.0, false))));
174/// assert_eq!(angle_parser(3, 1, 1)(b"12345W"), Ok((&b""[..], Angle::new(123, 4, 5.0, true))));
175/// ```
176pub fn angle_parser(
177    num_deg: usize,
178    num_min: usize,
179    num_sec: usize,
180) -> impl Fn(&[u8]) -> IResult<&[u8], Angle> {
181    move |input| to_angle(input, num_deg, num_min, num_sec)
182}
183
184/// Parses a byte slice into an unsigned integer,
185/// if the value is not a valid NAN DTED value
186///
187/// # Arguments
188///
189/// * `input` - A byte slice
190///
191/// # Returns
192///
193/// A [Option] containing a unsigned integer. Is None
194/// if the value is a valid NAN value
195///
196/// # Examples
197///
198/// ```
199/// use dted2::parsers::to_nan;
200/// assert_eq!(to_nan::<u32>(b"NA$$", 4), Ok((&b""[..], None)));
201/// assert_eq!(to_nan::<u32>(b"12345", 4), Ok((&b"5"[..], Some(1234 as u32))));
202/// ```
203pub fn to_nan<U>(input: &[u8], count: usize) -> IResult<&[u8], Option<U>>
204where
205    U: PrimInt + Unsigned,
206{
207    match tag::<_, _, nom::error::Error<_>>(RecognitionSentinel::NA.value())(input) {
208        Ok((input, _)) => {
209            let (input, _) = take(count - 2)(input)?;
210            Ok((input, None))
211        }
212        Err(e) => match e {
213            nom::Err::Error(err_input) => {
214                uint_parser::<U>(count)(err_input.input).map(|(input, x)| (input, Some(x)))
215            }
216            _ => Err(e),
217        },
218    }
219}
220
221/// Nom parser for NAN (either Not a Number or Not Available) values in DTED
222/// If not a valid NAN value, then the value (unsigned integer)
223/// is returned as [Option::Some], otherwise [Option::None]
224///
225/// # Arguments
226///
227/// * `count` - The number of bytes to parse
228///
229/// # Returns
230///
231/// An [Option] containing an unsigned integer,
232/// otherwise, if a valid NAN, returns [Option::None]
233///
234/// # Examples
235///
236/// ```
237/// use dted2::parsers::nan_parser;
238/// assert_eq!(nan_parser::<u32>(4)(b"NA$$"), Ok((&b""[..], None)));
239/// assert_eq!(nan_parser::<u32>(4)(b"12345"), Ok((&b"5"[..], Some(1234 as u32))));
240/// ```
241pub fn nan_parser<U>(
242    count: usize,
243) -> impl Fn(&[u8]) -> Result<(&[u8], Option<U>), nom::Err<nom::error::Error<&[u8]>>>
244where
245    U: PrimInt + Unsigned,
246{
247    move |input| to_nan(input, count)
248}
249
250// // Helper function: Convert signed magnitude int to i16
251// fn to_i16(x: u16) -> i16 {
252//     if x & U16_SIGN_BIT == U16_SIGN_BIT {
253//         -((x & !U16_SIGN_BIT) as i16)
254//     } else {
255//         x as i16
256//     }
257// }
258/// Convert signed magnitude int to i16
259///
260/// # Arguments
261///
262/// * `x` - The signed magnitude int (2 bytes, formatted as u16)
263///
264/// # Returns
265///
266/// An i16, converted from the signed magnitude int
267///
268/// # Examples
269///
270/// ```
271/// use dted2::parsers::to_i16;
272/// assert_eq!(to_i16(0x0000), 0);
273/// assert_eq!(to_i16(0x0003), 3);
274/// assert_eq!(to_i16(0x8003), -3);
275/// assert_eq!(to_i16(0x7fff), 32767);
276/// assert_eq!(to_i16(0xFFFF), -32767);
277/// ```
278pub fn to_i16(x: u16) -> i16 {
279    let v = (x & U16_DATA_MSK) as i16; // mask out the sign bit and get the value
280    let s = ((x & U16_SIGN_BIT) >> 15) as i16; // extract sign bit and extend to i16 directly
281    (1 - (s << 1)) * v // branchless negation, return (1 - 2s) * v
282}
283
284/// Nom parser for signed magnitude values in DTED
285///
286/// # Arguments
287///
288/// * `input` - A byte slice
289///
290/// # Returns
291///
292/// An [i16] parsed from the byte slice, using signed magnitude
293/// convention
294///
295/// # Examples
296///
297/// ```
298/// use dted2::parsers::signed_mag_parser;
299/// assert_eq!(signed_mag_parser(&[0x00, 0x00]), Ok((&b""[..], 0)));
300/// assert_eq!(signed_mag_parser(&[0x00, 0x03]), Ok((&b""[..], 3)));
301/// assert_eq!(signed_mag_parser(&[0x80, 0x03]), Ok((&b""[..], -3)));
302/// assert_eq!(signed_mag_parser(&[0x7f, 0xff]), Ok((&b""[..], 32767)));
303/// assert_eq!(signed_mag_parser(&[0xff, 0xff]), Ok((&b""[..], -32767)));
304/// ```
305pub fn signed_mag_parser(input: &[u8]) -> IResult<&[u8], i16> {
306    map_res(take(2_usize), |bytes: &[u8]| {
307        Ok::<i16, nom::Err<nom::error::Error<&[u8]>>>(to_i16(u16::from_be_bytes([
308            bytes[0], bytes[1],
309        ])))
310    })(input)
311}
312
313/// Nom parser for a [RawDTEDHeader]
314///
315/// # Arguments
316///
317/// * `input` - A byte slice
318///
319/// # Returns
320///
321/// A [RawDTEDHeader] parsed from the byte slice
322///
323/// # Examples
324///
325/// ```
326/// use dted2::dted::RawDTEDHeader;
327/// use dted2::primitives::{ Angle, AxisElement };
328/// use dted2::parsers::dted_uhl_parser;
329/// use dted2::dted::RecognitionSentinel;
330///
331/// assert_eq!(dted_uhl_parser(b"UHL11234556E8901234W123456789012UUUXXXXXXXXXXXX123445670XXXXXXXXXXXXXXXXXXXXXXXX"), Ok((&b""[..], RawDTEDHeader {
332///     origin: AxisElement { lat: Angle::new(890, 12, 34.0, true), lon: Angle::new(123, 45, 56.0, false) },
333///     interval_secs_x_10: AxisElement { lat: 5678, lon: 1234 },
334///     accuracy: Some(9012),
335///     count: AxisElement { lat: 4567, lon: 1234 },
336/// })));
337/// ```
338pub fn dted_uhl_parser(input: &[u8]) -> IResult<&[u8], RawDTEDHeader> {
339    // --------------------------------------------------
340    // verify is UHL
341    // --------------------------------------------------
342    let (input, _) = tag(RecognitionSentinel::UHL.value())(input)?;
343    // --------------------------------------------------
344    // parse header
345    // --------------------------------------------------
346    let (
347        input,
348        (
349            lon_origin,
350            lat_origin,
351            lon_interval_s,
352            lat_interval_s,
353            accuracy,
354            _,
355            lon_count,
356            lat_count,
357            _,
358        ),
359    ) = tuple((
360        angle_parser(3, 2, 2),
361        angle_parser(3, 2, 2),
362        uint_parser(4),
363        uint_parser(4),
364        nan_parser(4),
365        take(15_usize),
366        uint_parser(4),
367        uint_parser(4),
368        take(25_usize),
369    ))(input)?;
370    // --------------------------------------------------
371    // return
372    // --------------------------------------------------
373    Ok((
374        input,
375        RawDTEDHeader {
376            origin: AxisElement::new(lat_origin, lon_origin),
377            interval_secs_x_10: AxisElement::new(lat_interval_s, lon_interval_s),
378            accuracy,
379            count: AxisElement::new(lat_count, lon_count),
380        },
381    ))
382}
383
384pub fn dted_file_parser(input: &[u8]) -> IResult<&[u8], RawDTEDFile> {
385    // --------------------------------------------------
386    // get headers and header records
387    // --------------------------------------------------
388    let (input, (header, _dsi_record, _acc_record)) = tuple((
389        dted_uhl_parser,
390        // TODO: parse DSI record
391        // TODO: parse ACC record
392        take(DT2_DSI_RECORD_LENGTH),
393        take(DT2_ACC_RECORD_LENGTH),
394    ))(input)?;
395    // --------------------------------------------------
396    // parse the actual data
397    // --------------------------------------------------
398    let (input, records) = count(
399        |input| parse_dted_record(input, header.count.lat as usize),
400        header.count.lon as usize,
401    )(input)?;
402    // --------------------------------------------------
403    // return
404    // --------------------------------------------------
405    Ok((
406        input,
407        RawDTEDFile {
408            header,
409            data: records,
410            dsi_record: None,
411            acc_record: None,
412        },
413    ))
414}
415
416// Parse a DTED record
417pub fn parse_dted_record(input: &[u8], line_len: usize) -> IResult<&[u8], RawDTEDRecord> {
418    let (input, (block_byte0, block_rest, lon_count, lat_count, elevations, _)) = tuple((
419        preceded(
420            tag(RecognitionSentinel::DATA.value()),
421            take(1_usize), // starting block byte size, will always be 0
422        ),
423        be_u16,
424        be_u16,
425        be_u16,
426        count(signed_mag_parser, line_len),
427        take(4_usize), // checksum
428    ))(input)?;
429    // --------------------------------------------------
430    // return
431    // --------------------------------------------------
432    Ok((
433        input,
434        RawDTEDRecord {
435            blk_count: block_byte0[0] as u32 * 0x10000 + block_rest as u32,
436            lon_count,
437            lat_count,
438            elevations,
439        },
440    ))
441}