FITSHeaders.FITSHeaders
— ModuleFITSHeaders
A package implementing methods to store and parse FITS header cards.
FITSHeaders.Parser
— ModuleFITSHeaders.Parser
A sub-module of the FITSHeaders
package implementing methods for parsing FITS header cards.
FITSHeaders.Parser.ByteBuffer
— TypeFITSHeaders.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.ByteString
— TypeFITSHeaders.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_BLOCK_SIZE
— ConstantFITS_BLOCK_SIZE
is the number of bytes per FITS header/data block.
FITSHeaders.Parser.FITS_CARD_SIZE
— ConstantFITS_CARD_SIZE
is the number of bytes per FITS header card.
FITSHeaders.Parser.FITS_SHORT_KEYWORD_SIZE
— ConstantFITS_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.ByteVector
— TypeFITSHeaders.Parser.ByteVector
is an alias for types that can be considered as vectors of bytes to implement fast parsing methods (see FITSHeaders.Parser.ByteBuffer
).
FITSHeaders.Parser.FitsKey
— TypeFitsKey(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.FitsKey
— MethodFitsKey()
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.PointerCapability
— TypeFITSHeaders.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.byte_index_range
— MethodFITSHeaders.Parser.byte_index_range(A)
yields the range of byte indices in A
.
FITSHeaders.Parser.check_keyword
— MethodFITSHeaders.check_keyword(name) -> key, full_name
checks the FITS keyword name
and returns the corresponding quick key and full keyword name throwing an exception if name
is not a valid FITS keyword. The full keyword name is a string
instance either equal to name
or to "HIERARCH "*name
.
See also FITSHeaders.keyword
, FITSHeaders.parse_keyword
, and FITSHeaders.Parser.full_name
.
FITSHeaders.Parser.check_short_keyword
— MethodFITSHeaders.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.first_byte_index
— MethodFITSHeaders.Parser.first_byte_index(A)
yields the index of the first byte in A
.
FITSHeaders.Parser.full_name
— MethodFITSHeaders.Parser.full_name(pfx, name::AbstractString)
yields "HIERARCH "*name
if pfx
is true, name
otherwise. The result is a String
.
FITSHeaders.Parser.get_byte
— MethodFITSHeaders.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_comment
— MethodFITSHeaders.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_end
— MethodFITSHeaders.is_end(A::Union{FitsKey,FitsCardType,FitsCard})
yields whether A
indicates the END FITS keyword.
FITSHeaders.Parser.is_indexed
— MethodFITSHeaders.Parser.is_indexed(b...)
yields whether trailing bytes b...
of a FITS keyword indicate an indexed keyword.
FITSHeaders.Parser.is_naxis
— MethodFITSHeaders.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.is_structural
— MethodFITSHeaders.is_structural(A::Union{FitsKey,FitsCard})
yields whether A
is a structural FITS keyword or card.
FITSHeaders.Parser.keyword
— MethodFITSHeaders.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.last_byte_index
— MethodFITSHeaders.Parser.last_byte_index(A)
yields the index of the last byte in A
.
FITSHeaders.Parser.make_string
— MethodFITSHeaders.Parser.make_string(buf, rng) -> str::String
yields a string from the bytes of buf
in the range of indices rng
.
FITSHeaders.Parser.scan_card
— FunctionFITSHeaders.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, theEND
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_part
— MethodFITSHeaders.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_part
— MethodFITSHeaders.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), and
i_next` the index of the first byte where next token (value marker of comment) may start.
FITSHeaders.Parser.scan_short_keyword_part
— MethodFITSHeaders.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_parts
— MethodFITSHeaders.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_spaces
— FunctionFITSHeaders.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_spaces
— FunctionFITSHeaders.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_keyword
— MethodFITSHeaders.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
isfalse
.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
isfalse
.
FITSHeaders.Parser.@Fits_str
— Macro@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.Cards
— ModuleFITSHeaders.Cards
A sub-module of the FITSHeaders
package implementing the methods and properties for FITS header cards.
FITSHeaders.Cards.FitsCard
— Typecard = 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 typeFITS_COMMENT
;undef
ormissing
to yield a card of typeFITS_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.FitsCard
— MethodFitsCard(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.FitsCardType
— MethodFitsCardType(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.FitsCardType
— MethodFitsCardType(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.valtype
— Methodvaltype(x::Union{FitsCard,FitsCardType}) -> T
yields the Julia type T
corresponding to a given FITS card or card type.
FITSHeaders.Headers.FitsHeader
— TypeFitsHeader(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.FullName
— TypeFITSHeaders.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.collect
— Methodcollect(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.eachmatch
— Methodeachmatch(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.findfirst
— Methodfindfirst(what, hdr::FitsHeader) -> i :: Union{Int,Nothing}
finds the first occurence of a record in FITS header hdr
matching the pattern what
.
Base.findlast
— Methodfindlast(what, hdr::FitsHeader) -> i :: Union{Int,Nothing}
find the last occurence of a record in FITS header hdr
matching the pattern what
.
Base.findnext
— Functionfindnext(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.findprev
— Functionfindprev(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!
— Methodpush!(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.