Hi Robyx,
Try the following script:
#include <pjsr/DataType.jsh>
function LoadFITSKeywords( fitsFilePath )
{
// A keyword comments starts after a single slash character.
function searchCommentSeparator( b )
{
let inString = false;
for ( let i = 10; i < 80; ++i )
switch ( b.at( i ) )
{
case 39: // single quote
inString ^= true;
break;
case 47: // slash
if ( !inString )
return i;
break;
}
return -1;
}
// In HIERARCH keywords the equal sign is after the real keyword name.
function searchHierarchValueIndicator( b )
{
for ( let i = 9; i < 80; ++i )
switch ( b.at( i ) )
{
case 39: // single quote, = cannot be later
return -1;
case 47: // slash, cannot be later
return -1;
case 61: // =, may be value indicator after all
return i;
}
return -1;
}
var file = File.openFileForReading( fitsFilePath );
let keywords = [];
try
{
for ( ;; )
{
let rawData = file.read( DataType_ByteArray, 80 );
let name = rawData.toString( 0, 8 );
if ( name.trim().toUpperCase() == "END" ) // end of HDU keyword list?
break;
if ( file.isEOF )
throw new Error( "Unexpected end of file reading FITS keywords: " + fitsFilePath );
let value = "";
let comment = "";
let hasValue = false;
// Is a value separator (an equal sign at byte 8) present?
if ( rawData.at( 8 ) == 61 )
{
// This is a standard FITS keyword.
hasValue = true;
// Find comment separator slash
let cmtPos = searchCommentSeparator( rawData );
if ( cmtPos < 0 ) // no comment separator
cmtPos = 80;
// Value substring
value = rawData.toString( 9, cmtPos-9 );
// Comment substring
if ( cmtPos < 80 )
comment = rawData.toString( cmtPos+1, 80-cmtPos-1 );
}
else if ( name == "HIERARCH" )
{
// This keyword follows the nonstandard HIERARCH convention.
let viPos = searchHierarchValueIndicator( rawData );
if ( viPos > 0 )
{
hasValue = true;
name = rawData.toString( 9, viPos-10 );
// Find comment separator slash
let cmtPos = searchCommentSeparator( rawData );
if ( cmtPos < 0 ) // no comment
cmtPos = 80;
// Value substring
value = rawData.toString( viPos+1, cmtPos-viPos-1 );
// Comment substring
if ( cmtPos < 80 )
comment = rawData.toString( cmtPos+1, 80-cmtPos-1 );
}
}
// If there is no value in this keyword (e.g., HISTORY)
if ( !hasValue )
comment = rawData.toString( 8, 80-8 ).trim();
// Perform a naive sanity check: a valid FITS file must begin with a SIMPLE=T keyword.
if ( keywords.length == 0 )
if ( name != "SIMPLE " && value.trim() != 'T' )
throw new Error( "File does not seem to be a valid FITS file (SIMPLE T not found): " + fitsFilePath );
// Add new keyword.
let fitsKeyWord = new FITSKeyword( name, value, comment );
fitsKeyWord.trim();
keywords.push( fitsKeyWord );
}
}
catch ( x )
{
file.close();
throw x;
}
return keywords;
}
function main()
{
let T = new ElapsedTime;
let keywords = LoadFITSKeywords( "/foo/bar.fit" );
let time = T.text;
let maxValueLength = 0;
for ( let i = 0; i < keywords.length; ++i )
if ( keywords[i].value.length > maxValueLength )
maxValueLength = keywords[i].value.length;
for ( let i = 0; i < keywords.length; ++i )
console.writeln( format( "%8s | %s%s | %s",
keywords[i].name,
keywords[i].value,
' '.repeat( maxValueLength - keywords[i].value.length ),
keywords[i].comment ) );
console.writeln();
console.writeln( keywords.length, " keywords." );
console.writeln( time );
}
main();
Replace "/foo/bar.fit" with the appropriate path to a FITS file in your filesystem. On the machine I am writing this, the script reports about 1 ms after working on a FITS file with 100 keywords. Let me know if you find this script useful.
A similar script could be written to work on XISF files. In this case the script would have to decode the relevant part of the XISF header, which is an XML document, but obviously without parsing the XML code if we want a really fast implementation. This should be slightly faster than the FITS case, since there is no uncertainty when finding XML tag attribute names and values.