FITSHeaders.ParserModule
FITSHeaders.Parser

A sub-module of the FITSHeaders package implementing methods for parsing FITS header cards.

FITSHeaders.Parser.ByteBufferType
FITSHeaders.Parser.ByteBuffer

is the union of types that can be considered as buffers of bytes and that can treated as vectors of bytes to parse FITS header cards using the following helper functions (assuming A isa ByteBuffer holds):

first_byte_index(A) # yields the index of the first byte in A
last_byte_index(A)  # yields the index of the last byte in A
byte_index_range(A) # yields the range of byte indices in A
get_byte(A,i)       # yields the i-th byte from A

See FITSHeaders.Parser.ByteString and FITSHeaders.Parser.ByteVector.

FITSHeaders.Parser.ByteStringType
FITSHeaders.Parser.ByteString

is the union of types of strings that can be considered as vectors of bytes to implement fast parsing methods (see FITSHeaders.Parser.ByteBuffer).

FITS header cards consist in character from the restricted set of ASCII characters from ' ' to '~' (hexadecimal codes 0x20 to 0x7E). Hence Julia strings (encoded in ASCII or in UTF8) can be treated as vectors of bytes.

FITSHeaders.Parser.FITS_SHORT_KEYWORD_SIZEConstant
FITS_SHORT_KEYWORD_SIZE

is the number of bytes in a short FITS keyword, that is all FITS keyword but the HIERARCH ones. If a FITS keyword is shorther than this, it is equivalent to pad it with ASCII spaces (hexadecimal code 0x20).

FITSHeaders.Parser.FitsKeyType
FitsKey(buf, off=0, i_last=last_byte_index(buf))

encodes the, at most, first 8 bytes (or ASCII characters) of buf, starting at offset off, in a 64-bit integer value which is exactly equal to the first 8 bytes of a FITS keyword as stored in a FITS header. Argument buf may be a string or a vector of bytes.

Optional argument i_last is the index of the last byte available in buf. If fewer than 8 bytes are available (that is, if off + 8 > i_last), the result is as if buf has been padded with ASCII spaces (hexadecimal code 0x20).

The only operation that makes sense with an instance of FitsKey is comparison for equality for fast searching of keywords in a FITS header.

The caller may use @inbounds macro if it is certain that bytes in the range off+1:i_last are in bounds for buf.

For the fastest, but unsafe, computations call:

FitsKey(Val(:full), buf, off)
FitsKey(Val(:pad), buf, off, i_last)

where first argument should be Val(:full) if there are at least 8 bytes available after off, and Val(:pad) otherwise. These variants do not perfom bounds checking, it is the caller's responsibility to insure that the arguments are consistent.

FITSHeaders.Parser.FitsKeyMethod
FitsKey()
zero(FitsKey)

yield a null FITS key, that is whose bytes are all 0. This can be asserted by calling issero on the returned key. Since any valid FITS key cannot contain null bytes, a null FITS key may be useful for searching keys.

FITSHeaders.Parser.PointerCapabilityType
FITSHeaders.Parser.PointerCapability(T) -> Union{PointerNone,PointerFull}

yields whether Base.unsafe_convert(Ptr{UInt8},obj) and Base.cconvert(Ptr{UInt8},obj) are fully implemented for an object obj of type T. This also means that the object is stored in memory for some contiguous range of addresses.

FITSHeaders.Parser.check_short_keywordMethod
FITSHeaders.check_short_keyword(str) -> str

returns the string str throwing an exception if str is not a short FITS keyword consisting in, at most, 8 ASCII characters from the restricted set of upper case letters (bytes 0x41 to 0x5A), decimal digits (hexadecimal codes 0x30 to 0x39), hyphen (hexadecimal code 0x2D), or underscore (hexadecimal code 0x5F).

FITSHeaders.Parser.full_nameMethod
FITSHeaders.Parser.full_name(pfx, name::AbstractString)

yields "HIERARCH "*name if pfx is true, name otherwise. The result is a String.

FITSHeaders.Parser.get_byteMethod
FITSHeaders.Parser.get_byte(T = UInt8, A, i)

yields the i-th byte of A which may be a vector of bytes or a string. Optional first argument T is to specify the data type of the returned value.

When parsing a FITS header or keyword, it is possible to specify the index n of the last available byte in A and call:

FITSHeaders.Parser.get_byte(T = UInt8, A, i, n)

which yields the i-th byte of A if i ≤ n and 0x20 (an ASCII space) otherwise.

This function propagates the @inbounds macro.

FITSHeaders.Parser.is_commentMethod
FITSHeaders.is_comment(A::Union{FitsCardType,FitsCard})

yields whether A indicates a, possibly non-standard, commentary FITS keyword.

FITSHeaders.is_comment(key::FitsKey)

yields whether key is Fits"COMMENT" or Fits"HISTORY" which corresponds to a standard commentary FITS keyword.

FITSHeaders.Parser.is_endMethod
FITSHeaders.is_end(A::Union{FitsKey,FitsCardType,FitsCard})

yields whether A indicates the END FITS keyword.

FITSHeaders.Parser.is_indexedMethod
FITSHeaders.Parser.is_indexed(b...)

yields whether trailing bytes b... of a FITS keyword indicate an indexed keyword.

FITSHeaders.Parser.is_naxisMethod
FITSHeaders.is_naxis(A::Union{FitsKey,FitsCard})

yields whether A is a FITS "NAXIS" or "NAXIS#" keyword or card with# denoting a decimal number.

FITSHeaders.Parser.keywordMethod
FITSHeaders.keyword(name) -> full_name

yields the full FITS keyword corresponding to name, throwing an exception if name is not a valid FITS keyword. The result is equal to either name or to "HIERARCH "*name.

Examples:

julia> FITSHeaders.keyword("GIZMO")
"GIZMO"

julia> FITSHeaders.keyword("HIERARCH GIZMO")
"HIERARCH GIZMO"

julia> FITSHeaders.keyword("GIZ MO")
"HIERARCH GIZ MO"

julia> FITSHeaders.keyword("VERYLONGNAME")
"HIERARCH VERYLONGNAME"

where the 1st one is a short FITS keyword (with less than 8 characters), the 3rd one is explictely a HIERARCH keyword, while the 3rd and 4th ones are automatically turned into HIERARCH keywords because the 3rd one contains a space and because the 4th one is longer than 8 characters.

See also FITSHeaders.check_keyword and FITSHeaders.Parser.full_name.

FITSHeaders.Parser.make_stringMethod
FITSHeaders.Parser.make_string(buf, rng) -> str::String

yields a string from the bytes of buf in the range of indices rng.

FITSHeaders.Parser.scan_cardFunction
FITSHeaders.Parser.scan_card(A, off=0) -> type, key, name_rng, val_rng, com_rng

parses a FITS header card A as it is written in a FITS file. A may be a string or a vector of bytes. Optional argument off is an offset in bytes where to start the parsing. At most, 80 bytes after off are considered in A which may thus belong to a larger piece of data (e.g., a FITS header). The result is a 5-tuple:

  • type::FitsCardType is the type of the card value.

  • key::FitsKey is the quick key corresponding to the short keyword of the card.

  • name_rng is the range of bytes containing the keyword name without trailing spaces.

  • val_rng is the range of bytes containing the unparsed value part, without leading and trailing spaces but with parenthesis or quote delimiters for a complex or a string card. This range is empty for a commentary card, the END card, or if the card has an undefined value.

  • com_rng is the range of bytes containing the comment part without non-significant spaces.

FITSHeaders.Parser.scan_comment_partMethod
FITSHeaders.Parser.scan_comment_part(buf, rng) -> com_rng

scans the range rng of bytes to find the comment part of a FITS card stored in buf. If rng is not empty, first(rng) shall be the index of the first byte where the comment separator may be found, that is right after the last byte of the value part, and last(rng) shall be the index of the last byte to scan. For speed, these are not checked. The result is the index range for the comment part.

This method honors the bound-checking state.

FITSHeaders.Parser.scan_keyword_partMethod
FITSHeaders.Parser.scan_keyword_part(A, rng) -> key, name_rng, i_next

parses a the keyword part of FITS header card stored in bytes rng of A. Returns key the keyword quick key, name_rng the byte index range for the keyword name (with leading "HIERARCH "and trailing spaces removed), andi_next` the index of the first byte where next token (value marker of comment) may start.

FITSHeaders.Parser.scan_short_keyword_partMethod
FITSHeaders.Parser.scan_short_keyword_part(A, rng) -> name_rng

scans the first bytes of A in the index range rng for a valid short FITS keyword and returns the index range to this keyword. A short FITS keyword consists in, at most, 8 ASCII characters from the restricted set of upper case letters (bytes 0x41 to 0x5A), decimal digits (hexadecimal codes 0x30 to 0x39), hyphen (hexadecimal code 0x2D), or underscore (hexadecimal code 0x5F). Trailing spaces are ignored.

The following relations hold:

first(name_rng) == first(rng)
length(name_rng) ≤ min(length(rng), 8)

In case scanning shall be pursued, the next token to scan starts at or after index:

i_next = first(name_rng) + 8
FITSHeaders.Parser.scan_value_comment_partsMethod
FITSHeaders.Parser.scan_value_comment_parts(buf, rng) -> type, val_rng, com_rng

scans the range rng of bytes to find the value and comment of a FITS card stored in buf. If rng is not empty, first(rng) shall be the index of the first byte where the value may be found, that is right after the value indicator "= ", and last(rng) shall be the index of the last byte to scan. For speed, these are not checked. The result is a tuple with type the type of the FITS card value, val_rng the index range for the unparsed value part, and com_rng the index range of the comment part without leading spaces.

FITSHeaders.Parser.trim_leading_spacesFunction
FITSHeaders.Parser.trim_leading_spaces(buf[, rng]) -> sub_rng

yields the range sub_rng of byte indices in buf (a string or a vector of bytes) without the leading spaces in buf. Optional argument rng is to specify the range of byte indices to consider in buf. If rng is not provided, all the bytes of buf are considered. If rng is provided, sub_rng is such that:

first(sub_rng) ≥ first(rng)
last(sub_rng) == last(rng)
FITSHeaders.Parser.trim_trailing_spacesFunction
FITSHeaders.Parser.trim_trailing_spaces(buf[, rng]) -> sub_rng

yields the range sub_rng of byte indices in buf (a string or a vector of bytes) without the trailing spaces in buf. Optional argument rng is to specify the range of byte indices to consider in buf. If rng is not provided, all the bytes of buf are considered. If rng is provided, sub_rng is such that:

first(sub_rng) == first(rng)
last(sub_rng) ≤ last(rng)
FITSHeaders.Parser.try_parse_keywordMethod
FITSHeaders.try_parse_keyword(str)

parses the FITS keyword given by string str. In case of parsing error, the result is the first illegal character encountered in str. Otherwise, the result is the 2-tuple (key,pfx) with key the quick key of the keyword and pfx a boolean indicating whether the "HIERARCH " prefix should be prepended to str to form the full keyword name. If the string has more than 8 characters or if any single space separator occurs in the string, a HIERARCH keyword is assumed even though the string does not start with "HIERARCH ". Leading and trailing spaces are not allowed.

The returned key is Fits"HIERARCH" in 4 cases:

  • The string starts with "HIERARCH " followed by a name, possibly split in several words and possibly longer than 8 characters, pfx is false.

  • The string does not starts with "HIERARCH " but consists in at least two space-separated words, pfx is true.

  • The string does not starts with "HIERARCH " but is longer than 8 characters, pfx is true.

  • The string is "HIERARCH", pfx is false.

FITSHeaders.Parser.@Fits_strMacro
@Fits_str

A macro to construct a 64-bit quick key equivalent to the FITS keyword given in argument and as it is stored in the header of a FITS file. The argument must be a short FITS keyword (e.g., not a HIERARCH one) specified as a literal string of, at most, 8 ASCII characters with no trailing spaces. For example Fits"SIMPLE" or Fits"NAXIS2".

The result is the same as that computed by FitsKey but since the quick key is given by a string macro, it is like a constant computed at compile time with no runtime penalty.

FITSHeaders.CardsModule
FITSHeaders.Cards

A sub-module of the FITSHeaders package implementing the methods and properties for FITS header cards.

FITSHeaders.Cards.FitsCardType
card = FitsCard(key => (val, com))

builds a FITS header card associating keyword key with value val and comment string com. The value val may be:

  • a boolean to yield a card of type FITS_LOGICAL;
  • an integer to yield a card of type FITS_INTEGER;
  • a real to yield a card of type FITS_FLOAT;
  • a complex to yield a card of type FITS_COMPLEX;
  • a string to yield a card of type FITS_STRING;
  • nothing to yield a card of type FITS_COMMENT;
  • undef or missing to yield a card of type FITS_UNDEFINED.

The comment may be omitted for a normal FITS card and the value may be omitted for a commentary FITS card:

card = FitsCard(key => val::Number)
card = FitsCard(key => str::AbstractString)

In the 1st case, the comment is assumed to be empty. In the 2nd case, the string str is assumed to be the card comment if key is "COMMENT" or "HISTORY" and the card value otherwise.

FITS cards have properties:

card.type     # type of card: FITS_LOGICAL, FITS_INTEGER, etc.
card.key      # quick key of card: Fits"BITPIX", Fits"HIERARCH", etc.
card.name     # name of card
card.value    # callable object representing the card value
card.comment  # comment of card
card.units    # units of card value
card.unitless # comment of card without the units part if any

As the values of FITS keywords have different types, card.value does not yield a Julia value but a callable object. Called without any argument, this object yields the actual card value:

card.value() -> val::Union{Bool,Int64,Float64,ComplexF64,String,Nothing,UndefInitializer}

but such a call is not type-stable as indicated by the type assertion with an Union{...} above. For a type-stable result, the card value can be converted to a given data type T:

card.value(T)
convert(T, card.value)

both yield the value of card converted to type T. For readability, T may be an abstract type: card.value(Integer) yields the same result as card.value(Int64), card.value(Real) or card.value(AbstractFloat) yield the same result as card.value(Float64), card.value(Complex) yields the same result as card.value(ComplexF64), and card.value(AbstractString) yields the same result as card.value(String).

To make things easier, a few properties are aliases that yield the card value converted to a specific type:

card.logical :: Bool       # alias for card.value(Bool)
card.integer :: Int64      # alias for card.value(Integer)
card.float   :: Float64    # alias for card.value(Real)
card.complex :: ComplexF64 # alias for card.value(Complex)
card.string  :: String     # alias for card.value(String)

Conversion is automatically attempted if the actual card value is of a different type, throwing an error if the conversion is not possible or inexact.

valtype(card) yields the type of the value of card. isassigned(card) yields whether card has a value (that is whether it is neither a commentary card nor a card with an undefined value).

FITSHeaders.Cards.FitsCardMethod
FitsCard(buf; offset=0)

yields a FitsCard object built by parsing the FITS header card stored in the string or vector of bytes buf. Keyword offset can be used to specify the number of bytes to skip at the beginning of buf, so that it is possible to extract a specific FITS header card, not just the first one. At most, the 80 first bytes after the offset are scanned to build the FitsCard object. The next FITS card to parse is then at offset + 80 and so on.

The considered card may be shorter than 80 bytes, the result being exactly the same as if the missing bytes were spaces. If there are no bytes left, a FitsCard object equivalent to the final END card of a FITS header is returned.

FITSHeaders.FitsCardTypeMethod
FitsCardType(card::FitsCard)
card.type

yield the type code of the FITS header card card, one of: FITS_LOGICAL, FITS_INTEGER, FITS_FLOAT, FITS_COMPLEX, FITS_STRING, FITS_COMMENT, or FITS_UNDEFINED.

FITSHeaders.FitsCardTypeMethod
FitsCardType(T)

yields the FITS header card type code corresponding to Julia type T, one of: FITS_LOGICAL, FITS_INTEGER, FITS_FLOAT, FITS_COMPLEX, FITS_STRING, FITS_COMMENT, or FITS_UNDEFINED.

Base.valtypeMethod
valtype(x::Union{FitsCard,FitsCardType}) -> T

yields the Julia type T corresponding to a given FITS card or card type.

FITSHeaders.Headers.FitsHeaderType
FitsHeader(args...) -> hdr

yields a FITS header object initialized with records args...

A FITS header object behaves as a vector of FitsCard elements with integer or keyword (string) indices. When indexed by keywords, a FITS header object is similar to a dictionary except that the order of records is preserved and that commentary and continuation records (with keywords "COMMENT", "HISTORY", "", or "CONTINUE") may appear more than once.

To update or append a record to the FITS header hdr, call one of:

hdr[key] = x
push!(hdr, key => x)

where x is the value and/or comment of the record. If the keyword key already exists in hdr, the record is updated, otherwise a new record is appended. Commentary and continuation records are however always appended. More generally, the push! method can be called as:

push!(hdr, rec)

where rec is a FitsCard object or anything, such as key => x, that can be converted into an instance of this type.

To modify any existing record including commentary and continuation ones, use the syntax:

hdr[i] = rec

where i is a linear (integer) index whule rec is a above.

Searching for the index i of an existing record in FITS header object hdr can be done by the usual methods:

findfirst(what, hdr)
findlast(what, hdr)
findnext(what, hdr, start)
findprev(what, hdr, start)

which all return a valid integer index if a record matching what is found and nothing otherwise. The matching pattern what can be a keyword (string), a FITS card (an instance of FitsCard whose name is used as a matching pattern), or a predicate function which takes a FITS card argument and returns whether it matches. The find methods just yield nothing for any unsupported kind of pattern.

FITSHeaders.Headers.FullNameType
FITSHeaders.FullName(str) -> obj

yields the full name of a FITS header record given the, possibly shortened, name str. The returned object has 2 properties: obj.name is the full name and obj.key is the quick key uniquely representing the 8 first characters of the full name.

The "HIERARCH " prefix being optional to match a FITS keyword, and the quick key being useful to accelerate comparisons. FullName is a guarantee to have the full name and the quick key built from a, possibly shortened, keyword name.

Base.collectMethod
collect(what, hdr::FitsHeader; order::Ordering = Forward)

yields a vector of the records of hdr matching what and sorted according to order (Base.Order.Forward or Base.Order.Reverse).

Base.eachmatchMethod
eachmatch(what, hdr::FitsHeader)

yields an iterator over the records of hdr matching what.

For example:

@inbounds for rec in eachmatch(what, hdr)
    ... # do something
end

is equivalent to:

i = findfirst(what, hdr)
@inbounds while i !== nothing
    rec = hdr[i]
    ... # do something
    i = findnext(what, hdr, i+1)
end

while:

@inbounds for rec in reverse(eachmatch(what, hdr))
    ... # do something
end

is equivalent to:

i = findlast(what, hdr)
@inbounds while i !== nothing
    rec = hdr[i]
    ... # do something
    i = findprev(what, hdr, i-1)
end
Base.findfirstMethod
findfirst(what, hdr::FitsHeader) -> i :: Union{Int,Nothing}

finds the first occurence of a record in FITS header hdr matching the pattern what.

Base.findlastMethod
findlast(what, hdr::FitsHeader) -> i :: Union{Int,Nothing}

find the last occurence of a record in FITS header hdr matching the pattern what.

Base.findnextFunction
findnext(what, hdr::FitsHeader, start) -> i :: Union{Int,Nothing}

find the next occurence of a record in FITS header hdr matching the pattern what at or after index start.

Base.findprevFunction
findprev(what, hdr::FitsHeader, start) -> i :: Union{Int,Nothing}

find the previous occurence of a record in FITS header hdr matching the pattern what at or before index start.

Base.push!Method
push!(hdr::FitsHeader, rec) -> hdr

appends a new record rec into the FITS header hdr or, if the keyword of the card must be unique and a record with the same name already exists in hdr, replaces the existing record.

This is strictly equivalent to:

hdr[key] = x

with key the name of the record, and x its associated value and/or comment.

Note that commentary and continuation records (with keywords "COMMENT", "HISTORY", "", or "CONTINUE") are always appended.