astro_rs/fits/
hdu_types.rs

1//! Construct standard HDU types.
2
3use super::*;
4
5/// The header keyword indicating the name of a table column.
6pub const TTYPE_KEYWORD: [u8; 8] = *b"TTYPE   ";
7/// The header keyword indicating the size in bytes of a table column.
8pub const TFORM_KEYWORD: [u8; 8] = *b"TFORM   ";
9
10pub(crate) const DEFAULT_BITPIX_BYTES: [u8; 80] =
11    *b"BITPIX  =                    8                                                  ";
12pub(crate) const DEFAULT_NAXIS_BYTES: [u8; 80] =
13    *b"NAXIS   =                    0                                                  ";
14pub(crate) const DEFAULT_PCOUNT_BYTES: [u8; 80] =
15    *b"PCOUNT  =                    0                                                  ";
16pub(crate) const DEFAULT_GCOUNT_BYTES: [u8; 80] =
17    *b"GCOUNT  =                    1                                                  ";
18pub(crate) const DEFAULT_TFIELDS_BYTES: [u8; 80] =
19    *b"TFIELDS =                    0                                                  ";
20pub(crate) const DEFAULT_END_BYTES: [u8; 80] =
21    *b"END                                                                             ";
22
23/// Functions related to a Primary type HDU.
24pub mod primary_hdu {
25    use super::*;
26
27    /// Constructs an HDU pre-populated with the required cards to be a Primary HDU.
28    pub fn default() -> Hdu {
29        let simple_card = FitsHeaderCard::from(
30            *b"SIMPLE  =                    T                                                  ",
31        );
32        let bitpix_card = FitsHeaderCard::from(DEFAULT_BITPIX_BYTES);
33        let naxis_card = FitsHeaderCard::from(DEFAULT_NAXIS_BYTES);
34        let end_card = FitsHeaderCard::from(DEFAULT_END_BYTES);
35        let header = FitsHeader {
36            cards: vec![simple_card, bitpix_card, naxis_card, end_card],
37        };
38
39        Hdu {
40            header,
41            ..Default::default()
42        }
43    }
44}
45
46/// Functions related to an ASCII Table type HDU.
47pub mod ascii_table_hdu {
48    use super::*;
49
50    /// Constructs an HDU pre-populated with the required cards to be an ASCII Table HDU.
51    pub fn default() -> Hdu {
52        let xtension_card = FitsHeaderCard::from(
53            *b"XTENSION= 'TABLE   '                                                            ",
54        );
55        let bitpix_card = FitsHeaderCard::from(DEFAULT_BITPIX_BYTES);
56        let naxis_card = FitsHeaderCard::from(
57            *b"NAXIS   =                    2                                                  ",
58        );
59        let naxis1_card = FitsHeaderCard::from(
60            *b"NAXIS1  =                    0                                                  ",
61        );
62        let naxis2_card = FitsHeaderCard::from(
63            *b"NAXIS2  =                    0                                                  ",
64        );
65        let pcount_card = FitsHeaderCard::from(DEFAULT_PCOUNT_BYTES);
66        let gcount_card = FitsHeaderCard::from(DEFAULT_GCOUNT_BYTES);
67        let tfields_card = FitsHeaderCard::from(DEFAULT_TFIELDS_BYTES);
68        let end_card = FitsHeaderCard::from(DEFAULT_END_BYTES);
69        let header = FitsHeader {
70            cards: vec![
71                xtension_card,
72                bitpix_card,
73                naxis_card,
74                naxis1_card,
75                naxis2_card,
76                pcount_card,
77                gcount_card,
78                tfields_card,
79                end_card,
80            ],
81        };
82
83        Hdu {
84            header,
85            ..Default::default()
86        }
87    }
88}
89
90/// Functions related to an Image type HDU.
91pub mod image_hdu {
92    use super::*;
93
94    /// Constructs an HDU pre-populated with the required cards to be an Image HDU.
95    pub fn default() -> Hdu {
96        let xtension_card = FitsHeaderCard::from(
97            *b"XTENSION= 'IMAGE   '                                                            ",
98        );
99        let bitpix_card = FitsHeaderCard::from(DEFAULT_BITPIX_BYTES);
100        let naxis_card = FitsHeaderCard::from(DEFAULT_NAXIS_BYTES);
101        let pcount_card = FitsHeaderCard::from(DEFAULT_PCOUNT_BYTES);
102        let gcount_card = FitsHeaderCard::from(DEFAULT_GCOUNT_BYTES);
103        let end_card = FitsHeaderCard::from(DEFAULT_END_BYTES);
104        let header = FitsHeader {
105            cards: vec![
106                xtension_card,
107                bitpix_card,
108                naxis_card,
109                pcount_card,
110                gcount_card,
111                end_card,
112            ],
113        };
114
115        Hdu {
116            header,
117            ..Default::default()
118        }
119    }
120}
121
122/// Functions related to a Binary Table type HDU.
123pub mod binary_table_hdu {
124    use super::*;
125    use crate::fits::header_value::TForm;
126
127    /// Constructs an HDU pre-populated with the required cards to be a Binary Table HDU.
128    pub fn default() -> Hdu {
129        let xtension_card = FitsHeaderCard::from(
130            *b"XTENSION= 'BINTABLE'                                                            ",
131        );
132        let bitpix_card = FitsHeaderCard::from(DEFAULT_BITPIX_BYTES);
133        let naxis_card = FitsHeaderCard::from(
134            *b"NAXIS   =                    2                                                  ",
135        );
136        let naxis1_card = FitsHeaderCard::from(
137            *b"NAXIS1  =                    0                                                  ",
138        );
139        let naxis2_card = FitsHeaderCard::from(
140            *b"NAXIS2  =                    0                                                  ",
141        );
142        let pcount_card = FitsHeaderCard::from(DEFAULT_PCOUNT_BYTES);
143        let gcount_card = FitsHeaderCard::from(DEFAULT_GCOUNT_BYTES);
144        let tfields_card = FitsHeaderCard::from(DEFAULT_TFIELDS_BYTES);
145        let end_card = FitsHeaderCard::from(DEFAULT_END_BYTES);
146        let header = FitsHeader {
147            cards: vec![
148                xtension_card,
149                bitpix_card,
150                naxis_card,
151                naxis1_card,
152                naxis2_card,
153                pcount_card,
154                gcount_card,
155                tfields_card,
156                end_card,
157            ],
158        };
159
160        Hdu {
161            header,
162            ..Default::default()
163        }
164    }
165
166    /// Obtains the data in the column of the given name, or None if a column with the given name cannot be found.
167    pub fn column_by_name<T>(hdu: &mut Hdu, name: &str) -> Option<Vec<T>> {
168        let mut n = 1;
169        let mut column_start = 0;
170        let mut tform = None;
171        let mut naxis_keyword = NAXIS_KEYWORD;
172        naxis_keyword[5] = b'2';
173        let num_rows = *hdu
174            .header
175            .get_card(naxis_keyword)
176            .and_then(|card| card.get_value::<u32>().ok())
177            .unwrap_or_default() as usize;
178        naxis_keyword[5] = b'1';
179        let row_len = *hdu
180            .header
181            .get_card(naxis_keyword)
182            .and_then(|card| card.get_value::<u32>().ok())
183            .unwrap_or_default() as usize;
184        let mut tform_keyword = FitsHeaderKeyword::from(TFORM_KEYWORD);
185        let mut ttype_keyword = FitsHeaderKeyword::from(TTYPE_KEYWORD);
186        while n as usize <= num_rows {
187            tform_keyword.append_number(n);
188            if let Some(card) = hdu.header.get_card(tform_keyword) {
189                if let Ok(tform_value) = card.get_value::<TForm>() {
190                    ttype_keyword.append_number(n);
191                    if let Some(value) = hdu
192                        .header
193                        .get_card(ttype_keyword)
194                        .and_then(|card| card.get_value::<String>().ok())
195                    {
196                        if value.eq_ignore_ascii_case(name) {
197                            tform = Some(tform_value);
198                            break;
199                        }
200                    }
201
202                    column_start += tform_value.value();
203                }
204                n += 1;
205            } else {
206                break;
207            }
208        }
209        if let Some(tform) = tform {
210            return Some(*tform.create_column(hdu.data_raw(), column_start, row_len, num_rows));
211        }
212        None
213    }
214
215    /// Obtains the data in the column of the given index, or None if a column with the given index cannot be found.
216    /// Note that column indeces start at 1.
217    pub fn column_by_index<T>(hdu: &mut Hdu, index: u16) -> Option<Vec<T>> {
218        let mut n = 1;
219        let mut column_start = 0;
220        let mut tform = None;
221        let mut naxis_keyword = NAXIS_KEYWORD;
222        naxis_keyword[5] = b'2';
223        let num_rows = *hdu
224            .header
225            .get_card(naxis_keyword)
226            .and_then(|card| card.get_value::<u32>().ok())
227            .unwrap_or_default() as usize;
228        if index as usize > num_rows {
229            return None;
230        }
231        naxis_keyword[5] = b'1';
232        let row_len = *hdu
233            .header
234            .get_card(naxis_keyword)
235            .and_then(|card| card.get_value::<u32>().ok())
236            .unwrap_or_default() as usize;
237        let mut tform_keyword = FitsHeaderKeyword::from(TFORM_KEYWORD);
238        while n <= index {
239            tform_keyword.append_number(n);
240            if let Some(card) = hdu.header.get_card(tform_keyword) {
241                if let Ok(value) = card.get_value::<TForm>() {
242                    if n == index {
243                        tform = Some(value);
244                    } else {
245                        column_start += value.value();
246                    }
247                }
248                n += 1;
249            } else {
250                break;
251            }
252        }
253        if let Some(tform) = tform {
254            return Some(*tform.create_column(hdu.data_raw(), column_start, row_len, num_rows));
255        }
256        None
257    }
258}