1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use crate::result::Result;
use crate::volume::util::get_datetime;
use chrono::{DateTime, Duration, Utc};
use std::fmt;
use std::fmt::{Debug, Formatter};
use std::io::Read;

/// Header for an Archive II volume file containing metadata about the radar data. This header is
/// located at the beginning of the file.
#[repr(C)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
pub struct Header {
    /// The tape's filename which indicates the version of the data. Name is in the format
    /// `AR2V0 0xx.` where `xx` indicates the version of the data.
    ///
    /// Versions:
    ///   02 = Super Resolution disabled at the RDA (pre RDA Build 12.0)
    ///   03 = Super Resolution (pre RDA Build 12.0)
    ///   04 = Recombined Super Resolution
    ///   05 = Super Resolution disabled at the RDA (RDA Build 12.0 and later)
    ///   06 = Super Resolution (RDA Build 12.0 and later)
    ///   07 = Recombined Super Resolution (RDA Build 12.0 and later)
    /// NOTE: Dual-pol data introduced in RDA Build 12.0
    tape_filename: [u8; 9],

    /// Sequential number assigned to each volume of radar data in the queue, rolling over to 001
    /// after 999.
    extension_number: [u8; 3],

    /// This volume's date represented as a count of days since 1 January 1970 00:00 GMT. It is
    /// also referred-to as a "modified Julian date" where it is the Julian date - 2440586.5.
    date: u32,

    /// Milliseconds past midnight, GMT.
    time: u32,

    /// The ICAO identifier of the radar site.
    icao_of_radar: [u8; 4],
}

impl Header {
    /// Deserializes an Archive II header from the provided reader.
    #[cfg(all(feature = "serde", feature = "bincode"))]
    pub fn deserialize<R: Read>(reader: &mut R) -> Result<Self> {
        use bincode::{DefaultOptions, Options};
        Ok(DefaultOptions::new()
            .with_fixint_encoding()
            .with_big_endian()
            .deserialize_from(reader.by_ref())?)
    }

    /// The tape's filename which indicates the version of the data. Name is in the format
    /// `AR2V0 0xx.` where `xx` indicates the version of the data.
    ///
    /// Versions:
    ///   02 = Super Resolution disabled at the RDA (pre RDA Build 12.0)
    ///   03 = Super Resolution (pre RDA Build 12.0)
    ///   04 = Recombined Super Resolution
    ///   05 = Super Resolution disabled at the RDA (RDA Build 12.0 and later)
    ///   06 = Super Resolution (RDA Build 12.0 and later)
    ///   07 = Recombined Super Resolution (RDA Build 12.0 and later)
    /// NOTE: Dual-pol data introduced in RDA Build 12.0
    pub fn tape_filename(&self) -> Option<String> {
        String::from_utf8(self.tape_filename.to_vec()).ok()
    }

    /// Sequential number assigned to each volume of radar data in the queue, rolling over to 001
    /// after 999.
    pub fn extension_number(&self) -> Option<String> {
        String::from_utf8(self.extension_number.to_vec()).ok()
    }

    /// Returns the date and time of the volume.
    pub fn date_time(&self) -> Option<DateTime<Utc>> {
        get_datetime(self.date as u16, Duration::milliseconds(self.time as i64))
    }

    /// The ICAO identifier of the radar site.
    pub fn icao_of_radar(&self) -> Option<String> {
        String::from_utf8(self.icao_of_radar.to_vec()).ok()
    }
}

impl Debug for Header {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.debug_struct("Header")
            .field("tape_filename", &self.tape_filename())
            .field("extension_number", &self.extension_number())
            .field("date_time", &self.date_time())
            .field("icao_of_radar", &self.icao_of_radar())
            .finish()
    }
}