This program will show the FITS header from any file, or any extension within the file (by using a feature of fh_file() which searches for an extension when the filename contains ``fitsfile[extname]''.)
This small program is also a good way to test the behavior of fh_file().
#include "fh/fh.h" int main(int argc, const char* argv[]) { HeaderUnit hu = fh_create(); if (fh_file(hu, argv[1], FH_FILE_RDONLY) != FH_SUCCESS) exit(1); fh_show(hu); fh_destroy(hu); /* No errors possible, since RDONLY */ exit(0); }
#include "fh/fh.h" int main(int argc, const char* argv[]) { HeaderUnit hu = fh_create(); const char* card; if (fh_file(hu, argv[1], FH_FILE_RDONLY) != FH_SUCCESS) exit(1); for (card = fh_first(hu); card; card = fh_next(hu)) { printf("%.*s\n", strlen(card)==80?80:79, card); } fh_destroy(hu); /* No errors possible, since RDONLY */ exit(0); }
When there is a separate FITS file for each amplifier, finding the names of all the ``extensions'' is as easy as listing all the files in a subdirectory. Here's a utility which generates an equivalent list of names for MEF Files. The output from this program can be used as the arguments to a pipeline program which uses fh_file() to open one file or extension each time it is run. Multiple FITS files may be given on the command line. In this example, the `fhextname' program is used just as `ls' would have been with basic FITS files:
#!/bin/sh for i in `fhextname *.fits` do reduce "$i" done
Note that this also works for split files, since `fhextname' just prints the name of the fits file itself, if there are no extensions. Here is the source code for `fhextname':
#include <stdio.h> #include <stdlib.h> #include "fh/fh.h" int main(int argc, const char* argv[]) { HeaderUnit hu, ehu; char extname[FH_MAX_STRLEN+1]; int i, ext; for (i = 1; i < argc; i++) { hu = fh_create(); if (fh_file(hu, argv[i], FH_FILE_RDONLY) != FH_SUCCESS) exit(EXIT_FAILURE); if (fh_extensions(hu) < 1) { printf("%s\n", argv[i]); /* No extensions? Print the filename. */ } else for (ext = 1; ext <= fh_extensions(hu); ext++) { if (!(ehu = fh_ehu(hu, ext)) || fh_get_str(ehu, "EXTNAME", extname, sizeof(extname)) != FH_SUCCESS) { fprintf(stderr, "error: Cannot read EXTNAME from `%s' for extension #%d\n", argv[i], ext); fh_destroy(hu); exit(EXIT_FAILURE); } printf("%s[%s]\n", argv[i], extname); } fh_destroy(hu); /* No errors to check, since RDONLY */ } exit(EXIT_SUCCESS); }
Here is the source code for `fhset', a command line utility which sets arbitrary keywords and comment fields in a FITS file (and extension units, if it has any.) The following would cause this tool to update/add GAIN (a float value), OBSID (an integer), CCD (a string) and INHERIT (boolean).
#!/bin/sh fhset 12345o.fits "GAIN=1.35" "OBSID=12345" "CCD='Cam corder CCD'" "INHERIT=F"
Here is the source for the fhset.c program:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "fh/fh.h" int main(int argc, const char* argv[]) { HeaderUnit hu = fh_create(); int i, exitcode = EXIT_SUCCESS; if (argc < 2) { fprintf(stderr, "usage: fhset <fitsfile> KEYWORD=value [/comment] [KEYWORD=value [/comment] ...]\n"); exit(2); } if (fh_file(hu, argv[1], FH_FILE_RDWR) != FH_SUCCESS) exit(EXIT_FAILURE); for (i = 2; i < argc; i++) { char* arg = strdup(argv[i]); char* val = strchr(arg, '='); const char* com; com = 0; if (!val) { fprintf(stderr, "fhset: error: Specify KEYWORD=value (with no spaces)\n"); fh_destroy(hu); exit(2); } *val++ = '\0'; if (i + 1 < argc && *argv[i + 1]=='/') { com = argv[++i]; com++; } /* Don't include the '/' itself. */ fh_set_val(hu, FH_AUTO, arg, val, com); } /* * Write the changes to the file, and unlock it. */ if (fh_rewrite(hu) != FH_SUCCESS) exitcode = EXIT_FAILURE; if (fh_destroy(hu) != FH_SUCCESS) exitcode = EXIT_FAILURE; exit(exitcode); }
This one still has to be written . . .
Something which locks the entire file and shifts everything without removing and creating a completely new file would probably work the best over NFS.
For now, we must make sure the DetCom or anything else which creates FITS files is leaving enough reserved cards for the downstream programs.
In this example, the keyword WEATHER may or may not exist.
#include <stdio.h> #include <stdlib.h> #include "fh/fh.h" int main(int argc, const char* argv[]) { HeaderUnit hu = fh_create(); int exitcode = EXIT_SUCCESS; /* * This program does not like to read from pipes. Check explicitly. */ if (!argv[1] || !strcmp(argv[1], "-")) { fprintf(stderr, "error: Updating FITS cards requires an input file.\n") exit(EXIT_FAILURE); } if (fh_file(hu, argv[1], FH_FILE_RDWR) != FH_SUCCESS) exit(EXIT_FAILURE); fh_set_str(hu, FH_AUTO, "WEATHER", "Excellent", "This card is bogus"); if (fh_rewrite(hu) != FH_SUCCESS) exitcode = EXIT_FAILURE; /* File is now changed (and automatically unlocked) */ if (fh_destroy(hu) != FH_SUCCESS) exitcode = EXIT_FAILURE; exit(exitcode); }
This example is not a complete program, but a function within a larger program which has its own way of opening a file descriptor (and thus, does not want to use fh_file.)
PASSFAIL add_some_comments(int fd) { HeaderUnit hu = fh_create(); PASSFAIL rtn = PASS; if (fh_read(hu, fd, FH_AUTO) != FH_SUCCESS) rtn = FAIL; else { fh_set_com(hu, FH_AUTO, "COMMENT", "Here's the 1st of 3 lines"); fh_set_com(hu, FH_AUTO, "COMMENT", "which will be added"); fh_set_com(hu, FH_AUTO, "COMMENT", "to the end of the header"); if (fh_rewrite(hu) != FH_SUCCESS) rtn = FAIL; } /* Don't forget to free memory, even on failure, */ /* especially since this unlocks the file too. */ if (fh_destroy(hu) != FH_SUCCESS) rtn = FAIL; return rtn; }
Here a complete FITS file is built from scratch, using routines in libfh. It demonstrates several other things which the examples above do not:
Source code follows:
#include <stdio.h> #include <stdlib.h> #include <time.h> #include "fh/fh.h" int main(int argc, const char* argv[]) { HeaderUnit hu = fh_create(); time_t date = time(0); short* data; int exitcode = EXIT_SUCCESS; int fd = STDOUT_FILENO; /* By default, write to stdout */ double etime = 10.0; /* Normally these values might */ int w = 2048, h = 4096; /* not be hardcoded like this */ data = malloc(sizeof(short)*w*h); fh_reserve(hu, 50); /* Reserve space for 50 cards (TCS, Elixir?, etc.) */ fh_set_bool(hu, FH_AUTO, "SIMPLE", 1, "Standard FITS"); fh_set_int(hu, FH_AUTO, "BITPIX", 16,"16-bit data"); fh_set_int(hu, FH_AUTO, "NAXIS", 2, "Number of axes"); fh_set_int(hu, FH_AUTO, "NAXIS1", w, "Number of pixel columns"); fh_set_int(hu, FH_AUTO, "NAXIS2", h, "Number of pixel rows"); fh_set_int(hu, FH_AUTO, "PCOUNT", 0, "No 'random' parameters"); fh_set_int(hu, FH_AUTO, "GCOUNT", 1, "Only one group"); strftime(str, sizeof(str)-1, "%Y-%m-%dT%T", gmtime(&date)); fh_set_str(hu, 104, "DATE", str, "UTC Date of file creation"); strftime(str, sizeof(str)-1, "%a %b %d %H:%M:%S %Z %Y", localtime(&date)); fh_set_str(hu, 104.1,"HSTTIME", str, "Local time in Hawaii"); fh_set_str(hu, 105, "ORIGIN", "CFHT", "Canada-France-Hawaii Telescope"); fh_set_flt(hu, 141., "BZERO", 0.0, 6, "Zero factor"); fh_set_flt(hu, 142., "BSCALE", 1.0, 2, "Scale factor"); fh_set_flt(hu, 150, "DATAMIN", datamin, 6, "Minimum value of the data"); fh_set_flt(hu, 151, "DATAMAX", datamax, 6, "Maximum value of the data"); fh_set_flt(hu, 160, "SATURATE", 4016.0, 6, "Saturation value"); fh_set_flt(hu, 220, "EXPTIME", etime, 5, "Integration time (seconds)"); sprintf(str, "%d %d", binmode + 1, binmode + 1); fh_set_str(hu, 230, "CCDSUM", str, "Binning factors"); fh_set_com(hu, 1400.0, "COMMENT", ""); fh_set_com(hu, 1400.1, "COMMENT", " Camera status record:"); fh_set_com(hu, 1400.2, "COMMENT", ""); if (camera_status==0) fh_set_str(hu, 1410, "DETSTAT", "ok", "(camera_status is 0)"); else fh_set_int(hu, 1411, "DETSTAT", sbstat.imaging_ccd_status, "error!"); fh_set_str(hu, 1601,"DETECTOR", info.camera_name, info.serial_number); if (fh_write(hu, fd) != FH_SUCCESS || fh_write_padded_image(hu, fd, data, w*h*sizeof(unsigned short)) != FH_SUCCESS) exitcode = EXIT_FAILURE; if (fh_destroy(hu) != FH_SUCCESS) exitcode = EXIT_FAILURE; exit(exitcode); }
The fh_ehu*() functions are only used to access extensions within an already existing MEF. The are not used when building an MEF File for the first time. This task must be done by writing the appropriate interleaved header units and image data manually.
The following example constructs a very minimal MEF File with only two extensions. For a more comprehensive example, see the source code of DetCom, /cfht/src/medusa/detcom/detcom/det_data.c.
#include <stdio.h> #include <stdlib.h> #include "fh/fh.h" #define NEXTEND 2 int main(int argc, const char* argv[]) { short dummy_data[3 * 2] = { 1, 2, 3, 4, 5, 6 }; HeaderUnit phu, ehu; int i, fd = STDOUT_FILENO; phu = fh_create(); fh_set_bool(phu, FH_AUTO, "SIMPLE", 1, "Standard FITS"); fh_set_int( phu, FH_AUTO, "BITPIX", 16, "Bits per pixel (not appl. to PHU)"); fh_set_int( phu, FH_AUTO, "NAXIS", 0, "No image data with primary header"); fh_set_bool(phu, FH_AUTO, "EXTEND", 1, "File contains extensions"); fh_set_int( phu, FH_AUTO, "NEXTEND", NEXTEND, "Number of extensions"); fh_write(phu, fd); fh_destroy(phu); for (i = 0; i < NEXTEND; i++) { char str[FH_MAX_STRLEN+1]; sprintf(str, "im%02d", i); ehu = fh_create(); fh_set_str(ehu, FH_AUTO, "XTENSION", "IMAGE", "Image extension"); fh_set_int(ehu, FH_AUTO, "BITPIX", 16, "Bits per pixel"); fh_set_int(ehu, FH_AUTO, "NAXIS", 2, "Number of axes"); fh_set_int(ehu, FH_AUTO, "NAXIS1", 3, "Number of pixel columns"); fh_set_int(ehu, FH_AUTO, "NAXIS2", 2, "Number of pixel rows"); fh_set_int(ehu, FH_AUTO, "PCOUNT", 0, "No 'random' parameters"); fh_set_int(ehu, FH_AUTO, "GCOUNT", 1, "Only one group"); fh_set_str(ehu, FH_AUTO, "EXTNAME", str, "Extension name"); fh_write(ehu, fd); fh_write_padded_image(ehu, fd, dummy_data, sizeof(dummy_data)); fh_destroy(ehu); } }