Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add parseHeadersFromBlocks to header module in @observerly/fits #51

Merged
merged 1 commit into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}

/*****************************************************************************************************************/
Loading