Manual

Introduction

A FITS file (with defining extension .fits) consists of a sequence of one or more Header and Data Units (FITS_HDUs), each containing a FITS_dataobject preceeded by a FITS_header of records with metainformation.

The first HDU in a .fits file is called the PRIMARY HDU. It is an IMAGE HDU, one of the STANDARD HDU types IMAGE, ASCII TABLE and BINTABLE.

Let "example.fits" be an existing .fits file. By the commands

julia> filnam = "example.fits"
"example.fits"

julia> f = fits_read(filnam);

we asign the FITS object (read from the .fits file filnam on disc) to the variable f. All information of a given .fits file is stored in this FITS object, its structure is shown in the diagram below.

Image

The fields of f (f.hdu[1], f.hdu[2], ...) correspond to FITS_HDU objects. The PRIMARY HDU of the FITS object is represented by f.hdu[1]. The header records are contained in an array of single-record cards.

The formal terminology of the FITS standard can be consulted using fits_terminology:

julia> fits_terminology("HDU")
HDU:
Header and Data Unit. A data structure consisting of a header and the data the 
header describes. Note that an HDU may consist entirely of a header with no 
data blocks.

The words must, shall, may, recommended, required and optional are to be interpreted as described in the IETF standard - RFC 2119.

Creating a simple FITS file

FITS files can be created with the command fits_create. This process proceeds in two steps: (a) first a FITS object is constructed starting from the data provided (in Julia format); (b) this FITS object is autosaved under the specifified name (e.g., name.fits).

Example:

The minimal file conforming to the FITS standard consists of a single HDU with an empty data field.

julia> filnam = "minimal.fits";

julia> f = fits_create(filnam; protect=false);

julia> fits_info(f)

File: minimal.fits
hdu: 1
hdutype: PRIMARY
DataType: Any
Datasize: (0,)

Metainformation:
SIMPLE  =                    T / file does conform to FITS standard
BITPIX  =                   64 / number of bits per data pixel
NAXIS   =                    1 / number of data axes
NAXIS1  =                    0 / length of data axis 1
EXTEND  =                    T / FITS dataset may contain extensions
END

Any[]

Note how the FITS object is inspected using the fits_info tool.

The FITS_header of a FITS_HDU is contained in an array of single-record FITS_card objects as illustrated in the flow diagram above. To find the cardindex associated with a keyword (e.g., "NAXIS") we can use the header.map:

julia> i = f.hdu[1].header.map["NAXIS"]
3

The result is easily verified by inspecting the field card[i].cardindex:

julia> f.hdu[1].header.card[i].cardindex
3

Likewise, by inspecting the field card[i].keyword we confirm

julia> f.hdu[1].header.card[i].keyword
"NAXIS"

The full record is:

julia> f.hdu[1].header.card[i].record
"NAXIS   =                    1 / number of data axes                            "

Once ready it is good practice to remove the example:

julia> rm(filnam); f = nothing

FITS inspection tools

Rather than inspecting the FITS object directly (as demonstrated above), CamiFITS offers the fits_info and fits_record_dump tools. These tools will be demonstrated in the next sections to demonstrate several FITS_HDU types.

FITS file for a simple image

In this section we demonstrate the fits_info tool to inspect the primary hdu of a FITS object.

We start by creating a simple image in the form of a 3x3 matrix:

julia> filnam = "matrix.fits";

julia> data = [11,21,31,12,22,23,13,23,33];

julia> data = reshape(data,(3,3,1))
3×3×1 Array{Int64, 3}:
[:, :, 1] =
 11  12  13
 21  22  23
 31  23  33

We next create the FITS object for data (our image).

julia> f = fits_create(filnam, data; protect=false);

We then inspect the FITS object using the info tool:

julia> fits_info(f)

File: matrix.fits
hdu: 1
hdutype: PRIMARY
DataType: Int64
Datasize: (3, 3, 1)

Metainformation:
SIMPLE  =                    T / file does conform to FITS standard
BITPIX  =                   64 / number of bits per data pixel
NAXIS   =                    3 / number of data axes
NAXIS1  =                    3 / length of data axis 1
NAXIS2  =                    3 / length of data axis 2
NAXIS3  =                    1 / length of data axis 3
EXTEND  =                    T / FITS dataset may contain extensions
END

3×3×1 Array{Int64, 3}:
[:, :, 1] =
 11  12  13
 21  22  23
 31  23  33

julia> f = nothing

The keywords NAXIS1, NAXIS2 and NAXIS3 represent the dimensions of the $x, y$ data matrix stacked in the $z$ direction.

The matrix elements are referred to as pixels and their bit size is represented by the keyword BITPIX. In the above example the pixel value is used to indicate the matrix indices, typically it will be an integer representing the gray tone of an image pixel.

Above, the FITS object f was removed from memory but its contents was autosaved under the name filnam = 'matrix.fits'. To access the image data of filnam we can fits_read the FITS object from disk but it is simpler to access the data using fits_info for image processing in Julia:

julia> image = fits_info(filnam; hdr=false)
3×3×1 Array{Int64, 3}:
[:, :, 1] =
 11  12  13
 21  22  23
 31  23  33

julia> image == data
true

julia> rm(filnam); f = data = nothing

At this point the variable 'image' is available for further processing (e.g. plotting) in julia.

Comment on the casting procedures

Note that the relevant mandatory keywords are autogenerated by fits_create, starting from the Julia datatype and using the FITS object casting procedures, cast_FITS, cast_FITS_filnam, cast_FITS_HDU, cast_FITS_header, cast_FITS_card and cast_FITS_dataobject. Aside from constructing the FITS objects these procedures impose comformance to the FITS standard.

For users primarily interested in image processing, the casting procedures typically remain hidden as they are called internally by fits_create and fits_read.

Extending a FITS file with standard HDUs

In this section we demonstrate the fits_extend! tool to extend the primary hdu of a FITS object by examples of the three STANDARD HDU types (IMAGE, ASCII TABLE and BINTABLE).

We start by defining a simple image by reshaping a data vector into the form of a 3x3 matrix (as ilustrated above):

julia> data1 = [11,21,31,12,22,23,13,23,33];

julia> data1 = reshape(data1,(3,3,1));

We next create a FITS file named 'example.fits' for further use:

julia> filnam = "example.fits";

julia> fits_create(filnam, data1; protect=false);

At this point we could inspect filnam with the fits_info(filnam) command (see above), here we proceed by extending the file with an image HDU.

Adding an image HDU extension

We first define (or load) another image

julia> data2 = [11,21,12,22,13,23];

julia> data2 = reshape(data2,(2,3,1));

Next we apply the extension and inspect the new HDU (hdu[2]):

julia> fits_extend!(filnam, data2; hdutype="image");

julia> fits_info(filnam, 2; hdr=true)

File: example.fits
hdu: 2
hdutype: 'IMAGE   '
DataType: Int64
Datasize: (2, 3, 1)

  nr | Metainformation:
--------------------------------------------------------------------------------
   1 | XTENSION= 'IMAGE   '           / FITS standard extension
   2 | BITPIX  =                   64 / number of bits per data pixel
   3 | NAXIS   =                    3 / number of data axes
   4 | NAXIS1  =                    2 / length of data axis 1
   5 | NAXIS2  =                    3 / length of data axis 2
   6 | NAXIS3  =                    1 / length of data axis 3
   7 | END

2×3×1 Array{Int64, 3}:
[:, :, 1] =
 11  12  13
 21  22  23

Adding a table HDU extension

We first define (or load) the table (in this example a two-row table):

julia> table = let
       [true, 0x6c, 1081, 0x0439, 1081, 1.23, 1.01f-6, 1.01e-6, 'a', "a", "abc"],
       [false, 0x6d, 1011, 0x03f3, 1011, 123.4, 3.01f-6, 30.01e-6, 'b', "b", "abcdef"]
       end;

We extend the file by a table HDU by setting the hdutype to 'table'. The result is inspected using fits_info() by setting the hduindex to 3

julia> fits_extend!(filnam, table; hdutype="table");

julia> fits_info(filnam, 3; hdr=true)

File: example.fits
hdu: 3
hdutype: 'TABLE   '

  nr | Metainformation:
--------------------------------------------------------------------------------
   1 | XTENSION= 'TABLE   '           / FITS standard extension
   2 | BITPIX  =                    8 / number of bits per data pixel
   3 | NAXIS   =                    2 / number of data axes
   4 | NAXIS1  =                   56 / number of bytes/row
   5 | NAXIS2  =                    2 / number of rows
   6 | PCOUNT  =                    0 / number of bytes in supplemetal data area
   7 | GCOUNT  =                    1 / data blocks contain single table
   8 | TFIELDS =                   11 / number of data fields (columns)
   9 | COLSEP  =                    1 / number of spaces in column separator
  10 |
  11 | TTYPE1  = 'HEAD1             ' / field header
  12 | TBCOL1  =                    1 / pointer to field column 1
  13 | TFORM1  = 'I1      '           / field datatype specifier
  14 | TDISP1  = 'I1      '           / proposed field display format
  15 |
  16 | TTYPE2  = 'HEAD2             ' / field header
  17 | TBCOL2  =                    3 / pointer to field column 2
  18 | TFORM2  = 'I3      '           / field datatype specifier
  19 | TDISP2  = 'I3      '           / proposed field display format
  20 |
  21 | TTYPE3  = 'HEAD3             ' / field header
  22 | TBCOL3  =                    7 / pointer to field column 3
  23 | TFORM3  = 'I4      '           / field datatype specifier
  24 | TDISP3  = 'I4      '           / proposed field display format
  25 |
  26 | TTYPE4  = 'HEAD4             ' / field header
  27 | TBCOL4  =                   12 / pointer to field column 4
  28 | TFORM4  = 'I4      '           / field datatype specifier
  29 | TDISP4  = 'I4      '           / proposed field display format
  30 | TZERO4  =                32768 / zero offset of field 4
  31 | TSCAL4  =                  1.0 / scale factor of field 4
  32 |
  33 | TTYPE5  = 'HEAD5             ' / field header
  34 | TBCOL5  =                   17 / pointer to field column 5
  35 | TFORM5  = 'I4      '           / field datatype specifier
  36 | TDISP5  = 'I4      '           / proposed field display format
  37 |
  38 | TTYPE6  = 'HEAD6             ' / field header
  39 | TBCOL6  =                   22 / pointer to field column 6
  40 | TFORM6  = 'F6.2    '           / field datatype specifier
  41 | TDISP6  = 'F6.2    '           / proposed field display format
  42 |
  43 | TTYPE7  = 'HEAD7             ' / field header
  44 | TBCOL7  =                   29 / pointer to field column 7
  45 | TFORM7  = 'E7.2    '           / field datatype specifier
  46 | TDISP7  = 'E7.2    '           / proposed field display format
  47 |
  48 | TTYPE8  = 'HEAD8             ' / field header
  49 | TBCOL8  =                   37 / pointer to field column 8
  50 | TFORM8  = 'D8.3    '           / field datatype specifier
  51 | TDISP8  = 'D8.3    '           / proposed field display format
  52 |
  53 | TTYPE9  = 'HEAD9             ' / field header
  54 | TBCOL9  =                   46 / pointer to field column 9
  55 | TFORM9  = 'A1      '           / field datatype specifier
  56 | TDISP9  = 'A1      '           / proposed field display format
  57 |
  58 | TTYPE10 = 'HEAD10            ' / field header
  59 | TBCOL10 =                   48 / pointer to field column 10
  60 | TFORM10 = 'A1      '           / field datatype specifier
  61 | TDISP10 = 'A1      '           / proposed field display format
  62 |
  63 | TTYPE11 = 'HEAD11            ' / field header
  64 | TBCOL11 =                   50 / pointer to field column 11
  65 | TFORM11 = 'A6      '           / field datatype specifier
  66 | TDISP11 = 'A6      '           / proposed field display format
  67 |
  68 | END

2-element Vector{String}:
 " 1 108 1081 1081 1081   1.23 1.01E-6 1.010D-6 a a    abc"
 " 0 109 1011 1011 1011 123.40 3.01E-6 3.001D-5 b b abcdef"

Note that the numerical-input 'table' has been cast in the form of an array of printable-ASCII characters ordered in columns of width defined by the 'TBCOL#' keyword values in a FORTRAN format as specified by the 'TFORM#' keyword values.

Adding a binary table HDU extension

The same two-line table can also be included as a BINARY TABLE HDU by setting the hdutype to 'bintable'.

julia> fits_extend!(filnam, table; hdutype="bintable");

julia> fits_info(filnam, 4; hdr=true)

File: example.fits
hdu: 4
hdutype: 'BINTABLE'

  nr | Metainformation:
--------------------------------------------------------------------------------
   1 | XTENSION= 'BINTABLE'           / FITS standard extension
   2 | BITPIX  =                    8 / number of bits per data pixel
   3 | NAXIS   =                    2 / number of data axes
   4 | NAXIS1  =                   52 / number of bytes/row
   5 | NAXIS2  =                    2 / number of rows
   6 | PCOUNT  =                    0 / number of bytes in supplemetal data area
   7 | GCOUNT  =                    1 / data blocks contain single table
   8 | TFIELDS =                   11 / number of data fields (columns)
   9 |
  10 | TTYPE1  = 'HEAD1   '           / field header
  11 | TFORM1  = '1K      '           / field datatype specifier
  12 |
  13 | TTYPE2  = 'HEAD2   '           / field header
  14 | TFORM2  = '1B      '           / field datatype specifier
  15 |
  16 | TTYPE3  = 'HEAD3   '           / field header
  17 | TFORM3  = '1K      '           / field datatype specifier
  18 |
  19 | TTYPE4  = 'HEAD4   '           / field header
  20 | TFORM4  = '1I      '           / field datatype specifier
  21 | TZERO4  = 32768                / zero offset of field 4
  22 | TSCAL4  =                  1.0 / scale factor of field 4
  23 |
  24 | TTYPE5  = 'HEAD5   '           / field header
  25 | TFORM5  = '1K      '           / field datatype specifier
  26 |
  27 | TTYPE6  = 'HEAD6   '           / field header
  28 | TFORM6  = '1D      '           / field datatype specifier
  29 |
  30 | TTYPE7  = 'HEAD7   '           / field header
  31 | TFORM7  = '1E      '           / field datatype specifier
  32 |
  33 | TTYPE8  = 'HEAD8   '           / field header
  34 | TFORM8  = '1D      '           / field datatype specifier
  35 |
  36 | TTYPE9  = 'HEAD9   '           / field header
  37 | TFORM9  = '1A      '           / field datatype specifier
  38 |
  39 | TTYPE10 = 'HEAD10  '           / field header
  40 | TFORM10 = '1A      '           / field datatype specifier
  41 |
  42 | TTYPE11 = 'HEAD11  '           / field header
  43 | TFORM11 = '6A      '           / field datatype specifier
  44 |
  45 | END

2-element Vector{Any}:
 Any[1, 0x6c, 1081, 0x0439, 1081, 1.23, 1.01f-6, 1.01e-6, 'a', 'a', "   abc"]
 Any[0, 0x6d, 1011, 0x03f3, 1011, 123.4, 3.01f-6, 3.001e-5, 'b', 'b', "abcdef"]

Note that the two rows are of the same type (with Strings of equal length).

Easy navigation

By assigning the FITS object to the variable 'f' we have access to the 4 HDUs without the overhead of 4 times reading filnam into the info.

julia> f = fits_read(filnam);

julia> fits_info(f.hdu[1]; hdr=false)
3×3×1 Array{Int64, 3}:
[:, :, 1] =
 11  12  13
 21  22  23
 31  23  33

julia> fits_info(f.hdu[2]; hdr=false)
2×3×1 Array{Int64, 3}:
[:, :, 1] =
 11  12  13
 21  22  23

julia> fits_info(f.hdu[3]; hdr=false)
2-element Vector{String}:
 " 1 108 1081 1081 1081 1081 1081 1081   1.23 1.01E-6 1.01D-6 a a    abc"
 " 0 109 1011 1011 1011 1011 1011 1011 123.40 3.01E-6 3.02D-5 b b abcdef"

julia> fits_info(f.hdu[4]; hdr=false)
2-element Vector{Any}:
 Any[1, 0x6c, 1081, 0x0439, 1081, 1.23, 1.01f-6, 1.01e-6, 'a', 'a', "   abc"]
 Any[0, 0x6d, 1011, 0x03f3, 1011, 123.4, 3.01f-6, 3.001e-5, 'b', 'b', "abcdef"]

Finally we remove the example:

julia> rm(filnam); f = nothing