Skip to content

Commit 161db47

Browse files
Merge pull request #51 from observerly/feature/header/parseHeadersFromBlocks
feat: add parseHeadersFromBlocks to header module in @observerly/fits
2 parents 07db113 + 7b24680 commit 161db47

File tree

3 files changed

+286
-0
lines changed

3 files changed

+286
-0
lines changed
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
/*****************************************************************************************************************/
2+
3+
// @author Michael Roberts <michael@observerly.com>
4+
// @package @observerly/fits
5+
// @license Copyright © 2021-2023 observerly
6+
7+
/*****************************************************************************************************************/
8+
9+
import { describe, expect, it } from 'vitest'
10+
11+
import type { FITSBlock } from '../../types'
12+
import { parseHeadersFromBlocks } from '../parseHeadersFromBlocks'
13+
14+
/*****************************************************************************************************************/
15+
16+
describe('parseHeadersFromBlocks', () => {
17+
it('should correctly parse standard headers without CONTINUE keys', () => {
18+
const blocks: FITSBlock[] = [
19+
{
20+
buffer: '...',
21+
headers: [
22+
{
23+
key: 'SIMPLE',
24+
value: true,
25+
comment: 'file conforms to FITS standard'
26+
},
27+
{
28+
key: 'BITPIX',
29+
value: 16,
30+
comment: 'number of bits per data pixel'
31+
},
32+
{
33+
key: 'NAXIS',
34+
value: 2,
35+
comment: 'number of data axes'
36+
}
37+
],
38+
offsetStart: 0,
39+
offsetEnd: 2880
40+
}
41+
]
42+
43+
const headersMap = parseHeadersFromBlocks(blocks)
44+
45+
expect(headersMap.size).toBe(3)
46+
expect(headersMap.get('SIMPLE')).toEqual({
47+
key: 'SIMPLE',
48+
value: true,
49+
comment: 'file conforms to FITS standard'
50+
})
51+
expect(headersMap.get('BITPIX')).toEqual({
52+
key: 'BITPIX',
53+
value: 16,
54+
comment: 'number of bits per data pixel'
55+
})
56+
expect(headersMap.get('NAXIS')).toEqual({
57+
key: 'NAXIS',
58+
value: 2,
59+
comment: 'number of data axes'
60+
})
61+
})
62+
63+
it('should correctly handle CONTINUE keys by appending their values to the previous header', () => {
64+
const blocks: FITSBlock[] = [
65+
{
66+
buffer: '...',
67+
headers: [
68+
{
69+
key: 'COMMENT',
70+
value: 'This is a long comment that spans multiple',
71+
comment: 'initial part'
72+
},
73+
{
74+
key: 'CONTINUE',
75+
value: 'lines for better readability.',
76+
comment: 'continued part'
77+
},
78+
{
79+
key: 'END',
80+
value: true,
81+
comment: 'end of header'
82+
}
83+
],
84+
offsetStart: 0,
85+
offsetEnd: 2880
86+
}
87+
]
88+
89+
const headersMap = parseHeadersFromBlocks(blocks)
90+
91+
expect(headersMap.size).toBe(2)
92+
expect(headersMap.get('COMMENT')).toEqual({
93+
key: 'COMMENT',
94+
value: 'This is a long comment that spans multiple lines for better readability.',
95+
comment: 'initial part'
96+
})
97+
expect(headersMap.get('END')).toEqual({ key: 'END', value: true, comment: 'end of header' })
98+
})
99+
100+
it('should handle multiple CONTINUE keys sequentially', () => {
101+
const blocks: FITSBlock[] = [
102+
{
103+
buffer: '...',
104+
headers: [
105+
{
106+
key: 'COMMENT',
107+
value: 'First part of the comment',
108+
comment: 'initial'
109+
},
110+
{
111+
key: 'CONTINUE',
112+
value: 'second part',
113+
comment: 'continued'
114+
},
115+
{
116+
key: 'CONTINUE',
117+
value: 'third part',
118+
comment: 'continued'
119+
},
120+
{
121+
key: 'BITPIX',
122+
value: -32,
123+
comment: 'number of bits per data pixel'
124+
}
125+
],
126+
offsetStart: 0,
127+
offsetEnd: 2880
128+
}
129+
]
130+
131+
const headersMap = parseHeadersFromBlocks(blocks)
132+
133+
expect(headersMap.size).toBe(2)
134+
expect(headersMap.get('COMMENT')).toEqual({
135+
key: 'COMMENT',
136+
value: 'First part of the comment second part third part',
137+
comment: 'initial'
138+
})
139+
expect(headersMap.get('BITPIX')).toEqual({
140+
key: 'BITPIX',
141+
value: -32,
142+
comment: 'number of bits per data pixel'
143+
})
144+
})
145+
146+
it('should return an empty map when no headers are provided', () => {
147+
const blocks: FITSBlock[] = [
148+
{
149+
buffer: '...',
150+
headers: [],
151+
offsetStart: 0,
152+
offsetEnd: 2880
153+
}
154+
]
155+
156+
const headersMap = parseHeadersFromBlocks(blocks)
157+
158+
expect(headersMap.size).toBe(0)
159+
})
160+
161+
it('should handle multiple blocks with overlapping headers and CONTINUE keys', () => {
162+
const blocks: FITSBlock[] = [
163+
{
164+
buffer: '...',
165+
headers: [
166+
{
167+
key: 'COMMENT',
168+
value: 'Block1 comment part1',
169+
comment: 'initial'
170+
},
171+
{
172+
key: 'CONTINUE',
173+
value: 'part2',
174+
comment: 'continued'
175+
}
176+
],
177+
offsetStart: 0,
178+
offsetEnd: 2880
179+
},
180+
{
181+
buffer: '...',
182+
headers: [
183+
{
184+
key: 'COMMENT',
185+
value: 'Block2 comment part1',
186+
comment: 'initial'
187+
},
188+
{
189+
key: 'CONTINUE',
190+
value: 'part2',
191+
comment: 'continued'
192+
}
193+
],
194+
offsetStart: 2880,
195+
offsetEnd: 5760
196+
}
197+
]
198+
199+
const headersMap = parseHeadersFromBlocks(blocks)
200+
201+
expect(headersMap.size).toBe(1)
202+
expect(headersMap.get('COMMENT')).toEqual({
203+
key: 'COMMENT',
204+
value: 'Block2 comment part1 part2',
205+
comment: 'initial'
206+
})
207+
})
208+
209+
it('should ignore CONTINUE keys if there is no previous header', () => {
210+
const blocks: FITSBlock[] = [
211+
{
212+
buffer: '...',
213+
headers: [
214+
{
215+
key: 'CONTINUE',
216+
value: 'orphaned continue',
217+
comment: 'no previous header'
218+
},
219+
{
220+
key: 'BITPIX',
221+
value: 8,
222+
comment: 'number of bits per data pixel'
223+
}
224+
],
225+
offsetStart: 0,
226+
offsetEnd: 2880
227+
}
228+
]
229+
230+
const headersMap = parseHeadersFromBlocks(blocks)
231+
232+
expect(headersMap.size).toBe(1)
233+
expect(headersMap.get('BITPIX')).toEqual({
234+
key: 'BITPIX',
235+
value: 8,
236+
comment: 'number of bits per data pixel'
237+
})
238+
// The 'CONTINUE' key should be ignored as there is no previous header
239+
})
240+
})
241+
242+
/*****************************************************************************************************************/

src/header/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
export { getFITSHeaders } from './getFITSHeaders'
1010
export { parseFITSHeaderBlock } from './parseFITSHeaderBlock'
1111
export { parseFITSHeaderRow } from './parseFITSHeaderRow'
12+
export { parseHeadersFromBlocks } from './parseHeadersFromBlocks'
1213
export { readFITSHeaderFromBlocks } from './readFITSHeaderFromBlocks'
1314

1415
/*****************************************************************************************************************/
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*****************************************************************************************************************/
2+
3+
// @author Michael Roberts <michael@observerly.com>
4+
// @package @observerly/fits
5+
// @license Copyright © 2021-2023 observerly
6+
7+
/*****************************************************************************************************************/
8+
9+
import type { FITSBlock, FITSHeader } from '../types'
10+
11+
/*****************************************************************************************************************/
12+
13+
/**
14+
*
15+
* parseHeadersFromBlocks
16+
*
17+
* @param blocks - Array of FITS blocks to parse headers from
18+
* @returns Map of headers parsed from the blocks
19+
*/
20+
export const parseHeadersFromBlocks = (blocks: FITSBlock[]): Map<string, FITSHeader> => {
21+
const headers = new Map<string, FITSHeader>()
22+
23+
// Flatten all headers from blocks into a single array
24+
const headerLines = blocks.flatMap(block => block.headers)
25+
26+
// Keep track of the previous header for handling 'CONTINUE' keys
27+
let previousHeader: FITSHeader | null = null
28+
29+
for (const header of headerLines) {
30+
if (header.key !== 'CONTINUE') {
31+
// Add the new header to the map and update the previous header reference
32+
headers.set(header.key, header)
33+
previousHeader = header
34+
} else if (previousHeader) {
35+
// Append 'CONTINUE' values to the previous header's value
36+
previousHeader.value += ` ${header.value}`
37+
}
38+
}
39+
40+
return headers
41+
}
42+
43+
/*****************************************************************************************************************/

0 commit comments

Comments
 (0)