Skip to content

Commit

Permalink
Merge pull request #51 from observerly/feature/header/parseHeadersFro…
Browse files Browse the repository at this point in the history
…mBlocks

feat: add parseHeadersFromBlocks to header module in @observerly/fits
  • Loading branch information
michealroberts authored Jan 8, 2025
2 parents 07db113 + 7b24680 commit 161db47
Show file tree
Hide file tree
Showing 3 changed files with 286 additions and 0 deletions.
242 changes: 242 additions & 0 deletions src/header/__tests__/parseHeadersFromBlocks.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
/*****************************************************************************************************************/

// @author Michael Roberts <[email protected]>
// @package @observerly/fits
// @license Copyright © 2021-2023 observerly

/*****************************************************************************************************************/

import { describe, expect, it } from 'vitest'

import type { FITSBlock } from '../../types'
import { parseHeadersFromBlocks } from '../parseHeadersFromBlocks'

/*****************************************************************************************************************/

describe('parseHeadersFromBlocks', () => {
it('should correctly parse standard headers without CONTINUE keys', () => {
const blocks: FITSBlock[] = [
{
buffer: '...',
headers: [
{
key: 'SIMPLE',
value: true,
comment: 'file conforms to FITS standard'
},
{
key: 'BITPIX',
value: 16,
comment: 'number of bits per data pixel'
},
{
key: 'NAXIS',
value: 2,
comment: 'number of data axes'
}
],
offsetStart: 0,
offsetEnd: 2880
}
]

const headersMap = parseHeadersFromBlocks(blocks)

expect(headersMap.size).toBe(3)
expect(headersMap.get('SIMPLE')).toEqual({
key: 'SIMPLE',
value: true,
comment: 'file conforms to FITS standard'
})
expect(headersMap.get('BITPIX')).toEqual({
key: 'BITPIX',
value: 16,
comment: 'number of bits per data pixel'
})
expect(headersMap.get('NAXIS')).toEqual({
key: 'NAXIS',
value: 2,
comment: 'number of data axes'
})
})

it('should correctly handle CONTINUE keys by appending their values to the previous header', () => {
const blocks: FITSBlock[] = [
{
buffer: '...',
headers: [
{
key: 'COMMENT',
value: 'This is a long comment that spans multiple',
comment: 'initial part'
},
{
key: 'CONTINUE',
value: 'lines for better readability.',
comment: 'continued part'
},
{
key: 'END',
value: true,
comment: 'end of header'
}
],
offsetStart: 0,
offsetEnd: 2880
}
]

const headersMap = parseHeadersFromBlocks(blocks)

expect(headersMap.size).toBe(2)
expect(headersMap.get('COMMENT')).toEqual({
key: 'COMMENT',
value: 'This is a long comment that spans multiple lines for better readability.',
comment: 'initial part'
})
expect(headersMap.get('END')).toEqual({ key: 'END', value: true, comment: 'end of header' })
})

it('should handle multiple CONTINUE keys sequentially', () => {
const blocks: FITSBlock[] = [
{
buffer: '...',
headers: [
{
key: 'COMMENT',
value: 'First part of the comment',
comment: 'initial'
},
{
key: 'CONTINUE',
value: 'second part',
comment: 'continued'
},
{
key: 'CONTINUE',
value: 'third part',
comment: 'continued'
},
{
key: 'BITPIX',
value: -32,
comment: 'number of bits per data pixel'
}
],
offsetStart: 0,
offsetEnd: 2880
}
]

const headersMap = parseHeadersFromBlocks(blocks)

expect(headersMap.size).toBe(2)
expect(headersMap.get('COMMENT')).toEqual({
key: 'COMMENT',
value: 'First part of the comment second part third part',
comment: 'initial'
})
expect(headersMap.get('BITPIX')).toEqual({
key: 'BITPIX',
value: -32,
comment: 'number of bits per data pixel'
})
})

it('should return an empty map when no headers are provided', () => {
const blocks: FITSBlock[] = [
{
buffer: '...',
headers: [],
offsetStart: 0,
offsetEnd: 2880
}
]

const headersMap = parseHeadersFromBlocks(blocks)

expect(headersMap.size).toBe(0)
})

it('should handle multiple blocks with overlapping headers and CONTINUE keys', () => {
const blocks: FITSBlock[] = [
{
buffer: '...',
headers: [
{
key: 'COMMENT',
value: 'Block1 comment part1',
comment: 'initial'
},
{
key: 'CONTINUE',
value: 'part2',
comment: 'continued'
}
],
offsetStart: 0,
offsetEnd: 2880
},
{
buffer: '...',
headers: [
{
key: 'COMMENT',
value: 'Block2 comment part1',
comment: 'initial'
},
{
key: 'CONTINUE',
value: 'part2',
comment: 'continued'
}
],
offsetStart: 2880,
offsetEnd: 5760
}
]

const headersMap = parseHeadersFromBlocks(blocks)

expect(headersMap.size).toBe(1)
expect(headersMap.get('COMMENT')).toEqual({
key: 'COMMENT',
value: 'Block2 comment part1 part2',
comment: 'initial'
})
})

it('should ignore CONTINUE keys if there is no previous header', () => {
const blocks: FITSBlock[] = [
{
buffer: '...',
headers: [
{
key: 'CONTINUE',
value: 'orphaned continue',
comment: 'no previous header'
},
{
key: 'BITPIX',
value: 8,
comment: 'number of bits per data pixel'
}
],
offsetStart: 0,
offsetEnd: 2880
}
]

const headersMap = parseHeadersFromBlocks(blocks)

expect(headersMap.size).toBe(1)
expect(headersMap.get('BITPIX')).toEqual({
key: 'BITPIX',
value: 8,
comment: 'number of bits per data pixel'
})
// The 'CONTINUE' key should be ignored as there is no previous header
})
})

/*****************************************************************************************************************/
1 change: 1 addition & 0 deletions src/header/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
export { getFITSHeaders } from './getFITSHeaders'
export { parseFITSHeaderBlock } from './parseFITSHeaderBlock'
export { parseFITSHeaderRow } from './parseFITSHeaderRow'
export { parseHeadersFromBlocks } from './parseHeadersFromBlocks'
export { readFITSHeaderFromBlocks } from './readFITSHeaderFromBlocks'

/*****************************************************************************************************************/
43 changes: 43 additions & 0 deletions src/header/parseHeadersFromBlocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*****************************************************************************************************************/

// @author Michael Roberts <[email protected]>
// @package @observerly/fits
// @license Copyright © 2021-2023 observerly

/*****************************************************************************************************************/

import type { FITSBlock, FITSHeader } from '../types'

/*****************************************************************************************************************/

/**
*
* parseHeadersFromBlocks
*
* @param blocks - Array of FITS blocks to parse headers from
* @returns Map of headers parsed from the blocks
*/
export const parseHeadersFromBlocks = (blocks: FITSBlock[]): Map<string, FITSHeader> => {
const headers = new Map<string, FITSHeader>()

// Flatten all headers from blocks into a single array
const headerLines = blocks.flatMap(block => block.headers)

// Keep track of the previous header for handling 'CONTINUE' keys
let previousHeader: FITSHeader | null = null

for (const header of headerLines) {
if (header.key !== 'CONTINUE') {
// Add the new header to the map and update the previous header reference
headers.set(header.key, header)
previousHeader = header
} else if (previousHeader) {
// Append 'CONTINUE' values to the previous header's value
previousHeader.value += ` ${header.value}`
}
}

return headers
}

/*****************************************************************************************************************/

0 comments on commit 161db47

Please sign in to comment.