# This glish script contains two functions for retrieving or displaying the # full contents of a FITS binary table file. get_fits returns a record # containing all of the header keyword values labeled by the keyword names and # all of the column arrays labled by column name. show_fits displays the HDU, # keyword, and column tree with keyword values. Only a few values from each # column are shown since these arrays can be quite long. # Most of the FITS file retrieval code is in a glish client written in C/C++. # This uses some FITS access code written locally, but, if this client is to be # used more widely, it should be rewritten using the cfitsio library. # http://heasarc.gsfc.nasa.gov/docs/software/fitsio/fitsio.html # To run from the glish prompt: # - path := '/home/gbtdata/AGBT02A_069_01/SpectralProcessor/' # - ft := get_fits(spaste(path, '2003_06_17_09:22:34.fits')) # or # - show_fits(spaste(path, '2003_06_17_09:22:34.fits')) # start the FITS access client fits := client('/users/rfisher/Applications/FitsCode/fits_client'); # This routine returns a record containing all of the header keyword values # labeled by the keyword names and all of the column arrays labled by column # name. Contents of this record are retrieved by HDU extension name and # keyword or column name, e.g., # ft.RECEIVER.UTCSTART # ft.DATA.Columns.DATA[1:10,1,2,1] # The first or main HDU is called 'main_header' in the record. The # glish function field_names() can be used to explore the name space, e.g., # field_names(ft) # field_names(ft.RECEIVER) # field_names(ft.DATA.Columns) # The client returns the record pretty much as needed, but the arrays must be # passed as flat (one dimensional) arrays along with an array of integer # dimensions. Hence, the get_fits() function must go through the record # looking for arrays with associated 'Shapes' members and change these arrays # to the specified multiple dimensions. The glishify_arrays() function does # the array reshaping. func get_fits ( file_name ) { # get the FITS glish record with flat arrays from the client fr := fits->get_fits(file_name); # if retrieval fails, return now if (!is_record(fr)) return F; # look at up to two levels of record depth for arrays and modify when # found for (i in 1:length(fr)) { if (is_record(fr[i])) { glishify_arrays(fr[i]); if (length(fr[i]) > 0) { for (j in 1:length(fr[i])) { glishify_arrays(fr[i][j]); } } } } # send the fixed-up record to the user return fr; } # This function looks for 'Shapes' record members in the subrecord passed to # it by get_fits() and, if the 'Shapes' value contains more than one dimension, # the associate FITS column array is reshaped accordingly. Up to five array # dimensons are expected. func glishify_arrays ( ref rec ) { if (!is_record(rec)) return; if (has_field(rec, 'Shapes')) { if (length(rec.Shapes) < 1) return; for (sh in 1:length(rec.Shapes)) { if (length(rec.Shapes[sh]) == 2) { rec.Columns[sh] := array(rec.Columns[sh], rec.Shapes[sh][1], rec.Shapes[sh][2]); } else { if (length(rec.Shapes[sh]) == 3) { rec.Columns[sh] := array(rec.Columns[sh], rec.Shapes[sh][1], rec.Shapes[sh][2], rec.Shapes[sh][3]); } else { if (length(rec.Shapes[sh]) == 4) { rec.Columns[sh] := array(rec.Columns[sh], rec.Shapes[sh][1], rec.Shapes[sh][2], rec.Shapes[sh][3], rec.Shapes[sh][4]); } else { if (length(rec.Shapes[sh]) == 5) { rec.Columns[sh] := array(rec.Columns[sh], rec.Shapes[sh][1], rec.Shapes[sh][2], rec.Shapes[sh][3], rec.Shapes[sh][4], rec.Shapes[sh][5]); } } } } } } } # This function prints the contents of a FITS file in a tree according to the # structure of the Header-Data Units (HDU's) in the files. The field names are # printed exactly as the would be referred to in a glish record reference, # e.g., ft.RECEIVER.UTDATE or ft.DATA.Columns.SUBSCAN. Keyword values and # up to five vales of column array are also printed. The printed output may # be sent to a file by specifying file name string in the optional second # argument. func show_fits ( file_name, out_file=F ) { # get the FITS record and test for validity fr := get_fits(file_name); if (!is_record(fr)) return; # open an output file if requested if (!is_boolean(out_file)) { fd := open(spaste('> ', out_file)) } # get the first level of record field names and cycle through them name1 := field_names(fr); for (i in 1:length(fr)) { v := ''; # if the record member is a keyword or column and not another record # depth, print the name and value(s) if (!is_record(fr[i])) { # print no more than 5 values n := 1:min(length(fr[i]), 5); v := fr[i][n]; } str := spaste(name1[i], ': ', v); if (is_boolean(out_file)) { print str; } else { write(fd, str); } # repeat the search ad print process for two more record depths if (is_record(fr[i])) { name2 := field_names(fr[i]); for (j in 1:length(fr[i])) { v := ''; if (!is_record(fr[i][j])) { m := min(length(fr[i][j]), 5); if (m > 0) { n := 1:m v := fr[i][j][n]; } } # don't bother printing array shape dimensions if (name2[j] != 'Shapes') { str := spaste(' ', name2[j], ': ', v); if (is_boolean(out_file)) { print str; } else { write(fd, str); } if (is_record(fr[i][j])) { name3 := field_names(fr[i][j]); for (k in 1:length(fr[i][j])) { v := ''; if (!is_record(fr[i][j][k])) { m := min(length(fr[i][j][k]), 5); if (m > 0) { n := 1:m v := fr[i][j][k][n]; } } str := spaste(' ', name3[k], ': ', v); if (is_boolean(out_file)) { print str; } else { write(fd, str); } } } } } } } } # To test, uncover one of the calls and the print statement below and from # the glish prompt execute: # include 'get_fits,g' # ft = get_fits('/home/gbtdata/AGBT02A_069_01/SpectralProcessor/2003_06_17_09:22:34.fits') # print ft.DATA.Column.DATA::shape # print field_names(ft), '\n' # for (k in field_names(ft)) { # print k, ':', field_names(ft[k]) # if (has_field(ft[k], 'Columns')) { # print '----' # print field_names(ft[k].Columns) # } # print '\n' # } # show_fits('/home/gbtdata/AGBT02A_069_01/LO1A/2003_06_17_09:22:34.fits')