aboutsummaryrefslogtreecommitdiff
path: root/src/libcf
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcf')
-rw-r--r--src/libcf/Makefile.Linux.orig152
-rw-r--r--src/libcf/Makefile.Linux64.orig151
-rw-r--r--src/libcf/Makefile.MacOSX.orig152
-rw-r--r--src/libcf/Makefile.Solaris.orig152
-rw-r--r--src/libcf/Makefile.orig.orig152
-rw-r--r--src/libcf/calfits.c658
-rw-r--r--src/libcf/cf_active_region.c90
-rw-r--r--src/libcf/cf_apply_dead_time.c142
-rw-r--r--src/libcf/cf_apply_filters.c177
-rw-r--r--src/libcf/cf_astig_farf.c144
-rw-r--r--src/libcf/cf_astigmatism.c198
-rw-r--r--src/libcf/cf_cal_file.c84
-rw-r--r--src/libcf/cf_calculate_y_centroid.c289
-rw-r--r--src/libcf/cf_calculate_ycent_motion.c152
-rw-r--r--src/libcf/cf_check_digitizer.c101
-rw-r--r--src/libcf/cf_convert_to_ergs.c182
-rw-r--r--src/libcf/cf_count_rate_y_distort.c157
-rw-r--r--src/libcf/cf_dispersion.c85
-rw-r--r--src/libcf/cf_doppler_and_heliocentric.c87
-rw-r--r--src/libcf/cf_electronics_dead_time.c104
-rw-r--r--src/libcf/cf_error_msg.c263
-rw-r--r--src/libcf/cf_extraction_limits.c137
-rw-r--r--src/libcf/cf_fes_proc_check.c94
-rw-r--r--src/libcf/cf_fes_proc_update.c68
-rw-r--r--src/libcf/cf_fifo_dead_time.c164
-rw-r--r--src/libcf/cf_find_spectra.c396
-rw-r--r--src/libcf/cf_fpa_position.c231
-rw-r--r--src/libcf/cf_fuv_init.c592
-rw-r--r--src/libcf/cf_geometric_distort.c145
-rw-r--r--src/libcf/cf_grating_motion.c432
-rw-r--r--src/libcf/cf_header_io.c110
-rw-r--r--src/libcf/cf_identify_channel.c201
-rw-r--r--src/libcf/cf_idf_io.c161
-rw-r--r--src/libcf/cf_ids_dead_time.c109
-rw-r--r--src/libcf/cf_init_support.c1344
-rw-r--r--src/libcf/cf_make_mask.c175
-rw-r--r--src/libcf/cf_make_wave_array.c147
-rw-r--r--src/libcf/cf_mirror_motion.c189
-rw-r--r--src/libcf/cf_modify_hist_pha.c91
-rw-r--r--src/libcf/cf_modify_hist_times.c83
-rw-r--r--src/libcf/cf_nint.c47
-rw-r--r--src/libcf/cf_optimal_extraction.c551
-rw-r--r--src/libcf/cf_pha_x_distort.c107
-rw-r--r--src/libcf/cf_proc_check.c124
-rw-r--r--src/libcf/cf_proc_update.c97
-rw-r--r--src/libcf/cf_read_fpa_pos.c82
-rw-r--r--src/libcf/cf_rebin_background.c211
-rw-r--r--src/libcf/cf_rebin_probability_array.c180
-rw-r--r--src/libcf/cf_satellite_jitter.c245
-rw-r--r--src/libcf/cf_scale_bkgd.c956
-rw-r--r--src/libcf/cf_screen_airglow.c70
-rw-r--r--src/libcf/cf_screen_bad_pixels.c122
-rw-r--r--src/libcf/cf_screen_burst.c758
-rw-r--r--src/libcf/cf_screen_high_voltage.c146
-rw-r--r--src/libcf/cf_screen_jitter.c246
-rw-r--r--src/libcf/cf_screen_limb_angle.c87
-rw-r--r--src/libcf/cf_screen_pulse_height.c79
-rw-r--r--src/libcf/cf_screen_saa.c83
-rw-r--r--src/libcf/cf_set_good_time_intervals.c157
-rw-r--r--src/libcf/cf_set_photon_flags.c425
-rw-r--r--src/libcf/cf_set_user_gtis.c100
-rw-r--r--src/libcf/cf_source_aper.c67
-rw-r--r--src/libcf/cf_standard_or_optimal_extraction.c75
-rw-r--r--src/libcf/cf_target_count_rate.c112
-rw-r--r--src/libcf/cf_thermal_distort.c258
-rw-r--r--src/libcf/cf_time_xy_distort.c171
-rw-r--r--src/libcf/cf_timestamp.c78
-rw-r--r--src/libcf/cf_velang.c147
-rw-r--r--src/libcf/cf_write_extracted_spectrum.c226
-rw-r--r--src/libcf/eclipse.c148
-rw-r--r--src/libcf/geod_mag.c40
-rw-r--r--src/libcf/helio_vel.c82
-rw-r--r--src/libcf/lsrd_vel.c51
-rw-r--r--src/libcf/lsrk_vel.c52
-rw-r--r--src/libcf/month_day.c19
-rw-r--r--src/libcf/pole_ang.c64
-rw-r--r--src/libcf/read_tle.c303
-rw-r--r--src/libcf/saa.c74
-rw-r--r--src/libcf/set_orbit_parms.c86
-rw-r--r--src/libcf/sgp4.c426
-rw-r--r--src/libcf/solar_ang.c90
-rw-r--r--src/libcf/space_vel.c46
-rw-r--r--src/libcf/state_geod.c107
-rw-r--r--src/libcf/state_limb.c159
84 files changed, 16295 insertions, 0 deletions
diff --git a/src/libcf/Makefile.Linux.orig b/src/libcf/Makefile.Linux.orig
new file mode 100644
index 0000000..43b2e31
--- /dev/null
+++ b/src/libcf/Makefile.Linux.orig
@@ -0,0 +1,152 @@
+LIBRARY= libcf
+
+CALFUSEDIR= ${PWD}/../..
+SHARED= -shared
+FITSVER= 2.470
+
+# Symbols for include directories
+FUSEINCLDIR= -I${CALFUSEDIR}/include
+
+# Symbols used for compiling
+CC= cc
+OPT= -g -Wall -DCFORTRAN -Dg77Fortran -Df2cFortran
+CFLAGS= ${OPT} ${FUSEINCLDIR}
+
+FUSELIBDIR= -L${CALFUSEDIR}/lib
+FUSELIBS= -lcfitsio-${FITSVER} -lsla
+LIBS= -lc -lm -lnsl -ldl -lgfortran
+LDFLAGS=
+
+# Symbols used for creating shared libraries
+
+SO= .so
+
+OBJS= calfits.o sgp4.o eclipse.o saa.o \
+ state_limb.o state_geod.o space_vel.o helio_vel.o \
+ geod_mag.o pole_ang.o solar_ang.o lsrd_vel.o lsrk_vel.o \
+ month_day.o read_tle.o cf_velang.o \
+ set_orbit_parms.o cf_error_msg.o \
+ cf_cal_file.o cf_proc_check.o cf_proc_update.o \
+ cf_timestamp.o cf_fuv_init.o cf_header_io.o \
+ cf_check_digitizer.o cf_nint.o \
+ cf_idf_io.o cf_ids_dead_time.o cf_electronics_dead_time.o \
+ cf_fifo_dead_time.o cf_apply_dead_time.o \
+ cf_thermal_distort.o cf_count_rate_y_distort.o cf_time_xy_distort.o \
+ cf_geometric_distort.o cf_pha_x_distort.o \
+ cf_active_region.o cf_find_spectra.o cf_identify_channel.o \
+ cf_calculate_ycent_motion.o cf_source_aper.o\
+ cf_grating_motion.o cf_fpa_position.o cf_read_fpa_pos.o \
+ cf_make_mask.o cf_mirror_motion.o \
+ cf_satellite_jitter.o cf_calculate_y_centroid.o \
+ cf_target_count_rate.o \
+ cf_screen_jitter.o cf_screen_limb_angle.o cf_screen_saa.o \
+ cf_screen_high_voltage.o cf_screen_burst.o cf_screen_airglow.o \
+ cf_screen_bad_pixels.o cf_set_user_gtis.o \
+ cf_set_photon_flags.o cf_set_good_time_intervals.o \
+ cf_modify_hist_times.o cf_screen_pulse_height.o \
+ cf_convert_to_ergs.o cf_extraction_limits.o \
+ cf_astigmatism.o cf_dispersion.o cf_doppler_and_heliocentric.o \
+ cf_apply_filters.o cf_scale_bkgd.o \
+ cf_make_wave_array.o cf_rebin_background.o \
+ cf_rebin_probability_array.o cf_optimal_extraction.o \
+ cf_write_extracted_spectrum.o cf_standard_or_optimal_extraction.o \
+ cf_init_support.o cf_modify_hist_pha.o \
+ cf_fes_proc_check.o cf_fes_proc_update.o
+
+all: ${OBJS}
+ ${CC} ${SHARED} -o ${LIBRARY}${SO} ${OBJS} \
+ ${FUSELIBDIR} ${FUSELIBS} ${LIBS} ${LDFLAGS}
+
+install: all
+ chmod g+w ${OBJS} ${LIBRARY}${SO}
+ /bin/cp -p ${LIBRARY}${SO} ${CALFUSEDIR}/lib/${LIBRARY}${SO}
+
+clean:
+ - /bin/rm -f *.o ${LIBRARY}${SO} ${LIBRARY}${O}
+
+distclean:
+ - /bin/rm -f *.o ${LIBRARY}${SO} ${LIBRARY}${O}
+ cd ../../lib; /bin/rm -f ${LIBRARY}${SO}
+
+calfits.o: calfits.c
+sgp4.o: sgp4.c
+eclipse.o: eclipse.c
+set_orbit_parms.o: set_orbit_parms.c
+saa.o: saa.c
+state_limb.o: state_limb.c
+state_geod.o: state_geod.c
+space_vel.o: space_vel.c
+helio_vel.o: helio_vel.c
+geod_mag.o: geod_mag.c
+pole_ang.o: pole_ang.c
+solar_ang.o: solar_ang.c
+lsrd_vel.o: lsrd_vel.c
+lsrk_vel.o: lsrk_vel.c
+month_day.o: month_day.c
+read_tle.o: read_tle.c
+
+cf_error_msg.o: cf_error_msg.c
+cf_cal_file.o: cf_cal_file.c
+cf_fes_proc_check.o: cf_fes_proc_check.c
+cf_fes_proc_update.o: cf_fes_proc_update.c
+cf_proc_check.o: cf_proc_check.c
+cf_proc_update.o: cf_proc_update.c
+cf_fuv_init.o: cf_fuv_init.c
+cf_velang.o: cf_velang.c
+cf_timestamp.o: cf_timestamp.c
+
+cf_header_io.o: cf_header_io.c
+cf_check_digitizer.o: cf_check_digitizer.c
+cf_nint.o: cf_nint.c
+cf_idf_io.o: cf_idf_io.c
+cf_ids_dead_time.o: cf_ids_dead_time.c
+cf_electronics_dead_time.o: cf_electronics_dead_time.c
+cf_fifo_dead_time.o: cf_fifo_dead_time.c
+cf_apply_dead_time.o: cf_apply_dead_time.c
+cf_thermal_distort.o: cf_thermal_distort.c
+cf_count_rate_y_distort.o: cf_count_rate_y_distort.c
+cf_time_xy_distort.o : cf_time_xy_distort.c
+cf_geometric_distort: cf_geometric_distort.c
+cf_pha_x_distort.o: cf_pha_x_distort.c
+cf_active_region.o: cf_active_region.c
+cf_find_spectra.o: cf_find_spectra.c
+cf_identify_channel.o: cf_identify_channel.c
+cf_init_support.o: cf_init_support.c
+cf_target_count_rate.o: cf_target_count_rate.c
+cf_calculate_ycent_motion.o: cf_calculate_ycent_motion.c
+cf_source_aper.o: cf_source_aper.c
+cf_grating_motion.o: cf_grating_motion.c
+cf_fpa_position.o: cf_fpa_position.c
+cf_read_fpa_pos.o: cf_read_fpa_pos.c
+cf_make_mask.o: cf_make_mask.c
+cf_mirror_motion.o: cf_mirror_motion.c
+cf_satellite_jitter.o: cf_satellite_jitter.c
+cf_calculate_y_centroid.o: cf_calculate_y_centroid.c
+cf_screen_airglow.o: cf_screen_airglow.c
+cf_screen_bad_pixels.o: cf_screen_bad_pixels.c
+cf_screen_jitter.o: cf_screen_jitter.c
+cf_screen_limb_angle.o: cf_screen_limb_angle.c
+cf_screen_saa.o: cf_screen_saa.c
+cf_screen_high_voltage.o: cf_screen_high_voltage.c
+cf_screen_burst.o: cf_screen_burst.c
+cf_set_user_gtis.o: cf_set_user_gtis.c
+cf_set_photon_flags.o: cf_set_photon_flags.c
+cf_set_good_time_intervals.o: cf_set_good_time_intervals.c
+cf_modify_hist_pha.o: cf_modify_hist_pha.c
+cf_modify_hist_times.o: cf_modify_hist_times.c
+cf_screen_pulse_height.o: cf_screen_pulse_height.c
+cf_convert_to_ergs.o: cf_convert_to_ergs.c
+cf_extraction_limits.o: cf_extraction_limits.c
+cf_astigmatism.o: cf_astigmatism.c
+cf_dispersion.o: cf_dispersion.c
+cf_doppler_and_heliocentric.o: cf_doppler_and_heliocentric.c
+cf_apply_filters.o: cf_apply_filters.c
+cf_scale_bkgd.o: cf_scale_bkgd.c
+cf_make_mask.o: cf_make_mask.c
+cf_make_wave_array.o: cf_make_wave_array.c
+cf_rebin_background.o: cf_rebin_background.c
+cf_rebin_probability_array.o: cf_rebin_probability_array.c
+cf_standard_or_optimal_extraction.o: cf_standard_or_optimal_extraction.c
+cf_optimal_extraction.o: cf_optimal_extraction.c
+cf_write_extracted_spectrum.o: cf_write_extracted_spectrum.c
+
diff --git a/src/libcf/Makefile.Linux64.orig b/src/libcf/Makefile.Linux64.orig
new file mode 100644
index 0000000..3b00b44
--- /dev/null
+++ b/src/libcf/Makefile.Linux64.orig
@@ -0,0 +1,151 @@
+LIBRARY= libcf
+
+CALFUSEDIR= ${PWD}/../..
+SHARED= -shared -fPIC
+
+# Symbols for include directories
+FUSEINCLDIR= -I${CALFUSEDIR}/include
+
+# Symbols used for compiling
+CC= cc
+OPT= -g -Wall -DCFORTRAN -Dg77Fortran -Df2cFortran
+CFLAGS= ${OPT} ${FUSEINCLDIR}
+
+FUSELIBDIR= -L${CALFUSEDIR}/lib
+FUSELIBS= -lsla
+LIBS= -lc -lm -lnsl -ldl -lgfortran -lcfitsio
+LDFLAGS=
+
+# Symbols used for creating shared libraries
+
+SO= .so
+
+OBJS= calfits.o sgp4.o eclipse.o saa.o \
+ state_limb.o state_geod.o space_vel.o helio_vel.o \
+ geod_mag.o pole_ang.o solar_ang.o lsrd_vel.o lsrk_vel.o \
+ month_day.o read_tle.o cf_velang.o \
+ set_orbit_parms.o cf_error_msg.o \
+ cf_cal_file.o cf_proc_check.o cf_proc_update.o \
+ cf_timestamp.o cf_fuv_init.o cf_header_io.o \
+ cf_check_digitizer.o cf_nint.o \
+ cf_idf_io.o cf_ids_dead_time.o cf_electronics_dead_time.o \
+ cf_fifo_dead_time.o cf_apply_dead_time.o \
+ cf_thermal_distort.o cf_count_rate_y_distort.o cf_time_xy_distort.o \
+ cf_geometric_distort.o cf_pha_x_distort.o \
+ cf_active_region.o cf_find_spectra.o cf_identify_channel.o \
+ cf_calculate_ycent_motion.o cf_source_aper.o\
+ cf_grating_motion.o cf_fpa_position.o cf_read_fpa_pos.o \
+ cf_make_mask.o cf_mirror_motion.o \
+ cf_satellite_jitter.o cf_calculate_y_centroid.o \
+ cf_target_count_rate.o \
+ cf_screen_jitter.o cf_screen_limb_angle.o cf_screen_saa.o \
+ cf_screen_high_voltage.o cf_screen_burst.o cf_screen_airglow.o \
+ cf_screen_bad_pixels.o cf_set_user_gtis.o \
+ cf_set_photon_flags.o cf_set_good_time_intervals.o \
+ cf_modify_hist_times.o cf_screen_pulse_height.o \
+ cf_convert_to_ergs.o cf_extraction_limits.o \
+ cf_astigmatism.o cf_dispersion.o cf_doppler_and_heliocentric.o \
+ cf_apply_filters.o cf_scale_bkgd.o \
+ cf_make_wave_array.o cf_rebin_background.o \
+ cf_rebin_probability_array.o cf_optimal_extraction.o \
+ cf_write_extracted_spectrum.o cf_standard_or_optimal_extraction.o \
+ cf_init_support.o cf_modify_hist_pha.o \
+ cf_fes_proc_check.o cf_fes_proc_update.o
+
+all: ${OBJS}
+ ${CC} ${SHARED} -o ${LIBRARY}${SO} ${OBJS} \
+ ${FUSELIBDIR} ${FUSELIBS} ${LIBS} ${LDFLAGS}
+
+install: all
+ chmod g+w ${OBJS} ${LIBRARY}${SO}
+ /bin/cp -p ${LIBRARY}${SO} ${CALFUSEDIR}/lib/${LIBRARY}${SO}
+
+clean:
+ - /bin/rm -f *.o ${LIBRARY}${SO} ${LIBRARY}${O}
+
+distclean:
+ - /bin/rm -f *.o ${LIBRARY}${SO} ${LIBRARY}${O}
+ cd ../../lib; /bin/rm -f ${LIBRARY}${SO}
+
+calfits.o: calfits.c
+sgp4.o: sgp4.c
+eclipse.o: eclipse.c
+set_orbit_parms.o: set_orbit_parms.c
+saa.o: saa.c
+state_limb.o: state_limb.c
+state_geod.o: state_geod.c
+space_vel.o: space_vel.c
+helio_vel.o: helio_vel.c
+geod_mag.o: geod_mag.c
+pole_ang.o: pole_ang.c
+solar_ang.o: solar_ang.c
+lsrd_vel.o: lsrd_vel.c
+lsrk_vel.o: lsrk_vel.c
+month_day.o: month_day.c
+read_tle.o: read_tle.c
+
+cf_error_msg.o: cf_error_msg.c
+cf_cal_file.o: cf_cal_file.c
+cf_fes_proc_check.o: cf_fes_proc_check.c
+cf_fes_proc_update.o: cf_fes_proc_update.c
+cf_proc_check.o: cf_proc_check.c
+cf_proc_update.o: cf_proc_update.c
+cf_fuv_init.o: cf_fuv_init.c
+cf_velang.o: cf_velang.c
+cf_timestamp.o: cf_timestamp.c
+
+cf_header_io.o: cf_header_io.c
+cf_check_digitizer.o: cf_check_digitizer.c
+cf_nint.o: cf_nint.c
+cf_idf_io.o: cf_idf_io.c
+cf_ids_dead_time.o: cf_ids_dead_time.c
+cf_electronics_dead_time.o: cf_electronics_dead_time.c
+cf_fifo_dead_time.o: cf_fifo_dead_time.c
+cf_apply_dead_time.o: cf_apply_dead_time.c
+cf_thermal_distort.o: cf_thermal_distort.c
+cf_count_rate_y_distort.o: cf_count_rate_y_distort.c
+cf_time_xy_distort.o : cf_time_xy_distort.c
+cf_geometric_distort: cf_geometric_distort.c
+cf_pha_x_distort.o: cf_pha_x_distort.c
+cf_active_region.o: cf_active_region.c
+cf_find_spectra.o: cf_find_spectra.c
+cf_identify_channel.o: cf_identify_channel.c
+cf_init_support.o: cf_init_support.c
+cf_target_count_rate.o: cf_target_count_rate.c
+cf_calculate_ycent_motion.o: cf_calculate_ycent_motion.c
+cf_source_aper.o: cf_source_aper.c
+cf_grating_motion.o: cf_grating_motion.c
+cf_fpa_position.o: cf_fpa_position.c
+cf_read_fpa_pos.o: cf_read_fpa_pos.c
+cf_make_mask.o: cf_make_mask.c
+cf_mirror_motion.o: cf_mirror_motion.c
+cf_satellite_jitter.o: cf_satellite_jitter.c
+cf_calculate_y_centroid.o: cf_calculate_y_centroid.c
+cf_screen_airglow.o: cf_screen_airglow.c
+cf_screen_bad_pixels.o: cf_screen_bad_pixels.c
+cf_screen_jitter.o: cf_screen_jitter.c
+cf_screen_limb_angle.o: cf_screen_limb_angle.c
+cf_screen_saa.o: cf_screen_saa.c
+cf_screen_high_voltage.o: cf_screen_high_voltage.c
+cf_screen_burst.o: cf_screen_burst.c
+cf_set_user_gtis.o: cf_set_user_gtis.c
+cf_set_photon_flags.o: cf_set_photon_flags.c
+cf_set_good_time_intervals.o: cf_set_good_time_intervals.c
+cf_modify_hist_pha.o: cf_modify_hist_pha.c
+cf_modify_hist_times.o: cf_modify_hist_times.c
+cf_screen_pulse_height.o: cf_screen_pulse_height.c
+cf_convert_to_ergs.o: cf_convert_to_ergs.c
+cf_extraction_limits.o: cf_extraction_limits.c
+cf_astigmatism.o: cf_astigmatism.c
+cf_dispersion.o: cf_dispersion.c
+cf_doppler_and_heliocentric.o: cf_doppler_and_heliocentric.c
+cf_apply_filters.o: cf_apply_filters.c
+cf_scale_bkgd.o: cf_scale_bkgd.c
+cf_make_mask.o: cf_make_mask.c
+cf_make_wave_array.o: cf_make_wave_array.c
+cf_rebin_background.o: cf_rebin_background.c
+cf_rebin_probability_array.o: cf_rebin_probability_array.c
+cf_standard_or_optimal_extraction.o: cf_standard_or_optimal_extraction.c
+cf_optimal_extraction.o: cf_optimal_extraction.c
+cf_write_extracted_spectrum.o: cf_write_extracted_spectrum.c
+
diff --git a/src/libcf/Makefile.MacOSX.orig b/src/libcf/Makefile.MacOSX.orig
new file mode 100644
index 0000000..eade1ea
--- /dev/null
+++ b/src/libcf/Makefile.MacOSX.orig
@@ -0,0 +1,152 @@
+LIBRARY= libcf
+
+CALFUSEDIR= ${PWD}/../..
+SHARED= -dynamiclib
+FITSVER= 2.470
+
+# Symbols for include directories
+FUSEINCLDIR= -I${CALFUSEDIR}/include
+
+# Symbols used for compiling
+CC= cc
+OPT= -O3 -Wall -DCFORTRAN -Dg77Fortran -Df2cFortran
+CFLAGS= ${OPT} ${FUSEINCLDIR}
+
+FUSELIBDIR= -L${CALFUSEDIR}/lib
+# FUSELIBS= -lcfitsio-${FITSVER} -lsla
+FUSELIBS= -lcfitsio -lsla
+LIBS= -lc -lm -ldl -L/sw/lib/ -lgfortran
+
+# Symbols used for creating shared libraries
+
+SO= .dylib
+
+OBJS= calfits.o sgp4.o eclipse.o saa.o \
+ state_limb.o state_geod.o space_vel.o helio_vel.o \
+ geod_mag.o pole_ang.o solar_ang.o lsrd_vel.o lsrk_vel.o \
+ month_day.o read_tle.o cf_velang.o \
+ set_orbit_parms.o cf_error_msg.o \
+ cf_cal_file.o cf_proc_check.o cf_proc_update.o \
+ cf_timestamp.o cf_fuv_init.o cf_header_io.o \
+ cf_check_digitizer.o cf_nint.o \
+ cf_idf_io.o cf_ids_dead_time.o cf_electronics_dead_time.o \
+ cf_fifo_dead_time.o cf_apply_dead_time.o \
+ cf_thermal_distort.o cf_count_rate_y_distort.o cf_time_xy_distort.o \
+ cf_geometric_distort.o cf_pha_x_distort.o \
+ cf_active_region.o cf_find_spectra.o cf_identify_channel.o \
+ cf_calculate_ycent_motion.o cf_source_aper.o\
+ cf_grating_motion.o cf_fpa_position.o cf_read_fpa_pos.o \
+ cf_make_mask.o cf_mirror_motion.o \
+ cf_satellite_jitter.o cf_calculate_y_centroid.o \
+ cf_target_count_rate.o \
+ cf_screen_jitter.o cf_screen_limb_angle.o cf_screen_saa.o \
+ cf_screen_high_voltage.o cf_screen_burst.o cf_screen_airglow.o \
+ cf_screen_bad_pixels.o cf_set_user_gtis.o \
+ cf_set_photon_flags.o cf_set_good_time_intervals.o \
+ cf_modify_hist_times.o cf_screen_pulse_height.o \
+ cf_convert_to_ergs.o cf_extraction_limits.o \
+ cf_astigmatism.o cf_dispersion.o cf_doppler_and_heliocentric.o \
+ cf_apply_filters.o cf_scale_bkgd.o \
+ cf_make_wave_array.o cf_rebin_background.o \
+ cf_rebin_probability_array.o cf_optimal_extraction.o \
+ cf_write_extracted_spectrum.o cf_standard_or_optimal_extraction.o \
+ cf_init_support.o cf_modify_hist_pha.o \
+ cf_fes_proc_check.o cf_fes_proc_update.o
+
+all: ${OBJS}
+ ${CC} ${SHARED} -o ${LIBRARY}${SO} ${OBJS} \
+ ${FUSELIBDIR} ${FUSELIBS} ${LIBS}
+
+install: all
+ chmod g+w ${OBJS} ${LIBRARY}${SO}
+ /bin/cp -p ${LIBRARY}${SO} ${CALFUSEDIR}/lib/${LIBRARY}${SO}
+
+clean:
+ - /bin/rm -f *.o ${LIBRARY}${SO} ${LIBRARY}${O}
+
+distclean:
+ - /bin/rm -f *.o ${LIBRARY}${SO} ${LIBRARY}${O}
+ cd ../../lib; /bin/rm -f ${LIBRARY}${SO}
+
+calfits.o: calfits.c
+sgp4.o: sgp4.c
+eclipse.o: eclipse.c
+set_orbit_parms.o: set_orbit_parms.c
+saa.o: saa.c
+state_limb.o: state_limb.c
+state_geod.o: state_geod.c
+space_vel.o: space_vel.c
+helio_vel.o: helio_vel.c
+geod_mag.o: geod_mag.c
+pole_ang.o: pole_ang.c
+solar_ang.o: solar_ang.c
+lsrd_vel.o: lsrd_vel.c
+lsrk_vel.o: lsrk_vel.c
+month_day.o: month_day.c
+read_tle.o: read_tle.c
+
+cf_error_msg.o: cf_error_msg.c
+cf_cal_file.o: cf_cal_file.c
+cf_fes_proc_check.o: cf_fes_proc_check.c
+cf_fes_proc_update.o: cf_fes_proc_update.c
+cf_proc_check.o: cf_proc_check.c
+cf_proc_update.o: cf_proc_update.c
+cf_fuv_init.o: cf_fuv_init.c
+cf_velang.o: cf_velang.c
+cf_timestamp.o: cf_timestamp.c
+
+cf_header_io.o: cf_header_io.c
+cf_check_digitizer.o: cf_check_digitizer.c
+cf_nint.o: cf_nint.c
+cf_idf_io.o: cf_idf_io.c
+cf_ids_dead_time.o: cf_ids_dead_time.c
+cf_electronics_dead_time.o: cf_electronics_dead_time.c
+cf_apply_dead_time.o: cf_apply_dead_time.c
+cf_fifo_dead_time.o: cf_fifo_dead_time.c
+cf_thermal_distort.o: cf_thermal_distort.c
+cf_count_rate_y_distort.o: cf_count_rate_y_distort.c
+cf_time_xy_distort.o : cf_time_xy_distort.c
+cf_geometric_distort: cf_geometric_distort.c
+cf_pha_x_distort.o: cf_pha_x_distort.c
+cf_active_region.o: cf_active_region.c
+cf_find_spectra.o: cf_find_spectra.c
+cf_identify_channel.o: cf_identify_channel.c
+cf_init_support.o: cf_init_support.c
+cf_target_count_rate.o: cf_target_count_rate.c
+cf_calculate_ycent_motion.o: cf_calculate_ycent_motion.c
+cf_source_aper.o: cf_source_aper.c
+cf_grating_motion.o: cf_grating_motion.c
+cf_fpa_position.o: cf_fpa_position.c
+cf_read_fpa_pos.o: cf_read_fpa_pos.c
+cf_make_mask.o: cf_make_mask.c
+cf_mirror_motion.o: cf_mirror_motion.c
+cf_satellite_jitter.o: cf_satellite_jitter.c
+cf_calculate_y_centroid.o: cf_calculate_y_centroid.c
+cf_screen_airglow.o: cf_screen_airglow.c
+cf_screen_bad_pixels.o: cf_screen_bad_pixels.c
+cf_screen_jitter.o: cf_screen_jitter.c
+cf_screen_limb_angle.o: cf_screen_limb_angle.c
+cf_screen_saa.o: cf_screen_saa.c
+cf_screen_high_voltage.o: cf_screen_high_voltage.c
+cf_screen_burst.o: cf_screen_burst.c
+cf_set_user_gtis.o: cf_set_user_gtis.c
+cf_set_photon_flags.o: cf_set_photon_flags.c
+cf_set_good_time_intervals.o: cf_set_good_time_intervals.c
+cf_modify_hist_pha.o: cf_modify_hist_pha.c
+cf_modify_hist_times.o: cf_modify_hist_times.c
+cf_screen_pulse_height.o: cf_screen_pulse_height.c
+cf_convert_to_ergs.o: cf_convert_to_ergs.c
+cf_extraction_limits.o: cf_extraction_limits.c
+cf_astigmatism.o: cf_astigmatism.c
+cf_dispersion.o: cf_dispersion.c
+cf_doppler_and_heliocentric.o: cf_doppler_and_heliocentric.c
+cf_apply_filters.o: cf_apply_filters.c
+cf_scale_bkgd.o: cf_scale_bkgd.c
+cf_make_mask.o: cf_make_mask.c
+cf_make_wave_array.o: cf_make_wave_array.c
+cf_rebin_background.o: cf_rebin_background.c
+cf_rebin_probability_array.o: cf_rebin_probability_array.c
+cf_standard_or_optimal_extraction.o: cf_standard_or_optimal_extraction.c
+cf_optimal_extraction.o: cf_optimal_extraction.c
+cf_write_extracted_spectrum.o: cf_write_extracted_spectrum.c
+
diff --git a/src/libcf/Makefile.Solaris.orig b/src/libcf/Makefile.Solaris.orig
new file mode 100644
index 0000000..f817dfe
--- /dev/null
+++ b/src/libcf/Makefile.Solaris.orig
@@ -0,0 +1,152 @@
+LIBRARY= libcf
+
+CALFUSEDIR= ${PWD}/../..
+SHARED= -G
+FITSVER= 2.470
+
+# Symbols for include directories
+FUSEINCLDIR= -I${CALFUSEDIR}/include
+
+# Symbols used for compiling
+CC= cc
+# OPT= -p -v -xO2 -xdepend -xchip=ultra -xarch=generic
+OPT= -O -DCFORTRAN -KPIC
+CFLAGS= ${OPT} ${FUSEINCLDIR}
+
+FUSELIBDIR= -L${CALFUSEDIR}/lib
+FUSELIBS= -lcfitsio-${FITSVER} -lsla
+LIBS= -lc -lm -lnsl -ldl -lsocket
+LDFLAGS=
+
+# Symbols used for creating shared libraries
+
+SO= .so
+
+OBJS= calfits.o sgp4.o eclipse.o saa.o \
+ state_limb.o state_geod.o space_vel.o helio_vel.o \
+ geod_mag.o pole_ang.o solar_ang.o lsrd_vel.o lsrk_vel.o \
+ month_day.o read_tle.o cf_velang.o \
+ set_orbit_parms.o cf_error_msg.o \
+ cf_cal_file.o cf_proc_check.o cf_proc_update.o \
+ cf_timestamp.o cf_fuv_init.o cf_header_io.o \
+ cf_check_digitizer.o cf_nint.o \
+ cf_idf_io.o cf_ids_dead_time.o cf_electronics_dead_time.o \
+ cf_fifo_dead_time.o cf_apply_dead_time.o \
+ cf_thermal_distort.o cf_count_rate_y_distort.o cf_time_xy_distort.o \
+ cf_geometric_distort.o cf_pha_x_distort.o \
+ cf_active_region.o cf_find_spectra.o cf_identify_channel.o \
+ cf_calculate_ycent_motion.o cf_source_aper.o\
+ cf_grating_motion.o cf_fpa_position.o cf_read_fpa_pos.o \
+ cf_make_mask.o cf_mirror_motion.o \
+ cf_satellite_jitter.o cf_calculate_y_centroid.o \
+ cf_target_count_rate.o \
+ cf_screen_jitter.o cf_screen_limb_angle.o cf_screen_saa.o \
+ cf_screen_high_voltage.o cf_screen_burst.o cf_screen_airglow.o \
+ cf_screen_bad_pixels.o cf_set_user_gtis.o \
+ cf_set_photon_flags.o cf_set_good_time_intervals.o \
+ cf_modify_hist_times.o cf_screen_pulse_height.o \
+ cf_convert_to_ergs.o cf_extraction_limits.o \
+ cf_astigmatism.o cf_dispersion.o cf_doppler_and_heliocentric.o \
+ cf_apply_filters.o cf_scale_bkgd.o \
+ cf_make_wave_array.o cf_rebin_background.o \
+ cf_rebin_probability_array.o cf_optimal_extraction.o \
+ cf_write_extracted_spectrum.o cf_standard_or_optimal_extraction.o \
+ cf_init_support.o cf_modify_hist_pha.o \
+ cf_fes_proc_check.o cf_fes_proc_update.o
+
+all: ${OBJS}
+ ${CC} ${SHARED} -o ${LIBRARY}${SO} ${OBJS} \
+ ${FUSELIBDIR} ${FUSELIBS} ${LIBS} ${LDFLAGS}
+
+install: all
+ chmod g+w ${OBJS} ${LIBRARY}${SO}
+ /bin/cp -p ${LIBRARY}${SO} ${CALFUSEDIR}/lib/${LIBRARY}${SO}
+
+clean:
+ - /bin/rm -f *.o ${LIBRARY}${SO} ${LIBRARY}${O}
+
+distclean:
+ - /bin/rm -f *.o ${LIBRARY}${SO} ${LIBRARY}${O}
+ cd ../../lib; /bin/rm -f ${LIBRARY}${SO}
+
+calfits.o: calfits.c
+sgp4.o: sgp4.c
+eclipse.o: eclipse.c
+set_orbit_parms.o: set_orbit_parms.c
+saa.o: saa.c
+state_limb.o: state_limb.c
+state_geod.o: state_geod.c
+space_vel.o: space_vel.c
+helio_vel.o: helio_vel.c
+geod_mag.o: geod_mag.c
+pole_ang.o: pole_ang.c
+solar_ang.o: solar_ang.c
+lsrd_vel.o: lsrd_vel.c
+lsrk_vel.o: lsrk_vel.c
+month_day.o: month_day.c
+read_tle.o: read_tle.c
+
+cf_error_msg.o: cf_error_msg.c
+cf_cal_file.o: cf_cal_file.c
+cf_fes_proc_check.o: cf_fes_proc_check.c
+cf_fes_proc_update.o: cf_fes_proc_update.c
+cf_proc_check.o: cf_proc_check.c
+cf_proc_update.o: cf_proc_update.c
+cf_fuv_init.o: cf_fuv_init.c
+cf_velang.o: cf_velang.c
+cf_timestamp.o: cf_timestamp.c
+
+cf_header_io.o: cf_header_io.c
+cf_check_digitizer.o: cf_check_digitizer.c
+cf_nint.o: cf_nint.c
+cf_idf_io.o: cf_idf_io.c
+cf_ids_dead_time.o: cf_ids_dead_time.c
+cf_electronics_dead_time.o: cf_electronics_dead_time.c
+cf_apply_dead_time.o: cf_apply_dead_time.c
+cf_fifo_dead_time.o: cf_fifo_dead_time.c
+cf_thermal_distort.o: cf_thermal_distort.c
+cf_count_rate_y_distort.o: cf_count_rate_y_distort.c
+cf_time_xy_distort.o : cf_time_xy_distort.c
+cf_geometric_distort: cf_geometric_distort.c
+cf_pha_x_distort.o: cf_pha_x_distort.c
+cf_active_region.o: cf_active_region.c
+cf_find_spectra.o: cf_find_spectra.c
+cf_identify_channel.o: cf_identify_channel.c
+cf_init_support.o: cf_init_support.c
+cf_target_count_rate.o: cf_target_count_rate.c
+cf_calculate_ycent_motion.o: cf_calculate_ycent_motion.c
+cf_source_aper.o: cf_source_aper.c
+cf_grating_motion.o: cf_grating_motion.c
+cf_fpa_position.o: cf_fpa_position.c
+cf_read_fpa_pos.o: cf_read_fpa_pos.c
+cf_make_mask.o: cf_make_mask.c
+cf_mirror_motion.o: cf_mirror_motion.c
+cf_satellite_jitter.o: cf_satellite_jitter.c
+cf_calculate_y_centroid.o: cf_calculate_y_centroid.c
+cf_screen_airglow.o: cf_screen_airglow.c
+cf_screen_bad_pixels.o: cf_screen_bad_pixels.c
+cf_screen_jitter.o: cf_screen_jitter.c
+cf_screen_limb_angle.o: cf_screen_limb_angle.c
+cf_screen_saa.o: cf_screen_saa.c
+cf_screen_high_voltage.o: cf_screen_high_voltage.c
+cf_screen_burst.o: cf_screen_burst.c
+cf_set_user_gtis.o: cf_set_user_gtis.c
+cf_set_photon_flags.o: cf_set_photon_flags.c
+cf_set_good_time_intervals.o: cf_set_good_time_intervals.c
+cf_modify_hist_pha.o: cf_modify_hist_pha.c
+cf_modify_hist_times.o: cf_modify_hist_times.c
+cf_screen_pulse_height.o: cf_screen_pulse_height.c
+cf_convert_to_ergs.o: cf_convert_to_ergs.c
+cf_extraction_limits.o: cf_extraction_limits.c
+cf_astigmatism.o: cf_astigmatism.c
+cf_dispersion.o: cf_dispersion.c
+cf_doppler_and_heliocentric.o: cf_doppler_and_heliocentric.c
+cf_apply_filters.o: cf_apply_filters.c
+cf_scale_bkgd.o: cf_scale_bkgd.c
+cf_make_mask.o: cf_make_mask.c
+cf_make_wave_array.o: cf_make_wave_array.c
+cf_rebin_background.o: cf_rebin_background.c
+cf_rebin_probability_array.o: cf_rebin_probability_array.c
+cf_standard_or_optimal_extraction.o: cf_standard_or_optimal_extraction.c
+cf_optimal_extraction.o: cf_optimal_extraction.c
+cf_write_extracted_spectrum.o: cf_write_extracted_spectrum.c
diff --git a/src/libcf/Makefile.orig.orig b/src/libcf/Makefile.orig.orig
new file mode 100644
index 0000000..f817dfe
--- /dev/null
+++ b/src/libcf/Makefile.orig.orig
@@ -0,0 +1,152 @@
+LIBRARY= libcf
+
+CALFUSEDIR= ${PWD}/../..
+SHARED= -G
+FITSVER= 2.470
+
+# Symbols for include directories
+FUSEINCLDIR= -I${CALFUSEDIR}/include
+
+# Symbols used for compiling
+CC= cc
+# OPT= -p -v -xO2 -xdepend -xchip=ultra -xarch=generic
+OPT= -O -DCFORTRAN -KPIC
+CFLAGS= ${OPT} ${FUSEINCLDIR}
+
+FUSELIBDIR= -L${CALFUSEDIR}/lib
+FUSELIBS= -lcfitsio-${FITSVER} -lsla
+LIBS= -lc -lm -lnsl -ldl -lsocket
+LDFLAGS=
+
+# Symbols used for creating shared libraries
+
+SO= .so
+
+OBJS= calfits.o sgp4.o eclipse.o saa.o \
+ state_limb.o state_geod.o space_vel.o helio_vel.o \
+ geod_mag.o pole_ang.o solar_ang.o lsrd_vel.o lsrk_vel.o \
+ month_day.o read_tle.o cf_velang.o \
+ set_orbit_parms.o cf_error_msg.o \
+ cf_cal_file.o cf_proc_check.o cf_proc_update.o \
+ cf_timestamp.o cf_fuv_init.o cf_header_io.o \
+ cf_check_digitizer.o cf_nint.o \
+ cf_idf_io.o cf_ids_dead_time.o cf_electronics_dead_time.o \
+ cf_fifo_dead_time.o cf_apply_dead_time.o \
+ cf_thermal_distort.o cf_count_rate_y_distort.o cf_time_xy_distort.o \
+ cf_geometric_distort.o cf_pha_x_distort.o \
+ cf_active_region.o cf_find_spectra.o cf_identify_channel.o \
+ cf_calculate_ycent_motion.o cf_source_aper.o\
+ cf_grating_motion.o cf_fpa_position.o cf_read_fpa_pos.o \
+ cf_make_mask.o cf_mirror_motion.o \
+ cf_satellite_jitter.o cf_calculate_y_centroid.o \
+ cf_target_count_rate.o \
+ cf_screen_jitter.o cf_screen_limb_angle.o cf_screen_saa.o \
+ cf_screen_high_voltage.o cf_screen_burst.o cf_screen_airglow.o \
+ cf_screen_bad_pixels.o cf_set_user_gtis.o \
+ cf_set_photon_flags.o cf_set_good_time_intervals.o \
+ cf_modify_hist_times.o cf_screen_pulse_height.o \
+ cf_convert_to_ergs.o cf_extraction_limits.o \
+ cf_astigmatism.o cf_dispersion.o cf_doppler_and_heliocentric.o \
+ cf_apply_filters.o cf_scale_bkgd.o \
+ cf_make_wave_array.o cf_rebin_background.o \
+ cf_rebin_probability_array.o cf_optimal_extraction.o \
+ cf_write_extracted_spectrum.o cf_standard_or_optimal_extraction.o \
+ cf_init_support.o cf_modify_hist_pha.o \
+ cf_fes_proc_check.o cf_fes_proc_update.o
+
+all: ${OBJS}
+ ${CC} ${SHARED} -o ${LIBRARY}${SO} ${OBJS} \
+ ${FUSELIBDIR} ${FUSELIBS} ${LIBS} ${LDFLAGS}
+
+install: all
+ chmod g+w ${OBJS} ${LIBRARY}${SO}
+ /bin/cp -p ${LIBRARY}${SO} ${CALFUSEDIR}/lib/${LIBRARY}${SO}
+
+clean:
+ - /bin/rm -f *.o ${LIBRARY}${SO} ${LIBRARY}${O}
+
+distclean:
+ - /bin/rm -f *.o ${LIBRARY}${SO} ${LIBRARY}${O}
+ cd ../../lib; /bin/rm -f ${LIBRARY}${SO}
+
+calfits.o: calfits.c
+sgp4.o: sgp4.c
+eclipse.o: eclipse.c
+set_orbit_parms.o: set_orbit_parms.c
+saa.o: saa.c
+state_limb.o: state_limb.c
+state_geod.o: state_geod.c
+space_vel.o: space_vel.c
+helio_vel.o: helio_vel.c
+geod_mag.o: geod_mag.c
+pole_ang.o: pole_ang.c
+solar_ang.o: solar_ang.c
+lsrd_vel.o: lsrd_vel.c
+lsrk_vel.o: lsrk_vel.c
+month_day.o: month_day.c
+read_tle.o: read_tle.c
+
+cf_error_msg.o: cf_error_msg.c
+cf_cal_file.o: cf_cal_file.c
+cf_fes_proc_check.o: cf_fes_proc_check.c
+cf_fes_proc_update.o: cf_fes_proc_update.c
+cf_proc_check.o: cf_proc_check.c
+cf_proc_update.o: cf_proc_update.c
+cf_fuv_init.o: cf_fuv_init.c
+cf_velang.o: cf_velang.c
+cf_timestamp.o: cf_timestamp.c
+
+cf_header_io.o: cf_header_io.c
+cf_check_digitizer.o: cf_check_digitizer.c
+cf_nint.o: cf_nint.c
+cf_idf_io.o: cf_idf_io.c
+cf_ids_dead_time.o: cf_ids_dead_time.c
+cf_electronics_dead_time.o: cf_electronics_dead_time.c
+cf_apply_dead_time.o: cf_apply_dead_time.c
+cf_fifo_dead_time.o: cf_fifo_dead_time.c
+cf_thermal_distort.o: cf_thermal_distort.c
+cf_count_rate_y_distort.o: cf_count_rate_y_distort.c
+cf_time_xy_distort.o : cf_time_xy_distort.c
+cf_geometric_distort: cf_geometric_distort.c
+cf_pha_x_distort.o: cf_pha_x_distort.c
+cf_active_region.o: cf_active_region.c
+cf_find_spectra.o: cf_find_spectra.c
+cf_identify_channel.o: cf_identify_channel.c
+cf_init_support.o: cf_init_support.c
+cf_target_count_rate.o: cf_target_count_rate.c
+cf_calculate_ycent_motion.o: cf_calculate_ycent_motion.c
+cf_source_aper.o: cf_source_aper.c
+cf_grating_motion.o: cf_grating_motion.c
+cf_fpa_position.o: cf_fpa_position.c
+cf_read_fpa_pos.o: cf_read_fpa_pos.c
+cf_make_mask.o: cf_make_mask.c
+cf_mirror_motion.o: cf_mirror_motion.c
+cf_satellite_jitter.o: cf_satellite_jitter.c
+cf_calculate_y_centroid.o: cf_calculate_y_centroid.c
+cf_screen_airglow.o: cf_screen_airglow.c
+cf_screen_bad_pixels.o: cf_screen_bad_pixels.c
+cf_screen_jitter.o: cf_screen_jitter.c
+cf_screen_limb_angle.o: cf_screen_limb_angle.c
+cf_screen_saa.o: cf_screen_saa.c
+cf_screen_high_voltage.o: cf_screen_high_voltage.c
+cf_screen_burst.o: cf_screen_burst.c
+cf_set_user_gtis.o: cf_set_user_gtis.c
+cf_set_photon_flags.o: cf_set_photon_flags.c
+cf_set_good_time_intervals.o: cf_set_good_time_intervals.c
+cf_modify_hist_pha.o: cf_modify_hist_pha.c
+cf_modify_hist_times.o: cf_modify_hist_times.c
+cf_screen_pulse_height.o: cf_screen_pulse_height.c
+cf_convert_to_ergs.o: cf_convert_to_ergs.c
+cf_extraction_limits.o: cf_extraction_limits.c
+cf_astigmatism.o: cf_astigmatism.c
+cf_dispersion.o: cf_dispersion.c
+cf_doppler_and_heliocentric.o: cf_doppler_and_heliocentric.c
+cf_apply_filters.o: cf_apply_filters.c
+cf_scale_bkgd.o: cf_scale_bkgd.c
+cf_make_mask.o: cf_make_mask.c
+cf_make_wave_array.o: cf_make_wave_array.c
+cf_rebin_background.o: cf_rebin_background.c
+cf_rebin_probability_array.o: cf_rebin_probability_array.c
+cf_standard_or_optimal_extraction.o: cf_standard_or_optimal_extraction.c
+cf_optimal_extraction.o: cf_optimal_extraction.c
+cf_write_extracted_spectrum.o: cf_write_extracted_spectrum.c
diff --git a/src/libcf/calfits.c b/src/libcf/calfits.c
new file mode 100644
index 0000000..29b3a08
--- /dev/null
+++ b/src/libcf/calfits.c
@@ -0,0 +1,658 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Description: Wrappers for cfitsio routines which includes error checking.
+ *
+ * History: 04/14/99 peb Finished work on subset of routines
+ * from cfitsio v2.031.
+ * 04/15/99 emm Replaced #include FITSIO.h with
+ * #include calfuse.h
+ * 04/15/99 barrett Added FITS_get_rowsize.
+ * 04/16/99 barrett Added FITS_get_hdu_num.
+ * 04/20/99 barrett Added FITS_insert_key_[str, log, lng,
+ * fixflt, flt, fixdbl, dbl, fixcmp,
+ * cmp, fixdblcmp, dblcmp]
+ * 04/23/99 emurphy Deleted error checking from
+ * cf_get_hdu_num
+ * 04/04/01 kruk Force casesen to FALSE in FITS_get_colnum
+ * 12/18/03 bjg Change calfusettag.h to calfuse.h
+ *
+ ****************************************************************************/
+
+#include "fitsio.h"
+#include "calfuse.h"
+
+int FITS_open_file(fitsfile **fptr, const char *filename, int iomode,
+ int *status)
+{
+ ffopen(fptr, filename, iomode, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_reopen_file(fitsfile *openfptr, fitsfile **newfptr, int *status)
+{
+ ffreopen(openfptr, newfptr, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_create_file(fitsfile **fptr, const char *filename, int *status)
+{
+ ffinit(fptr, filename, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_flush_file(fitsfile *fptr, int *status)
+{
+ ffflus(fptr, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_close_file(fitsfile *fptr, int *status)
+{
+ ffclos(fptr, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_delete_file(fitsfile *fptr, int *status)
+{
+ ffdelt(fptr, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_file_name(fitsfile *fptr, char *filename, int *status)
+{
+ ffflnm(fptr, filename, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_file_mode(fitsfile *fptr, int *filemode, int *status)
+{
+ ffflmd(fptr, filemode, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_write_record(fitsfile *fptr, const char *card, int *status)
+{
+ ffprec(fptr, card, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_write_key(fitsfile *fptr, int datatype, char *keyname, void *value,
+ char *comm, int *status)
+{
+ ffpky(fptr, datatype, keyname, value,
+ comm, status);
+ if (*status) fprintf(stderr, "Error writing keyword %16.16s\n",keyname);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_write_comment(fitsfile *fptr, const char *comm, int *status)
+{
+ ffpcom(fptr, comm, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_write_history(fitsfile *fptr, const char *history, int *status)
+{
+ ffphis(fptr, history, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_write_date(fitsfile *fptr, int *status)
+{
+ ffpdat(fptr, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_copy_key(fitsfile *infptr,fitsfile *outfptr,int incol,int outcol,
+ char *rootname, int *status)
+{
+ ffcpky(infptr,outfptr,incol,outcol,
+ rootname, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_write_imghdr(fitsfile *fptr, int bitpix, int naxis, long naxes[],
+ int *status)
+{
+ ffphps( fptr, bitpix, naxis, naxes, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_write_grphdr(fitsfile *fptr, int simple, int bitpix, int naxis,
+ long naxes[], long pcount, long gcount, int extend,
+ int *status)
+{
+ ffphpr( fptr, simple, bitpix, naxis, naxes,
+ pcount, gcount, extend, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_write_btblhdr(fitsfile *fptr, long naxis2, int tfields, char **ttype,
+ char **tform, char **tunit, char *extname, long pcount,
+ int *status)
+{
+ ffphbn(fptr, naxis2, tfields, ttype,
+ tform, tunit, extname, pcount, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_get_hdrpos(fitsfile *fptr, int *nexist, int *position, int *status)
+{
+ ffghps(fptr, nexist, position, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_read_record(fitsfile *fptr, int nrec, char *card, int *status)
+{
+ ffgrec(fptr, nrec, card, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_read_card(fitsfile *fptr, char *keyname, char *card, int *status)
+{
+ ffgcrd(fptr, keyname, card, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_read_key(fitsfile *fptr, int datatype, char *keyname, void *value,
+ char *comm, int *status)
+{
+ ffgky( fptr, datatype, keyname, value,
+ comm, status);
+ if (*status) fprintf(stderr, "Error reading keyword %16.16s\n",keyname);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_read_imghdr(fitsfile *fptr, int maxdim, int *simple, int *bitpix,
+ int *naxis, long naxes[], long *pcount, long *gcount,
+ int *extend, int *status)
+{
+ ffghpr(fptr, maxdim, simple, bitpix, naxis,
+ naxes, pcount, gcount, extend, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_read_btblhdr(fitsfile *fptr, int maxfield, long *naxis2, int *tfields,
+ char **ttype, char **tform, char **tunit, char *extname,
+ long *pcount, int *status)
+{
+ ffghbn(fptr, maxfield, naxis2, tfields,
+ ttype, tform, tunit, extname,
+ pcount, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_update_card(fitsfile *fptr, char *keyname, char *card, int *status)
+{
+ ffucrd(fptr, keyname, card, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_update_key(fitsfile *fptr, int datatype, char *keyname, void *value,
+ char *comm, int *status)
+{
+ ffuky(fptr, datatype, keyname, value,
+ comm, status);
+ if (*status) fprintf(stderr, "Error updating keyword %16.16s\n",keyname);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_modify_record(fitsfile *fptr, int nkey, char *card, int *status)
+{
+ ffmrec(fptr, nkey, card, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_modify_card(fitsfile *fptr, char *keyname, char *card, int *status)
+{
+ ffmcrd(fptr, keyname, card, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_modify_comment(fitsfile *fptr, char *keyname, char *comm, int *status)
+{
+ ffmcom(fptr, keyname, comm, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_record(fitsfile *fptr, int nkey, char *card, int *status)
+{
+ ffirec(fptr, nkey, card, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_key_str(fitsfile *fptr, char *keyname, char *value,
+ char *comment, int *status)
+{
+ ffikys(fptr, keyname, value, comment, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_key_log(fitsfile *fptr, char *keyname, int value,
+ char *comment, int *status)
+{
+ ffikyl(fptr, keyname, value, comment, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_key_lng(fitsfile *fptr, char *keyname, long value,
+ char *comment, int *status)
+{
+ ffikyj(fptr, keyname, value, comment, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_key_flt(fitsfile *fptr, char *keyname, float value,
+ int decimals, char *comment, int *status)
+{
+ ffikye(fptr, keyname, value, decimals, comment, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_key_fixflt(fitsfile *fptr, char *keyname, float value,
+ int decimals, char *comment, int *status)
+{
+ ffikyf(fptr, keyname, value, decimals, comment, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_key_dbl(fitsfile *fptr, char *keyname, double value,
+ int decimals, char *comment, int *status)
+{
+ ffikyd(fptr, keyname, value, decimals, comment, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_key_fixdbl(fitsfile *fptr, char *keyname, double value,
+ int decimals, char *comment, int *status)
+{
+ ffikyg(fptr, keyname, value, decimals, comment, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_key_cmp(fitsfile *fptr, char *keyname, float *value,
+ int decimals, char *comment, int *status)
+{
+ ffikyc(fptr, keyname, value, decimals, comment, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_key_dblcmp(fitsfile *fptr, char *keyname, double *value,
+ int decimals, char *comment, int *status)
+{
+ ffikym(fptr, keyname, value, decimals, comment, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_key_fixcmp(fitsfile *fptr, char *keyname, float *value,
+ int decimals, char *comment, int *status)
+{
+ ffikfc(fptr, keyname, value, decimals, comment, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_key_fixdblcmp(fitsfile *fptr, char *keyname, double *value,
+ int decimals, char *comment, int *status)
+{
+ ffikfm(fptr, keyname, value, decimals, comment, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_key_null(fitsfile *fptr, char *keyname, char *comment,
+ int *status)
+{
+ ffikyu(fptr, keyname, comment, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_delete_key(fitsfile *fptr, char *keyname, int *status)
+{
+ ffdkey(fptr, keyname, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_delete_record(fitsfile *fptr, int keypos, int *status)
+{
+ ffdrec(fptr, keypos, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_get_hdu_num(fitsfile *fptr, int *hdunum)
+{
+ /* This routine returns the HDU number instead of status. Therefore,
+ * it should not use any error checking. */
+ int status;
+ status = ffghdn(fptr, hdunum);
+ return status;
+}
+
+int FITS_get_hdu_type(fitsfile *fptr, int *exttype, int *status)
+{
+ ffghdt(fptr, exttype, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_movabs_hdu(fitsfile *fptr, int hdunum, int *exttype, int *status)
+{
+ ffmahd(fptr, hdunum, exttype, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_movrel_hdu(fitsfile *fptr, int hdumov, int *exttype, int *status)
+{
+ ffmrhd(fptr, hdumov, exttype, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_movnam_hdu(fitsfile *fptr, int exttype, char *hduname, int hduvers,
+ int *status)
+{
+ ffmnhd(fptr, exttype, hduname, hduvers,
+ status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_get_num_hdus(fitsfile *fptr, int *nhdu, int *status)
+{
+ ffthdu(fptr, nhdu, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_create_img(fitsfile *fptr, int bitpix, int naxis, long *naxes,
+ int *status)
+{
+ ffcrim(fptr, bitpix, naxis, naxes, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_create_tbl(fitsfile *fptr, int tbltype, long naxis2, int tfields,
+ char **ttype, char **tform, char **tunit, char *extname,
+ int *status)
+{
+ ffcrtb(fptr, tbltype, naxis2, tfields, ttype,
+ tform, tunit, extname, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_create_hdu(fitsfile *fptr, int *status)
+{
+ ffcrhd(fptr, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_img(fitsfile *fptr, int bitpix, int naxis, long *naxes,
+ int *status)
+{
+ ffiimg(fptr, bitpix, naxis, naxes, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_atbl(fitsfile *fptr, long naxis1, long naxis2, int tfields,
+ char **ttype, long *tbcol, char **tform, char **tunit,
+ char *extname, int *status)
+{
+ ffitab(fptr, naxis1, naxis2, tfields, ttype,
+ tbcol, tform, tunit, extname, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_btbl(fitsfile *fptr,long naxis2, int tfields, char **ttype,
+ char **tform, char **tunit, char *extname, long pcount,
+ int *status)
+{
+ ffibin(fptr,naxis2, tfields, ttype, tform,
+ tunit, extname, pcount, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_delete_hdu(fitsfile *fptr, int *hdutype, int *status)
+{
+ ffdhdu(fptr, hdutype, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_copy_hdu(fitsfile *infptr, fitsfile *outfptr, int morekeys,
+ int *status)
+{
+ ffcopy(infptr, outfptr, morekeys, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_copy_header(fitsfile *infptr, fitsfile *outfptr, int *status)
+{
+ ffcphd(infptr, outfptr, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_copy_data(fitsfile *infptr, fitsfile *outfptr, int *status)
+{
+ ffcpdt(infptr, outfptr, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_get_colnum(fitsfile *fptr, int casesen, char *templt, int *colnum,
+ int *status)
+{
+ /* force casesen to FALSE, in accordance with the FITS standard */
+ casesen = FALSE;
+ ffgcno(fptr, casesen, templt, colnum,
+ status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_get_colname(fitsfile *fptr, int casesen, char *templt, char *colname,
+ int *colnum, int *status)
+{
+ ffgcnn(fptr, casesen, templt, colname,
+ colnum, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_get_coltype(fitsfile *fptr, int colnum, int *typecode, long *repeat,
+ long *width, int *status)
+{
+ ffgtcl(fptr, colnum, typecode, repeat,
+ width, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_get_num_rows(fitsfile *fptr, long *nrows, int *status)
+{
+ ffgnrw(fptr, nrows, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_get_rowsize(fitsfile *fptr, long *nrows, int *status)
+{
+ ffgrsz(fptr, nrows, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_get_num_cols(fitsfile *fptr, int *ncols, int *status)
+{
+ ffgncl(fptr, ncols, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_get_acolparms(fitsfile *fptr, int colnum, char *ttype, long *tbcol,
+ char *tunit, char *tform, double *tscal, double *tzero,
+ char *tnull, char *tdisp, int *status)
+{
+ ffgacl(fptr, colnum, ttype, tbcol,
+ tunit, tform, tscal, tzero,
+ tnull, tdisp, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_get_bcolparms(fitsfile *fptr, int colnum, char *ttype, char *tunit,
+ char *dtype, long *repeat, double *tscal, double *tzero,
+ long *tnull, char *tdisp, int *status)
+{
+ ffgbcl(fptr, colnum, ttype, tunit,
+ dtype, repeat, tscal, tzero,
+ tnull, tdisp, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_read_img(fitsfile *fptr, int datatype, long firstelem, long nelem,
+ void *nulval, void *array, int *anynul, int *status)
+{
+ ffgpv(fptr, datatype, firstelem, nelem,
+ nulval, array, anynul, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_read_col( fitsfile *fptr, int datatype, int colnum, long firstrow,
+ long firstelem, long nelem, void *nulval, void *array,
+ int *anynul, int *status)
+{
+ ffgcv( fptr, datatype, colnum, firstrow,
+ firstelem, nelem, nulval, array, anynul,
+ status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_write_img(fitsfile *fptr, int datatype, long firstelem, long nelem,
+ void *array, int *status)
+{
+ ffppr(fptr, datatype, firstelem, nelem,
+ array, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_write_col(fitsfile *fptr, int datatype, int colnum, long firstrow,
+ long firstelem, long nelem, void *array, int *status)
+{
+ ffpcl(fptr, datatype, colnum, firstrow,
+ firstelem, nelem, array, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_rows(fitsfile *fptr, long firstrow, long nrows, int *status)
+{
+ ffirow(fptr, firstrow, nrows, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_delete_rows(fitsfile *fptr, long firstrow, long nrows, int *status)
+{
+ ffdrow(fptr, firstrow, nrows, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_delete_rowlist(fitsfile *fptr, long *rownum, long nrows, int *status)
+{
+ ffdrws(fptr, rownum, nrows, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_col(fitsfile *fptr, int numcol, char *ttype, char *tform,
+ int *status)
+{
+ fficol(fptr, numcol, ttype, tform, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_insert_cols(fitsfile *fptr, int firstcol, int ncols, char **ttype,
+ char **tform, int *status)
+{
+ fficls(fptr, firstcol, ncols, ttype,
+ tform, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_delete_col(fitsfile *fptr, int numcol, int *status)
+{
+ ffdcol(fptr, numcol, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
+int FITS_copy_col(fitsfile *infptr, fitsfile *outfptr, int incol, int outcol,
+ int create_col, int *status)
+{
+ ffcpcl(infptr, outfptr, incol, outcol,
+ create_col, status);
+ cf_if_fits_error(*status);
+ return *status;
+}
+
diff --git a/src/libcf/cf_active_region.c b/src/libcf/cf_active_region.c
new file mode 100644
index 0000000..8a3fd6e
--- /dev/null
+++ b/src/libcf/cf_active_region.c
@@ -0,0 +1,90 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_active_region(fitsfile *header, long nevents, float *xfarf,
+ * float *yfarf, unsigned char *locflags)
+ *
+ * Description: Flags events outside of detector active area.
+ *
+ * Note: Both cf_ttag_init and cf_hist_init set LOCATION_SHLD
+ * when initializing IDF, but they look only at XRAW and use
+ * limits that are less restrictive than those used here.
+ *
+ * Arguments: fitsfile *header Pointer to FITS file containing the
+ * header of the intermediate data file
+ * long nevents The number of events
+ * float *xfarf An array of event X positions
+ * float *yfarf An array of event Y positions
+ * unsigned char *locflags Location flags of each event
+ *
+ * Calls:
+ *
+ * Return: 0 on success
+ *
+ * History: 11/12/02 1.1 peb Begin and finish work
+ * 03/11/03 1.2 wvd Changed locflags to unsigned char
+ * 05/20/03 1.3 rdr Added call to cf_proc_check
+ * 07/29/03 1.4 wvd If cf_proc_check fails, return errflg.
+ * 04/07/07 1.5 wvd Clean up compiler warnings.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdlib.h>
+#include "calfuse.h"
+
+int
+cf_active_region(fitsfile *header, long nevents, float *xfarf, float *yfarf,
+ unsigned char *locflags)
+{
+ char CF_PRGM_ID[] = "cf_active_region";
+ char CF_VER_NUM[] = "1.5";
+
+ char elecfile[FLEN_VALUE]={'\0'};
+ char keyword[FLEN_KEYWORD]={'\0'}, detector[FLEN_VALUE]={'\0'};
+ int errflg=0, status=0, active_l, active_r, active_b, active_t;
+ long j;
+ fitsfile *elecfits=NULL;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ FITS_read_key(header, TSTRING, "DETECTOR", detector, NULL, &status);
+ /*
+ * Read keywords from ELEC_CAL file
+ */
+ FITS_read_key(header, TSTRING, "ELEC_CAL", elecfile, NULL, &status);
+ FITS_open_file(&elecfits, cf_cal_file(elecfile), READONLY, &status);
+ sprintf(keyword, "ACTVL_%s", detector);
+ FITS_read_key(elecfits, TINT, keyword, &active_l, NULL, &status);
+ sprintf(keyword, "ACTVR_%s", detector);
+ FITS_read_key(elecfits, TINT, keyword, &active_r, NULL, &status);
+ sprintf(keyword, "ACTVB_%s", detector);
+ FITS_read_key(elecfits, TINT, keyword, &active_b, NULL, &status);
+ sprintf(keyword, "ACTVT_%s", detector);
+ FITS_read_key(elecfits, TINT, keyword, &active_t, NULL, &status);
+ FITS_close_file(elecfits, &status);
+ cf_verbose(3, "Active area limits: X from %d to %d, Y from %d to %d",
+ active_l, active_r, active_b, active_t);
+ /*
+ * Apply active region flags to each event.
+ */
+ for(j=0; j<nevents; j++) {
+ /*
+ * Flag events that fall outside of detector active region.
+ */
+ if (xfarf[j] < active_l || xfarf[j] > active_r ||
+ yfarf[j] < active_b || yfarf[j] > active_t) {
+ locflags[j] |= LOCATION_SHLD;
+ }
+ }
+
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return 0;
+}
diff --git a/src/libcf/cf_apply_dead_time.c b/src/libcf/cf_apply_dead_time.c
new file mode 100644
index 0000000..a714a28
--- /dev/null
+++ b/src/libcf/cf_apply_dead_time.c
@@ -0,0 +1,142 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_apply_dead_time(fitsfile *header, long nevents, float *time,
+ * float *weight, long nseconds, float *timeline,
+ * float *ids_dtc, float *elec_dtc)
+ *
+ * Description: Applies dead-time correction to all photons. For HIST data,
+ * uses mean of dead-time arrays. Writes mean dead-time
+ * values to file header. Issue warning if DET_DEAD > 1.5.
+ *
+ * Arguments: fitsfile *header Pointer to FITS file containing the
+ * header of the intermediate data file
+ * long nevents The number of events
+ * float *time An array of event times
+ * float *weight An array of event weights
+ * long nseconds The number of timeline values
+ * float *timeline An array of timeline times
+ * float *aic_rate Active Image Counter rate
+ * float *ids_dtc Output of cf_ids_dead_time
+ * float *elec_dtc Output of cf_elec_dead_time
+ *
+ * Calls:
+ *
+ * Return: 0 on success
+ *
+ * History: 08/01/03 1.1 wvd Begin work
+ * 08/04/03 1.2 wvd Convert aic_rate to type short.
+ * 11/25/03 1.3 wvd For HIST data, require that
+ * mean_tot_dtc be >= 1.
+ * 11/26/03 1.4 wvd Change aic_rate to type float.
+ * 02/09/04 1.5 wvd Re-write with new logic.
+ * 04/09/04 1.6 bjg Include stdlib.h
+ * 11/07/05 1.7 wvd Add a test to catch bad DTC values.
+ * 03/10/06 1.8 wvd Clean up i/o.
+ * 12/29/06 1.9 wvd Check DET_DEAD rather than TOT_DEAD.
+ * 04/07/07 1.10 wvd Clean up compiler errors.
+ *
+ ****************************************************************************/
+#include <stdlib.h>
+#include <string.h>
+#include "calfuse.h"
+
+int
+cf_apply_dead_time(fitsfile *header, long nevents, float *time, float *weight,
+ long nseconds, float *timeline, float *ids_dtc, float *elec_dtc)
+{
+ char CF_PRGM_ID[] = "cf_apply_dead_time";
+ char CF_VER_NUM[] = "1.10";
+
+ char comment[FLEN_CARD], datestr[FLEN_CARD], instmode[FLEN_VALUE];
+ char hkexists[FLEN_VALUE];
+ double ctimeb, ctimee, eng_time;
+ int errflg=0, status=0, timeref;
+ int det_flag=FALSE, ids_flag=FALSE;
+ long j, k;
+ float *tot_dtc, det_dead, ids_dead, mean_tot_dtc=0.;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ /* Read header keywords. Set flag if DET_DEAD > 1.5 */
+ FITS_read_key(header, TSTRING, "INSTMODE", instmode, NULL, &status);
+ FITS_read_key(header, TSTRING, "HKEXISTS", hkexists, NULL, &status);
+ FITS_read_key(header, TFLOAT, "DET_DEAD", &det_dead, NULL, &status);
+ if (det_dead > 1.5) det_flag = TRUE;
+
+ /*
+ * If there is no housekeeping file, AND the engineering snapshot times
+ * are bad, AND the mean value of any DTC array is > 1.5, set the
+ * combined DTC array to unity and issue a warning.
+ */
+ if(!strncasecmp(hkexists, "N", 1)) {
+ FITS_read_key(header, TDOUBLE, "CTIME_B", &ctimeb, NULL, &status);
+ FITS_read_key(header, TDOUBLE, "CTIME_E", &ctimee, NULL, &status);
+ if ((eng_time = (ctimee-ctimeb) * 86400.) < 1.) {
+ FITS_read_key(header, TFLOAT, "IDS_DEAD", &ids_dead, NULL, &status);
+ if (ids_dead > 1.5) ids_flag = TRUE;
+ }
+ }
+
+ /*
+ * Compute total dead-time correction and its mean.
+ */
+ tot_dtc = (float *) cf_malloc(sizeof(float) * nseconds);
+ if (det_flag || ids_flag) {
+ for (k = 0; k < nseconds; k++) tot_dtc[k] = 1.0;
+ mean_tot_dtc = 1.0;
+ }
+ else {
+ for (k = 0; k < nseconds; k++) {
+ tot_dtc[k] = elec_dtc[k] * ids_dtc[k];
+ mean_tot_dtc += tot_dtc[k];
+ }
+ mean_tot_dtc /= nseconds;
+ }
+
+ /* In TTAG mode, scale weights by tot_dtc as a function of time. */
+ if (!strncmp(instmode, "TTAG", 4)) for (j=k=0; j<nevents; j++) {
+ while(timeline[k+1]-FRAME_TOLERANCE < time[j] && k+1 < nseconds)
+ k++;
+ weight[j] *= tot_dtc[k];
+ }
+
+ /* In HIST mode, scale all weights by mean dead-time correction. */
+ else if (mean_tot_dtc > 1.)
+ for (j = 0; j < nevents; j++)
+ weight[j] *= mean_tot_dtc;
+ else mean_tot_dtc = 1.;
+
+ cf_verbose(2, "Mean total dead-time correction: %f", mean_tot_dtc);
+ FITS_update_key(header, TFLOAT, "TOT_DEAD", &mean_tot_dtc, NULL, &status);
+
+ if (det_flag || ids_flag) {
+ FITS_write_comment(header, " ", &status);
+ if (ids_flag) {
+ sprintf(comment,
+ "IDS_DEAD > 1.5. Setting dead-time correction to unity.");
+ cf_if_warning(comment);
+ FITS_write_comment(header, comment, &status);
+ }
+ if (det_flag) {
+ sprintf(comment,
+ "DET_DEAD > 1.5. Setting dead-time correction to unity.");
+ cf_if_warning(comment);
+ FITS_write_comment(header, comment, &status);
+ }
+ fits_get_system_time(datestr, &timeref, &status);
+ sprintf(comment, "CalFUSE v%s %.10s", CALFUSE_VERSION, datestr);
+ FITS_write_comment(header, comment, &status);
+ FITS_write_comment(header, " ", &status);
+ }
+
+ free (tot_dtc);
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return 0;
+}
diff --git a/src/libcf/cf_apply_filters.c b/src/libcf/cf_apply_filters.c
new file mode 100644
index 0000000..ff47f21
--- /dev/null
+++ b/src/libcf/cf_apply_filters.c
@@ -0,0 +1,177 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_apply_filters(fitsfile *header, int tscreen, long nevents,
+ * unsigned char *timeflags, unsigned char *loc_flags,
+ * long ntimes, unsigned char *gtiflags,
+ * float *dtime, float *ntime, long *ngood, long **index)
+ *
+ * Description: Select photons that satisfy filter criteria
+ *
+ * Arguments: fitsfile *header Pointer to FITS file containing the
+ * header of the intermediate data file
+ * int tscreen Flag indicating whether to screen on
+ * time flags (yes if > 0)
+ * long nevents The number of events
+ * unsigned char *timeflags Photon event time flags
+ * unsigned char *loc_flags Photon event location flags
+ * long ntimes Number of entries in timeline table
+ * unsigned char *gtiflags Status flags from timeline table
+ * float *dtime Day time exposure length
+ * float *ntime Night time exposure length
+ * long *ngood Number of good photons
+ * long **index List of good photon indexes
+ *
+ * Return: status
+ *
+ * History: 02/19/03 1.1 peb Begin work
+ * 03/20/03 1.2 peb Fixed daytime and nighttime
+ * calculation and fill header with
+ * correct EXPTIME and EXPNIGHT value.
+ * 04/08/03 1.3 wvd Get timeline-table arrays from
+ * calling routine.
+ * 05/10/03 1.4 wvd Read DAYNIGHT from file header;
+ * test only first character.
+ * 05/20/03 1.5 rdr Move to top level HDU before reading
+ * header keywords
+ * 06/09/03 1.6 rdr Ignore timing flags in the screening
+ * if tscreen is 0.
+ * 10/06/03 1.8 wvd Properly deal with new format of
+ * timeline table.
+ * 12/08/03 1.9 wvd If tscreen is 0, ignore temporal
+ * flags when calculating dtime & ntime.
+ * 02/11/04 1.10 wvd Correct error in calculation of
+ * dtime and ntime.
+ * 06/02/04 1.11 wvd Mask BRST, SAA, and LIMB flags
+ * when calculating exposure time
+ * for HIST data.
+ * 06/10/04 1.12 wvd Delete timeline times array from
+ * argument list.
+ * 01/27/05 1.13 wvd Use TEMPORAL_MASK rather than
+ * TEMPORAL_DAY to screen photon status
+ * flags. They differ only in HIST mode.
+ * 07/18/08 1.14 wvd If EXP_STAT = 2, target is bright
+ * earth or airglow. Mask limb-angle flag.
+ * 08/22/08 1.15 wvd Compare EXP_STAT with TEMPORAL_LIMB,
+ * rather than a particular value.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include "calfuse.h"
+
+int
+cf_apply_filters(fitsfile *header, int tscreen, long nevents,
+ unsigned char *timeflags, unsigned char *loc_flags,
+ long ntimes, unsigned char *gtiflags,
+ long *dtime, long *ntime, long *ngood, long **index)
+{
+ char CF_PRGM_ID[] = "cf_apply_filters";
+ char CF_VER_NUM[] = "1.15";
+
+ char daynight[FLEN_VALUE], instmode[FLEN_VALUE];
+ int day=0, expstat, night=0, status=0;
+ long j;
+ float exptime;
+ unsigned char TEMPORAL_MASK;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ /*
+ * Read INSTMODE keyword. If in HIST mode, mask out
+ * LIMB, SAA, and BRST flags. TEMPORAL_DAY is the default.
+ */
+ TEMPORAL_MASK = TEMPORAL_DAY;
+ FITS_read_key(header, TSTRING, "INSTMODE", instmode, NULL, &status);
+ if (!strncmp(instmode, "HIST", 4)) {
+ TEMPORAL_MASK |= TEMPORAL_LIMB;
+ TEMPORAL_MASK |= TEMPORAL_SAA;
+ TEMPORAL_MASK |= TEMPORAL_BRST;
+ }
+ /*
+ * If EXP_STAT = TEMPORAL_LIMB, target is bright earth or airglow.
+ * Mask limb-angle flag.
+ */
+ FITS_read_key(header, TINT, "EXP_STAT", &expstat, NULL, &status);
+ if (expstat == (int) TEMPORAL_LIMB) TEMPORAL_MASK |= TEMPORAL_LIMB;
+
+ /*
+ * Calculate day/night exposure time.
+ * If tscreen is 0, ignore the temporal flags (other than day/night).
+ */
+ *dtime = *ntime = 0.;
+ for (j=0; j<ntimes-1; j++) {
+ if ( !(gtiflags[j] & ~TEMPORAL_MASK) || !tscreen ) {
+ if (gtiflags[j] & TEMPORAL_DAY)
+ *dtime += 1;
+ else
+ *ntime += 1;
+ }
+ }
+ cf_verbose(2, "Exposure time: day = %ld, night = %ld", *dtime, *ntime);
+ /*
+ * Read EXPTIME and DAYNIGHT keywords from file header.
+ */
+ FITS_movabs_hdu(header, 1, NULL, &status) ;
+ FITS_read_key(header, TFLOAT, "EXPTIME", &exptime, NULL, &status);
+ FITS_read_key(header, TSTRING, "DAYNIGHT", daynight, NULL, &status);
+
+ /* Decode DAYNIGHT keyword */
+ if (!strncmp(daynight, "D", 1) || !strncmp(daynight, "d", 1)) {
+ day = 1;
+ *ntime = 0.;
+ }
+ else if (!strncmp(daynight, "N", 1) || !strncmp(daynight, "n", 1)) {
+ night = 1;
+ *dtime = 0.;
+ }
+ else if (!strncmp(daynight, "B", 1) || !strncmp(daynight, "b", 1)) {
+ day = night = 1;
+ }
+ else
+ cf_if_error("Unknown DAYNIGHT keyword value");
+ cf_verbose(3, "DAYNIGHT flag set to %s", daynight);
+
+ /* Quick test of internal consistency */
+ if (fabs(*dtime + *ntime - exptime) > exptime/10.)
+ cf_if_warning("Sum of day and night time differs from EXPTIME by > 10%%");
+
+ /*
+ * Apply screening to each photon event.
+ * First mask out airglow and day flags and test for zero
+ * (good) values. Next, see if the user wants daytime,
+ * night time, or both and confirm that appropriate flag is set.
+ */
+
+ *index = (long *) cf_calloc(nevents, sizeof(long));
+
+ if (tscreen) {
+ for(*ngood=j=0; j<nevents; j++) {
+ if (!(loc_flags[j] & ~LOCATION_AIR) &&
+ !(timeflags[j] & ~TEMPORAL_MASK) &&
+ ((day && (timeflags[j] & TEMPORAL_DAY)) ||
+ (night && (timeflags[j] ^ TEMPORAL_DAY)))) {
+ (*index)[(*ngood)++] = j;
+ }
+ }
+ }
+
+ /* If tscreen is 0, then screen only on the location flags. */
+ else {
+ cf_verbose(1, "Ignoring time flags in the screening") ;
+ for(*ngood=j=0; j<nevents; j++)
+ if (!(loc_flags[j] & ~LOCATION_AIR))
+ (*index)[(*ngood)++] = j;
+ }
+
+ cf_verbose(2, "%ld good events", *ngood);
+
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return status;
+}
diff --git a/src/libcf/cf_astig_farf.c b/src/libcf/cf_astig_farf.c
new file mode 100644
index 0000000..aabcdae
--- /dev/null
+++ b/src/libcf/cf_astig_farf.c
@@ -0,0 +1,144 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Description: Correct XFARF array for instrumental astigmatism.
+ *
+ * Returns: 0 upon successful completion.
+ *
+ * History: 12/12/02 1.1 jch Begin work
+ * 02/24/03 1.2 peb Using calfusettag.h include file
+ * 03/04/03 1.3 peb Removed math.h - unnecessary
+ * 03/11/03 1.4 wvd Changed channel to type *char
+ * 04/04/03 1.5 wvd Test for overflow of astig array.
+ * 05/20/03 1.6 rdr Add call to cf_proc_check
+ * 09/16/03 1.7 wvd Remove calls to astig_target_aperture
+ * and astig_read_file. Change sign of
+ * astigmatism correction to match
+ * the new astg**009.fit files.
+ * Make yoff a float.
+ * 11/05/03 1.8 wvd Change channel to unsigned char.
+ * 04/07/07 1.9 wvd Clean up compiler warnings.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "calfuse.h"
+
+static char CF_PRGM_ID[] = "cf_astig_farf";
+static char CF_VER_NUM[] = "1.9";
+
+
+/*
+ * Add astigmatism correction to XFARF for each photon within target
+ * aperture. Note that spectrum centroid moves with time.
+ */
+static int
+farf_astig_shifts(fitsfile *header, long nevents, float *xfarf,
+ float *yfarf, unsigned char *channel, float *photon_time,
+ long nseconds, float *timeline_time, float *ycentl, float *ycents)
+{
+ char astig_file[FLEN_VALUE];
+ int status=0;
+ int active_ap[2], ap, ast_cen, extno, overflow=0;
+ long i, j, k;
+ long npix, nx, ny, ii, jj, kk;
+ float yoff;
+ float *astig=NULL, *centroid=NULL;
+ fitsfile *astigfits;
+
+ /* Read target apertures from the file header */
+ (void) cf_source_aper (header, active_ap);
+
+ /* Open the astigmatism file */
+ FITS_read_key(header, TSTRING, "ASTG_CAL", astig_file, NULL, &status);
+ FITS_open_file(&astigfits, cf_cal_file(astig_file), READONLY, &status);
+
+ /* Since we presently have no astig correction for extended
+ * sources, we apply correction only to target aperture.
+ */
+ for (i = 0; i < 2; i++) {
+
+ ap = active_ap[i];
+ if (i == 0)
+ centroid = ycentl;
+ else
+ centroid = ycents;
+
+ /* Read astigmatism correction for this aperture */
+ extno = ap+1;
+ cf_verbose(3, "Reading extension %d of %s", extno, astig_file);
+ FITS_movabs_hdu(astigfits, extno, NULL, &status);
+ FITS_read_key(astigfits, TINT, "SLIT_CEN", &ast_cen, NULL, &status);
+ FITS_read_key(astigfits, TLONG, "NAXIS1", &nx, NULL, &status);
+ FITS_read_key(astigfits, TLONG, "NAXIS2", &ny, NULL, &status);
+
+ npix = nx * ny;
+ astig = (float *) cf_malloc(sizeof(float) * npix);
+ FITS_read_img(astigfits, TFLOAT, 1L, npix, 0, astig, NULL, &status);
+
+ /* Go through the photon list, find the astigmatism (delta(X))
+ * corresponding to the (xfarf, yfarf) position and channel,
+ * and add it to xfarf[k].
+ */
+ k = 0;
+ for (j = 0; j < nevents; j++) {
+ if (channel[j] == ap) {
+ while ((photon_time[j] > timeline_time[k+1] - FRAME_TOLERANCE)
+ && (k < nseconds-1)) {
+ k++;
+ }
+
+ /* Determine offset between spectrum and astig correction file. */
+ yoff = centroid[k] - ast_cen;
+
+ /* Find the astigmatism correction appropriate
+ to the photon's (xfarf, yfarf) position. */
+ if ((ii = (xfarf[j] + 0.5)) >= nx) {overflow = 1; continue;};
+ if ((jj = (yfarf[j] - yoff + 0.5)) >= ny) {overflow = 1; continue;}
+ if ((kk = jj * nx + ii) >= npix) {overflow = 1; continue;}
+ xfarf[j] += astig[kk];
+ }
+ }
+
+ /* If overflow flag is set, there is a problem with the ASTG_CAL file. */
+ if (overflow)
+ cf_if_warning("Overflow of ASTG_CAL file in aperture %d", ap);
+
+ /* Space for astig array is allocated in each loop. */
+ free(astig);
+ }
+
+ FITS_close_file(astigfits, &status);
+ return status;
+}
+
+int cf_astig_farf(fitsfile *header, long nevents, float *xfarf, float *yfarf,
+ unsigned char *channel, float *photon_time, long nseconds,
+ float *timeline_time, float *ycentl, float *ycents)
+{
+ int errflg=0, status=0;
+
+ /* Enter a timestamp into the log. */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ /* Check that astigmatism correction is appropriate for input image */
+ if (astig_check_input_image(header) == 1) {
+
+ /* Read astigmatism corrections for each aperture, add to XFARF */
+ farf_astig_shifts(header, nevents, xfarf, yfarf, channel,
+ photon_time, nseconds, timeline_time, ycentl, ycents);
+ }
+
+ /* Update processing flags. */
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done Processing");
+ return (status);
+}
diff --git a/src/libcf/cf_astigmatism.c b/src/libcf/cf_astigmatism.c
new file mode 100644
index 0000000..e02f361
--- /dev/null
+++ b/src/libcf/cf_astigmatism.c
@@ -0,0 +1,198 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Description: Modifies X array to correct for instrumental astigmatism.
+ *
+ * Since we have no astigmatism correction for extended
+ * sources, this program does not attempt to apply one.
+ *
+ * In keeping with the CalFUSE standard, the astigmatism
+ * correction is added to the measured X position of each photon.
+ * Versions of the ASTG_CAL file prior to v009 have the opposite
+ * sign and are incompatible with this version of the program.
+ *
+ * Returns: 0 upon successful completion.
+ *
+ * History: 12/02/02 1.1 jch Adapt from cf_astig.c
+ * 12/20/02 1.2 wvd Install
+ * 02/11/03 1.3 wvd Apply astig correction only if
+ * YCENT# > O for target aperture.
+ * Add ASTG_KEY keyword. Comment field:
+ * Performed? 1=LiF, 2=SiC, 3=Both
+ * 02/13/03 1.4 wvd Change all indices to type long.
+ * 02/24/03 1.5 peb Changed include file to calfusettag.h
+ * 03/04/03 1.6 peb Removed unused variables and math.h.
+ * Corrected sprintf:185 argument error.
+ * 03/11/03 1.7 wvd Change channel to type char
+ * 03/12/03 1.8 wvd If wavelength undefined, set
+ * channel[k] = 0
+ * 04/04/03 1.9 wvd Test for overflow of astig array.
+ * 04/07/03 1.10 wvd Delete test for quality of YCENT.
+ * Delete ASTG_KEY keyword.
+ * Don't round yoff to nearest integer.
+ * 04/09/03 1.11 wvd Write name of ASTG_CAL file to trailer.
+ * 05/20/03 1.12 rdr Added call to cf_proc_check
+ * 09/16/03 1.13 wvd Change sign of ASTG_CAL file so that
+ * correction is additive.
+ * 09/22/03 1.14 wvd Initialize overflow counter.
+ * Print out number of overflows.
+ * 10/17/03 1.15 wvd Discard photons that fall outside of
+ * the astigmatism-correction window.
+ * 10/31/03 1.16 wvd Change channel to unsigned char.
+ * 11/11/03 1.17 wvd Interpolate between tabulated
+ * wavelength values. Use cf_read_col
+ * to read wavecal files.
+ * 02/17/04 1.18 wvd Replace cf_nint() with cf_nlong()
+ * 03/02/04 1.19 wvd Change name of assign_wavelengths
+ * to cf_x2lambda and make it
+ * generally accessible.
+ * 07/16/04 1.20 wvd Must change ASTG_COR to COMPLETE or
+ * SKIPPED by hand.
+ * 02/18/05 1.21 wvd Add some diagnostic output.
+ * 11/30/05 1.22 wvd Don't complain when photon events
+ * fall outside of astig images.
+ * 05/15/06 1.23 wvd Divide cf_astigmatism_and_dispersion
+ * into two separate routines.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "calfuse.h"
+
+static char CF_PRGM_ID[] = "cf_astigmatism";
+static char CF_VER_NUM[] = "1.23";
+
+
+/*
+ * Loop over the apertures, read appropriate astigmatism correction,
+ * and add to the input X shifts of pixels within extraction window.
+ */
+static int
+add_astig_shifts(fitsfile *infits, long nevents, float *x, float *y,
+ unsigned char *channel)
+{
+ char astig_file[FLEN_VALUE], keyword[FLEN_VALUE];
+ int status=0;
+ int ap, active_ap[2], ast_cen, extno, i, overflow;
+ long k, ii, jj, kk, npix, nx, ny;
+ float centroid, yoff;
+ float *astig=NULL;
+ fitsfile *astigfits;
+
+ /* Read target apertures from the file header */
+ (void) cf_source_aper (infits, active_ap);
+
+ /* Open the astigmatism file */
+ FITS_read_key(infits, TSTRING, "ASTG_CAL", astig_file, NULL, &status);
+ FITS_open_file(&astigfits, cf_cal_file(astig_file), READONLY, &status);
+
+ /* Since we presently have no astig correction for extended
+ * sources, we apply correction only to target aperture.
+ */
+ for (i = 0; i < 2; i++) {
+ ap = active_ap[i];
+
+ /* Read the centroid of the aperture from the input file. */
+ sprintf(keyword, "YCENT%1d", ap);
+ FITS_read_key(infits, TFLOAT, keyword, &centroid, NULL, &status);
+ cf_verbose(3, "Target aperture: %d\t Y centroid: %f", ap, centroid);
+
+ /* Read astigmatism correction for this aperture */
+ extno = ap+1;
+ cf_verbose(3, "Reading extension %d of %s", extno, astig_file);
+ FITS_movabs_hdu(astigfits, extno, NULL, &status);
+ FITS_read_key(astigfits, TINT, "SLIT_CEN", &ast_cen, NULL, &status);
+ FITS_read_key(astigfits, TLONG, "NAXIS1", &nx, NULL, &status);
+ FITS_read_key(astigfits, TLONG, "NAXIS2", &ny, NULL, &status);
+
+ npix = nx * ny;
+ astig = (float *) cf_malloc(sizeof(float) * npix);
+ FITS_read_img(astigfits, TFLOAT, 1L, npix, 0, astig, NULL, &status);
+
+ /* Determine offset between spectrum and astig correction file */
+ yoff = centroid - ast_cen;
+ cf_verbose(3, "Offset between spectrum and astig correction: %f", yoff);
+
+ /* Go through the event list and find the astigmatism correction
+ * appropriate for the photon's (x, y) position.
+ */
+ overflow = 0;
+ for (k = 0; k < nevents; k++) {
+ if (channel[k] == ap) {
+ if (((ii = cf_nlong(x[k])) < 0 || ii >= nx) ||
+ ((jj = cf_nlong(y[k] - yoff)) < 0 || jj >= ny) ||
+ ((kk = jj * nx + ii) >= npix)) {
+ channel[k] = 0;
+ overflow++;
+ cf_verbose(3, "Cannot correct pixel %05ld (%f, %f)",
+ k, x[k], y[k]);
+ }
+ else x[k] += astig[kk];
+ }
+ }
+ /* If overflow flag is set, there is a problem either with
+ the ASTG_CAL file or the input photon list.
+ if (overflow)
+ cf_if_warning("%d events fell outside of aperture %d ASTG_CAL window",
+ overflow, ap);
+ Comment this out, as it is always triggered by cf_bad_pixels. - wvd (11/30/05) */
+
+ /* Space for astig array is allocated in each loop. */
+ free(astig);
+ cf_verbose(3, "End of loop for aperture %d", ap);
+ }
+
+ FITS_close_file(astigfits, &status);
+ return status;
+}
+
+
+/*
+ * If APERTURE = RFPT or target is not a point source, exit without
+ * applying an astigmatism correction.
+ */
+int cf_astigmatism(fitsfile *infits, long nevents, float *x,
+ float *y, unsigned char *channel)
+{
+ char aperture[FLEN_VALUE];
+ char source_type[FLEN_VALUE];
+ int errflg=0, fileok=TRUE, status=0;
+
+ /* Enter a timestamp into the log. */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ if ((errflg = cf_proc_check(infits, CF_PRGM_ID))) return errflg;
+
+ FITS_read_key(infits, TSTRING, "APERTURE", aperture, NULL, &status);
+ if (!strncmp(aperture, "RFPT", 4)) {
+ cf_verbose(1, "Aperture is RFPT. No astig correction applied.");
+ fileok = FALSE;
+ }
+
+ /* Check source type */
+ FITS_read_key(infits, TSTRING, "SRC_TYPE", source_type, NULL, &status);
+ if (strncmp(source_type, "P", 1)) {
+ cf_verbose(1, "Source type is %s. No astig correction applied.",
+ source_type);
+ fileok = FALSE;
+ }
+
+ if (fileok) {
+ add_astig_shifts(infits, nevents, x, y, channel);
+ cf_proc_update(infits, CF_PRGM_ID, "COMPLETE");
+ }
+ else
+ cf_proc_update(infits, CF_PRGM_ID, "SKIPPED");
+
+ /* Update processing flags. */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done Processing");
+
+ return fileok;
+}
diff --git a/src/libcf/cf_cal_file.c b/src/libcf/cf_cal_file.c
new file mode 100644
index 0000000..59c873d
--- /dev/null
+++ b/src/libcf/cf_cal_file.c
@@ -0,0 +1,84 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_cal_file(char *calfile)
+ *
+ * Description: Returns a pointer to a string with the full calibration
+ * path (from the CF_CALDIR environment variable) prepended
+ * to the given file name.
+ *
+ * Arguments: char *calfile Name of the calibration file.
+ *
+ * Returns: A pointer to a string which contains the full path filename.
+ *
+ * History: 02/26/99 emurphy Begin and finish work.
+ * 03/01/99 emurphy Added some error checking
+ * 03/02/99 emurphy Added use of cf_if_warning
+ * 04/04/99 emurphy removed cf_error_init
+ * 04/08/99 barrett replaced fitsio.h with calfuse.h
+ * 05/18/99 emurphy Added checks on strlen and ending "/"
+ * 06/04/99 peb Removed 160 character string limit
+ * Added cf_malloc function.
+ * 08/05/99 emurphy Created cf_populate_file, to support
+ * cf_cal_file and cf_hist_file.c
+ * 08/25/99 emurphy Added cf_parm_file for parameter files.
+ * 12/18/03 bjg Change calfusettag.h to calfuse.h
+ *
+ ****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include "calfuse.h"
+
+static char
+*cf_populate_file(char *calfile, char *enviro_name)
+{
+ char *enviro, *filen;
+ int strl, strt;
+
+ enviro = getenv(enviro_name);
+
+ if (enviro) {
+ strl = strlen(enviro);
+ strt = strl + strlen(calfile) + 16;
+
+ filen = cf_malloc(strt);
+ strcpy(filen, enviro);
+
+ /* Check to see if last character is a "/" */
+ if (strncmp(filen+strl-1, "/", 1)!=0)
+ strcat(filen, "/");
+
+ strcat(filen, calfile);
+ return filen;
+ }
+ else {
+ printf("Environment variable %-20.20s undefined.",enviro_name);
+ cf_if_warning("Environment variable undefined");
+ return calfile;
+ }
+}
+
+char *cf_cal_file(char *calfile)
+{
+
+ return cf_populate_file(calfile, "CF_CALDIR");
+
+}
+
+char *cf_hist_file(char *calfile)
+{
+
+ return cf_populate_file(calfile, "CF_HISTDIR");
+
+}
+
+char *cf_parm_file(char *calfile)
+{
+
+ return cf_populate_file(calfile, "CF_PARMDIR");
+
+}
diff --git a/src/libcf/cf_calculate_y_centroid.c b/src/libcf/cf_calculate_y_centroid.c
new file mode 100644
index 0000000..ebcd264
--- /dev/null
+++ b/src/libcf/cf_calculate_y_centroid.c
@@ -0,0 +1,289 @@
+/**************************************************************************
+ * Johns Hopkins University
+ * Center for Astrophysical Sciences
+ * FUSE
+ **************************************************************************
+ *
+ * Synopsis: cf_calculate_y_centroid(infits, nevents, weight, x, y,
+ * channel, timeflags, locflags)
+ *
+ * Description: Determines the y centroid of the target and airglow spectra
+ * in each aperture. The value written to the header depends
+ * on the quality flag for each channel in the IDF header.
+ * If QUALITY = HIGH, the target centroid is used; if MEDIUM,
+ * the airglow centroid is used; if LOW, the default centroid
+ * (from the CHID_CAL file) is used.
+ *
+ * Arguments: fitsfile infits Pointer to the input Intermediate Data File
+ * long nevents Number of photon events in the file
+ * float weight Scale factor for each photon
+ * float *x, *y X and Y positions of the photon
+ * unsigned char *channel Aperture associated with each photon
+ * unsigned char *timeflags Time flags - used to identify photons
+ * arriving during bursts, etc.
+ * unsigned char *locflags Location flags - used to identify photons
+ * in geocoronal line regions
+ *
+ * Calibration files required:
+ *
+ * Returns: 0 on success
+ *
+ * HISTORY: 10/16/02 v1.1 RDR started work
+ * 12/02/02 v1.2 RDR cleaned up variables
+ * 12/12/02 v1.3 wvd added include files
+ * 12/26/02 v1.4 RDR corrected error in calculating centroid
+ * 02/24/03 v1.5 peb changed include file to calfitsio.h
+ * 03/11/03 v1.6 wvd Changed screen to unsigned char
+ * 03/25/03 v1.7 rdr ignore pinhole aperture
+ * 04/03/03 v1.9 wvd Simplify calculation of centroid.
+ * Include airglow in centroid
+ * calculation of non-target apertures.
+ * Replace printf() with cf_verbose().
+ * 04/17/03 v1.10 wvd Fix bug in calculation of non-target
+ * centroids.
+ * 04/18/03 v1.11 wvd Call cf_proc_update only if final_call
+ * is TRUE. Scale YCENT by photon weight
+ * for use with HIST data. Changed
+ * screen to locflags.
+ * 05/20/03 v1.12 rdr Added call to cf_proc_check
+ * 05/22/03 v1.14 wvd cf_error_init to stderr
+ * 08/21/03 v1.16 wvd Change channel to unsigned char
+ * 09/02/03 v1.17 bjg Now read user-specified centroid
+ * information from PARM_CAL and
+ * expected centroids from CHID_CAL.
+ * 09/02/03 v1.18 wvd Tidy up code.
+ * 09/17/03 v1.19 wvd Return -1 if a target centroid
+ * is too far from expected value.
+ * 10/28/03 v1.20 wvd Rewrite program with new logic.
+ * Delete use of final_call.
+ * 11/12/03 v1.21 wvd If no photons in channel, don't
+ * crash, just use default centroid.
+ * If quality = LOW, ygeo = ycent.
+ * 04/09/04 v1.22 bjg Include string.h and stdio.h
+ * 06/04/04 v1.23 wvd Use photon time flags to exclude
+ * bursts, etc. from calculation.
+ * 08/18/04 v1.24 wvd Test all spectra to see whether
+ * centroid is out of bounds.
+ * 03/11/05 v1.25 wvd Tabulate centroids as doubles,
+ * then convert to floats.
+ * Write target centroid values
+ * to trailer file if verbose >= 2.
+ * 04/15/05 v1.26 wvd Clean up i/o.
+ * 04/26/05 v1.27 wvd Don't populate YGEO keywords, as
+ * they are wrong for point sources.
+ * Consider only events with good
+ * LOCATION flags.
+ * Use the largest aperture with a valid
+ * YGEO to determine offset from tabulated
+ * position. Use this shift to calculate
+ * ygeo for the smaller apertures.
+ * 05/18/05 v1.28 wvd Test both SPEX_LIF and SPEX_SIC.
+ * Don't override user-specified centroid,
+ * even if it's far from expected value.
+ * Fix bug in tabulation of expected
+ * Y centroids.
+ * 02/01/06 v1.29 wvd If ycent is too far from default value,
+ * use default value, not airglow centroid.
+ * Allow computation of yshift from
+ * airglow photons in target aperture.
+ * 11/25/08 1.30 wvd In HIST mode, there are no spectra in
+ * other apertures to cause confusion,
+ * so we need not require that the target
+ * centroid lie within X pixels of either
+ * the airglow or tabulated centroid.
+ *
+ **************************************************************************/
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include "calfuse.h"
+
+int cf_calculate_y_centroid(fitsfile *infits, long nevents, float *weight,
+ float *x, float *y, unsigned char *channel, unsigned char *timeflags,
+ unsigned char *locflags)
+{
+
+ char CF_PRGM_ID[] = "cf_calculate_y_centroid";
+ char CF_VER_NUM[] = "1.30";
+
+ char aperture[FLEN_VALUE], instmode[FLEN_VALUE];
+ char ycentname[FLEN_KEYWORD], file_name[FLEN_VALUE];
+ char key[FLEN_KEYWORD], quality[8][FLEN_VALUE];
+ double dncent, dngeo, dycent, dygeo;
+ float ycent, ygeo, yshift, ycent_tab[8];
+ int chan_num, errflg=0, status=0, active_ap[2];
+ int got_shift, i, hdu, target_ap, user;
+ int spex_sic, spex_lif, emax_sic, emax_lif, emax, extended;
+ long n;
+ fitsfile *parm_cal, *chid_cal;
+
+ /* Initialize error checking */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+
+ /* Enter a time stamp into the log */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ /* Check whether routine is appropriate for this data file. */
+ if ( (errflg = cf_proc_check(infits, CF_PRGM_ID)) ) return errflg;
+
+ /* Read the source aperture from the file header. */
+ FITS_read_key(infits, TSTRING, "APERTURE", aperture, NULL, &status);
+ extended=cf_source_aper(infits, active_ap) ;
+
+ /* Were data taken in HIST or TTAG mode? */
+ FITS_read_key(infits, TSTRING, "INSTMODE", instmode, NULL, &status);
+
+ /* Read centroid quality keywords from file header. */
+ for (i = 1; i < 8; i++) {
+ if (i == 4) continue;
+ sprintf(key, "YQUAL%1d", i);
+ FITS_read_key(infits, TSTRING, key, &quality[i][0], NULL, &status);
+ }
+
+ /* Read extraction parameters from PARM_CAL file. */
+ FITS_read_key(infits, TSTRING, "PARM_CAL", file_name, NULL, &status);
+ FITS_open_file(&parm_cal, cf_parm_file(file_name), READONLY, &status);
+ FITS_read_key(parm_cal,TINT,"SPEX_SIC",&spex_sic,NULL,&status);
+ FITS_read_key(parm_cal,TINT,"SPEX_LIF",&spex_lif,NULL,&status);
+ FITS_read_key(parm_cal,TINT,"EMAX_SIC",&emax_sic,NULL,&status);
+ FITS_read_key(parm_cal,TINT,"EMAX_LIF",&emax_lif,NULL,&status);
+ FITS_close_file(parm_cal,&status);
+
+ /* Read expected centroids from CHID_CAL file. Use extended-aperture
+ values except for apertures containing point-source targets. */
+ FITS_read_key(infits, TSTRING, "CHID_CAL", file_name, NULL, &status);
+ FITS_open_file(&chid_cal, cf_cal_file(file_name), READONLY, &status);
+ for (i = 1; i < 8; i++) {
+ if (i==4) continue;
+ if ((i == active_ap[0] || i == active_ap[1]) && !extended) hdu=i+1;
+ else hdu=i+9;
+ FITS_movabs_hdu(chid_cal, hdu, NULL, &status);
+ FITS_read_key(chid_cal,TFLOAT,"CENTROID",ycent_tab+i,NULL,&status);
+ }
+ FITS_close_file(chid_cal,&status);
+
+ /* Determine the centroids for each channel */
+ yshift = 0;
+ got_shift = FALSE;
+ for (chan_num = 7; chan_num > 0; chan_num--) {
+ if (chan_num == 4) {
+ yshift = 0;
+ got_shift = FALSE;
+ continue;
+ }
+ dngeo = dncent = 0.;
+ dygeo = dycent = 0.;
+ sprintf (ycentname, "YCENT%d", chan_num);
+
+ /* Is this a target aperture? */
+ if (chan_num == active_ap[0] || chan_num == active_ap[1])
+ target_ap = TRUE;
+ else
+ target_ap = FALSE;
+
+ /* If not, and we're in histogram mode, skip to the next channel. */
+ if (!target_ap && !strncmp(instmode, "HIST", 4)) continue;
+
+ /* Calculate separate target and geocoronal centroids. */
+ for (n = 0; n < nevents; n++) {
+ if (channel[n] == chan_num && !(timeflags[n] & ~TEMPORAL_DAY) &&
+ !(locflags[n] & ~LOCATION_AIR)) {
+
+ if(locflags[n] & LOCATION_AIR) { /* Airglow photon */
+ dygeo += y[n] * weight[n];
+ dngeo += weight[n];
+ }
+ else { /* Target photon */
+ dycent += y[n] * weight[n];
+ dncent += weight[n];
+ }
+ }
+ }
+
+ /* If we can't find any photons but expect to, set quality to LOW. */
+ if ((quality[chan_num][0] == 'H' && dncent < 1) ||
+ (quality[chan_num][0] == 'M' && dngeo < 1)) {
+ quality[chan_num][0] = 'L';
+ sprintf(key, "YQUAL%1d", chan_num);
+ FITS_update_key(infits, TSTRING, key, "LOW", NULL, &status) ;
+ cf_verbose(1, "No photon events in channel %d", chan_num);
+ }
+
+ /* Compute centroids of target and airglow spectra */
+ ycent = ygeo = 0;
+ if (dncent > 0) ycent = dycent / dncent;
+ if (dngeo > 0) ygeo = dygeo / dngeo;
+
+ /* If yshift is already known, use it to calculate ygeo.
+ * If not, and ygeo is valid for this aperture, compute yshift.
+ */
+ if (got_shift)
+ ygeo = yshift + ycent_tab[chan_num];
+ else if (dngeo > 0) {
+ yshift = ygeo - ycent_tab[chan_num];
+ got_shift = TRUE;
+ }
+
+ /* Now decide which values to write to the header. */
+
+ /* If we are in the target aperture and the user has specified
+ * YCENT for this channel, use it.
+ */
+ if (target_ap && ((chan_num<5 && spex_lif>=0 && spex_lif<1024) ||
+ (chan_num>4 && spex_sic>=0 && spex_sic<1024))) {
+ if (chan_num<5) ycent=spex_lif;
+ else ycent=spex_sic;
+ cf_verbose(1, "Channel %d: User-specified Y centroid = %g",
+ chan_num, ycent);
+ user = TRUE;
+ }
+ else
+ user = FALSE;
+
+ /* If the centroid quality is HIGH, use the calculated target
+ * value, regardless of whether we are in a target aperture.
+ * (This happens by default, so we don't have to do anything.)
+ */
+
+ /* If the centroid quality is MED, use the airglow centroid. */
+ if (quality[chan_num][0] == 'M')
+ ycent = ygeo;
+
+ /* If the quality is LOW, use the default value. */
+ else if (quality[chan_num][0] == 'L')
+ ycent = ycent_tab[chan_num];
+
+ /*
+ * Test whether YCENT is too far from the expected value.
+ * If so, use the tabulated value and set the quality to LOW.
+ * Don't test HIST data. (wvd, 11/25/2008)
+ */
+ if (chan_num<5) emax=emax_lif; else emax=emax_sic;
+ if (fabs(ycent-ycent_tab[chan_num]) > emax && !user && !strncmp(instmode, "TTAG", 4)) {
+ if(target_ap) {
+ cf_verbose(1, "Channel %d: computed centroid (%.1f) is too far "
+ "from expected value.", chan_num, ycent);
+ cf_verbose(1, "Channel %d: using default centroid of %.1f",
+ chan_num, ycent_tab[chan_num]);
+ }
+ else {
+ cf_verbose(2, "Channel %d: using default centroid of %.1f",
+ chan_num, ycent_tab[chan_num]);
+ }
+ ycent = ycent_tab[chan_num];
+ sprintf(key, "YQUAL%1d", chan_num);
+ FITS_update_key(infits, TSTRING, key, "LOW", NULL, &status) ;
+ }
+
+ /* Write centroid of target spectrum to the header */
+ FITS_update_key(infits, TFLOAT, ycentname, &ycent, NULL, &status) ;
+ cf_verbose(2, "For channel %d, Y centroid = %.1f", chan_num, ycent) ;
+
+ } /* End of loop over channels. */
+
+ cf_proc_update(infits, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished processing");
+
+ return status;
+}
diff --git a/src/libcf/cf_calculate_ycent_motion.c b/src/libcf/cf_calculate_ycent_motion.c
new file mode 100644
index 0000000..8879213
--- /dev/null
+++ b/src/libcf/cf_calculate_ycent_motion.c
@@ -0,0 +1,152 @@
+/**************************************************************************
+ * Johns Hopkins University
+ * Center for Astrophysical Sciences
+ * FUSE
+ *************************************************************************
+ *
+ * synopsis: cf_calculate_ycent_motion(header, nevents, ptime, y, channel,
+ * locflags, nsec, ttime, ycentl, ycents)
+ *
+ * Description: Determines the Y centroid of the emission as a function
+ * of time within the target apertures
+ *
+ * Arguments: fitsfile *header : pointer to Intermediate Data File
+ * long nevents : number of photon events in the file
+ * float *ptime : detection time for each photon
+ * float *y : y position of each photon
+ * unsigned char *channel: aperture associated with each photon
+ * unsigned char *locflags: location flag array
+ * long nsec : number of seconds tabulated in timeline
+ * float *ttime : tabulated times in the timeline
+ * geocoronal photons
+ * float *ycentl, *ycents :
+ * y centroids of the Lif and SiC apertures,
+ * tabulated once per second throughout
+ * the observation
+ *
+ *
+ * Calibration files required: None
+ *
+ * Returns: 0 on success
+ *
+ *
+ * HISTORY: 11/05/02 v1.1 RDR started work
+ * 11/27/07 v1.2 RDR corrected a bug in determining ycent
+ * cleaned up variables
+ * 12/04/02 v1.3 RDR locflags out geocoronal emission
+ * before determining centroids
+ * 12/12/02 v1.4 wvd change order of subroutine arguments
+ * 01/17/03 v1.5 wvd call cf_update_proc()
+ * 01/20/03 v1.6 rdr correct error in calculating ycent
+ * during the last time interval
+ * 02/24/03 v1.7 peb Changed include file to calfitsio.h
+ * 03/11/03 v1.8 wvd Changed locflags to unsigned char
+ * 05/20/03 v1.9 rdr Added call to cf_proc_check
+ * 05/22/03 v1.10 wvd cf_error_init to stderr
+ * 06/04/03 v1.11 wvd Implement cf_verbose throughout.
+ * 08/21/03 v1.12 wvd Change channel to unsigned char.
+ * 10/06/03 v1.13 wvd Change screen to locflags throughout.
+ * 10/21/04 v1.14 bjg Corrected several bugs
+ * 04/07/07 v1.15 wvd Clean up compiler warnings.
+ * 04/07/07 v1.16 wvd Clean up compiler warnings.
+ *
+ **************************************************************************/
+
+#include "calfuse.h"
+
+
+int
+cf_calculate_ycent_motion(fitsfile *header, long nevents, float *ptime,
+ float *y, unsigned char *channel, unsigned char *locflags,
+ long nsec, float *ttime, float *ycentl, float *ycents)
+{
+ char CF_PRGM_ID[] = "cf_calculate_ycent_motion";
+ char CF_VER_NUM[] = "1.16";
+
+ char ycentname[FLEN_CARD];
+ int chan_num, status=0, j, src_type, active_ap[2], nave=500;
+ int errflg=0;
+ long i, ndx, ndxs, nsam;
+ float ycent, ptst, dt, dt1;
+ float ycentdef[2];
+ float *ycentptr[2];
+
+ ycentptr[0]=ycentl;
+ ycentptr[1]=ycents;
+
+ /* Initialize error checking */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+
+ /* Enter a time stamp into the log */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ /* Check whether routine is appropriate for this data file. */
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ /* nave = number of photons to average in determining the centroid.
+ Should eventually be read from a parameter file. */
+ nave = 500;
+
+ /* Determine the source aperture and type from the header data.
+ src_type = 0 for a point source and 1 for an extended source. */
+
+ src_type = cf_source_aper(header, active_ap);
+ cf_verbose(3, "active_ap = %d and %d, src_type = %d",
+ active_ap[0], active_ap[1], src_type);
+
+
+ /* Determine the centroids. Do LiF and SiC apertures separately. */
+
+ for (j=0; j<2; j++) {
+
+
+ /* Select the channel number of the appropriate active aperture */
+ chan_num = active_ap[j];
+ sprintf(ycentname, "YCENT%1d", chan_num);
+ FITS_read_key(header, TFLOAT, ycentname, &ycentdef[j], NULL, &status);
+
+ nsam = 0;
+ ycent = 0.;
+ ptst = ptime[0];
+ ndxs = 0;
+
+ /* Go through all of the photons, determine the y centroids,
+ and fill in the arrays. Avoid airglow lines. */
+ for (i=1; i<nevents; i++) {
+ dt = ptime[i] - ptst;
+ dt1 = ptime[i] - ptime[i-1];
+ if (channel[i] == chan_num && !(locflags[i] & LOCATION_AIR)) {
+ ycent += y[i];
+ nsam += 1;
+ }
+ if ((nsam > nave && dt > 1.) || i == nevents-1 || dt1 > 10) {
+
+ if (nsam==0) ycent = ycentdef[j];
+ else ycent = ycent / nsam;
+
+ ndx = ndxs;
+
+ while ( ndx < nsec && (ndx==0 || ttime[ndx-1] < ptime[i])) {
+
+ if (nsam > nave) ycentptr[j][ndx] = ycent;
+ else {
+ if (ndx==0) ycentptr[j][ndx] = ycentdef[j];
+ else ycentptr[j][ndx] = ycentptr[j][ndx-1];
+ }
+
+
+ ndx += 1;
+ }
+
+ ndxs=ndx;
+ if (ndxs > nsec-1) ndxs = nsec-1;
+ nsam = 0;
+ ycent=0.;
+ ptst=ptime[i];
+ }
+ }
+ }
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished processing");
+ return (status);
+}
diff --git a/src/libcf/cf_check_digitizer.c b/src/libcf/cf_check_digitizer.c
new file mode 100644
index 0000000..81e9702
--- /dev/null
+++ b/src/libcf/cf_check_digitizer.c
@@ -0,0 +1,101 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_check_digitizer(fitsfile *infits);
+ *
+ * Description: Check the digitizer values against a reference file.
+ *
+ * Arguments: fitsfile *infits Input FITS file pointer
+ *
+ * Calls:
+ *
+ * Returns: 0 on success
+ *
+ * History: 01/16/03 1.1 jch Initial coding
+ * 01/28/03 1.2 wvd Check only values for this detector.
+ * 02/29/03 1.3 rch Correct error in making comparisons.
+ * 01/29/03 1.4 rdr Include math.h
+ * 03/04/03 1.5 peb Remove unused variables and header
+ * 05/20/03 1.6 rdr Add call to cf_proc_check
+ * 07/29/03 1.8 wvd If cf_proc_check fails, return errflg.
+ * 01/28/05 1.9 wvd On error, set EXP_STAT flag to -999 and
+ * issue a warning rather than an error.
+ * 03/11/05 1.10 wvd On error, write a warning to the file
+ * header using our standard format.
+ * 03/30/07 1.11 wvd On error, set EXP_STAT flag to -2,
+ * rather than -999.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <math.h>
+#include "calfuse.h"
+
+#define N 32
+
+int
+cf_check_digitizer(fitsfile *infits)
+{
+ char CF_PRGM_ID[] = "cf_check_digitizer";
+ char CF_VER_NUM[] = "1.11";
+
+ char comment[FLEN_COMMENT], datestr[FLEN_CARD],
+ file_name[FLEN_VALUE], detector[FLEN_VALUE];
+ fitsfile *digifits;
+ int errflg=0, status=0, i, first, last, exp_stat = -2;
+ int timeref;
+ float val, ref_val;
+
+ char key[N][9] = {"DET1ASCL", "DET1BSCL", "DET1AXOF", "DET1BXOF", "DET1AUCT", "DET1BUCT", "DET1ABWK", "DET1BBWK", "DET1AEWK", "DET1BEWK", "DET1ABSL", "DET1BBSL", "DET1ALCT", "DET1BLCT", "DET1ALTT", "DET1BLTT", "DET2ASCL", "DET2BSCL", "DET2AXOF", "DET2BXOF", "DET2AUCT", "DET2BUCT", "DET2ABWK", "DET2BBWK", "DET2AEWK", "DET2BEWK", "DET2ABSL", "DET2BBSL", "DET2ALCT", "DET2BLCT", "DET2ALTT", "DET2BLTT"};
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ if ((errflg = cf_proc_check(infits, CF_PRGM_ID))) return errflg;
+
+ /*
+ * Determine which detector produced our data file.
+ */
+ FITS_read_key(infits, TSTRING, "DETECTOR", detector, 0, &status);
+ if (detector[0] == '1')
+ first = 0, last = N/2;
+ else
+ first = N/2, last = N;
+
+ /*
+ * Read the digitizer reference file name and open it.
+ */
+ FITS_read_key(infits, TSTRING, "DIGI_CAL", file_name, 0, &status);
+ FITS_open_file(&digifits, cf_cal_file(file_name), READONLY, &status);
+
+ /*
+ * Read keywords. Compare with reference values.
+ */
+ for (i = first; i < last; i++) {
+ FITS_read_key(infits, TFLOAT, key[i], &val, NULL, &status);
+ FITS_read_key(digifits, TFLOAT, key[i], &ref_val, NULL, &status);
+
+ if (fabs(val - ref_val) > 2.0) {
+ cf_if_warning("Digitizer keyword %s is out of bounds.", key[i]);
+ sprintf(comment, "Data suspect: keyword %s out of bounds", key[i]);
+ FITS_update_key(infits, TINT, "EXP_STAT", &exp_stat, comment,
+ &status);
+ FITS_write_comment(infits, " ", &status);
+ sprintf(comment, "Data are suspect. Keyword %s is out of bounds.",
+ key[i]);
+ FITS_write_comment(infits, comment, &status);
+ fits_get_system_time(datestr, &timeref, &status);
+ sprintf(comment, "CalFUSE v%s %.10s", CALFUSE_VERSION, datestr);
+ FITS_write_comment(infits, comment, &status);
+ FITS_write_comment(infits, " ", &status);
+ }
+ }
+
+ FITS_close_file(digifits, &status);
+ cf_proc_update(infits, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return 0;
+}
diff --git a/src/libcf/cf_convert_to_ergs.c b/src/libcf/cf_convert_to_ergs.c
new file mode 100644
index 0000000..2eaaeef
--- /dev/null
+++ b/src/libcf/cf_convert_to_ergs.c
@@ -0,0 +1,182 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_convert_to_ergs(fitsfile *header, long nevents,
+ * float *weight, float *ergcm2, unsigned char *channel,
+ * float *lambda);
+ *
+ * Description: Convert counts to ergs.
+ *
+ * Arguments: *header Input FITS file pointer
+ * nevents number of points in the photon list
+ * *weight weight of each event
+ * *ergcm2 flux (returned)
+ * *channel channel number in the photon list
+ * *lambda wavelength in the photon list
+ *
+ * Returns: 0 (int) upon successful completion.
+ *
+ * History: 01/02/03 1.0 jch Initial coding
+ * 02/10/03 1.1 wvd Install
+ * 02/10/03 1.2 wvd Replace FLUX_CAL with AEFF_CAL
+ * Define HC in calfusettag.h
+ * Don't sort photons by wavelength
+ * 03/04/03 1.3 peb Fixed sprintf argument formating,
+ * deleted unused variable, localized
+ * scope of some variables, made sure
+ * that memory is allocated for aeff,
+ * and fixed memory leak with aeff,
+ * aeff1, and aeff2.
+ * 03/11/03 1.4 wvd Changed channel to type char
+ * 03/12/03 1.5 wvd Fixed bug in calculation of dl.
+ * If unable to calculate flux,
+ * set channel[k] = 0.
+ * Made aeff, aeff1, aeff2 doubles
+ * to match aeff*fit files.
+ * 05/07/03 1.6 wvd Change ERGCM2S to ERGCM2 throughout.
+ * Don't divide by EXPTIME.
+ * Use cf_verbose throughout.
+ * 05/16/03 1.7 wvd Update version number.
+ * 05/20/03 1.8 rdr Add call to cf_proc_check
+ * 06/11/03 1.9 wvd Pass datatype to cf_read_col.
+ * Change calfusettag.h to calfuse.h
+ * 08/25/03 1.10 wvd Change coltype from string to int
+ * in cf_read_col.
+ * 09/17/03 1.11 wvd Change aeff arrays to type float
+ * to match AEFF_CAL files.
+ * 11/05/03 1.12 wvd Change channel to unsigned char
+ * 04/27/04 1.13 wvd Print out relative weighting of
+ * the two effective-area files.
+ * 11/18/05 1.14 wvd Change aeff arrays to type double
+ * to match AEFF_CAL files.
+ * 04/07/07 1.15 wvd Clean up compiler warnings.
+ * 04/07/07 1.16 wvd Clean up compiler warnings.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "calfuse.h"
+
+static char CF_PRGM_ID[] = "cf_convert_to_ergs";
+static char CF_VER_NUM[] = "1.16";
+
+
+int
+cf_convert_to_ergs(fitsfile *header, long nevents, float *weight,
+ float *ergcm2, unsigned char *channel, float *lambda)
+{
+ char aeff1file[FLEN_VALUE], aeff2file[FLEN_VALUE];
+ int ap, errflg=0, interp=0, status=0;
+ float sig1=0, sig2=0;
+ fitsfile *aeff1fits, *aeff2fits;
+
+ /* Enter a timestamp into the log. */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ /* Read the AEFF1CAL and AEFF2CAL keywords. */
+ FITS_read_key(header, TSTRING, "AEFF1CAL", aeff1file, NULL, &status);
+ FITS_read_key(header, TSTRING, "AEFF2CAL", aeff2file, NULL, &status);
+
+ /* Open the calibration files. */
+ FITS_open_file(&aeff1fits, cf_cal_file(aeff1file), READONLY, &status);
+
+ /* Determine whether we need to interpolate getween AEFF_CAL files. */
+ if (strcmp(aeff1file, aeff2file)) {
+ float dt1, dt2, dt3, a1date, a2date, expstart, expend, expmjd;
+ interp = 1;
+ FITS_open_file(&aeff2fits, cf_cal_file(aeff2file), READONLY, &status);
+ FITS_read_key(aeff1fits, TFLOAT, "EFFMJD", &a1date, NULL, &status);
+ FITS_read_key(aeff2fits, TFLOAT, "EFFMJD", &a2date, NULL, &status);
+
+ /* Linearly interpolate the aeff according to exposure time */
+ FITS_read_key(header, TFLOAT, "EXPSTART", &expstart, NULL, &status);
+ FITS_read_key(header, TFLOAT, "EXPEND", &expend, NULL, &status);
+ expmjd = (expstart + expend) / 2.;
+ dt1=a2date-a1date;
+ dt2=a2date-expmjd;
+ dt3=expmjd-a1date;
+ if (dt1 < 0.01) {
+ cf_if_error("Calibration file dates are identical: "
+ "cal1=%10.5f obs=%10.5f cal2=%10.5f", a1date, expmjd, a2date);
+ }
+ sig1=dt2/dt1;
+ sig2=dt3/dt1;
+ cf_verbose(3, "Interpolation: %0.2f x %s, %0.2f x %s",
+ sig1, aeff1file, sig2, aeff2file);
+ }
+ else
+ cf_verbose(3, "No interpolation. Using Aeff file %s", aeff1file);
+
+ /* Step through the apertures, skipping aperture 4 (pinhole) */
+ for (ap = 1; ap < 8; ap++) {
+ long jmax=0, /* size of wave and aeff arrays */
+ j, /* index through wave and aeff */
+ k; /* index through photon array */
+
+ double *aeff;
+ float *wavelength, dl, w0;
+
+ if (ap == 4)
+ continue;
+
+ if (interp == 1) {
+ /* Interpolate the calibrated aeff. */
+ double *aeff1, *aeff2;
+
+ FITS_movabs_hdu(aeff1fits, ap+1, NULL, &status);
+ jmax = cf_read_col(aeff1fits, TDOUBLE, "AREA", (void **) &aeff1);
+
+ FITS_movabs_hdu(aeff2fits, ap+1, NULL, &status);
+ jmax = cf_read_col(aeff2fits, TDOUBLE, "AREA", (void **) &aeff2);
+
+ aeff = (double *) cf_calloc(jmax, sizeof(double));
+
+ for (j = 0; j < jmax; j++) {
+ aeff[j] = sig1 * aeff1[j] + sig2 * aeff2[j];
+ }
+ free(aeff1);
+ free(aeff2);
+ } else {
+ FITS_movabs_hdu(aeff1fits, ap+1, NULL, &status);
+ jmax = cf_read_col(aeff1fits, TDOUBLE, "AREA", (void **) &aeff);
+ }
+
+ /* Read wavelength array in AEFF_CAL file */
+ jmax = cf_read_col(aeff1fits, TFLOAT, "WAVE", (void **) &wavelength);
+
+ /* Compute translation from wavelength to pixel within AEFF_CAL file. */
+ w0 = wavelength[0];
+ dl = (wavelength[jmax-1] - wavelength[0]) / (jmax - 1);
+
+ /* Go through the photon list */
+ for (k = 0; k < nevents; k++) {
+ if (channel[k] == ap) {
+ long j = (lambda[k] - w0) / dl + 0.5;
+ if (j >= 0L && j < jmax)
+ ergcm2[k] = weight[k]*HC/lambda[k]/aeff[j];
+ else
+ channel[k] = 0;
+ }
+ }
+ free(wavelength);
+ free(aeff);
+ }
+
+ /* Clean up. */
+ FITS_close_file(aeff1fits, &status);
+ if (interp == 1)
+ FITS_close_file(aeff2fits, &status);
+
+ /* Update processing flags. */
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done Processing");
+ return (status);
+}
diff --git a/src/libcf/cf_count_rate_y_distort.c b/src/libcf/cf_count_rate_y_distort.c
new file mode 100644
index 0000000..a42a4ac
--- /dev/null
+++ b/src/libcf/cf_count_rate_y_distort.c
@@ -0,0 +1,157 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_count_rate_y_distort(fitsfile *header, long nevents,
+ * float *time, float *yfarf,
+ * unsigned char *locflags, long nseconds,
+ * float *timeline, float *fec_rate)
+ *
+ * Description: Corrects the Y position of each event based on the
+ * instantaneous count rate.
+ *
+ * Arguments: fitsfile *header Pointer to FITS file containing the
+ * header of the intermediate data file
+ * long nevents The number of events
+ * float *time An array of event times
+ * float *yfarf An array of event Y positions
+ * unsigned char *locflags Location flags for each event
+ * long nseconds The number of seconds in the timeline
+ * float *timeline The timeline in seconds
+ * float *fec_rate Front End Counter rate
+ *
+ * Calls:
+ *
+ * Return: 0 on success
+ *
+ * History: 08/06/02 1.1 peb Begin work
+ * 10/27/02 1.2 peb Added a time-dependent correction
+ * which uses the Front End Counter
+ * (FEC) rates.
+ * 11/11/02 1.3 peb Removed non-time-dependent correction.
+ * (Code now expects timeline extension
+ * data.)
+ * 11/11/02 1.4 peb Fixed compile error - added buffer
+ * variable.
+ * 11/12/02 1.5 peb Added check to move only events in
+ * active region.
+ * 03/11/03 1.6 wvd Changed locflags to unsigned char
+ * 05/20/03 1.7 rdr Added call to cf_proc_check
+ * 07/29/03 1.8 wvd If cf_proc_check fails, return errflg.
+ * 08/04/03 1.9 wvd Convert fec_rate to type short.
+ * 11/26/03 1.10 wvd Convert fec_rate to type float.
+ * 08/18/04 1.11 wvd Interpolate stretch correction among
+ * tabulated Y values.
+ * 10/06/05 1.12 wvd For HIST data, set each element of
+ * fec_rate to weighted mean of array.
+ * 04/07/07 1.13 wvd Clean up compiler warnings.
+ *
+ ****************************************************************************/
+
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include "calfuse.h"
+
+int
+cf_count_rate_y_distort(fitsfile *header, long nevents, float *time,
+ float *yfarf, unsigned char *locflags, long nseconds,
+ float *timeline, float *fec_rate)
+{
+ char CF_PRGM_ID[] = "cf_count_rate_y_distort";
+ char CF_VER_NUM[] = "1.13";
+
+ char instmode[FLEN_VALUE], ystrfile[FLEN_VALUE];
+ short xlen, ylen;
+ int errflg=0, hdutype, status=0, anynull=0;
+ int i, ii, i_max, rndx, yndx;
+ long j, k;
+ float *ystretch, ystr_1d[NYMAX], flt=0.;
+ fitsfile *ystrfits;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ /*
+ * For HIST data, replace fec_rate with weighted mean value.
+ */
+ FITS_read_key(header, TSTRING, "INSTMODE", instmode, NULL, &status);
+ if (!strncmp(instmode, "HIST", 4)) {
+ float *mean_rate, ratio, numerator=0, denominator=0;
+ mean_rate = (float *) cf_malloc(sizeof(float) * nseconds);
+ for (i = 0; i < nseconds; i++) {
+ numerator += fec_rate[i] * fec_rate[i];
+ denominator += fec_rate[i];
+ }
+ ratio = numerator / denominator;
+ for (i = 0; i < nseconds; i++) mean_rate[i] = ratio;
+ fec_rate = mean_rate;
+ cf_verbose(2, "Weighted mean count rate = %f", ratio);
+ }
+
+ /*
+ * Read the rate calibration file.
+ */
+ FITS_read_key(header, TSTRING, "RATE_CAL", ystrfile, NULL, &status);
+ FITS_open_file(&ystrfits, cf_cal_file(ystrfile), READONLY, &status);
+ FITS_movabs_hdu(ystrfits, 2, &hdutype, &status);
+ FITS_read_key(ystrfits, TSHORT, "NAXIS1", &xlen, 0, &status);
+ FITS_read_key(ystrfits, TSHORT, "NAXIS2", &ylen, 0, &status);
+ ystretch = (float *) cf_malloc(sizeof(float)*xlen*ylen);
+ FITS_read_img(ystrfits, TFLOAT, 1L, xlen*ylen, &flt, ystretch, &anynull,
+ &status);
+ FITS_close_file(ystrfits, &status);
+
+ i_max = (xlen-1) * 10;
+ for(j=k=0; j<nevents; j++) { /* Loop through all events. */
+ /*
+ * Move only events in active region.
+ */
+ if (!(locflags[j] & LOCATION_SHLD)) {
+
+ /* If time has changed, compute a new ystretch array. */
+ while(timeline[k]-FRAME_TOLERANCE < time[j] && k < nseconds) {
+ k++;
+
+ /* This index reflects the count rate. */
+ rndx = (int)((log10(fec_rate[k])-1.)*10.);
+ if (rndx < 0)
+ rndx = 0;
+ else if (rndx >= ylen)
+ rndx = ylen-1;
+
+ /*
+ * This index reflects Y position on the detector.
+ * Shift information is provided for every 10 Y pixels.
+ * We interpolate to the nearest Y pixel. Otherwise,
+ * counts pile up at the 10-pixel boundaries.
+ */
+ for (i = 0; i < i_max; i++) {
+ ii = (i/10) * 10;
+ ystr_1d[i] = ((ii+10-i) * (ystretch+rndx*xlen)[i/10] +
+ (i - ii) * (ystretch+rndx*xlen)[i/10+1]) / 10.;
+ }
+ for ( ; i < NYMAX; i++)
+ ystr_1d[i] = (ystretch+rndx*xlen)[xlen-1];
+ }
+
+ /* Now apply the shift. */
+ yndx = (int) yfarf[j];
+ if (yndx < 0)
+ yndx = 0;
+ else if (yndx >= NYMAX)
+ yndx = NYMAX-1;
+ yfarf[j] -= ystr_1d[yndx];
+ }
+ }
+ free(ystretch);
+ if (!strncmp(instmode, "HIST", 4)) free(fec_rate);
+
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return 0;
+}
diff --git a/src/libcf/cf_dispersion.c b/src/libcf/cf_dispersion.c
new file mode 100644
index 0000000..19a47c1
--- /dev/null
+++ b/src/libcf/cf_dispersion.c
@@ -0,0 +1,85 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Description: Assigns wavelength to each photon in data stream.
+ *
+ * Returns: 0 upon successful completion.
+ *
+ * History: 05/15/06 1.1 wvd Adapt from cf_astigmatism_and_dispersion.c
+ * Program incorporates cf_x2lambda.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "calfuse.h"
+
+static char CF_PRGM_ID[] = "cf_dispersion";
+static char CF_VER_NUM[] = "1.1";
+
+
+int
+cf_dispersion(fitsfile *infits, long nevents, float *x,
+ unsigned char *channel, float *lambda)
+{
+ char wave_file[FLEN_VALUE];
+ float *wavelength=NULL;
+ float x_lower, x_upper, weight_l, weight_u, wave_l, wave_u;
+ int ap, errflg=0, nwave, xl_int, xu_int, status=0;
+ long k;
+ fitsfile *wavefits;
+
+ /* Enter a timestamp into the log. */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ if ((errflg = cf_proc_check(infits, CF_PRGM_ID))) return errflg;
+
+ FITS_read_key(infits, TSTRING, "WAVE_CAL", wave_file, NULL, &status);
+ FITS_open_file(&wavefits, cf_cal_file(wave_file), READONLY, &status);
+
+ /* Cycle through channels, skipping #4 (pinhole) */
+ for (ap = 1; ap < 8; ap++) {
+ if (ap == 4) continue;
+
+ FITS_movabs_hdu(wavefits, ap+1, NULL, &status);
+ nwave = cf_read_col(wavefits, TFLOAT, "WAVELENGTH", (void **) &wavelength);
+
+ /* Compute wavelength for each event by interpolation. */
+ for (k = 0; k < nevents; k++) {
+ if (channel[k] == ap) {
+ x_lower = floor(x[k]);
+ x_upper = ceil(x[k]);
+ xl_int = (int) (x_lower + 0.5);
+ xu_int = (int) (x_upper + 0.5);
+ if (xl_int >= 0 && xu_int < nwave) {
+ if (xl_int == xu_int)
+ lambda[k] = wavelength[xl_int];
+ else {
+ weight_l = x_upper - x[k];
+ weight_u = x[k] - x_lower;
+ wave_l = wavelength[xl_int];
+ wave_u = wavelength[xu_int];
+ lambda[k] = wave_l * weight_l + wave_u * weight_u;
+ }
+ }
+ else
+ channel[k] = 0;
+ }
+ }
+ /* Space for wavelength array is allocated in each loop. */
+ free(wavelength);
+ }
+ FITS_close_file(wavefits, &status);
+
+ /* Update processing flags. */
+ cf_proc_update(infits, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done Processing");
+
+ return status;
+}
diff --git a/src/libcf/cf_doppler_and_heliocentric.c b/src/libcf/cf_doppler_and_heliocentric.c
new file mode 100644
index 0000000..f95672d
--- /dev/null
+++ b/src/libcf/cf_doppler_and_heliocentric.c
@@ -0,0 +1,87 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_doppler_and_heliocentric(*infits, nevents, *photon_time,
+ * *channel, *lambda, nseconds, *timeline_time,
+ * *timeline_velocity);
+ *
+ * Description: Doppler-correct each photon for both heliocentric and orbital
+ * motions. Resulting wavelength scale is heliocentric.
+ *
+ * Arguments: *infits Input FITS file pointer
+ * nevents number of points in the photon list
+ * *photon_time time array of photon events
+ * *channel channel number in the photon list
+ * *lambda wavelength in the photon list
+ * nseconds number of points in the timeline table
+ * *timeline_time time array of timeline list
+ * *timeline_velocity orbital velocity in km/s
+ *
+ * Returns: O upon successful completion
+ *
+ * History: 12/09/2002 jch 1.1 Initial coding
+ * 12/11/2002 wvd 1.2 Move Doppler calculation to separate
+ * loop.
+ * 12/11/2002 wvd 1.3 Change nevents and nseconds to long.
+ * 12/20/2002 wvd 1.4 Change channel to unsigned char.
+ * 02/24/03 peb 1.5 Change include file to calfusettag.h
+ * and calfitsio.h
+ * 03/11/2003 wvd 1.6 Change channel to char.
+ * 05/20/2003 rdr 1.7 Added call to cf_proc_check
+ * 09/17/2003 wvd 1.8 Return errflg from cf_prock_check
+ * 10/21/2003 wvd 1.9 Change channel to unsigned char.
+ * 04/21/2004 bjg 1.10 Cosmetic change to prevent warning
+ * with gcc -Wall
+ * 11/17/2005 wvd 1.11 Change velocity and doppler to doubles.
+ *
+ ****************************************************************************/
+
+#include <stdlib.h>
+#include "calfuse.h"
+
+char CF_PRGM_ID[] = "cf_doppler_and_heliocentric";
+char CF_VER_NUM[] = "1.11";
+
+
+int cf_doppler_and_heliocentric(fitsfile *infits, long nevents,
+ float *photon_time, unsigned char *channel, float *lambda,
+ long nseconds, float *timeline_time, float *timeline_velocity)
+{
+ int errflg=0, status=0;
+ long i, k;
+ float v_helio; /* heliocentric velocity in km/sec */
+ double velocity, *doppler;
+
+ /* Enter a timestamp into the log. */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+
+ if ((errflg = cf_proc_check(infits, CF_PRGM_ID) )) return errflg;
+
+ FITS_read_key(infits, TFLOAT, "V_HELIO", &v_helio, NULL, &status);
+
+ /* Compute array of Doppler corrections. */
+ doppler = (double *) cf_malloc(sizeof(double) * nseconds);
+ for (k = 0; k < nseconds; k++) {
+ velocity = timeline_velocity[k] + v_helio;
+ doppler[k] = 1. + velocity / C;
+ }
+
+ /* Apply Doppler correction to each photon assigned to an aperture. */
+ k = 0;
+ for (i = 0; i < nevents; i++)
+ if (channel[i] > 0) {
+ while (photon_time[i] > timeline_time[k+1] - FRAME_TOLERANCE
+ && k < nseconds-1) k++;
+ lambda[i] *= doppler[k];
+ }
+ free (doppler);
+
+ /* Update processing flags. */
+ cf_proc_update(infits, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done Processing");
+ return (status);
+}
diff --git a/src/libcf/cf_electronics_dead_time.c b/src/libcf/cf_electronics_dead_time.c
new file mode 100644
index 0000000..38c2219
--- /dev/null
+++ b/src/libcf/cf_electronics_dead_time.c
@@ -0,0 +1,104 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_electronics_dead_time(fitsfile *header, long nseconds,
+ * float *fec_rate, float *elec_dtc)
+ *
+ * Description: Scales the weighting factor to correct for the electronics
+ * dead time.
+ *
+ * Arguments: fitsfile *header Pointer to FITS file containing the
+ * header of the intermediate data file
+ * long nseconds The number of timeline values
+ * float *fec_rate An array of Front End Counter (FEC)
+ * rates
+ * float *elec_dtc Dead-time correction array (returned)
+ * Calls:
+ *
+ * Return: 0 on success
+ *
+ * History: 10/27/02 1.1 peb Begin work
+ * 11/11/02 1.2 peb Corrected function description and
+ * added cf_timestamp after ELEC_COR
+ * check.
+ * 12/06/02 1.4 wvd Calculate DT correction for each time
+ * step, then apply to photons.
+ * Set keyword DET_DEAD.
+ * 05/20/03 1.5 rdr Added proc_check call
+ * 08/01/03 1.7 wvd Just calculate correction; don't
+ * apply it. Return elec_dtc array.
+ * 08/04/03 1.8 wvd Convert fec_rate to type short.
+ * 11/26/03 1.9 wvd Change fec_rate to type float.
+ * 02/12/04 1.10 wvd In verbose mode, write mean DTC.
+ * 04/07/07 1.11 wvd Clean up compiler warnings.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <math.h>
+#include "calfuse.h"
+
+int
+cf_electronics_dead_time(fitsfile *header, long nseconds,
+ float *fec_rate, float *elec_dtc)
+{
+ char CF_PRGM_ID[] = "cf_electronics_dead_time";
+ char CF_VER_NUM[] = "1.11";
+
+ char elecfile[FLEN_VALUE]={"\0"}, detector[FLEN_VALUE]={"\0"};
+ char keyword[FLEN_KEYWORD]={"\0"};
+ int errflg=0, status=0;
+ long k;
+ float abort, clock, state;
+ float mean_elec_dtc = 0.;
+ fitsfile *elecfits;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ FITS_read_key(header, TSTRING, "DETECTOR", detector, NULL, &status);
+ /*
+ * Get the electronics dead-time constants.
+ */
+ FITS_read_key(header, TSTRING, "ELEC_CAL", elecfile, NULL, &status);
+ FITS_open_file(&elecfits, cf_cal_file(elecfile), READONLY, &status);
+ sprintf(keyword, "ABORT_%s", detector);
+ FITS_read_key(elecfits, TFLOAT, keyword, &abort, NULL, &status);
+ sprintf(keyword, "CLOCK_%s", detector);
+ FITS_read_key(elecfits, TFLOAT, keyword, &clock, NULL, &status);
+ sprintf(keyword, "STATE_%s", detector);
+ FITS_read_key(elecfits, TFLOAT, keyword, &state, NULL, &status);
+ FITS_close_file(elecfits, &status);
+ /*
+ * Calculate dead-time correction.
+ */
+ for (k=0; k<nseconds; k++) {
+ float xa, x, x2, x4, x6, x10;
+
+ xa = exp(-fec_rate[k]*abort);
+ x = exp(-fec_rate[k]*clock);
+ x2 = x*x;
+ x4 = x2*x2;
+ x6 = x4*x2;
+ x10 = x6*x4;
+
+ elec_dtc[k] = (1. + fec_rate[k]*state + 4.*(x2-x) + x6 - x10)/xa;
+ if (elec_dtc[k] < 1.0) elec_dtc[k] = 1.0;
+ mean_elec_dtc += elec_dtc[k];
+ }
+ /*
+ * Calculate mean dead-time correction. Write to file header.
+ */
+ mean_elec_dtc /= nseconds;
+ cf_verbose(2, "Mean detector electronics dead-time correction: %f", mean_elec_dtc);
+ FITS_update_key(header, TFLOAT, "DET_DEAD", &mean_elec_dtc, NULL, &status);
+
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return 0;
+}
diff --git a/src/libcf/cf_error_msg.c b/src/libcf/cf_error_msg.c
new file mode 100644
index 0000000..12fb7e5
--- /dev/null
+++ b/src/libcf/cf_error_msg.c
@@ -0,0 +1,263 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_error_init(char *prgmid, char *vernum, FILE *errfile)
+ *
+ * Description: Writes a timestamp, the calling program name, and an
+ * error message to stdout for a cfitsio routine.
+ *
+ * Arguments: char *progid Name of the calling program
+ * char *vernum Version of the calling program
+ * (should be version control number)
+ * FILE *errfile Output file name (default is stderr)
+ *
+ * Returns: none
+ *
+ * History: 02/22/99 peb Some simple functions to make error
+ * reporting easier. Deficiencies in
+ * cfitsio make printing comprehensive
+ * messages difficult.
+ * 03/15/99 emm Added cf_if_memory_error.
+ * 04/30/99 peb Added cf_malloc and cf_calloc.
+ * 06/07/99 peb Added reporting of version number
+ * 09/01/99 emm Added fflush to see if it helps print
+ * out error messages on failure.
+ * 11/08/99 emm Added !!!!!!!! to error reporting to
+ * better delimit the error message and
+ * to make them more obvious.
+ * 04/23/01 1.10 wvd Make cf_malloc & cf_calloc print error
+ * message returned by malloc & calloc
+ * 03/07/03 1.2 peb Added cf_verbose and external variable
+ * verbose_level
+ * 03/13/03 1.3 peb Enabled cf_verbose, cf_if_warning, and
+ * cf_if_error to accept a variable
+ * number of parameters. Changed the
+ * output format to print month, day, time
+ * hostname, function name and message.
+ * 03/25/03 1.4 peb Changed output to print time, program,
+ * and version number for warning and
+ * error messages.
+ * Changed output to use verbose_level
+ * to modify output format:
+ * level==1 prints program, version, and
+ * message.
+ * level>=2 prints only message.
+ * 10/30/03 1.5 peb Replaced cftime function with strftime
+ * for UNIX compatibility.
+ * Removed unnecessary header files,
+ * malloc.h and unistd.h
+ * 04/07/04 1.6 bjg fflush stdout and error_file
+ *
+ ****************************************************************************/
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "fitsio.h"
+
+#define N_STYM 40
+
+int verbose_level=0;
+
+static char *program_name=NULL;
+static char *version_no=NULL;
+static FILE *error_file=NULL;
+
+void cf_error_init(const char *progid, const char *vernum, FILE *errfile)
+{
+ static char unknown[] = "unknown file";
+ static char version[] = "1.6";
+ if (progid) {
+ program_name = malloc(strlen(progid)+1);
+ strcpy(program_name, progid);
+ }
+ else {
+ program_name = malloc(strlen(unknown)+1);
+ strcpy(program_name, unknown);
+ }
+ if (vernum) {
+ version_no = malloc(strlen(vernum)+1);
+ strcpy(version_no, vernum);
+ }
+ else {
+ version_no = malloc(strlen(version)+1);
+ strcpy(version_no, version);
+ }
+ if (errfile)
+ error_file = errfile;
+ else
+ error_file = stderr;
+}
+
+void cf_verbose(int level, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+ if (verbose_level >= level) {
+ if (level == 1)
+ printf(" %s-%s: ", program_name, version_no);
+ else if (level >= 2)
+ printf("\t");
+ vprintf(format, args);
+ printf("\n");
+ fflush(stdout);
+ }
+
+ va_end(args);
+}
+
+void cf_if_warning(char *format, ...)
+{
+ char stym[N_STYM]={'\0'};
+ time_t tym;
+ va_list args;
+
+ va_start(args, format);
+ time(&tym);
+ strftime(stym, N_STYM, "%Y %b %e %T", localtime(&tym));
+
+ fprintf(error_file, "%s %s-%s: WARNING - ",
+ stym, program_name, version_no);
+ vfprintf(error_file, format, args);
+ fprintf(error_file, "\n");
+ fflush(error_file);
+ va_end(args);
+}
+
+void cf_if_error(char *format, ...)
+{
+ char stym[N_STYM]={'\0'};
+ time_t tym;
+ va_list args;
+
+ va_start(args, format);
+ time(&tym);
+ strftime(stym, N_STYM, "%Y %b %e %T", localtime(&tym));
+
+ fprintf(error_file, "%s %s-%s: ERROR - ",
+ stym, program_name, version_no);
+ vfprintf(error_file, format, args);
+ fprintf(error_file, "\n");
+
+ fflush(error_file);
+ va_end(args);
+ exit(1);
+}
+
+void cf_if_fits_warning(int status)
+{
+ if (status) {
+ char stym[N_STYM]={'\0'};
+ time_t tym;
+
+ time(&tym);
+ strftime(stym, N_STYM, "%Y %b %e %T", localtime(&tym));
+
+ fprintf(error_file, "%s %s-%s: FITS WARNING - ",
+ stym, program_name, version_no);
+ fits_report_error(error_file, status);
+ fflush(error_file);
+ }
+}
+
+void cf_if_fits_error(int status)
+{
+ if (status) {
+ char stym[N_STYM]={'\0'};
+ time_t tym;
+
+ time(&tym);
+ strftime(stym, N_STYM, "%Y %b %e %T", localtime(&tym));
+
+ fprintf(error_file, "%s %s-%s: FITS ERROR - ",
+ stym, program_name, version_no);
+ fits_report_error(error_file, status);
+ fflush(error_file);
+ exit(1);
+ }
+}
+
+void *cf_malloc(size_t size)
+{
+ void *ptr;
+
+ if (!(ptr = malloc(size))) {
+ char stym[N_STYM]={'\0'};
+ time_t tym;
+
+ time(&tym);
+ strftime(stym, N_STYM, "%Y %b %e %T", localtime(&tym));
+
+ fprintf(error_file, "%s %s-%s: ERROR - %s\n",
+ stym, "cf_malloc", "1.0", strerror(errno));
+
+ fflush(error_file);
+ exit(1);
+ /*
+ fprintf(error_file,
+ " \n malloc: Attemping to allocate %d bytes", size);
+ */
+ }
+ return ptr;
+}
+
+void *cf_calloc(size_t nelem, size_t elsize)
+{
+ void *ptr;
+
+ if (!(ptr = calloc(nelem, elsize))) {
+ char stym[N_STYM]={'\0'};
+ time_t tym;
+
+ time(&tym);
+ strftime(stym, N_STYM, "%Y %b %e %T", localtime(&tym));
+
+ fprintf(error_file, "%s %s-%s: ERROR - %s\n",
+ stym, "cf_calloc", "1.0", strerror(errno));
+
+ fflush(error_file);
+ exit(1);
+ /*
+ fprintf(error_file,
+ " \n calloc: Attemping to allocate %d bytes",
+ nelem * elsize);
+ */
+ }
+ return ptr;
+}
+
+void cf_if_memory_error(int status)
+{
+ time_t tym;
+
+ if (!status) {
+ time(&tym);
+ fprintf(error_file, "Memory allocation error in %s-%s: %s",
+ program_name, version_no, ctime(&tym));
+
+ /* Print out cfitsio error messages and continue */
+
+ fits_report_error(error_file, status);
+ fflush(error_file);
+ }
+}
+
+/*
+int main()
+{
+ int i;
+ cf_error_init("test program", "1.0", stdout);
+ cf_if_warning("Non-fatal error occurred");
+
+ for (i=0; i < 10; i++)
+ cf_if_fits_warning(i);
+ exit(0);
+}
+*/
diff --git a/src/libcf/cf_extraction_limits.c b/src/libcf/cf_extraction_limits.c
new file mode 100644
index 0000000..5567d14
--- /dev/null
+++ b/src/libcf/cf_extraction_limits.c
@@ -0,0 +1,137 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_extraction_limits(fitsfile *infits, int aperture,
+ * int srctype, short **ylow, short **yhigh, short *xmin,
+ * short *xmax)
+ *
+ * Description: Read the extraction limits for a given aperture and source
+ * type (point or extended) from the CHID_CAL file. Shift these
+ * limits to match the centroid of the observed spectrum, if
+ * that measurement has been performed. For point sources
+ * observed in HIST mode, pad YLOW and YHIGH by SPECBINY pixels.
+ *
+ * Arguments: fitsfile *infits Pointer to the location of the FITS
+ * header of the Intermediate Data File
+ * int aperture aperture designation (1 through 8)
+ * int srctype 0 = point source, 1 = extended
+ * short **ylow lower Y bound for the extraction window
+ * short **yhigh upper Y bound for the extraction window
+ * short *xmin lower X bound for the extraction window
+ * short *xmax upper X bound for the extraction window
+ *
+ * Calls: None
+ *
+ * Return: npts = length of arrays ylow and yhigh
+ *
+ * History: 08/22/03 1.1 BJG Based on subroutine from v1.6 of
+ * cf_bad_pixels.c by RDR.
+ * Change coltype from char to int in
+ * cf_read_col.
+ * 03/15/05 1.2 wvd Read HIST_PAD from appropriate HDU
+ * of CHID_CAL file and pad apertures
+ * if data are in HIST mode.
+ * 03/16/05 1.3 wvd For extended sources, return large
+ * apertures.
+ * 10/06/05 1.4 wvd Always pad apertures by 8 pixels
+ * in HIST mode.
+ * 05/03/06 1.5 wvd Always pad apertures by SPECBINY
+ * pixels in HIST mode.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "calfuse.h"
+
+long cf_extraction_limits(fitsfile *infits, int aperture, int srctype,
+ short **ylow, short **yhigh, short *xmin, short *xmax) {
+
+ char CF_PRGM_ID[] = "cf_extraction_limits";
+ char CF_VER_NUM[] = "1.5";
+
+ fitsfile *chidfits ;
+ int status=0, hdutype;
+ int dcent, hdu ;
+ short histpad=0, specbiny;
+ short xmint, xmaxt, *ylowt=NULL, *yhight=NULL;
+ float ycent_data, ycent_tab ;
+ long j, npts ;
+ char ycentname[FLEN_VALUE]={'\0'}, chidfile[FLEN_VALUE] ;
+ char instmode[FLEN_VALUE];
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ /* Read the measured centroid from the input file header. */
+ FITS_movabs_hdu(infits, 1, &hdutype, &status) ;
+ sprintf(ycentname,"YCENT%1d",aperture) ;
+ FITS_read_key(infits, TFLOAT, ycentname, &ycent_data, NULL, &status);
+ cf_verbose(3, "Reading extraction limits for aperture %d ", aperture) ;
+ cf_verbose(3, "ycent keyword = %s, value=%5.1f ", ycentname, ycent_data) ;
+
+ /* Read instrument mode and binning factor from file header. */
+ FITS_read_key(infits, TSTRING, "INSTMODE", instmode, NULL, &status);
+ FITS_read_key(infits, TSHORT, "SPECBINY", &specbiny, NULL, &status);
+
+ /* Which HDU to read depends on both channel and source type. */
+ hdu = aperture + 1;
+ if (srctype) hdu += 8;
+
+ /* Open the CHID_CAL file. */
+ FITS_read_key(infits, TSTRING, "CHID_CAL", chidfile, NULL, &status);
+ cf_verbose(3, "spectral extraction file = %s, HDU = %d", chidfile, hdu);
+ FITS_open_file(&chidfits, cf_cal_file(chidfile), READONLY, &status);
+ FITS_movabs_hdu(chidfits, hdu, &hdutype, &status);
+
+ /* Read X limits of extraction window. */
+ FITS_read_key(chidfits, TSHORT, "XMIN", &xmint, NULL, &status);
+ FITS_read_key(chidfits, TSHORT, "XMAX", &xmaxt, NULL, &status);
+
+ /* Read centroid of tabulated extraction window. */
+ FITS_read_key(chidfits,TFLOAT,"CENTROID",&ycent_tab,NULL,&status);
+
+ /* For point-source data taken in HIST mode, pad window. */
+ if ((!strncmp(instmode, "HIST", 4)) && !srctype) histpad = specbiny;
+ /* FITS_read_key(chidfits,TSHORT,"HIST_PAD",&histpad,NULL,&status); */
+
+ /* Read the upper and lower boundaries of the extraction slit. */
+ npts=cf_read_col(chidfits, TSHORT, "YLOW", (void **) &ylowt);
+ npts=cf_read_col(chidfits, TSHORT, "YHIGH", (void **) &yhight);
+ FITS_close_file(chidfits, &status) ;
+
+ /* If the spectral centroid has been measured, shift the aperture
+ limits so that the tabulated centroid equals the measured value. */
+ if (ycent_data > 1 && ycent_data < NYMAX) {
+ dcent = (short) cf_nint(ycent_data - ycent_tab) ;
+ cf_verbose(3, "shifting extraction window: ycent_tab=%5.1f, delta=%d",
+ ycent_tab, dcent) ;
+ for (j = xmint; j <= xmaxt; j++) {
+ ylowt[j] += dcent ;
+ yhight[j] += dcent ;
+ }
+ }
+
+ /* If HIST_PAD > 0, pad the extraction window, top and bottom. */
+ if (histpad > 0) {
+ cf_verbose(3, "HIST point source. "
+ "Padding extraction window by +/- %d pixels", histpad);
+ for (j = xmint; j <= xmaxt; j++) {
+ ylowt[j] -= histpad ;
+ yhight[j] += histpad ;
+ }
+ }
+
+ /* Return the extraction limits. */
+ *xmin = xmint;
+ *xmax = xmaxt;
+ *ylow = ylowt ;
+ *yhigh = yhight ;
+
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return npts ;
+}
diff --git a/src/libcf/cf_fes_proc_check.c b/src/libcf/cf_fes_proc_check.c
new file mode 100644
index 0000000..74a41b4
--- /dev/null
+++ b/src/libcf/cf_fes_proc_check.c
@@ -0,0 +1,94 @@
+/******************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ ******************************************************************************
+ *
+ * Synopsis: cf_fes_proc_check(fitsfile *fptr, char *prog_id)
+ *
+ * Description: cf_fes_proc_check will determine if a given calibration
+ * step is to be performed on the data. It will also
+ * determine whether all previous steps have been
+ * completed successfully.
+ *
+ * Arguments: fitsfile *fptr Pointer to input file
+ * char *prog_id Procedure name
+ *
+ * History: 06/21/98 emm Begin work.
+ * 08/19/2004 1.1 wvd Move to v3.0, add CF_VER_NUM,
+ * change cf_errmsg to cf_if_error.
+ * 09/07/2007 1.2 bot Removed unused CF_VER_NUM and CF_PRGM_ID
+ *
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include "calfuse.h"
+#define MAXCHARS 120
+
+int cf_fes_proc_check(fitsfile *fptr, char *prog_id)
+{
+ int i,j, status=0;
+ char comment[FLEN_CARD], key_value[FLEN_CARD];
+ char complete[19], skipped[19], perform[19];
+
+ /*
+ * The calfuse.h file contains the definitions of fes_keyword_tab
+ * NUM_PROC_STEPS, and CALIBRATION_STEP_KEYS.
+ */
+ struct fes_keyword_tab keytab[NUM_FES_PROC_STEPS]=FES_CALIBRATION_STEP_KEYS;
+ status=0;
+
+ strncpy(complete,"COMPLETE ",19);
+ strncpy(skipped, "SKIPPED ",19);
+ strncpy(perform, "PERFORM ",19);
+
+ j=0;
+ /*
+ * First, determine if this procedure is even supposed to be
+ * run on this data.
+ */
+ while ((strncmp(keytab[j].proc,prog_id, strlen(keytab[j].proc)) != 0) &&
+ (j < NUM_FES_PROC_STEPS)) j++;
+ if ((j >= NUM_FES_PROC_STEPS) ||
+ (strncmp(keytab[j].value,perform,7))) {
+ cf_if_error("Processing step does not "
+ "need to be run on this type of data.");
+ fprintf(stderr," Step %18.18s does not need "
+ "to be run.\n",prog_id);
+ return 1;
+ }
+
+ status=0;
+ fits_read_key_str(fptr, keytab[j].name, key_value, comment,
+ &status);
+
+ /* Now check to see if the step has already been completed. */
+ if (strncmp(key_value,complete,7)==0) {
+ cf_if_error("Processing step has already been completed.\n");
+ fprintf(stderr," Step %18.18s does not need to be run.\n",
+ prog_id);
+ return 1;
+ }
+
+ /* Now determine if the previous programs are all complete. */
+ for (i=0; i<j; i++) {
+ fits_read_key_str(fptr, keytab[i].name, key_value, comment,
+ &status);
+ if (strncmp(keytab[i].value,perform,7)==0) {
+ if (strncmp(key_value,complete,8) &&
+ strncmp(key_value,skipped,7)) {
+ cf_if_error("Processing step not completed.");
+ fprintf(stderr," Step %8.8s has value "
+ "%8.8s.\n",keytab[i].name,key_value);
+ fprintf(stderr," It should be either "
+ "COMPLETE or SKIPPED before running "
+ "%18.18s\n",prog_id);
+ return 1;
+ } /* End if check on key_value */
+ } /* End check on keytab[i].value */
+ } /* End for */
+
+ /* If we got to here, it must be OK */
+ return 0;
+}
diff --git a/src/libcf/cf_fes_proc_update.c b/src/libcf/cf_fes_proc_update.c
new file mode 100644
index 0000000..14f1f78
--- /dev/null
+++ b/src/libcf/cf_fes_proc_update.c
@@ -0,0 +1,68 @@
+/******************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ ******************************************************************************
+ *
+ * Synopsis: cf_fes_proc_update(fitsfile *fptr, char *prgm_id,
+ * char *key_value)
+ *
+ * Description: cf_fes_proc_update will update the FITS header keyword
+ * which corresponds to the given prgm_id in the file
+ * fptr to the value given in key_value.
+ *
+ * Arguments: fitsfile *fptr Pointer to input file
+ * char *prgm_id Procedure name
+ * char *key_value Updated procedure status
+ *
+ * History: 07/21/98 emm Begin work.
+ * 08/19/2004 1.1 wvd Move to v3.0, add CF_VER_NUM,
+ * change cf_errmsg to cf_if_error.
+ * Delete fits_print_err.
+ * 09/07/2007 1.2 bot Removed unused CF_VER_NUM and CF_PRGM_ID
+ *
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include "calfuse.h"
+
+int cf_fes_proc_update(fitsfile *fptr, char *prgm_id, char *key_value)
+{
+ int i=0, status=0;
+ /*
+ * The calfuse.h file contains the definitions of fes_keyword_tab
+ * NUM_FES_PROC_STEPS, and FES_CALIBRATION_STEP_KEYS.
+ */
+ struct fes_keyword_tab keytab[NUM_FES_PROC_STEPS]=FES_CALIBRATION_STEP_KEYS;
+ /*
+ * Find the keyword associated with prgm_id by looping
+ * through keytab[i].proc
+ */
+ while ((strncmp(keytab[i].proc,prgm_id, strlen(keytab[i].proc)) != 0) &&
+ (i < NUM_FES_PROC_STEPS)) i++;
+ if (i < NUM_FES_PROC_STEPS) {
+ /*
+ * We found a match to prgm_id, so change the associated
+ * keyword in the header.
+ */
+ fits_modify_key_str(fptr,keytab[i].name,key_value,
+ "&",&status);
+ if (status) {
+ cf_if_error("Error updating keyword");
+ return status;
+ }
+ }
+ else {
+ /*
+ * The given prgm_id did not match any of the known
+ * keytab[i].hist_proc, so return 1.
+ */
+ cf_if_error("Program ID does not apply to this"
+ "type of file, could not update");
+ fprintf(stderr,"Failed to update program id:%20.20s\n",
+ prgm_id);
+ return 1;
+ }
+ return status;
+}
diff --git a/src/libcf/cf_fifo_dead_time.c b/src/libcf/cf_fifo_dead_time.c
new file mode 100644
index 0000000..67ac065
--- /dev/null
+++ b/src/libcf/cf_fifo_dead_time.c
@@ -0,0 +1,164 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_fifo_dead_time(fitsfile *header, long nevents,
+ * float *ptime, long nseconds, float *ttime, float *aic_rate,
+ * float *ids_dtc)
+ *
+ * Description: Searches high-count-rate observations for data drop-outs
+ * due to FIFO overflows. If found, calculates time-dependent
+ * scale factor to correct for lost photon events.
+ *
+ * Arguments: fitsfile *header Pointer to FITS file containing the
+ * header of the intermediate data file
+ * long nevents Number of photon events in the file
+ * float *ptime Detection time for each photon
+ * long nseconds The number of timeline values
+ * float *ttime Tabulated times in the timeline
+ * float *aic_rate Active Image Counter array
+ * float *ids_dtc IDS dead-time correction array
+ * (modified)
+ *
+ * Calls:
+ *
+ * Return: 0 on success
+ *
+ * History of cf_screen_fifo_overflow:
+ *
+ * 02/10/04 1.1 wvd Begin work
+ * 02/17/05 1.2 wvd Place parentheses around assignment
+ * used as truth value.
+ * 11/09/05 1.3 wvd In the current scheme, the rate_lif
+ * and rate_sic arrays contain values
+ * from the housekeeping file and do
+ * not reflect data loss due to FIFO
+ * overflows. Now we calculate the
+ * count-rate array directly from the
+ * photon time array.
+ * 02/23/06 1.4 wvd Change length of data_rate array
+ * from nevents to nseconds.
+ *
+ * History: 12/29/06 1.1 wvd Derived from cf_screen_fifo_overflow.
+ * 01/15/07 1.2 wvd When count rate is zero, correct one
+ * second before to two seconds after.
+ * 02/08/07 1.3 wvd If TTPERIOD = 0, set it to 1.
+ * 04/07/07 1.4 wvd Clean up compiler warnings.
+ *
+ ****************************************************************************/
+
+#include <stdlib.h>
+#include "calfuse.h"
+
+int
+compute_count_rate(long nevents, float *ptime, long nseconds,
+ float *ttime, long **data_rate)
+{
+ float delta_max = 1.0 + FRAME_TOLERANCE;
+ long j,k;
+
+ *data_rate = (long *) cf_calloc(nseconds, sizeof(long));
+ for (j=k=0; j<nevents; j++) {
+ while(ttime[k+1]-FRAME_TOLERANCE < ptime[j] && k+1 < nseconds) k++;
+ if ((ptime[j] - ttime[k]) < delta_max) (*data_rate)[k]++;
+ }
+ return 0;
+}
+
+int
+cf_fifo_dead_time(fitsfile *header, long nevents, float *ptime,
+ long nseconds, float *ttime, float *aic_rate, float *ids_dtc)
+{
+ char CF_PRGM_ID[] = "cf_fifo_dead_time";
+ char CF_VER_NUM[] = "1.4";
+
+ char elecfile[FLEN_VALUE];
+ int errflg=0, status=0;
+ int fifo_drain_rate;
+ int *flag_array, found_drop_out = FALSE;
+ long *data_rate, k;
+ float mean_ids_rate, ttperiod;
+ float mean_ids_dtc;
+ fitsfile *elecfits;
+
+ /* Exit if data were obtained in HIST mode. */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ /* Read IDS drain rate from calibration file. */
+ FITS_read_key(header, TSTRING, "ELEC_CAL", elecfile, NULL, &status);
+ FITS_open_file(&elecfits, cf_cal_file(elecfile), READONLY, &status);
+ FITS_read_key(elecfits, TINT, "FIFO_DRN", &fifo_drain_rate, NULL, &status);
+ FITS_close_file(elecfits, &status);
+ cf_verbose(4, "fifo_drain_rate = %d", fifo_drain_rate);
+
+ /* Read interval between time stamps. */
+ FITS_read_key(header, TFLOAT, "TTPERIOD", &ttperiod, NULL, &status);
+ if (ttperiod < 1E-6) ttperiod = 1.0;
+
+ /* Compute mean IDS count rate. */
+ mean_ids_rate = 0.;
+ for (k=0; k<nseconds; k++)
+ mean_ids_rate += aic_rate[k];
+ mean_ids_rate /= nseconds;
+ mean_ids_rate += 1./ttperiod;
+ cf_verbose(4, "mean_ids_rate = %f", mean_ids_rate);
+
+ /* If mean IDS count rate > 3200, look for data drop-outs. */
+ if (mean_ids_rate > 3200 && nseconds > 4) {
+ compute_count_rate(nevents, ptime, nseconds, ttime, &data_rate);
+ flag_array = (int *) cf_calloc(nseconds, sizeof(int));
+
+ /* If count rate goes to zero, set flag_array. Where possible,
+ set flags from one second before to two seconds after. */
+ for (k=0; k<1; k++) if (data_rate[k] == 0)
+ flag_array[k] = flag_array[k+1] = flag_array[k+2] = TRUE;
+
+ for ( ; k<nseconds-2; k++) if (data_rate[k] == 0)
+ flag_array[k-1] = flag_array[k] = flag_array[k+1] =
+ flag_array[k+2] = TRUE;
+
+ for ( ; k<nseconds-1; k++) if (data_rate[k] == 0)
+ flag_array[k-1] = flag_array[k] = flag_array[k+1] = TRUE;
+
+ for ( ; k<nseconds; k++) if (data_rate[k] == 0)
+ flag_array[k-1] = flag_array[k] = TRUE;
+
+ for (k=0; k<nseconds; k++) if (data_rate[k] == 0) {
+ found_drop_out = TRUE;
+ break;
+ }
+
+ /* If there are any drop-outs, modify IDS dead-time correction. */
+ if (found_drop_out)
+ for ( ; k < nseconds; k++) if (flag_array[k])
+ ids_dtc[k] = (aic_rate[k] - 1./ttperiod) / fifo_drain_rate;
+
+ free(data_rate);
+ free(flag_array);
+ }
+
+ /*
+ * Calculate mean dead-time correction. Write to file header.
+ */
+ if (found_drop_out) {
+ mean_ids_dtc = 0;
+ for (k = 0; k < nseconds; k++) mean_ids_dtc += ids_dtc[k];
+ mean_ids_dtc /= nseconds;
+ cf_verbose(1, "IDS FIFO overflow detected.");
+ cf_verbose(2, "Modified mean IDS dead-time correction: %f",
+ mean_ids_dtc);
+ FITS_update_key(header, TFLOAT, "IDS_DEAD", &mean_ids_dtc, NULL,
+ &status);
+ }
+
+ /*
+ * Clean up and go home.
+ */
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return 0;
+}
diff --git a/src/libcf/cf_find_spectra.c b/src/libcf/cf_find_spectra.c
new file mode 100644
index 0000000..412a1e4
--- /dev/null
+++ b/src/libcf/cf_find_spectra.c
@@ -0,0 +1,396 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_find_spectra(fitsfile *header, long nevents,
+ * float *weight, float, *xfarf, float *yfarf, char *chan,
+ * unsigned char *timeflags, unsigned char *locflags,
+ * int airglow_centroid)
+ *
+ * Description: Determines the spectral centroid for each aperture.
+ * First, we compute the airglow centroid for the LWRS
+ * aperture and determine its offset relative to the
+ * tabulated value. For the MDRS and HIRS apertures, we
+ * apply this offset to the tabulated centroid to get the
+ * airglow centroid. For each aperture, we search for a
+ * target spectrum in a narrow range around the airglow
+ * centroid. If we find it, we use it; otherwise, we use
+ * the airglow centroid. If airglow_centroid = TRUE, use
+ * airglow centroid for the target spectrum.
+ *
+ *
+ * Arguments: fitsfile *header Pointer to the location of the FITS
+ * header of the Intermediate Data File
+ * long nevents Number of photons in the file
+ * float *weight scale factor for each photon event
+ * float *xfarf X position of each photon event
+ * float *yfarf Y position of each photon event
+ * unsigned char *chan Pointer to the array containing
+ * channel information for each event
+ * unsigned char *timeflags Pointer to the array containing
+ * time flags for each event
+ * unsigned char *locflags Pointer to the array containing
+ * location flags for each event
+ * int airglow_centroid If TRUE, use airglow lines to compute
+ * centroid of target spectrum.
+ *
+ * Calls:
+ *
+ * Return: 0 on success
+ *
+ * History: 04/18/03 1.1 wvd Initial work
+ * 05/20/03 1.2 rdr Added call to cf_proc_check
+ * 07/29/03 1.3 rdr Deal with case where no source is
+ * present (i.e. Aperture = RFPT)
+ * 08/21/03 1.6 wvd Change channel to unsigned char.
+ * 09/08/03 1.7 wvd Fix typo in verbose statement.
+ * 09/18/03 1.8 wvd Stop iteration if
+ * cf_calculate_y_centroid returns -1.
+ * 10/22/03 1.9 wvd Rewrite program.
+ * 11/12/03 1.10 wvd In HIST mode, use default centroid
+ * for non-target channels.
+ * 04/09/04 1.11 bjg Include stdlib.h and stdio.h
+ * Change formats in printf to
+ * match arg types.
+ * 04/19/04 1.12 wvd Sum background only over detector
+ * active area. Scale intrinsic count
+ * rate by EXPTIME and detector width.
+ * 05/03/04 1.13 wvd Move call to cf_identify_channel
+ * from this routine to cf_remove_motions.
+ * 06/02/04 1.14 wvd Don't need PHA array; can use locflags
+ * instead. Use time flags to exclude
+ * bursts, etc.
+ * 04/21/05 1.15 wvd Rewrite program using airglow lines
+ * (if available) to constrain search
+ * for target centroid. Don't bother
+ * reading model background files.
+ * 06/03/05 1.16 wvd Fix bug in tabulation of exected Y
+ * centroids. Remove unused variables.
+ * 02/01/06 1.17 wvd Tighten region over which centroids
+ * are computed.
+ * If target centroid is too far from
+ * airglow centroid, reject it.
+ * If ycent is too far from default value,
+ * use default, not airglow value.
+ * Change ysum and norm to doubles.
+ * 08/30/06 1.18 wvd Added airglow_centroid argument.
+ * 03/14/08 1.19 wvd In HIST mode, there are no spectra in
+ * other apertures to cause confusion,
+ * so we need not require that the
+ * target centroid be within 30 pixels
+ * of the airglow value.
+ * 11/25/08 1.20 wvd Don't compare HIST centroids to
+ * either airglow or tabulated centroid.
+ *
+ ****************************************************************************/
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include "calfuse.h"
+
+#define YLENGTH NYMAX/16
+
+int
+cf_find_spectra(fitsfile *header, long nevents,
+ float *weight, float *xfarf, float *yfarf, unsigned char *channel,
+ unsigned char *timeflags, unsigned char *locflags, int airglow_centroid)
+{
+ char aper[FLEN_VALUE], instmode[FLEN_VALUE], quality[FLEN_VALUE];
+ char key[FLEN_KEYWORD], filename[FLEN_VALUE], detector[FLEN_VALUE];
+ double norm, ysum;
+ float bkgd, peak, ycent, yshift=0;
+ float yairg[NYMAX], ydist[NYMAX], ysigma[NYMAX];
+ float ybin[YLENGTH], sbin[YLENGTH], ycent_tab[8];
+ float dy, sigma, xmin, xmax, ycent_air;
+ int active_ap[2], airglow=FALSE, target_ap, status=0;
+ int bkgd_num, bkgd_min[8], bkgd_max[8];
+ int chan_num, errflg = 0, lwrs;
+ int chan_order[] = { 3, 2, 1, 7, 6, 5 };
+ int center, high, low, npeak, hdu;
+ int spex_sic, spex_lif, emax, emax_sic, emax_lif, extended;
+ long i, j, k;
+ fitsfile *chidfits, *parmfits;
+
+ char CF_PRGM_ID[] = "cf_find_spectra";
+ char CF_VER_NUM[] = "1.20";
+
+ /* Initialize error checking */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+
+ /* Enter a time stamp into the log */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ /* Check whether routine is appropriate for this data file. */
+ if ( (errflg = cf_proc_check(header, CF_PRGM_ID)) ) return errflg;
+
+ /* Initialize Y-distribution arrays. */
+ for (i = 0; i < NYMAX; i++)
+ yairg[i] = ydist[i] = ysigma[i] = 0.;
+ for (i = 0; i < YLENGTH; i++)
+ ybin[i] = sbin[i] = 0.;
+
+ /* Read header keywords from IDF. */
+ FITS_read_key(header, TSTRING, "INSTMODE", instmode, NULL, &status);
+ FITS_read_key(header, TSTRING, "DETECTOR", detector, NULL, &status);
+ FITS_read_key(header, TSTRING, "APERTURE", aper, NULL, &status);
+ extended = cf_source_aper(header, active_ap);
+
+ FITS_read_key(header, TINT, "BKGD_NUM", &bkgd_num, NULL, &status);
+ for (i = 0; i < bkgd_num; i++) {
+ sprintf(key, "BKG_MIN%ld", i);
+ FITS_read_key(header, TINT, key, bkgd_min+i, NULL, &status);
+ sprintf(key, "BKG_MAX%ld", i);
+ FITS_read_key(header, TINT, key, bkgd_max+i, NULL, &status);
+ }
+
+ /* Read expected channel centroids from CHID_CAL file. */
+ FITS_read_key(header, TSTRING, "CHID_CAL", filename, NULL, &status);
+ FITS_open_file(&chidfits, cf_cal_file(filename), READONLY, &status);
+ for (i = 1; i < 8; i++) {
+ if (i==4) continue;
+ if ((i == active_ap[0] || i == active_ap[1]) && !extended) hdu=i+1;
+ else hdu=i+9;
+ FITS_movabs_hdu(chidfits, hdu, NULL, &status);
+ FITS_read_key(chidfits, TFLOAT, "CENTROID", ycent_tab+i, NULL, &status);
+ }
+ FITS_read_key(chidfits, TFLOAT, "XMIN", &xmin, NULL, &status);
+ FITS_read_key(chidfits, TFLOAT, "XMAX", &xmax, NULL, &status);
+ FITS_close_file(chidfits, &status);
+
+ /*
+ * Generate photon and airglow arrays as a function of YFARF.
+ * Ignore events near the detector edge.
+ */
+ xmin += 250;
+ xmax -= 250;
+ for (i = 0; i < nevents; i++) {
+ if (!(timeflags[i] & ~TEMPORAL_DAY) &&
+ (xfarf[i] > xmin) && (xfarf[i] < xmax)) {
+ if (!locflags[i])
+ ydist[cf_nint(yfarf[i])] += weight[i];
+ else if (locflags[i] == LOCATION_AIR)
+ yairg[cf_nint(yfarf[i])] += weight[i];
+ }
+ }
+ /* Set error array equal to photon counts (= variance). */
+ for (i = 0; i < NYMAX; i++)
+ ysigma[i] = ydist[i];
+
+ /* If exposure was taken in TTAG mode, estimate BKGD level */
+ bkgd = 0.;
+ if (!strcmp(instmode, "TTAG")) {
+ k = 0;
+ for (i = 0; i < bkgd_num; i++) {
+ for (j = bkgd_min[i]; j <= bkgd_max[i]; j++) {
+ bkgd += ydist[j];
+ k++;
+ }
+ }
+ bkgd /= k;
+ cf_verbose(3, "Mean background level: %.1f", bkgd);
+ }
+
+ /* Subtract mean background and bin data by 16 pixels. */
+ for (i = 0; i < NYMAX; i++) {
+ j = i / 16;
+ ybin[j] += ydist[i] - bkgd;
+ sbin[j] += ysigma[i];
+ }
+ for (i = 0; i < YLENGTH; i++) sbin[i] = sqrt(sbin[i]);
+
+ /* Read extraction parameters from PARM_CAL file. */
+ FITS_read_key(header, TSTRING, "PARM_CAL", filename, NULL, &status);
+ FITS_open_file(&parmfits, cf_parm_file(filename), READONLY, &status);
+ FITS_read_key(parmfits,TINT,"SPEX_SIC",&spex_sic,NULL,&status);
+ FITS_read_key(parmfits,TINT,"SPEX_LIF",&spex_lif,NULL,&status);
+ FITS_read_key(parmfits,TINT,"EMAX_SIC",&emax_sic,NULL,&status);
+ FITS_read_key(parmfits,TINT,"EMAX_LIF",&emax_lif,NULL,&status);
+ FITS_close_file(parmfits,&status);
+
+ /* Determine centroid for each channel. */
+ for (k = 0; k < 6; k++) {
+ chan_num = chan_order[k];
+
+ /* Is this the LWRS aperture? */
+ if (chan_num == 3 || chan_num == 7)
+ lwrs = TRUE;
+ else
+ lwrs = FALSE;
+
+ /* Is it the target aperture? */
+ if (chan_num == active_ap[0] || chan_num == active_ap[1])
+ target_ap = TRUE;
+ else
+ target_ap = FALSE;
+
+ /* If we're in histogram mode, and this is not the target aperture,
+ move to next aperture. */
+ if (!strncmp(instmode, "HIST", 4) && !target_ap) {
+ if (lwrs) yshift = 0;
+ continue;
+ }
+
+ /* First, we compute a centroid from the airglow lines.
+ For the LWRS aperture, search within 70 pixels of
+ the expected centroid. Compute the offset between the
+ measured and tabulated centroids. For the MDRS and HIRS
+ apertures, apply the offset computed from the LWRS aperture
+ to the tabulated centroid.
+ Disregard regions near the bottom and top of the detector. */
+
+ if (lwrs) {
+ center = cf_nint(ycent_tab[chan_num]);
+ dy = 70;
+ if ((low = center-dy) < 16) low = 16;
+ if ((high = center+dy) > NYMAX-50) high = NYMAX-50;
+ norm = ysum = 0.;
+ for (i = low; i <= high; i++) {
+ ysum += i * yairg[i];
+ norm += yairg[i];
+ }
+
+ /* If the airglow lines contain at least 33 events, use their
+ centroid. If not, use the tabulated centroid. */
+ if (norm > 33.) {
+ ycent = ysum / norm;
+ yshift = ycent - ycent_tab[chan_num];
+ airglow = TRUE;
+ }
+ else {
+ ycent = ycent_tab[chan_num];
+ yshift = 0;
+ airglow = FALSE;
+ }
+ }
+ /* For MDRS and HIRS apertures */
+ else
+ ycent = yshift + ycent_tab[chan_num];
+
+ /* Remember airglow centroid */
+ ycent_air = ycent;
+
+ /* Now we find the centroid for the target spectrum. If we are
+ in a target aperture and user has specified YCENT, use it. */
+
+ if (target_ap && ((chan_num<5 && spex_lif>=0 && spex_lif<1024) ||
+ (chan_num>4 && spex_sic>=0 && spex_sic<1024))) {
+ if (chan_num<5) ycent=spex_lif;
+ else ycent=spex_sic;
+ sprintf(key, "YCENT%1d", chan_num);
+ FITS_update_key(header, TFLOAT, key, &ycent, NULL, &status);
+ sprintf(quality, "HIGH");
+ sprintf(key, "YQUAL%1d", chan_num);
+ FITS_update_key(header, TSTRING, key, quality, NULL, &status);
+ cf_verbose(1, "Channel %d: User-specified Y centroid = %.2f",
+ chan_num, ycent);
+ continue;
+ }
+
+ /* If we must find the target spectrum ourselves, use the binned
+ data array to improve the S/N. Search within 2 binned pixels
+ of ycent, however it was computed. */
+
+ center = cf_nint(ycent / 16.);
+ dy = 2;
+ if ((low = center - dy) < 3) low = 3;
+ if ((high = center + dy) > YLENGTH-4) high = YLENGTH-4;
+
+ npeak = 0;
+ peak = -1.0E5;
+ for (i = low; i <= high; i++) {
+ if (ybin[i] > peak) {
+ peak = ybin[i];
+ npeak = i;
+ }
+ }
+
+ /* If peak is significant, compute Y centroid for events falling
+ within 40 pixels of peak. If this number is within 30 pixels
+ of airglow centroid, use it. If not, use airglow centroid.
+ Note that we require a higher significance for the SiC LWRS
+ channel on side 1, because it sits on an elevated background. */
+
+ if (!strncmp(detector, "1", 1) && chan_num == 7) sigma = 9.;
+ else sigma = 5.;
+
+ sprintf(quality, "UNKNOWN");
+
+ if (peak > sigma * sbin[npeak] && !airglow_centroid) {
+ center = npeak * 16 + 8;
+ dy = 40;
+ if ((low = center - dy) < 32) low = 32;
+ if ((high = center + dy) > NYMAX-50) high = NYMAX-50;
+ norm = ysum = 0.;
+ for (i = low; i <= high; i++) {
+ ysum += i * ydist[i];
+ norm += ydist[i];
+ }
+ ycent = ysum / norm;
+
+ /* Don't test offset for HIST data. (wvd, 03/14/2008) */
+ if (fabs(ycent - ycent_air) < 30 || !strncmp(instmode, "HIST", 4)) {
+ sprintf(quality, "HIGH");
+ cf_verbose(3, "Channel %d: default centroid: %.2f",
+ chan_num, ycent_tab[chan_num]);
+ if (airglow) cf_verbose(3, "Channel %d: airglow centroid: %.2f",
+ chan_num, ycent_air);
+ cf_verbose(3,"Channel %d: counts in peak = %.0f, limit = %.0f",
+ chan_num, peak, sigma * sbin[npeak]);
+ cf_verbose(2, "Channel %d: using target centroid = %.2lf",
+ chan_num, ycent);
+ }
+ }
+ /* If peak is not significant or ycent differs too much from
+ the airglow centroid, use the airglow centroid. */
+ if (!strncmp(quality, "UNKNOWN", 1)) {
+ if (airglow) {
+ sprintf(quality, "MEDIUM");
+ cf_verbose(3, "Channel %d: default centroid: %.2f",
+ chan_num, ycent_tab[chan_num]);
+ cf_verbose(3,"Channel %d: peak = %.0f, limit = %.0f",
+ chan_num, peak, sigma * sbin[npeak]);
+ cf_verbose(2, "Channel %d: using airglow centroid = %.2lf",
+ chan_num, ycent);
+ }
+ else {
+ sprintf(quality, "LOW");
+ cf_verbose(3,"Channel %d: peak = %f, limit = %f",
+ chan_num, peak, sigma * sbin[npeak]);
+ cf_verbose(2, "Channel %d: using default centroid = %.2lf",
+ chan_num, ycent);
+ }
+ }
+
+ /* If centroid lies too far from the default value, use default. */
+ if (chan_num<5) emax=emax_lif; else emax=emax_sic;
+ /* Don't test offset for HIST data. (wvd, 11/25/2008) */
+ if (fabs(ycent - ycent_tab[chan_num]) > emax && !strncmp(instmode, "TTAG", 4)) {
+ if (target_ap) {
+ cf_verbose(1, "Computed centroid (%.1lf) is too far from "
+ "expected value.", ycent);
+ cf_verbose(1, "Channel %d: using default centroid of %.1f",
+ chan_num, ycent_tab[chan_num]);
+ }
+ else {
+ cf_verbose(2, "Channel %d: using default centroid of %.1f",
+ chan_num, ycent_tab[chan_num]);
+ }
+ ycent = ycent_tab[chan_num];
+ sprintf(quality, "LOW");
+ }
+
+ /* Write results to file header */
+ sprintf(key, "YCENT%1d", chan_num);
+ FITS_update_key(header, TFLOAT, key, &ycent, NULL, &status);
+ sprintf(key, "YQUAL%1d", chan_num);
+ FITS_update_key(header, TSTRING, key, quality, NULL, &status);
+ }
+
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done Processing");
+
+ return status;
+}
diff --git a/src/libcf/cf_fpa_position.c b/src/libcf/cf_fpa_position.c
new file mode 100644
index 0000000..fd7229c
--- /dev/null
+++ b/src/libcf/cf_fpa_position.c
@@ -0,0 +1,231 @@
+/****************************************************************************
+ *
+ * Synopsis: cf_fpa_position(fitsfile *header, long nevents, float *x,
+ * unsigned char *channel)
+ *
+ * Description: Modify X position of photon events to account for an offset
+ * of the focal plane assembly (FPA)
+ *
+ * Arguments: fitsfile *header Pointer to the FITS header
+ * of the Intermediate Data File
+ * long npts Number of photons in the file
+ * float *x X positions for each photon event
+ * unsigned char *channel Assigned channel of each photon
+ *
+ * Calls: None
+ *
+ * Return: 0 on success
+ *
+ * History: 09/04/02 v1.1 wvd Based on cf_dpix by HSU
+ * 01/14/03 v1.3 wvd Rename cf_fpa_pos to cf_read_fpa_pos
+ * Move spectrograph optical parameters
+ * to separate calibration file.
+ * Abandon linear wavelength scale.
+ * 02/13/03 v1.4 wvd Write FPA shifts to file header.
+ * 04/07/03 v1.5 wvd Change keywords to FPADXLIF and
+ * FPADXSIC. Implement cf_verbose.
+ * Comment out cf_proc_check.
+ * Test channel[i] as int, not char.
+ * 05/20/03 v1.6 rdr Added call to cf_proc_check.
+ * 08/21/03 v1.7 wvd Change channel to unsigned char.
+ * 08/23/05 v1.8 wvd If either FPA position is out of
+ * bounds, issue a warning and set
+ * that FPA correction to zero.
+ * 08/01/05 v1.9 wvd Broaden FPA limits by +/- 1.
+ * 09/06/05 v1.10 wvd Correct for both X and Z motions
+ * of FPA.
+ * Delete call to cf_read_fpa_pos.c
+ * 12/02/05 v1.11 wvd Delete unused variables.
+ * 04/07/07 v1.12 wvd Clean up compiler warnings.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include "calfuse.h"
+
+#define PIX1 2500 /* PIX1,2 are just inside the non-linear */
+#define PIX2 13500 /* regions of the detector */
+
+int cf_fpa_position(fitsfile *header, long nevents, float *x,
+ unsigned char *channel)
+{
+ char CF_PRGM_ID[] = "cf_fpa_position";
+ char CF_VER_NUM[] = "1.12";
+
+ int errflg=0, status=0, hdutype, hdunum;
+ int nwave; /* number of pixels in calibration file */
+ char aperture[FLEN_VALUE]; /* aperture keyword from FITS header */
+ char detector[FLEN_VALUE]; /* detector keyword from FITS header */
+ char wave_file[FLEN_FILENAME]; /* name of wavelength calibration file */
+ char spec_file[FLEN_FILENAME]; /* name of spectrograph calibration file */
+ long i;
+ /* Spectrograph optical parameters */
+ float alpha, alpha_lif, alpha_sic, sigma_lif, sigma_sic, diam;
+ float *wavelength; /* wavelengths read from calibration file */
+ float fpaxlifdata; /* LiF FPA X position in microns for spectrum */
+ float fpaxsicdata; /* SiC FPA X position in microns for spectrum */
+ float fpaxcal; /* FPA X position in microns for calibration file */
+ float dxfpa; /* fpax_data - fpaxcal */
+ float fpazlifdata; /* LiF FPA Z position in microns for spectrum */
+ float fpazsicdata; /* SiC FPA Z position in microns for spectrum */
+ float fpazcal; /* FPA Z position in microns for calibration file */
+ float dzfpa; /* fpaz_data - fpazcal */
+ float dpix, dpix_LiF, dpix_SiC; /* Spectral shift (in pixels)*/
+
+ double w1, w2; /* wavelengths at which to sample calibration */
+ double beta1,beta2; /* grating exit angle for w1, w2 */
+ double sinalpha; /* sin (spectrograph entrance angle) */
+ double cosalpha; /* cos (spectrograph entrance angle) */
+ double sigma; /* grating groove spacing in Angstroms */
+ double cosbeta; /* cos (mean grating exit angle) */
+ double pixscale; /* mean effective microns/pixel for det segment */
+ fitsfile *specfits, *wavefits;
+
+ /* Initialize error checking */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+
+ /* Enter a timestamp into the log. */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ /* Check whether routine is appropriate for this data file. */
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ /* Read keywords from data file header. */
+ FITS_read_key(header, TSTRING, "DETECTOR", detector, NULL, &status);
+ FITS_read_key(header, TSTRING, "APERTURE", aperture, NULL, &status);
+ FITS_read_key(header, TFLOAT, "FPASXPOS", &fpaxsicdata, NULL, &status);
+ FITS_read_key(header, TFLOAT, "FPALXPOS", &fpaxlifdata, NULL, &status);
+ FITS_read_key(header, TFLOAT, "FPASZPOS", &fpazsicdata, NULL, &status);
+ FITS_read_key(header, TFLOAT, "FPALZPOS", &fpazlifdata, NULL, &status);
+ FITS_read_key(header, TSTRING, "SPEC_CAL", spec_file, NULL, &status);
+ FITS_read_key(header, TSTRING, "WAVE_CAL", wave_file, NULL, &status);
+
+ /* Read spectrograph optical parameters from SPEC_CAL file. */
+ FITS_open_file(&specfits, cf_cal_file(spec_file), READONLY, &status);
+ FITS_read_key(specfits, TFLOAT, "ALPHALIF", &alpha_lif, NULL, &status);
+ FITS_read_key(specfits, TFLOAT, "ALPHASIC", &alpha_sic, NULL, &status);
+ FITS_read_key(specfits, TFLOAT, "SIGMALIF", &sigma_lif, NULL, &status);
+ FITS_read_key(specfits, TFLOAT, "SIGMASIC", &sigma_sic, NULL, &status);
+ FITS_read_key(specfits, TFLOAT, "DIAM", &diam, NULL, &status);
+ FITS_close_file(specfits, &status);
+
+ /* Determine the extension number (LiF) from aperture */
+ hdunum = 4; /* Default is LWRS */
+ if (!strcmp(aperture, "HIRS")) hdunum = 2;
+ if (!strcmp(aperture, "MDRS")) hdunum = 3;
+
+ /* Open wavelength calibration file */
+ FITS_open_file(&wavefits, cf_cal_file(wave_file), READONLY, &status);
+
+ /* Loop over the two apertures; first (i==0) LiF, then SiC. */
+ /* Determine shifts for LiF and SiC channels. */
+ for(i=0; i<2; i++) {
+
+ /* Move to appropriate HDU in WAVECAL file. */
+ hdunum += i*4;
+ FITS_movabs_hdu(wavefits, hdunum, &hdutype, &status);
+
+ /* Get FPA position for which WAVECAL was derived */
+ FITS_read_key(wavefits, TFLOAT, "FPACXPOS", &fpaxcal, NULL, &status);
+ FITS_read_key(wavefits, TFLOAT, "FPACZPOS", &fpazcal, NULL, &status);
+
+ /* Read the wavelength array from the WAVECAL file. */
+ nwave=cf_read_col(wavefits, TFLOAT,"WAVELENGTH", (void **) &wavelength);
+
+ /* Set LiF/SIC-dependent quantities */
+ if (i == 1) { /* SiC data */
+ dxfpa = fpaxsicdata - fpaxcal;
+ dzfpa = fpazsicdata - fpazcal;
+ sigma = sigma_sic;
+ alpha = alpha_sic;
+ } else { /* LiF data */
+ dxfpa = fpaxlifdata - fpaxcal;
+ dzfpa = fpazlifdata - fpazcal;
+ sigma = sigma_lif;
+ alpha = alpha_lif;
+ }
+ /*
+ * Compute mean pixel scale from points just inside the
+ * highly-nonlinear regions at edges of detectors */
+ w1 = wavelength[PIX1];
+ w2 = wavelength[PIX2];
+ sinalpha = sin (alpha * RADIAN);
+ cosalpha = cos (alpha * RADIAN);
+ beta1 = asin (w1/sigma - sinalpha);
+ beta2 = asin (w2/sigma - sinalpha);
+ cosbeta = cos ((beta1 + beta2)/2.); /* mean cosbeta for segment */
+ pixscale = diam * fabs(beta1 - beta2) * 1000. / (double)(PIX2-PIX1);
+
+ /* Compute shift of spectrum in pixels due to the FPA offset. */
+ if (i == 1) /* SiC data */
+ dpix = (dxfpa * cosalpha + dzfpa * sinalpha) / cosbeta / pixscale;
+
+ else /* LiF data */
+ dpix = (dxfpa * cosalpha - dzfpa * sinalpha) / cosbeta / pixscale;
+
+ /* For side 2, multiply the above expressions by -1. */
+ if (!strncmp(detector, "2", 1))
+ dpix *= -1;
+
+ /* Write info to log file */
+ if (i == 1) {
+ dpix_SiC = dpix;
+ cf_verbose(3, "FPAX data: %8.3f FPAX calib: %8.3f",
+ fpaxsicdata, fpaxcal);
+ cf_verbose(3, "FPAZ data: %8.3f FPAZ calib: %8.3f",
+ fpazsicdata, fpazcal);
+ cf_verbose(2, "SiC spectra to be shifted by %8.3f pixels", dpix_SiC);
+ }
+ else {
+ dpix_LiF = dpix;
+ cf_verbose(3, "FPAX data: %8.3f FPAX calib: %8.3f",
+ fpaxlifdata, fpaxcal);
+ cf_verbose(3, "FPAZ data: %8.3f FPAZ calib: %8.3f",
+ fpazlifdata, fpazcal);
+ cf_verbose(2, "LiF spectra to be shifted by %8.3f pixels", dpix_LiF);
+ }
+
+ } /* End of i loop */
+
+ /* Close the WAVECAL file */
+ FITS_close_file(wavefits, &status);
+
+ /* If tabulated FPA position is out of range, set the corresponding
+ dpix value to zero and issue a warning. */
+ if (fpaxlifdata < -1 || fpaxlifdata > 401) {
+ dpix_LiF = 0;
+ cf_if_warning("Keyword FPALXPOS is out of bounds. Setting FPADXLIF = 0.");
+ }
+ if (fpaxsicdata < -1 || fpaxsicdata > 401) {
+ dpix_SiC = 0;
+ cf_if_warning("Keyword FPASXPOS is out of bounds. Setting FPADXSIC = 0.");
+ }
+
+ /* Shift only photons with assigned apertures. */
+ for(i = 0; i < nevents; i++) {
+ switch(channel[i]) {
+ case 1: case 2: case 3: case 4:
+ x[i] += dpix_LiF;
+ break;
+ case 5: case 6: case 7: case 8:
+ x[i] += dpix_SiC;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Write shifts to IDF file header */
+ FITS_update_key(header, TFLOAT, "FPADXLIF", &dpix_LiF, NULL, &status);
+ FITS_update_key(header, TFLOAT, "FPADXSIC", &dpix_SiC, NULL, &status);
+
+ /* Update processing flags. */
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+
+ /* Enter a timestamp into the log. */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+
+ return status;
+}
diff --git a/src/libcf/cf_fuv_init.c b/src/libcf/cf_fuv_init.c
new file mode 100644
index 0000000..a2bb181
--- /dev/null
+++ b/src/libcf/cf_fuv_init.c
@@ -0,0 +1,592 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_fuv_init(fitsfile *fptr)
+ *
+ * Description: cf_fuv_init performs the following functions:
+ * 1. Populates the data processing step keywords based upon
+ * the type of data (TTAG or HIST). The processing steps
+ * and the associated keywords are defined in the
+ * structure CALIBRATION_STEP_KEYS in calfuse.h.
+ * 2. Populates the orbital parameters in the header by
+ * calling read_tle. The orbital elements come from
+ * the file FUSE.TLE, which is in standard NORAD two-line
+ * element format.
+ * 3. Populates the calibration file keywords based on the
+ * time and date of observation, the detector segment,
+ * and the interpolation status of the calibration file.
+ * The relation between processing steps and associated
+ * calibration file keywords is defined in the structure
+ * CALIBRATION_FILE_KEYS in calfuse.h. The names and
+ * effective dates of the calibration files are found in
+ * the ASCII-format file master_calib_file.dat in the
+ * CF_PARMDIR directory.
+ *
+ * Input: fitsfile *fptr Pointer to input file
+ *
+ * History: 05/11/98 emm Begin work.
+ * 05/15/98 emm finished
+ * 06/04/98 emm Modified keywords to reflect updated
+ * calfuse design
+ * 11/12/98 emm Added ability to read orbital elements
+ * from file FUSE.TLE.
+ * 03/18/99 emm Added call to cf_cal_file. master_
+ * calib_file.dat must now be in the
+ * CF_CALDIR directory. (on 08/25/99
+ * this became CF_PARMDIR).
+ * 03/18/99 emm Edited header only.
+ * 04/14/99 peb Increased strings sizes to prevent
+ * character overflow.
+ * Implemented FITS wrapper functions.
+ * 05/19/99 peb Implemented cf_error_msg functions.
+ * 06/07/99 peb Added reporting of version number.
+ * 07/29/99 emm Added CF_VERS keyword update.
+ * 08/25/99 emm master_calib_file.dat now in CF_PARMDIR
+ * 12/06/99 v1.8 emm Removed cf_if_error call when master
+ * calib file keyword not found.
+ * 10/13/00 v1.9 jwk change CF_VER_NUM from "1.5"
+ * 08/23/01 v1.10 wvd Read PARM file; if requested, set
+ * BRST_COR, ASTG_COR, PHAX_COR,
+ * MKBK_COR, or BKGD_COR to "OMIT"
+ * 10/23/01 v1.11 hsu Check OPUS version
+ * 10/26/01 v1.12 wvd Install subroutine.
+ * 02/11/02 v1.13 wvd Check detector bias levels.
+ * 02/13/02 v1.14 wvd Modified bias logic; write warning
+ * to data header as COMMENT lines.
+ * 02/13/02 v1.15 wvd Don't crash if bias keywords missing.
+ * 02/19/02 v1.16 wvd Check whether voltage too high
+ * 02/20/02 v1.17 wvd If keyword RAWTIME does not exist,
+ * create with value of EXPTIME
+ * 10/30/02 1.18 peb Converted header to calfusettag.h
+ * 03/26/03 1.4 wvd Removed "Warning" from warnings.
+ * 04/16/03 v1.5 wvd Test for corrupted bias values.
+ * Update comment field for HV_FLAG.
+ * 04/28/03 v1.6 wvd Change logic of assigning cal files
+ * never to extrapolate forward in time.
+ * 07/23/03 v1.7 wvd Write HSKP_CAL to file header.
+ * 07/23/03 v1.8 wvd Change calfusettag.h to calfuse.h
+ * 09/09/03 v1.9 wvd Read SAA_SCR and LIMB_SCR from
+ * SCRN_CAL and set RUN_SAA and RUN_LIMB.
+ * 04/27/04 v1.10 wvd Keywords MKBK_COR and BKGD_COR are
+ * gone, so we don't populate them.
+ * 06/04/04 v1.11 wvd Allow for the possibility that the
+ * time associated with a calibration
+ * file exactly equals EXPSTART.
+ * 06/14/04 v1.12 wvd Check value of BRIT_OBJ keyword and
+ * modify header accordingly.
+ * Write CF_VERS to trailer file.
+ * 07/16/04 v1.13 wvd Must set ASTG_COR to PERFORM, as
+ * pipeline doesn't do it automatically.
+ * 11/26/04 v1.14 wvd Check whether SAA_SCR and LIMB_SCR
+ * are ON or OFF, not YES or NO.
+ * 12/02/04 v1.15 wvd Read value of RUN_JITR from PARM file.
+ * 03/24/05 v1.16 wvd Use HVGOODLM to determine whether
+ * detector voltage is low.
+ * 06/12/06 v1.17 wvd Call cf_verbose rather than
+ * cf_if_warning for non-optimal voltage.
+ * 07/18/08 v1.18 wvd For bright-earth and airglow exposures,
+ * change EXP_STAT to 2,
+ * change SRC_TYPE to EE, and
+ * write warning to file header.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "calfuse.h"
+
+#define MAXCHARS 120
+#define MASTER_CAL_FILE "master_calib_file.dat"
+
+int cf_fuv_init(fitsfile *fptr)
+{
+ char CF_PRGM_ID[] = "cf_fuv_init";
+ char CF_VER_NUM[] = "1.18";
+
+ int i, status=0, expstat, interp_in, timeref;
+ int n, hv_flag, lowv, highv, saav, fullv;
+ char comment[FLEN_CARD], instmode[FLEN_CARD], detector[FLEN_CARD];
+ char opus_str[FLEN_CARD], keyword_in[FLEN_CARD], segment_in[FLEN_CARD];
+ char datestr[FLEN_CARD];
+ char filename_in[]=" ", rootname[FLEN_CARD];
+ char hkexists[FLEN_CARD];
+ char keyword_out1[FLEN_CARD], keyword_out2[FLEN_CARD];
+ char linin[MAXCHARS];
+ char run_limb[FLEN_VALUE], run_saa[FLEN_VALUE], src_type[FLEN_VALUE];
+ char run_brst[FLEN_VALUE], run_walk[FLEN_VALUE], run_astg[FLEN_VALUE];
+ /* char run_mkbk[FLEN_VALUE], run_bkgd[FLEN_VALUE], brit_obj[FLEN_VALUE]; */
+ char run_jitr[FLEN_VALUE], brit_obj[FLEN_VALUE];
+ char hv_str[FLEN_CARD], lv_str[FLEN_CARD], mjd_str[FLEN_CARD];
+ char saa_str[FLEN_CARD], full_str[FLEN_CARD];
+ float hvgood, mjdv, expstart, aftermjd_in;
+ fitsfile *parmfits, *scrnfits;
+
+ struct keyword_tab keytab[NUM_PROC_STEPS]=CALIBRATION_STEP_KEYS;
+ struct cal_file_tab calkey[NUMCALKEYS]=CALIBRATION_FILE_KEYS;
+ FILE *fpin;
+
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin initializing headers");
+
+ /* Check the OPUS version number */
+ fits_read_key(fptr, TSTRING, "OPUSVERS", opus_str, NULL, &status);
+ if (status) {
+ status = 0;
+ cf_add_header_keywords(fptr);
+ cf_verbose(2, "Data file generated by old version of OPUS.");
+ }
+ else if (atof(opus_str) < OPUS_VERSION) {
+ cf_add_header_keywords(fptr);
+ cf_verbose(2, "Data file generated by old version (%s) of OPUS.", opus_str);
+ }
+ FITS_update_key(fptr, TSTRING, "CF_VERS", CALFUSE_VERSION, NULL, &status);
+ cf_verbose (1, "This is CalFUSE v%s", CALFUSE_VERSION);
+
+ /*
+ * Populate data processing keywords based on observing mode found
+ * in the INSTMODE keyword. NOTE: We check only the first
+ * letter of the INSTMODE keyword.
+ */
+ FITS_read_key(fptr, TSTRING, "INSTMODE", instmode, NULL, &status);
+
+ switch (instmode[0]) {
+ case 'H':
+ /* Histogram mode */
+ for (i=0; i<NUM_PROC_STEPS; i++)
+ FITS_update_key(fptr, TSTRING, keytab[i].name,
+ keytab[i].hist_value, 0, &status);
+ break;
+ case 'T':
+ /* Time tag mode */
+ for (i=0; i<NUM_PROC_STEPS; i++)
+ FITS_update_key(fptr, TSTRING, keytab[i].name,
+ keytab[i].ttag_value, 0, &status);
+ break;
+ default:
+ /* Invalid mode */
+ cf_if_error("Invalid INSTMODE value");
+ FITS_update_key(fptr, TSTRING, "INIT_COR", "ERROR", 0, &status);
+ break;
+ } /* End switch (instmode[0]) */
+
+ /* Populate the orbital parameters from a file of two-line elements. */
+ read_tle(fptr);
+
+ /* Populate calibration file keywords based on the date of the
+ * observation and the segment/channel number. */
+
+ FITS_read_key(fptr, TFLOAT, "EXPSTART", &expstart, NULL, &status);
+ FITS_read_key(fptr, TSTRING, "DETECTOR", detector, NULL, &status);
+
+ /* Open the Master calibration database file. */
+
+ fpin = fopen(cf_parm_file(MASTER_CAL_FILE), "r");
+ if (fpin == NULL)
+ cf_if_error("Master calibration database file not found");
+
+ /* Cycle through the master cal file until we run out of lines. */
+
+ /* Here's how this section works. We need to keep track of 3 calibration
+ * files for each keyword: the cal file which immediately preceedes the
+ * date of observation, the cal file which immediately preceedes the cal
+ * file which immediately preceedes the observation, and the cal file
+ * immediately after the observation. These are stored in strut calkey
+ * in:
+ * calkey[i].filenames[0] => second cal file preceeding observation
+ * calkey[i].filenames[1] => cal file immediately preceeding observa-
+ * tion
+ * calkey[i].filenames[2] => cal file immediately after observation
+ *
+ * Here is the plan. Cycle through the master cal file, reading each
+ * line. Determine the keyword for each line, and use that to determine
+ * the appropriate index [i] for calkey. Then, if the detector segment
+ * is correct, determine where the new file fits into the structure above.
+ * If it is more recent than one or both of the preceeding cal files,
+ * but is older than the observation (expstart) replace either
+ * filenames[0] or filenames[1]. If filenames[1] is replaced, shift
+ * the old value down into filenames[0]. If the file is more recent
+ * than the observation, and closer to the observation than the file
+ * in filenames[2], replace filenames[2]. The values
+ * calkey[i].aftermjd[] and calkey[i].interp[] store the date and
+ * interpolation status of the best files. This organization allows
+ * the master calibration database file to be written in any order.
+ */
+
+ while (fgets(linin, MAXCHARS, fpin) != NULL) {
+ /* Check for comment lines */
+
+ if ((linin[0] != '#') && (linin[0] != '\n')) {
+ sscanf(linin, "%4c%*2c%2c%*2c%13c%*2c%9f%*2c%1d",keyword_in,
+ segment_in,filename_in,&aftermjd_in,&interp_in);
+ /*
+ * Determine which keyword we just read in.
+ */
+ i=0;
+ while ((strncmp(calkey[i].name,keyword_in,4) != 0) &&
+ (i < NUMCALKEYS)) i++;
+ if (i >= NUMCALKEYS) {
+ cf_if_warning("Keyword %s in %s is unknown", keyword_in, MASTER_CAL_FILE);
+ } else {
+ if ((strncmp(segment_in," ",2) == 0) ||
+ (strncmp(segment_in,detector,2) == 0)) {
+ if ((aftermjd_in <= expstart) &&
+ (aftermjd_in > calkey[i].aftermjd[0]) &&
+ (aftermjd_in > calkey[i].aftermjd[1])) {
+ strncpy(calkey[i].filenames[0],
+ calkey[i].filenames[1],18);
+ calkey[i].aftermjd[0]=calkey[i].aftermjd[1];
+ calkey[i].interp[0]=calkey[i].interp[1];
+ strncpy(calkey[i].filenames[1],
+ filename_in,18);
+ calkey[i].aftermjd[1]=aftermjd_in;
+ calkey[i].interp[1]=interp_in;
+ } else if ((aftermjd_in < expstart) &&
+ (aftermjd_in > calkey[i].aftermjd[0])) {
+ strncpy(calkey[i].filenames[0],
+ filename_in,18);
+ calkey[i].aftermjd[0]=aftermjd_in;
+ calkey[i].interp[0]=interp_in;
+ }
+ if ((aftermjd_in > expstart) &&
+ (aftermjd_in < calkey[i].aftermjd[2])) {
+ strncpy(calkey[i].filenames[2],
+ filename_in,18);
+ calkey[i].aftermjd[2]=aftermjd_in;
+ calkey[i].interp[2]=interp_in;
+ }
+ } /* Endif segment match */
+ } /* end else unrecognized keyword */
+ } /* end while linin not comment */
+ } /* end while(linin != NULL) */
+
+ fclose(fpin);
+
+/*************************************************************************
+ I have changed Rule 3 so that we NEVER extrapolate calibration
+ files forward in time. Instead, we just use the most recent file.
+ This reduces from 8 to 4 the number of possible cases, since we
+ no longer care about the value of interp[0].
+ This section and the previous one ought to be re-written,
+ but I will simply change 0 to 1 for Case 110 below.
+ WVD - 04/28/03
+*************************************************************************/
+
+ /* OK, now that we have identified the closest two files before
+ * the observation, and the file after the observation, we can
+ * decide what to do with them. First, the easy part: if
+ * calkey[i].numfiles=1, no interpolation is possible, so we
+ * write out calkey[i].filenames[1], which is the file immediately
+ * preceeding the observation. If calkey[i].numfiles=2, interpolation
+ * is possible, and we will have to write two keywords into the header.
+ * The decision as to which two keywords to write follows these rules:
+ * Rule 1: If an observation occurs between two files which can be
+ * interpolated, then an interpolation must be done.
+ * Rule 2: If the calibration file immediately preceeding an
+ * observation is to be used in a stepwise manner, then said
+ * file shall be used in stepwise manner.
+ * Rule 3: If the two calibration files immediately preceeding an
+ * observation can be interpolated, but the following file
+ * cannot, then the two previous files will be extrapolated
+ * forward in time.
+ * Rule 4: If the closest preceeding calibration file has an interpolation
+ * flag, but the file preceeding it has a stepwise flag, the the
+ * closest preceeding file will be used in a sterpwise manner.
+ * Rule 5: Never interpolate backward in time.
+ *
+ * In fact, there are only 8 possible cases:
+ *
+ * Remember, the files were sorted such that the observation
+ * always occurs between [1] and [2]. Also remember that
+ * interp[i]=0 => stepwise):
+ *
+ * Case Action
+ *
+ * interp[0] = 0
+ * interp[1] = 0 filenames[1] will be used in a stepwise manner
+ * interp[2] = 0
+ *
+ * interp[0] = 0
+ * interp[1] = 0 filenames[1] will be used in a stepwise manner
+ * interp[2] = 1
+ *
+ * interp[0] = 0
+ * interp[1] = 1 filenames[1] will be used in a stepwise manner
+ * interp[2] = 0
+ *
+ * interp[0] = 1
+ * interp[1] = 0 filenames[1] will be used in a stepwise manner
+ * interp[2] = 0
+ *
+ * interp[0] = 1
+ * interp[1] = 0 filenames[1] will be used in a stepwise manner
+ * interp[2] = 1
+ *
+ * interp[0] = 0
+ * interp[1] = 1 filenames[1] and filenames[2] will be interpolated
+ * interp[2] = 1
+ *
+ * interp[0] = 1
+ * interp[1] = 1 filenames[0] and filenames[1] will be extrapolated
+ * interp[2] = 0
+ *
+ * interp[0] = 1
+ * interp[1] = 1 filenames[1] and filenames[2] will be interpolated
+ * interp[2] = 1
+ *
+ */
+
+ /* Write the calibration files into the header keywords. */
+ /* Cycle through the keywords. */
+ for (i=0; i<NUMCALKEYS; i++) {
+ if (calkey[i].numfiles == 1) {
+ /* This is the easy case, no interpolation is possible. */
+
+ sprintf(keyword_out1,"%4.4s%1.1s%3.3s",calkey[i].name,"_",
+ calkey[i].extension);
+
+ FITS_update_key(fptr, TSTRING, keyword_out1,
+ calkey[i].filenames[1], 0, &status);
+ }
+ else {
+ /* OK, interpolation is possible, now decide which two files
+ * to write out. */
+
+ /* Create the two output keywords */
+ sprintf(keyword_out1,"%4.4s%1.1s%3.3s",calkey[i].name,"1",
+ calkey[i].extension);
+ sprintf(keyword_out2,"%4.4s%1.1s%3.3s",calkey[i].name,"2",
+ calkey[i].extension);
+
+ if (calkey[i].interp[1] == 0) {
+ /* Easy, preceeding file is a 0, so write filenames[1] to
+ * both keywords. Takes care of cases 000, 001, 100, 101.
+ */
+ FITS_update_key(fptr, TSTRING, keyword_out1,
+ calkey[i].filenames[1], 0, &status);
+ FITS_update_key(fptr, TSTRING, keyword_out2,
+ calkey[i].filenames[1], 0, &status);
+ }
+ else if ((calkey[i].interp[1] == 1) &&
+ (calkey[i].interp[2] == 1)) {
+ /* Case 011 and 111 */
+ FITS_update_key(fptr, TSTRING, keyword_out1,
+ calkey[i].filenames[1], 0, &status);
+ FITS_update_key(fptr, TSTRING, keyword_out2,
+ calkey[i].filenames[2], 0, &status);
+ } else if ((calkey[i].interp[0] == 1) &&
+ (calkey[i].interp[1] == 1) &&
+ (calkey[i].interp[2] == 0)){
+ /* Case 110 */
+ /* CHANGED 0 to 1 in the following line - wvd 04/28/03 */
+ FITS_update_key(fptr, TSTRING, keyword_out1,
+ calkey[i].filenames[1], 0, &status);
+ FITS_update_key(fptr, TSTRING, keyword_out2,
+ calkey[i].filenames[1], 0, &status);
+ }
+ else if ((calkey[i].interp[0] == 0) && (calkey[i].interp[1] == 1)
+ && (calkey[i].interp[2] == 0)){
+ /* Case 010 */
+ FITS_update_key(fptr, TSTRING, keyword_out1,
+ calkey[i].filenames[1], 0, &status);
+ FITS_update_key(fptr, TSTRING, keyword_out2,
+ calkey[i].filenames[1], 0, &status);
+ }
+ else
+ cf_if_error("The interpolation/stepwise logic has"
+ "failed. I am confused.");
+ } /* end else calkey.numfiles == 1 */
+ } /* end for i=0; i<NUMCALKEYS */
+
+ /*
+ * If housekeeping file exists,
+ * add HSKP_CAL and JITR_CAL filenames to the header.
+ */
+ FITS_read_key(fptr, TSTRING, "HKEXISTS", hkexists, NULL, &status);
+ if(!strncasecmp(hkexists, "Y", 1)) {
+ FITS_read_key(fptr, TSTRING, "ROOTNAME", rootname, NULL, &status);
+ sprintf(keyword_out1, "%s%s", rootname, "hskpf.fit");
+ FITS_update_key(fptr, TSTRING, "HSKP_CAL", keyword_out1, 0, &status);
+ sprintf(keyword_out2, "%s%s", rootname, "jitrf.fit");
+ FITS_update_key(fptr, TSTRING, "JITR_CAL", keyword_out2, 0, &status);
+ }
+ /*
+ * Check min and max voltage levels and set HV_FLAG accordingly.
+ */
+ sprintf(hv_str, "DET%cHV%cH", detector[0], detector[1]);
+ sprintf(lv_str, "DET%cHV%cL", detector[0], detector[1]);
+
+ fits_read_key(fptr, TINT, hv_str, &highv, NULL, &status);
+ if (status) {
+ status = 0;
+ hv_flag = -1;
+ sprintf(comment, "Detector bias keywords not found");
+ FITS_update_key(fptr, TINT, "HV_FLAG", &hv_flag, comment, &status);
+ cf_verbose(2, comment);
+ }
+ else {
+ FITS_read_key(fptr, TINT, lv_str, &lowv, NULL, &status);
+ FITS_read_key(fptr, TSTRING, "VOLT_CAL", filename_in, NULL, &status);
+ FITS_open_file(&parmfits, cf_cal_file(filename_in), READONLY, &status);
+ FITS_read_key(parmfits, TFLOAT, "HVGOODLM", &hvgood, NULL, &status);
+
+ n = 0;
+ do {
+ n++;
+ sprintf(mjd_str, "MJD%d", n);
+ FITS_read_key(parmfits, TFLOAT, mjd_str, &mjdv, NULL, &status);
+ } while (expstart > mjdv);
+ n--;
+
+ sprintf(saa_str, "SAA%d", n);
+ sprintf(full_str, "FULL%d", n);
+ FITS_read_key(parmfits, TINT, saa_str, &saav, NULL, &status);
+ FITS_read_key(parmfits, TINT, full_str, &fullv, NULL, &status);
+ FITS_close_file(parmfits, &status);
+
+ if (lowv > fullv + 3) {
+ hv_flag = 5;
+ sprintf(comment, "Detector voltage high throughout exposure");
+ }
+ else if ((float) lowv / fullv > hvgood) {
+ hv_flag = 4;
+ sprintf(comment, "Detector voltage full throughout exposure");
+ }
+ else if (lowv > saav + 3) {
+ hv_flag = 3;
+ sprintf(comment, "Detector voltage low throughout exposure");
+ }
+ else if (lowv > saav - 3) {
+ hv_flag = 2;
+ sprintf(comment, "Detector at SAA voltage throughout exposure");
+ }
+ else {
+ hv_flag = 1;
+ sprintf(comment, "Detector below SAA voltage throughout exposure");
+ }
+
+ if (highv - lowv > 3) {
+ hv_flag = 0;
+ sprintf (comment, "Detector voltage changed during exposure");
+ }
+ else if (lowv > highv) {
+ hv_flag = -1;
+ sprintf(comment, "Detector bias keywords are corrupted");
+ }
+
+ FITS_update_key(fptr, TINT, "HV_FLAG", &hv_flag, comment, &status);
+
+ if (hv_flag != 4) {
+ if (hv_flag == -1) cf_if_warning(comment);
+ else cf_verbose(1, comment);
+ FITS_write_comment(fptr, " ", &status);
+ FITS_write_comment(fptr, comment, &status);
+ fits_get_system_time(datestr, &timeref, &status);
+ sprintf(comment, "CalFUSE v%s %.10s", CALFUSE_VERSION, datestr);
+ FITS_write_comment(fptr, comment, &status);
+ FITS_write_comment(fptr, " ", &status);
+ }
+ }
+
+ /* Read BRIT_OBS keyword and modify header accordingly. */
+
+ FITS_read_key(fptr, TSTRING, "SRC_TYPE", src_type, NULL, &status);
+ FITS_read_key(fptr, TSTRING, "BRIT_OBJ", brit_obj, NULL, &status);
+ if (!strcmp(brit_obj, "DEFOCUS") && !strncmp(src_type, "P", 1)) {
+ *src_type = 'E';
+ FITS_update_key(fptr, TSTRING, "SRC_TYPE", src_type,
+ "DEFOCUS OBS ==> Treat as extended source", &status);
+ }
+
+ /* Flag bright-earth and airglow (M106, S100, and 900+) exposures. */
+
+ FITS_read_key(fptr, TSTRING, "ROOTNAME", rootname, NULL, &status);
+ if (!strncmp(rootname, "M106", 4) || !strncmp(rootname, "S100", 4)) {
+
+ cf_verbose(1, "Bright-earth exposure.");
+
+ /* Set EXP_STAT to TEMPORAL_LIMB. */
+ expstat = (int) TEMPORAL_LIMB;
+ FITS_update_key(fptr, TINT, "EXP_STAT", &expstat,
+ "Bright-earth exposure", &status);
+ }
+
+ if (!strncmp(rootname+8, "9", 1)) {
+
+ cf_verbose(1, "Airglow exposure. Will treat as extended source.");
+
+ /* Set EXP_STAT to TEMPORAL_LIMB. */
+ expstat = (int) TEMPORAL_LIMB;
+ FITS_update_key(fptr, TINT, "EXP_STAT", &expstat,
+ "Airglow exposure", &status);
+
+ /* Change SRC_TYPE to EE. */
+ (void) strcpy(src_type, "EE");
+ FITS_update_key(fptr, TSTRING, "SRC_TYPE", src_type,
+ "Airglow exposure ==> Treat as extended source", &status);
+
+ /* Write warning to trailer file and IDF header. */
+ FITS_write_comment(fptr, " ", &status);
+ sprintf(comment, "Airglow exposure. Not an astrophysical target.");
+ FITS_write_comment(fptr, comment, &status);
+ fits_get_system_time(datestr, &timeref, &status);
+ sprintf(comment, "CalFUSE v%s %.10s", CALFUSE_VERSION, datestr);
+ FITS_write_comment(fptr, comment, &status);
+ FITS_write_comment(fptr, " ", &status);
+ }
+
+ /* Read the SCRN file and see if user wants to skip any steps. */
+
+ FITS_read_key(fptr, TSTRING, "SCRN_CAL", filename_in, NULL, &status);
+ FITS_open_file(&scrnfits, cf_parm_file(filename_in), READONLY, &status);
+ FITS_read_key(scrnfits, TSTRING, "SAA_SCR", run_saa, NULL, &status);
+ FITS_read_key(scrnfits, TSTRING, "LIMB_SCR", run_limb, NULL, &status);
+ FITS_close_file(scrnfits, &status);
+
+ /* Now read the PARM file and see if user wants to skip any steps. */
+
+ FITS_read_key(fptr, TSTRING, "PARM_CAL", filename_in, NULL, &status);
+ FITS_open_file(&parmfits, cf_parm_file(filename_in), READONLY, &status);
+ FITS_read_key(parmfits, TSTRING, "RUN_BRST", run_brst, NULL, &status);
+ FITS_read_key(parmfits, TSTRING, "RUN_PHAX", run_walk, NULL, &status);
+ /* FITS_read_key(parmfits, TSTRING, "RUN_MKBK", run_mkbk, NULL, &status); */
+ /* FITS_read_key(parmfits, TSTRING, "RUN_BKGD", run_bkgd, NULL, &status); */
+ FITS_read_key(parmfits, TSTRING, "RUN_ASTG", run_astg, NULL, &status);
+ FITS_read_key(parmfits, TSTRING, "RUN_JITR", run_jitr, NULL, &status);
+ FITS_close_file(parmfits, &status);
+
+ if (!strncmp(run_limb, "OFF", 3) || !strncmp(run_limb, "off", 3))
+ FITS_update_key(fptr, TSTRING, "LIMB_COR", "OMIT", NULL, &status);
+
+ if (!strncmp(run_saa, "OFF", 3) || !strncmp(run_saa, "off", 3))
+ FITS_update_key(fptr, TSTRING, "SAA__COR", "OMIT", NULL, &status);
+
+ if (!strncmp(run_brst, "N", 1) || !strncmp(run_brst, "n", 1))
+ FITS_update_key(fptr, TSTRING, "BRST_COR", "OMIT", NULL, &status);
+
+ if (!strncmp(run_walk, "N", 1) || !strncmp(run_walk, "n", 1))
+ FITS_update_key(fptr, TSTRING, "PHAX_COR", "OMIT", NULL, &status);
+
+ /*
+ if (!strncmp(run_mkbk, "N", 1) || !strncmp(run_mkbk, "n", 1))
+ FITS_update_key(fptr, TSTRING, "MKBK_COR", "OMIT", NULL, &status);
+
+ if (!strncmp(run_bkgd, "N", 1) || !strncmp(run_bkgd, "n", 1))
+ FITS_update_key(fptr, TSTRING, "BKGD_COR", "OMIT", NULL, &status);
+ */
+
+ if (!strncmp(run_astg, "N", 1) || !strncmp(run_astg, "n", 1))
+ FITS_update_key(fptr, TSTRING, "ASTG_COR", "OMIT", NULL, &status);
+ else
+ FITS_update_key(fptr, TSTRING, "ASTG_COR", "PERFORM", NULL, &status);
+
+ if (!strncmp(run_jitr, "N", 1) || !strncmp(run_jitr, "n", 1))
+ FITS_update_key(fptr, TSTRING, "JITR_COR", "OMIT", NULL, &status);
+ else
+ FITS_update_key(fptr, TSTRING, "JITR_COR", "PERFORM", NULL, &status);
+
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done initializing headers");
+ return status;
+}
diff --git a/src/libcf/cf_geometric_distort.c b/src/libcf/cf_geometric_distort.c
new file mode 100644
index 0000000..1164b5c
--- /dev/null
+++ b/src/libcf/cf_geometric_distort.c
@@ -0,0 +1,145 @@
+/****************************************************************************
+ *
+ * Synopsis: cf_geometric_distort(fitsfile *header, long nevents,
+ * float *xfarf, float *yfarf,
+ * unsigned char *locflags)
+ *
+ * Description: Corrects the X and Y positions of the event for geometric
+ * distortion.
+ *
+ * Arguments: fitsfile *header Pointer to the location of the FITS
+ * header of the Intermediate Data File
+ * long nevents number of photons in the file
+ * float *xfarf X positions for each photon event
+ * float *yfarf Y positions for each photon event
+ * unsigned char *locflags Location flags for each event
+ *
+ * Calls : None
+ *
+ * Return: 0 on success
+ *
+ * History: 08/09/02 1.1 rdr Begin work
+ * 11/12/02 1.2 peb Added check to move only events in
+ * active region. Added locflags to
+ * function cal.
+ * 03/11/03 1.3 wvd Changed locflags to unsigned char.
+ * 05/20/03 1.4 rdr Added call to cf_proc_check
+ * 07/29/03 1.5 wvd If cf_proc_check fails, return errflg.
+ * 03/26/04 1.6 rdr Modify specbiny
+ * 04/05/04 1.7 wvd Modify specbiny only if specbiny > 1.
+ * 04/07/07 1.8 wvd Clean up compiler warnings.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdlib.h>
+#include "calfuse.h"
+
+int
+cf_geometric_distort(fitsfile *header, long nevents, float *xfarf,
+ float *yfarf, unsigned char *locflags)
+{
+ char CF_PRGM_ID[] = "cf_geometric_distort";
+ char CF_VER_NUM[] = "1.8";
+
+ char geomfile[FLEN_VALUE]={'\0'};
+ short *geomx_img, *geomy_img;
+ int errflg=0, status=0, hdutype, anynull, nullval;
+ int specbiny ;
+ long fpixel=1, j;
+ long xdim_gx, ydim_gx, xdim_gy, ydim_gy, npix_gx, npix_gy;
+ float scale_gx=1., zero_gx=0., scale_gy=1., zero_gy=0.;
+ float yexp ;
+ fitsfile *geomfits=NULL;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ /*
+ * Get the name of the calibration file and open it
+ */
+ FITS_read_key(header, TINT, "SPECBINY", &specbiny, NULL, &status);
+ cf_verbose(5,"Initial value of specbiny=%d",specbiny) ;
+ FITS_read_key(header, TSTRING, "GEOM_CAL", geomfile, NULL, &status);
+ cf_verbose(4, "Geometric correction file = %s", geomfile);
+ FITS_open_file(&geomfits, cf_cal_file(geomfile), READONLY, &status);
+
+ /* Read the y expansion factor from the calibration header */
+ FITS_movabs_hdu(geomfits, 1, &hdutype, &status);
+ FITS_read_key(geomfits, TFLOAT, "YEXPAND", &yexp, NULL, &status);
+ cf_verbose(3,"Y scale expansion factor = %f",yexp) ;
+
+ /* If SPECBINY > 1, scale by Y expansion factor */
+ if (specbiny > 1) {
+ specbiny = (int) (0.5 + (float) specbiny * yexp) ;
+ FITS_update_key(header, TINT, "SPECBINY", &specbiny, NULL, &status) ;
+ cf_verbose(5,"Corrected value of specbiny = %d",specbiny) ;
+ }
+
+ /*
+ * Read in the X distortion calibration image
+ */
+ FITS_movabs_hdu(geomfits, 2, &hdutype, &status);
+ FITS_read_key(geomfits, TLONG, "NAXIS1", &xdim_gx, NULL, &status);
+ FITS_read_key(geomfits, TLONG, "NAXIS2", &ydim_gx, NULL, &status);
+ npix_gx = xdim_gx * ydim_gx;
+
+ FITS_read_key(geomfits, TFLOAT, "BSCALE", &scale_gx, NULL, &status);
+ FITS_read_key(geomfits, TFLOAT, "BZERO", &zero_gx, NULL, &status);
+ geomx_img = (short *) cf_malloc(sizeof(short) * npix_gx);
+ fits_set_bscale(geomfits, 1., 0., &status);
+ FITS_read_img(geomfits, TSHORT, fpixel, npix_gx, &nullval, geomx_img,
+ &anynull, &status);
+ /*
+ * Read the Y distortion calibration image
+ */
+ FITS_movabs_hdu(geomfits, 3, &hdutype, &status);
+ FITS_read_key(geomfits, TLONG, "NAXIS1", &xdim_gy, NULL, &status);
+ FITS_read_key(geomfits, TLONG, "NAXIS2", &ydim_gy, NULL, &status);
+ npix_gy = xdim_gy * ydim_gy;
+
+ FITS_read_key(geomfits, TFLOAT, "BSCALE", &scale_gy, NULL, &status);
+ FITS_read_key(geomfits, TFLOAT, "BZERO", &zero_gy, NULL, &status);
+ geomy_img = (short *) cf_malloc(sizeof(short) * npix_gy);
+ fits_set_bscale(geomfits, 1., 0., &status);
+ FITS_read_img(geomfits, TSHORT, fpixel, npix_gy, &nullval, geomy_img,
+ &anynull, &status);
+ FITS_close_file(geomfits, &status);
+ /*
+ * Make sure that the X and Y distortion images are the same size
+ */
+ if (xdim_gx != xdim_gy || ydim_gx != ydim_gy)
+ cf_if_warning("X and Y distortion images not the same size.");
+ /*
+ * Use the data in the geometric distortion calibration images to
+ * correct XFARF and YFARF
+ */
+ for (j=0; j<nevents; j++) {
+ /*
+ * Move only events in active region.
+ */
+ if (!(locflags[j] & LOCATION_SHLD)) {
+ int xndx = (int) xfarf[j], yndx = (int) yfarf[j], ndx;
+ /*
+ * Check that the y indices are within bounds
+ */
+ if (yndx < 0)
+ yndx = 0;
+ else if (yndx > ydim_gy-1)
+ yndx = ydim_gy-1;
+
+ ndx = yndx*xdim_gx + xndx;
+ xfarf[j] += geomx_img[ndx]*scale_gx + zero_gx;
+ yfarf[j] += geomy_img[ndx]*scale_gy + zero_gy;
+ }
+ }
+ free(geomx_img);
+ free(geomy_img);
+
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done Processing");
+
+ return 0;
+}
diff --git a/src/libcf/cf_grating_motion.c b/src/libcf/cf_grating_motion.c
new file mode 100644
index 0000000..fe50d63
--- /dev/null
+++ b/src/libcf/cf_grating_motion.c
@@ -0,0 +1,432 @@
+/****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_grating_motion(header, nevents, time, x, y, channel,
+ * ntimes, ttime, tsunrise)
+ *
+ * Description: Reads spectral shift caused by thermal grating motions
+ * and corrects the X and Y locations of each photon event.
+ *
+ * fitsfile *header Pointer to the FITS header of the
+ * Intermediate Data File
+ * long nevents Number of photons in the file
+ * int *time Time (in seconds) since exposure start
+ * float *x Position of the photon (in pixels)
+ * float *y Position of the photon (in pixels)
+ * unsigned char channel Channel ID of each photon
+ * long ntimes Number of entries in timeline table
+ * float ttime Time array of timeline table
+ * short tsunrise Time since last sunrise
+ *
+ * Returns: 0 on success
+ *
+ * History: 09/05/02 1.0 RDR Begin work; adapted from cf_make_shift
+ * 04/21/03 1.3 wvd Use tsunrise array from timeline table.
+ * Do not assume that ttime is continuous.
+ * 05/20/03 1.4 rdr Added call to cf_proc_check
+ * 08/21/03 1.5 wvd Change channel to unsigned char.
+ * 06/25/04 1.6 wvd Estimate time between sunrises using
+ * orbit period in file header.
+ * 03/22/05 1.7 wvd Change tsunrise from float to short.
+ * Read orbital period from file header.
+ * 04/15/05 1.8 wvd Close GRAT_CAL file before exiting.
+ * If ORBPERID keyword is not found,
+ * assume that orbit period is 6000 s.
+ * 12/30/05 1.9 wvd Current algorithm corrects for orbital
+ * drift of spectrum, but wavelength zero
+ * point still varies with beta angle and
+ * observation date. New subroutine
+ * corrects LiF 1 data for these effects.
+ * 07/06/06 2.0 wvd Rewrite to use Dave Sahnow's new
+ * grating-correction file (version 003).
+ * 07/12/06 2.1 wvd Modify to read an arbitrary number of
+ * extensions, each with phase information
+ * in file header keywords.
+ * 10/20/06 2.2 wvd Grating motion also depends on APER_PA.
+ * Now we read it from the file header
+ * and select appropriate coefficients.
+ * If no correction is defined for a
+ * particular set of parameters, set
+ * 11/28/06 2.3 wvd Grating motion includes a long-term,
+ * non-periodic drift. Read it from the
+ * first four extensions of GRAT_CAL file.
+ * 04/07/07 2.4 wvd Clean up compiler warnings.
+ *
+ ****************************************************************************/
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "calfuse.h"
+
+static int
+read_nonperiodic_shifts(fitsfile *header, fitsfile *shiftfits,
+ long ntimes, float *dxlif, float *dylif, float *dxsic, float *dysic)
+{
+ char channel[FLEN_VALUE], comment[FLEN_CARD], detector[FLEN_VALUE];
+ int nrows, tndx;
+ int status=0, hdutype=0, hdunum=0;
+ long i;
+ float *mjd, *xshift, *yshift;
+ double expstart;
+
+ /* Read header keywords from data file. */
+ fits_read_key(header, TDOUBLE, "EXPSTART", &expstart, NULL, &status);
+ FITS_read_key(header, TSTRING, "DETECTOR", detector, NULL, &status);
+
+ /* First do the LiF channels */
+ if (!strncmp(detector,"1",1)) {
+ hdunum=2;
+ } else if (!strncmp(detector,"2",1)) {
+ hdunum=4;
+ } else {
+ cf_if_error("Cannot parse DETECTOR keyword in data file.");
+ }
+ cf_verbose(3, "Reading HDU %d of GRAT_CAL file.", hdunum);
+
+ FITS_movabs_hdu(shiftfits, hdunum, &hdutype, &status);
+ FITS_read_key(shiftfits, TSTRING, "CHANNEL", channel, NULL, &status);
+ sprintf(comment, "LiF%c", detector[0]);
+ if (strncmp(channel, comment, 4))
+ cf_if_error("Cannot find %s correction info in extension %d",
+ comment, hdunum);
+
+ /* Read X and Y corrections as a function of time. */
+ nrows = cf_read_col(shiftfits, TFLOAT, "MJD", (void *) &mjd);
+ nrows = cf_read_col(shiftfits, TFLOAT, "XSHIFT", (void *) &xshift);
+ nrows = cf_read_col(shiftfits, TFLOAT, "YSHIFT", (void *) &yshift);
+
+ /* Select appropriate MJD for this exposure. */
+ i = 0;
+ while (expstart > mjd[i] && i < nrows) i++;
+ tndx = i-1;
+ if (tndx < 0) tndx = 0;
+ cf_verbose(3,"EXPSTART = %g", expstart);
+ cf_verbose(3,"Using LiF corrections for MJD >= %g", mjd[tndx]);
+ if (tndx < nrows-1)
+ cf_verbose(3," and MJD < %g", mjd[tndx+1]);
+ cf_verbose(3,"xshift = %f, yshift = %f", xshift[tndx], yshift[tndx]);
+
+ /* Copy X and Y corrections to output arrays. */
+ for (i = 0; i < ntimes; i++) {
+ dxlif[i] = xshift[tndx];
+ dylif[i] = yshift[tndx];
+ }
+
+ /* Now do the SiC channels */
+ hdunum += 1;
+ cf_verbose(3, "Reading HDU %d of GRAT_CAL file.", hdunum);
+ FITS_movabs_hdu(shiftfits, hdunum, &hdutype, &status);
+ FITS_read_key(shiftfits, TSTRING, "CHANNEL", channel, NULL, &status);
+ sprintf(comment, "SiC%c", detector[0]);
+ if (strncmp(channel, comment, 4))
+ cf_if_error("Cannot find %s correction info in extension %d",
+ comment, hdunum);
+
+ /* Read X and Y corrections as a function of time. */
+ nrows = cf_read_col(shiftfits, TFLOAT, "MJD", (void *) &mjd);
+ nrows = cf_read_col(shiftfits, TFLOAT, "XSHIFT", (void *) &xshift);
+ nrows = cf_read_col(shiftfits, TFLOAT, "YSHIFT", (void *) &yshift);
+
+ /* Select appropriate MJD for this exposure. */
+ i = 0;
+ while (expstart > mjd[i] && i < nrows) i++;
+ tndx = i-1;
+ if (tndx < 0) tndx = 0;
+ cf_verbose(3,"Using SiC corrections for MJD >= %g", mjd[tndx]);
+ if (tndx < nrows-1)
+ cf_verbose(3," and MJD < %g", mjd[tndx+1]);
+ cf_verbose(3,"xshift = %f, yshift = %f", xshift[tndx], yshift[tndx]);
+
+ /* Copy X and Y corrections to output arrays. */
+ for (i = 0; i < ntimes; i++) {
+ dxsic[i] = xshift[tndx];
+ dysic[i] = yshift[tndx];
+ }
+
+ return 0;
+}
+
+
+static int
+read_periodic_shifts(fitsfile *header, fitsfile *shiftfits, int ncorrection,
+ double *lif_mjd0, double *lif_period, double *sic_mjd0, double *sic_period,
+ double *lif_x_coef, double *lif_y_coef, double *sic_x_coef, double *sic_y_coef)
+{
+ char detector[FLEN_CARD], channel[FLEN_CARD], comment[FLEN_CARD];
+ int status=0, hdutype=0, hdunum=0, anynul=0;
+ int i, nregions, region, typecode;
+ long max_coeff, ncoeff, width;
+ float aper_pa, *aperpalo, *aperpahi;
+ float *betalo, *betahi, *polelo, *polehi;
+ double beta, pole, sunang;
+
+ /* Initialize variables. */
+ *lif_mjd0 = *lif_period = *sic_mjd0 = *sic_period = 0.;
+ for (i = 0; i < 11; i++) {
+ lif_x_coef[i] = 0.;
+ lif_y_coef[i] = 0.;
+ sic_x_coef[i] = 0.;
+ sic_y_coef[i] = 0.;
+ }
+
+ FITS_read_key(header, TFLOAT, "APER_PA", &aper_pa, NULL, &status);
+ FITS_read_key(header, TDOUBLE, "SUNANGLE", &sunang, NULL, &status);
+ FITS_read_key(header, TDOUBLE, "POLEANGL", &pole, NULL, &status);
+ FITS_read_key(header, TSTRING, "DETECTOR", detector, NULL, &status);
+ while (999.9 > aper_pa && aper_pa > 360.) aper_pa -= 360.;
+ while (aper_pa < 0.) aper_pa += 360.;
+ beta = 180.0 - sunang;
+
+ cf_verbose(3, "S/C orientation: beta = %5.1f, pole = %5.1f, aper_pa = %5.1f",
+ beta, pole, aper_pa);
+
+ /* First do the LiF channels */
+ if (!strncmp(detector,"1",1)) {
+ hdunum=2;
+ } else if (!strncmp(detector,"2",1)) {
+ hdunum=4;
+ } else {
+ cf_if_error("Cannot parse DETECTOR keyword in read_shift_file");
+ }
+ hdunum += ncorrection * 4;
+ cf_verbose(3, "Reading HDU %d of GRAT_CAL file.", hdunum);
+
+ FITS_movabs_hdu(shiftfits, hdunum, &hdutype, &status);
+ FITS_read_key(shiftfits, TSTRING, "CHANNEL", channel, NULL, &status);
+ sprintf(comment, "LiF%c", detector[0]);
+ if (strncmp(channel, comment, 4))
+ cf_if_error("Cannot find %s correction info in extension %d",
+ comment, hdunum);
+
+ if (ncorrection > 1) {
+ FITS_read_key(shiftfits, TDOUBLE, "PERIOD", lif_period, NULL, &status);
+ FITS_read_key(shiftfits, TDOUBLE, "MJD_ZERO", lif_mjd0, NULL, &status);
+ cf_verbose(3, "%s: period = %g, mjd_zero = %g",
+ channel, *lif_period, *lif_mjd0);
+ }
+
+ nregions = cf_read_col(shiftfits, TFLOAT, "BETALO", (void *) &betalo);
+ nregions = cf_read_col(shiftfits, TFLOAT, "BETAHI", (void *) &betahi);
+ nregions = cf_read_col(shiftfits, TFLOAT, "POLELO", (void *) &polelo);
+ nregions = cf_read_col(shiftfits, TFLOAT, "POLEHI", (void *) &polehi);
+ nregions = cf_read_col(shiftfits, TFLOAT, "APERPALO", (void *) &aperpalo);
+ nregions = cf_read_col(shiftfits, TFLOAT, "APERPAHI", (void *) &aperpahi);
+ FITS_get_coltype(shiftfits, 7, &typecode, &ncoeff, &width, &status);
+ max_coeff = ncoeff;
+
+ for (i = 0; i < nregions; i++)
+ if (betalo[i] <= beta && beta < betahi[i] &&
+ polelo[i] <= pole && pole < polehi[i] &&
+ aperpalo[i] <= aper_pa && aper_pa < aperpahi[i]) break;
+
+ if (i == nregions)
+ cf_verbose(3, "LiF correction not defined for beta = %g, pole = %g, "
+ "aper_pa = %g", beta, pole, aper_pa);
+ else {
+ cf_verbose(3, "LIF REGION %2d: beta = %g - %g, pole = %g - %g,",
+ i, betalo[i], betahi[i], polelo[i], polehi[i]);
+ cf_verbose(3, " aper_pa = %g - %g, %d coefficients",
+ aperpalo[i], aperpahi[i], ncoeff);
+
+ region = i+1; /* CFITSIO counts from 1, not 0. */
+ FITS_read_col(shiftfits, TDOUBLE, 7, region, 1, ncoeff, NULL, lif_x_coef,
+ &anynul, &status);
+ FITS_read_col(shiftfits, TDOUBLE, 8, region, 1, ncoeff, NULL, lif_y_coef,
+ &anynul, &status);
+ }
+
+ /* Now the SiC channels */
+ hdunum += 1;
+ cf_verbose(3, "Reading HDU %d of GRAT_CAL file.", hdunum);
+ FITS_movabs_hdu(shiftfits, hdunum, &hdutype, &status);
+ FITS_read_key(shiftfits, TSTRING, "CHANNEL", channel, NULL, &status);
+ sprintf(comment, "SiC%c", detector[0]);
+ if (strncmp(channel, comment, 4))
+ cf_if_error("Cannot find %s correction info in extension %d",
+ comment, hdunum);
+
+ if (ncorrection > 1) {
+ FITS_read_key(shiftfits, TDOUBLE, "PERIOD", sic_period, NULL, &status);
+ FITS_read_key(shiftfits, TDOUBLE, "MJD_ZERO", sic_mjd0, NULL, &status);
+ cf_verbose(3, "%s: period = %g, mjd_zero = %g",
+ channel, *sic_period, *sic_mjd0);
+ }
+
+ nregions = cf_read_col(shiftfits, TFLOAT, "BETALO", (void *) &betalo);
+ nregions = cf_read_col(shiftfits, TFLOAT, "BETAHI", (void *) &betahi);
+ nregions = cf_read_col(shiftfits, TFLOAT, "POLELO", (void *) &polelo);
+ nregions = cf_read_col(shiftfits, TFLOAT, "POLEHI", (void *) &polehi);
+ nregions = cf_read_col(shiftfits, TFLOAT, "APERPALO", (void *) &aperpalo);
+ nregions = cf_read_col(shiftfits, TFLOAT, "APERPAHI", (void *) &aperpahi);
+ FITS_get_coltype(shiftfits, 7, &typecode, &ncoeff, &width, &status);
+ if (max_coeff < ncoeff) max_coeff = ncoeff;
+
+ for (i = 0; i < nregions; i++)
+ if (betalo[i] <= beta && beta < betahi[i] &&
+ polelo[i] <= pole && pole < polehi[i] &&
+ aperpalo[i] <= aper_pa && aper_pa < aperpahi[i]) break;
+
+ if (i == nregions)
+ cf_verbose(3, "SiC correction not defined for beta = %g, pole = %g, "
+ "aper_pa = %g", beta, pole, aper_pa);
+ else {
+ cf_verbose(3, "SIC REGION %2d: beta = %g - %g, pole = %g - %g,",
+ i, betalo[i], betahi[i], polelo[i], polehi[i]);
+ cf_verbose(3, " aper_pa = %g - %g, %d coefficients",
+ aperpalo[i], aperpahi[i], ncoeff);
+
+ region = i+1; /* CFITSIO counts from 1, not 0. */
+ FITS_read_col(shiftfits, TDOUBLE, 7, region, 1, ncoeff, NULL, sic_x_coef,
+ &anynul, &status);
+ FITS_read_col(shiftfits, TDOUBLE, 8, region, 1, ncoeff, NULL, sic_y_coef,
+ &anynul, &status);
+ }
+
+ cf_verbose(2, "Fourier coefficients of grating-motion correction:");
+ cf_verbose(2," LiF X LiF Y SiC X SiC Y");
+ for (i=0; i<max_coeff; i++) {
+ cf_verbose(2, "%10.5f %10.5f %10.5f %10.5f",lif_x_coef[i],
+ lif_y_coef[i], sic_x_coef[i], sic_y_coef[i]);
+ }
+
+ return 0;
+}
+
+
+static double
+thermal_shift(double theta, double *a)
+{
+ theta *= 2.0 * PI;
+ return (a[0]+
+ a[1]*sin(1.0*theta)+a[2]*cos(1.0*theta)+
+ a[3]*sin(2.0*theta)+a[4]*cos(2.0*theta)+
+ a[5]*sin(3.0*theta)+a[6]*cos(3.0*theta)+
+ a[7]*sin(4.0*theta)+a[8]*cos(4.0*theta)+
+ a[9]*sin(5.0*theta)+a[10]*cos(5.0*theta));
+}
+
+
+int
+cf_grating_motion(fitsfile *header, long nevents, float *time, float *x,
+ float *y, unsigned char *channel, long ntimes, float *ttime,
+ short *tsunrise)
+{
+ char CF_PRGM_ID[] = "cf_grating_motion";
+ char CF_VER_NUM[] = "2.4";
+
+ char shift_file[FLEN_CARD];
+ int errflg=0, status=0;
+ int calfvers, num_hdus, ncorrections;
+ long i, j, k;
+ float *dxlif, *dylif, *dxsic, *dysic, period;
+ double lif_mjd0, lif_period, sic_mjd0, sic_period;
+ double expstart, phase, lif_phase, sic_phase;
+ double lif_x_coef[11], lif_y_coef[11], sic_x_coef[11], sic_y_coef[11];
+ fitsfile *shiftfits;
+
+ /* Initialize error checking. */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+
+ /* Enter a time stamp into the log */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ /* Check whether routine is appropriate for this data file. */
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ /* Read exposure start time from IDF header. */
+ fits_read_key(header, TDOUBLE, "EXPSTART", &expstart, NULL, &status);
+
+ /* Read orbital period from IDF header. If not found, estimate. */
+ fits_read_key(header, TFLOAT, "ORBPERID", &period, NULL, &status);
+ if (status) {
+ status = 0;
+ period = 6000;
+ cf_verbose(1, "Keyword ORBPERID not found; assuming 6000 seconds.");
+ }
+ else
+ cf_verbose(2, "Estimated orbital period is %d seconds", cf_nint(period));
+
+ /* Allocate space for shift arrays. */
+ dxlif = (float *) cf_calloc(ntimes, sizeof(float));
+ dylif = (float *) cf_calloc(ntimes, sizeof(float));
+ dxsic = (float *) cf_calloc(ntimes, sizeof(float));
+ dysic = (float *) cf_calloc(ntimes, sizeof(float));
+
+ /* Open the GRAT_CAL file, check version number, and count extensions. */
+ FITS_read_key(header, TSTRING, "GRAT_CAL", shift_file, NULL, &status);
+ FITS_open_file(&shiftfits, cf_cal_file(shift_file), READONLY, &status);
+ FITS_get_num_hdus(shiftfits, &num_hdus, &status);
+ cf_verbose(3, "GRAT_CAL file %s contains %d HDUs.", shift_file, num_hdus);
+ ncorrections = num_hdus / 4;
+
+ /* Exit if the CALFVERS keyword is less than 4. */
+ FITS_read_key(shiftfits, TINT, "CALFVERS", &calfvers, NULL, &status);
+ if (calfvers < 4)
+ cf_if_error("Grating-calibration file format is out of date.");
+
+ /* First correct for the long-term, non-periodic grating motions. */
+ read_nonperiodic_shifts(header, shiftfits, ntimes, dxlif, dylif, dxsic, dysic);
+
+ /* Next correct for periodic motions. */
+ for (i = 1; i < ncorrections; i++) {
+
+ /* Read the calibration file. If there's no correction for this spacecraft
+ orientation, the coefficients will be set to zero. */
+ read_periodic_shifts(header, shiftfits, i, &lif_mjd0, &lif_period, &sic_mjd0,
+ &sic_period, lif_x_coef, lif_y_coef, sic_x_coef, sic_y_coef);
+
+ /* For orbital motions, the period is just the orbit period. */
+ if (i == 1) {
+ for (k=0; k<ntimes; k++) {
+ phase = tsunrise[k]/period;
+ dxlif[k] += thermal_shift(phase, lif_x_coef);
+ dylif[k] += thermal_shift(phase, lif_y_coef);
+ dxsic[k] += thermal_shift(phase, sic_x_coef);
+ dysic[k] += thermal_shift(phase, sic_y_coef);
+ }
+ }
+ /* For longer-term motions, read the period and zero point from the file header. */
+ else {
+ for (k=0; k<ntimes; k++) {
+ lif_phase = ((expstart - lif_mjd0) + ttime[k]/(24.*3600.))/lif_period;
+ sic_phase = ((expstart - sic_mjd0) + ttime[k]/(24.*3600.))/sic_period;
+ dxlif[k] += thermal_shift(lif_phase, lif_x_coef);
+ dylif[k] += thermal_shift(lif_phase, lif_y_coef);
+ dxsic[k] += thermal_shift(sic_phase, sic_x_coef);
+ dysic[k] += thermal_shift(sic_phase, sic_y_coef);
+ }
+ }
+ }
+
+ /* Close the GRAT_CAL file. */
+ FITS_close_file(shiftfits, &status);
+
+ /* Apply grating-motion correction. */
+ for (j=k=0; j<nevents; j++) {
+ while(ttime[k+1] - FRAME_TOLERANCE < time[j] && k+1 < ntimes) k++;
+ if (channel[j] > 0 && channel[j] < 4) {
+ x[j] += dxlif[k];
+ y[j] += dylif[k];
+ }
+ else if (channel[j] > 4) {
+ x[j] += dxsic[k];
+ y[j] += dysic[k];
+ }
+ }
+
+ /* Release memory. */
+ free(dxlif);
+ free(dylif);
+ free(dxsic);
+ free(dysic);
+
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished execution.");
+
+ return status;
+}
diff --git a/src/libcf/cf_header_io.c b/src/libcf/cf_header_io.c
new file mode 100644
index 0000000..d534ac2
--- /dev/null
+++ b/src/libcf/cf_header_io.c
@@ -0,0 +1,110 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_copy_to_memory(*infile, **memp);
+ *
+ * Description: Returns an in-memory FITS file pointer
+ *
+ *
+ * Arguments: char *infile Input data file name
+ * fitsfile **memp In memory FITS file pointer
+ *
+ * Returns: None
+ *
+ * History: 08/15/2002 1.0 jch Initial coding
+ * 12/18/03 bjg Change calfusettag.h to calfuse.h
+ *
+ ****************************************************************************/
+
+
+#include <string.h>
+#include <stdio.h>
+#include "calfitsio.h"
+#include "calfuse.h"
+
+void
+cf_read_header(char *infile, fitsfile **memp)
+{
+ fitsfile *ip; /* input file pointer */
+ int status=0; /* status used in CFITSIO */
+
+ /* open the input file */
+ FITS_open_file(&ip, infile, READONLY, &status);
+
+ /* create the temp file in memory */
+ FITS_create_file(memp, "mem://", &status);
+
+ /* copy the input primary HDU to the temp file */
+ FITS_copy_hdu(ip, *memp, 0, &status);
+
+ /* close the input file */
+ FITS_close_file(ip, &status);
+}
+
+
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_copy_to_file(*memp, *outfile);
+ *
+ * Description: Copy the header in the in-memory FITS file to an output FITS
+ * file's primary HDU.
+ *
+ *
+ * Arguments: fitsfile *memp In memory FITS file pointer
+ * char *outfile Output data file name
+ *
+ * Returns: None
+ *
+ * History: 08/15/2002 jch 1.0 Initial coding
+ *
+ ****************************************************************************/
+
+void
+cf_write_header(fitsfile *memp, char *outfile)
+{
+ fitsfile *op; /* output file pointer */
+ int status=0; /* status used in CFITSIO */
+
+ int ncards_memp, ncards_op, morekeys=0;
+ char card[FLEN_CARD];
+
+ /* Open the output FITS file. */
+ FITS_open_file(&op, outfile, READWRITE, &status);
+
+ fits_get_hdrspace(memp, &ncards_memp, &morekeys, &status);
+ fits_get_hdrspace(op, &ncards_op, &morekeys, &status);
+
+ /* overwrite output file's primary HDU card by card */
+ if (ncards_op >= ncards_memp) {
+ int i;
+ for (i=1; i<=ncards_memp; i++) {
+ FITS_read_record(memp, i, card, &status);
+ FITS_modify_record(op, i, card, &status);
+ }
+ /* delete from the bottom */
+ for (i=ncards_op; i>=ncards_memp+1; i--) {
+ FITS_delete_record(op, i, &status);
+ }
+ }
+ else {
+ int i;
+ for (i=1; i<=ncards_op; i++) {
+ FITS_read_record(memp, i, card, &status);
+ FITS_modify_record(op, i, card, &status);
+ }
+ for (i=ncards_op+1; i<=ncards_memp; i++) {
+ FITS_read_record(memp, i, card, &status);
+ FITS_write_record(op, card, &status);
+ }
+ }
+
+ /* close output file. */
+ FITS_close_file(op, &status);
+}
diff --git a/src/libcf/cf_identify_channel.c b/src/libcf/cf_identify_channel.c
new file mode 100644
index 0000000..7d6a8e3
--- /dev/null
+++ b/src/libcf/cf_identify_channel.c
@@ -0,0 +1,201 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_identify_channel(fitsfile *header, long nevents,
+ * float *xfarf, float *yfarf, unsigned char *chan,
+ * unsigned char *locflags, int pad, int final_call)
+ *
+ * Description: Assigns an aperture to each photon in the IDF.
+ *
+ * Change of logic in v1.11: This routine may be called
+ * multiple times. All photons are examined and assigned to
+ * a channel unless final_call = TRUE. On the final call, only
+ * photons with channel > 0 are considered. Because only photons
+ * assigned to a channel are motion corrected, the spectrum can
+ * move on top of previously-rejected photons. We don't want
+ * to include them in the final spectrum, so leave their channel
+ * values unchanged.
+ *
+ * Change of logic in v1.16: We've decided to use extended-
+ * source apertures for the non-target channels. These
+ * apertures can overlap, so we assign disputed photons to
+ * their most likely source. In decreasing order, these are
+ * the target aperture, the larger non-target aperture, and
+ * the smaller non-target aperture.
+ *
+ * Arguments: fitsfile *header Pointer to the location of the FITS
+ * header of the Intermediate Data File
+ * long nevents Number of photons in the file
+ * float *xfarf X positions for each photon event
+ * float *yfarf Y positions for each photon event
+ * unsigned char *chan Pointer to pointer to the array
+ * containing the channel information
+ * unsigned char *locflags Location flags for each photon
+ * int pad Switch saying whether to expand the
+ * width of the tabulated extraction
+ * window (yes if > 0). The number refers
+ * to the amount of padding (e.g. pad=5
+ * will add 5 pixels to each side of the
+ * extraction window.
+ * int final_call This subroutine may be called
+ * many times. The last time, set
+ * final_call = TRUE.
+ *
+ * Return: 0 on success
+ *
+ * History: 08/09/02 1.0 RDR Begin work
+ * 09/04/02 1.1 rdr Change program so that it just fills
+ * the channel array rather than creating
+ * and filling it. Decreased the size of
+ * the mask. Use the value of pad to
+ * specify degree of window padding.
+ * Check for extended source.
+ * 12/11/02 1.2 RDR Adjust aperture limits by measured
+ * y centroid (if available)
+ * 02/11/03 1.3 rdr changed test for ycent values
+ * 02/12/03 1.6 wvd Don't crash if YCENT# is undefined
+ * 03/25/03 1.7 rdr Ignore pinhole aperture
+ * 03/31/03 1.10 wvd Ignore regions where YMAX = YMIN + 1.
+ * They are not valid channel regions.
+ * 04/07/03 1.11 wvd Shift channel boundaries to match FPA
+ * positions. Implement use of cf_verbose.
+ * Update CHID_COR if final_call = TRUE.
+ * 05/20/03 1.12 rdr Added call to cf_proc_check
+ * 07/23/03 1.14 rdr Correct procedure which reads CHID
+ * calibration file
+ * 08/21/03 1.15 wvd Change channel to unsigned char.
+ * 08/25/03 1.16 wvd Use extended windows for non-target
+ * apertures. Replace aperture mask
+ * with a grid of aperture limits.
+ * Use cf_nint() to round floats.
+ * 08/25/03 1.17 wvd Change coltype from string to int
+ * in cf_read_col.
+ * 09/08/03 1.18 wvd Default value is channel[i] = 0.
+ * Identify low and high values of x
+ * by setting y limits to -NYMAX.
+ * 09/18/03 1.19 wvd Consider only photons falling on the
+ * active area of the detector.
+ * 11/12/03 1.20 wvd Don't bother with non-target channels
+ * in HIST mode.
+ * 06/14/04 1.21 wvd Don't bother shifting extraction
+ * windows to correct for FPA position.
+ * If known, shift is not needed.
+ * 06/17/04 1.22 wvd Because the extraction windows don't
+ * quite match the WGTS arrays, we pad
+ * windows by XPAD pixels in X.
+ * 07/21/04 1.23 wvd Change i from long to int.
+ * 03/15/05 1.24 wvd Call cf_extraction_limits to get limits
+ * of extraction windows, rather than
+ * reading CHID_CAL file directly.
+ * Do not pad windows in X.
+ * 03/16/05 1.25 wvd Pass srctype to cf_extraction_limits.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include "calfuse.h"
+
+int
+cf_identify_channel(fitsfile* header, long npts, float* x, float* y,
+ unsigned char* channel, unsigned char* locflags, int pad,
+ int final_call)
+{
+ char CF_PRGM_ID[] = "cf_identify_channel";
+ char CF_VER_NUM[] = "1.25";
+
+ char instmode[FLEN_VALUE], zero=0;
+ int errflg=0, extended, n_chan=6, status=0;
+ int active_ap[2], chan, srctype[8];
+ int i; /* Always steps through targ_ap */
+ int *targ_ap,
+ lwrs[]={3,7,2,6,1,5}, /* LWRS, MDRS, HIRS (LiF & SiC) */
+ mdrs[]={2,6,3,7,1,5}, /* MDRS, LWRS, HIRS (LiF & SiC) */
+ hirs[]={1,5,3,7,2,6}; /* HIRS, LWRS, MDRS (LiF & SiC) */
+ long j, /* Always steps through extraction window arrays */
+ k; /* Always steps through photon list */
+ short xint, yint, xmin, xmax, *ymin[6], *ymax[6];
+
+ /* Initialize error checking */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+
+ /* Enter a time stamp into the log */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ /* Check whether routine is appropriate for this data file. */
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ /* If data were taken in HIST mode, ignore non-target channels. */
+ FITS_read_key(header, TSTRING, "INSTMODE", instmode, NULL, &status);
+ if (!strncmp(instmode, "HIST", 4)) n_chan = 2;
+
+ /* Determine source type and active apertures */
+ extended = cf_source_aper(header, active_ap);
+
+ /* Set the order in which we will assign photons to channels. */
+ switch (active_ap[0]) {
+ case 1: targ_ap = hirs;
+ break;
+ case 2: targ_ap = mdrs;
+ break;
+ default: targ_ap = lwrs;
+ break;
+ }
+
+ /* For target apertures, srctype reflects target.
+ Use extended apertures for the rest. */
+ for (i = 0; i < 2; i++) srctype[i] = extended;
+ for ( ; i < n_chan; i++) srctype[i] = 1;
+
+ /* While we're at it, here's one more bit of i/o. */
+ if (pad > 0)
+ cf_verbose(3, "Padding extraction windows by %d Y pixels", pad);
+
+ /*
+ * Loop through all channels, read extraction limits,
+ * and apply any requested padding.
+ */
+ for (i = 0; i < n_chan; i++) {
+ chan = targ_ap[i];
+
+ /* Read the extraction limits for this aperture. */
+ (void) cf_extraction_limits(header, chan, srctype[i],
+ (ymin+i), (ymax+i), &xmin, &xmax);
+
+ /* Add any padding requested by the user. */
+ if (pad > 0) {
+ for (j = xmin; j <= xmax; j++) {
+ ymin[i][j] -= pad;
+ ymax[i][j] += pad;
+ }
+ }
+ }
+
+ /* Loop through photon list. Assign a channel ID to each. */
+ cf_verbose(3, "Entering loop to assign channel ID's.");
+ for (k = 0; k < npts; k++) {
+ if (final_call && channel[k] == zero) continue;
+ channel[k] = zero;
+ if (!(locflags[k] & LOCATION_SHLD)) {
+ xint = (short) cf_nint(x[k]);
+ yint = (short) cf_nint(y[k]);
+ if (xint < xmin || xint > xmax) continue;
+ for (i = 0; i < n_chan; i++) {
+ if (yint >= ymin[i][xint] && yint <= ymax[i][xint]) {
+ channel[k] = (char) targ_ap[i];
+ break;
+ }
+ }
+ }
+ }
+
+ /* If final_call = TRUE, update IDF header keyword for this subroutine. */
+ if (final_call)
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done Processing");
+
+ return status;
+}
diff --git a/src/libcf/cf_idf_io.c b/src/libcf/cf_idf_io.c
new file mode 100644
index 0000000..8d8d43a
--- /dev/null
+++ b/src/libcf/cf_idf_io.c
@@ -0,0 +1,161 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: npts = cf_read_col(fitsfile, coltype, colname, colval)
+ *
+ * Description: Procedure to read the values from a column in a FUSE
+ * Intermediate Data File (IDF).
+ * Because floating-point data may be stored as shorts,
+ * must convert data to type expected by calling routine.
+ *
+ * Variables: fitsfile *fileptr Pointer to the input file
+ * int coltype Data type of the column
+ * (TBYTE, TDOUBLE, TSHORT, or TFLOAT)
+ * char *colname Name of the column to re read
+ * (e.g. TIME or XFARF)
+ * void **colval Address of the array containing
+ * the data (must be cast to void)
+ *
+ * Return: long npts Number of points in the array
+ *
+ * Example:
+ * fitsfile *infits ;
+ * long npts ;
+ * float *xfarf ;
+ *
+ * FITS_open_file(&infits, argv[1], READWRITE, &status);
+ * npts = cf_read_col(infits, TFLOAT, "XFARF", (void **) &xfarf);
+ *
+ * History: 08/08/02 rdr Begin work
+ * 06/11/03 wvd v1.4 Pass coltype from calling routine.
+ * 08/25/03 wvd v1.5 Change coltype from string to int.
+ * 08/28/03 wvd v1.6 Use CASEINSEN in calls to
+ * fits_get_colnum
+ * 12/18/03 bjg v1.7 Change calfusettag.h to calfuse.h
+ * 04/28/05 wvd v1.8 Allow arrays to be read as doubles.
+ * 08/24/07 wvd v1.9 Read nrows as type TLONG.
+ *
+ *************************************************************************/
+
+#include <string.h>
+#include "calfuse.h"
+
+long
+cf_read_col(fitsfile *infits, int coltype, char *colname, void **colval)
+{
+ int colnum, tcode, intnull=0, anynull, status=0;
+ long nrows, width, repeat, nentries;
+
+ /* Get the column number of the column to be extracted. */
+ FITS_get_colnum(infits, CASEINSEN, colname, &colnum, &status);
+
+ /* Determine the properties of the column. */
+ fits_get_coltype(infits, colnum, &tcode, &repeat, &width, &status);
+
+ /* Read the number of rows in the table. */
+ FITS_read_key(infits, TLONG, "NAXIS2", &nrows, NULL, &status);
+
+ /* Calculate the number of entries in the table. */
+ nentries = nrows * repeat;
+
+ /* Set tcode and width to values expected by calling routine. */
+ if (coltype == TFLOAT) {
+ tcode = TFLOAT;
+ width = (long) sizeof (float);
+ }
+ if (coltype == TDOUBLE) {
+ tcode = TDOUBLE;
+ width = (long) sizeof (double);
+ }
+
+ /* Allocate space for the file. */
+ *colval = (void *) cf_malloc(nentries * width);
+
+ /* Read the data from the file. */
+ FITS_read_col(infits, tcode, colnum, 1, 1, nentries, &intnull, *colval,
+ &anynull, &status);
+
+ return nentries;
+}
+
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_write_col(fitsfile, colname, colval, npts)
+ *
+ * Description: Procedure to write an array into a specified column of a
+ * FUSE Intermediate Data File (IDF).
+ * Because floating-point data may be stored as shorts,
+ * data type of array may not match that of output file header.
+ *
+ * Variables: fitsfile *fileptr Pointer to the input file
+ * int coltype Data type of the column
+ * (TBYTE, TDOUBLE, TSHORT, or TFLOAT)
+ * char *colname Name of the column to re read
+ * (e.g. TIME or XFARF)
+ * void **colval Address of the array containing
+ * the data (must be cast to void)
+ * long npts Number of points in the array
+ *
+ * Reture: status
+ *
+ * Example:
+ * fitsfile *infits ;
+ * long npts=100 ;
+ * float *xfarf ;
+ *
+ * FITS_open_file(&infits, argv[1], READWRITE, &status);
+ * npts = cf_read_col(infits, TFLOAT, "XFARF", (void **) &xfarf);
+ *
+ * History: 08/08/02 rdr Begin work
+ * 06/11/03 wvd v1.4 Pass coltype from calling routine.
+ * Check for overflow in X arrays.
+ * 08/25/03 wvd v1.5 Change coltype from string to int.
+ * 08/28/03 wvd v1.6 Use CASEINSEN in calls to
+ * fits_get_colnum
+ *
+ *************************************************************************/
+
+int
+cf_write_col(fitsfile *infits, int coltype, char *colname, void *colval,
+ long npts)
+{
+ int colnum, tcode, status=0;
+ long i, width, nevents;
+
+ /* Get the column number of the column to be written. */
+ FITS_get_colnum(infits, CASEINSEN, colname, &colnum, &status);
+
+ /* Determine the properties of the column. */
+ fits_get_coltype(infits, colnum, &tcode, &nevents, &width, &status);
+
+ /*
+ * If the calling routine and the IDF differ on the data type of this
+ * array, use the data type from the calling routine.
+ */
+ if (coltype == TFLOAT) {
+ tcode = TFLOAT;
+ width = (long) sizeof (float);
+ }
+
+ /* Check for overflow in X arrays. */
+ if (!strncmp(colname, "X", 1)) {
+ float *x;
+ x = (float *) colval;
+ for (i = 0; i < nevents; i++) {
+ if (x[i] < 0) x[i] = 0.;
+ if (x[i] > NXMAX - 1) x[i] = NXMAX - 1;
+ }
+ }
+
+ /* Write the data to the relevant column. */
+ FITS_write_col(infits, tcode, colnum, 1, 1, npts, colval, &status);
+
+ return status;
+}
diff --git a/src/libcf/cf_ids_dead_time.c b/src/libcf/cf_ids_dead_time.c
new file mode 100644
index 0000000..656222e
--- /dev/null
+++ b/src/libcf/cf_ids_dead_time.c
@@ -0,0 +1,109 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_ids_dead_time(fitsfile *header, long nseconds,
+ * float *aic_rate, float *ids_dtc)
+ *
+ * Description: Computes the weighting factor needed to correct
+ * for the IDS dead time.
+ *
+ * Arguments: fitsfile *header Pointer to FITS file containing the
+ * header of the intermediate data file
+ * long nseconds The number of timeline values
+ * float *aic_rate An array of Active Image Counter
+ * values
+ * float *ids_dtc Dead-time correction array (returned)
+ *
+ * Calls:
+ *
+ * Return: 0 on success
+ *
+ * History: 10/27/02 1.1 peb Begin work
+ * 11/11/02 1.2 peb Correct function description and add
+ * cf_timestamp after IDS__COR check.
+ * 12/09/02 1.4 wvd Calculate DT correction for each time
+ * step, then apply to photons.
+ * Set keyword IDS_DEAD.
+ * 05/20/03 1.5 rdr Add proc_check call.
+ * 08/01/03 1.8 wvd Just calculate correction; don't
+ * apply it. Return ids_dtc array.
+ * 08/04/03 1.9 wvd Convert aic_rate to type short.
+ * 11/26/03 1.10 wvd Convert aic_rate to type float.
+ * 02/09/04 1.11 wvd max_ids_rate depends on instrument
+ * mode. For TTAG data, include time
+ * stamps in aic_rate.
+ * 04/07/07 1.12 wvd Clean up compiler warnings.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include "calfuse.h"
+
+int
+cf_ids_dead_time(fitsfile *header, long nseconds, float *aic_rate,
+ float *ids_dtc)
+{
+ char CF_PRGM_ID[] = "cf_ids_dead_time";
+ char CF_VER_NUM[] = "1.12";
+
+ char elecfile[FLEN_VALUE], instmode[FLEN_VALUE];
+ int errflg=0, status=0;
+ long k;
+ float ids_rate, tstamps, ttperiod;
+ float mean_ids_dtc = 0.;
+ float max_ids_rate = 1.;
+ fitsfile *elecfits;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+ /*
+ * Read observing mode and interval between time stamps.
+ */
+ FITS_read_key(header, TSTRING, "INSTMODE", instmode, NULL, &status);
+ FITS_read_key(header, TFLOAT, "TTPERIOD", &ttperiod, NULL, &status);
+ /*
+ * Compute number of time stamps per second (0 for HIST data).
+ */
+ if (!strncmp(instmode, "TTAG", 4)) /* TTAG MODE */
+ if (ttperiod > 1e-8) tstamps = 1./ttperiod;
+ else tstamps = 1.;
+ else /* HIST MODE */
+ tstamps = 0.;
+ /*
+ * Maximum IDS rate depends on instrument mode.
+ */
+ FITS_read_key(header, TSTRING, "ELEC_CAL", elecfile, NULL, &status);
+ FITS_open_file(&elecfits, cf_cal_file(elecfile), READONLY, &status);
+ if (!strncmp(instmode, "TTAG", 4))
+ FITS_read_key(elecfits, TFLOAT, "TTAG_BUS", &max_ids_rate, NULL, &status);
+ else
+ FITS_read_key(elecfits, TFLOAT, "HIST_BUS", &max_ids_rate, NULL, &status);
+ FITS_close_file(elecfits, &status);
+ cf_verbose(4, "max_ids_rate = %f", max_ids_rate);
+ /*
+ * Calculate the IDS dead-time correction.
+ */
+ for (k=0; k<nseconds; k++) {
+ ids_rate = aic_rate[k] + tstamps;
+
+ if (ids_rate > max_ids_rate)
+ ids_dtc[k] = ids_rate/max_ids_rate;
+ else
+ ids_dtc[k] = 1.0;
+ mean_ids_dtc += ids_dtc[k];
+ }
+ /*
+ * Write mean IDS dead-time correction to file header.
+ */
+ mean_ids_dtc /= nseconds;
+ cf_verbose(2, "Mean IDS dead-time correction: %f", mean_ids_dtc);
+ FITS_update_key(header, TFLOAT, "IDS_DEAD", &mean_ids_dtc, NULL, &status);
+
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return 0;
+}
diff --git a/src/libcf/cf_init_support.c b/src/libcf/cf_init_support.c
new file mode 100644
index 0000000..68db0f6
--- /dev/null
+++ b/src/libcf/cf_init_support.c
@@ -0,0 +1,1344 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Description: Routines used by cf_ttag_init and cf_hist_init.
+ *
+ * History: 07/15/03 v1.1 wvd Move routines from cf_ttag_init
+ * 07/24/03 1.2 wvd If tsunrise and tsunset get huge,
+ * store them as floats, not integers.
+ * 07/30/03 1.5 wvd Omit TZERO and TSCALE for these
+ * timeline table entries:
+ * HIGH_VOLTAGE
+ * LIF_CNT_RATE
+ * SIC_CNT_RATE
+ * FEC_CNT_RATE
+ * AIC_CNT_RATE
+ * BKGD_CNT_RATE
+ * This will effectively convert them
+ * to arrays of shorts.
+ * Add 0.5 to all count rates in
+ * anticipation of truncation.
+ * 08/22/03 1.7 wvd Populate MIN_LIMB keyword.
+ * 09/03/03 1.8 wvd Implement cf_verbose throughout.
+ * Add EXP_JITR and EXPNIGHT to IDF header.
+ * 09/22/03 1.9 wvd Correct comment field for YCENT1
+ * 10/02/03 1.10 wvd Modify timeline table:
+ * - no time gaps
+ * - set TEMPORAL_OPUS for times outside
+ * of OPUS good-time intervals.
+ * Delete cf_get_times.
+ * Use cf_read_col in cf_get_gti
+ * and cf_get_geocorona.
+ * Don't modify gtis[0] for HIST data.
+ * 10/13/03 1.11 wvd Add 1s to end of timeline table for
+ * TTAG data.
+ * 10/15/03 1.12 wvd Swap voltages for detectors 2A and 2B
+ * if housekeeping file was written
+ * before April of 2003.
+ * 10/22/03 1.13 wvd Compute EXPNIGHT for raw data file
+ * and write to output file header.
+ * Add YQUALLIF & YQUALSIC keywords.
+ * 11/25/03 1.14 wvd Raise upper limits on LIF_CNT_RATE and
+ * SIC_CNT_RATE to 32000. Raise limits
+ * on FEC_CNT_RATE and AIC_CNT_RATE
+ * to 64000. Use TZERO = 32768 for
+ * FEC_CNT_RATE and AIC_CNT_RATE.
+ * They must be read as floats.
+ * 01/15/04 1.15 bjg Now performs cnt0 -= 16777216 when
+ * cnt0 < cnt before computing
+ * cnt_rate for the first cnt and
+ * cnt0 in function
+ * fill_cntrate_array
+ * cf_timeline 1.6 -> 1.7
+ * free the arrays only at the end
+ * version 1.6 tries to read tflag
+ * array after it has been freed
+ * 02/06/04 1.16 wvd Modify fill_cntrate_array() so
+ * that the output array no longer runs
+ * 16 seconds behind the HSKP data.
+ * 02/18/04 1.17 wvd Voltages for detectors 2A and 2B
+ * are still swapped. Set HKERR_ENDS
+ * to 20100000.
+ * 03/04/04 1.18 bjg Read begin and end counters as float
+ * Set countrate to zero when cntb is more
+ * than 16777216.
+ * 03/19/04 bjg FITS data types (TFLOAT, TSTRING, TBYTE)
+ * now match type of C containers (float,
+ * char *, char) when reading and writing
+ * in FITS header.
+ * 04/06/04 1.19 bjg Include ctype.h
+ * Functions now return EXIT_SUCCESS
+ * Remove unused variables
+ * Remove static keyword in struct key
+ * definition
+ * If cnt_rate>32000 set cnt_rate=0
+ * 05/07/04 1.20 wvd Delete HKERR_ENDS. If HSKPVERS keyword
+ * is not present in HSKP file, test to
+ * determine whether high-voltage arrays
+ * for 2A and 2B are swapped.
+ * 05/27/04 1.21 bjg Test counter keywords. If bad, use
+ * cnt_rate=NEVENTS/exptime or 0
+ * 06/14/04 1.22 wvd Add BRIT_OBJ keyword to header.
+ * Rewrite fill_hv_array() to make it more
+ * robust.
+ * If HV values in header are good, use
+ * them to determine whether HV
+ * arrays for 2A and 2B are swapped.
+ * 10/17/04 bjg Replaced lots of
+ * "while (val<array[i] && i<nmax)"
+ * with
+ * "while (i<nmax && val<array[i])"
+ * 11/09/04 1.23 bjg Fixed Timeline AIC countrate.
+ * Sum of AIC countrates
+ * for all 4 detectors
+ * 02/09/05 1.24 wvd Use count rates, rather than counts,
+ * when testing header keywords.
+ * 02/15/05 1.25 wvd If the count-rate arrays get too big,
+ * truncate and issue a warning.
+ * 02/28/05 1.26 wvd Fix bug in AIC count-rate test.
+ * Lower AIC and FEC thresholds to
+ * 0.8 * NEVENTS/EXPTIME.
+ * If GTI values span more than 25 ks,
+ * make timeline table entries only for
+ * times in the GTI's.
+ * Set OPUS flag for timeline-table
+ * entries whose values exceed 25 ks.
+ * Rewrite fill_hv_array() to make it more
+ * robust.
+ * Set overflow count-rate values to zero.
+ * Treat HV as array of shorts throughout.
+ * 03/03/05 1.27 wvd Raise exposure-length limit to 55 ksec.
+ * 03/14/05 1.28 wvd Modify gtis[0] only if TTPERIOD = 1.0
+ * 03/17/05 1.29 wvd Issue a warning if bigtime = TRUE.
+ * 03/21/05 1.30 wvd If there's a break in the timeline
+ * array, recalculate times of sunrise
+ * and sunset. Delete bigtime and the
+ * mechanism for storing tsunrise and
+ * tsunset as floats. Define lastsunrise
+ * and lastsunset relative to ptime[0],
+ * rather than mjd_start. Write time
+ * between sunsets to header in ORBPERID.
+ * 08/30/05 1.31 wvd Allow for the possibility that some of
+ * the photons have absurd arrival times
+ * AND others are not included in the
+ * OPUS good-time intervals.
+ * 08/31/05 1.32 wvd In cf_set_background_limits, use LWRS
+ * limits for RFPT observations.
+ * 11/03/05 1.33 wvd When housekeeping file is missing and
+ * engineering snapshot times are
+ * corrupted, use EXPSTART and EXPEND
+ * to estimate eng_time. If EXPEND
+ * is corrupted, use EXPTIME.
+ * 04/09/06 1.34 wvd Flag as bad times after final OPUS GTI.
+ * 08/21/06 1.35 wvd Treat housekeeping time array as double.
+ * Don't add 0.5 to arrays in subroutine
+ * fill_cntrate_array.
+ * 12/29/06 1.36 wvd Correct bug in construction of AIC
+ * count-rate array.
+ * 02/13/07 1.37 wvd If TTPERIOD = 0, set to 1.
+ * 03/07/07 1.38 wvd Remove pos from call to space_vel.
+ * 03/23/07 1.39 wvd Give more detailed error message if
+ * housekeeping file is not usable.
+ * 04/07/07 1.40 wvd Clean up compiler warnings.
+ * 05/18/07 1.41 wvd If HV array in HSKP file consists of a
+ * single 0 followed by all -1's, use
+ * header info to populate timeline table.
+ * Rename fill_hv_array to hv_from_hskp
+ * and add hv_from_header.
+ *
+ ****************************************************************************/
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <ctype.h>
+#include "calfuse.h"
+#include "sgp4.h"
+
+
+struct key {
+ char keyword[FLEN_KEYWORD];
+ float value;
+ };
+
+
+/***********************************************************************
+ *
+ * procedure to get the GTI values from the input file
+ *
+ **********************************************************************/
+
+int cf_get_gti(fitsfile *infits, double **gtis, double **gtie) {
+
+ char instmode[FLEN_VALUE];
+ int ngti, status=0;
+ float ttperiod;
+
+ cf_verbose(3, "Entering cf_get_gti.");
+
+ /* Read header keywords. */
+ FITS_movabs_hdu(infits, 1, NULL, &status);
+ FITS_read_key(infits, TSTRING, "INSTMODE", instmode, NULL, &status);
+ FITS_read_key(infits, TFLOAT, "TTPERIOD", &ttperiod, NULL, &status);
+ if (ttperiod < 1E-6) ttperiod = 1.0;
+
+ /* Move to the second extension and read the GTI's */
+ FITS_movabs_hdu(infits, 3, NULL, &status);
+ ngti = cf_read_col(infits, TDOUBLE, "START", (void **) gtis);
+ ngti = cf_read_col(infits, TDOUBLE, "STOP", (void **) gtie);
+
+ /*
+ * If gtis[0] = 0.000, then it is probably wrong.
+ * Set it equal to the fractional part of gtie[0].
+ * In HIST mode, gtis[0] is always 0., so don't change it.
+ * This trick only works if TTPERIOD = 1.0.
+ */
+ if (**gtis < FRAME_TOLERANCE && !strncmp(instmode, "TTAG", 4)
+ && fabs(ttperiod - 1.0) < FRAME_TOLERANCE)
+ **gtis = fmod(*gtie[0], 1.);
+
+ cf_verbose(3, "Exiting cf_get_gti.");
+ return ngti;
+}
+
+/***********************************************************************
+ *
+ * procedure to get a list of geocoronal line locations from the AIRG
+ * calibration file
+ *
+ ***********************************************************************/
+
+int
+cf_get_geocorona(fitsfile *outfits, short **gxmin, short **gxmax,
+ short **gymin, short **gymax)
+{
+ char airgfile[FLEN_VALUE];
+ int nrow, status=0;
+ fitsfile *airgfits;
+
+ cf_verbose(3, "Entering cf_get_geocorona.");
+
+ /* Read the name of the AIRG calibration file to use and open it */
+ FITS_movabs_hdu(outfits, 1, NULL, &status);
+ FITS_read_key(outfits, TSTRING, "AIRG_CAL", airgfile, NULL, &status);
+ FITS_open_file(&airgfits, cf_cal_file(airgfile), READONLY, &status);
+ cf_verbose(3, "AIRG_CAL = %s", airgfile);
+
+ /*
+ * Move to the first extension and read the positions of the
+ * geocoronal lines.
+ */
+ FITS_movabs_hdu(airgfits, 2, NULL, &status);
+ nrow = cf_read_col(airgfits, TSHORT, "XMIN", (void **) gxmin);
+ nrow = cf_read_col(airgfits, TSHORT, "XMAX", (void **) gxmax);
+ nrow = cf_read_col(airgfits, TSHORT, "YMIN", (void **) gymin);
+ nrow = cf_read_col(airgfits, TSHORT, "YMAX", (void **) gymax);
+
+ FITS_close_file(airgfits, &status);
+ cf_verbose(3, "Exiting cf_get_geocorona.");
+ return nrow;
+}
+
+
+/***********************************************************************
+ *
+ * Procedures to compute times of most recent sunrise and sunset
+ *
+ ***********************************************************************/
+
+SGP4 set_orbit_parms(fitsfile *);
+
+static double
+sunrise(double mjd_start, SGP4 sgp4)
+{
+ double ang_sep, mjd, ptime, pos[3], vel[3];
+ int i, dn_flag, dn_flag_last;
+
+ dn_flag_last = 1; /* Assume that we begin in night. */
+
+ for (i=1; i>-8000; i--) {
+ ptime = (double) i;
+ mjd = mjd_start + ptime/86400.0;
+
+ /* Get the state vector at time mjd */
+ SGP4_getStateVector(sgp4, mjd, pos, vel);
+ SGP4_precess(pos, mjd, MJD2000);
+
+ dn_flag = eclipse(pos, mjd, &ang_sep); /* 0=day, 1=night */
+
+ /* We're going backward in time, so sunrise is day into night. */
+ if ((dn_flag == 1) && (dn_flag_last == 0)) break;
+
+ dn_flag_last=dn_flag;
+ }
+ /* Historically, we've defined sunrise as the time increment after
+ the change, so add 1 to the number we've just calculated. */
+ return (ptime + 1.);
+}
+
+
+static double
+sunset(double mjd_start, SGP4 sgp4)
+{
+ double ang_sep, mjd, ptime, pos[3], vel[3];
+ int i, dn_flag, dn_flag_last;
+
+ dn_flag_last = 0; /* Assume that we begin in day. */
+
+ for (i=1; i>-8000; i--) {
+ ptime = (double) i;
+ mjd = mjd_start + ptime/86400.0;
+
+ /* Get the state vector at time mjd */
+ SGP4_getStateVector(sgp4, mjd, pos, vel);
+ SGP4_precess(pos, mjd, MJD2000);
+
+ dn_flag = eclipse(pos, mjd, &ang_sep); /* 0=day, 1=night */
+
+ /* We're going backward in time, so sunset is night into day. */
+ if ((dn_flag == 0) && (dn_flag_last == 1)) break;
+
+ dn_flag_last = dn_flag;
+ }
+ /* Historically, we've defined sunset as the time increment after
+ the change, so add 1 to the number we've just calculated. */
+ return (ptime + 1.);
+}
+
+/****************************************************************************/
+
+static void
+fill_cntrate_array(long nsam_hk, long *hk_colval, double *time_hk,
+ long ntime, double *ttime, float *tarray)
+{
+ /*
+ * Procedure to fill an array with count rate housekeeping (HK)
+ * information, which is tabulated on an approx 16s time interval.
+ *
+ * nsam_hk = number of time samples in the housekeeping file
+ * hk_colval = array containing the housekeeping data
+ * time_hk = time from the exposure start for each second in the
+ * houskeeping file
+ * ntime = number of tabulated rows in the timeline array
+ * ttime = tabulated times in the timeline array
+ * tarray = timeline array to be filled, one sample per second
+ * starting at the start of the exposure
+ */
+
+ double hk_time, hk_time0;
+ long hk_cnt, hk_cnt0;
+ long i; /* index through timeline table */
+ long k; /* index through housekeeping array */
+ float cnt_rate = 0.;
+
+ i = k = 0; /* Initialize indices */
+
+ /*
+ * Find the first non-negative value in the HK array
+ */
+ while(k < nsam_hk && hk_colval[k] < 0) k++;
+ hk_cnt0 = hk_colval[k];
+ hk_time0 = time_hk[k];
+
+ /*
+ * Begin big loop through the timeline table
+ */
+ while (i < ntime) {
+
+ /* Find the next non-negative value in the HK array. */
+ k++;
+ while(k < nsam_hk && hk_colval[k] < 0) k++;
+
+ /* If we've run out of HK entries,
+ fill in the rest of the timeline and quit. */
+ if (k == nsam_hk) {
+ for ( ; i < ntime; i++)
+ tarray[i] = cnt_rate;
+ break;
+ }
+
+ /* Otherwise, calculate the count rate between
+ times hk_time0 and hk_time. */
+ hk_cnt = hk_colval[k];
+ hk_time = time_hk[k];
+ if (hk_cnt < hk_cnt0)
+ hk_cnt0 -= 16777216;
+ cnt_rate = (hk_cnt - hk_cnt0) / (hk_time - hk_time0);
+
+ /* Populate the timeline table for times less than hk_time */
+ while (i < ntime && cf_nlong(ttime[i]) < hk_time)
+ tarray[i++] = cnt_rate;
+
+ /* Save latest HK time and count values. */
+ hk_cnt0 = hk_cnt;
+ hk_time0 = hk_time;
+ }
+}
+
+
+/****************************************************************************/
+
+static void
+hv_from_header(fitsfile *outfits, char *det, char *side, long ntime, short *hv)
+{
+
+ char hv_key[FLEN_CARD];
+ int det_hv, hv_flag, status=0;
+ long i;
+
+ fits_read_key(outfits, TINT, "HV_FLAG", &hv_flag, NULL, &status);
+ if (hv_flag == -1) {
+ det_hv = -999;
+ cf_verbose(1, "Bad HV keywords: setting HV to -999 in timeline table.");
+ }
+ else {
+ sprintf(hv_key, "DET%.1sHV%.1sH", det, side);
+ FITS_read_key(outfits, TINT, hv_key, &det_hv, NULL, &status);
+ }
+ if (hv_flag == 0)
+ cf_verbose(1, "Applying max voltage value to whole exposure.");
+
+ cf_verbose(2, "High voltage = %d", det_hv);
+ for (i=0; i<ntime; i++)
+ hv[i] = det_hv;
+}
+
+
+/****************************************************************************/
+
+int
+hv_from_hskp(long nsam_hk, long *hk_colval, double *time_hk,
+ long ntime, double *ttime, short *tarray)
+{
+ /*
+ * Procedure to fill an array with high voltage housekeeping (HK)
+ * information, which is tabulated on an approx 16s time interval.
+ *
+ * nsam_hk = number of time samples in the housekeeping file
+ * hk_colval = array containing the housekeeping data
+ * time_hk = time from the exposure start for each second in the
+ * housekeeping file
+ * ntime = number of tabulated rows in the timeline array
+ * ttime = tabulated times in the timeline array
+ * tarray = timeline array to be filled, one sample per second
+ * starting at the start of the exposure
+ */
+
+ long i; /* index through timeline table */
+ long k; /* index through housekeeping array */
+ short hv = 0;
+
+ i = k = 0; /* Initialize indices */
+
+ /*
+ * Scan the input HK array. If it consists of a zero followed by
+ * all -1's, exit with an error.
+ */
+
+ if (hk_colval[0] == 0) {
+ for (i = 1; i < nsam_hk; i++) if (hk_colval[i] != -1) break;
+ if (i == nsam_hk) return (-1);
+ }
+
+ /*
+ * Find the first non-negative value in the HK array
+ */
+
+ while(k < nsam_hk && hk_colval[k] < 0) k++;
+ hv = hk_colval[k];
+
+ /*
+ * Begin big loop through the timeline table
+ */
+
+ while (i < ntime) {
+
+ /* Find the next non-negative value in the HK array */
+ k++;
+ while(k < nsam_hk && hk_colval[k] < 0) k++;
+
+ /* If we've run out of HK entries, fill in the rest of the
+ timeline and quit. */
+ if (k == nsam_hk) {
+ for ( ; i < ntime; i++)
+ tarray[i] = hv;
+ break;
+ }
+
+ /* Otherwise, populate the timeline table for times less than hk_time */
+ while (i < ntime && ttime[i] < time_hk[k])
+ tarray[i++] = hv;
+
+ /* Save latest HV value from HK array */
+ hv = hk_colval[k];
+ }
+
+ return (0);
+}
+
+
+/****************************************************************************/
+
+int cf_timeline(fitsfile *outfits)
+{
+ /*************************************************************
+ *
+ * Procedure to fill in the third extension of the IDF,
+ * which contains information about orbital parameters,
+ * count rates, high voltage info, and Y centroids as
+ * a function of time during the observation. The data
+ * are tabulated once each second.
+ *
+ **************************************************************/
+
+ char hkexists[FLEN_VALUE], det[FLEN_VALUE], side[2];
+ char fmt_byte[FLEN_CARD], fmt_float[FLEN_CARD], fmt_short[FLEN_CARD];
+ char instmode[FLEN_VALUE], keyword[FLEN_KEYWORD], card[FLEN_CARD];
+ char volt_cal[FLEN_VALUE];
+ unsigned char *tflag;
+ int hv_flag, ngti, n_bad_times, TIMES_TOO_BIG = FALSE;
+ int status=0, anynull, intnull=0, dn_flag_last=0;
+ int biglif = FALSE, bigsic = FALSE, bigfec = FALSE, bigaic = FALSE;
+ long i, j, k, expnight, nevents, ntime, vmax;
+ short *hv;
+ float *lifcr, *siccr, *feccr, *aiccr, *bkgdcr;
+ float first_time, last_time, bad_times[1024], *time=NULL;
+ float *ycentl, *ycents;
+ float max_lif, max_sic, max_fec, max_aic;
+ double *gtis, *gtie, min_limb=999., period;
+ double mjd_start, mjd_end, mjd, *ptime, *lon, *lat, *limbang;
+ double *vorb, *tsunrise, lastsunrise=0, *tsunset, lastsunset=0;
+ orbital orb;
+ SGP4 sgp4;
+ struct key tscal[16], tzero[16];
+ long nevts;
+
+ char * dets[]={"1A","1B","2A","2B"};
+
+ char extname[]="TIMELINE";
+ int tfields=16; /* output table will have 16 columns */
+ char *ttype[]={"TIME", "STATUS_FLAGS", "TIME_SUNRISE", "TIME_SUNSET",
+ "LIMB_ANGLE", "LONGITUDE", "LATITUDE", "ORBITAL_VEL",
+ "HIGH_VOLTAGE", "LIF_CNT_RATE", "SIC_CNT_RATE",
+ "FEC_CNT_RATE", "AIC_CNT_RATE", "BKGD_CNT_RATE",
+ "YCENT_LIF","YCENT_SIC"};
+
+ char *tform[16]; /* we will define tform later, when the number
+ of photons is known */
+
+ char *tunit[]={"seconds", "unitless", "seconds", "seconds", "degrees",
+ "degrees", "degrees", "km/s", "unitless", "counts/sec",
+ "counts/sec", "counts/sec", "counts/sec", "counts/sec",
+ "pixels","pixels" };
+
+ char CF_PRGM_ID[] = "cf_timeline";
+ char CF_VER_NUM[] = "1.41";
+
+ /* Initialize error checking. */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Started execution.");
+
+ /* Set the orbital parameters keywords. */
+ FITS_movabs_hdu(outfits, 1, NULL, &status);
+ FITS_read_key(outfits, TDOUBLE, "EXPSTART", &mjd_start, NULL, &status);
+ FITS_read_key(outfits, TDOUBLE, "EXPEND", &mjd_end, NULL, &status);
+ FITS_read_key(outfits, TDOUBLE, "RA_TARG", &orb.ra_ap, NULL, &status);
+ FITS_read_key(outfits, TDOUBLE, "DEC_TARG", &orb.dec_ap, NULL, &status);
+ FITS_read_key(outfits, TSTRING, "INSTMODE", instmode, NULL, &status);
+ FITS_read_key(outfits, TSTRING, "VOLT_CAL", volt_cal, NULL, &status);
+
+ cf_velang(outfits, (mjd_end+mjd_start)/2.0);
+
+ sgp4 = set_orbit_parms(outfits);
+
+ /* Read the good time intervals */
+ ngti = cf_get_gti(outfits, &gtis, &gtie);
+ for (i=0; i<ngti; i++)
+ cf_verbose(3, "GTI start = %0.1f, end= %0.1f", gtis[i], gtie[i]);
+
+ /*
+ * An exposure should never span more than 55 ks. If it appears to
+ * do so, include only the good-time intervals in the timeline table.
+ * Otherwise, include all times between EXPSTART and EXPEND, whether
+ * or not there are any associated photons.
+ */
+ if (gtie[ngti-1] - gtis[0] > MAX_EXPTIME) {
+ TIMES_TOO_BIG = TRUE;
+ cf_if_warning("Exposure appears to span %0.0f ks. Truncating "
+ "timeline table.", (gtie[ngti-1] - gtis[0])/1000.);
+ }
+
+ /* For HIST data, use the good-time intervals
+ to set the times in the timeline table. */
+ if (!strncmp(instmode, "HIST", 4)) {
+ if (TIMES_TOO_BIG) {
+ ntime = 0;
+ for (i = 0; i < ngti; i++)
+ ntime += (long) (gtie[i] - gtis[i] + 1.5);
+ ptime = (double *) cf_malloc(sizeof(double) * ntime);
+ k = 0;
+ for (i = 0; i < ngti; i++) {
+ ntime = (long) (gtie[i] - gtis[i] + 1.5);
+ for (j = 0; j < ntime; j++)
+ ptime[k++] = gtis[i] + j;
+ }
+ ntime = k;
+ }
+ else {
+ ntime = (long) (gtie[ngti-1] - gtis[0] + 1.5);
+ ptime = (double *) cf_malloc(sizeof(double) * ntime);
+ for (i = 0; i < ntime; i++)
+ ptime[i] = gtis[0] + i;
+ }
+ }
+
+ /* For TTAG data, use the photon times themselves. */
+ else {
+ FITS_movabs_hdu(outfits, 2, NULL, &status);
+ nevents = cf_read_col(outfits, TFLOAT, "TIME", (void **) &time);
+ FITS_movabs_hdu(outfits, 1, NULL, &status);
+ n_bad_times = 0;
+ if (TIMES_TOO_BIG) {
+ last_time = 0;
+ i = nevents - 1;
+ while (time[i] > MAX_EXPTIME && i > 0) {
+ if (fabs(time[i] - last_time) > FRAME_TOLERANCE && n_bad_times < 1024) {
+ bad_times[n_bad_times++] = time[i];
+ last_time = time[i];
+ }
+ i--;
+ }
+ last_time = time[i] + 1.;
+ }
+ else
+ last_time = time[nevents-1] + 1.;
+ first_time = time[0];
+ if (first_time < fmod(last_time, 1.))
+ first_time = fmod(last_time, 1.) - 1.;
+ ntime = (long) (last_time - first_time + 1.5) + n_bad_times;
+ ptime = (double *) cf_malloc(sizeof(double) * ntime);
+ for (i = 0; i < ntime - n_bad_times; i++)
+ ptime[i] = first_time + i;
+ for (j = n_bad_times-1; i < ntime; i++, j--)
+ ptime[i] = bad_times[j];
+ free(time);
+ }
+ cf_verbose(2, "Timeline table will contain %ld entries", ntime);
+
+ /* Now generate all of the other arrays in the timeline table. */
+
+ lon = (double *) cf_calloc(ntime, sizeof(double));
+ lat = (double *) cf_calloc(ntime, sizeof(double));
+ limbang = (double *) cf_calloc(ntime, sizeof(double));
+ vorb = (double *) cf_calloc(ntime, sizeof(double));
+ tsunrise = (double *) cf_calloc(ntime, sizeof(double));
+ tsunset = (double *) cf_calloc(ntime, sizeof(double));
+ hv = (short *) cf_calloc(ntime, sizeof(short));
+ lifcr = (float *) cf_calloc(ntime, sizeof(float));
+ siccr = (float *) cf_calloc(ntime, sizeof(float));
+ feccr = (float *) cf_calloc(ntime, sizeof(float));
+ aiccr = (float *) cf_calloc(ntime, sizeof(float));
+ bkgdcr = (float *) cf_calloc(ntime, sizeof(float));
+ tflag = (unsigned char *) cf_calloc(ntime, sizeof(unsigned char));
+ ycentl = (float *)cf_calloc(ntime, sizeof(float));
+ ycents = (float *)cf_calloc(ntime, sizeof(float));
+
+ for (i=0; i<ntime; i++) {
+ int dn_flag, day_limb;
+ float dx;
+ double geo_lon, geo_lat, gmst, pos[3], vel[3], zdist, ang_sep;
+
+ mjd = mjd_start + ptime[i]/86400.0;
+
+ /* get the state vector at time mjd */
+ SGP4_getStateVector(sgp4, mjd, pos, vel);
+ SGP4_precess(pos, mjd, MJD2000);
+ SGP4_precess(vel, mjd, MJD2000);
+
+ dx = space_vel(vel, orb.ra_ap, orb.dec_ap);
+
+ state_geod(pos, mjd, &geo_lon, &geo_lat, &gmst);
+ limbang[i] = (double) state_limb(pos, mjd, orb.ra_ap, orb.dec_ap,
+ &zdist, &day_limb);
+ if (min_limb > limbang[i]) min_limb = limbang[i];
+
+ dn_flag = eclipse(pos, mjd, &ang_sep); /* 0=day, 1=night */
+
+ /* The first time through, and after any breaks in the timeline,
+ * compute times of most recent sunrise and sunset. */
+ if ((i == 0) || (ptime[i] - ptime[i-1] - 1. > FRAME_TOLERANCE)) {
+ lastsunrise = ptime[i] + sunrise(mjd, sgp4);
+ lastsunset = ptime[i] + sunset(mjd, sgp4);
+ if (lastsunset > lastsunrise) dn_flag_last = 1 ;
+ else dn_flag_last = 0 ;
+ /* The first time through, compute the orbital period and
+ * write it to file header. */
+ if (i == 0) {
+ mjd += (lastsunset - ptime[i] + 7000.)/86400.0;
+ period = 7000. + sunset(mjd, sgp4);
+ FITS_movabs_hdu(outfits, 1, NULL, &status);
+ FITS_update_key(outfits, TDOUBLE, "ORBPERID", &period,
+ "[s] Estimate of orbital period", &status);
+ }
+ }
+ /* If we've moved from day into night (or night into day), update
+ last sunrise/sunset times. */
+ else if ((dn_flag == 0) && (dn_flag_last == 1))
+ lastsunrise = ptime[i];
+ else if ((dn_flag == 1) && (dn_flag_last == 0))
+ lastsunset = ptime[i];
+
+ /* If this is daytime, set the day bit in TFLAG array. */
+ if (dn_flag == 0)
+ tflag[i] |= TEMPORAL_DAY;
+
+ lon[i] = geo_lon;
+ lat[i] = geo_lat;
+ vorb[i] = dx;
+ tsunrise[i] = ptime[i] - lastsunrise;
+ tsunset[i] = ptime[i] - lastsunset;
+ dn_flag_last = dn_flag;
+ }
+
+
+ /*
+ * Set the OPUS flag for times outside of the OPUS good-time
+ * intervals. Since there are no photons associated with the
+ * last second of a GTI, we make sure that it is flagged as
+ * bad. The first second of a good-time interval is good.
+ */
+ j = 0;
+ for (i = 0; i < ntime; i++) {
+ while (j < ngti-1 && ptime[i] > gtie[j] - 0.5)
+ j++;
+ if (ptime[i] < gtis[j] - FRAME_TOLERANCE ||
+ ptime[i] > gtie[j] + FRAME_TOLERANCE)
+ tflag[i] |= TEMPORAL_OPUS;
+ }
+ tflag[ntime-1] |= TEMPORAL_OPUS;
+
+ /*
+ * No exposure should be longer than 55 ks. For HIST data, we'll just
+ * put up with it, but for TTAG data, we'll flag as bad any timeline
+ * table entries with absurd arrival times. We use the OPUS flag for
+ * this. What we're really saying is that we don't believe that the
+ * times associated with these photons are correct.
+ */
+ for (i = 0; i < ntime; i++)
+ if (ptime[i] > MAX_EXPTIME)
+ tflag[i] |= TEMPORAL_OPUS;
+
+ /* Fill housekeeping columns (count rates and high voltage). */
+ FITS_movabs_hdu(outfits, 1, NULL, &status);
+ FITS_read_key(outfits, TSTRING, "HKEXISTS", hkexists, NULL, &status);
+ FITS_read_key(outfits, TSTRING, "DETECTOR", det, NULL, &status);
+ strncpy(side, det+1, 1);
+
+ /* Populate MIN_LIMB keyword. */
+ FITS_update_key(outfits, TDOUBLE, "MIN_LIMB", &min_limb, NULL, &status);
+
+ /* If no housekeeping file exists, use header info to fill timeline. */
+ loop:
+ if(!strncasecmp(hkexists, "N", 1)) {
+ char cntb_key[FLEN_CARD], cnte_key[FLEN_CARD];
+ float cntb, cnte;
+ float cnt_rate, cnt_rate2, eng_time, exp_time;
+ double ctimeb, ctimee;
+
+ cf_verbose(1, "No housekeeping file: filling timeline with header info.");
+
+ /* move to the top level header to read the relevant keywords */
+ FITS_movabs_hdu(outfits, 1, NULL, &status);
+ FITS_read_key(outfits, TFLOAT, "EXPTIME", &exp_time, NULL, &status);
+ FITS_read_key(outfits, TLONG, "NEVENTS", &nevts, NULL, &status);
+
+ /* calculate engineering count rates */
+ FITS_read_key(outfits, TDOUBLE, "CTIME_B", &ctimeb, NULL, &status);
+ FITS_read_key(outfits, TDOUBLE, "CTIME_E", &ctimee, NULL, &status);
+ eng_time = (ctimee-ctimeb) * 86400.;
+
+ /* If eng_time is unreasonable, use the EXPTIME keyword */
+ if (eng_time < 1) {
+ cf_if_warning("Engineering snapshot time less than or equal to zero.");
+ if ((eng_time = (mjd_end-mjd_start) * 86400.) < MAX_EXPTIME)
+ cf_if_warning("Estimating LiF, SiC, FEC and AIC count rates from EXPSTART and EXPEND.");
+ else {
+ eng_time = exp_time;
+ cf_if_warning("Estimating LiF, SiC, FEC and AIC count rates from EXPTIME.");
+ }
+ }
+
+ /* SiC count rate timeline */
+ sprintf(cntb_key, "C%.2sSIC_B", det);
+ FITS_read_key(outfits, TFLOAT, cntb_key, &cntb, NULL, &status);
+ sprintf(cnte_key, "C%.2sSIC_E", det);
+ FITS_read_key(outfits, TFLOAT, cnte_key, &cnte, NULL, &status);
+
+ if ((cnte<0) || (cntb<0) || (cntb>cnte) || eng_time < 1. ||
+ (cnte==0xEB90EB90) || (cnte==0xDEADDEAD) ||
+ (cntb==0xEB90EB90) || (cntb==0xDEADDEAD) ||
+ ((cnt_rate = (cnte - cntb) / eng_time) > 32000)) {
+ cf_if_warning("Bad SiC counter. SiC count rate will be set to zero.");
+ cnt_rate=0;
+ }
+
+ cf_verbose(2, "SiC count rate = %d", (int) cnt_rate);
+ for (i=0; i<ntime; i++)
+ siccr[i] = cnt_rate;
+
+ /* LiF count rate timeline */
+ sprintf(cntb_key, "C%.2sLIF_B", det);
+ FITS_read_key(outfits, TFLOAT, cntb_key, &cntb, NULL, &status);
+ sprintf(cnte_key, "C%.2sLIF_E", det);
+ FITS_read_key(outfits, TFLOAT, cnte_key, &cnte, NULL, &status);
+
+ if ((cnte<0) || (cntb<0) || (cntb>cnte) || eng_time < 1. ||
+ (cnte==0xEB90EB90) || (cnte==0xDEADDEAD) ||
+ (cntb==0xEB90EB90) || (cntb==0xDEADDEAD) ||
+ ((cnt_rate = (cnte - cntb) / eng_time) > 32000)) {
+ cf_if_warning("Bad LiF counter. LiF count rate will be set to zero.");
+ cnt_rate=0;
+ }
+
+ cf_verbose(2, "LiF count rate = %d", (int) cnt_rate);
+ for (i=0; i<ntime; i++)
+ lifcr[i] = cnt_rate;
+
+ /* FEC count rate timeline */
+ sprintf(cntb_key, "C%.2sFE_B", det);
+ FITS_read_key(outfits, TFLOAT, cntb_key, &cntb, NULL, &status);
+ sprintf(cnte_key, "C%.2sFE_E", det);
+ FITS_read_key(outfits, TFLOAT, cnte_key, &cnte, NULL, &status);
+
+ if ((cnte<0) || (cntb<0) || (cntb>cnte) || eng_time<1 ||
+ (cnte==0xEB90EB90) || (cnte==0xDEADDEAD) ||
+ (cntb==0xEB90EB90) || (cntb==0xDEADDEAD) ||
+ ((cnt_rate = (cnte - cntb) / eng_time) > 64000) ||
+ (cnt_rate < 0.8*nevts/exp_time)) {
+ cf_if_warning("Bad FEC counter");
+ cf_if_warning("-- FEC count rate will be computed from NEVENTS and EXPTIME.");
+ cf_if_warning("-- Electronic deadtime correction will be underestimated.");
+ cf_if_warning("-- Y stretch will be underestimated.");
+ cnt_rate=nevts/exp_time;
+ }
+
+ cf_verbose(2, "FEC count rate = %d", (int) cnt_rate);
+ for (i=0; i<ntime; i++)
+ feccr[i] = cnt_rate;
+
+ /* AIC count rate timeline */
+ cnt_rate=0;
+ for (i=0;i<4;i++){
+ sprintf(cntb_key, "C%.2sAI_B", dets[i]);
+ FITS_read_key(outfits, TFLOAT, cntb_key, &cntb, NULL, &status);
+ sprintf(cnte_key, "C%.2sAI_E", dets[i]);
+ FITS_read_key(outfits, TFLOAT, cnte_key, &cnte, NULL, &status);
+
+ if (((cnte<0) || (cntb<0) || (cntb>cnte) || (eng_time<1) ||
+ (cnte==0xEB90EB90) || (cnte==0xDEADDEAD) ||
+ (cntb==0xEB90EB90) || (cntb==0xDEADDEAD) ||
+ ((cnt_rate2 = (cnte - cntb)/ eng_time) > 64000)) ||
+ ((!(strncasecmp(dets[i],det,2))) && (cnt_rate2<0.8*nevts/exp_time))) {
+ cf_if_warning("Bad AIC counter %s",dets[i]);
+ cf_if_warning("-- AIC count rate will be computed from NEVENTS and EXPTIME.");
+ cf_if_warning("-- IDS deadtime correction will be underestimated.");
+ cnt_rate=nevts/exp_time;
+ break;
+ }
+ cnt_rate+=cnt_rate2;
+ }
+
+ cf_verbose(2, "AIC count rate = %d", (int) cnt_rate);
+ for (i=0; i<ntime; i++)
+ aiccr[i] = cnt_rate;
+
+ /* High voltage timeline */
+ hv_from_header(outfits, det, side, ntime, hv);
+ }
+ else {
+ /*
+ * Housekeeping file exists. Use it to calculate timeline values.
+ */
+ char cntb_key[FLEN_VALUE], hk_file_name[FLEN_VALUE];
+ int hk_colnum, hskpvers, MUST_TEST_HV=FALSE, swap_HV=FALSE;
+ long nsam_hk, *hk_colval;
+ double *hk_mjd, *time_hk;
+ fitsfile *hskpfits;
+ float * aiccr2;
+
+ FITS_read_key(outfits, TSTRING, "HSKP_CAL", hk_file_name, NULL, &status);
+ /* If you can't open the housekeeping file, try a lower-case filename.*/
+ if (fits_open_file(&hskpfits, hk_file_name, READONLY, &status)) {
+ status = 0;
+ cf_verbose(3, "Can't find housekeeping file %s", hk_file_name);
+ hk_file_name[0] = (char) tolower(hk_file_name[0]);
+ cf_verbose(3, "Will look for file named %s", hk_file_name);
+
+ /* If you still can't open the housekeeping file, use header keywords. */
+ if (fits_open_file(&hskpfits, hk_file_name, READONLY, &status)) {
+ status = 0;
+ cf_verbose(3, "Can't find housekeeping file %s", hk_file_name);
+ strncpy(hkexists, "N", 1);
+ goto loop;
+ }
+ /* If lower-case filename works, write it to the file header. */
+ FITS_update_key(outfits, TSTRING, "HSKP_CAL", hk_file_name, NULL, &status);
+ }
+ cf_verbose(1, "Housekeeping file exists: using it to fill timeline.");
+
+ /* If we're on side 2 and the HSKPVERS keyword does not exist, then we'll
+ need to check whether the HV arrays are swapped. */
+ if (*det == '2' &&
+ fits_read_key(hskpfits, TINT, "HSKPVERS", &hskpvers, NULL, &status)) {
+ status = 0;
+ MUST_TEST_HV = TRUE;
+ }
+
+ FITS_movabs_hdu(hskpfits, 2, NULL, &status);
+ FITS_read_key(hskpfits, TLONG, "NAXIS2", &nsam_hk, NULL, &status);
+ cf_verbose(3, "Number of samples in the HK file = %ld", nsam_hk);
+
+ /* Allocate space to hold the column contents */
+ hk_colval = (long *) cf_calloc(nsam_hk, sizeof(long));
+ hk_mjd = (double *) cf_calloc(nsam_hk, sizeof(double));
+ time_hk = (double *) cf_calloc(nsam_hk, sizeof(double));
+
+ /* Read in the various columns */
+
+ /* First the MJD values - convert to time from start in seconds */
+ FITS_get_colnum(hskpfits, TRUE, "MJD", &hk_colnum, &status);
+ FITS_read_col(hskpfits, TDOUBLE, hk_colnum, 1L, 1L, nsam_hk, &intnull,
+ hk_mjd, &anynull, &status);
+ for (i=0; i<nsam_hk; i++)
+ time_hk[i] = (hk_mjd[i]-mjd_start) * 86400.;
+
+ /* SiC count rate timeline */
+ sprintf(cntb_key, "I_DET%.1sCSIC%.1s", det, side);
+
+ /* Check to see whether the column exists. If not, then
+ reset the housekeeping flag and do the analysis using
+ keyword values in the input file header. If so, then check
+ whether it contains real values. If not, then use the
+ keywords */
+ if(!fits_get_colnum(hskpfits, TRUE, cntb_key, &hk_colnum, &status)) {
+ FITS_read_col(hskpfits, TLONG, hk_colnum, 1L, 1L, nsam_hk, &intnull,
+ hk_colval, &anynull, &status);
+ vmax = 0;
+ for (j=0; j< nsam_hk; j++)
+ if (hk_colval[j] > vmax)
+ vmax = hk_colval[j];
+ if (vmax <= 0) {
+ cf_if_warning("Data missing from housekeeping column. "
+ "Will treat file as missing.");
+ strncpy(hkexists, "N", 1);
+ status=0;
+ goto loop; }
+ else fill_cntrate_array(nsam_hk, hk_colval, time_hk, ntime,
+ ptime, siccr);}
+ else {
+ cf_if_warning("Data column missing from housekeeping file. "
+ "Will treat file as missing.");
+ strncpy(hkexists, "N", 1);
+ status=0;
+ goto loop;
+ }
+ cf_verbose(3,"SiC count-rate info read from housekeeping file.");
+
+ /* LiF count rate timeline */
+ sprintf(cntb_key, "I_DET%.1sCLIF%.1s", det, side);
+ FITS_get_colnum(hskpfits, TRUE, cntb_key, &hk_colnum, &status);
+ FITS_read_col(hskpfits, TLONG, hk_colnum, 1L, 1L, nsam_hk, &intnull,
+ hk_colval, &anynull, &status);
+ fill_cntrate_array(nsam_hk, hk_colval, time_hk, ntime, ptime, lifcr);
+ cf_verbose(3,"LiF count-rate info read from housekeeping file.");
+
+ /* FEC count rate timeline */
+ sprintf(cntb_key, "I_DET%.1sCFE%.1s", det, side);
+ FITS_get_colnum(hskpfits, TRUE, cntb_key, &hk_colnum, &status);
+ FITS_read_col(hskpfits, TLONG, hk_colnum, 1L, 1L, nsam_hk, &intnull,
+ hk_colval, &anynull, &status);
+ fill_cntrate_array(nsam_hk, hk_colval, time_hk, ntime, ptime, feccr);
+ cf_verbose(3,"FEC count-rate info read from housekeeping file.");
+
+ /* AIC count rate timeline */
+ aiccr2 = (float *) cf_calloc(ntime, sizeof(float));
+ for (j=0;j<ntime;j++) aiccr[j]=0;
+ for (i=0;i<4;i++){
+ sprintf(cntb_key, "I_DET%.1sCAI%.1s", dets[i], dets[i]+1);
+ FITS_get_colnum(hskpfits, TRUE, cntb_key, &hk_colnum, &status);
+ FITS_read_col(hskpfits, TLONG, hk_colnum, 1L, 1L, nsam_hk, &intnull,
+ hk_colval, &anynull, &status);
+ fill_cntrate_array(nsam_hk, hk_colval, time_hk, ntime, ptime, aiccr2);
+ for (j=0;j<ntime;j++) aiccr[j]+=aiccr2[j];
+ }
+ cf_verbose(3,"AIC count-rate info read from housekeeping file.");
+
+ /* If MUST_TEST_HV = TRUE, determine whether HV for 2A and 2B are swapped. */
+ if (MUST_TEST_HV) {
+ char mjd_str[FLEN_VALUE], full_str[FLEN_VALUE], saa_str[FLEN_VALUE];
+ double mjd_volt;
+ int full, hdr_max2a, max2a = 0, saa;
+ long n, nhk, *hv2a;
+ fitsfile *voltfits;
+
+ cf_verbose(3, "Testing whether HV arrays are swapped.");
+
+ /* Read HV array for 2A from HSKP file; find max value. */
+ nhk = cf_read_col(hskpfits, TLONG, "I_DET2HVBIASAST", (void **) &hv2a);
+ for (n = 0L; n < nhk; n++) if (max2a < hv2a[n]) max2a = hv2a[n];
+ free (hv2a);
+
+ /*
+ * If HV values in file header are good, read 2A max.
+ * Compare with value from HSKP file.
+ */
+ fits_read_key(outfits, TINT, "HV_FLAG", &hv_flag, NULL, &status);
+ if (hv_flag > -1) {
+ FITS_read_key(outfits, TINT, "DET2HVAH", &hdr_max2a, NULL, &status);
+ cf_verbose(3, " Max for 2A: header says %d, housekeeping file says %d.",
+ hdr_max2a, max2a);
+ if (abs(max2a - hdr_max2a) < 5)
+ cf_verbose(3, " HV arrays are OK. No need to swap.");
+ else
+ swap_HV = TRUE;
+ }
+
+ /*
+ * If HV values in file header are bad, compare with expected
+ * values from VOLT_CAL file.
+ */
+ else {
+ FITS_open_file(&voltfits, cf_cal_file(volt_cal), READONLY, &status);
+ n = 0L;
+ do {
+ n++;
+ sprintf(mjd_str, "MJD%ld", n);
+ FITS_read_key(voltfits, TDOUBLE, mjd_str, &mjd_volt, NULL, &status);
+ } while (mjd_start > mjd_volt);
+ n--;
+
+ sprintf(full_str, "FULL%ld", n);
+ sprintf(saa_str, "SAA%ld", n);
+ FITS_read_key(voltfits, TINT, full_str, &full, NULL, &status);
+ FITS_read_key(voltfits, TINT, saa_str, &saa, NULL, &status);
+ FITS_close_file(voltfits, &status);
+
+ /* If max for 2A matches expected full or SAA voltage -- and
+ we're on side 2A -- then we're OK. Otherwise, must swap. */
+ cf_verbose(3, " 2A max = %d; Expected values for %s: full = %d, SAA = %d",
+ max2a, det, full, saa);
+ if ((abs(max2a - full) < 10 || abs(max2a - saa) < 5) &&
+ (*side == 'A'))
+ cf_verbose(3, " HV arrays are OK. No need to swap.");
+ else swap_HV = TRUE;
+ }
+ }
+
+ /* High voltage timeline */
+ if (swap_HV) {
+ if (*side == 'A') sprintf(cntb_key, "I_DET2HVBIASBST");
+ else sprintf(cntb_key, "I_DET2HVBIASAST");
+ cf_verbose(3, " Swapping HV values for detectors 2A and 2B");
+ }
+ else sprintf(cntb_key, "I_DET%.1sHVBIAS%.1sST", det, side);
+ FITS_get_colnum(hskpfits, TRUE, cntb_key, &hk_colnum, &status);
+ FITS_read_col(hskpfits, TLONG, hk_colnum, 1L, 1L, nsam_hk, &intnull,
+ hk_colval, &anynull, &status);
+ if (!hv_from_hskp(nsam_hk, hk_colval, time_hk, ntime, ptime, hv))
+ cf_verbose(3,"HV info read from housekeeping file.");
+ else {
+ cf_verbose(1,"Housekeeping file corrupted: reading HV info from file header.");
+ hv_from_header(outfits, det, side, ntime, hv);
+ }
+
+ free(time_hk);
+ free(hk_mjd);
+ free(hk_colval);
+ FITS_close_file(hskpfits, &status);
+ }
+
+ /*
+ * Set TFORM, TSCALE, and TZERO values for output table.
+ */
+ sprintf(fmt_byte, "%ldB", ntime);
+ sprintf(fmt_float, "%ldE", ntime);
+ sprintf(fmt_short, "%ldI", ntime);
+
+ /* First set default values. */
+ tform[0] = fmt_float;
+ tform[1] = fmt_byte;
+ for (i=2; i<tfields; i++)
+ tform[i] = fmt_short;
+
+ /* For most arrays, we keep one decimal place. */
+ for (i=2; i<tfields; i++) {
+ sprintf(tscal[i].keyword, "TSCAL%ld", i+1);
+ tscal[i].value = 0.1;
+ sprintf(tzero[i].keyword, "TZERO%ld", i+1);
+ tzero[i].value = 0.;
+ }
+
+ /* Now we start tinkering. */
+
+ /* The orbital velocity is small, so we can keep two decimal places. */
+ tscal[7].value = 0.01;
+
+ /* FEC_CNT_RATE and AIC_CNT_RATE can get big, so we
+ use TZERO AND TSCALE to compress them. */
+ tscal[11].value = 1;
+ tscal[12].value = 2;
+ tzero[11].value = 32768;
+ tzero[12].value = 65536;
+
+ /* If a count rate is high, set to zero. */
+ max_lif = max_sic = 32767.;
+ max_fec = 65535.;
+ max_aic = 131070.;
+
+ for (i = 0; i < ntime; i++) {
+ if (lifcr[i] > max_lif) {
+ lifcr[i] = 0.;
+ biglif = TRUE;
+ }
+ if (siccr[i] > max_sic) {
+ siccr[i] = 0.;
+ bigsic = TRUE;
+ }
+ if (feccr[i] > max_fec) {
+ feccr[i] = 0.;
+ bigfec = TRUE;
+ }
+ if (aiccr[i] > max_aic) {
+ aiccr[i] = 0.;
+ bigaic = TRUE;
+ }
+ }
+
+ if (biglif) cf_if_warning("LIF_CNT_RATE out of bounds. Setting bad values to zero.");
+ if (bigsic) cf_if_warning("SIC_CNT_RATE out of bounds. Setting bad values to zero.");
+ if (bigfec) cf_if_warning("FEC_CNT_RATE out of bounds. Setting bad values to zero.");
+ if (bigaic) cf_if_warning("AIC_CNT_RATE out of bounds. Setting bad values to zero.");
+
+ /*
+ * Write the timeline table to the output file.
+ */
+ FITS_movabs_hdu(outfits, 3, NULL, &status);
+ FITS_create_hdu(outfits, &status);
+
+ FITS_create_tbl(outfits, BINARY_TBL, 1L, tfields, ttype,
+ tform, tunit, extname, &status);
+
+ /* Write TSCALE and TZERO entries to header */
+ for (i=2; i<tfields; i++) {
+
+ /* Omit TSCALE and TZERO for these six arrays. */
+ if (i == 2) continue; /* TIME_SUNRISE */
+ if (i == 3) continue; /* TIME_SUNSET */
+ if (i == 8) continue; /* HIGH_VOLTAGE */
+ if (i == 9) continue; /* LIF_CNT_RATE */
+ if (i == 10) continue; /* SIC_CNT_RATE */
+ if (i == 13) continue; /* BKGD_CNT_RATE */
+
+ sprintf(keyword, "TUNIT%ld", i+1);
+ if (fits_read_keyword(outfits, keyword, card, NULL, &status))
+ cf_if_fits_error(status);
+ FITS_insert_key_flt(outfits, tscal[i].keyword, tscal[i].value,
+ -1, NULL, &status);
+ FITS_insert_key_flt(outfits, tzero[i].keyword, tzero[i].value,
+ -5, NULL, &status);
+ }
+
+ FITS_write_col(outfits, TDOUBLE, 1, 1L, 1L, ntime, ptime, &status);
+ cf_verbose(3, "ptime array written to timeline table");
+ FITS_write_col(outfits, TBYTE, 2, 1L, 1L, ntime, tflag, &status);
+ cf_verbose(3, "tflag array written to timeline table");
+ FITS_write_col(outfits, TDOUBLE, 3, 1L, 1L, ntime, tsunrise, &status);
+ cf_verbose(3, "tsunrise array written to timeline table");
+ FITS_write_col(outfits, TDOUBLE, 4, 1L, 1L, ntime, tsunset, &status);
+ cf_verbose(3, "tsunset array written to timeline table");
+ FITS_write_col(outfits, TDOUBLE, 5, 1L, 1L, ntime, limbang, &status);
+ cf_verbose(3, "limbang array written to timeline table");
+ FITS_write_col(outfits, TDOUBLE, 6, 1L, 1L, ntime, lon, &status);
+ cf_verbose(3, "lon array written to timeline table");
+ FITS_write_col(outfits, TDOUBLE, 7, 1L, 1L, ntime, lat, &status);
+ cf_verbose(3, "lat array written to timeline table");
+ FITS_write_col(outfits, TDOUBLE, 8, 1L, 1L, ntime, vorb, &status);
+ cf_verbose(3, "vorb array written to timeline table");
+ FITS_write_col(outfits, TSHORT, 9, 1L, 1L, ntime, hv, &status);
+ cf_verbose(3, "hv array written to timeline table");
+ FITS_write_col(outfits, TFLOAT, 10, 1L, 1L, ntime, lifcr, &status);
+ cf_verbose(3, "lifcr array written to timeline table");
+ FITS_write_col(outfits, TFLOAT, 11, 1L, 1L, ntime, siccr, &status);
+ cf_verbose(3, "siccr array written to timeline table");
+ FITS_write_col(outfits, TFLOAT, 12, 1L, 1L, ntime, feccr, &status);
+ cf_verbose(3, "feccr array written to timeline table");
+ FITS_write_col(outfits, TFLOAT, 13, 1L, 1L, ntime, aiccr, &status);
+ cf_verbose(3, "aiccr array written to timeline table");
+ FITS_write_col(outfits, TFLOAT, 14, 1L, 1L, ntime, bkgdcr, &status);
+ cf_verbose(3, "bkgdcr array written to timeline table");
+ FITS_write_col(outfits, TFLOAT, 15, 1L, 1L, ntime, ycentl, &status);
+ cf_verbose(3, "ycentl array written to timeline table");
+ FITS_write_col(outfits, TFLOAT, 16, 1L, 1L, ntime, ycents, &status);
+ cf_verbose(3, "ycents array written to timeline table");
+
+ /* Compute EXPNIGHT for raw data file and write to output header. */
+ expnight = 0;
+ for (i=0; i<ntime; i++)
+ if (!tflag[i]) expnight++;
+ FITS_movabs_hdu(outfits, 1, NULL, &status);
+ FITS_update_key(outfits, TLONG, "EXPNIGHT", &expnight, NULL, &status);
+ cf_verbose(3, "For raw data, EXPNIGHT = %d", expnight);
+
+ free(ptime);
+ free(lon);
+ free(lat);
+ free(limbang);
+ free(vorb);
+ free(tsunrise);
+ free(tsunset);
+ free(hv);
+ free(lifcr);
+ free(siccr);
+ free(feccr);
+ free(aiccr);
+ free(bkgdcr);
+ free(tflag);
+ free(ycentl);
+ free(ycents);
+
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished execution.");
+ return EXIT_SUCCESS;
+}
+
+
+/* Add new header keywords - needed only for old versions of OPUS */
+
+int cf_add_header_keywords(fitsfile *outfits)
+{
+ char hkexists[FLEN_CARD];
+ int status=0;
+ float dum=0., rawtime;
+ short sdum=0;
+ long ldum=0;
+ short bkgd_num=0;
+
+ cf_verbose(3, "Entering cf_add_header_keywords");
+
+ /* If keyword RAWTIME does not exist, create with value of EXPTIME */
+ fits_read_key(outfits, TFLOAT, "RAWTIME", &rawtime, NULL, &status);
+ if (status) {
+ status = 0;
+ FITS_read_key(outfits, TFLOAT, "EXPTIME", &rawtime, NULL, &status);
+ FITS_update_key(outfits, TFLOAT, "RAWTIME", &rawtime,
+ "[s] Exposure duration of raw data file", &status);
+ }
+
+ /* If keyword HKEXISTS is missing, create with value of "NO" */
+ fits_read_key(outfits, TSTRING, "HKEXISTS", hkexists, NULL, &status);
+ if (status != 0) {
+ status=0;
+ cf_verbose(1, "HKEXISTS keyword missing: assume no housekeeping file.");
+ FITS_update_key(outfits, TSTRING, "HKEXISTS", "NO",
+ "Housekeeping data file exists", &status);
+ }
+
+ FITS_update_key(outfits, TSTRING, "BRIT_OBJ", "NO", "Not an over-bright observation", &status);
+ FITS_update_key(outfits, TSTRING, "HSKP_CAL", " ", "Housekeeping data file", &status);
+ FITS_update_key(outfits, TSTRING, "DAYNIGHT", "BOTH", "Use only DAY, NIGHT or BOTH", &status);
+ FITS_update_key(outfits, TLONG, "EXP_HV", &ldum, "[s] Integration time lost to low voltage", &status);
+ FITS_update_key(outfits, TLONG, "EXP_JITR", &ldum, "[s] Integration time lost to jitter", &status);
+ FITS_update_key(outfits, TLONG, "EXPNIGHT", &ldum, "[s] Integration time during night after screening", &status);
+ FITS_update_key(outfits, TFLOAT, "FPADXLIF", &dum, "[pixels] Correction for FPA position",&status);
+ FITS_update_key(outfits, TFLOAT, "FPADXSIC", &dum, "[pixels] Correction for FPA position",&status);
+ FITS_update_key(outfits, TFLOAT, "YCENT1", &dum, "[pixels] Y centroid of LIF HIRS aperture",&status);
+ FITS_update_key(outfits, TFLOAT, "YCENT2", &dum, "[pixels] Y centroid of LIF MDRS aperture",&status);
+ FITS_update_key(outfits, TFLOAT, "YCENT3", &dum, "[pixels] Y centroid of LIF LWRS aperture",&status);
+ FITS_update_key(outfits, TFLOAT, "YCENT5", &dum, "[pixels] Y centroid of SIC HIRS aperture",&status);
+ FITS_update_key(outfits, TFLOAT, "YCENT6", &dum, "[pixels] Y centroid of SIC MDRS aperture",&status);
+ FITS_update_key(outfits, TFLOAT, "YCENT7", &dum, "[pixels] Y centroid of SIC LWRS aperture",&status);
+ FITS_update_key(outfits, TSTRING, "YQUAL1", " ", "Quality of Y centroid value (LIF HIRS)",&status);
+ FITS_update_key(outfits, TSTRING, "YQUAL2", " ", "Quality of Y centroid value (LIF MDRS)",&status);
+ FITS_update_key(outfits, TSTRING, "YQUAL3", " ", "Quality of Y centroid value (LIF LWRS)",&status);
+ FITS_update_key(outfits, TSTRING, "YQUAL5", " ", "Quality of Y centroid value (SIC HIRS)",&status);
+ FITS_update_key(outfits, TSTRING, "YQUAL6", " ", "Quality of Y centroid value (SIC MDRS)",&status);
+ FITS_update_key(outfits, TSTRING, "YQUAL7", " ", "Quality of Y centroid value (SIC LWRS)",&status);
+ FITS_update_key(outfits, TFLOAT, "YGEO_LIF", &dum, "Y centroid of geocoronal lines in target spectrum", &status);
+ FITS_update_key(outfits, TFLOAT, "YGEO_SIC", &dum, "Y centroid of geocoronal lines in target spectrum", &status);
+ FITS_update_key(outfits, TSHORT, "BKGD_NUM", &bkgd_num, "Number of background regions defined", &status);
+ FITS_update_key(outfits, TSHORT, "BKG_MIN0", &sdum, "[pixels] bkgd sample region 0 - lower limit",&status);
+ FITS_update_key(outfits, TSHORT, "BKG_MIN1", &sdum, "[pixels] bkgd sample region 1 - lower limit",&status);
+ FITS_update_key(outfits, TSHORT, "BKG_MIN2", &sdum, "[pixels] bkgd sample region 2 - lower limit",&status);
+ FITS_update_key(outfits, TSHORT, "BKG_MIN3", &sdum, "[pixels] bkgd sample region 3 - lower limit",&status);
+ FITS_update_key(outfits, TSHORT, "BKG_MIN4", &sdum, "[pixels] bkgd sample region 4 - lower limit",&status);
+ FITS_update_key(outfits, TSHORT, "BKG_MIN5", &sdum, "[pixels] bkgd sample region 5 - lower limit",&status);
+ FITS_update_key(outfits, TSHORT, "BKG_MIN6", &sdum, "[pixels] bkgd sample region 6 - lower limit",&status);
+ FITS_update_key(outfits, TSHORT, "BKG_MIN7", &sdum, "[pixels] bkgd sample region 7 - lower limit",&status);
+ FITS_update_key(outfits, TSHORT, "BKG_MAX0", &sdum, "[pixels] bkgd sample region 0 - upper limit",&status);
+ FITS_update_key(outfits, TSHORT, "BKG_MAX1", &sdum, "[pixels] bkgd sample region 1 - upper limit",&status);
+ FITS_update_key(outfits, TSHORT, "BKG_MAX2", &sdum, "[pixels] bkgd sample region 2 - upper limit",&status);
+ FITS_update_key(outfits, TSHORT, "BKG_MAX3", &sdum, "[pixels] bkgd sample region 3 - upper limit",&status);
+ FITS_update_key(outfits, TSHORT, "BKG_MAX4", &sdum, "[pixels] bkgd sample region 4 - upper limit",&status);
+ FITS_update_key(outfits, TSHORT, "BKG_MAX5", &sdum, "[pixels] bkgd sample region 5 - upper limit",&status);
+ FITS_update_key(outfits, TSHORT, "BKG_MAX6", &sdum, "[pixels] bkgd sample region 6 - upper limit",&status);
+ FITS_update_key(outfits, TSHORT, "BKG_MAX7", &sdum, "[pixels] bkgd sample region 7 - upper limit",&status);
+
+ cf_verbose(3, "Exiting cf_add_header_keywords");
+ return EXIT_SUCCESS;
+}
+
+/* Set limits to the background sample regions */
+
+int cf_set_background_limits(fitsfile *outfits) {
+
+ char aper[FLEN_VALUE];
+ char bchrfile[FLEN_FILENAME];
+ int status=0;
+ long npts;
+ short bkgd_num=3, *ylim;
+ fitsfile *bchrfits;
+
+ cf_verbose(3, "Entering cf_set_background_limits");
+
+/* Read the target aperture from the input file header. */
+ FITS_read_key(outfits,TSTRING,"APERTURE",aper, NULL, &status);
+ cf_verbose(3, "aperture = %s", aper);
+
+/* If APERTURE = RFPT, use same background region as for LWRS. */
+ if (!strncmp (aper, "RFPT", 4)) {
+ strncpy(aper, "LWRS", 4);
+ cf_verbose(1, "APERTURE = RFPT. Will treat as LWRS observation.");
+ }
+
+/* Read the name of the BCHR parameter file and open it. */
+ FITS_read_key(outfits, TSTRING, "BCHR_CAL",bchrfile, NULL, &status);
+ cf_verbose(3,"BCHR parameter file = %s ",bchrfile);
+ FITS_open_file(&bchrfits,cf_parm_file(bchrfile), READONLY, &status);
+
+/* From the first extension of the BCHR_CAL file, read the limits
+ to the background regions. Close the file */
+ FITS_movabs_hdu(bchrfits, 2, NULL, &status);
+ npts = cf_read_col(bchrfits, TSHORT, aper, (void **) &ylim);
+ FITS_close_file(bchrfits, &status);
+
+ cf_verbose(3, "Number of background regions: %d", bkgd_num);
+ cf_verbose(3, "Limits of background regions: %d, %d, %d, %d, %d, %d",
+ ylim[0],ylim[1],ylim[2],ylim[3],ylim[4],ylim[5]);
+
+/* Update the header keywords */
+ FITS_update_key(outfits, TSHORT, "BKGD_NUM", &bkgd_num, NULL, &status);
+ FITS_update_key(outfits, TSHORT, "BKG_MIN0", &ylim[0], NULL, &status);
+ FITS_update_key(outfits, TSHORT, "BKG_MIN1", &ylim[2], NULL, &status);
+ FITS_update_key(outfits, TSHORT, "BKG_MIN2", &ylim[4], NULL, &status);
+ FITS_update_key(outfits, TSHORT, "BKG_MAX0", &ylim[1], NULL, &status);
+ FITS_update_key(outfits, TSHORT, "BKG_MAX1", &ylim[3], NULL, &status);
+ FITS_update_key(outfits, TSHORT, "BKG_MAX2", &ylim[5], NULL, &status);
+
+ free (ylim);
+ cf_verbose(3, "Exiting cf_set_background_limits");
+ return EXIT_SUCCESS ;
+}
diff --git a/src/libcf/cf_make_mask.c b/src/libcf/cf_make_mask.c
new file mode 100644
index 0000000..090222c
--- /dev/null
+++ b/src/libcf/cf_make_mask.c
@@ -0,0 +1,175 @@
+/***********************************************************************
+ *
+ * CF_MAKE_MASK
+ *
+ * Procedure to create a mask of the bad pixels from the pixel list
+ * generated by cf_bad_pixels
+ *
+ * CALL: cf_make_mask(header, ap, nout, wave_out, bny, bymin, bpmask)
+ *
+ * Arguments: fitsfile header name of the input IDF
+ * int ap Aperture designation
+ * long nout number of points in wavelength array
+ * float wave_out wavelength array
+ * int bny Y dimension of the output image -
+ * should be same as background
+ * int bymin Y coordinate of first element in
+ * output array. Should match background
+ * float bpmask Output bad pixel mask - dimensions
+ * are nout * bny
+ *
+ * History: 04/29/03 rdr v1.0 Begin work
+ * 04/30/03 wvd v1.1 Install
+ * 09/30/03 wvd v1.2 Use cf_read_col to read BPM_CAL
+ * file.
+ * 11/11/03 wvd v1.3 Fix bug in normalization of
+ * pothole map.
+ * 07/21/04 wvd v1.4 Fix bug in construction of
+ * pothole mask: change & to &&
+ * 11/25/05 wvd v1.5 Fill gaps in the mask that
+ * open when the bad-pixel map
+ * is converted from pixels to
+ * wavelengths.
+ *
+ ***********************************************************************/
+
+#include "calfuse.h"
+
+int cf_make_mask(fitsfile *header, int ap, long nout, float *wave_out,
+int bny, int bymin, float **bpmask){
+
+ char CF_PRGM_ID[] = "cf_make_mask";
+ char CF_VER_NUM[] = "1.5";
+
+ char bpm_file[FLEN_VALUE], *chan;
+ int *bphist, nhist, status=0;
+ long bpsum, i, j, k, ndx, nsam, npix, npts;
+ float *bpmaskt, w0, wpc, pnorm=1;
+ float *x, *y, *wt, *lam ;
+ float bpmax ;
+ fitsfile *bpmfits ;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ cf_verbose(3, "Entering routine cf_make_mask") ;
+
+ /* Generate an array (nout * bny) to contain the map. */
+ npix = nout * bny;
+ bpmaskt = cf_calloc(npix, sizeof(float)) ;
+
+ /* Read wavelength information from input file header. */
+ FITS_movabs_hdu(header, 1, NULL, &status);
+ FITS_read_key(header, TFLOAT, "WPC", &wpc, NULL, &status);
+ w0 = wave_out[0];
+ cf_verbose(3, "Wavelengths: w0=%g, dw=%g", w0, wpc);
+
+ /* Open the bad-pixel file. */
+ if (fits_read_key(header, TSTRING, "BPM_CAL", bpm_file, NULL, &status)) {
+ cf_verbose(0,
+ "No bad pixel (BPM_CAL) file specified. Assuming no potholes.") ;
+ for (i=0; i<npix; i++) bpmaskt[i] = 1.;
+ *bpmask = bpmaskt;
+ return 0;
+ }
+ cf_verbose(3, "Bad Pixel file = %s ", bpm_file) ;
+ if (fits_open_file(&bpmfits, bpm_file, READONLY, &status) ) {
+ cf_verbose(0,
+ "No bad pixel (BPM_CAL) file found. Assuming no potholes.");
+ for (i=0; i<npix; i++) bpmaskt[i] = 1.;
+ *bpmask = bpmaskt;
+ return 0;
+ };
+
+ /* Read the BPM file. */
+ FITS_movabs_hdu(bpmfits, 2, NULL, &status);
+ npts = cf_read_col(bpmfits, TFLOAT, "X", (void **) &x);
+ npts = cf_read_col(bpmfits, TFLOAT, "Y", (void **) &y);
+ npts = cf_read_col(bpmfits, TFLOAT, "WEIGHT", (void **) &wt);
+ npts = cf_read_col(bpmfits, TFLOAT, "LAMBDA", (void **) &lam);
+ npts = cf_read_col(bpmfits, TBYTE, "CHANNEL", (void **) &chan);
+
+ /* Fill the map with pothole information. */
+ nsam = 0;
+ for (i=0; i<npts; i++) {
+ if (chan[i] == ap) {
+ j = (long) ( (lam[i] - w0)/wpc + 0.5 ) ;
+ k = (long) ( (y[i] - bymin) + 0.5 ) ;
+ if (j < nout && k < bny) {
+ nsam++ ;
+ ndx = k*nout + j ;
+ if (ndx > 0 && ndx < npix) bpmaskt[ndx] += wt[i] ;
+ }
+ }
+ }
+ cf_verbose(3, "Channel %d contains %d bad pixels.", ap, nsam) ;
+
+ /* If any potholes are present, generate the pothole map. */
+ if(nsam > 0) {
+
+ /* Determine a normalization for the potholes. This is done by
+ creating a histogram of the weights between 0 and the maximum
+ and then selecting the 95% level as the normalization factor.
+ We use 95%, rather than the maximum, to avoid the possibility
+ of having a few spurious points define the normalization. */
+
+ /* Determine the maximum value. */
+ bpmax = 0.;
+ for (i=0; i<npix; i++)
+ if (bpmaskt[i] > bpmax) bpmax = bpmaskt[i] ;
+
+ /* Generate the histogram. */
+ nhist = (int) (bpmax * 10.) + 1 ;
+ bphist = (int *) cf_calloc(nhist, sizeof(int)) ;
+ bpsum = 0;
+ for (i=0; i < npix; i++) {
+ ndx = (int) (bpmaskt[i] * 10.) ;
+ if (ndx > 0 && ndx < nhist) {
+ bphist[ndx]++;
+ bpsum++;
+ }
+ }
+
+ /* Now select the 95% level. */
+ nsam = 0;
+ i = nhist-1 ;
+ while (nsam < bpsum/20 && i >= 0) {
+ nsam += bphist[i] ;
+ pnorm = bpmax - (nhist-1-i) / 10. ;
+ i--;
+ }
+
+ cf_verbose(3, "bpmax = %.1f, normalizing factor = %.2f", bpmax, pnorm) ;
+
+ /* Normalize and invert the mask so that the center of the
+ pothole is zero and the region outside is 1. */
+
+ for (i=0; i<npix; i++) {
+ bpmaskt[i] = 1. - (bpmaskt[i] / pnorm) ;
+ if (bpmaskt[i] < 0.) bpmaskt[i] = 0. ;
+ }
+
+ /* If a pixel in the mask is higher than both of its neighbors,
+ it probably represents a gap that opened when converting from
+ pixels to wavelengths. Fill it. */
+
+ for (j = 1; j < nout-1; j++) {
+ for (k = 0; k < bny; k++) {
+ ndx = k*nout + j ;
+ if (bpmaskt[ndx-1] < bpmaskt[ndx] && bpmaskt[ndx] > bpmaskt[ndx+1])
+ bpmaskt[ndx] = (bpmaskt[ndx-1] + bpmaskt[ndx+1]) / 2.;
+ }
+ }
+ }
+
+ /* If there are no potholes, generate an array with all ones. */
+ else
+ for (i=0; i<npix; i++) bpmaskt[i] = 1. ;
+
+ /* Redirect the pointer. */
+ *bpmask = bpmaskt;
+
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+
+ return status ;
+}
diff --git a/src/libcf/cf_make_wave_array.c b/src/libcf/cf_make_wave_array.c
new file mode 100644
index 0000000..bbbefcd
--- /dev/null
+++ b/src/libcf/cf_make_wave_array.c
@@ -0,0 +1,147 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_make_wave_array(fitsfile *header, int aperture, long *nout,
+ * float **wave_out)
+ *
+ * Description: Reads W0, WMAX, and WPC from the scrn*.fit file and returns
+ * output wavelength array and its size. If not supplied by
+ * user, values are taken from the WAVE_CAL file.
+ *
+ * The FITS wavelength-calibration file has 8 extensions in the
+ * following order:
+ * Ext# HDU# Channel Aperture
+ * 1 2 LiF HIRS
+ * 2 3 LiF MDRS
+ * 3 4 LiF LWRS
+ * 4 5 LiF PINH
+ * 5 6 SiC HIRS
+ * 6 7 SiC MDRS
+ * 7 8 SiC LWRS
+ * 8 9 SiC PINH
+ *
+ * Arguments: fitsfile *header Pointer to IDF FITS file header
+ * int aperture Target aperture (values 1-8)
+ * long *nout Length of output arrays
+ * float **wave_out Pointer to output wavelength array
+ *
+ * Returns: 0 on success
+ *
+ * History: 02/24/03 1.1 wvd Initial coding.
+ * 03/02/03 1.2 wvd Write W0 and WPC to IDF header.
+ * 09/29/03 1.3 wvd Change calfusettag.h to calfuse.h
+ * 10/15/03 1.4 wvd Read either LIF or SIC wavelength
+ * parameters from PARM_CAL file,
+ * depending upon aperture.
+ * 03/16/04 1.5 wvd Cast wave_out array as type float.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include "calfuse.h"
+
+static char CF_PRGM_ID[] = "cf_make_wave_array";
+static char CF_VER_NUM[] = "1.5";
+
+int
+cf_make_wave_array(fitsfile *header, int aperture, long *nout,
+ float **wave_out)
+{
+ char parm_file[FLEN_VALUE]; /* Name of parameter file */
+ char wave_file[FLEN_VALUE]; /* Name of WAVE_CAL file */
+ char w0_key[FLEN_KEYWORD]; /* Depends on aperture */
+ char wmax_key[FLEN_KEYWORD]; /* Depends on aperture */
+ char wpc_key[FLEN_KEYWORD]; /* Depends on aperture */
+
+ int status=0, hdunum;
+ long i;
+
+ float default_w0, default_wmax, default_wpc; /* Default values */
+ float w0, wmax, wpc; /* Requested values */
+
+ fitsfile *parmfits, *wavefits;
+
+ /* Initialize error checking */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin processing");
+
+ /*
+ * Set keywords to read either LIF or SIC parameters, depending on
+ * the aperture number.
+ */
+ if (aperture < 5) {
+ sprintf(w0_key, "LIF_W0");
+ sprintf(wmax_key, "LIF_WMAX");
+ sprintf(wpc_key, "LIF_WPC");
+ } else {
+ sprintf(w0_key, "SIC_W0");
+ sprintf(wmax_key, "SIC_WMAX");
+ sprintf(wpc_key, "SIC_WPC");
+ }
+
+ /* Read recommended values of W0, WMAX, and WPC from WAVE_CAL header */
+ hdunum = aperture + 1;
+ FITS_read_key(header, TSTRING, "WAVE_CAL", wave_file, NULL, &status);
+ FITS_open_file(&wavefits, cf_cal_file(wave_file), READONLY, &status);
+ FITS_movabs_hdu(wavefits, hdunum, NULL, &status);
+ FITS_read_key(wavefits, TFLOAT, "W0", &default_w0, NULL, &status);
+ FITS_read_key(wavefits, TFLOAT, "WMAX", &default_wmax, NULL, &status);
+ FITS_read_key(wavefits, TFLOAT, "WPC", &default_wpc, NULL, &status);
+ FITS_close_file(wavefits, &status);
+ cf_verbose(3, "Recommended wavelength parameters:");
+ cf_verbose(3, "\tW0 = %g, WMAX = %g, WPC = %g",
+ default_w0, default_wmax, default_wpc);
+
+ /*
+ * Read requested values of W0, WMAX, and WPC from PARM_CAL.
+ * If no value requested, use default values.
+ * Compare requested values with default; complain if weird.
+ */
+ FITS_read_key(header, TSTRING, "PARM_CAL", parm_file, NULL, &status);
+ FITS_open_file(&parmfits, cf_parm_file(parm_file), READONLY, &status);
+ if (fits_read_key(parmfits, TFLOAT, w0_key, &w0, NULL, &status)) {
+ w0 = default_w0;
+ status = 0;
+ }
+ else if (w0 < default_w0)
+ cf_if_warning("Requested value of W0 is less than recommended value.");
+
+ if (fits_read_key(parmfits, TFLOAT, wmax_key, &wmax, NULL, &status)) {
+ wmax = default_wmax;
+ status = 0;
+ }
+ else if (wmax > default_wmax)
+ cf_if_warning("Requested value of WMAX is greater than "
+ "recommended value.");
+
+ if (fits_read_key(parmfits, TFLOAT, wpc_key, &wpc, NULL, &status)) {
+ wpc = default_wpc;
+ status = 0;
+ }
+ else if (wpc < default_wpc)
+ cf_if_warning("Requested value of WPC is less than "
+ "recommended value.");
+ FITS_close_file(parmfits, &status);
+ cf_verbose(3, "Using these wavelength parameters:");
+ cf_verbose(3, "\tW0 = %g, WMAX = %g, WPC = %g", w0, wmax, wpc);
+
+ /* Compute length of output wavelength array, allocate memory,
+ * and fill array.
+ */
+ *nout = (long) ((wmax - w0)/wpc + 0.5) + 1L;
+ *wave_out = (float *) cf_calloc(*nout, sizeof(float));
+ for (i = 0; i < *nout; i++)
+ (*wave_out)[i] = w0 + wpc * (float) i;
+
+ /*
+ * Write WO and WPC to output file header.
+ */
+ FITS_update_key(header, TFLOAT, "WPC", &wpc, NULL, &status);
+ FITS_update_key(header, TFLOAT, "W0", &w0, NULL, &status);
+
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return status;
+}
diff --git a/src/libcf/cf_mirror_motion.c b/src/libcf/cf_mirror_motion.c
new file mode 100644
index 0000000..d5dd3b3
--- /dev/null
+++ b/src/libcf/cf_mirror_motion.c
@@ -0,0 +1,189 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_mirror_motion(header, nevents, time, x, y, channel,
+ * ntimes, ttime, tsunset)
+ *
+ * Description: Calculates the shift of the spectrum in both X and Y caused
+ * by thermal changes in the mirror position and corrects
+ * the X and Y coordinates of each photon event.
+ *
+ * fitsfile *header Pointer to the location of the FITS
+ * header of the Intermediate data File
+ * long nevents Number of photons in the file
+ * int *time Time stamp (in seconds) since the
+ * start of the exposure
+ * float *x Position of the photon (in pixels)
+ * float *y Position of the photon (in pixels)
+ * unsigned char channel Channel id of each photon
+ * long ntimes Number of entries in timeline table
+ * float ttime Time array of timeline table
+ * short tsunset Time since last sunset
+ *
+ * Returns: 0 on success
+ *
+ * History: 09/06/02 1.1 RDR Begin work, adapted from cf_make_shift
+ * 03/01/03 1.3 wvd Correct use of pointer in FITS_read_key()
+ * 04/21/03 1.4 wvd Use tsunset array from timeline table.
+ * Do not assume that ttime is continuous.
+ * Interpolate between tabulated shifts.
+ * 05/20/03 1.5 rdr Added call to cf_proc_check
+ * 08/21/03 1.6 wvd Change channel to unsigned char.
+ * 06/22/04 1.7 wvd Estimate time between sunsets using
+ * orbit period in file header.
+ * 07/14/04 1.8 rdr Correct line which selects calibration
+ * 03/22/05 1.9 wvd Change tsunset from float to short.
+ * Read orbital period from file header.
+ * 04/15/05 1.10 wvd If ORBPERID keyword is not present,
+ * assume that it is 6000 s.
+ * 12/20/05 1.11 wvd Omit correction for an extended
+ * source.
+ * 04/07/07 1.12 wvd Clean up compiler warnings.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "calfuse.h"
+
+int
+cf_mirror_motion(fitsfile *header, long nevents, float *time, float *x,
+ float *y, unsigned char *channel, long ntimes, float *ttime,
+ short *tsunset) {
+
+ char CF_PRGM_ID[] = "cf_mirror_motion";
+ char CF_VER_NUM[] = "1.12";
+
+ fitsfile *mmfits;
+ int errflg=0, status=0, anynull=0, hdutype, nullval=0;
+ int active_ap[2], extended;
+ long j, k, ndx;
+ char det[FLEN_VALUE], mmcal[FLEN_VALUE];
+ float *tdxlif, *tdxsic, *dxlif, *dylif, *dxsic, *dysic;
+ float frac, period, w0, w1, w99;
+
+ /* Initialize error checking. */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+
+ /* Enter a time stamp into the log */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ /* Check whether routine is appropriate for this data file. */
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ /* Read detector keyword from header. */
+ FITS_read_key(header, TSTRING, "DETECTOR", det, NULL, &status);
+
+ /* Determine the channel numbers of the active apertures */
+ extended = cf_source_aper(header, active_ap);
+
+ /* If source is extended, exit now. */
+ if (extended) {
+ cf_verbose(1, "Extended source. Omitting photon shift.");
+ cf_proc_update(header, CF_PRGM_ID, "SKIPPED");
+ return (status);
+ }
+
+ /*
+ * Read orbital period from file header.
+ */
+ fits_read_key(header, TFLOAT, "ORBPERID", &period, NULL, &status);
+ if (status) {
+ status = 0;
+ period = 6000;
+ cf_verbose(1, "Keyword ORBPERID not found; assuming 6000 seconds");
+ }
+ else
+ cf_verbose(2, "Estimated orbital period is %.2f seconds", period);
+
+ /* Allocate space for shift arrays. */
+ dxlif = (float *) cf_calloc(ntimes, sizeof(float));
+ dylif = (float *) cf_calloc(ntimes, sizeof(float));
+ dxsic = (float *) cf_calloc(ntimes, sizeof(float));
+ dysic = (float *) cf_calloc(ntimes, sizeof(float));
+
+ /* Read the name of the mirror-motion calibration file */
+ FITS_read_key(header, TSTRING, "MIRR_CAL", mmcal, NULL, &status);
+ cf_verbose(3, "Mirror motion calibration file = %s", mmcal);
+
+ /* Open the calibration file and read in the information relating
+ * x shifts as a function of time (in minutes) from sunset for one orbit
+ * (100 minutes)
+ */
+ FITS_open_file(&mmfits, cf_cal_file(mmcal), READONLY, &status);
+ tdxsic = (float *) cf_calloc(100, sizeof(float));
+ tdxlif = (float *) cf_calloc(100, sizeof(float));
+ cf_verbose(3,"detector= %s",det) ;
+ if (!strncmp(det, "1", 1) ) {
+ cf_verbose(3,"Correcting detector 1") ;
+ /*
+ * NOTE: LiF 1 is used as reference and so, by definition, has no
+ * mirror motions. Thus, on side 1 only the SiC spectrum will move.
+ */
+ FITS_movabs_hdu(mmfits,2, &hdutype, &status);
+ FITS_read_img(mmfits, TFLOAT, 1,100, &nullval, tdxsic, &anynull,
+ &status);
+ }
+ else {
+ cf_verbose(3,"Correcting detector 2") ;
+ FITS_movabs_hdu(mmfits,3, &hdutype, &status);
+ FITS_read_img(mmfits, TFLOAT, 1,100, &nullval, tdxlif, &anynull,
+ &status);
+ FITS_movabs_hdu(mmfits,4, &hdutype, &status);
+ FITS_read_img(mmfits, TFLOAT, 1,100, &nullval, tdxsic, &anynull,
+ &status);
+ }
+ FITS_close_file(mmfits, &status);
+ /* Determine the shifts for each second of the observation. */
+ for (k=0; k<ntimes; k++) {
+ frac = tsunset[k] / period * 100.;
+ ndx = (long) frac;
+ if (ndx >= 99) {
+ w99 = 100. - frac;
+ w0 = frac - 99.;
+ dxlif[k] = w0 * tdxlif[0] + w99 * tdxlif[99];
+ dxsic[k] = w0 * tdxsic[0] + w99 * tdxsic[99];
+ }
+ else {
+ w0 = (float) (ndx + 1) - frac;
+ w1 = frac - (float) (ndx);
+ dxlif[k] = w0 * tdxlif[ndx] + w1 * tdxlif[ndx + 1];
+ dxsic[k] = w0 * tdxsic[ndx] + w1 * tdxsic[ndx + 1];
+ }
+ }
+
+ /* Apply the calculated shifts to the data. */
+ for (j=k=0; j<nevents; j++) {
+ while(ttime[k+1] - FRAME_TOLERANCE < time[j] && k+1 < ntimes) k++;
+ /*
+ * Shift only photons in the active aperture.
+ * active_ap[0] = lif, active_ap[1] = sic
+ */
+ if (channel[j] == active_ap[0]) {
+ x[j] += dxlif[k];
+ y[j] += dylif[k];
+
+ }
+ if (channel[j] == active_ap[1]) {
+ x[j] += dxsic[k];
+ y[j] += dysic[k];
+ }
+ }
+
+ /* Release array storage */
+ free(tdxlif);
+ free(tdxsic);
+ free(dxlif);
+ free(dylif);
+ free(dxsic);
+ free(dysic);
+
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished execution.");
+
+ return status;
+}
diff --git a/src/libcf/cf_modify_hist_pha.c b/src/libcf/cf_modify_hist_pha.c
new file mode 100644
index 0000000..a842b5d
--- /dev/null
+++ b/src/libcf/cf_modify_hist_pha.c
@@ -0,0 +1,91 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_modify_hist_pha (fitsfile *header, long nevents,
+ * unsigned char *pha, unsigned char *channel)
+ *
+ * Description: For histogram data, set pulse heights of all photons to the
+ * most likely value, a function of aperture and obs date.
+ *
+ * Arguments: fitsfile *header Input FITS file pointer
+ * long nevents Number of photon events
+ * unsigned char *pha Pulse-height array (output)
+ * unsigned char *channel Channel assignment (input)
+ *
+ * Calls:
+ *
+ * Returns: 0 on success
+ *
+ * History: 03/01/05 1.1 wvd Initial coding
+ * 03/16/05 1.2 wvd Read PHA array as type TBYTE, not TINT.
+ * 04/28/05 1.3 wvd Fix bug in loop through MJD array.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "calfuse.h"
+
+int
+cf_modify_hist_pha (fitsfile *header, long nevents, unsigned char *pha,
+ unsigned char *channel)
+{
+ char CF_PRGM_ID[] = "cf_modify_hist_pha";
+ char CF_VER_NUM[] = "1.3";
+
+ char phahfile[FLEN_FILENAME];
+ unsigned char pha_chan[8], *pha_mjd=NULL;
+ int errflg=0, status=0;
+ long i, j, jmax;
+ double expstart, *mjd=NULL;
+ fitsfile *phahfits;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ /* If data were not taken in HIST mode, exit now. */
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ /* Read header keywords. */
+ FITS_read_key(header, TDOUBLE, "EXPSTART", &expstart, NULL, &status);
+
+ /* Open the pulse-height calibration file. */
+ FITS_read_key(header, TSTRING, "PHAH_CAL", phahfile, NULL, &status);
+ cf_verbose(3, "Pulse-height calibration file = %s", phahfile);
+ FITS_open_file(&phahfits, cf_cal_file(phahfile), READONLY, &status);
+
+ /*
+ * Loop through all channels, reading expected pulse heights from PHAH_CAL.
+ * Use the values appropriate for the date of this exposure.
+ */
+ pha_chan[0] = 20; /* Default for events not in a channel */
+ for (i = 1; i < 8; i++) {
+ if (i == 4) continue;
+ FITS_movabs_hdu(phahfits, i+1, NULL, &status);
+ jmax = cf_read_col(phahfits, TDOUBLE, "MJD", (void *) &mjd);
+ jmax = cf_read_col(phahfits, TBYTE, "PHA", (void *) &pha_mjd);
+ j = 0;
+ while (j < jmax-1 && mjd[j+1] < expstart) j++;
+ pha_chan[i] = pha_mjd[j];
+ cf_verbose(3, "channel = %d, j = %d, pha = %d", i, j, pha_chan[i]);
+ }
+
+ /* Close the pulse-height calibration file. */
+ FITS_close_file(phahfits, &status);
+
+ /* Set pulse height for each photon according to its channel number. */
+ for (j = 0; j < nevents; j++)
+ pha[j] = pha_chan[(int) channel[j]];
+
+ free(mjd);
+ free(pha_mjd);
+
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+
+ return status;
+}
diff --git a/src/libcf/cf_modify_hist_times.c b/src/libcf/cf_modify_hist_times.c
new file mode 100644
index 0000000..ba1b5d0
--- /dev/null
+++ b/src/libcf/cf_modify_hist_times.c
@@ -0,0 +1,83 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_modify_hist_times (fitsfile *infits, long nevents,
+ * float *time, GTI *gti);
+ *
+ * Description: For histogram data, set photon-arrival times to the midpoint
+ * of the longest good-time interval.
+ *
+ * Arguments: fitsfile *infits Input FITS file pointer
+ * long nevents Number of photon events
+ * float *time Time array for photon list
+ * GIT *gti Good time interval(s)
+ *
+ * Calls:
+ *
+ * Returns: TRUE if time array is changed.
+ * FALSE if time array is not changed.
+ *
+ * History: 06/02/04 1.1 wvd Initial coding
+ * 06/07/04 1.2 wvd Use standard return values.
+ * 02/17/05 1.3 wvd Initalize jmax to 0.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "calfuse.h"
+
+int
+cf_modify_hist_times (fitsfile *infits, long nevents, float *time, GTI *gti)
+{
+ char CF_PRGM_ID[] = "cf_modify_hist_times";
+ char CF_VER_NUM[] = "1.3";
+
+ int errflg=0, status=0;
+ long j, jmax=0;
+ float exptime, gti_time, max_time, photon_time, rawtime;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ /* If data were not taken in HIST mode, exit now. */
+ if ((errflg = cf_proc_check(infits, CF_PRGM_ID))) return errflg;
+
+ /* Read header keywords. */
+ FITS_read_key(infits, TFLOAT, "EXPTIME", &exptime, NULL, &status);
+ FITS_read_key(infits, TFLOAT, "RAWTIME", &rawtime, NULL, &status);
+
+ /*
+ * If the entire exposure was rejected by the screening routines,
+ * we don't bother changing photon-arrival times. If no time was
+ * lost to screening, the default arrival times are OK. Exit now.
+ */
+ if (exptime < 1. || exptime > rawtime - 1.) {
+ cf_proc_update(infits, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return -1;
+ }
+
+ /* Determine which is the longest good-time interval. */
+ max_time = 0.;
+ for (j = 0; j < gti->ntimes; j++) {
+ if ((gti_time = gti->stop[j] - gti->start[j]) > max_time) {
+ max_time = gti_time;
+ jmax = j;
+ }
+ }
+
+ /* Set all photon-arrival times to midpoint of longest GTI. */
+ photon_time = (gti->start[jmax] + gti->stop[jmax]) / 2.;
+ for (j = 0; j < nevents; j++)
+ time[j] = photon_time;
+ cf_verbose(1, "Setting photon-arrival times to %.1f", photon_time);
+
+ cf_proc_update(infits, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return status;
+}
diff --git a/src/libcf/cf_nint.c b/src/libcf/cf_nint.c
new file mode 100644
index 0000000..8488911
--- /dev/null
+++ b/src/libcf/cf_nint.c
@@ -0,0 +1,47 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: n = cf_nint(x)
+ *
+ * Description: Converts double to nearest integer.
+ *
+ * Variables: double x A double
+ *
+ * Return: int n Nearest integer
+ *
+ * History: 08/25/03 wvd v1.1 Begin work
+ * 02/09/04 wvd v1.2 Add cf_nlong()
+ * 02/17/04 wvd v1.3 Test for overflow of int, long
+ * 02/18/04 wvd v1.4 Change format of error message.
+ * Change from values.h to limits.h
+ *
+ *************************************************************************/
+
+#include <limits.h>
+#include "calfuse.h"
+
+int
+cf_nint(double x)
+
+{
+ if (x > INT_MAX || x < INT_MIN)
+ cf_if_error("Cannot convert %10.4e to an integer", x);
+
+ if (x < 0.) return (int) (x - 0.5);
+ else return (int) (x + 0.5);
+}
+
+
+long
+cf_nlong(double x)
+
+{
+ if (x > LONG_MAX || x < LONG_MIN)
+ cf_if_error("Cannot convert %10.4e to a long", x);
+
+ if (x < 0.) return (long) (x - 0.5);
+ else return (long) (x + 0.5);
+}
diff --git a/src/libcf/cf_optimal_extraction.c b/src/libcf/cf_optimal_extraction.c
new file mode 100644
index 0000000..4bbca9f
--- /dev/null
+++ b/src/libcf/cf_optimal_extraction.c
@@ -0,0 +1,551 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_optimal_extraction(fitsfile *header, int optimal,
+ * int aperture, float *weight, float *y, unsigned char *channel,
+ * float *lambda, long ngood, long *good_index,
+ * float *barray, float *bpmask, int pny, float pycent,
+ * float *parray, long nout, float *wave_out, float **flux_out,
+ * float **sigma_out, long **counts_out, float **weights_out,
+ * float **bkgd_out, short **bpix_out)
+ *
+ * Description: Perform standard or optimal extraction of target spectrum.
+ *
+ * Note: We use a modified version of the optimal-extraction
+ * algorithm described by Keith Horne (PASP, 98, 609, 1986).
+ *
+ * Arguments: fitsfile *header Pointer to IDF FITS file header
+ * int optimal TRUE if optimal extraction requested.
+ * int aperture Target aperture (values 1-8)
+ * float *weight Scale factor for each photon
+ * float *y Final y position of each photon
+ * unsigned char *channel Channel ID of each photon
+ * float *lambda Wavelength for each photon
+ * long ngood Length of array good_index
+ * long *good_index Indices of screened photon events
+ * float *barray 2-D background array
+ * (flux-calibrated and binned)
+ * float *bpmask 2-D array containing bad pixel locations
+ * Same size and location as the background array
+ * int pny Size in Y of weights array
+ * float pycent Y centroid of weights array
+ * float parray 2-D weights array (binned)
+ * long nout Length of output arrays
+ * float *wave_out Output wavelength array
+ * float **flux_out Output flux array
+ * float **sigma_out Output error array
+ * long **counts_out Output counts array
+ * float **weights_out Output weights array
+ * float **bkgd_out Output background array
+ * short **bpix_out Output bad pixel spectrum
+ *
+ * Returns: 0 on success
+ *
+ * History: 01/28/03 1.1 wvd Initial coding
+ * 02/28/03 1.2 peb Changed flux_out, var_out, counts_out
+ * and weight_out argument variables to
+ * pointer-to-pointers, so the arrays can
+ * be returned to the calling routine.
+ * Also tidied the code using lint.
+ * 03/11/03 1.3 wvd Update documentation regarding use of
+ * unsigned char
+ * 03/12/03 1.4 wvd Change weights_out to sum of weights
+ * 03/19/03 1.5 wvd Divide flux_out and var_out by WPC
+ * 04/04/03 1.6 rdr - correct bugs in optimum extraction
+ * - adjust criterion for redetermining
+ * y centroid
+ * 05/07/03 1.7 wvd Change ergcm2s to ergcm2 throughout.
+ * Divide by EXPTIME. Use cf_verbose.
+ * 05/22/03 1.8 wvd If EXPTIME = 0, set flux and error to 0.
+ * 05/28/03 1.9 rdr - For HIST data, spread counts
+ * along the y dimension of
+ * the data and variance arrays
+ * - Incorporate the bad pixel mask into
+ * optimal extraction algorithm
+ * - Return bad-pixel spectrum
+ * 09/30/03 1.10 wvd Negative values of YCENT are
+ * meaningless. Use S/N of data to
+ * determine significance of YCENT.
+ * Exclude airglow lines from S/N.
+ * 10/08/03 1.11 wvd Change counts_out array to type long.
+ * Add 0.5 to it before rounding.
+ * Limit Y coordinates of probability
+ * array before comparing with bkgd.
+ * 10/31/03 1.12 wvd Read centroid quality flags from IDF
+ * header. Do not attempt to calculate
+ * spectral centroid.
+ * 11/03/03 1.13 wvd Modify calculation of variance estimate.
+ * Modify scheme for smoothing HIST data.
+ * Iterate only to 20% accuracy.
+ * 12/05/03 1.14 wvd Check value of SPECBINY before
+ * smoothing HIST data in the Y dimension.
+ * 03/16/04 1.15 wvd In HIST mode, assume counts are
+ * distributed across one X pixel.
+ * Delete wave_out -- not used.
+ * After 50 iterations, abandon optimal
+ * extraction and use boxcar extraction
+ * instead.
+ * Don't test size of barray,
+ * as it is now the same as parray.
+ * Change pycent from int to float.
+ * 04/06/04 1.16 bjg Include string.h
+ * Remove unused variables
+ * 04/21/04 1.17 wvd Set dispapix = fabs(dispapix).
+ * Iterate to 5% accuracy.
+ * In boxcar extraction, use parray to
+ * set extraction limits.
+ * For TTAG data, compute counts_out
+ * directly from photon list.
+ * 04/26/04 1.18 wvd Perform all calculations with
+ * weights array, rather than ergcm2.
+ * Return counts spectrum.
+ * Convert from variance to sigma here,
+ * rather than in cf_write_spectra.
+ * Iterate to 10% accuracy.
+ * 05/17/04 1.19 wvd If weights_out[j] = 0, set variance
+ * = bkgd. Iterate optimal extraction
+ * to accuracy of 0.01 counts. In boxcar
+ * extraction, reduce parray limit to
+ * 1E-4 to better match fluxes from
+ * optimal extraction for bright stars.
+ * 08/13/04 1.20 wvd Use QUALITY array to set X limits
+ * of extraction region. Insure that
+ * sigma_out array is non-zero.
+ * 11/22/05 1.21 wvd Scale variance estimate by bad-pixel
+ * mask to better approximate observed
+ * counts.
+ * 06/12/06 1.22 wvd Call cf_verbose rather than
+ * cf_if_warning.
+ *
+ ****************************************************************************/
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "calfuse.h"
+
+static char CF_PRGM_ID[] = "cf_optimal_extraction";
+static char CF_VER_NUM[] = "1.22";
+
+int
+cf_optimal_extraction(fitsfile *header, int optimal, int aperture,
+ float *weight, float *y, unsigned char *channel,
+ float *lambda, long ngood, long *good_index,
+ float *barray, float *bpmask,
+ int pny, float pycent, float *parray, long nout, float *wave_out,
+ float **flux_out, float **sigma_out, long **counts_out,
+ float **weights_out, float **bkgd_out, short **bpix_out)
+
+{
+ char instmode[FLEN_VALUE], yquality_str[FLEN_KEYWORD],
+ ycent_str[FLEN_KEYWORD], yquality[FLEN_VALUE];
+ int status=0;
+ int nloop; /* Iterations of optimal
+ extraction loop */
+ int ycent_uncertain = FALSE;/* TRUE if centroid quality
+ less than HIGH */
+
+ long i, /* index through photon list */
+ ii, /* ii = good_index[i] */
+ j, /* X index through 2-D arrays */
+ jmin, jmax, /* limits of output spectrum */
+ k; /* Y index through 2-D arrays */
+
+
+ float exptime, /* exposure time */
+ tdead, /* mean dead-time correction */
+ w0, /* minimum value of output wave array */
+ wpc, /* wavelength increment per output bin */
+ *flux_old, /* flux values from previous iteration */
+ *flux_box, /* flux values from boxcar extraction */
+ *var_out, /* 1-D variance array */
+ *var_box, /* variance values from boxcar extraction */
+
+ ycent; /* Y centroid of target spectrum */
+
+ double numerator, denominator, /* Used in optimal-extraction calculations */
+ var_num, weights_num,
+ bkgd_num,
+ *data, /* 2-D data array */
+ *variance, /* 2-D variance array */
+ *w; /* weights array for variance calculation */
+
+ int l, lmin, lmax, /* These variables are */
+ specbiny, specbiny2; /* are used to smooth */
+ float psum, pval; /* HIST data in the */
+ double vval; /* Y dimension. */
+
+ long jj, jjj, m, m_min, n; /* These variables are used to smooth */
+ double edge[2], frac[2], x; /* HIST data in the X dimension. */
+ double dispapix; /* Mean dispersion per pixel */
+ char wave_file[FLEN_VALUE]; /* Name of WAVE_CAL file */
+ int hdunum; /* HDU number of WAVE_CAL file */
+ fitsfile *wavefits; /* Pointer to WAVE_CAL file */
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ cf_verbose(3, "Entering cf_optimal_extraction");
+
+ /*
+ * Check type of observation (HIST or TTAG) and binning factor.
+ */
+ FITS_movabs_hdu(header, 1, NULL, &status) ;
+ FITS_read_key(header, TSTRING, "INSTMODE", instmode, NULL, &status);
+ FITS_read_key(header, TINT, "SPECBINY", &specbiny, NULL, &status);
+ specbiny2 = specbiny/2 ;
+ if (specbiny2 < 1) specbiny2 = 1 ;
+ cf_verbose(3, "Aperture = %d, instrument mode = %s, binning in Y = %d",
+ aperture, instmode, specbiny) ;
+
+ /*
+ * Read EXPTIME and TOT_DEAD from file header.
+ */
+ FITS_read_key(header, TFLOAT, "EXPTIME", &exptime, NULL, &status);
+ FITS_read_key(header, TFLOAT, "TOT_DEAD", &tdead, NULL, &status);
+ if (tdead < 1.0) tdead = 1.0;
+ cf_verbose (3, "EXPTIME = %.0f, TOT_DEAD=%g", exptime, tdead);
+
+ /*
+ * Read wavelength parameters W0 and WPC from file header.
+ */
+ FITS_read_key(header, TFLOAT, "WPC", &wpc, NULL, &status);
+ FITS_read_key(header, TFLOAT, "W0", &w0, NULL, &status);
+ cf_verbose (3, "Wavelength info: w0=%g, dw=%g", w0, wpc) ;
+
+ /*
+ * Read spectral centroid YCENT and YQUAL from file header.
+ */
+ sprintf(ycent_str, "YCENT%1d", aperture);
+ FITS_read_key(header, TFLOAT, ycent_str, &ycent, NULL, &status);
+ sprintf(yquality_str, "YQUAL%1d", aperture);
+ FITS_read_key(header, TSTRING, yquality_str, yquality, NULL, &status);
+ if (*yquality != 'H') ycent_uncertain = TRUE;
+ cf_verbose (3, "%s = %g, quality = %s", ycent_str, ycent, yquality);
+
+ /*
+ * If HIST data, read DISPAPIX from header of WAVE_CAL file.
+ */
+ if (!strncmp(instmode,"H",1)) {
+ hdunum = aperture + 1;
+ FITS_read_key(header, TSTRING, "WAVE_CAL", wave_file, NULL, &status);
+ FITS_open_file(&wavefits, cf_cal_file(wave_file), READONLY, &status);
+ FITS_movabs_hdu(wavefits, hdunum, NULL, &status);
+ FITS_read_key(wavefits, TDOUBLE, "DISPAPIX", &dispapix, NULL, &status);
+ dispapix = fabs(dispapix);
+ FITS_close_file(wavefits, &status);
+ }
+
+ /*
+ * Allocate space for all arrays.
+ */
+ data = (double *) cf_calloc(pny*nout, sizeof(double));
+ variance = (double *) cf_calloc(pny*nout, sizeof(double));
+ w = (double *) cf_calloc(nout, sizeof(double));
+
+ flux_old = (float *) cf_calloc(nout, sizeof(float));
+ flux_box = (float *) cf_calloc(nout, sizeof(float));
+ var_box = (float *) cf_calloc(nout, sizeof(float));
+
+ *flux_out = (float *) cf_calloc(nout, sizeof(float));
+ var_out = (float *) cf_calloc(nout, sizeof(float));
+ *counts_out = (long *) cf_calloc(nout, sizeof(long));
+ *weights_out = (float *) cf_calloc(nout, sizeof(float));
+ *bkgd_out = (float *) cf_calloc(nout, sizeof(float));
+ *bpix_out = (short *) cf_calloc(nout, sizeof(short)) ;
+
+ /*
+ * The quality array (bpix_out) is the product of the probability
+ * array and the bad-pixel mask. We use it to set the limits of
+ * the extraction region.
+ */
+ cf_verbose(3, "Generating bad-pixel spectrum.") ;
+ for (j=0; j<nout; j++) {
+ float bpix=0. ;
+ for (k=0; k<pny; k++)
+ bpix += (parray[k*nout + j] * bpmask[k*nout + j]);
+ (*bpix_out)[j] = (short) (100. * bpix + 0.5) ;
+ }
+ jmin = 0L; jmax = nout;
+ for (j=0; j<nout; j++) {
+ if ((*bpix_out)[j] > 0) {
+ jmin = j;
+ break;
+ }
+ }
+ for (j = nout-1; j >= 0; j--) {
+ if ((*bpix_out)[j] > 0) {
+ jmax = j+1;
+ break;
+ }
+ }
+
+ /*
+ * Construct data and variance arrays from photon lists.
+ * Arrays are pny pixels in Y (to match probability array)
+ * and nout pixels in X (to match output wavelength array).
+ * We must thus shift individual photons by PYCENT - YCENT
+ * pixels in Y.
+ */
+
+ cf_verbose(3, "Filling the data array") ;
+ for (i = 0; i < ngood; i++) {
+ ii = good_index[i];
+
+ if (channel[ii] != aperture) continue;
+
+ j = (long) ((lambda[ii] - w0)/wpc + 0.5);
+ if (j < jmin || j >= jmax) continue;
+
+ k = (long) (y[ii] - ycent + pycent + 0.5);
+ if (k < 0 || k >= pny) continue;
+
+ if (!strncmp(instmode,"T",1)) {
+ /* TTAG data */
+ (*counts_out)[j]++;
+ data[k*nout + j] += weight[ii];
+ variance[k*nout + j] += weight[ii] * weight[ii];
+ }
+ else {
+ /* If HIST data are already smoothed in Y, it's easy: */
+ if (specbiny == 1) {
+ data[k*nout + j] += weight[ii];
+ variance[k*nout + j] += weight[ii] * tdead;
+ }
+ /* If HIST data are binned, smooth in both X and Y dimensions. */
+ else {
+ m_min = 0;
+ edge[0] = ((double) j - 0.5) * wpc + w0;
+ edge[1] = ((double) j + 0.5) * wpc + w0;
+ if ((x = (lambda[ii] - edge[0]) / dispapix) < 0.5) {
+ frac[0] = 0.5 - x;
+ frac[1] = 0.5 + x;
+ jj = j - 1;
+ n = 2;
+ if (jj == -1) m_min = 1;
+ }
+ else if ((x = (edge[1] - lambda[ii]) / dispapix) < 0.5) {
+ frac[0] = 0.5 + x;
+ frac[1] = 0.5 - x;
+ jj = j;
+ n = 2;
+ if (jj + 1 == nout) n = 1;
+ }
+ else {
+ frac[0] = 1.0;
+ jj = j;
+ n = 1;
+ }
+
+ psum = 0.;
+ vval = weight[ii] * tdead;
+ lmin = k - specbiny2;
+ lmax = lmin + specbiny;
+ if (lmin < 0) lmin = 0;
+ if (lmax > pny) lmax = pny;
+ pval = 1. / (lmax - lmin);
+ for (l = lmin; l < lmax; l++)
+ psum += parray[l*nout + j];
+ for (l = lmin; l < lmax; l++) {
+ if (psum > 0) pval = parray[l*nout + j] / psum;
+ for (m = m_min; m < n; m++) {
+ jjj = jj + m;
+ data[l*nout + jjj] += pval * frac[m] * weight[ii];
+ variance[l*nout + jjj] += pval * frac[m] * vval;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Boxcar extraction. Use parray to set extraction limits in Y.
+ */
+ cf_verbose(3, "Doing the boxcar extraction.") ;
+ nloop = 0;
+ for (j = jmin; j < jmax; j++) {
+ for (k = 0; k < pny; k++) {
+ if (parray[k*nout + j] > 1E-4) {
+ (*bkgd_out)[j] += barray[k*nout + j];
+ var_out[j] += variance[k*nout + j];
+ (*weights_out)[j] += data[k*nout + j];
+ }
+ }
+ (*flux_out)[j] = (*weights_out)[j] - (*bkgd_out)[j];
+ }
+
+ /*
+ * Has the user requested optimal extraction?
+ */
+ cf_verbose(3,"Optimal keyword = %d ", optimal) ;
+ if (optimal) {
+
+ /*
+ * If Y centroid is uncertain, abandon optimal extraction.
+ */
+ if (ycent_uncertain)
+ cf_verbose(1, "%s uncertain. Cannot perform optimal extraction.",
+ ycent_str);
+
+ else { /* Proceed with optimal extraction */
+
+ cf_verbose(3, "Attempting optimal extraction");
+
+ /*
+ * Save boxcar versions of flux and variance arrays.
+ */
+ for (j = jmin; j < jmax; j++) {
+ flux_box[j] = (*flux_out)[j];
+ var_box[j] = var_out[j];
+ }
+
+ /*
+ * Need array w[j] to scale the variance estimate.
+ * w[j] is mean scale factor from counts to weights at each wavelength.
+ * Must calculate for TTAG data; can use TOT_DEAD for HIST data.
+ */
+ if (!strncmp(instmode,"T",1)) for (j = jmin; j < jmax; j++) {
+ if ((*counts_out)[j] > 0) {
+ for (k = 0; k < pny; k++) w[j] += data[k*nout + j];
+ w[j] /= (*counts_out)[j];
+ }
+ else w[j] = tdead;
+ }
+ else for (j = jmin; j < jmax; j++) w[j] = tdead;
+
+ /*
+ * Begin big loop.
+ */
+ do {
+ /*
+ * Revise variance estimate
+ * We can omit w[j] here, as it appears in both the numerator
+ * and denominator of the flux estimate (below).
+ * 05/17/02 wvd: If weights_out[j] = 0, set variance = bkgd.
+ * 11/22/05 wvd: Scale variance estimate by bpmask.
+ */
+ for (j = jmin; j < jmax; j++) {
+ if ((*weights_out)[j] > 0)
+ for (k = 0; k < pny; k++)
+ variance[k*nout + j] = bpmask[k*nout + j] *
+ fabs(parray[k*nout + j] * (*flux_out)[j] + barray[k*nout + j]);
+ else
+ for (k = 0; k < pny; k++)
+ variance[k*nout + j] = bpmask[k*nout + j] * barray[k*nout + j];
+ }
+
+ /*
+ * Optimal extraction
+ */
+ nloop++;
+ for (j = jmin; j < jmax; j++) {
+ numerator = denominator = 0.;
+ for (k = 0; k < pny; k++) if (variance[k*nout+j] > 0) {
+ numerator += bpmask[k*nout+j] * parray[k*nout + j] *
+ (data[k*nout + j] - barray[k*nout + j]) / variance[k*nout + j];
+ denominator += bpmask[k*nout+j] * parray[k*nout + j] *
+ parray[k*nout + j] / variance[k*nout + j];
+ }
+ flux_old[j] = (*flux_out)[j];
+ if (denominator > 0) (*flux_out)[j] = numerator / denominator;
+ else (*flux_out)[j] = flux_old[j] = 0. ;
+ }
+
+ /*
+ * Repeat loop if any element of flux_out has changed by > 0.01 counts.
+ */
+ for (j = jmin; j < jmax; j++) {
+ if (fabs((*flux_out)[j]-flux_old[j]) > 0.01) {
+ cf_verbose(3, "%d\t%d\t%g", nloop, j, (*flux_out)[j]);
+ break;
+ }
+ }
+
+ } while (j < jmax && nloop < 50); /* End of do loop */
+
+ /*
+ * If nloop < 50, calculate var_out, weights_out, and bkgd_out.
+ * Note that we must scale var_out by w[j] here.
+ */
+ if (nloop < 50) {
+
+ memset (*bkgd_out, 0, nout*sizeof(float));
+ memset (*weights_out, 0, nout*sizeof(float));
+ memset (var_out, 0, nout*sizeof(float));
+
+ for (j = jmin; j < jmax; j++) {
+ bkgd_num = weights_num = var_num = denominator = 0.;
+ for (k = 0; k < pny; k++) if (variance[k*nout+j] > 0) {
+ bkgd_num += bpmask[k*nout+j] * parray[k*nout + j] *
+ barray[k*nout + j] / variance[k*nout + j];
+ weights_num += bpmask[k*nout+j] * parray[k*nout + j] *
+ data[k*nout + j] / variance[k*nout + j];
+ var_num += bpmask[k*nout+j] * parray[k*nout + j];
+ denominator += bpmask[k*nout+j] * parray[k*nout + j] *
+ parray[k*nout + j] / variance[k*nout + j];
+ }
+ if (denominator > 0) {
+ (*bkgd_out)[j] = bkgd_num / denominator;
+ (*weights_out)[j] = weights_num / denominator;
+ var_out[j] = var_num / denominator * w[j];
+ }
+ else {
+ (*bkgd_out)[j] = (*weights_out)[j] = var_out[j] = 0.;
+ }
+ }
+ }
+ /*
+ * If nloop = 50, use boxcar extraction instead.
+ */
+ else {
+ cf_verbose(1, "Optimal extraction failed to converge for aperture %d."
+ " Using boxcar extraction.", aperture);
+ nloop = 0;
+ for (j = jmin; j < jmax; j++) {
+ (*flux_out)[j] = flux_box[j];
+ var_out[j] = var_box[j];
+ }
+ }
+
+ } /* End of if/else test of YCENT */
+ } /* End of optimal extraction block */
+
+ /*
+ * Write number of iterations to file header.
+ */
+ FITS_update_key(header, TINT, "OPT_EXTR", &nloop, NULL, &status);
+ cf_verbose(2, "Number of iterations in optimal extraction = %d", nloop) ;
+
+ /* Convert error array from variance to sigma. */
+ for (j = jmin; j < jmax; j ++) {
+ if (var_out[j] > 0.) var_out[j] = sqrt(var_out[j]);
+ else if ((*bkgd_out)[j] > 0.) var_out[j] = sqrt((*bkgd_out)[j]);
+ else var_out[j] = 0.;
+ }
+ *sigma_out = var_out;
+
+ /*
+ * For HIST data, counts array = weights "uncorrected" for dead time.
+ */
+ if (!strncmp(instmode,"H",1)) for (j = jmin; j < jmax; j++)
+ (*counts_out)[j] = (long) ((*weights_out)[j] / tdead + 0.5);
+
+ /*
+ * Clean up
+ */
+ free(data);
+ free(flux_old);
+ free(flux_box);
+ free(var_box);
+ free(variance);
+ free(w);
+
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return status;
+}
diff --git a/src/libcf/cf_pha_x_distort.c b/src/libcf/cf_pha_x_distort.c
new file mode 100644
index 0000000..5f58824
--- /dev/null
+++ b/src/libcf/cf_pha_x_distort.c
@@ -0,0 +1,107 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_pha_x_distort(fitsfile *header, long nevents,
+ * unsigned char *pha, float *xfarf,
+ * unsigned char *locflags)
+ *
+ * Description: Corrects the X position of low-pulse-height events.
+ *
+ * Note: To conform with the CalFUSE standard, the PHAX
+ * correction is now ADDED to the input photon X coordinate.
+ * This change from previous versions of the code requires
+ * a new set of calibration files. The program now checks the
+ * version number of the PHAX_CAL file and returns an error if
+ * it is less than 4.
+ *
+ * Arguments: fitsfile *header Pointer to FITS file containing the
+ * header of the intermediate data file
+ * long nevents The number of events
+ * unsigned char *pha An array of PHA values
+ * float *xfarf An array of event X positions
+ * unsigned char *locflags Location flags of each event
+ *
+ * Calls:
+ *
+ * Return: 0 on success
+ *
+ * History: 11/11/02 1.1 peb Begin work
+ * 11/12/02 1.3 peb Added check to move only events in
+ * active region.
+ * 03/11/03 1.4 wvd Changed pha and locflags to unsigned
+ * char
+ * 05/20/03 1.5 rdr Added call to cf_proc_check
+ * 07/29/03 1.6 wvd If cf_proc_check fails, return errflg.
+ * 10/14/03 1.7 wvd Add, rather than subtract, PHAX
+ * correction. Check cal file version.
+ * 10/17/03 1.8 wvd Don't crash if CALFVERS keyword
+ * is missing.
+ * 04/07/07 1.9 wvd Clean up compiler warnings.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdlib.h>
+#include "calfuse.h"
+
+int
+cf_pha_x_distort(fitsfile *header, long nevents, unsigned char *pha,
+ float *xfarf, unsigned char *locflags)
+{
+ char CF_PRGM_ID[] = "cf_pha_x_distort";
+ char CF_VER_NUM[] = "1.9";
+
+ char phaxfile[FLEN_VALUE]={'\0'};
+ int anynull=0, calfvers, errflg=0, status=0, xlen, ylen;
+ long j;
+ float *phax, flt=0.;
+ fitsfile *phaxfits=NULL;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ /*
+ * Read PHA correction (Walk) file
+ */
+ FITS_read_key(header, TSTRING, "PHAX_CAL", phaxfile, NULL, &status);
+ FITS_open_file(&phaxfits, cf_cal_file(phaxfile), READONLY, &status);
+ FITS_read_key(phaxfits, TINT, "NAXIS1", &xlen, NULL, &status);
+ FITS_read_key(phaxfits, TINT, "NAXIS2", &ylen, NULL, &status);
+
+ /* If CALFVERS keyword missing or less than 4, abort pipeline. */
+ fits_read_key(phaxfits, TINT, "CALFVERS", &calfvers, NULL, &status);
+ if (status || calfvers < 4) {
+ status = 0;
+ FITS_close_file(phaxfits, &status);
+ cf_if_error("Old versions of PHAX_CAL have wrong sign. Aborting.");
+ }
+
+ /* Otherwise, read walk correction as an image. */
+ phax = (float *) cf_malloc(sizeof(float)*xlen*ylen);
+ FITS_read_img(phaxfits, TFLOAT, 1L, xlen*ylen, &flt, phax, &anynull, &status);
+ FITS_close_file(phaxfits, &status);
+
+ if (xlen != NXMAX)
+ cf_if_warning("NAXIS1 of %s != 16384", phaxfile);
+ /*
+ * Apply PHA X correction to each event.
+ */
+ for(j=0; j<nevents; j++) {
+ /*
+ * Move only events in active region.
+ */
+ if (!(locflags[j] & LOCATION_SHLD)) {
+ xfarf[j] += phax[(int)(pha[j]*xlen + xfarf[j])];
+ }
+ }
+ free(phax);
+
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return 0;
+}
diff --git a/src/libcf/cf_proc_check.c b/src/libcf/cf_proc_check.c
new file mode 100644
index 0000000..0dd2c72
--- /dev/null
+++ b/src/libcf/cf_proc_check.c
@@ -0,0 +1,124 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_proc_check(fitsfile *fptr, char *prog_id)
+ *
+ * Description: cf_proc_check will determine whether a given calibration
+ * step is to be performed on the data.
+ *
+ * Arguments: fitsfile *fptr Pointer to input file
+ * char *prog_id Procedure name
+ *
+ * History: 05/21/98 emm Begin work.
+ * 05/21/98 emm finished
+ * 06/17/98 emm Modified so that the keyword_tab
+ * structure is read from ed_calfuse.h
+ * file, which simplifies revisions in
+ * the order of processing.
+ * 03/04/99 emm I added use of errflg instead of
+ * return so that it checks all parms.
+ * 04/01/03 wvd Change fits_read_key_str to
+ * FITS_read_key, delete fits_print_err
+ * 05/20/03 rdr remove part which checks previous steps
+ * 05/22/03 v1.6 wvd Modify i/o; implement cf_verbose
+ * 07/23/03 v1.7 wvd Modify i/o
+ * 09/10/03 v1.8 wvd Bug fix: was printing HIST values
+ * whenever called with verbose option.
+ * Now returns TTAG when appropriate.
+ * 03/17/04 v1.9 wvd Call cf_verbose rather than
+ * cf_if_warning when program not
+ * appropriate for HIST data.
+ * 06/02/04 v1.10 wvd Call cf_verbose rather than
+ * cf_if_warning when program not
+ * appropriate for TTAG data.
+ * 11/26/04 v1.11 wvd If header keyword is set to OMIT, skip
+ * this step and set keyword to SKIPPED.
+ * 03/30/05 v1.12 wvd If header keyword is set to SKIPPED,
+ * skip this step.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include "calfuse.h"
+
+
+int cf_proc_check(fitsfile *fptr, char *prog_id)
+{
+ char comment[FLEN_CARD], instmode[FLEN_CARD];
+ char key_value[FLEN_CARD];
+ int j=0, errflg=0, status=0;
+
+ /* The calfuse.h file contains the definitions of keyword_tab,
+ * NUM_PROC_STEPS, and CALIBRATION_STEP_KEYS.
+ */
+ struct keyword_tab keytab[NUM_PROC_STEPS]=CALIBRATION_STEP_KEYS;
+
+ FITS_read_key(fptr, TSTRING, "INSTMODE", instmode, comment, &status);
+
+ switch (instmode[0]) {
+ case 'H':
+ /* Histogram mode */
+ /* First, determine if this procedure is even supposed to be
+ * run on this data. */
+
+ while ((strncmp(keytab[j].hist_proc,prog_id,
+ strlen(keytab[j].hist_proc)) != 0) &&
+ (j < NUM_PROC_STEPS)) j++;
+ if (j < NUM_PROC_STEPS)
+ cf_verbose(3, "cf_proc_check: prog_id=%s, key=%s, j=%d, value=%s",
+ prog_id, keytab[j].hist_proc, j, keytab[j].hist_value) ;
+ if ((j >= NUM_PROC_STEPS) ||
+ (strncmp(keytab[j].hist_value, "PERFORM", 7))) {
+ cf_verbose(1, "Not appropriate for HIST data. Exiting.");
+ errflg=1;
+ }
+
+ break;
+
+ case 'T':
+ /* Time-tag mode */
+ /* First, determine if this procedure is even supposed to be
+ * run on this data. */
+ while ((strncmp(keytab[j].ttag_proc,prog_id,
+ strlen(keytab[j].ttag_proc)) != 0) &&
+ (j < NUM_PROC_STEPS)) j++;
+ if (j < NUM_PROC_STEPS)
+ cf_verbose(3, "cf_proc_check: prog_id=%s, key=%s, j=%d, value=%s",
+ prog_id, keytab[j].ttag_proc, j, keytab[j].ttag_value) ;
+ if ((j >= NUM_PROC_STEPS) ||
+ (strncmp(keytab[j].ttag_value, "PERFORM", 7))) {
+ cf_verbose(1, "Not appropriate for TTAG data. Exiting.");
+ errflg=1;
+ }
+
+ break;
+
+ default:
+ errflg = 1;
+ }
+
+ if (!errflg) {
+ /* Check to see whether user wants to skip this step. */
+ FITS_read_key(fptr, TSTRING, keytab[j].name, key_value, comment,
+ &status);
+ if (!strncmp(key_value, "OMIT", 4) || !strncmp(key_value, "SKIPPED", 7)) {
+ cf_verbose(1, "Header keyword %s = %s. Exiting.",
+ keytab[j].name, key_value);
+ FITS_update_key(fptr, TSTRING, keytab[j].name, "SKIPPED", NULL,
+ &status);
+ errflg=1;
+ }
+
+ /* Now check to see if the step has already been completed. */
+ if (!strncmp(key_value, "COMPLETE", 7)) {
+ cf_if_warning("Exiting.\n\t%s has already been run on this file.",
+ prog_id);
+ errflg=1;
+ }
+ }
+ return errflg;
+}
diff --git a/src/libcf/cf_proc_update.c b/src/libcf/cf_proc_update.c
new file mode 100644
index 0000000..2e15d29
--- /dev/null
+++ b/src/libcf/cf_proc_update.c
@@ -0,0 +1,97 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_proc_update(fitsfile *fptr, char *prgm_id, char *key_value)
+ *
+ * Description: cf_proc_update will update the FITS header keyword
+ * which corresponds to the given prgm_id in the file
+ * fptr to the value given in key_value.
+ *
+ * Arguments: fitsfile *fptr Pointer to input file
+ * char *prgm_id Procedure name
+ * char *key_value Updated procedure status
+ *
+ * History: 05/21/98 emm Begin work.
+ * 05/21/98 emm finished
+ * 06/17/98 emm Modified so that the keyword_tab
+ * structure is read from ed_calfuse.h
+ * file, which simplifies revisions in
+ * the order of processing.
+ * 09/08/99 peb Added lines to update NEXTEND keyword
+ * 01/31/00 emm Added update to DATE keyword.
+ * 04/01/03 wvd Changed cf_errmsg to cf_if_warning,
+ * fits_modify_key_str to FITS_update_key,
+ * fits_read_key_str to FITS_read_key
+ * 12/18/03 bjg Change calfusettag.h to calfuse.h
+ * 04/07/07 1.5 wvd Delete CF_PRGM_ID, as it is not used.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include "calfuse.h"
+
+int cf_proc_update(fitsfile *fptr, char *prgm_id, char *key_value)
+{
+
+ char comment[FLEN_CARD], instmode[FLEN_CARD];
+ int i, status=0, hdutype=0, nextend;
+ /*
+ * The calfuse.h file contains the definitions of keyword_tab
+ * NUM_PROC_STEPS, and CALIBRATION_STEP_KEYS.
+ */
+ struct keyword_tab keytab[NUM_PROC_STEPS]=CALIBRATION_STEP_KEYS;
+
+ FITS_movabs_hdu(fptr, 1, &hdutype, &status);
+ FITS_get_num_hdus(fptr, &nextend, &status);
+ nextend -= 1;
+ FITS_update_key(fptr, TINT, "NEXTEND", &nextend, NULL, &status);
+
+ FITS_read_key(fptr, TSTRING, "INSTMODE", instmode, comment, &status);
+
+ FITS_write_date(fptr, &status);
+
+ switch (instmode[0]) {
+ case 'H':
+ /* Find the keyword associated with prgm_id by looping
+ * through keytab[i].hist_proc */
+ i=0;
+ while ((strncmp(keytab[i].hist_proc,prgm_id,
+ strlen(keytab[i].hist_proc)) != 0) &&
+ (i < NUM_PROC_STEPS)) i++;
+ if (i < NUM_PROC_STEPS) {
+ /* We found a match to prgm_id, so change the associated
+ * keyword in the header. */
+ FITS_update_key(fptr, TSTRING,keytab[i].name,key_value, NULL, &status);
+ } else {
+ /* The given prgm_id did not match any of the known
+ * keytab[i].hist_proc, so return 1. */
+ cf_if_warning("No PROCESSING STEP keyword is defined for %s", prgm_id);
+ return 1;
+ }
+ break;
+ case 'T':
+ /* Timetag mode */
+ /* Find the keyword associated with prgm_id by looping
+ * through keytab[i].ttag_proc */
+ i=0;
+ while ((strncmp(keytab[i].ttag_proc,prgm_id,
+ strlen(keytab[i].ttag_proc)) != 0) &&
+ (i < NUM_PROC_STEPS)) i++;
+ if (i < NUM_PROC_STEPS) {
+ /* We found a match to prgm_id, so change the associated
+ * keyword in the header. */
+ FITS_update_key(fptr, TSTRING,keytab[i].name,key_value, NULL, &status);
+ } else {
+ /* The given prgm_id did not match any of the known
+ * keytab[i].ttag_proc, so return 1. */
+ cf_if_warning("No PROCESSING STEP keyword is defined for %s", prgm_id);
+ return 1;
+ }
+ break;
+ }
+ return status;
+}
diff --git a/src/libcf/cf_read_fpa_pos.c b/src/libcf/cf_read_fpa_pos.c
new file mode 100644
index 0000000..87e8099
--- /dev/null
+++ b/src/libcf/cf_read_fpa_pos.c
@@ -0,0 +1,82 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_read_fpa_pos (fitsfile *infits, float *fpaxsic, float *fpaxlif)
+ *
+ * Description: Returns SiC and LiF FPA positions in microns. Can read both
+ * old and new FPA position keywords.
+ *
+ * Arguments: fitsfile *infile Input data file name
+ * float *fpaxsic FPA SiC position (returned)
+ * float *fpaxlif FPA LiF position (returned)
+ *
+ * Returns: int status CFITSIO status value
+ *
+ * History: 08/21/01 v1.1 wvd Subroutine taken from cf_wfits.c
+ * 01/14/03 v1.3 wvd Change name to cf_read_fpa_pos.
+ * 12/18/03 v1.4 bjg Change calfusettag.h to calfuse.h
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include "calfuse.h"
+
+/* for all FPA's an increase in "data number" moves the FPA towards +Xipcs */
+#define FPALIF1DN2X0 -69.949 /* DN to microns conversion, zero-point */
+#define FPALIF1DN2X1 .14144 /* DN to microns conversion, linear term */
+#define FPALIF2DN2X0 -73.189 /* DN to microns conversion, zero-point */
+#define FPALIF2DN2X1 .14487 /* DN to microns conversion, linear term */
+#define FPASIC1DN2X0 -132.507 /* DN to microns conversion, zero-point */
+#define FPASIC1DN2X1 .14156 /* DN to microns conversion, linear term */
+#define FPASIC2DN2X0 -89.116 /* DN to microns conversion, zero-point */
+#define FPASIC2DN2X1 .14206 /* DN to microns conversion, linear term */
+
+int cf_read_fpa_pos (fitsfile *infits, float *fpaxsic, float *fpaxlif)
+{
+ char detector[FLEN_VALUE]; /* detector keyword from FITS header */
+ int status=0;
+ float tmpxsic, tmpxlif;
+
+ FITS_read_key(infits, TSTRING, "DETECTOR", detector, NULL, &status);
+
+ /* Get FPA positions */
+ /* Use ffgky instead of FITS_read_key so we can read either the old
+ * or new FPA position keywords */
+ ffgky (infits, TFLOAT, "FPASXPOS", fpaxsic, NULL, &status);
+ if (status != 0) {
+ /* New keywords are not present; must be the old ones.
+ * Determine which detector provided current spectrum
+ * and convert raw FPA position to microns.
+ */
+ status = 0;
+ if (strncmp(detector,"1", 1)==0) {
+ FITS_read_key (infits, TFLOAT, "FP1SXPOS", &tmpxsic, NULL,
+ &status);
+ FITS_read_key (infits, TFLOAT, "FP1LXPOS", &tmpxlif, NULL,
+ &status);
+ *fpaxsic = FPASIC1DN2X0 + FPASIC1DN2X1 * tmpxsic;
+ *fpaxlif = FPALIF1DN2X0 + FPALIF1DN2X1 * tmpxlif;
+ }
+ else if (strncmp(detector,"2", 1)==0) {
+ FITS_read_key (infits, TFLOAT, "FP2SXPOS", &tmpxsic, NULL,
+ &status);
+ FITS_read_key (infits, TFLOAT, "FP2LXPOS", &tmpxlif, NULL,
+ &status);
+ *fpaxsic = FPASIC2DN2X0 + FPASIC2DN2X1 * tmpxsic;
+ *fpaxlif = FPALIF2DN2X0 + FPALIF2DN2X1 * tmpxlif;
+ }
+ else {
+ cf_if_error("Could not parse DETECTOR keyword, cannot "
+ "convert raw FPA positions.");
+ }
+ }
+ else {
+ /* New SiC FPA keyword present. Assume new LiF FPA keyword
+ * is also present */
+ FITS_read_key(infits, TFLOAT, "FPALXPOS", fpaxlif, NULL, &status);
+ }
+ return status;
+}
diff --git a/src/libcf/cf_rebin_background.c b/src/libcf/cf_rebin_background.c
new file mode 100644
index 0000000..23e15f3
--- /dev/null
+++ b/src/libcf/cf_rebin_background.c
@@ -0,0 +1,211 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_rebin_background(infits, aperture, nout, wave_out,
+ * binx, biny, bnx, bny, bimage, barray)
+ *
+ * Arguments: *fitsfile infits : Input IDF file
+ * int aperture : Aperture ID for the analysis
+ * long nout : Length of wave_out array
+ * float wave_out : Wavelength vector
+ * int binx, biny : Binning factors of the background array
+ * int bnx, bny : X and Y dimension of the background array
+ * float bimage : 2-D background image (original)
+ * float barray : (OUTPUT) Binned background image.
+ *
+ * Description:
+ *
+ * The input background image is binned in X.
+ * We need an image binned in wavelength.
+ * To get it, we use the WAVE_CAL file (shifted to account
+ * for the FPA position) to derive a wavelength array for the
+ * background image. For each row in the background image (bimage),
+ * we build a second image (barray) scaled to the size of the output
+ * wavelength bins.
+ *
+ * History 02/21/03 v1.1 rdr Started work
+ * 04/01/03 v1.2 rdr Made aeff arrays double precision in
+ * the cf_read_fluxcal routine to be
+ * compatible with data in calfiles
+ * 05/07/03 v1.4 wvd Don't divide by EXPTIME.
+ * Read WPC from file header.
+ * Use cf_verbose.
+ * 06/02/03 v1.5 rdr Correct bug in filling output arrays
+ * 06/11/03 v1.7 wvd Pass datatype to cf_read_col.
+ * Change calfusettag.h to calfuse.h
+ * 08/22/03 v1.8 bjg Move get_extraction_limits to external
+ * subroutine. Free orphan arrays.
+ * Change coltype from char to int in
+ * cf_read_col.
+ * 08/28/03 v1.9 wvd Debug
+ * 09/29/03 v1.10 wvd Shift background model to match
+ * position of FPA.
+ * 03/16/04 v1.11 wvd Correct error in calculation of dw_ave
+ * and dweff.
+ * 04/05/04 v1.12 wvd Modify i/o.
+ * 04/26/04 v1.13 wvd Do not flux-calibrate 2-D background
+ * model. Do not extract 1-D background
+ * spectrum.
+ * 05/13/04 v1.14 wvd Correct value of CF_PRGM_ID.
+ * 07/21/04 v1.16 wvd Delete unused variables.
+ *
+ *****************************************************************************/
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "calfuse.h"
+
+
+int cf_rebin_background(fitsfile *infits, int aperture, long nout,
+ float *wave_out, int binx, int biny, int bnx, int bny,
+ float *bimage, float **barray) {
+
+ char CF_PRGM_ID[] = "cf_rebin_background";
+ char CF_VER_NUM[] = "1.16";
+
+ fitsfile *wavefits ;
+ int status=0;
+ int dndx, dpix, nyout;
+ long npix, npix_out, nwave, ndx, ndx1, ndx2;
+ long i, j, k;
+ float dwb, dw_out, mf, ctot, dw_ave;
+ float dpix_fpa;
+ float *wave, *wavet, *outarr;
+ char wavefile[FLEN_VALUE], det[FLEN_VALUE] ;
+
+ /* Initialize error checking */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Started execution.");
+
+ /* Read wavelength spacing of the output array */
+ FITS_read_key(infits, TFLOAT, "WPC", &dw_out, NULL, &status) ;
+ cf_verbose(3, "Bin size of the output wavelength array = %5.3f A",
+ dw_out) ;
+ FITS_read_key(infits, TSTRING, "DETECTOR", det, NULL, &status) ;
+ cf_verbose(3,"Detector = %s, aperture = %d", det, aperture) ;
+
+ /* Read wavelength-calibration file */
+ FITS_read_key(infits, TSTRING, "WAVE_CAL", wavefile, NULL, &status) ;
+ cf_verbose(3, "Wavelength calibration file = %s",wavefile) ;
+ FITS_open_file(&wavefits, cf_cal_file(wavefile), READONLY, &status) ;
+ cf_verbose(3, "HDU to read = %d",aperture+1) ;
+ FITS_movabs_hdu(wavefits, aperture +1, NULL, &status) ;
+ nwave=cf_read_col(wavefits,TFLOAT,"WAVELENGTH",(void **) &wave);
+ cf_verbose(3, "Points read = %d, min & max: %4.2f, %4.2f",
+ nwave, wave[0], wave[nwave-1]);
+
+ /* Read pixel shifts due to FPA positions */
+ if (aperture < 5)
+ FITS_read_key(infits, TFLOAT, "FPADXLIF", &dpix_fpa, NULL, &status);
+ else
+ FITS_read_key(infits, TFLOAT, "FPADXSIC", &dpix_fpa, NULL, &status);
+ dpix = cf_nint(dpix_fpa);
+
+ /* To correct for the position of the FPA, each photon has been shifted
+ in X by dpix pixels. Rather than shifting the background to match,
+ we modify the wavelength array that maps the background onto the
+ output spectrum. */
+ cf_verbose(2, "Shifting aperture %d background by %d X pixels "
+ "to match the FPA shift.", aperture, cf_nint(dpix_fpa));
+
+ /* Generate a new wavelength array binned to match the background array */
+ cf_verbose(3, "X and Y binning factors for background array = %d, %d",
+ binx, biny) ;
+
+ wavet = (float *) cf_calloc(bnx, sizeof(float));
+ for (i = 0; i < bnx; i++) {
+ ndx=i * binx + (binx/2) + dpix;
+ if (ndx > nwave-1) ndx = nwave-1 ;
+ if (ndx < 0) ndx = 0;
+ wavet[i] = wave[ndx];
+ }
+
+ /* Error check: average wavelength spacing */
+ dw_ave = fabs(wavet[bnx-1]-wavet[0])/(bnx-1);
+ cf_verbose(3,"Average wavelength spacing of background array = %f", dw_ave) ;
+
+ if ((aperture < 4 && strncmp(det,"1",1) == 0) ||
+ (aperture > 4 && strncmp(det,"2",1) == 0)) {
+ cf_verbose(3, "Maximum tabulated wavelength = %.2f", wavet[bnx-1]);
+ cf_verbose(3, "Maximum of wave_out = %.2f", wave_out[nout-1] );
+ }
+ else {
+ cf_verbose(3, "Maximum tabulated wavelength = %.2f", wave_out[nout-1] );
+ cf_verbose(3, "Maximum of wave_out = %.2f", wave_out[nout-1] );
+ }
+
+ /* Define some sizes and allocate space for the output arrays */
+ nyout = bny * biny ; /* Y dimension of output 2-D bkgd array */
+ npix_out = nout * nyout ; /* Size of output 2-D bkgd array */
+ npix = bnx * bny ; /* Size of input 2-D bkgd array */
+ outarr = (float *) cf_calloc(npix_out, sizeof(float) ); /* 2-D output array */
+
+ /* Error check: total counts in the background image */
+ ctot=0.;
+ for (i=0; i<npix; i++) ctot+=bimage[i] ;
+ cf_verbose(3, "Number of counts in the input bkgd image = %f",ctot) ;
+
+ /* For each wavelength in the output array, find the nearest point
+ in the input background array. Scale its value by the relative
+ sizes of the background and output wavelength bins.
+
+ Remember: wavelengths for the Lif channels and the SiC channels run
+ in opposite directions. For side one, wavelengths increase
+ with index for Lif but decrease for SiC. The opposite is true
+ for side two. */
+ ndx=0 ;
+ dndx = 1 ;
+ if ((aperture < 4 && strncmp(det,"2",1) == 0) ||
+ (aperture > 4 && strncmp(det,"1",1) == 0 ) ) {
+ ndx = bnx - 1 ;
+ dndx = -1 ;
+ }
+
+ cf_verbose(3,"Filling output: starting ndx=%d, increment =%d ", ndx, dndx) ;
+
+ dwb = dw_ave;
+ for (i=0; i<nout; i++) {
+ /* Locate the element of the bkgd array corresponding to this wavelength */
+ while (wavet[ndx] < wave_out[i] && ndx < bnx && ndx >= 0) ndx += dndx ;
+
+ /* compute the relative
+ wavelength coverage of the background and output arrays */
+ if (ndx < bnx-2) dwb = fabs((double) (wavet[ndx+1]-wavet[ndx])) ;
+ mf = dw_out/dwb ;
+ /* correct the scale factor for binning in y */
+ mf /= biny ;
+ /* populate the column of the array */
+ for (j=0; j< nyout ; j++) {
+ k = j/biny ;
+ ndx1 = j*nout + i ;
+ if (ndx1 > npix_out-1) ndx1 = npix_out-1 ;
+ ndx2 = k*bnx + ndx ;
+ if (ndx2 > npix-1) ndx2=npix-1 ;
+ outarr[ndx1] = mf * bimage[ndx2] ;
+ }
+ }
+
+
+ /* Error checking: total counts in the background image */
+ ctot=0.;
+ for (i=0; i<npix_out; i++) ctot+=outarr[i] ;
+ cf_verbose(3, "Number of counts in the output bkgd image = %f", ctot) ;
+
+
+ /* Return output arrays */
+ *barray = outarr ;
+
+ /* Close wavelength calibration file */
+ FITS_close_file(wavefits, &status) ;
+
+ free(wave);
+ free(wavet);
+
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished execution.");
+ return status;
+}
diff --git a/src/libcf/cf_rebin_probability_array.c b/src/libcf/cf_rebin_probability_array.c
new file mode 100644
index 0000000..285441e
--- /dev/null
+++ b/src/libcf/cf_rebin_probability_array.c
@@ -0,0 +1,180 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_rebin_probability_array(infits, extended, aperture, nout,
+ * wave_out, pny, pycent, parray)
+ *
+ * Arguments: *fitsfile infits : Input IDF file
+ * int extended : TRUE if target is extended
+ * int aperture : Aperture ID for the analysis
+ * long nout : number of output wavelength bins
+ * float wave_out : output wavelength vector
+ * int pny : y dimension of probability array
+ * float pycent : y position of the peak of the
+ * probability array
+ * float parray : probability array
+ *
+ * History: 02/20/03 v1.1 rdr Started work
+ * 04/03/03 v1.2 rdr Normalize so that integral along y
+ * at each sample point is 1
+ * 06/02/03 v1.3 rdr Correct error in filling output array
+ * 09/30/03 v1.5 wvd Use cf_read_col to read WAVE_CAL.
+ * 03/22/04 v1.6 wvd Change pycent from int to float.
+ * 03/23/04 v1.7 wvd Shift weights array to account for
+ * FPA shifts.
+ * 06/14/04 v1.8 wvd Because the weights array is defined
+ * wrt the FARF, we need not correct
+ * for FPA shifts here.
+ * 05/17/05 v1.9 wvd Don't normalize regions where the
+ * total probability is less than 0.1.
+ * Set them to zero.
+ * 06/15/05 v1.10 wvd BUG FIX: Program always read the
+ * probability arrays for point-source
+ * targets from the WGTS_CAL file. Now
+ * it distinguishes between point-source
+ * and extended targets.
+ *
+ *****************************************************************************/
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "calfuse.h"
+
+int cf_rebin_probability_array(fitsfile *infits, int extended, int aperture,
+ long nout, float *wave_out, int *pny, float *pycent, float **parray)
+{
+
+ char CF_PRGM_ID[] = "cf_rebin_probability_array";
+ char CF_VER_NUM[] = "1.10";
+
+ fitsfile *wgtsfits, *wavefits ;
+ int status=0, fpixel=1, nullval=0, anynull=0 ;
+ int hdu, nxwt, nywt, dndx, wtbin;
+ long npix, npix_out, nwave, ndx, ndx1, ndx2, i, j ;
+ float *wave, *wavet, *wgts_arr, *ynorm, *outarr ;
+ float total, wgt_cent ;
+ char wgtsfile[FLEN_VALUE]={'\0'}, wavefile[FLEN_VALUE]={'\0'} ;
+ char buffer[FLEN_CARD], det[FLEN_CARD] ;
+
+ /* Initialize error checking */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Started execution.");
+
+ /* Read calibration file names from the IDF file header. */
+ FITS_movabs_hdu(infits, 1, NULL, &status) ;
+ FITS_read_key(infits, TSTRING, "WGTS_CAL", wgtsfile, NULL, &status) ;
+ cf_verbose(3,"weights cal file = %s ",wgtsfile) ;
+ FITS_read_key(infits, TSTRING, "WAVE_CAL", wavefile, NULL, &status) ;
+ cf_verbose(3, "wavelength cal file = %s ",wavefile) ;
+ FITS_read_key(infits, TSTRING, "DETECTOR", det, NULL, &status) ;
+ cf_verbose(3,"Detector = %s ",det) ;
+
+ /* Open the weights file and read the array */
+ FITS_open_file(&wgtsfits, cf_cal_file(wgtsfile), READONLY, &status) ;
+ hdu = extended * 8 + aperture + 1;
+ cf_verbose(3, "WGTS_CAL: extended = %d, aperture = %d, hdu = %d",
+ extended, aperture, hdu);
+ FITS_movabs_hdu(wgtsfits, hdu, NULL, &status) ;
+ FITS_read_key(wgtsfits, TINT, "NAXIS1", &nxwt, buffer, &status) ;
+ FITS_read_key(wgtsfits, TINT, "NAXIS2", &nywt, buffer, &status) ;
+ FITS_read_key(wgtsfits, TFLOAT, "WGT_CENT", &wgt_cent, buffer, &status) ;
+ npix=nxwt * nywt ;
+ cf_verbose(3, "prob array: nx=%d, ny=%d, wgt_cent=%f ",nxwt, nywt, wgt_cent);
+ wgts_arr = (float *) calloc(npix, sizeof(float)) ;
+ FITS_read_img(wgtsfits, TFLOAT, fpixel, npix, &nullval, wgts_arr,
+ &anynull, &status) ;
+ FITS_close_file(wgtsfits, &status) ;
+
+ /* Read the wavelength array */
+ FITS_open_file(&wavefits, cf_cal_file(wavefile), READONLY, &status) ;
+ FITS_movabs_hdu(wavefits, aperture +1, NULL, &status) ;
+ nwave=cf_read_col(wavefits,TFLOAT,"WAVELENGTH", (void **) &wave);
+ FITS_close_file(wavefits, &status) ;
+
+ /* Determine the binning factor for the weights and generate a new
+ wavelength array that corresponds to the binning factor used. */
+ wtbin = NXMAX / nxwt ;
+ cf_verbose(3, "Binning factor for weights array = %d ", wtbin) ;
+ wavet = (float *) cf_calloc(nxwt, sizeof(float) ) ;
+ for (i=0; i<nxwt; i++) {
+ ndx=i * wtbin + (wtbin/2);
+ if (ndx > nwave-1) ndx = nwave-1 ;
+ if (ndx < 0) ndx = 0 ;
+ wavet[i] = wave[ndx] ;
+ }
+
+ /* Allocate space for the output array */
+ npix_out = nout * nywt ;
+ if ((aperture < 4 && strncmp(det,"1",1) == 1) ||
+ (aperture > 4 && strncmp(det,"2",1) == 1))
+ cf_verbose(3, "maximum tabulated wavelength = %g, "
+ "maximum of wave_out=%g", wavet[nxwt-1],wave_out[nout-1] );
+ else
+ cf_verbose(3, "maximum tabulated wavelength = %g, "
+ "maximum of wave_out=%g", wavet[0],wave_out[nout-1] );
+ outarr = (float *) cf_calloc(npix_out, sizeof(float)) ;
+
+ /* Fill in the output array - use the weight profile which is closest
+ to the individual wavelengths in the output wavelength array.
+ Remember : the Lif channels and the SiC channels have wavelengths
+ that run in opposite directions, e.g. for side 1 wavelengths
+ increase with index for Lif but decrease for SiC. The opposite is
+ true for side 2. */
+
+ ndx=0 ;
+ dndx = 1 ;
+ if ((aperture < 4 && strncmp(det,"2",1) == 0) ||
+ (aperture > 4 && strncmp(det,"1",1) == 0 ) ) {
+ ndx = nxwt - 1 ;
+ dndx = -1 ;
+ }
+
+ cf_verbose(3, "filling output: starting index = %d, increment = %d ",
+ ndx, dndx) ;
+
+ for (i=0; i<nout; i++) {
+ /* locate the wavelength corresponding to this entry */
+ while (wavet[ndx] < wave_out[i] && ndx <= nxwt-1 && ndx >= 0 )
+ ndx += dndx ;
+ /* populate the column of the array */
+ for (j=0; j<nywt ; j++) {
+ ndx1 = j*nout + i ;
+ if (ndx1 > npix_out-1) ndx1 = npix_out-1 ;
+ ndx2 = j*nxwt + ndx ;
+ if (ndx2 > npix-1) ndx2=npix-1 ;
+ outarr[ndx1] = wgts_arr[ndx2] ;
+ }
+ }
+
+ /* Adjust the probabilities so that the integral along y at every
+ wavelength position is 1 */
+
+ /* First sum the points along y (ynorm) at each point */
+ ynorm= (float *) cf_calloc(nout, sizeof(float)) ;
+ for (i=0; i<nout; i++) {
+ total=0. ;
+ for (j=0; j<nywt; j++) total += outarr[j*nout+i] ;
+ ynorm[i]=total ;
+ }
+
+ /* Normalize all points by dividing by the total */
+ for (i=0; i<nout; i++) {
+ if (ynorm[i] > 0.1)
+ for (j=0; j<nywt; j++) outarr[j*nout+i] /= ynorm[i] ;
+ else
+ for (j=0; j<nywt; j++) outarr[j*nout+i] = 0. ;
+ }
+
+ /* Redirect the output pointers. */
+ *pny = nywt ;
+ *pycent = wgt_cent ;
+ *parray = outarr ;
+
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished execution.");
+ return status;
+}
diff --git a/src/libcf/cf_satellite_jitter.c b/src/libcf/cf_satellite_jitter.c
new file mode 100644
index 0000000..9f3a5d9
--- /dev/null
+++ b/src/libcf/cf_satellite_jitter.c
@@ -0,0 +1,245 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center for Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_satellite_jitter infile, nevents, time, x, y, channel,
+ * nsec, ttime, status_flag)
+ *
+ * Description: Correct photon coordinates for spacecraft motion.
+ *
+ * Operates only on point-source observations made in TTAG mode.
+ * Only photon events in the target aperture are corrected.
+ * Minimum trustworthy TRKFLG value is read from parameter file.
+ *
+ * Key to new TRKFLG values:
+ * 5 = dx, dy from known FPDs and q_cmd from telemetry
+ * 4 = good dx, dy from ACS q_est and q_cmd from telemetry
+ * 3 = maybe good dx, dy from ACS q_est
+ * 2 = dx, dy from known FPDs, but q_cmd from FITS header coords
+ * 1 = dx, dy from ACS q_est, but q_cmd from FITS header coordis
+ * 0 = no pointing information (missing telemetry)
+ * -1 = pointing is assumed to be bad (never achieved known track)
+ *
+ * Arguments: fitsfile infile Pointer to the IDF
+ * long nevents The number of events
+ * float *time An array of event times
+ * float *x An array of event X positions
+ * float *y An array of event Y positions
+ * unsigned char *channel Channel assignment for each photon
+ * long nsec Number of seconds in the timeline
+ * long *ttime time (sec) of each point in the timeline
+ * unsigned char *status_flag Timeline status flags
+ *
+ * Returns: 0 upon success
+ *
+ * History: 09/12/02 v1.1 RDR Adapted from cf_ttag_jitter
+ * 10/02/02 v1.2 RDR Modified to be compatable with
+ * new IDF file format
+ * 12/10/02 v1.3 RDR Adjusted method of populating the
+ * timeline jitter status flag.
+ * 12/13/02 v1.4 RDR Exit gracefully if there is no jitr file
+ * 03/01/03 v1.6 wvd Correct use of pointer in FITS_read_key
+ * 04/07/03 v1.7 wvd Confirm that TRKFLG <= 5.
+ * Replace printf with cf_verbose.
+ * 04/14/03 v1.8 rdr Changed flag from char to unsigned char
+ * 04/22/03 v1.9 wvd Move only photons in target aperture.
+ * Do not assume that ttime is continuous.
+ * 04/29/03 v1.10 wvd Allow possiblity that jitter
+ * filename is in lower-case letters.
+ * 05/20/03 v1.11 rdr Added call to cf_proc_check
+ * 05/22/03 v1.12 wvd Direct cf_error_init to stderr
+ * 07/11/03 v1.13 rdr Set all points as bad if JIT_STAT=1
+ * (no reference position found)
+ * 08/06/03 v1.14 wvd Improve documentation, delete
+ * comparison with GTI's, change channel
+ * array to unsigned char.
+ * 08/29/03 v1.15 wvd Set all points as bad if JIT_STAT != 0
+ * 09/18/03 v1.16 wvd Print name of jitter file only if
+ * the file exists.
+ * 10/03/03 v1.17 wvd Check HKEXISTS before looking for
+ * jitter file.
+ * 02/12/04 v1.18 wvd Make interpolation immune
+ * to errors in jitter time array.
+ * 02/24/04 v1.19 rdr Change limits of acceptable jitter
+ * values
+ * 03/01/04 v1.20 rdr Populate the PLATESC keyword in the
+ * input file header
+ * 06/01/04 v1.21 bjg Exit when keyword JIT_STAT not found.
+ * 06/07/04 v1.22 wvd Return EXP_JITR to calling routine.
+ * 01/26/05 v1.23 wvd Fix tiny bug in a verbose stmt.
+ * 04/05/05 v1.24 wvd If JIT_STAT != 0, APERTURE = RFPT,
+ * or target = airglow, exit without
+ * flagging any times as bad.
+ * Use new TRKFLG values (see above).
+ * Delete adjust_trkflg subroutine and
+ * the J_TRKFLG and JTIMEGAP keywords.
+ * Use cf_read_col to read jitter file.
+ * 07/06/05 v1.25 wvd If JIT_VERS < 2.0, issue a warning.
+ * 11/29/05 v1.26 wvd Move screening functions into
+ * cf_screen_jitter.
+ * 08/21/06 v1.27 wvd Use EXPSTART in jitter and data
+ * headers to correct jitter time array.
+ * 08/25/06 v1.28 wvd Change meaning of TRKFLG values.
+ * TRKFLG = 3 is only used internally.
+ * 11/06/06 v1.29 wvd Change meaning of TRKFLG values.
+ * Read APER_COR keyword in file header.
+ * Read min TRKFLG value from parmfile.
+ * Don't have to test observing mode.
+ * 03/23/07 v1.30 wvd Store offset between the jitter and
+ * data values of EXPSTART in variable
+ * time_diff.
+ * 04/07/07 v1.31 wvd Clean up compiler warnings.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "calfuse.h"
+
+int
+cf_satellite_jitter(fitsfile *infits, long nevents, float *time,
+ float *x, float *y, unsigned char *channel, long nsec,
+ float *ttime, unsigned char *status_flag)
+{
+ char CF_PRGM_ID[] = "cf_satellite_jitter";
+ char CF_VER_NUM[] = "1.31";
+
+ fitsfile *jitfits, *parmfits;
+ char det[FLEN_VALUE];
+ char jitr_cal[FLEN_VALUE], parmfile[FLEN_VALUE];
+ char comment[FLEN_COMMENT];
+ short c, *trkflg, trkflg_min;
+ int active_ap[2], extended, status=0, hdutype;
+ long j, k, njitr, nrej, *time_jit, time_diff;
+ double data_expstart, jitr_expstart;
+ float *dx_jit, *dy_jit, x_factor, y_factor;
+ float *dx_tt, *dy_tt;
+ float factor[2][4] = {{1.743,1.743,-1.743, -1.743},
+ {1.154,1.163,-0.6335,-0.581}};
+
+ /* Initialize error checking */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+
+ /* Enter a time stamp into the log */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ /* Read exposure start time from IDF header */
+ FITS_read_key(infits, TDOUBLE, "EXPSTART", &data_expstart, NULL, &status);
+
+ /* If APER_COR is not set to "COMPLETE", exit now. */
+ FITS_read_key(infits, TSTRING, "APER_COR", comment, NULL, &status);
+ if (strncmp(comment, "COMPLETE", 8)) {
+ cf_verbose(1, "APER_COR = %s. Omitting photon shift.", comment);
+ cf_proc_update(infits, CF_PRGM_ID, "SKIPPED");
+ return (status);
+ }
+
+ /* If target is an extended source, exit now. */
+ extended = cf_source_aper(infits, active_ap);
+ if (extended) {
+ cf_verbose(1, "Extended source. Omitting photon shift.");
+ cf_proc_update(infits, CF_PRGM_ID, "SKIPPED");
+ return (status);
+ }
+
+ /* Determine the scale factor to convert arcseconds to pixels. */
+ FITS_read_key(infits, TSTRING, "DETECTOR", det, NULL, &status);
+ c = det[0] =='2'? 2: 0;
+ if (det[1] == 'B')
+ c++;
+ x_factor=factor[0][c];
+ y_factor=factor[1][c];
+
+ /* Update the PLATESC (plate scale) keyword in the header */
+ FITS_update_key(infits, TFLOAT, "PLATESC", &y_factor, NULL, &status);
+
+ cf_verbose(3, "Arcsec to pixels: X = %5.3f\tY = %5.3f",
+ x_factor, y_factor);
+
+ /* Try to open the jitter file. */
+ FITS_read_key(infits, TSTRING, "JITR_CAL", jitr_cal, NULL, &status);
+ fits_open_file(&jitfits, jitr_cal, READONLY, &status);
+
+ /* If the jitter file is not found, try a lower-case filename. */
+ if (status != 0) {
+ status = 0;
+ jitr_cal[0] = (char) tolower(jitr_cal[0]);
+ fits_open_file(&jitfits, jitr_cal, READONLY, &status);
+ }
+
+ /* Read exposure start time from jitter file header */
+ FITS_read_key(jitfits, TDOUBLE, "EXPSTART", &jitr_expstart, NULL, &status);
+
+ /* Allocate space for correction arrays */
+ dx_tt = (float *) cf_calloc(nsec, sizeof(float));
+ dy_tt = (float *) cf_calloc(nsec, sizeof(float));
+
+ /* Read the jitter file. */
+ FITS_movabs_hdu(jitfits, 2, &hdutype, &status);
+ njitr=cf_read_col(jitfits, TLONG, "TIME", (void **) &time_jit);
+ njitr=cf_read_col(jitfits, TFLOAT, "DX", (void **) &dx_jit);
+ njitr=cf_read_col(jitfits, TFLOAT, "DY", (void **) &dy_jit);
+ njitr=cf_read_col(jitfits, TSHORT, "TRKFLG", (void **) &trkflg);
+ FITS_close_file(jitfits, &status);
+
+ /* Read minimum acceptable TRKFLG value from PARM_CAL file */
+ FITS_read_key(infits, TSTRING, "PARM_CAL", parmfile, NULL, &status);
+ FITS_open_file(&parmfits, cf_parm_file(parmfile), READONLY, &status);
+ FITS_read_key(parmfits, TSHORT, "TRKFLG", &trkflg_min, NULL, &status);
+ FITS_close_file(parmfits, &status);
+
+ /* Correct for difference in data and jitter EXPSTART times */
+ time_diff = cf_nlong((data_expstart - jitr_expstart) * 86400.);
+ for (j=0; j< njitr; j++) time_jit[j] -= time_diff;
+
+ /*
+ * Map times in the jitter file to times in the timeline table
+ * and calculate the jitter offsets.
+ */
+ for (j=k=0; k < nsec; k++) {
+ while ((float) time_jit[j+1] < ttime[k] && j+1 < njitr) j++;
+ if (trkflg[j] >= trkflg_min) {
+ dx_tt[k] = x_factor * dx_jit[j];
+ dy_tt[k] = y_factor * dy_jit[j];
+ }
+ }
+ /*
+ * Map times in the timeline table to the times associated
+ * with individual photons and apply the appropriate shifts.
+ * Move only photons in the active aperture and for which the
+ * timeline status jitter flag has not been set.
+ * Record number of rejected events.
+ * Note: active_ap[0] = lif, active_ap[1] = sic
+ */
+ nrej = 0;
+ for (j=k=0; j<nevents; j++) {
+ while (ttime[k+1] - FRAME_TOLERANCE < time[j] && k+1 < nsec) k++;
+ if (channel[j] == active_ap[0] || channel[j] == active_ap[1]) {
+ if (status_flag[k] & TEMPORAL_JITR) nrej++;
+ else {
+ x[j] += dx_tt[k];
+ y[j] += dy_tt[k];
+ }
+ }
+ }
+
+ /* Write to trailer file */
+ cf_verbose(2, "Rejected %ld photons from target aperture", nrej);
+
+ /* Deallocate storage space */
+ free(time_jit);
+ free(dx_jit);
+ free(dy_jit);
+ free(trkflg);
+ free(dx_tt);
+ free(dy_tt);
+
+ cf_proc_update(infits, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished processing");
+
+ return (status);
+}
diff --git a/src/libcf/cf_scale_bkgd.c b/src/libcf/cf_scale_bkgd.c
new file mode 100644
index 0000000..556c0c7
--- /dev/null
+++ b/src/libcf/cf_scale_bkgd.c
@@ -0,0 +1,956 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_scale_bkgd(infits, nevents, x_in, y_in, w_in, channel,
+ * timeflags, locflags, ngood, good_index, dtime, ntime,
+ * &binx, &biny, &nx_lif, &ny_lif,
+ * &ymin_lif, img_lif, &nx_sic, &ny_sic, &ymin_sic, &img_sic)
+ *
+ * Arguments: *fitsfile infits Input IDF file
+ * long nevents Number of photon events recorded
+ * float x_in, y_in Coordinates of each photon
+ * float w_in Weighting factor for each photon
+ * unsigned char channel Aperture ID for each photon
+ * unsigned char timeflags Photon status flags regarding time
+ * unsigned char locflags Photon status flags regarding location
+ * long ngood Number of good photons
+ * long good_index List of indices for the good photons
+ * long dtime Length of the daytime exposure
+ * long ntime Length of the night time exposure
+ * int binx, biny Binning factors of the background
+ * array along the X and Y axes
+ * int nx_lif, nx_sic X Size of background array
+ * int ny_lif, ny_sic Y Size of background array
+ * int ymin_lif, ymin_sic Y value corresponding to the
+ * first row of the bkgd image
+ * float *img_lif, *img_sic 2D background image (binned)
+ *
+ * Description:
+ * (a) Determine the total counts in a background sample region for data
+ * taken during the night and during the day. Use the data in the
+ * IDF event list, after screening out the geocoronal lines.
+ * (b) Read estimated intrinsic count rates from the background characterization
+ * file and estimate the intrinsic counts in the background region from
+ * the exposure times and number of pixels in the region.
+ * (c) Subtract the estimated intrinsic counts from the measured total to
+ * get an estimate of the scattered light contribution for both day
+ * and night portions of the observation
+ * (d) Use the scattered light counts to scale daytime and night background
+ * images obtained from the background calibration files
+ * (e) Combine the two background images to obtain the final background model
+ *
+ * A previous assumption that the intrinsic background is constant was
+ * found to be incorrect. The program now iterates the assumed intrinsic
+ * background count rate (assumed constant in day and night) and checks the
+ * calculated model against the observations by comparing the intensity
+ * variations along the x direction. In this comparison, we mask out regions
+ * that are affected by geocoronal contamination.
+ *
+ * Caveats: The "intrinsic" component of the background is neither intrinsic
+ * nor constant in day and night. It is better called the "spatially uniform"
+ * component and should be computed independently for day and night portions
+ * of the exposure. wvd 01/24/2006
+ *
+ * History: 01/11/03 v1.1 rdr Started work - adapted program
+ * from cf_make_ttag_bkgd
+ * 03/01/03 v1.3 wvd Correct use of pointer in FITS_read_key
+ * 03/11/03 v1.4 wvd Update documentation regarding use of
+ * unsigned char.
+ * 03/28/03 v1.5 rdr corrected error in freeing storage
+ * 03/31/03 v1.6 rdr corrected error in assigning subarrays
+ * for lif and sic apertures
+ * 05/08/03 v1.7 rdr read limits to the background region
+ * from the header of the IDF. Also
+ * changed print statements to cf_verbose.
+ * 05/20/03 v1.8 rdr - Make a model using only exposure time
+ * if no background sample regions have
+ * been specified (generally for HIST data)
+ * - Look into parm file to determine
+* whether to generate a background model
+ * 05/22/03 v1.9 rdr Correct problems occuring when exptime=0
+ * 06/04/03 v1.10 rdr Change method for determining limits of
+ * output arrays
+ * 09/08/03 v1.12 wvd Use cf_read_col in get_limits()
+ * 09/29/03 v1.13 wvd Fix bug in population of 2-D bkgd
+ * array; clean up i/o.
+ * 10/07/03 v1.14 wvd Reject airglow photons using a
+ * bit-wise comparison of locflags.
+ * 10/31/03 v1.15 wvd Increase aperture pad from 10 to 15.
+ * Change channel to unsigned char.
+ * 10/31/03 v1.16 wvd Use cf_read_col in get_geocoronal_mask()
+ * 03/22/04 v1.17 wvd Change get_limits so that sizes of
+ * bkgd and probability arrays are equal.
+ * 04/08/04 v1.18 bjg Replace NXMAX by nx
+ * Remove unused variables
+ * Change format in printf to match
+ * arg type.
+ * 04/27/04 v1.19 wvd Replace RUN_MKBK with RUN_BKGD
+ * throughout.
+ * 07/01/04 v1.20 wvd Fix bug: nxb was undefined for
+ * RUN_BKGD = NO.
+ * 03/08/05 v1.21 wvd Convert to cf_scale_bkgd.
+ * Treat data and models the same way.
+ * Modify get_xvar_img. Add
+ * make_bad_pixel_mask and sum_good_pixels.
+ * 06/15/05 v1.22 wvd BUG FIX: get_limits always read the
+ * probability arrays for point-source
+ * targets from the WGTS_CAL file. Now
+ * it distinguishes between point-source
+ * and extended targets.
+ * 01/20/06 v1.23 wvd BUG FIX: Apply uniform X limits to
+ * data and background models.
+ * BUG FIX: Allow output bkgd images
+ * to fall off the detector if target
+ * centroid requires it.
+ * 01/24/06 v1.24 wvd BUG FIX: Test y coordinate of photon
+ * before adding it to the data image.
+ * 02/01/06 v1.25 wvd BUG FIX: Make npix a long in
+ * sum_good_pixels. Correct error in
+ * extraction of sub-arrays.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "calfuse.h"
+
+
+/*********************************************************************
+ *
+ * GET_XVAR_IMG
+ *
+ * Subroutine to compute the intensity variation along the X direction
+ * of a background image. Pixels flagged as bad are excluded.
+ * bkgimg = image from which to extract the data
+ * maskimg = mask for data image (1 = good pixel)
+ * nxb = size of bkgimg in x
+ * nxsam = number of elements in the xvars array
+ * xvars = array of x variations
+ *
+ ***********************************************************************/
+
+
+int
+get_xvar_img(float *bkgimg, float *maskimg, int nxb, int nxsam, float *xvars)
+{
+ int nbin;
+ long i, j;
+
+ nbin = nxb/nxsam ;
+ for (i = 0; i < NYMAX; i++)
+ for (j = 0; j < nxb; j++)
+ xvars[j/nbin] += bkgimg[i*nxb+j] * maskimg[i*nxb+j];
+
+ return 0 ;
+}
+
+/*********************************************************************
+ *
+ * MAKE_MODEL
+ *
+ * Subroutine to combine a day and night calibration image to generate the
+ * final background model:
+ * npix = number of pixels in the full background image
+ * nsam = number of pixels in the background regions of the detector
+ * nxb = size of the x dimension of the background images
+ * intcr = assumed intrinsic count rate
+ * expnight, expday = exposure times during the night and day
+ * data_sum_night, data_sum_day = sum of weights within the background
+ * sample regions for night and day exposures
+ * model_sum_night, model_sum_day = number of counts in the unscaled
+ * night and day background calibration images
+ * - background regions only
+ * bkgimgn = image containing the night calibration image.
+ * bkgimgd = image containing the daytime calibration image
+ * bkgimg = final background image
+ * maskimg = mask defining good background regions
+ *
+ *****************************************************************************/
+
+int
+make_model(long npix, long nsam, int nxb, float intcr, float expnight,
+ float expday, double data_sum_night, double data_sum_day,
+ double model_sum_night, double model_sum_day,
+ float *bkgimgn, float *bkgimgd, float *bkgimg, float *maskimg)
+{
+ int bin_size ;
+ long i;
+ float int_counts;
+ float sfd, sfn, intnight, intday, scatnight, scatday ;
+
+ /* bin_size = binning factor of the background image */
+ bin_size = NXMAX / nxb ;
+
+ cf_verbose(3, "\n\tConstructing the background model\n") ;
+ cf_verbose(3, "total counts in the data: night= %.1f, day = %.1f",
+ data_sum_night, data_sum_day);
+ cf_verbose(3, "intrinsic count rate = %.2f x 10^-7 ",intcr ) ;
+ cf_verbose(3, "exptime night = %.1f, exptime day = %.1f ",
+ expnight, expday );
+
+ /* Sum the intrinsic background counts */
+ intnight = nsam * bin_size * intcr * expnight * 1.e-7 ;
+ intday = nsam * bin_size * intcr * expday * 1.e-7 ;
+ cf_verbose(3, "total intrinsic counts: night= %.1f , day= %.1f ",
+ intnight, intday) ;
+
+ /* Sum of scattered light in data (measured - intrinsic) */
+ scatnight = data_sum_night - intnight ;
+ if (scatnight <= 0.) scatnight = 0. ;
+ scatday = data_sum_day - intday ;
+ if (scatday <= 0.) scatday = 0. ;
+ cf_verbose(3, "total scattered light count: night = %.1f, day = %.1f",
+ scatnight, scatday);
+ cf_verbose(3, "total counts in unscaled background model: "
+ "night = %.2f, day = %.2f", model_sum_night, model_sum_day);
+
+ /* Calculate the scale factor for the night scattered light image */
+ sfn = 0. ;
+ if (model_sum_night > 0) sfn = scatnight / model_sum_night ;
+
+ /* Calculate the scale factor for the day scattered light image */
+ sfd = 0. ;
+ if (model_sum_day > 0) sfd = scatday / model_sum_day ;
+ cf_verbose(3, "scale factors for background images: "
+ "night = %.1f, day = %.1f ", sfn, sfd) ;
+
+ /* Calculate the intrinsic counts per binned pixel */
+ int_counts = bin_size * intcr * (expnight + expday) * 1.e-7 ;
+ cf_verbose(3, "intrinsic counts per binned pixel = %.6f ", int_counts ) ;
+
+ /* Use the scattered light scaling factors and the tabulated intrinsic
+ count rate to model the final background image */
+
+ for (i = 0; i < npix; i++)
+ bkgimg[i] = (sfn * bkgimgn[i]) + (sfd * bkgimgd[i]) + int_counts;
+
+ return 0 ;
+}
+
+
+/*********************************************************************
+ *
+ * MAKE_MODEL_EXPTIME :
+ *
+ * Subroutine to combine a day and night calibration image based on
+ * exposure times only to generate the final background model:
+ * npix = total number of pixels in the image
+ * nxb = length of the x axis in the background images
+ * intcr = assumed intrinsic count rate
+ * expnight, expday = exposure times during the night and day
+ * bkging = image containing the night calibration image.
+ * bkgimgd = image containing the daytime calibration image
+ * bkgimg = final background model
+ *
+ *********************************************************************/
+
+
+int
+make_model_exptime(long npix, int nxb, float intcr, float expnight,
+ float expday, float *bkgimgn, float *bkgimgd, float *bkgimg)
+{
+
+ int i, bin_size ;
+ float intnight, intday ;
+
+ /* Compute the binning factor in x */
+ bin_size = NXMAX/nxb ;
+
+ /* Calculate the expected intrinsic counts in the background */
+ intnight = bin_size * intcr * expnight * 1.e-7 ;
+ intday = bin_size * intcr * expday * 1.e-7 ;
+
+ /* Combine the day and night images based on exposure times */
+ for (i=0 ; i<npix ; i++)
+ bkgimg[i] = intnight + intday + bkgimgn[i]*expnight + bkgimgd[i]*expday ;
+
+ return 0 ;
+}
+
+/*****************************************************************************
+ *
+ * MAKE_BAD_PIXEL_MASK :
+ *
+ * Produces a mask the same size as the background image.
+ * Good pixels are flagged with 1 and bad pixels with 0. Bad pixels are
+ * - all pixels that are NOT in the user-selected background regions;
+ * - all pixels containing airglow lines (from AIRG_CAL); and
+ * - all pixels lying outside of the active area of the detector.
+ * bkgd_num = number of background regions
+ * ylim - limits of background regions
+ * npix = number of pixels in the background array
+ * nxb = dimension of the x axis of the array
+ * maskimg = array to contain the mask (must be initialized to zero)
+ * nsam = number of good pixels in the mask
+ *
+ ****************************************************************************/
+
+int
+make_bad_pixel_mask (fitsfile *infits, int bkgd_num, short *ylim,
+ long npix, int nxb, float *maskimg, long *nsam)
+{
+
+ int nbin;
+ int xmin,xmax,ymin,ymax ;
+ long nrow, ndx, i, j, k, n_air, n_good;
+ short *gxmin, *gxmax, *gymin, *gymax ;
+
+ /* Compute the binning factor in x */
+ nbin = NXMAX/nxb;
+
+ /* Ignore regions near the detector edges. */
+ xmin = 2048 / nbin;
+ xmax = 14336 / nbin;
+
+ /* Flag all pixels in the user-defined background regions as good (1). */
+ for (i = 0; i < bkgd_num; i++)
+ for (j = ylim[2*i]; j <= ylim[2*i+1]; j++)
+ for (k = xmin; k < xmax; k++) {
+ ndx = (long) (j*nxb+k) ;
+ maskimg[ndx] = 1.;
+ }
+
+ /* Read the positions of the geocoronal lines. */
+ nrow = cf_get_geocorona(infits, &gxmin, &gxmax, &gymin, &gymax);
+ cf_verbose(3, "Number of geocoronal lines = %ld", nrow) ;
+
+ /* Flag pixels affected by geocoronal lines as bad (0). */
+ n_air = 0;
+ for (i=0; i<nrow; i++) {
+ xmin = gxmin[i] / nbin ;
+ xmax = gxmax[i] / nbin ;
+ ymin = gymin[i] ;
+ ymax = gymax[i] ;
+ for (j=ymin; j<ymax; j++)
+ for (k=xmin; k<xmax; k++) {
+ ndx = j * nxb + k ;
+ if (maskimg[ndx] > 0) {
+ maskimg[ndx] = 0. ;
+ n_air++ ;
+ }
+ }
+ }
+
+ free(gxmin);
+ free(gxmax);
+ free(gymin);
+ free(gymax);
+
+ cf_verbose(3, "Number of bkgd pixels affected by geocoronal lines = %ld",
+ n_air) ;
+
+ /* Count up and return the number of good pixels. */
+ n_good = 0;
+ for (i = 0; i < npix; i++) if(maskimg[i] > 0) n_good++;
+ cf_verbose(3, "Number of bkgd pixels flagged as good = %ld", n_good) ;
+
+ *nsam = n_good;
+ return 0 ;
+}
+
+
+/******************************************************************************
+ *
+ * GET_LIMITS
+ *
+ * Set limits of background subimage to match those of probability array.
+ *
+ *
+ ******************************************************************************/
+
+int get_limits(fitsfile *infits, int extended, int aperture,
+ int *ymin, int *ymax)
+{
+
+ char wgtsfile[FLEN_VALUE], ycentname[FLEN_KEYWORD];
+ int hdu, nywt, status=0;
+ float ycentv, wgt_cent;
+ fitsfile *wgtsfits;
+
+ cf_verbose(3, "Determining Y limits for aperture %d background array.",
+ aperture);
+
+ /* Read the measured centroid of the target spectrum. */
+ sprintf(ycentname,"YCENT%1d", aperture);
+ FITS_movabs_hdu(infits, 1, NULL, &status);
+ FITS_read_key(infits, TFLOAT, ycentname, &ycentv, NULL, &status);
+
+ /* Read the centroid and size of the probability array. */
+ FITS_read_key(infits, TSTRING, "WGTS_CAL", wgtsfile, NULL, &status);
+ FITS_open_file(&wgtsfits, cf_cal_file(wgtsfile), READONLY, &status);
+ hdu = extended * 8 + aperture + 1;
+ cf_verbose(3, "WGTS_CAL: extended = %d, aperture = %d, hdu = %d",
+ extended, aperture, hdu);
+ FITS_movabs_hdu(wgtsfits, hdu, NULL, &status);
+ FITS_read_key(wgtsfits, TINT, "NAXIS2", &nywt, NULL, &status);
+ FITS_read_key(wgtsfits, TFLOAT, "WGT_CENT", &wgt_cent, NULL, &status);
+ FITS_close_file(wgtsfits, &status);
+
+ cf_verbose(3, "Spectrum centroid=%6.2f, weights centroid=%5.2f, "
+ "weights ny=%4d", ycentv, wgt_cent, nywt) ;
+
+ /* Note that we allow these values to exceed the limits of the
+ * detector. This is required if the bkgd arrays are to cover
+ * the same area as the 2-D probability and data arrays. */
+
+ *ymin = cf_nint (ycentv - wgt_cent);
+ *ymax = *ymin + nywt - 1;
+
+ return status ;
+}
+
+
+/*********************************************************************
+ *
+ * SUM_GOOD_PIXELS
+ *
+ * Subroutine multiplies image and mask arrays, then sums the result.
+ * image = image from which to extract the data
+ * mask = mask for data image (1 = good pixels)
+ * npix = size of image and mask arrays
+ *
+ ***********************************************************************/
+
+
+int
+sum_good_pixels(float *image, float *mask, long npix, double *image_sum)
+{
+
+ long i;
+
+ *image_sum = 0;
+
+ for (i = 0; i < npix; i++)
+ *image_sum += image[i] * mask[i];
+
+ return 0 ;
+}
+
+
+/******************************************************************************
+ *
+ * MAIN PROGRAM BEGINS HERE
+ *
+ ******************************************************************************/
+
+
+int cf_scale_bkgd(fitsfile *infits, long nevents, float *x_in, float *y_in,
+ float *w_in, unsigned char *channel, unsigned char *timeflags,
+ unsigned char *locflags, long ngood, long *good_index, long dtime,
+ long ntime, int *binx, int *biny, int *nx_lif, int *ny_lif,
+ int *ymin_lif, float **img_lif, int *nx_sic, int *ny_sic, int *ymin_sic,
+ float **img_sic)
+{
+
+ char CF_PRGM_ID[] = "cf_scale_bkgd";
+ char CF_VER_NUM[] = "1.25";
+
+ fitsfile *bkgdfits, *scrnfits, *bchrfits, *parmfits ;
+ char buffer[FLEN_CARD], bkgdfile[FLEN_CARD], scrnfile[FLEN_CARD];
+ char bchrfile[FLEN_CARD], keyname[FLEN_CARD], parmfile[FLEN_CARD] ;
+ char run_bkgd[FLEN_VALUE];
+ char comment[FLEN_CARD], datestr[FLEN_CARD];
+ int nx, nxb, ny, status, hdutype, frow, felem, niter;
+ int src_ap[2], nullval, anynull, bkgd_num;
+ int ymin, ymax, phalow, nbinx, nxsam, bkgdtype ;
+ int bin_size, extended, keyval, timeref;
+ double data_sum_day, data_sum_night, data_sum;
+ double model_sum_night, model_sum_day, model_sum ;
+ float *xvarg, *bkgarr, *xvars0, *xvarm0 ;
+ float *bkgimg, *bkgimgd, *bkgimgn, *xvars, *xvarm;
+ float floatnull, exptime, expnight, expday, xvars_ave ;
+ float intcrarray[9], intcr, scattot, rms, rms0, dcr ;
+ float intcnt, scat_int_ratio, zero, mf, numtot, xvars_tot, xvarm_tot ;
+ float *dataimg, *maskimg;
+ short *ylim ;
+ long fpixel, npix, i, j, k, nsam;
+ long nbgood, npts, ndx, ndx1, ndx2, bkgd_ndx;
+
+ /* Initialize error checking */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Started execution.");
+
+ /* Initialize CFITSIO variables */
+ anynull = 0 ;
+ hdutype = 0 ;
+ frow = 1 ;
+ felem = 1 ;
+ floatnull = 0. ;
+ fpixel = 1 ;
+ status = 0 ;
+
+ /* Initialize other variables */
+ data_sum_day=0.;
+ data_sum_night=0.;
+ zero = 0.;
+
+ /* Open parameter file and check whether user wants a background model. */
+ FITS_movabs_hdu(infits, 1, &hdutype, &status) ;
+ FITS_read_key(infits, TSTRING, "PARM_CAL", parmfile, NULL, &status) ;
+ cf_verbose(3, "parameter file is %s ", parmfile) ;
+ FITS_open_file(&parmfits,cf_parm_file(parmfile), READONLY, &status) ;
+ FITS_read_key(parmfits, TSTRING, "RUN_BKGD", run_bkgd, NULL, &status) ;
+ FITS_close_file(parmfits, &status) ;
+ cf_verbose(3, "RUN_BKGD = %s", run_bkgd) ;
+
+ /* If the RUN_BKGD = NO, then fill background array with zeros. */
+ if (!strncmp(run_bkgd, "N", 1) || !strncmp(run_bkgd, "n", 1)) {
+ cf_verbose(1, "RUN_BKGD = NO. No background subtraction.") ;
+ FITS_write_comment(infits, " ", &status);
+ FITS_write_comment(infits, "RUN_BKGD = NO. No background subtraction.",
+ &status);
+ fits_get_system_time(datestr, &timeref, &status);
+ sprintf(comment, "CalFUSE v%s %.10s", CALFUSE_VERSION, datestr);
+ FITS_write_comment(infits, comment, &status);
+ FITS_write_comment(infits, " ", &status);
+
+ bin_size = 1 ;
+ nx=nxb=NXMAX ;
+ ny=NYMAX ;
+ npix=nx * ny ;
+ bkgimg = (float *) cf_calloc(npix, sizeof(float)) ;
+ goto fin ;
+ }
+
+/***********************************************************************
+ *
+ * Set up the parameters for the analysis
+ *
+ ***********************************************************************/
+
+ /* Set day and night exposure times */
+ expday = (float) dtime ;
+ expnight = (float) ntime ;
+ exptime = expday + expnight ;
+
+ /* Get name of background characterization file */
+ FITS_read_key(infits, TSTRING, "BCHR_CAL",bchrfile, NULL, &status) ;
+ cf_verbose(3, "BCHR parameter file is %s ",bchrfile) ;
+
+ /* Get name of background calibration file */
+ FITS_read_key(infits, TSTRING, "BKGD_CAL", bkgdfile, buffer, &status);
+ cf_verbose(3,"Background calibration file = %s ",bkgdfile ) ;
+
+ /* Get name of screening file */
+ FITS_read_key(infits, TSTRING, "SCRN_CAL",scrnfile, NULL, &status) ;
+ cf_verbose(4, "screening file is %s ",scrnfile) ;
+
+ /* Read pha screening limit from the input file header */
+ FITS_read_key(infits,TINT,"PHALOW",&phalow, NULL, &status) ;
+ if (phalow > 8) phalow=8 ;
+ cf_verbose(2, "pha screening limit = %d", phalow) ;
+
+ /* Determine the type of background model to make by checking the
+ BKGDTYPE flag in the screening file. If BKGDTYPE > 0, do a full
+ model. If BKGDTYPE < 0, calculate a model based on exposure time. */
+ FITS_open_file(&scrnfits,cf_parm_file(scrnfile), READONLY, &status) ;
+ FITS_read_key(scrnfits, TINT, "BKGDTYPE",&bkgdtype, NULL, &status) ;
+ FITS_close_file(scrnfits, &status) ;
+
+ /* Read the intrinsic count rate from the BCHR file */
+ FITS_open_file(&bchrfits,cf_parm_file(bchrfile), READONLY, &status) ;
+ FITS_movabs_hdu(bchrfits, 3, &hdutype, &status) ;
+ FITS_read_img(bchrfits, TFLOAT, fpixel, 9, &nullval, intcrarray,
+ &anynull, &status) ;
+ FITS_close_file(bchrfits, &status) ;
+ intcr=intcrarray[phalow] ;
+ cf_verbose(2,"Initial guess for the intrinsic count rate = "
+ "%.1f x 10^-7 c/s/pixel ", intcr) ;
+
+ /* Read the limits of the background sample regions from the header
+ of the input file */
+ FITS_read_key(infits, TINT, "BKGD_NUM", &bkgd_num, NULL, &status) ;
+ cf_verbose(3, "Number of background sample regions = %d ", bkgd_num) ;
+
+ ylim = (short *) cf_calloc(bkgd_num*2, sizeof(short) ) ;
+ for (i=0; i<bkgd_num; i++) {
+ sprintf(keyname,"BKG_MIN%ld",i) ;
+ FITS_read_key(infits, TINT, keyname,&keyval, NULL, &status) ;
+ ylim[2*i] = keyval ;
+ sprintf(keyname,"BKG_MAX%ld",i) ;
+ FITS_read_key(infits, TINT, keyname,&keyval, NULL, &status) ;
+ ylim[2*i+1] = keyval ;
+ }
+ cf_verbose(3, "Limits of background regions") ;
+ for (i=0; i<bkgd_num; i++) cf_verbose(3,
+ "region %d = %d to %d ", i, ylim[2*i], ylim[2*i+1] ) ;
+
+ /* If no background sample regions have been specified (as is usual for
+ HIST data), then generate a model based only on exposure time */
+ if (bkgd_num == 0) {
+ bkgdtype = -1 ;
+ cf_verbose(1, "No background sample regions specified "
+ "- probably HIST") ;
+ }
+
+
+/*************************************************************************
+ *
+ * Read in the background calibration information
+ *
+ *************************************************************************/
+
+ /* Open background image file and go to the night image hdu */
+ FITS_open_file(&bkgdfits, cf_cal_file(bkgdfile), READONLY, &status);
+ FITS_movabs_hdu(bkgdfits, 2, &hdutype, &status);
+ FITS_read_key(bkgdfits, TINT, "NAXIS1", &nx, buffer, &status);
+ FITS_read_key(bkgdfits, TINT, "NAXIS2", &ny, buffer, &status);
+ cf_verbose(3, "Reading background calibration file:",
+ "naxis1=%d, naxis2 = %d ",nx,ny) ;
+
+ /* Background image files are binned in X, but not Y.
+ Determine the degree of compression. */
+ bin_size= NXMAX/nx ;
+ cf_verbose(3, "Background images are binned by %d pixels", bin_size) ;
+
+ /* Allocate space to hold the background, data, and mask images */
+ npix = nx * ny ;
+ nxb = nx ;
+ bkgimgn = (float *) cf_calloc(npix, sizeof(float)) ;
+ bkgimgd = (float *) cf_calloc(npix, sizeof(float)) ;
+ bkgimg = (float *) cf_calloc(npix, sizeof(float)) ;
+ dataimg = (float *) cf_calloc(npix, sizeof(float)) ;
+ maskimg = (float *) cf_calloc(npix, sizeof(float)) ;
+
+ /* Read the night and day scattered-light images */
+ FITS_read_img(bkgdfits, TFLOAT, fpixel, npix, &nullval, bkgimgn,
+ &anynull, &status) ;
+ FITS_movabs_hdu(bkgdfits, 3, &hdutype, &status);
+ FITS_read_img(bkgdfits, TFLOAT, fpixel, npix, &nullval, bkgimgd,
+ &anynull, &status) ;
+ FITS_close_file(bkgdfits, &status) ;
+
+ /* Rescale the background images if they have been compressed. */
+ if (bin_size > 1) for (i=0; i<npix; i++) {
+ bkgimgn[i] *= bin_size ;
+ bkgimgd[i] *= bin_size ;
+ }
+
+ /* Generate a mask of the same dimensions as the background image.
+ It is 1 in the user-defined background regions, but 0 around
+ airglow lines and near the edges of the detector. */
+ make_bad_pixel_mask (infits, bkgd_num, ylim, npix, nxb, maskimg, &nsam);
+
+
+/****************************************************************************
+ *
+ * Read the photon list.
+ * Store in an array the same size as the the background image.
+ * Populate only regions flagged as good in the background mask.
+ * Omit photons flagged as airglow.
+ * This scheme rejects non-airglow photons that drift into airglow
+ * regions due to mirror or spacecraft motion. We thus under-estimate
+ * the background, but assume that the resulting error is small.
+ *
+ ****************************************************************************/
+
+ /* If the background model is to be calculated only by exposure time,
+ then skip the IDF file analysis */
+ if (bkgdtype < 0) {
+ cf_verbose(3, "Skipping the analysis of the IDF file") ;
+ }
+ else { /* Analysis of the data begins here. */
+ cf_verbose(3, "Beginning the analysis of the IDF file") ;
+
+ nbgood=0;
+ for (i=0; i<ngood; i++) {
+ ndx=good_index[i] ;
+ if (locflags[ndx] & LOCATION_AIR) continue;
+ if ((j = cf_nint(y_in[ndx])) < 0 || j >= NYMAX) continue;
+ bkgd_ndx = j*nxb + cf_nint(x_in[ndx])/bin_size;
+ if (maskimg[bkgd_ndx] > 0) {
+ dataimg[bkgd_ndx] += w_in[ndx];
+ if (timeflags[ndx] & TEMPORAL_DAY) data_sum_day += w_in[ndx] ;
+ else data_sum_night += w_in[ndx];
+ nbgood++ ;
+ }
+ }
+
+ cf_verbose(3, "Number of photons events in the background region = %ld",
+ nbgood);
+ cf_verbose(3, "Sum of daytime weights = %.1lf ", data_sum_day) ;
+ cf_verbose(3, "Sum of nighttime weights = %.1lf ", data_sum_night) ;
+
+ /* If the background scattered light contribution is too large, the
+ background may be contaminated, perhaps by a source in one of the
+ other apertures. This is common in crowded fields or with nebulocity.
+ In this case, we calculate the background model based on the
+ exposure times, as done for the histogram data */
+
+ /* Check the level of the scattered-light background */
+ intcnt = intcr * nsam * bin_size * exptime * 1.e-7 ;
+ scattot = data_sum_day + data_sum_night - intcnt ;
+ scat_int_ratio = 0. ;
+ if (intcnt > 0) scat_int_ratio = scattot / intcnt ;
+ cf_verbose(3, "int cnt = %.1f, scat cnt = %.1f , scat/int = %f ",
+ intcnt, scattot, scat_int_ratio) ;
+
+ if (scat_int_ratio > 10) {
+ cf_verbose(1, "The scattered light background is too large.");
+ bkgdtype = -1;
+ }
+ else {
+ cf_verbose(2,
+ "Scattered light appears reasonable. Proceeding with analysis.") ;
+ }
+
+ } /* Analysis of the data ends here. */
+
+ /* If the background model is to be calculated using only the exposure
+ time, then do it now. */
+ if (bkgdtype < 0) {
+ cf_verbose(1, "Constructing background based only on exposure time.") ;
+ make_model_exptime( npix, nxb, intcr, expnight, expday,
+ bkgimgn, bkgimgd, bkgimg) ;
+ goto fin ;
+ }
+
+/****************************************************************************
+ *
+ * In this section, we attempt to determine the intrinsic count rate.
+ * The scattered light shows little structure in the Y dimension, so
+ * we project all of the arrays onto the X axis. Only good pixels
+ * in the user-specified background regions are included in the sum.
+ *
+ ****************************************************************************/
+
+ /* Make a scattered light reference image assuming an intrinsic count
+ rate of zero. */
+ make_model_exptime(npix, nxb, zero, expnight, expday,
+ bkgimgn, bkgimgd, bkgimg) ;
+
+ /* We bin the data by another factor of 8 in X */
+ /* nbinx = number of detector pixels in one bin */
+ /* nxsam = number of samples in the array */
+ nbinx=128 ;
+ nxsam = NXMAX/nbinx ;
+ cf_verbose(3, "X variation array information: "
+ "nbinx = %d, nxsam= %d ", nbinx, nxsam) ;
+
+ xvars = (float *) cf_calloc(nxsam,sizeof(float)) ;
+ xvarm = (float *) cf_calloc(nxsam,sizeof(float)) ;
+ xvars0 = (float *) cf_calloc(nxsam,sizeof(float)) ;
+ xvarm0 = (float *) cf_calloc(nxsam,sizeof(float)) ;
+ xvarg = (float *) cf_calloc(nxsam,sizeof(float)) ;
+
+ /* Project each image onto the X axis */
+ get_xvar_img(bkgimg, maskimg, nxb, nxsam, xvarm0) ;
+ get_xvar_img(dataimg, maskimg, nxb, nxsam, xvars0) ;
+ get_xvar_img(maskimg, maskimg, nxb, nxsam, xvarg) ;
+
+ /* Calculate the average counts per "good" bin in the data array. */
+ xvars_ave = 0;
+ for (i = j = 0; i < nxsam; i++)
+ if (xvarg[i] > 0) {
+ xvars_ave += xvars0[i];
+ j++;
+ }
+ xvars_ave /= j;
+ cf_verbose (3, "xvars_ave = %.1f (should be > 100)", xvars_ave);
+
+ /* If the average number of counts in the xvars0 array (which holds
+ the observed intensity variation along the x direction) is small,
+ then we can't determine an intrinsic count rate from the data and
+ we use the tabulated rate. If the count is large enough, then we
+ can do a more detailed fit of the model to the observations. */
+
+ if (xvars_ave > 100) { /* Begin fit to intrinsic background */
+
+ cf_verbose(1, "Calculating a full background model.") ;
+ cf_verbose(3, "\n\tInterpolating to derive intrinsic count rate\n") ;
+
+ /* Sum the counts in the data and model arrays. */
+ sum_good_pixels(dataimg, maskimg, npix, &data_sum);
+ sum_good_pixels(bkgimg, maskimg, npix, &model_sum);
+
+ cf_verbose(3, "Total counts in initial scattered-light model = %7.0lf",
+ model_sum);
+ cf_verbose(3, "Intrinsic counts summed over good regions = %7.0f",
+ (intcnt = intcr * nsam * bin_size * exptime * 1.e-7));
+ cf_verbose(3, "Sum of above = %7.0lf",
+ model_sum + intcnt);
+ cf_verbose(3, "Total counts in good regions of data array = %7.0lf",
+ data_sum);
+
+ /* Sum the counts in the data and model x variation arrays */
+ xvarm_tot = 0.;
+ xvars_tot = 0.;
+ for (i = j = 0; i < nxsam; i++) if (xvarg[i] > 0) {
+ xvarm_tot += xvarm0[i];
+ xvars_tot += xvars0[i];
+ }
+ cf_verbose(3, "Total counts in scattered-light xvar array = %7.0f",
+ xvarm_tot);
+ cf_verbose(3, "Total counts in data x variation array = %7.0f",
+ xvars_tot);
+
+ /* Convert the x variation arrays from total counts to units of
+ 1.e-7 c/s/pix, the units of the intrinsic count rate. */
+ mf = bin_size * exptime * 1.e-7 ;
+ for (i=0 ; i<nxsam ; i++) if (xvarg[i] > 0) {
+ xvars0[i] /= xvarg[i] * mf ;
+ xvarm0[i] /= xvarg[i] * mf ;
+ }
+
+ /* Sum the counts in the rescaled xvar arrays */
+ xvarm_tot = 0.;
+ xvars_tot = 0.;
+ for (i = j = 0; i < nxsam; i++) if (xvarg[i] > 0) {
+ xvars_tot += xvars0[i];
+ xvarm_tot += xvarm0[i];
+ j++;
+ }
+ cf_verbose(3, "Mean xvar count rates in units of 10^-7 c/s/pixel:");
+ cf_verbose(3, " Initial model = %.1f", xvarm_tot/j);
+ cf_verbose(3, " Intrinsic counts = %.1f", intcr);
+ cf_verbose(3, " Data array = %.1f", xvars_tot/j);
+
+ /* Determine the value of the intrinsic background by comparing the X
+ variations of the data (after removing a guess for the intrinsic
+ background) with the model scattered light variations, making sure
+ that the final average count rate is the same for both. Iterate
+ until the intrinsic background changes by less than 0.1 units. */
+
+ dcr = 1. ;
+ niter = 0 ;
+ rms0 = 1.e10 ;
+ while ( fabs(dcr) > 0.1) {
+
+ /* Subtract intrinsic counts from the data array and sum it. */
+ xvars_tot = 0. ;
+ for (i=0; i<nxsam; i++) if (xvarg[i] > 0) {
+ xvars[i] = xvars0[i] - intcr ;
+ xvars_tot += xvars[i] ;
+ }
+
+ /* Scale the model array to match the data. Sum it. */
+ numtot = 0. ;
+ for (i=0 ; i<nxsam ; i++) if (xvarg[i] > 0) {
+ xvarm[i] = xvarm0[i] * (xvars_tot / xvarm_tot) ;
+ numtot += xvarm[i] ;
+ }
+
+ /* Sum the absolute difference between the data and model arrays. */
+ rms = 0. ;
+ for (i=0 ; i<nxsam ; i++) if (xvarg[i] > 0)
+ rms += (fabs(xvars[i]-xvarm[i])) ;
+ cf_verbose(3, "intr cr= %5.2f, data-model = %6.2f, "
+ "dat cnts = %5.0f, mod cnts=%5.0f", intcr,rms,xvars_tot,numtot) ;
+
+ niter++ ;
+ if (niter > 20) break ;
+
+ /* if the current rsm value is greater than the preceeding one,
+ reverse direction and reduce the step size by 50% */
+ if ( rms > rms0 ) dcr = -0.5*dcr ;
+ intcr += dcr ;
+
+ /* initialize the next iteration */
+ rms0 = rms ;
+ }
+ } /* End of fit to intrinsic background */
+
+ else {
+ cf_verbose(1, "Background count too small for a detailed fit.");
+ cf_verbose(1, "Assuming an intrinsic count rate of %.1f cts/s", intcr ) ;
+ }
+
+ /* Sum the good pixels in the unscaled day and night background images. */
+ sum_good_pixels(bkgimgn, maskimg, npix, &model_sum_night);
+ sum_good_pixels(bkgimgd, maskimg, npix, &model_sum_day);
+
+ /* Make the final background model */
+ make_model(npix, nsam, nxb, intcr, expnight, expday,
+ data_sum_night, data_sum_day, model_sum_night, model_sum_day,
+ bkgimgn, bkgimgd, bkgimg, maskimg) ;
+
+ /* Sum the counts in the final background image. */
+ sum_good_pixels(bkgimg, maskimg, npix, &model_sum);
+
+ cf_verbose(3, "Total counts in final background model = %.0lf", model_sum);
+ cf_verbose(3, " measured counts = %.0lf",
+ data_sum_day+data_sum_night);
+
+ free(bkgimgn) ;
+ free(bkgimgd) ;
+ free(dataimg) ;
+ free(maskimg) ;
+ free(xvars) ;
+ free(xvars0) ;
+ free(xvarm) ;
+ free(xvarm0) ;
+ free(xvarg) ;
+
+ fin:
+
+ /**************************************************************************
+ *
+ * Extract the subarrays covering the active LiF and SiC apertures
+ *
+ ***************************************************************************/
+
+ extended = cf_source_aper(infits, src_ap) ;
+ cf_verbose(3, "\n\tSelecting subarrays\n") ;
+ cf_verbose(3, "source apertures: lif=%d, sic=%d ",src_ap[0], src_ap[1]) ;
+
+ /* Populate some output values */
+ *binx=bin_size ;
+ *biny=1 ;
+
+ for (k=0; k<2 ; k++) { /* Loop over LiF and SiC channels */
+
+ /* Read the y limits for the output array */
+ get_limits(infits, extended, src_ap[k], &ymin, &ymax) ;
+
+ /* Set up array to contain the image */
+ npts = nxb * (ymax-ymin+1) ;
+ bkgarr = (float *) cf_calloc(npts, sizeof(float)) ;
+
+ /* Fill the array */
+ ndx1 = -1 ;
+ model_sum = 0 ;
+ for (i=ymin; i<=ymax ; i++) {
+ if (i < 0 || i >= ny)
+ ndx1 += nxb ;
+ else {
+ for (j=0; j<nxb; j++) {
+ ndx2 = i*nxb + j ;
+ if (ndx2 >= npix) break ;
+ ndx1++;
+ if (ndx1 >= npts) break ;
+ bkgarr[ndx1]=bkgimg[ndx2] ;
+ model_sum += bkgarr[ndx1] ;
+ }
+ }
+ }
+
+ /* Populate the output arrays and values */
+ if (k == 0) {
+ *nx_lif = nxb ;
+ *ny_lif = (ymax - ymin + 1) ;
+ *ymin_lif = ymin ;
+ *img_lif = bkgarr ;
+ }
+ else {
+ *nx_sic = nxb ;
+ *ny_sic = (ymax - ymin + 1) ;
+ *ymin_sic = ymin ;
+ *img_sic = bkgarr ;
+ }
+
+ cf_verbose(2, "for aperture %d, nx=%d, ny=%d, ymin=%d, model_sum=%.0f",
+ src_ap[k], nxb, (ymax-ymin+1), ymin, model_sum) ;
+
+ } /* End loop over LiF and SiC channels */
+
+ /* release memory for the background image */
+ free(bkgimg) ;
+
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished execution.");
+ return 0;
+}
diff --git a/src/libcf/cf_screen_airglow.c b/src/libcf/cf_screen_airglow.c
new file mode 100644
index 0000000..5a7b2b3
--- /dev/null
+++ b/src/libcf/cf_screen_airglow.c
@@ -0,0 +1,70 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_screen_airglow (fitsfile *header, long nevents,
+ * float *x, float *y, unsigned char *location)
+ *
+ * Description: Flag photon events in airglow regions.
+ *
+ * Arguments: fitsfile *header Input FITS file pointer
+ * long nevents Number of photon events
+ * float *x X coordinate array
+ * float *y Y coordinate array
+ * unsigned char *location Photon location flag array
+ *
+ * Calls: cf_get_geocorona
+ *
+ * Returns: 0 on success
+ *
+ * History: 03/10/05 1.1 wvd Initial coding
+ *
+ ****************************************************************************/
+
+#include <stdlib.h>
+#include "calfuse.h"
+
+int
+cf_screen_airglow (fitsfile *header, long nevents, float *x, float *y,
+ unsigned char *location)
+{
+ char CF_PRGM_ID[] = "cf_screen_airglow";
+ char CF_VER_NUM[] = "1.1";
+
+ int errflg=0, ngeo;
+ long i, j;
+ short *gxmin, *gxmax, *gymin, *gymax;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ /*
+ * Read limits of airglow regions from AIRG_CAL file.
+ */
+ ngeo = cf_get_geocorona(header, &gxmin, &gxmax, &gymin, &gymax);
+
+ /*
+ * Set the airglow bit of photons in the airglow regions.
+ */
+ for (i = 0; i < nevents; i++) {
+ for (j=0; j<ngeo; j++) {
+ if (x[i] > gxmin[j] && x[i] < gxmax[j] &&
+ y[i] > gymin[j] && y[i] < gymax[j] ) {
+ location[i] = location[i] | LOCATION_AIR;
+ break;
+ }
+ }
+ }
+
+ free(gxmin);
+ free(gxmax);
+ free(gymin);
+ free(gymax);
+
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return 0;
+}
diff --git a/src/libcf/cf_screen_bad_pixels.c b/src/libcf/cf_screen_bad_pixels.c
new file mode 100644
index 0000000..99884a5
--- /dev/null
+++ b/src/libcf/cf_screen_bad_pixels.c
@@ -0,0 +1,122 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_screen_bad_pixels (fitsfile *header, long nevents,
+ * float *x, float *y, unsigned char *location)
+ *
+ * Description: Flag photon events in bad-pixel regions.
+ *
+ * Arguments: fitsfile *header Input FITS file pointer
+ * long nevents Number of photon events
+ * float *x X coordinate array
+ * float *y Y coordinate array
+ * unsigned char *location Photon location flag array
+ *
+ * Calls: cf_get_potholes
+ *
+ * Returns: 0 on success
+ *
+ * History: 11/22/05 1.1 wvd Initial coding
+ *
+ ****************************************************************************/
+
+#include <stdlib.h>
+#include "calfuse.h"
+
+/***********************************************************************
+ *
+ * Procedure to read pothole locations from the QUAL_CAL file
+ *
+ ***********************************************************************/
+
+int
+cf_get_potholes(fitsfile *header, float **xbad, float **ybad,
+ float **rxbad, float **rybad)
+{
+ char qualfile[FLEN_VALUE];
+ int nbad, status=0;
+ fitsfile *qualfits;
+
+ cf_verbose(3, "Entering cf_get_potholes.");
+
+ /*
+ * Read limits of bad-pixel regions from the first extension
+ * of the QUAL_CAL file.
+ */
+ FITS_movabs_hdu(header, 1, NULL, &status);
+ FITS_read_key(header, TSTRING, "QUAL_CAL", qualfile, NULL, &status);
+ cf_verbose(3, "QUAL_CAL = %s", qualfile);
+
+ FITS_open_file(&qualfits, cf_cal_file(qualfile), READONLY, &status);
+ FITS_movabs_hdu(qualfits, 2, NULL, &status);
+ nbad = cf_read_col(qualfits, TFLOAT, "X", (void **) xbad);
+ nbad = cf_read_col(qualfits, TFLOAT, "Y", (void **) ybad);
+ nbad = cf_read_col(qualfits, TFLOAT, "RX", (void **) rxbad);
+ nbad = cf_read_col(qualfits, TFLOAT, "RY", (void **) rybad);
+ FITS_close_file(qualfits, &status);
+
+ cf_verbose(3, "Exiting cf_get_potholes.");
+ return nbad;
+}
+
+
+int
+cf_screen_bad_pixels (fitsfile *header, long nevents, float *x, float *y,
+ unsigned char *location)
+{
+ char CF_PRGM_ID[] = "cf_screen_bad_pixels";
+ char CF_VER_NUM[] = "1.1";
+
+ int errflg=0, nbad;
+ long counter=0, i, j;
+ float *xbad, *ybad, *rxbad, *rybad, *rx2, *ry2;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ /*
+ * Read limits of bad-pixel regions from QUAL_CAL file.
+ */
+ nbad = cf_get_potholes(header, &xbad, &ybad, &rxbad, &rybad);
+ cf_verbose(2, "For first pothole: x = %.0f, y = %.0f, rx = %.0f, ry = %.0f",
+ xbad[0], ybad[0], rxbad[0], rybad[0]);
+
+ /*
+ * Square the semi-major axes.
+ */
+ for (j = 0; j < nbad; j++) {
+ rxbad[j] *= rxbad[j];
+ rybad[j] *= rybad[j];
+ }
+ rx2 = rxbad;
+ ry2 = rybad;
+ cf_verbose(2, "For first pothole: rx2 = %.0f, ry2 = %.0f", rx2[0], ry2[0]);
+
+ /*
+ * Flag photons in the bad-pixel regions.
+ */
+ for (i = 0; i < nevents; i++) {
+ for (j = 0; j < nbad; j++) {
+ if ((x[i] - xbad[j]) * (x[i] - xbad[j]) / rx2[j] +
+ (y[i] - ybad[j]) * (y[i] - ybad[j]) / ry2[j] < 1.0) {
+ location[i] = location[i] | LOCATION_BADPX;
+ counter++;
+ break;
+ }
+ }
+ }
+ cf_verbose(2, "Flagged %d pixels as bad", counter);
+
+ free(xbad);
+ free(ybad);
+ free(rxbad);
+ free(rybad);
+
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return 0;
+}
diff --git a/src/libcf/cf_screen_burst.c b/src/libcf/cf_screen_burst.c
new file mode 100644
index 0000000..f80374e
--- /dev/null
+++ b/src/libcf/cf_screen_burst.c
@@ -0,0 +1,758 @@
+/****************************************************************************
+ * Johns Hopkins University
+ * Center for Astrophysical Sciences
+ * FUSE
+ ****************************************************************************
+ *
+ * Synopsis: cf_screen_burst(infits, nevents, ptime, x, y, locflags,
+ * gti, nsec, ttime, sflag, aic_rate, bkgd)
+ *
+ * Description: Determines the times for bursts and sets the burst
+ * flag in the timeline status array so that the busts
+ * can be removed later.
+ *
+ * Arguments: fitsfile infile Pointer to Intermediate Data File
+ * long nevents Number of photons
+ * float ptime Time of event
+ * float *x, *y X and Y positions of the photon
+ * byte locflags flags on photon location
+ * (LOC_FLGS column in IDF)
+ * GTI gti Structure with Good Time Intervals
+ * long nsec Length of timeline array
+ * float ttime Timeline array
+ * byte sflag Status flags in timeline table
+ * float aic_rate AIC count rate
+ * short bkgd Count rate for the background
+ * sample region
+ *
+ * Algorithm used:
+ *
+ * 1. Generate an array (flgarr) with 1 second intervals that
+ * contains flags indicating whether that time interval is good
+ * or not. All elements of this array are originally set to negative
+ * numbers, indicating bad times.
+ * 2. Read in the good time intervals from the initial screening
+ * analysis and set all good time elements in flgarr to positive
+ * values
+ * 3. Determine the median count per binned element in the entire time
+ * sequence (ignoring times with no data) and set all elements with
+ * a count > 5 times this median value to negative values (indicating
+ * a burst at that time).
+ * 4. Do a median filter of the modified time sequence (ignoring the neg
+ * numbers) and identify all times where the count per bin is greater
+ * than a specified value or is more than a specified number of
+ * standard deviations from the median. These points are flagged with
+ * negative numbers to indicate bursts. The screening parameters used
+ * are specified in the screening parameter file.
+ * 5. Iterate time-sequence analysis until no more points are removed.
+ * 6. Transfer the burst times from the cnt array to flgarr
+ * 7. Go through all detected photons. If the time of arrival for that
+ * photon has been flagged as bad in FLGARR then ignore it,
+ * otherwise copy the information to the output file.
+ *
+ *
+ * Calibration files:
+ * BCHR_CAL: location of the background sample regions
+ * SCRN_CAL: burst screening params
+ *
+ * Parameter file keywords used to control the burst screening process
+ *
+ * MNCNT : minimum enhancement of the background (in counts) needed to
+ * flag a burst (currently = 5)
+ * STDRE : minimum number of standard deviations from the local mean
+ * for a burst to be detected (currently=5)
+ * NBIN : binning factor (in seconds) for the tiome sequence before
+ * analysis. (currently=15 seconds)
+ * NSMED : Interval (in seconds) used in determining the median filter
+ * (currently=600)
+ * SRCFRAC : Fraction of the source intensity required for a
+ * burst to be removed. SCRFRAC=0.01 requires that a burst
+ * exceed 1% of the integrated source intensity before being
+ * removed (default 0.001)
+ *
+ * Returns: 0 on success
+ *
+ * History: 10/29/02 v1.1 RDR Adapted from cf_ttag_burst
+ * 11/14/02 v1.2 peb Tidied code and made it compatible
+ * with cf_screen_burst
+ * 12/09/02 v1.3 RDR - Adjusted procedure for filling
+ * burst flags in the timeline
+ * - Corrected error in the procedure
+ * that tabulates bursts properties.
+ * - Used LOCATION flags to exclude
+ * geocoronal emission and photons
+ * off the active detector.
+ * 12/20/02 v1.4 rdr changed status flag arrays to
+ * unsigned char
+ * 01/24/03 v1.5 rdr corrected problem with high
+ * count rates
+ * 03/01/03 v1.6 wvd Correct use of pointer in FITS_read_key
+ * 05/20/03 v1.7 rdr Add cf_proc_check
+ * 05/22/03 v1.8 wvd Exit if EXPTIME !> 0.
+ * Change cf_error_init to stderr.
+ * Implement cf_verbose throughout.
+ * 04/06/03 v1.9 rdr Exclude times which have previously
+ * had a screening bit set
+ * 09/03/03 v1.10 wvd Delete extraneous print statements
+ * 09/10/03 v1.11 wvd Change background array to type short
+ * Set burst flag only when flgarr[i]
+ * < -1, not 0, to prevent bursts
+ * from echoing LIMB/SAA/HV flags.
+ * 09/15/03 v1.12 wvd Use ttime[i] to populate bkgd array.
+ * Old scheme didn't work.
+ * 10/06/03 v1.13 wvd Change locflgs to locflags throughout.
+ * Use bit-wise logic to test sflag array.
+ * 06/04/04 v1.14 wvd Clean up i/o.
+ * 02/02/05 v1.15 wvd Read AIC count rate from timeline table
+ * rather than file header keywords.
+ * 02/02/05 v1.16 wvd Generate bkgd array for the entire
+ * exposure, not just good times.
+ * 08/30/05 v1.17 wvd Ignore photons with arrival times
+ * greater than MAX_EXPTIME.
+ * 04/09/06 v1.18 wvd In subroutine median_filter, require
+ * that nwin >= 1.
+ * 06/12/06 v1.19 wvd Call cf_verbose rather than
+ * cf_if_warning.
+ *
+ ***************************************************************************/
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "calfuse.h"
+
+/*
+ * Function to determine the median value of an array.
+ * Arguments are the array,
+ * the number of points in the array (npt) and the maximum count to
+ * use in the histogram analysis (nmax). The median value is returned.
+ *
+ * The analysis uses a histogram approach to determine the median
+ * value. Negative values of the array have been flagged as bursts
+ * and should be ignored in determining the median value. If all of
+ * the points in the array have been flagged, returns zero.
+ */
+
+static int
+median(int *array, int npts, int nmax)
+{
+ int i, ngood=0, *hist, medval;
+
+ /*
+ * Set up an array to contain the histogram of the data.
+ */
+ hist = (int *) cf_calloc((nmax+1), sizeof(int));
+ /*
+ * Fill the histogram with the data.
+ * Negative points are not counted.
+ */
+ for (i=0; i< npts; i++) {
+ int ndx = array[i];
+ if (ndx > nmax-1)
+ ndx = nmax-1;
+ if (ndx >= 0 ) {
+ ngood ++;
+ hist[ndx]++;
+ }
+ }
+
+ /*
+ * Determine the median value. Set the data to 0 if there
+ * are fewer than 2 points.
+ */
+ if (ngood > 2 ) {
+ int hnum= (int) ngood/2, atot=0;
+ i=-1;
+ while ((atot <= hnum) && (i < nmax) )
+ atot += hist[++i];
+ medval = (int) i;
+ }
+ else
+ medval = 0;
+
+ free(hist) ;
+
+ return medval;
+}
+
+/*
+ * Function to construct a median filter for an array:
+ * array = array for which the median filter is to be constructed
+ * npts = number of points in the array
+ * nflt = number of points over which to determine the median value
+ * medflt = array containing the median filter.
+ */
+
+static void
+median_filter(int *array, int npts, int nflt, int *medflt)
+{
+ int cntmax=0, i, hwidth, nwin, *win, medval, nsam ;
+ /*
+ * Determine the maximum value of the array.
+ */
+ for (i = 0; i < npts; i++)
+ if (array[i] > cntmax)
+ cntmax=array[i];
+
+ cf_verbose(3,"Constructing median filter - max cnt = %d",cntmax) ;
+
+ /*
+ * Determine the properties of the window over which to calculate
+ * the median values. Make sure that the window is not larger
+ * than the width of the data.
+ */
+ if (nflt > npts)
+ hwidth = (int) ( npts/2. ) - 1;
+ else
+ hwidth = (int) (0.5 + nflt/2. );
+ nwin = (int) 2*hwidth+1;
+ if (nwin < 1) nwin = 1;
+ win = (int *) cf_calloc(nwin, sizeof(int));
+ /*
+ * Construct the median filter.
+ */
+ for (i = 0; i < npts; i++) {
+ int j, nmin, nmax;
+ /*
+ * Determine the limits to the sample.
+ */
+ nmin = i - hwidth;
+ if (nmin < 0)
+ nmin=0;
+ nmax = nmin+nwin;
+ if (nmax > npts-1 )
+ nmin = npts-nwin-1;
+
+ /* Fill the sample array */
+ nsam=0 ;
+ for (j=0; j<=2*hwidth; j++) {
+ int ndx = nmin+j;
+ if (ndx > npts-1)
+ ndx = npts-1;
+ win[j] = array[ndx];
+ if (array[ndx] > 0) nsam++ ;
+ }
+
+ /* Get the median value for the sample array */
+ medval = median(win, nwin, cntmax);
+
+ /*
+ cf_verbose(6,"at point %d, nmin=%d, nmax=%d, nsam=%d, medval=%d",
+ i, nmin, nmax, nsam, medval) ;
+ */
+
+ /* Update the median filter */
+ medflt[i] = medval;
+ }
+
+ free(win) ;
+
+}
+
+/*
+ * Function to take an array marked with the times and intensities of
+ * the bursts and identify individual bursts events and output these
+ * to an ASCII file for later analysis.
+ */
+
+static void
+tab_burst(int *bursts, int npts, int nbin, int mjd, long tst, char *outfile)
+{
+ FILE *output;
+ int i, bflg=-1, bdur=0, bcnts=0, bst=0;
+ /*
+ * Go through the bursts array and determine the times and
+ * properties of the bursts.
+ */
+ output = fopen(outfile, "w");
+
+ cf_verbose(2, "BURST SUMMARY") ;
+ for (i=0; i<npts; i++) {
+ if (bursts[i] > 0 && bflg < 0) {
+ bflg=1;
+ bst= i*nbin;
+ }
+ if (bursts[i] > 0 && bflg > 0) {
+ bdur++;
+ bcnts = bcnts + bursts[i];
+ }
+ if (bursts[i] < 0 && bflg > 0) {
+ cf_verbose(2, "start=%d, duration=%ld seconds, counts=%d",
+ bst, bdur*nbin, bcnts) ;
+ fprintf(output, " %d %ld %d %d \n",
+ mjd, bst+tst, bdur*nbin, bcnts);
+ bflg=-1;
+ bdur=0;
+ bcnts=0;
+ }
+ }
+
+ /* Check to see whether a burst is in progress at the end of the obs */
+ if (bflg > 0) {
+ cf_verbose(2, "start=%d, duration=%ld seconds, counts=%d",
+ bst, bdur*nbin, bcnts) ;
+ fprintf(output, " %d %ld %d %d\n",
+ mjd, bst+tst, bdur*nbin, bcnts);
+ }
+
+}
+
+
+int
+cf_screen_burst(fitsfile *infits, long nevents, float *ptime, float *x,
+ float *y, unsigned char *locflags, GTI *gti, long nsec,
+ float *ttime, unsigned char *sflag, float *aic_rate,
+ short *bkgd)
+{
+ char CF_PRGM_ID[] = "cf_screen_burst";
+ char CF_VER_NUM[] = "1.19";
+
+ short ylim[8], k;
+ int anynull, errflg=0, intnull=0, cflg=-1, nrowbkg=0, ngood;
+ int npts, nmax, nflt, iflg, numflg, ndxs, ndxe, ndx, ndxb;
+ int *bcnts, *cnts, *gcnts, *medflt, *flgarr, *bursts, lim_col;
+ int nflg, niter, mjd , status=0, medval;
+ long frow=1, felem=1, tst , i, j;
+ long stdrej, mncnt, nsmed, mnburst, nbin, nflgarr;
+ float exptime, max_aic_rate, bkgsf, bkgsft, srcfrac, mxtime;
+ double expstrt;
+ char burst_tab[FLEN_CARD];
+ char expid[FLEN_CARD], progid[FLEN_CARD], targid[FLEN_CARD];
+ char scobsid[FLEN_CARD], scrnfile[FLEN_CARD], aperf[FLEN_CARD];
+ char det[FLEN_CARD], bchrfile[FLEN_CARD], aper[5];
+ fitsfile *scrnfits, *bchrfits;
+
+ /*
+ * Initialize error checking
+ */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+ if ((errflg = cf_proc_check(infits, CF_PRGM_ID))) return errflg;
+
+ /*
+ * Read header keywords.
+ */
+ FITS_read_key(infits, TFLOAT, "EXPTIME", &exptime, NULL, &status);
+ FITS_read_key(infits, TDOUBLE, "EXPSTART", &expstrt, NULL, &status);
+ FITS_read_key(infits, TSTRING, "PRGRM_ID", progid, NULL, &status);
+ FITS_read_key(infits, TSTRING, "TARG_ID", targid, NULL, &status);
+ FITS_read_key(infits, TSTRING, "SCOBS_ID", scobsid, NULL, &status);
+ FITS_read_key(infits, TSTRING, "EXP_ID", expid, NULL, &status);
+ FITS_read_key(infits, TSTRING, "DETECTOR", det, NULL, &status);
+
+ /*
+ * Exit if EXPTIME is 0.
+ */
+ if (exptime < 1) {
+ cf_verbose(1, "EXPTIME = %f. Exiting.", exptime);
+ return status;
+ }
+
+ /*
+ * Determine the Julian date and start time from expstrt.
+ */
+ mjd = (int) expstrt;
+ tst = (int) ((expstrt - mjd)*86400.);
+ det[1] = tolower(det[1]);
+
+ /* Ignore events with arrival times greater than MAX_EXPTIME. */
+ i = nevents - 1;
+ while (ptime[i] > MAX_EXPTIME && i > 0) i--;
+ nevents = i + 1;
+ i = nsec - 1;
+ while (ttime[i] > MAX_EXPTIME && i > 0) i--;
+ nsec = i + 1;
+
+ /*
+ * Determine the maximum count rate for the exposure.
+ * If the count rate is too high, there is the possibility of
+ * losing counts and of dropouts - Check the count rate and set
+ * a flag if it is too high.
+ */
+ max_aic_rate = -999.;
+ for (i = 0; i < nsec; i++)
+ if (aic_rate[i] > max_aic_rate) max_aic_rate = aic_rate[i];
+ cf_verbose(2, "Max AIC count rate = %d", cf_nint(max_aic_rate));
+ if(max_aic_rate > 4000) cflg=1;
+
+ /*
+ * Read the screening parameter file.
+ */
+ FITS_read_key(infits, TSTRING, "SCRN_CAL", scrnfile, NULL, &status);
+ cf_verbose(3, "Screening parameters from %s\n", scrnfile);
+ FITS_open_file(&scrnfits, cf_parm_file(scrnfile), READONLY, &status);
+ FITS_read_key(scrnfits, TLONG, "MNCNT", &mncnt, NULL, &status);
+ FITS_read_key(scrnfits, TLONG, "STDREJ", &stdrej, NULL, &status);
+ FITS_read_key(scrnfits, TLONG, "NBIN", &nbin, NULL, &status);
+ /*
+ * Set the binning factor to 1 for high count rate data.
+ */
+ if (cflg > 0) {
+ nbin=1;
+ cf_verbose(1, "High count-rate observation: using 1 sec time bins") ;
+ }
+ FITS_read_key(scrnfits,TLONG,"NSMED",&nsmed, NULL, &status);
+ /*
+ * srcfrac=required fraction of the source intensity for a
+ * burst to be counted, i.e. the burst must exceed this fraction
+ * before it is deemed to be significant.
+ */
+ FITS_read_key(scrnfits, TFLOAT, "SRCFRAC", &srcfrac, NULL, &status);
+ FITS_read_key(infits, TSTRING, "APERTURE", aperf, NULL, &status);
+ FITS_close_file(scrnfits, &status);
+
+ cf_verbose(3, "Screening parameters used:");
+ cf_verbose(3, "Minimum count rate = %ld", mncnt);
+ cf_verbose(3, "Detection criterion (num sigma) = %ld", stdrej);
+ cf_verbose(3, "Number of seconds to bin the time sequence = %ld", nbin);
+ cf_verbose(3, "Interval (seconds) used in median filter = %ld", nsmed);
+ cf_verbose(3, "Fraction of source intensity for burst = %f\n", srcfrac);
+ /*
+ * Select the background extraction regions - RFPT implies
+ * that there is no source, so the entire detector can be used.
+ */
+ strncpy(aper, aperf, 5);
+ cf_verbose(3, "Aperture designation = %s", aper);
+ if (strcmp ( aper , "RFPT") == 0 ) {
+ cf_verbose(3, "APERTURE = RFPT. No source in aperture.");
+ ylim[0] = 0;
+ ylim[1] = 1;
+ ylim[2] = 2;
+ ylim[3] = 1020;
+ ylim[4] = 1021;
+ ylim[5] = 1022;
+ ylim[6] = 1022;
+ ylim[7] = 1022;
+ }
+ else {
+ /*
+ * open the bchr parameter file
+ */
+ FITS_read_key(infits, TSTRING, "BCHR_CAL", bchrfile, NULL, &status);
+ cf_verbose(3, "BCHR parameter file: %s", bchrfile);
+ FITS_open_file(&bchrfits, cf_parm_file(bchrfile), READONLY, &status);
+
+ /* Move to the first extension of the BCHR file, which contains
+ the limits to the background regions */
+ FITS_movabs_hdu(bchrfits, 2, NULL, &status);
+
+ /* Get the column number for the data to be extracted */
+ FITS_get_colnum(bchrfits, CASEINSEN, aper, &lim_col, &status);
+
+ /* Read the data from the table */
+ FITS_read_col(bchrfits, TSHORT, lim_col, frow, felem, 8, &intnull,
+ ylim, &anynull, &status);
+
+ /* Close the bchr file */
+ FITS_close_file(bchrfits, &status);
+ }
+ cf_verbose(3, "Y limits of background region: %d-%d, %d-%d, %d-%d\n",
+ ylim[0], ylim[1], ylim[2], ylim[3], ylim[4], ylim[5]);
+ /*
+ * Now get the time sequence and a good time interval flag array.
+ */
+ cf_verbose(3, "nevents = %ld", nevents);
+ mxtime=ptime[nevents-1];
+ nflgarr = cf_nint(mxtime);
+ npts= (int) mxtime/nbin;
+ cf_verbose(3, "Number of points in the (binned) time sequence = %d", npts);
+ cf_verbose(3, "Number of points in the good time flag array = %ld", nflgarr);
+ /*
+ * Generate arrays to contain the time sequence in the
+ * background sample regions (cnts) as well as the entire
+ * detector (gcnts). Also generate an array to contain only
+ * the bursts (bursts).
+ * Array bcnts will contain the background count rate.
+ */
+ bcnts = (int *) cf_calloc(npts, sizeof(int));
+ cnts = (int *) cf_calloc(npts, sizeof(int));
+ gcnts = (int *) cf_calloc(npts, sizeof(int));
+ bursts = (int *) cf_calloc(npts, sizeof(int));
+ /* Generate an array to contain the good time flags - this is an array
+ with 1s time intervals. Each interval is set to a negative number (-1)
+ if it contains a bad time interval or a positive number (1) if it
+ contains a good time interval. The contents of this array are used to
+ screen the photons and to determine the good time intervals */
+ flgarr = (int*) cf_calloc(nflgarr, sizeof(int) );
+ /* Generate an array with 1s time intervals to contain the day/night
+ information - this will be 1 for night and 0 for day */
+
+ /* Initially flag all of the points with a negative number.
+ The good time intervals will be determined later */
+ for (i=0; i<npts; i++) {
+ cnts[i] = -10 ;
+ gcnts[i] = 0 ;
+ bursts[i] = -10 ;
+ }
+ for (i=0; i<nflgarr; i++) flgarr[i] = -1 ;
+ /*
+ * Set the counts array to zero (i.e. unflag the point) for all
+ * good time points.
+ */
+ ngood = 0 ;
+ for (i=0; i < gti->ntimes; i++) {
+ ndxs= (int) gti->start[i]/nbin;
+ if (ndxs < 0 ) ndxs = 0 ;
+ ndxe = (int) (0.5 + gti->stop[i]/nbin);
+ if (ndxe > npts-1) ndxe = npts-1;
+ for (j=ndxs; j<= ndxe; j++) {
+ ngood++;
+ cnts[j]=0;
+ }
+ }
+ cf_verbose(2, "Number of good time points = %d\n", ngood);
+
+ /* Set the good time flags array to a positive value for all good
+ time points */
+
+ for (i=0; i < gti->ntimes; i++) {
+ ndxs= (int) gti->start[i];
+ if (ndxs < 0) ndxs = 0 ;
+ ndxe = (int) gti->stop[i];
+ if (ndxe > nflgarr-1) ndxe=nflgarr-1;
+ for (j=ndxs; j<= ndxe; j++)
+ flgarr[j]=1;
+ }
+
+ /* Check the timeline-table screening flag and mark all times that have
+ a screening bit set (except for day/night) */
+ for (i=0; i<nsec; i++) {
+ ndx = (int) (0.5+ttime[i]) ;
+ if( ndx > nflgarr-1) ndx = nflgarr-1;
+ if( ndx < 0) ndx = 0 ;
+ ndxb = (int) (0.5 + (float) ndx/ (float) nbin) ;
+ if(ndxb > npts-1) ndxb = npts-1 ;
+ if (ndxb < 0) ndxb=0 ;
+ if (sflag[i] & ~TEMPORAL_DAY) {
+ flgarr[ndx] = -1 ;
+ cnts[ndxb] = -10 ;
+ }
+ }
+
+ /* Generate the time sequence for the total counts from the detector.
+ Use only the active area and ignore geocoronal emission. */
+
+ for (i=0; i < nevents; i+=1)
+ if( (locflags[i] & LOCATION_SHLD) == 0 &&
+ (locflags[i] & LOCATION_AIR) == 0 ) {
+ int ndx = (int) ptime[i]/nbin;
+ if (ndx > npts-1)
+ ndx=npts-1;
+ if (ndx < 0)
+ ndx=0;
+ gcnts[ndx] += 1;
+ }
+
+ /* Determine the number of rows in the background sample region */
+ for (k=0; k < 3; k++)
+ nrowbkg += ylim[2*k+1] - ylim[2*k];
+ /*
+ * Determine the scaling factor needed to convert the measured
+ * counts in the background regions to an estimate of the
+ * background counts over the active aperture.
+ */
+ bkgsf= (float) ylim[6]/nrowbkg;
+ bkgsft = (float) ylim[7]/nrowbkg;
+
+ cf_verbose(2, "Rows in target aperture = %d", ylim[6]);
+ cf_verbose(2, "Rows on the detector = %d", ylim[7]);
+ cf_verbose(2, "Rows in background region = %d", nrowbkg);
+ cf_verbose(2, "Scale factor to aperture = %f ", bkgsf);
+ cf_verbose(2, "Scale factor to entire detector = %f", bkgsft);
+
+ /* Generate the time sequence using only the counts from the three
+ selected background regions - ignore geocoronal lines and times
+ that have been flagged as bad (with negative count values) */
+ for (k=0; k<3; k++) {
+ short ylow=ylim[2*k];
+ short yhigh=ylim[2*k+1];
+ for (i=0; i < nevents; i+=1) {
+ if( (locflags[i] & LOCATION_SHLD) == 0 &&
+ (locflags[i] & LOCATION_AIR) == 0 &&
+ (y[i] > ylow) && (y[i] < yhigh) ) {
+ int ndx = (int) ptime[i]/nbin;
+ if (ndx > npts-1)
+ ndx=npts-1;
+ if (ndx < 0)
+ ndx=0;
+ if (cnts[ndx] >= 0) cnts[ndx] += 1;
+ bcnts[ndx] ++;
+ }
+ }
+ }
+
+ /* Fill background count rate array with cnts array, and divide by nbin
+ to give count rate */
+/* THIS SCHEME WORKS ONLY IF THERE ARE NO TIME BREAKS IN THE TIMELINE
+ * TABLE. SINCE TIME BREAKS ARE COMMON, MUST USE TTIME ARRAY TO MAP
+ * CNTS ARRAY TO BKGD ARRAY. WVD 09/15/03
+ * for (i=0; i<npts; i++) {
+ * int ndxs= i*nbin;
+ * int ndxe= ndxs + nbin;
+ * if (ndxe > nsec-1)
+ * ndxe=nsec-1;
+ * for (j=ndxs;j<ndxe; j++)
+ * bkgd[j] = (short) ((float) cnts[i] / nbin + 0.5);
+ * }
+ */
+ for (i=0; i<nsec; i++) {
+ ndx = cf_nint(ttime[i]/nbin) ;
+ if( ndx > nflgarr-1)
+ ndx = nflgarr-1;
+/* if (cnts[ndx] > 0.)
+ bkgd[i] = (short) ((float) cnts[ndx] / nbin + 0.5);
+ else
+ bkgd[i] = 0;
+*/
+ bkgd[i] = bcnts[ndx] / nbin;
+ }
+ free (bcnts);
+
+ /* Remove the background counts (scaled to the entire detector) from the
+ measured gross count rate */
+ for (i=0; i < npts; i++) if (cnts[i] > 0) {
+ gcnts[i] = gcnts[i] - bkgsft*cnts[i];
+ if (gcnts[i] < 0)
+ gcnts[i] = 0;
+ }
+
+ /* Check for zeros in the count array for high count rate data - these
+ indicate dropouts. If one is found, flag it with a negative number so
+ that the program will ignore it in future analysis */
+ numflg=0;
+ if (cflg > 0) {
+ for (i=0; i < npts; i++) {
+ if (cnts[i] <= 0.1 ) {
+ numflg += 1;
+ cnts[i]=-10;
+ }
+ }
+ }
+ cf_verbose(2, "Number of dropouts found = %d", numflg);
+
+ /* Determine the median value of the array */
+ nmax=2000;
+ medval = median(cnts, npts, nmax);
+ cf_verbose(2, "Median value of the entire original array = %d ", medval);
+
+ /* Screen the time series for very large count rates (> 3 x median) */
+ cf_verbose(2, "Searching for large bursts ") ;
+ for ( i=0; i<npts; i++)
+ if (cnts[i] > 4*medval) {
+ cf_verbose(2,"point rej: time=%d, cnts=%d, 4*medval=%d ",
+ i*nbin, cnts[i], 4*medval) ;
+ bursts[i] = cnts[i]-medval;
+ cnts[i] = -100;
+ }
+
+ /* Do a median filter on the pre-filtered array */
+
+ /* Generate the array to contain the median filter */
+ medflt = (int *) cf_calloc(npts, sizeof(int) );
+
+ /*
+ * Now specify the parameters of the filter -
+ * nsmed = number of seconds over which to determine median values
+ * nflt = number of points in the cnts array over which to
+ * determine the median value
+ */
+ nflt = nsmed / nbin;
+ niter=0;
+
+ cf_verbose(3,"Searching for small bursts - nflt = %d", nflt) ;
+
+ do {
+ median_filter(cnts, npts, nflt, medflt);
+
+ /* Throw out all points which are more than (stdrej) standard
+ deviations above the median. Also require that the counts exceed
+ a certain minimum value. */
+ nflg=0;
+ mnburst=mncnt*nbin;
+ for (i=0; i<npts; i++) {
+ int dcnt=cnts[i]-medflt[i];
+ if ( (dcnt > mnburst) &&
+ (dcnt > stdrej*sqrt((double) cnts[i])) &&
+ (dcnt*bkgsf > srcfrac*gcnts[i]) ) {
+ nflg++;
+ cf_verbose(2, "point rej: time=%ld, cnts=%d, medflt=%d, "
+ "dif=%d, gcnts= %d",
+ i*nbin, cnts[i], medflt[i], dcnt, gcnts[i]);
+ cnts[i] = -100;
+ bursts[i] = dcnt;
+ }
+ }
+ niter++;
+ cf_verbose(3,"After iteration %d, we found %d times affected by bursts",
+ niter, nflg) ;
+ /* Iterate the median filter if any additional points have
+ * been modified*/
+ } while(nflg > 0 && niter < 10);
+
+ /* Print out the results of the screening
+ cf_verbose(5, " ");
+ for (i=0; i<npts; i++)
+ cf_verbose(5, "At time %4d, cnts= %4d, medflt= %4d, dif= %4d, gcnts= %4d",
+ i*nbin, cnts[i],medflt[i], cnts[i]-medflt[i], gcnts[i]);
+ cf_verbose(3, " ");
+ */
+
+ /* Use the regions of the cnts array equal to -100 to specify
+ the burst times in the flgarr array */
+ for (i=0; i<npts; i++ ) {
+ if (cnts[i] < -20) {
+ int ndxs = i*nbin, ndxe;
+ if (ndxs > nflgarr-nbin-2)
+ ndxs=nflgarr-nbin-2;
+ ndxe = ndxs + nbin - 1;
+ for (j=ndxs; j<=ndxe; j++)
+ flgarr[j] = -10;
+ }
+ }
+
+ /* tabulate the burst times */
+ sprintf(burst_tab, "%4s%2s%2s%3s%2s_bursts.dat", progid, targid, scobsid, expid, det);
+ tab_burst(bursts, npts, nbin, mjd, tst, burst_tab);
+ cf_verbose(3, "Burst properties written to file: %s", burst_tab);
+
+ /* Check the last 2*nbin elements of flgarr. If any of these are set as
+ bad, then set all the end points as bad. The binned count time sequence
+ will not sample the end of the ovarall time sequence if there is not an
+ integral number of bins present. We also do not need the last bin if the
+ second-to-last is bad. */
+
+ i=nflgarr-2*nbin;
+ iflg=10;
+ do{
+ if (flgarr[i] < -1) { /* 09/12/03 - wvd - Change from 0 */
+ for (j=i; j<nflgarr; j++)
+ flgarr[j]=-10;
+ iflg = -10;
+ };
+ i++;
+ } while (iflg > 0 && i < nflgarr);
+
+ /* set the timeline status flag (sflag - TEMPORAL_BRST) for all
+ times containing bursts */
+ for (i=0; i<nsec; i++) {
+ ndx = (int) (0.5+ttime[i]) ;
+ if( ndx > nflgarr-1)
+ ndx = nflgarr-1;
+ if (flgarr[ndx] < -1) /* 09/12/03 - wvd - Change from 0 */
+ sflag[i] = sflag[i] | TEMPORAL_BRST;
+ }
+ /* deallocate storage space */
+ free(cnts) ;
+ free(gcnts) ;
+ free(flgarr) ;
+ free(medflt) ;
+ free(bursts) ;
+
+ /* Enter a time stamp into the log */
+ cf_proc_update(infits, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished processing");
+
+ return (status);
+}
diff --git a/src/libcf/cf_screen_high_voltage.c b/src/libcf/cf_screen_high_voltage.c
new file mode 100644
index 0000000..57a9716
--- /dev/null
+++ b/src/libcf/cf_screen_high_voltage.c
@@ -0,0 +1,146 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_screen_high_voltage(fitsfile *infits, long nseconds,
+ * unsigned char *timeline_status, short *timeline_hv);
+ *
+ * Description: Set the screening bit if detector voltage is low.
+ *
+ * Arguments: fitsfile *infits Input FITS file pointer
+ * long nseconds Number of points in the timeline table
+ * unsigned char *timeline_status The status flag array
+ * short *timeline_hv The high voltage array
+ *
+ * Calls:
+ *
+ * Returns: 0 on success
+ *
+ * History: 10/23/02 1.1 jch Initial coding
+ * 12/20/02 1.3 jch Make sure the flags are "unsigned" char
+ * 01/24/03 1.4 rdr Lower the voltage threshold required
+ * to flag a time to 95% of the full
+ * voltage
+ * 02/11/03 1.6 jch Skip HV which is negative
+ * 05/20/03 1.7 rdr Add call to cf_proc_check
+ * 05/22/03 1.8 wvd Implement cf_verbose
+ * 06/11/03 1.9 wvd Change HV array to type short.
+ * 06/13/03 1.10 wvd Change calfusettag.h to calfuse.h
+ * 09/10/03 1.11 wvd Return errflg from cf_proc_check.
+ * 07/21/04 1.12 wvd Delete unused variable volt_cor.
+ * 02/17/05 1.13 wvd Place parentheses around assignment
+ * used as truth value.
+ * 03/18/05 1.14 wvd HV can be 0, so test for HV > -1
+ * 03/24/05 1.15 wvd Read HVGOODLM and HVBADLIM from
+ * VOLT_CAL file. If HV/FULLV drops
+ * below HVGOODLM, issue warning.
+ * If it falls below HVBADLM, set
+ * TEMPORAL_HV flag.
+ * 06/22/06 1.16 wvd Don't set EXP_STAT keyword.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include "calfuse.h"
+
+int
+cf_screen_high_voltage(fitsfile *infits, long nseconds,
+ unsigned char *timeline_status, short *timeline_hv)
+{
+ char CF_PRGM_ID[] = "cf_screen_high_voltage";
+ char CF_VER_NUM[] = "1.16";
+
+ char file_name[FLEN_VALUE], mjd_str[FLEN_VALUE], full_str[FLEN_VALUE];
+ char datestr[FLEN_VALUE], comment[FLEN_COMMENT], instmode[FLEN_VALUE];
+ int errflg=0, status=0, timeref, hvflag=FALSE, n, fullv;
+ long i;
+ float expstart, hvfrac, hvgood, hvbad, mjdv;
+ fitsfile *scrnfits;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+ if ((errflg = cf_proc_check(infits, CF_PRGM_ID))) return errflg;
+
+ /* Read exposure start time and instrument mode. */
+ FITS_read_key(infits, TFLOAT, "EXPSTART", &expstart, NULL, &status);
+ FITS_read_key(infits, TSTRING, "INSTMODE", instmode, NULL, &status);
+ /*
+ * Open the high-voltage calibration file.
+ */
+ FITS_read_key(infits, TSTRING, "VOLT_CAL", file_name, NULL, &status);
+ FITS_open_file(&scrnfits, cf_cal_file(file_name), READONLY, &status);
+ /*
+ * Read the limits of the good and questionable voltage regimes.
+ */
+ FITS_read_key(scrnfits, TFLOAT, "HVGOODLM", &hvgood, NULL, &status);
+ FITS_read_key(scrnfits, TFLOAT, "HVBADLIM", &hvbad, NULL, &status);
+ /*
+ * Read full-voltage level in use at time of observation.
+ */
+ n = 0;
+ do {
+ n++;
+ sprintf(mjd_str, "MJD%d", n);
+ FITS_read_key(scrnfits, TFLOAT, mjd_str, &mjdv, NULL, &status);
+ } while (expstart > mjdv);
+ n--;
+
+ sprintf(full_str, "FULL%d", n);
+ FITS_read_key(scrnfits, TINT, full_str, &fullv, NULL, &status);
+ FITS_close_file(scrnfits, &status);
+
+ /*
+ * Check for low values of the detector voltage.
+ * Set the high voltage bit if timeline_hv/fullv < hvbad.
+ */
+ cf_verbose(2, "Threshold voltage = %d", cf_nint(hvbad * fullv)) ;
+
+ for (i = 0; i < nseconds; i++) if (timeline_hv[i] > -1) {
+ hvfrac = timeline_hv[i] / (float) fullv;
+ if (hvfrac < hvgood) {
+ if (hvfrac < hvbad) timeline_status[i] |= TEMPORAL_HV;
+ else hvflag = TRUE;
+ }
+ }
+
+ /*
+ * If the detector voltage ever falls between the good and bad
+ * levels, issue a stern warning to the user.
+ */
+ if (hvflag) {
+ /* Write warning to trailer file. */
+ if (!strncmp(instmode, "HIST", 4))
+ cf_if_warning("Detector voltage is < 90%% of optimal value. "
+ "Check photometry and wavelength scale.");
+ else
+ cf_if_warning("Detector voltage is < 90%% of optimal value. "
+ "Check pulse-height distribution.");
+
+ /* Write warning to IDF file header. */
+ FITS_write_comment(infits, " ", &status);
+ FITS_write_comment(infits,
+ "Detector voltage is less than 90%% of optimal value.", &status);
+ if (!strncmp(instmode, "HIST", 4)) {
+ FITS_write_comment(infits,
+ "Carefully examine these data for photometric errors", &status);
+ FITS_write_comment(infits, "and walk effects.", &status);
+ }
+ else {
+ FITS_write_comment(infits,
+ "Carefully examine the pulse height distribution of photons", &status);
+ FITS_write_comment(infits,
+ "in the target aperture before accepting these data.", &status);
+ }
+ fits_get_system_time(datestr, &timeref, &status);
+ sprintf(comment, "CalFUSE v%s %.10s", CALFUSE_VERSION, datestr);
+ FITS_write_comment(infits, comment, &status);
+ FITS_write_comment(infits, " ", &status);
+ }
+
+ cf_proc_update(infits, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return status;
+}
diff --git a/src/libcf/cf_screen_jitter.c b/src/libcf/cf_screen_jitter.c
new file mode 100644
index 0000000..68327a0
--- /dev/null
+++ b/src/libcf/cf_screen_jitter.c
@@ -0,0 +1,246 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center for Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_screen_jitter (infile, nsec, ttime, status_flag)
+ *
+ * Description: Flag times when target is out of the aperture or pointing
+ * is known to be bad. Read the minimum trustworthy TRKFLG
+ * value and aperture limits from the parameter file.
+ *
+ * Key to new TRKFLG values:
+ * 5 = dx, dy from known FPDs and q_cmd from telemetry
+ * 4 = good dx, dy from ACS q_est and q_cmd from telemetry
+ * 3 = maybe good dx, dy from ACS q_est
+ * 2 = dx, dy from known FPDs, q_cmd from FITS header coordinates
+ * 1 = dx, dy from ACS q_est, q_cmd from FITS header coordinates
+ * 0 = no pointing information (missing telemetry)
+ * -1 = Pointing is assumed to be bad (never achieved known track)
+ *
+ * Arguments: fitsfile infile Pointer to the IDF
+ * long nsec Number of seconds in the timeline
+ * long *ttime time (sec) of each point in the timeline
+ * unsigned char *status_flag Timeline status flags
+ *
+ * Returns: 0 upon success
+ *
+ * History: 11/29/05 v1.1 wvd Adapted from cf_satellite_jitter
+ * 03/28/06 1.2 wvd Test both dx and dy.
+ * 05/09/06 1.3 wvd Apply jitter screening to RFPT obs.
+ * 06/12/06 1.4 wvd Call cf_verbose when jitter file not
+ * found.
+ * 08/21/06 1.5 wvd Use EXPSTART in jitter and data
+ * headers to correct jitter time array.
+ * 08/25/06 1.6 wvd Change meaning of TRKFLG values.
+ * TRKFLG = 3 is only used internally.
+ * 11/06/06 1.7 wvd Change meaning of TRKFLG values.
+ * Read minimum TRKFLG value and
+ * aperture limits from parmfile.
+ * Require jitter file version >= 3.0.
+ * Don't screen moving targets.
+ * Bail out only if JIT_STAT = 1.
+ * 12/29/06 1.8 wvd Exit if JIT_VERS < 3.0.
+ * 03/23/07 1.9 wvd Store offset between the jitter and
+ * data values of EXPSTART in variable
+ * time_diff.
+ * 04/07/07 1.10 wvd Clean up compiler warnings.
+ * 08/16/07 1.11 wvd Don't apply jitter correction to
+ * observations taken after the
+ * spacecraft lost pointing control.
+ * 07/25/08 1.12 wvd Use EXP_STAT keyword, rather than
+ * file name, to check for bright-earth
+ * or airglow observations.
+ * 11/21/08 1.13 wvd Don't apply jitter correction to
+ * ERO observations of Jupiter.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "calfuse.h"
+
+#define JIT_VERS_MIN 3.0
+#define BAD_TRKFLG -1
+#define LAST_MJD 54300.
+
+int
+cf_screen_jitter(fitsfile *infits, long nsec, float *ttime,
+ unsigned char *status_flag)
+{
+ char CF_PRGM_ID[] = "cf_screen_jitter";
+ char CF_VER_NUM[] = "1.13";
+
+ char jitr_cal[FLEN_VALUE], parmfile[FLEN_VALUE];
+ char comment[FLEN_COMMENT], hkexists[FLEN_VALUE], jit_vers[FLEN_VALUE];
+ char mov_targ[FLEN_VALUE], program[FLEN_VALUE];
+ int errflg=0, status=0, expstat, jitter_status, hdutype;
+ long j, k, njitr, *time_jit, time_diff;
+ short *trkflg, trkflg_min;
+ double data_expstart, jitr_expstart;
+ float *dx_jit, *dy_jit, dx_max, dy_max;
+ fitsfile *jitfits, *parmfits;
+
+ /* Initialize error checking */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+
+ /* Enter a time stamp into the log */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ /* Read exposure start time from IDF header */
+ FITS_read_key(infits, TDOUBLE, "EXPSTART", &data_expstart, NULL, &status);
+
+ /* Check whether program is appropriate for this data set. */
+ if ((errflg = cf_proc_check(infits, CF_PRGM_ID))) return errflg;
+
+ /* Don't apply jitter screening to bright-earth or airglow observations. */
+ FITS_read_key(infits, TINT, "EXP_STAT", &expstat, NULL, &status);
+ if (expstat == (int) TEMPORAL_LIMB) {
+ cf_verbose(1, "Bright-earth or airglow observation. ",
+ "No jitter correction.");
+ cf_proc_update(infits, CF_PRGM_ID, "SKIPPED");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished processing");
+ return 0;
+ }
+
+ /* Don't apply jitter screening to moving targets. */
+ FITS_read_key(infits, TSTRING, "MOV_TARG", mov_targ, NULL, &status);
+ if (!strncmp(mov_targ, "M", 1)) {
+ cf_verbose(1, "Moving-target observation. No jitter correction.");
+ cf_proc_update(infits, CF_PRGM_ID, "SKIPPED");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished processing");
+ return 0;
+ }
+
+ /* Don't apply jitter screening to ERO observations of Jupiter. */
+ FITS_read_key(infits, TSTRING, "PRGRM_ID", program, NULL, &status);
+ if (!strncmp(program, "X006", 4)) {
+ cf_verbose(1, "Moving-target observation. No jitter correction.");
+ cf_proc_update(infits, CF_PRGM_ID, "SKIPPED");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished processing");
+ return 0;
+ }
+
+ /* Don't apply jitter screening after pointing control is lost. */
+ if (data_expstart > LAST_MJD) {
+ cf_verbose(1, "No pointing control. No jitter correction.");
+ cf_proc_update(infits, CF_PRGM_ID, "SKIPPED");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished processing");
+ return 0;
+ }
+
+ /* Check HKEXISTS before looking for jitter file. */
+ FITS_read_key(infits, TSTRING, "HKEXISTS", hkexists, NULL, &status);
+ if(!strncasecmp(hkexists, "N", 1) || !strncasecmp(hkexists, "n", 1)) {
+ cf_verbose(1, "HKEXISTS = %s. No jitter correction.", hkexists);
+ cf_proc_update(infits, CF_PRGM_ID, "SKIPPED");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished processing");
+ return 0;
+ }
+
+ /* Try to open the jitter file. */
+ FITS_read_key(infits, TSTRING, "JITR_CAL", jitr_cal, NULL, &status);
+ fits_open_file(&jitfits, jitr_cal, READONLY, &status);
+
+ /* If the jitter file is not found, try a lower-case filename. */
+ if (status != 0) {
+ status = 0;
+ jitr_cal[0] = (char) tolower(jitr_cal[0]);
+ fits_open_file(&jitfits, jitr_cal, READONLY, &status);
+ }
+
+ /* If the jitter file is still not found, exit the program */
+ if (status != 0) {
+ cf_verbose(1, "Jitter file not found. No jitter correction.");
+ cf_proc_update(infits, CF_PRGM_ID, "SKIPPED");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished processing");
+ return 0;
+ }
+ cf_verbose(3, "Jitter calibration file = %s", jitr_cal);
+
+ /* Check status keyword in jitter file. Exit if missing or non-zero. */
+ fits_read_key(jitfits, TINT, "JIT_STAT", &jitter_status, comment, &status);
+ if (status != 0) {
+ status = 0;
+ cf_verbose(1, "Keyword JIT_STAT not found. No jitter correction.");
+ cf_proc_update(infits, CF_PRGM_ID, "SKIPPED");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished processing");
+ return 0;
+ }
+ if (jitter_status == 1) {
+ cf_verbose(1, "Keyword JIT_STAT = %d. No jitter correction.",
+ jitter_status) ;
+ cf_proc_update(infits, CF_PRGM_ID, "SKIPPED");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished processing");
+ return 0;
+ }
+
+ /* Read exposure start time from jitter file header */
+ FITS_read_key(jitfits, TDOUBLE, "EXPSTART", &jitr_expstart, NULL, &status);
+
+ /* Check JIT_VERS keyword. Exit if missing or value too low. */
+ fits_read_key(jitfits, TSTRING, "JIT_VERS", jit_vers, NULL, &status);
+ if (status != 0 || atof(jit_vers) < JIT_VERS_MIN) {
+ status = 0;
+ cf_verbose(1, "Jitter file format is out of date. No jitter correction.");
+ cf_proc_update(infits, CF_PRGM_ID, "SKIPPED");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished processing");
+ return 0;
+ }
+
+ /* Read the jitter file. */
+ FITS_movabs_hdu(jitfits, 2, &hdutype, &status);
+ njitr=cf_read_col(jitfits, TLONG, "TIME", (void **) &time_jit);
+ njitr=cf_read_col(jitfits, TFLOAT, "DX", (void **) &dx_jit);
+ njitr=cf_read_col(jitfits, TFLOAT, "DY", (void **) &dy_jit);
+ njitr=cf_read_col(jitfits, TSHORT, "TRKFLG", (void **) &trkflg);
+ FITS_close_file(jitfits, &status);
+
+ /* Correct for difference in data and jitter EXPSTART times */
+ time_diff = cf_nlong((data_expstart - jitr_expstart) * 86400.);
+ cf_verbose(2, "Offset between IDF and jitter EXPSTART values: %d sec",
+ time_diff);
+ for (j=0; j< njitr; j++) time_jit[j] -= time_diff;
+
+ /* Read minimum TRKFLG value and aperture boundaries from PARM_CAL file */
+ FITS_read_key(infits, TSTRING, "PARM_CAL", parmfile, NULL, &status);
+ FITS_open_file(&parmfits, cf_parm_file(parmfile), READONLY, &status);
+ FITS_read_key(parmfits, TSHORT, "TRKFLG", &trkflg_min, NULL, &status);
+ FITS_read_key(parmfits, TFLOAT, "DX_MAX", &dx_max, NULL, &status);
+ FITS_read_key(parmfits, TFLOAT, "DY_MAX", &dy_max, NULL, &status);
+ FITS_close_file(parmfits, &status);
+
+ /* Reject times when tracking is good, but target is out of aperture. */
+ for (j=0; j< njitr; j++) {
+ if (trkflg[j] >= trkflg_min &&
+ (dx_jit[j] < -dx_max || dx_jit[j] > dx_max ||
+ dy_jit[j] < -dy_max || dy_jit[j] > dy_max)) {
+ trkflg[j] = BAD_TRKFLG;
+ }
+ }
+
+ /*
+ * Map times in the jitter file to times in the timeline table.
+ * If trkflg[j] = BAD_TRKFLG, set the jitter flag in the timeline
+ * status array.
+ */
+ for (j=k=0; k < nsec; k++) {
+ while ((float) time_jit[j+1] < ttime[k] && j+1 < njitr) j++;
+ if (trkflg[j] == BAD_TRKFLG) {
+ status_flag[k] |= TEMPORAL_JITR;
+ }
+ }
+
+ /* Deallocate storage space */
+ free(time_jit);
+ free(dy_jit);
+ free(trkflg);
+
+ cf_proc_update(infits, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished processing");
+
+ return (status);
+}
diff --git a/src/libcf/cf_screen_limb_angle.c b/src/libcf/cf_screen_limb_angle.c
new file mode 100644
index 0000000..5e2f54b
--- /dev/null
+++ b/src/libcf/cf_screen_limb_angle.c
@@ -0,0 +1,87 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_screen_limb_angle(fitsfile *infits, long nseconds,
+ * unsigned char *timeline_status,
+ * float *timeline_limb);
+ *
+ * Description: Set the screening bit due to limb angle constraint.
+ *
+ * Arguments: fitsfile *infits Input FITS file pointer
+ * long nseconds Number of points in the timeline table
+ * unsigned char *timeline_status The status flag array
+ * float *timeline_limb The limb angle distance array
+ *
+ * Calls:
+ *
+ * Returns: 0 on success
+ *
+ * History: 10/21/02 1.1 jch Initial coding
+ * 12/20/02 1.3 jch Make sure the flags are unsigned char
+ * 05/20/03 1.4 rdr Added call to cf_proc_check
+ * 09/10/03 1.5 wvd Move test of LIMB_SCR to cf_fuv_init.
+ * 07/21/04 1.7 wvd Delete unused variable limb_cor.
+ * 02/17/05 1.8 wvd Place parentheses around assignment
+ * used as truth value.
+ * 03/30/05 1.9 wvd Write BRITLIMB and DARKLIMB keywords
+ * to IDF file header.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include "calfuse.h"
+
+int
+cf_screen_limb_angle(fitsfile *infits, long nseconds,
+ unsigned char *timeline_status, float *timeline_limb)
+{
+ char CF_PRGM_ID[] = "cf_screen_limb_angle";
+ char CF_VER_NUM[] = "1.9";
+
+ int errflg=0, status=0;
+ long i;
+ char file_name[FLEN_VALUE];
+ double angle, britlimb, darklimb;
+ fitsfile *scrnfits;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ if ((errflg = cf_proc_check(infits, CF_PRGM_ID))) return errflg;
+
+ /*
+ * Open the screening parameters file and read the parameters.
+ */
+ FITS_read_key(infits, TSTRING, "SCRN_CAL", file_name, NULL, &status);
+ FITS_open_file(&scrnfits, cf_parm_file(file_name), READONLY, &status);
+
+ FITS_read_key(scrnfits, TDOUBLE, "BRITLIMB", &britlimb, NULL, &status);
+ FITS_read_key(scrnfits, TDOUBLE, "DARKLIMB", &darklimb, NULL, &status);
+ /*
+ * Write BRITLIMB and DARKLIMB keywords to IDF file header.
+ */
+ FITS_update_key(infits, TDOUBLE, "BRITLIMB", &britlimb, NULL, &status);
+ FITS_update_key(infits, TDOUBLE, "DARKLIMB", &darklimb, NULL, &status);
+ /*
+ * If the limb angle is less than the limit, set the limb-angle bit.
+ * Use day or night limit as appropriate.
+ */
+ for (i = 0; i < nseconds; i++) {
+ angle = darklimb;
+ if ((timeline_status[i] & TEMPORAL_DAY) == TEMPORAL_DAY) {
+ angle = britlimb;
+ }
+ if (timeline_limb[i] < angle) {
+ timeline_status[i] |= TEMPORAL_LIMB;
+ }
+ }
+ FITS_close_file(scrnfits, &status);
+
+ cf_proc_update(infits, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return status;
+}
diff --git a/src/libcf/cf_screen_pulse_height.c b/src/libcf/cf_screen_pulse_height.c
new file mode 100644
index 0000000..3b2c43e
--- /dev/null
+++ b/src/libcf/cf_screen_pulse_height.c
@@ -0,0 +1,79 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_screen_pulse_height(fitsfile *infits, long nevents,
+ * unsigned char *photon_ph,
+ * unsigned char *photon_locflag);
+ *
+ * Description: Set the screening bit according to PHA limits.
+ *
+ * Arguments: fitfile *infits Input FITS file pointer
+ * long nevents Number of points in the photon list
+ * unsigned char *photon_ph The photon pulse height array
+ * unsigned char *photon_locflag The photon location array
+ *
+ * Returns: 0 on success
+ *
+ * History: 11/01/02 1.1 jch Initial coding
+ * 12/20/02 1.3 jch Make sure the flags are "unsigned" char
+ * 02/14/03 1.4 rdr Update phalow and phahigh keywords
+ * in the IDF
+ * 05/20/03 1.5 rdr Add call to cf_proc_check
+ * 09/10/03 1.6 wvd Write NBADPHA as type LONG.
+ * 10/02/03 1.7 wvd Move PHA bit from TEMPORAL to
+ * LOCATION flags.
+ * 05/04/04 1.9 bjg Cosmetic change to prevent
+ * compiler warning with gcc -Wall
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include "calfuse.h"
+
+int
+cf_screen_pulse_height(fitsfile *infits, long nevents, unsigned char *photon_ph,
+ unsigned char *photon_locflag)
+{
+ char CF_PRGM_ID[] = "cf_screen_pulse_height";
+ char CF_VER_NUM[] = "1.9";
+
+ char scrnfile[FLEN_VALUE];
+ unsigned char phalow, phahigh;
+ int errflg=0, status=0;
+ long i, nbadpha=0;
+ fitsfile *scrnfits;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ if ((errflg = cf_proc_check(infits, CF_PRGM_ID))) return errflg;
+
+ /*
+ * Open the screening parameters file and read the parameters.
+ */
+ FITS_read_key(infits, TSTRING, "SCRN_CAL", scrnfile, NULL, &status);
+ FITS_open_file(&scrnfits, cf_parm_file(scrnfile), READONLY, &status);
+ FITS_read_key(scrnfits, TBYTE, "PHALOW", &phalow, NULL, &status);
+ FITS_read_key(scrnfits, TBYTE, "PHAHIGH", &phahigh, NULL, &status);
+ FITS_close_file(scrnfits, &status);
+ /*
+ * Set the pulse-height bit if pulse height is outside limits.
+ */
+ for (i = 0; i < nevents; i++) {
+ if (photon_ph[i] < phalow || photon_ph[i] > phahigh) {
+ photon_locflag[i] |= LOCATION_PHA;
+ nbadpha++;
+ }
+ }
+ FITS_update_key(infits, TLONG, "NBADPHA", &nbadpha, NULL, &status);
+ FITS_update_key(infits, TBYTE, "PHALOW", &phalow, NULL, &status);
+ FITS_update_key(infits, TBYTE, "PHAHIGH", &phahigh, NULL, &status);
+
+ cf_proc_update(infits, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return status;
+}
diff --git a/src/libcf/cf_screen_saa.c b/src/libcf/cf_screen_saa.c
new file mode 100644
index 0000000..c79dc6e
--- /dev/null
+++ b/src/libcf/cf_screen_saa.c
@@ -0,0 +1,83 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_screen_saa(fitsfile *infits, long nseconds,
+ * unsigned char *timeline_status,
+ * float *timeline_long, float *timeline_lat);
+ *
+ * Description: Set the screening bit due to SAA boundary.
+ *
+ * Arguments: fitsfile *infits Input FITS file pointer
+ * long nseconds Number of points in the timeline table
+ * unsigned char *timeline_status The status flag array
+ * float *timeline_long The longitude array
+ * float *timeline_lat The latitude array
+ *
+ * Calls: saa
+ *
+ * Returns: 0 on success
+ *
+ * History: 10/29/02 1.1 jch Initial coding
+ * 12/20/02 1.3 jch Include reading SAA table here
+ * 12/20/02 1.4 wvd Delete call to calfuse.h
+ * 05/20/03 1.5 rdr Add call to cf_proc_check
+ * 08/28/03 1.6 bjg New definition of structure saareg
+ * 09/02/03 1.7 wvd Change calfusettag.h to calfuse.h
+ * 09/10/03 1.8 wvd Tidy up code.
+ * 02/17/05 1.9 wvd Place parentheses around assignment
+ * used as truth value.
+ *
+ ****************************************************************************/
+
+#include <stdlib.h>
+
+#include "calfuse.h"
+
+int
+cf_screen_saa(fitsfile *infits, long nseconds, unsigned char *timeline_status,
+ float *timeline_long, float *timeline_lat)
+{
+ char CF_PRGM_ID[] = "cf_screen_saa";
+ char CF_VER_NUM[] = "1.9";
+
+ char saa_cal[FLEN_VALUE];
+ int errflg=0, status=0, hdutype;
+ long i;
+ saareg saa_bounds;
+ fitsfile *saafits;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+ if ((errflg = cf_proc_check(infits, CF_PRGM_ID))) return errflg;
+
+ /*
+ * Read SAA boundaries from the specified calibration file.
+ */
+ FITS_read_key(infits, TSTRING, "SAAC_CAL", saa_cal, NULL, &status);
+ FITS_open_file(&saafits, cf_cal_file(saa_cal), READONLY, &status);
+ FITS_movabs_hdu(saafits, 2, &hdutype, &status);
+
+ saa_bounds.n_points =
+ cf_read_col(saafits, TFLOAT, "LATITUDE", (void **) &(saa_bounds.lat));
+ (void) cf_read_col(saafits, TFLOAT, "LONGITUDE", (void **) &(saa_bounds.lon));
+
+ fits_close_file(saafits, &status);
+
+ /*
+ * Set the SAA boundary bit if in SAA
+ */
+ for (i = 0; i < nseconds; i++) {
+ if (saa(&saa_bounds, (double)timeline_long[i], (double)timeline_lat[i])) {
+ timeline_status[i] |= TEMPORAL_SAA;
+ }
+ }
+ free(saa_bounds.lon);
+ free(saa_bounds.lat);
+
+ cf_proc_update(infits, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return status;
+}
diff --git a/src/libcf/cf_set_good_time_intervals.c b/src/libcf/cf_set_good_time_intervals.c
new file mode 100644
index 0000000..2ed0d54
--- /dev/null
+++ b/src/libcf/cf_set_good_time_intervals.c
@@ -0,0 +1,157 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_set_good_time_intervals(fitsfile *infits, long nseconds,
+ * float *timeline_time,
+ * unsigned char *timeline_status, GTI *gti);
+ *
+ * Description: Compute the new good time intervals based on the timeline
+ * status flag.
+ *
+ * Arguments: fitsfile *infits Input FITS file pointer
+ * long nseconds Length of timeline table
+ * float *timeline_time Time array of timeline list
+ * unsigned char *timeline_status Status array of timeline list
+ * GIT *gti Good time interval(s)
+ *
+ * Calls:
+ *
+ * Returns: 0 on success
+ * -1 on error
+ *
+ * History: 11/01/02 1.1 jch Initial coding
+ * 12/20/02 1.3 jch Make sure that flags are unsigned char
+ * 05/09/03 1.4 wvd Include D/N screening in GTI's.
+ * Calculate EXPTIME as SUM (GTI's).
+ * Update header keyword EXPTIME.
+ * 05/20/03 1.5 rdr Add call to cf_proc_check
+ * 05/29/03 1.6 wvd Allow lower-case value of DAYNIGHT.
+ * Correct bug in calculation of gti.stop
+ * If in HIST mode, ignore LIMB, SAA,
+ * and BRST flags.
+ * 09/10/03 1.7 wvd Change calfusettag.h to calfuse.h
+ * 10/13/03 1.8 wvd Modify for new timeline table format.
+ * No time skips.
+ * 04/20/04 1.9 bjg Remove unused variables
+ * 12/01/04 1.10 bjg Set gti start and stop to zero
+ * for the single GTI when there are no
+ * Good Times.
+ * 08/23/07 1.11 wvd Change malloc to cf_malloc.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "calfuse.h"
+
+int
+cf_set_good_time_intervals(fitsfile *infits, long nseconds,
+ float *timeline_time, unsigned char *timeline_status, GTI *gti)
+{
+ char CF_PRGM_ID[] = "cf_set_good_time_intervals";
+ char CF_VER_NUM[] = "1.11";
+
+ char daynight[FLEN_VALUE], instmode[FLEN_VALUE];
+ int in_bad; /* True between good-time intervals */
+ int day=FALSE, night=FALSE, errflg=0, status=0;
+ long j, jj;
+ float exptime=0.;
+ double last_good_time=-99;
+ unsigned char TEMPORAL_MASK;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ if ((errflg = cf_proc_check(infits, CF_PRGM_ID) )) return errflg;
+
+ /* Read and interpret DAYNIGHT keyword. */
+
+ FITS_read_key(infits, TSTRING, "DAYNIGHT", daynight, NULL, &status);
+ if (!strncmp(daynight, "D", 1) || !strncmp(daynight, "d", 1)) {
+ day = TRUE;
+ }
+ else if (!strncmp(daynight, "N", 1) || !strncmp(daynight, "n", 1)) {
+ night = TRUE;
+ }
+ else if (!strncmp(daynight, "B", 1) || !strncmp(daynight, "b", 1)) {
+ day = night = TRUE;
+ }
+ else
+ cf_if_error("Unknown DAYNIGHT keyword value: %s", daynight);
+ cf_verbose(3, "DAYNIGHT flag set to %s", daynight);
+
+ /*
+ * Read INSTMODE keyword. If in HIST mode, mask out
+ * LIMB, SAA, and BRST flags. TEMPORAL_DAY is the default.
+ */
+ TEMPORAL_MASK = TEMPORAL_DAY;
+ FITS_read_key(infits, TSTRING, "INSTMODE", instmode, NULL, &status);
+ if (!strncmp(instmode, "HIST", 4)) {
+ TEMPORAL_MASK |= TEMPORAL_LIMB;
+ TEMPORAL_MASK |= TEMPORAL_SAA;
+ TEMPORAL_MASK |= TEMPORAL_BRST;
+ }
+
+ /*
+ * Determine limits of good-time intervals.
+ */
+ gti->start = (double *) cf_malloc(sizeof(double)*nseconds);
+ gti->stop = (double *) cf_malloc(sizeof(double)*nseconds);
+
+ in_bad = TRUE;
+ jj = -1;
+ for (j = 0; j < nseconds; j++) {
+ /*
+ * If we are in a good time interval and were in a bad one,
+ * increment jj and set gti->start.
+ */
+ if (!(timeline_status[j] & ~TEMPORAL_MASK) &&
+ ((day && (timeline_status[j] & TEMPORAL_DAY)) ||
+ (night && (timeline_status[j] ^ TEMPORAL_DAY)))) {
+ if (in_bad) {
+ jj++;
+ gti->start[jj] = timeline_time[j];
+ in_bad = FALSE;
+ }
+ /*
+ * If we are in a good time interval and were in a good one,
+ * update last_good_time.
+ */
+ last_good_time = timeline_time[j];
+ }
+ /*
+ * If we are in a bad time interval and were in a good one,
+ * set in_bad to TRUE and update gti->stop.
+ */
+ else if (in_bad == FALSE) {
+ gti->stop[jj] = timeline_time[j];
+ in_bad = TRUE;
+ }
+ }
+ gti->ntimes = jj + 1;
+
+ /* If the entire exposure is bad, return a zero-length GTI. */
+ if (gti->ntimes == 0) {
+ gti->ntimes = 1;
+ gti->start[0] = gti->stop[0] = 0;
+ }
+
+ /* Calculate total exposure time and write it to file header */
+ for (j = 0; j < gti->ntimes; j++)
+ exptime += gti->stop[j] - gti->start[j];
+ FITS_update_key(infits, TFLOAT, "EXPTIME", &exptime, NULL, &status);
+
+ /* Results to trailer file. */
+ cf_verbose(2, "GTI's:\t START\t STOP");
+ for (j = 0; j < gti->ntimes; j++)
+ cf_verbose(2, "%3d\t%6.0f\t%6.0f", j, gti->start[j], gti->stop[j]);
+ cf_verbose(2, "Total EXPTIME = %6.0f", exptime);
+
+ cf_proc_update(infits, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return status;
+}
diff --git a/src/libcf/cf_set_photon_flags.c b/src/libcf/cf_set_photon_flags.c
new file mode 100644
index 0000000..de2b623
--- /dev/null
+++ b/src/libcf/cf_set_photon_flags.c
@@ -0,0 +1,425 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_set_photon_flags(fitsfile *infits, long nevents,
+ * float *photon_time,
+ * float *photon_weight,
+ * unsigned char *photon_status,
+ * unsigned char *photon_locflag,
+ * long nseconds, float *timeline_time,
+ * unsigned char *timeline_status);
+ *
+ * Description: Copy status flags from the timeline table to the photon list.
+ * Update header keywords.
+ *
+ * For histogram data, we set only the DAY flag.
+ * Other situations are treated as follows:
+ *
+ * DAY All photons flagged if exposure >10% day
+ *
+ * LIMB True for all if true for one.
+ * SAA Do not flag photons or modify EXPTIME.
+ * BRST Instead, set header keyword EXP_STAT.
+ * Ignore all violations during the
+ * first and last 20 seconds of an exposure.
+ *
+ * HV Modify EXPTIME, but do not flag photons.
+ * JITR Ditto.
+ *
+ * OPUS Ignore for now.
+ * USER Ignore for now.
+ *
+ * Arguments: fitsfile *infits Input FITS file pointer
+ * long nevents Number of points in the photon list
+ * float *photon_time Time array of photon events
+ * float *photon_weight Scale factor for each photon
+ * unsigned char *photon_status Status flags for photon events
+ * unsigned char *photon_locflag Location flags for photon events
+ * long nseconds Number of points in the timeline table
+ * float *timeline_time Time array of timeline list
+ * unsigned char *timeline_status Status flags for timeline list
+ *
+ * Calls:
+ *
+ * Returns: 0 on success
+ *
+ * History: 10/31/02 1.1 jch Initial coding
+ * 12/20/02 1.3 jch Make sure the flags are "unsigned" char
+ * 05/09/03 1.4 wvd Change EXP* keywords to type long.
+ * Consider DAYNIGHT keyword in evaluation
+ * of NBADEVNT and EXP_BAD.
+ * Use frame_tolerance in time comparison.
+ * Use photon_locflag in counting NBADEVNT
+ * 05/20/03 1.5 rdr Add call to cf_proc_check
+ * 05/29/03 1.6 wvd Modify to handle HIST data:
+ * Use single flag for all photon events.
+ * Ignore LIMB, SAA, and BRST flags when
+ * counting EXP_BAD. Scale NBADEVNT by
+ * photon_weight[i]/TOT_DEAD.
+ * 06/02/03 1.7 wvd Properly deal with GTI's.
+ * 07/14/03 1.8 wvd Read DAYNIGHT from SCRN_CAL and
+ * populate IDF header keyword.
+ * Use FRAME_TOLERANCE from calfuse.h.
+ * 09/15/03 1.9 wvd Copy flags only to photons whose times
+ * are within 1s of timeline table time.
+ * 10/02/03 1.10 wvd Change timeline table: no breaks in
+ * time; add TEMPORAL_OPUS flag.
+ * 10/13/03 1.11 wvd Use same scheme for calculating
+ * EXP_BAD and exp_screen.
+ * 04/20/04 1.12 bjg Cosmetic change to prevent
+ * compiler warning with gcc -Wall
+ * 05/20/04 1.13 wvd Don't flag HIST photons as bad
+ * when HV or JITR are bad.
+ * We'll reduce EXPTIME instead.
+ * 06/01/04 1.14 wvd For HIST data, set only DAY flag.
+ * Write other flags to EXP_STAT keyword.
+ * 06/28/04 1.15 wvd If > 90% of exposure is lost to a
+ * burst, jitter, etc., write cause to
+ * trailer file.
+ * 03/24/05 1.16 wvd If EXP_STAT != 0, don't change it.
+ * 01/01/07 1.17 wvd Remove mention of FIFO flag.
+ * 07/18/08 1.18 wvd If EXP_STAT = 2, target is bright
+ * earth or airglow. Set limb-angle
+ * flags to zero.
+ * 08/22/08 1.19 wvd Compare EXP_STAT to TEMPORAL_LIMB,
+ * rather than a particular value.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include "calfuse.h"
+
+#define HIST_PAD 20 /* Ignore first and last 20 s of each exposure. */
+
+/*
+ * Compute integration time lost to a particular screening step.
+ * Ignore times outside of OPUS-defined good-time intervals.
+ */
+static long
+time_loss(long nseconds, unsigned char *timeline_status,
+ unsigned char criterion)
+{
+ long exp_screen = 0, j;
+
+ for (j = 0; j < nseconds; j++) {
+ if (timeline_status[j] & TEMPORAL_OPUS) continue;
+ if (timeline_status[j] & criterion) exp_screen++;
+ }
+
+ return(exp_screen);
+}
+
+
+/*
+ * Compute integration time lost to a particular screening step,
+ * but ignore first and last HIST_PAD seconds of exposure.
+ */
+static long
+time_loss_pad(long nseconds, unsigned char *timeline_status,
+ unsigned char criterion)
+{
+ long exp_screen = 0, j;
+
+ for (j = HIST_PAD; j < nseconds - HIST_PAD; j++) {
+ if (timeline_status[j] & TEMPORAL_OPUS) continue;
+ if (timeline_status[j] & criterion) exp_screen++;
+ }
+ return (exp_screen);
+}
+
+
+/*
+ * In HIST mode, generate a status flag good for all photons.
+ * Except for D/N flag, we ignore first and last 20 seconds of exposure.
+ * D/N flag is written to TEMPORAL_MASK.
+ * LIMB/SAA/BRST flags are written to EXP_STAT.
+ */
+static void
+generate_temporal_mask(float rawtime, long nseconds, float *timeline_time,
+ unsigned char *timeline_status, unsigned char *TEMPORAL_MASK,
+ unsigned char *EXP_STAT, char *comment)
+{
+ long exp_day;
+
+ *EXP_STAT = *TEMPORAL_MASK = 0;
+
+ /*
+ * Day if exp_day > 10% of total exposure time.
+ */
+ exp_day=time_loss(nseconds, timeline_status, TEMPORAL_DAY);
+ if (exp_day * 10 > rawtime) {
+ *TEMPORAL_MASK = TEMPORAL_DAY;
+ sprintf(comment, "Exposure more than 10 percent day");
+ cf_verbose(1, comment);
+ }
+ /*
+ * If exposure is less than 40 seconds long, we're done.
+ */
+ if (rawtime < 2. * HIST_PAD) return;
+
+ /*
+ * Are there limb-angle violations?
+ */
+ if (time_loss_pad(nseconds, timeline_status, TEMPORAL_LIMB)) {
+ *EXP_STAT |= TEMPORAL_LIMB;
+ sprintf(comment, "Data suspect: Limb-angle violation");
+ cf_verbose(1, comment);
+ }
+ /*
+ * Do we ever enter the SAA?
+ */
+ if (time_loss_pad(nseconds, timeline_status, TEMPORAL_SAA)) {
+ *EXP_STAT |= TEMPORAL_SAA;
+ sprintf(comment, "Data suspect: SAA violation");
+ cf_verbose(1, comment);
+ }
+ /*
+ * Are there any bursts?
+ */
+ if (time_loss_pad(nseconds, timeline_status, TEMPORAL_BRST)) {
+ *EXP_STAT |= TEMPORAL_BRST;
+ sprintf(comment, "Data suspect: Burst detected");
+ cf_verbose(1, comment);
+ }
+
+ /*
+ * Is the detector voltage ever too low?
+ * If so, just issue a warning; we'll modify the exposure time later.
+ */
+ if (time_loss_pad(nseconds, timeline_status, TEMPORAL_HV))
+ cf_verbose(1, "EXPTIME modified: Detector voltage low.");
+ /*
+ * Was any time rejected by the jitter routine?
+ * If so, just issue a warning; we'll modify the exposure time later.
+ */
+ if (time_loss_pad(nseconds, timeline_status, TEMPORAL_JITR))
+ cf_verbose(1, "EXPTIME modified: Target out of aperture.");
+
+ return;
+}
+
+
+int
+cf_set_photon_flags(fitsfile *infits, long nevents, float *photon_time,
+ float *photon_weight, unsigned char *photon_status,
+ unsigned char *photon_locflag, long nseconds,
+ float *timeline_time, unsigned char *timeline_status)
+{
+ char CF_PRGM_ID[] = "cf_set_photon_flags";
+ char CF_VER_NUM[] = "1.19";
+
+ char comment[FLEN_COMMENT], daynight[FLEN_KEYWORD];
+ char instmode[FLEN_KEYWORD], scrnfile[FLEN_KEYWORD];
+ long i, j, k;
+
+ long nbadevnt=0; /* number of events deleted in screening */
+ long exp_bad=0; /* integration time lost to screening */
+ long exp_brst=0; /* integration time lost to event bursts */
+ long exp_hv=0; /* integration time lost to low detector voltage */
+ long exp_jitr=0; /* integration time lost to jitter */
+ long exp_limb=0; /* integration time with low limb angle */
+ long exp_saa=0; /* integration time while in SAA */
+ long exp_day=0; /* integration time during day after screening */
+ long expnight=0; /* integration time during night after screening */
+ int day = FALSE, night = FALSE;
+ int errflg = 0; /* value returned by cf_proc_check */
+ int status = 0; /* CFITSIO status */
+ int expstat; /* initial value of EXP_STAT keyword */
+ float deadtime, rawtime;
+ fitsfile *scrnfits;
+ unsigned char EXP_STAT, TEMPORAL_MASK;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ if ((errflg = cf_proc_check(infits, CF_PRGM_ID) )) return errflg;
+
+ /* Read and interpret DAYNIGHT keyword. Copy to IDF header. */
+
+ FITS_read_key(infits, TSTRING, "SCRN_CAL", scrnfile, NULL, &status);
+ FITS_open_file(&scrnfits, cf_parm_file(scrnfile), READONLY, &status);
+ FITS_read_key(scrnfits, TSTRING, "DAYNIGHT", daynight, NULL, &status);
+ FITS_close_file(scrnfits, &status);
+
+ if (!strncmp(daynight, "D", 1) || !strncmp(daynight, "d", 1)) {
+ day = TRUE;
+ }
+ else if (!strncmp(daynight, "N", 1) || !strncmp(daynight, "n", 1)) {
+ night = TRUE;
+ }
+ else if (!strncmp(daynight, "B", 1) || !strncmp(daynight, "b", 1)) {
+ day = night = TRUE;
+ }
+ else
+ cf_if_error("Unknown DAYNIGHT keyword value: %s", daynight);
+
+ /*
+ * Read EXP_STAT, RAWTIME, INSTMODE and TOT_DEAD keywords.
+ */
+ FITS_read_key(infits, TINT, "EXP_STAT", &expstat, NULL, &status);
+ FITS_read_key(infits, TFLOAT, "RAWTIME", &rawtime, NULL, &status);
+ FITS_read_key(infits, TSTRING, "INSTMODE", instmode, NULL, &status);
+ FITS_read_key(infits, TFLOAT, "TOT_DEAD", &deadtime, NULL, &status);
+
+ /*
+ * If EXP_STAT = TEMPORAL_LIMB, target is bright earth or airglow.
+ * Compute EXP_LIMB, then set limb-angle flags to 0.
+ */
+ exp_limb=time_loss(nseconds, timeline_status, TEMPORAL_LIMB);
+ if (expstat == (int) TEMPORAL_LIMB)
+ for (i = 0; i < nseconds-1; i++)
+ timeline_status[i] &= ~TEMPORAL_LIMB;
+
+ /*
+ * If in HIST mode:
+ * Generate TEMPORAL_MASK
+ * Copy status flags from TEMPORAL_MASK to photon list.
+ * Count bad photons.
+ */
+ if (!strncmp(instmode, "HIST", 4)) {
+ generate_temporal_mask(rawtime, nseconds, timeline_time,
+ timeline_status, &TEMPORAL_MASK, &EXP_STAT, comment);
+ for (i = 0; i < nevents; i++) {
+ photon_status[i] = TEMPORAL_MASK;
+ /*
+ * A bad photon is one for which
+ * - something other than AIRGLOW is wrong; or
+ * - you don't want day, but this one is; or
+ * - you don't want night, but this one is.
+ */
+ if ((photon_locflag[i] & ~LOCATION_AIR) ||
+ (!day && (photon_status[i] & TEMPORAL_DAY)) ||
+ (!night && (photon_status[i] ^ TEMPORAL_DAY)))
+ nbadevnt += (long)(photon_weight[i]/deadtime + 0.5);
+ }
+ if (EXP_STAT && !expstat)
+ FITS_update_key(infits, TBYTE, "EXP_STAT", &EXP_STAT,
+ comment, &status);
+ }
+
+ /*
+ * If in TTAG mode:
+ * Copy status flags from timeline table to photon list.
+ * Confirm that photon time is included in timeline table.
+ * Count bad photons.
+ */
+ else {
+ for (i = k = 0; i < nevents; i++) {
+ while (photon_time[i] > timeline_time[k+1]-FRAME_TOLERANCE &&
+ k < nseconds-1) { k++; }
+
+ if (photon_time[i] - timeline_time[k] < 2.0)
+ photon_status[i] = timeline_status[k];
+ else
+ cf_if_warning("Time %g not included in timeline table",
+ photon_time[i]);
+
+ /*
+ * A bad photon is one for which
+ * - something other than DAYNIGHT is wrong; or
+ * - something other than AIRGLOW is wrong; or
+ * - you don't want day, but this one is; or
+ * - you don't want night, but this one is.
+ */
+ if ((photon_status[i] & ~TEMPORAL_DAY) ||
+ (photon_locflag[i] & ~LOCATION_AIR) ||
+ (!day && (photon_status[i] & TEMPORAL_DAY)) ||
+ (!night && (photon_status[i] ^ TEMPORAL_DAY)))
+ nbadevnt++;
+ }
+ }
+
+ /*
+ * Now count day, night, and bad seconds.
+ * If in HIST mode, mask out LIMB, SAA, and BRST flags.
+ * Exclude OPUS-defined bad times.
+ */
+ TEMPORAL_MASK = TEMPORAL_DAY;
+
+ if (!strncmp(instmode, "HIST", 4)) {
+ TEMPORAL_MASK |= TEMPORAL_LIMB;
+ TEMPORAL_MASK |= TEMPORAL_SAA;
+ TEMPORAL_MASK |= TEMPORAL_BRST;
+ }
+
+ for (j = 0; j < nseconds; j++) {
+ if (timeline_status[j] & TEMPORAL_OPUS)
+ continue;
+ if (timeline_status[j] & ~TEMPORAL_MASK)
+ exp_bad++;
+ else if (timeline_status[j] & TEMPORAL_DAY)
+ exp_day++;
+ else
+ expnight++;
+ }
+ /*
+ * If user rejects either day or night data, add to EXP_BAD.
+ * If user rejects night data, set EXPNIGHT = 0.
+ */
+ if (!day) {
+ exp_bad += exp_day;
+ }
+ if (!night) {
+ exp_bad += expnight;
+ expnight = 0;
+ }
+
+ /*
+ * Compute EXP_BRST
+ */
+ exp_brst=time_loss(nseconds, timeline_status, TEMPORAL_BRST);
+ /*
+ * Compute EXP_HV
+ */
+ exp_hv=time_loss(nseconds, timeline_status, TEMPORAL_HV);
+ /*
+ * Compute EXP_JITR
+ */
+ exp_jitr=time_loss(nseconds, timeline_status, TEMPORAL_JITR);
+ /*
+ * Compute EXP_LIMB (already done)
+ exp_limb=time_loss(nseconds, timeline_status, TEMPORAL_LIMB);
+ */
+ /*
+ * Compute EXP_SAA
+ */
+ exp_saa=time_loss(nseconds, timeline_status, TEMPORAL_SAA);
+
+ /*
+ * If data are in time-tag mode and more than 90% of time was
+ * was rejected by screening, say why.
+ */
+ if (!strncmp(instmode, "TTAG", 4)) {
+ if (exp_brst > 0.9 * rawtime)
+ cf_verbose(1, "%d sec lost to bursts.", exp_brst);
+ if (exp_hv > 0.9 * rawtime)
+ cf_verbose(1, "%d sec lost to low voltage.", exp_hv);
+ if (exp_jitr > 0.9 * rawtime)
+ cf_verbose(1, "%d sec lost to jitter.", exp_jitr);
+ if (exp_limb > 0.9 * rawtime)
+ cf_verbose(1, "%d sec lost to low limb angle.", exp_limb);
+ if (exp_saa > 0.9 * rawtime)
+ cf_verbose(1, "%d sec lost to SAA.", exp_saa);
+ }
+
+ /*
+ * Update the header keywords
+ */
+ FITS_update_key(infits, TLONG, "NBADEVNT", &nbadevnt, NULL, &status);
+ FITS_update_key(infits, TLONG, "EXP_BAD", &exp_bad, NULL, &status);
+ FITS_update_key(infits, TLONG, "EXP_BRST", &exp_brst, NULL, &status);
+ FITS_update_key(infits, TLONG, "EXP_HV", &exp_hv, NULL, &status);
+ FITS_update_key(infits, TLONG, "EXP_JITR", &exp_jitr, NULL, &status);
+ FITS_update_key(infits, TLONG, "EXP_LIM", &exp_limb, NULL, &status);
+ FITS_update_key(infits, TLONG, "EXP_SAA", &exp_saa, NULL, &status);
+ FITS_update_key(infits, TLONG, "EXPNIGHT", &expnight, NULL, &status);
+ FITS_update_key(infits, TSTRING, "DAYNIGHT", &daynight, NULL, &status);
+
+ cf_proc_update(infits, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return status;
+}
diff --git a/src/libcf/cf_set_user_gtis.c b/src/libcf/cf_set_user_gtis.c
new file mode 100644
index 0000000..54a88d5
--- /dev/null
+++ b/src/libcf/cf_set_user_gtis.c
@@ -0,0 +1,100 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_set_user_gtis(fitsfile *infits, long nseconds,
+ * float *timeline_times,
+ * unsigned char *timeline_status);
+ *
+ * Description: Apply user-defined good time intervals to timeline table.
+ *
+ * Arguments: fitsfile *infits Input FITS file pointer
+ * long nseconds Number of points in the timeline table
+ * float *timeline_times Array of times
+ * unsigned char *timeline_status The status flag array
+ *
+ * Calls:
+ *
+ * Returns: 0 on success
+ *
+ * History: 09.10.03 1.1 bjg Initial coding
+ *
+ * 09.12.03 1.2 bjg Bug fix. When nusergti was zero or less
+ * this function set all the photons to
+ * bad. Now it doesn't change anything.
+ * 04.20.04 1.3 bjg Remove unused variables
+ * Change format to match arg type in
+ * sprintf.
+ *
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include "calfuse.h"
+
+int
+cf_set_user_gtis(fitsfile *infits, long nseconds,
+ float *timeline_times, unsigned char *timeline_status)
+{
+ char CF_PRGM_ID[] = "cf_set_user_gtis";
+ char CF_VER_NUM[] = "1.3";
+
+ int errflg=0, status=0, nusergti;
+ long i,n;
+ char file_name[FLEN_VALUE];
+ float limit1,limit2;
+ char keywd1[FLEN_KEYWORD],keywd2[FLEN_KEYWORD];
+ fitsfile *scrnfits;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ if ((errflg = cf_proc_check(infits, CF_PRGM_ID) )) return errflg;
+
+ /*
+ * Open the screening parameters file and read the good time intervals.
+ */
+ FITS_read_key(infits, TSTRING, "SCRN_CAL", file_name, NULL, &status);
+ FITS_open_file(&scrnfits, cf_parm_file(file_name), READONLY, &status);
+
+ FITS_read_key(scrnfits, TINT, "NUSERGTI", &nusergti, NULL, &status);
+
+ if (nusergti>0){
+
+ i=0;
+
+ for (n=1;n<=nusergti;n++){
+ sprintf(keywd1, "GTIBEG%02ld",n);
+ FITS_read_key(scrnfits, TFLOAT, keywd1, &limit1, NULL, &status);
+ sprintf(keywd2, "GTIEND%02ld",n);
+ FITS_read_key(scrnfits, TFLOAT, keywd2, &limit2, NULL, &status);
+
+
+ while ((i<nseconds)&&(timeline_times[i]<limit1)) {
+ timeline_status[i] |= TEMPORAL_USER;
+ i++;
+ }
+
+ while ((i<nseconds)&&(timeline_times[i]<limit2)) {
+ i++;
+ }
+
+ }
+
+
+ while (i<nseconds){
+ timeline_status[i] |= TEMPORAL_USER;
+ i++;
+ }
+
+ }
+
+ FITS_close_file(scrnfits, &status);
+
+ cf_proc_update(infits, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return status;
+}
diff --git a/src/libcf/cf_source_aper.c b/src/libcf/cf_source_aper.c
new file mode 100644
index 0000000..3b1cb60
--- /dev/null
+++ b/src/libcf/cf_source_aper.c
@@ -0,0 +1,67 @@
+/**************************************************************************
+ * Johns Hopkins University
+ * Center for Astrophysical Sciences
+ * FUSE
+ *************************************************************************
+ *
+ * Synopsis: cf_source_aper(infits, ap)
+ *
+ * Description: Procedure to determine which aperture contains the source
+ *
+ * Arguments : fitsfile infits Pointer to Integmediate Data File
+ * int *ap Array containing the two active
+ * apertures
+ *
+ * Returns int srctype Type of source in aperture
+ *
+ * HISTORY 12/12/02 v1.1 rdr First delivery
+ * 12/13/02 v1.3 wvd Changed file name
+ * 02/24/03 v1.4 peb Changed include file to calfitsio.h
+ * 04/17/03 v1.5 wvd Change to cf_source_aper throughout.
+ * 07/11/03 v1.6 rdr Change so that src_type returns
+ * 1 rather than 3 for extended source
+ * 08/21/03 v1.7 wvd If APERTURE=RFPT, treat as LWRS.
+ * 04/09/04 v1.8 bjg Include string.h
+ * Remove unused variables
+ *
+ ************************************************************************/
+
+#include "calfuse.h"
+#include <string.h>
+
+int
+cf_source_aper(fitsfile *infits, int *active_ap )
+{
+ /* char CF_PRGM_ID[] = "cf_source_aper"; */
+ /* char CF_VER_NUM[] = "1.8"; */
+
+ char aper[FLEN_CARD], src[FLEN_CARD];
+ int status=0, hdutype, src_type;
+
+ /* Determine the active aperture */
+ FITS_movabs_hdu(infits, 1, &hdutype, &status);
+ FITS_read_key(infits, TSTRING, "APERTURE", aper, NULL, &status);
+
+ /* Specify the channels of the active aperture */
+ if (!strncmp(aper,"HIRS",4)) {
+ active_ap[0] = 1;
+ active_ap[1] = 5; }
+ else if (!strncmp(aper,"MDRS",4)) {
+ active_ap[0] = 2;
+ active_ap[1] = 6; }
+ else if (!strncmp(aper,"LWRS",4) || !strncmp(aper,"RFPT",4)) {
+ active_ap[0] = 3;
+ active_ap[1] = 7;
+ }
+ else
+ cf_if_error("APERTURE keyword corrupted.");
+ cf_verbose(3, "APERTURE = %s, channels are %d and %d",
+ aper, active_ap[0], active_ap[1]);
+
+ FITS_read_key(infits, TSTRING,"SRC_TYPE", src, NULL, &status);
+ src_type = 0;
+ if (!strncmp(src,"E",1) || !strncmp(src,"e",1) ) src_type = 1;
+
+ cf_verbose(3, "SRC_TYPE = %s, source type = %d", src, src_type);
+ return src_type;
+}
diff --git a/src/libcf/cf_standard_or_optimal_extraction.c b/src/libcf/cf_standard_or_optimal_extraction.c
new file mode 100644
index 0000000..2b2a6fa
--- /dev/null
+++ b/src/libcf/cf_standard_or_optimal_extraction.c
@@ -0,0 +1,75 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_standard_or_optimal_extraction (fitsfile *header,
+ * int *optimal)
+ *
+ * Description: Sets optimal to TRUE if optimal extraction is desired.
+ * FALSE if RUN_OPTI = 'NO' in parm*.fit file.
+ * FALSE if SRC_TYPE[0] = 'E' in file header.
+ *
+ * Arguments: fitsfile *header Pointer to IDF FITS file header
+ * int *optimal TRUE if optimal extraction desired
+ *
+ * Calls:
+ *
+ * Returns: 0 on success
+ *
+ * History: 03/02/03 1.1 wvd Initial coding.
+ * 03/12/03 1.2 wvd Write value of optimal to trailer file.
+ * 03/19/03 1.3 wvd Read SRC_TYPE, not SP_TYPE.
+ * 03/19/03 1.4 wvd Add call to cf_timestamp at start.
+ * Change printf to cf_verbose
+ * 09/30/03 1.5 wvd Check for lower-case value of
+ * RUN_OPTI.
+ * 03/15/05 1.6 wvd No optimal extraction for HIST targets
+ * unless try_optimal = TRUE;
+ * 03/18/05 1.7 wvd v1.6 was a bad idea. Returning to v1.5
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include "calfuse.h"
+
+static char CF_PRGM_ID[] = "cf_standard_or_optimal_extraction";
+static char CF_VER_NUM[] = "1.7";
+
+int
+cf_standard_or_optimal_extraction(fitsfile *header, int *optimal)
+{
+ char parm_file[FLEN_VALUE], instmode[FLEN_VALUE];
+ char run_opti[FLEN_VALUE], src_type[FLEN_VALUE];
+ int status=0;
+ fitsfile *parmfits;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin processing");
+
+ /* Read SRC_TYPE from IDF header. */
+ FITS_read_key(header, TSTRING, "SRC_TYPE", src_type, NULL, &status);
+ FITS_read_key(header, TSTRING, "INSTMODE", instmode, NULL, &status);
+
+ /* Read RUN_OPTI from PARM_CAL. */
+ FITS_read_key(header, TSTRING, "PARM_CAL", parm_file, NULL, &status);
+ FITS_open_file(&parmfits, cf_parm_file(parm_file), READONLY, &status);
+ FITS_read_key(parmfits, TSTRING, "RUN_OPTI", run_opti, NULL, &status);
+ FITS_close_file(parmfits, &status);
+
+ /* Determine value of optimal. */
+ *optimal = TRUE;
+ if (*run_opti != 'Y' && *run_opti != 'y') {
+ cf_verbose (1, "RUN_OPTI = %s. No optimal extraction.", run_opti);
+ *optimal = FALSE;
+ }
+ if (*src_type != 'P') {
+ cf_verbose (1, "SRC_TYPE = %s. No optimal extraction.", src_type);
+ *optimal = FALSE;
+ }
+ if (*optimal) cf_verbose (1, "Attempting optimal extraction.");
+
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return status;
+}
diff --git a/src/libcf/cf_target_count_rate.c b/src/libcf/cf_target_count_rate.c
new file mode 100644
index 0000000..eab5b26
--- /dev/null
+++ b/src/libcf/cf_target_count_rate.c
@@ -0,0 +1,112 @@
+/**************************************************************************
+ * Johns Hopkins University
+ * Center for Astrophysical Sciences
+ * FUSE
+ *************************************************************************
+ *
+ * Synopsis: cf_target_count_rate(header, nevents, ptime, weight,
+ * channel, locflags, ntimes, ttime, rate_lif, rate_sic)
+ *
+ * Description: Determines the count rate in the target aperture for both
+ * LiF and SiC channels. Excludes airglow lines.
+ *
+ * Arguments: fitsfile *header : pointer to IDF header
+ * long nevents : number of photon events in the file
+ * float *ptime : detection time for each photon
+ * float *weight : weight associated with each photon
+ * unsigned char *channel: aperture associated with each photon
+ * unsigned char *locflags: location flag array - flags the
+ * geocoronal photons
+ * long ntimes : number of seconds tabulated in timeline
+ * float *ttime : tabulated times in the timeline
+ * float *rate_lif, *rate_sic :
+ * count rates through the Lif and SiC
+ * apertures, excluding geocoronal emission
+ *
+ * Calibration files required: None
+ *
+ * Returns: 0 on success
+ *
+ *
+ * HISTORY: 03/03/03 v1.1 RDR started work
+ * 03/05/03 v1.2 wvd change name of subroutine & install
+ * 03/10/03 v1.21 rdr changed specification of locflags
+ * from char to unsigned char
+ * 05/20/03 v1.3 rdr Added call to cf_proc_check
+ * 05/22/03 v1.5 wvd Direct cf_error_init to stderr
+ * 06/02/03 v1.6 wvd Implement cf_verbose throughout.
+ * 06/04/03 v1.7 wvd Revise scheme for summing counts.
+ * 08/20/03 v1.8 wvd Use FRAME_TOLERANCE from calfuse.h,
+ * change channel to unsigned char.
+ * 09/15/03 v1.9 wvd Test that photon times match
+ * timeline times to within 1 sec.
+ * 10/06/03 v1.10 wvd Changed screen to locflags throughout.
+ * 12/29/06 v1.11 wvd Sum weights rather than counts.
+ * Convert output arrays to type float.
+ * Scale HIST count rate by DET_DEAD.
+ * 04/07/07 v1.12 wvd Clean up compiler warnings.
+ * 04/07/07 v1.13 wvd Clean up compiler warnings.
+ *
+ **************************************************************************/
+
+#include <string.h>
+#include "calfuse.h"
+
+int
+cf_target_count_rate(fitsfile *header, long nevents, float *ptime,
+ float *weight, unsigned char *channel, unsigned char *locflags,
+ long ntimes, float *ttime, float *rate_lif, float *rate_sic) {
+
+ char CF_PRGM_ID[] = "cf_target_count_rate" ;
+ char CF_VER_NUM[] = "1.13" ;
+
+ char instmode[FLEN_VALUE];
+ int errflg=0, status=0, active_ap[2] ;
+ float delta_max = 1.0 + FRAME_TOLERANCE;
+ float det_dead;
+ long j, k ;
+
+ /* Initialize error checking. */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+
+ /* Enter a time stamp into the log. */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ /* Confirm that this subroutine should be run. */
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ /* Determine the source aperture from the header data. */
+ FITS_read_key(header, TSTRING, "INSTMODE", instmode, NULL, &status);
+ (void) cf_source_aper(header, active_ap) ;
+
+ /*
+ * For histogram data, scale the LiF and SiC count rates, which come
+ * from the housekeeping data file, by DET_DEAD.
+ */
+ if (!strncmp(instmode, "HIST", 4)) {
+ FITS_read_key(header, TFLOAT, "DET_DEAD", &det_dead, NULL, &status);
+ for (k = 0; k < ntimes; k++) rate_lif[k] *= det_dead;
+ for (k = 0; k < ntimes; k++) rate_sic[k] *= det_dead;
+ }
+
+ /*
+ * For time-tag data, count the photons arriving during each second
+ * of the exposure. Exclude events near airglow regions.
+ */
+ else {
+ for (k = 0; k < ntimes; k++) rate_lif[k] = rate_sic[k] = 0.;
+ for (j=k=0; j<nevents; j++) {
+ while(ttime[k+1]-FRAME_TOLERANCE < ptime[j] && k+1 < ntimes)
+ k++;
+ if (!(locflags[j] & LOCATION_AIR) &&
+ (ptime[j] - ttime[k] < delta_max)) {
+ if (channel[j] == active_ap[0]) rate_lif[k] += weight[j] ;
+ if (channel[j] == active_ap[1]) rate_sic[k] += weight[j] ;
+ }
+ }
+ }
+
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished processing");
+ return (status);
+}
diff --git a/src/libcf/cf_thermal_distort.c b/src/libcf/cf_thermal_distort.c
new file mode 100644
index 0000000..fcfb2e6
--- /dev/null
+++ b/src/libcf/cf_thermal_distort.c
@@ -0,0 +1,258 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_thermal_distort(fitsfile *header, long nevents,
+ * float *xfarf, float *yfarf, float *weight,
+ * unsigned char *locflags)
+ *
+ * Description: Corrects the X position of the event for thermal distortion
+ * using the locations of the stim pulses. The correction is
+ * made only to stim pulses and events in the detector active
+ * region.
+ *
+ * Arguments: fitsfile *header Pointer to FITS file containing the
+ * header of the intermediate data file
+ * long nevents The number of events
+ * float *xfarf An array of event X positions
+ * float *yfarf An array of event Y positions
+ * float *weight Number of photons per pixel
+ * unsigned char *locflags Location flags for each event
+ *
+ * Calls: cf_estimate_drift_coefficients
+ * cf_get_stim_positions These are static functions.
+ *
+ * Return: 0 on success
+ *
+ * History: 08/06/02 1.1 peb Begin work
+ * 11/11/02 1.2 peb Corrected function description and
+ * added cf_timestamp after ELEC_COR
+ * check.
+ * 11/12/02 1.3 peb Sets left and right stim pulse flags.
+ * 11/12/02 1.4 peb Added check to move only events in
+ * stim-pulse and active region.
+ * 03/11/03 1.5 wvd Changed locflags to unsigned char
+ * 05/20/03 1.6 rdr Added cf_proc_check call
+ * 07/29/03 1.8 wvd If cf_proc_check fails, return errflg.
+ * 02/27/04 1.9 rdr Added weights to the calculation -
+ * needed for histogram mode
+ * 03/01/04 1.10 rdr Corrected bug in procedure for finding
+ * stim pulses
+ * 07/21/04 1.13 wvd Delete variable i; unused.
+ * 03/03/05 1.14 wvd Read thermal coefficients from STIM_CAL
+ * file if STIM pulses are unavailable.
+ * 04/06/05 1.15 wvd Require at least 500 counts in a stim
+ * pulse to compute a centroid.
+ * 10/05/07 1.16 bot Corrected jmax in estimate_drift
+ *
+ ****************************************************************************/
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "calfuse.h"
+
+#define STIMS (LOCATION_STIML|LOCATION_STIMR)
+
+static int
+cf_estimate_drift_coefficients (fitsfile *header, float *x0, float *x1,
+ float *y0, float *y1)
+{
+ char stimfile[FLEN_FILENAME];
+ int status=0;
+ long j, jmax;
+ float *x0_array=NULL, *x1_array=NULL, *y0_array=NULL, *y1_array=NULL;
+ double expstart, *mjd=NULL;
+ fitsfile *stimfits;
+
+ /* Read header keywords. */
+ FITS_read_key(header, TDOUBLE, "EXPSTART", &expstart, NULL, &status);
+ FITS_read_key(header, TSTRING, "STIM_CAL", stimfile, NULL, &status);
+ cf_verbose(3, "Drift-coefficient calibration file = %s", stimfile);
+
+ /* Read drift-coefficients from the calibration file. */
+ FITS_open_file(&stimfits, cf_cal_file(stimfile), READONLY, &status);
+ FITS_movabs_hdu(stimfits, 2, NULL, &status);
+ jmax = cf_read_col(stimfits, TDOUBLE, "MJD", (void *) &mjd);
+ jmax = cf_read_col(stimfits, TFLOAT, "X0", (void *) &x0_array);
+ jmax = cf_read_col(stimfits, TFLOAT, "X1", (void *) &x1_array);
+ jmax = cf_read_col(stimfits, TFLOAT, "Y0", (void *) &y0_array);
+ jmax = cf_read_col(stimfits, TFLOAT, "Y1", (void *) &y1_array);
+ FITS_close_file(stimfits, &status);
+
+ /* Find the coefficients appropriate for this exposure. */
+ j = 0;
+ while (j < jmax-1 && mjd[j+1] < expstart) j++;
+ *x0 = x0_array[j];
+ *x1 = x1_array[j];
+ *y0 = y0_array[j];
+ *y1 = y1_array[j];
+
+ free(mjd);
+ free(x0_array);
+ free(x1_array);
+ free(y0_array);
+ free(y1_array);
+
+ return status;
+}
+
+
+static int
+cf_get_stim_positions(long nevents, float *xfarf, float *yfarf, float *weight,
+ unsigned char *loc, float width, float *stimlx, float *stimly,
+ float *stimrx, float *stimry)
+{
+ int stim_status=0;
+ long j;
+ double lx=0., ly=0., rx=0., ry=0., nl=0., nr=0.;
+
+ cf_verbose(3,"initial: stimlx = %0.1f, stimly=%0.1f, width=%0.1f ",
+ *stimlx, *stimly, width) ;
+ cf_verbose(3,"initial: stimrx = %0.1f, stimry=%0.1f, width=%0.1f ",
+ *stimrx, *stimry, width) ;
+
+ for (j=0; j<nevents; j++) {
+ if (xfarf[j] >= *stimlx-width && xfarf[j] <= *stimlx+width &&
+ yfarf[j] >= *stimly-width && yfarf[j] <= *stimly+width) {
+ lx += xfarf[j] * weight[j];
+ ly += yfarf[j] * weight[j];
+ nl += weight[j];
+ loc[j] |= LOCATION_STIML;
+ }
+ else if (xfarf[j] >= *stimrx-width && xfarf[j] <= *stimrx+width &&
+ yfarf[j] >= *stimry-width && yfarf[j] <= *stimry+width) {
+ rx += xfarf[j] * weight[j];
+ ry += yfarf[j] * weight[j];
+ nr += weight[j];
+ loc[j] |= LOCATION_STIMR;
+ }
+ else {
+ loc[j] &= ~STIMS;
+ }
+ }
+
+ cf_verbose(3,"nl=%0.1f, nr=%0.1f ",nl,nr) ;
+
+ if (nl > 500) {
+ *stimlx = lx/nl;
+ *stimly = ly/nl;
+ } else {
+ cf_if_warning("Cannot determine left STIM position. Estimating.");
+ stim_status = 1;
+ }
+ if (nr > 500) {
+ *stimrx = rx/nr;
+ *stimry = ry/nr;
+ } else {
+ cf_if_warning("Cannot determine right STIM position. Estimating.");
+ stim_status = 1;
+ }
+
+ if (!stim_status) {
+ cf_verbose(3,"after iteration: stimlx = %0.1f, stimly=%0.1f ", *stimlx, *stimly);
+ cf_verbose(3,"after iteration: stimrx = %0.1f, stimry=%0.1f ", *stimrx, *stimry);
+ }
+
+ return stim_status;
+}
+
+int
+cf_thermal_distort(fitsfile *header, long nevents, float *xfarf,
+ float *yfarf, float *weight, unsigned char *locflags)
+{
+ char CF_PRGM_ID[] = "cf_thermal_distort";
+ char CF_VER_NUM[] = "1.16";
+
+ char elecfile[FLEN_VALUE], detector[FLEN_VALUE];
+ char keyword[FLEN_KEYWORD];
+ int errflg=0, status=0, stim_status=0;
+ long j;
+ float farflx, farfly, farfrx, farfry, x0, x1, y0, y1;
+ float stimlx, stimly, stimrx, stimry, oldlx, oldly, oldrx, oldry;
+ float width=128., tol=0.01;
+ fitsfile *elecfits;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ FITS_read_key(header, TSTRING, "DETECTOR", detector, NULL, &status);
+ /*
+ * Get the name of the calibration file and open it
+ */
+ FITS_read_key(header, TSTRING, "ELEC_CAL", elecfile, NULL, &status);
+ FITS_open_file(&elecfits, cf_cal_file(elecfile), READONLY, &status);
+ /*
+ * Read stim locations in FARF frame.
+ */
+ FITS_read_key(elecfits, TFLOAT, "STIMWDTH", &width, NULL, &status);
+ FITS_read_key(elecfits, TFLOAT, "STIMTOLR", &tol, NULL, &status);
+ sprintf(keyword, "STIMLX%s", detector);
+ FITS_read_key(elecfits, TFLOAT, keyword, &farflx, NULL, &status);
+ sprintf(keyword, "STIMLY%s", detector);
+ FITS_read_key(elecfits, TFLOAT, keyword, &farfly, NULL, &status);
+ sprintf(keyword, "STIMRX%s", detector);
+ FITS_read_key(elecfits, TFLOAT, keyword, &farfrx, NULL, &status);
+ sprintf(keyword, "STIMRY%s", detector);
+ FITS_read_key(elecfits, TFLOAT, keyword, &farfry, NULL, &status);
+ FITS_close_file(elecfits, &status);
+ /*
+ * Get stim positions (iteratively).
+ */
+ stimlx = farflx; stimly = farfly;
+ stimrx = farfrx; stimry = farfry;
+ do {
+ oldlx = stimlx; oldly = stimly;
+ oldrx = stimrx; oldry = stimry;
+ width /= 2.;
+ stim_status = cf_get_stim_positions(nevents, xfarf, yfarf, weight,
+ locflags, width, &stimlx, &stimly, &stimrx, &stimry);
+ if (!stim_status)
+ cf_verbose(3,"dstimlx=%.1f, dstimly=%.1f, dstimrx=%.1f,"
+ "dstimry=%.1f ", fabs(oldlx-stimlx), fabs(oldly-stimly),
+ fabs(oldrx-stimrx), fabs(oldry-stimry) ) ;
+ } while (fabs(oldlx-stimlx) > tol && fabs(oldly-stimly) > tol &&
+ fabs(oldrx-stimrx) > tol && fabs(oldry-stimry) > tol &&
+ !stim_status);
+ /*
+ * Write stim pulse positions to file header.
+ */
+ if (!stim_status) {
+ FITS_update_key(header, TFLOAT, "STIM_L_X", &stimlx, NULL, &status);
+ FITS_update_key(header, TFLOAT, "STIM_L_Y", &stimly, NULL, &status);
+ FITS_update_key(header, TFLOAT, "STIM_R_X", &stimrx, NULL, &status);
+ FITS_update_key(header, TFLOAT, "STIM_R_Y", &stimry, NULL, &status);
+ }
+ /*
+ * Calculate drift coefficients from stim pulse positions
+ */
+ if (!stim_status) {
+ x0 = (stimlx*farfrx - stimrx*farflx)/(stimlx - stimrx);
+ x1 = (farflx - farfrx)/(stimlx - stimrx);
+ y0 = 0.5*((farfly + farfry) - (stimly + stimry));
+ y1 = 1.0;
+ }
+ /*
+ * ...or estimate them from date of exposure.
+ */
+ else
+ cf_estimate_drift_coefficients (header, &x0, &x1, &y0, &y1);
+ cf_verbose(2, "Drift coefficients: %f %f, %f %f", x0, x1, y0, y1);
+ /*
+ * Apply thermal correction to events in active area and
+ * stim-pulse regions.
+ */
+ for (j=0; j<nevents; j++) {
+ if (!(locflags[j] & LOCATION_SHLD) || (locflags[j] & STIMS)) {
+ xfarf[j] = x1*xfarf[j] + x0;
+ yfarf[j] = y1*yfarf[j] + y0;
+ }
+ }
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return 0;
+}
diff --git a/src/libcf/cf_time_xy_distort.c b/src/libcf/cf_time_xy_distort.c
new file mode 100644
index 0000000..bcb8af0
--- /dev/null
+++ b/src/libcf/cf_time_xy_distort.c
@@ -0,0 +1,171 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_time_xy_distort(fitsfile *header, long nevents,
+ * float *xfarf, float *yfarf, unsigned char *locflags)
+ *
+ * Description: Corrects each photon event in the active area for long-term
+ * drifts in the X and Y coordinate scales.
+ *
+ * Arguments: fitsfile *header Pointer to FITS file containing the
+ * header of the intermediate data file
+ * long nevents The number of events
+ * float *xfarf An array of event X positions
+ * float *yfarf An array of event Y positions
+ * unsigned char *locflags Location flags for each event
+ *
+ * Calls:
+ *
+ * Return: 0 on success
+ *
+ * History: 11/01/06 1.1 wvd Based on cf_count_rate_y_distort.c
+ * 11/22/06 1.2 wvd Read BIN_FACT from file header.
+ * Make X correction a function of X.
+ * 01/19/07 1.3 wvd Clean up i/o.
+ * 04/07/07 1.4 wvd Clean up compiler warnings.
+ *
+ ****************************************************************************/
+
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include "calfuse.h"
+
+int
+cf_time_xy_distort(fitsfile *header, long nevents,
+ float *xfarf, float *yfarf, unsigned char *locflags)
+{
+ char CF_PRGM_ID[] = "cf_time_xy_distort";
+ char CF_VER_NUM[] = "1.4";
+
+ char tmxyfile[FLEN_VALUE];
+ int errflg=0, hdutype, status=0, anynull=0;
+ int binfact, i, ii, i_max, tndx, xndx, yndx;
+ long j, xlen, ylen;
+ double expstart;
+ float *xstretch2d, *ystretch2d, xstretch1d[NXMAX], ystretch1d[NYMAX];
+ fitsfile *tmxyfits;
+
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ if ((errflg = cf_proc_check(header, CF_PRGM_ID))) return errflg;
+
+ /*
+ * Read MJD of exposure start.
+ */
+ FITS_read_key(header, TDOUBLE, "EXPSTART", &expstart, NULL, &status);
+
+ /*
+ * Read the X correction array.
+ * Note: the first column of the calibration array contains MJD.
+ */
+ FITS_read_key(header, TSTRING, "TMXY_CAL", tmxyfile, NULL, &status);
+ cf_verbose(3, "Reading TMXY_CAL file %s", tmxyfile);
+ FITS_open_file(&tmxyfits, cf_cal_file(tmxyfile), READONLY, &status);
+ FITS_movabs_hdu(tmxyfits, 2, &hdutype, &status);
+ FITS_read_key(tmxyfits, TLONG, "NAXIS1", &xlen, NULL, &status);
+ FITS_read_key(tmxyfits, TLONG, "NAXIS2", &ylen, NULL, &status);
+ FITS_read_key(tmxyfits, TINT, "BIN_FACT", &binfact, NULL, &status);
+ xstretch2d = (float *) cf_malloc(sizeof(float)*xlen*ylen);
+ FITS_read_img(tmxyfits, TFLOAT, 1L, xlen*ylen, NULL, xstretch2d,
+ &anynull, &status);
+
+ /*
+ * Find the row in the calibration array that corresponds to the
+ * exposure start time.
+ */
+ i = 0;
+ while (expstart > *(xstretch2d+i*xlen) && i < ylen) i++;
+ tndx = i-1;
+ if (tndx < 0) tndx = 0;
+ cf_verbose(2,"EXPSTART = %g.", expstart);
+ cf_verbose(2,"Using X corrections for MJD >= %g", *(xstretch2d+tndx*xlen));
+ if (tndx < ylen-1)
+ cf_verbose(2," and MJD < %g",
+ *(xstretch2d+(tndx+1)*xlen));
+
+ /*
+ * Now compute a 1-D array containing corrections to X
+ * coordinate as a function of X position on the detector.
+ * Shift information binned by BIN_FACT pixels.
+ * We interpolate to the nearest X pixel. Otherwise,
+ * counts pile up at the boundaries.
+ */
+ i_max = (xlen-2) * binfact;
+ for (i = 0; i < i_max; i++) {
+ ii = (i/binfact) * binfact;
+ xstretch1d[i] = ((ii+binfact-i) * (xstretch2d+tndx*xlen)[1+i/binfact] +
+ (i-ii) * (xstretch2d+tndx*xlen)[2+i/binfact]) / binfact;
+ }
+ for ( ; i < NXMAX; i++)
+ xstretch1d[i] = (xstretch2d+tndx*xlen)[xlen-1];
+
+ /*
+ * Read the Y correction array.
+ */
+ FITS_movabs_hdu(tmxyfits, 3, &hdutype, &status);
+ FITS_read_key(tmxyfits, TLONG, "NAXIS1", &xlen, NULL, &status);
+ FITS_read_key(tmxyfits, TLONG, "NAXIS2", &ylen, NULL, &status);
+ FITS_read_key(tmxyfits, TINT, "BIN_FACT", &binfact, NULL, &status);
+ ystretch2d = (float *) cf_malloc(sizeof(float)*xlen*ylen);
+ FITS_read_img(tmxyfits, TFLOAT, 1L, xlen*ylen, NULL, ystretch2d,
+ &anynull, &status);
+ FITS_close_file(tmxyfits, &status);
+
+ /*
+ * Find the row in the calibration array that corresponds to the
+ * exposure start time.
+ */
+ i = 0;
+ while (expstart > *(ystretch2d+i*xlen) && i < ylen) i++;
+ tndx = i-1;
+ if (tndx < 0) tndx = 0;
+ cf_verbose(3,"Using Y corrections for MJD >= %g", *(ystretch2d+tndx*xlen));
+ if (tndx < ylen-1)
+ cf_verbose(3," and MJD < %g",
+ *(ystretch2d+(tndx+1)*xlen));
+
+ /*
+ * Now compute a 1-D array containing corrections to Y
+ * coordinate as a function of Y position on the detector.
+ */
+ i_max = (xlen-2) * binfact;
+ for (i = 0; i < i_max; i++) {
+ ii = (i/binfact) * binfact;
+ ystretch1d[i] = ((ii+binfact-i) * (ystretch2d+tndx*xlen)[1+i/binfact] +
+ (i-ii) * (ystretch2d+tndx*xlen)[2+i/binfact]) / binfact;
+ }
+ for ( ; i < NYMAX; i++)
+ ystretch1d[i] = (ystretch2d+tndx*xlen)[xlen-1];
+ cf_verbose(3, "For y = 430, ystretch = %f\n", ystretch1d[430]);
+ cf_verbose(3, "For y = 810, ystretch = %f\n", ystretch1d[810]);
+
+ /*
+ * Loop through all events. Move only events in active region.
+ */
+ for (j = 0; j < nevents; j++) {
+ if (!(locflags[j] & LOCATION_SHLD)) {
+ xndx = (int) xfarf[j];
+ if (xndx < 0) xndx = 0;
+ else if (xndx >= NXMAX) xndx = NXMAX-1;
+
+ yndx = (int) yfarf[j];
+ if (yndx < 0) yndx = 0;
+ else if (yndx >= NYMAX) yndx = NYMAX-1;
+
+ xfarf[j] += xstretch1d[xndx];
+ yfarf[j] += ystretch1d[yndx];
+ }
+ }
+
+ free(xstretch2d);
+ free(ystretch2d);
+
+ cf_proc_update(header, CF_PRGM_ID, "COMPLETE");
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done processing");
+ return 0;
+}
diff --git a/src/libcf/cf_timestamp.c b/src/libcf/cf_timestamp.c
new file mode 100644
index 0000000..52ef311
--- /dev/null
+++ b/src/libcf/cf_timestamp.c
@@ -0,0 +1,78 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_timestamp(const char *prgmid, const char *vernum, char *msg)
+ *
+ * Description: Writes a timestamp and the calling program name to stdout.
+ *
+ * Arguments: char *prgmid Name of the calling program
+ * char *msg Optional additional message string
+ *
+ * Returns: none
+ *
+ * History: 01/04/91 mlr Original "warn_str.c" for libhut
+ * 03/03/98 gak Adapted for placing timestamps in
+ * FUSE pipeline programs.
+ * 06/07/99 peb Added reporting of version number.
+ * 10/27/99 emm Added fflush(stdout)
+ * 04/23/01 wvd Change declaration of tim from
+ * long to time_t
+ * 03/19/03 peb Made this function a wrapper for
+ * cf_verbose.
+ * 03/25/03 peb Changed output to print time, program,
+ * version number and to use verbose_level
+ * when printing output
+ * level>=1 prints "Finished processing"
+ * level>=2 prints "Begin processing"
+ * 09/15/03 v1.5 wvd To reduce size of the trailer file,
+ * if verbose level = 1, print "Finished"
+ * only for top-level routines.
+ * 10/17/03 v1.6 wvd Use strcmp, rather than strncmp, to
+ * set this_is_a_top_level_routine.
+ * 10/30/03 1.7 peb Replaced cftime function with strftime
+ * for UNIX compatibility.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+#include <time.h>
+#include "calfuse.h"
+
+#define N_STYM 80
+
+void cf_timestamp(const char *prgmid, const char *vernum, char *msg)
+{
+ char stym[N_STYM]={'\0'};
+ char *top_level_routines[]=TOP_LEVEL_ROUTINES;
+ int i, this_is_a_top_level_routine = FALSE;
+ time_t tym;
+
+ time(&tym);
+ strftime(stym, N_STYM, "%Y %b %e %T", localtime(&tym));
+
+ /* Compare program ID with list of top-level routines. */
+ for (i = 0; i < NTOP_LEVEL_ROUTINES; i++) {
+ if (!strcmp(prgmid, top_level_routines[i])) {
+ this_is_a_top_level_routine = TRUE;
+ break;
+ }
+ }
+
+ /*
+ * The following test should get most "Finished" or "Done" type of
+ * timestamps. For those not caught if might be best to change them to
+ * one of these two.
+ */
+ if (((verbose_level == 1 && this_is_a_top_level_routine) ||
+ (verbose_level > 1)) &&
+ (strncmp(msg, "Finish", 6) == 0 || strncmp(msg, "Done", 4) == 0))
+ printf("%s %s-%s: %s\n", stym, prgmid, vernum, "Finished processing");
+ else if (verbose_level >= 2 &&
+ (strncmp(msg, "Start", 5) == 0 || strncmp(msg, "Begin", 5) == 0))
+ printf("%s %s-%s: %s\n", stym, prgmid, vernum, "Begin processing");
+
+ fflush(stdout);
+}
diff --git a/src/libcf/cf_velang.c b/src/libcf/cf_velang.c
new file mode 100644
index 0000000..8395738
--- /dev/null
+++ b/src/libcf/cf_velang.c
@@ -0,0 +1,147 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: cf_velang(fitsfile *outfits, double mjd)
+ *
+ * Description: Calculates the sun angle, moon angle, and conversions to
+ * heliocentric and LSR velocities at given mjd, which should be
+ * the middle of the exposure. For FUV images, the needed
+ * data is in the primary header, so we have to go back
+ * up to the top. For FES images, the data should be in
+ * each HDU, so you should call cf_velang_calc instead.
+ *
+ * Arguments: fitsfile *outfits Pointer to FITS file containing the
+ * input data and orbital elements
+ * double mjd The Modified Julian Date of the
+ * photon arrival time.
+ *
+ * Calls: slaDsep dsep.f
+ * slaRdplan rdplan.f
+ *
+ * History: 07/13/98 emm Begin work
+ * 06/10/99 emm Added keyword header update, added
+ * calculation of MOONANG.
+ * 06/11/99 emm Fixed some bugs that Jayant found.
+ * 06/21/99 jm Corrected name
+ * 07/16/99 emm Changed GEO_LON to GEO_LONG
+ * 07/21/99 peb RA and Dec now use RA_TARG and DEC_TARG
+ * 07/22/99 emm Added V_GEOCEN update.
+ * 07/23/99 emm Added cf_velang_calc for FES images.
+ * 05/31/00 peb Implemented cfortran.h calls for slalib
+ * functions.
+ * 04/01/03 v1.3 wvd Replaced printf with cf_verbose
+ * 04/04/03 v1.4 wvd Modifed calls to cf_verbose.
+ * 12/18/03 v1.5 bjg Change calfusettag.h to calfuse.h
+ * 07/06/06 v1.6 wvd Add call to pole_ang and populate
+ * header keyword POLEANGL.
+ * 03/07/07 v1.7 wvd Remove pos from call to space_vel.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+
+#ifdef CFORTRAN
+#include "cfortran.h"
+PROTOCCALLSFFUN4(DOUBLE, SLA_DSEP, sla_dsep, DOUBLE, DOUBLE, DOUBLE, \
+ DOUBLE)
+#define slaDsep(A1, B1, A2, B2) \
+ CCALLSFFUN4(SLA_DSEP, sla_dsep, DOUBLE, DOUBLE, DOUBLE, DOUBLE, \
+ A1, B1, A2, B2)
+PROTOCCALLSFSUB7(SLA_RDPLAN, sla_rdplan, DOUBLE, INT, DOUBLE, DOUBLE, \
+ PDOUBLE, PDOUBLE, PDOUBLE)
+#define slaRdplan(DATE, NP, ELONG, PHI, RA, DEC, DIAM) \
+ CCALLSFSUB7(SLA_RDPLAN, sla_rdplan, DOUBLE, INT, DOUBLE, DOUBLE, \
+ PDOUBLE, PDOUBLE, PDOUBLE, \
+ DATE, NP, ELONG, PHI, RA, DEC, DIAM)
+#else
+#include "slalib.h"
+#include "slamac.h"
+#endif
+
+#include "calfuse.h"
+#include "sgp4.h"
+
+SGP4 set_orbit_parms(fitsfile *);
+
+static void
+cf_velang_calc(fitsfile *outfits, double mjd)
+{
+ int status=0;
+ char comment[FLEN_CARD];
+ double ra, dec, geo_lon, geo_lat, mag_lat, pos[3], vel[3], gmst, helvel;
+ double ra_moon, dec_moon, moon_ang, dia_moon, vlsr1, vlsr2, sun_ang;
+ double geo_vel, pole_angle;
+ SGP4 sgp4;
+
+ /* get the state vector at time mjd */
+ sgp4 = set_orbit_parms(outfits);
+ SGP4_getStateVector(sgp4, mjd, pos, vel);
+ SGP4_precess(pos, mjd, MJD2000); SGP4_precess(vel, mjd, MJD2000);
+
+ cf_verbose(2,"State vector at MJD=%14.6f days", mjd);
+ cf_verbose(2,"x=%14.6f y=%14.6f z=%14.6f km", pos[0], pos[1], pos[2]);
+ cf_verbose(2,"vx=%14.6f vy=%14.6f vz=%14.6f km/s",vel[0], vel[1], vel[2]);
+
+ state_geod(pos, mjd, &geo_lon, &geo_lat, &gmst);
+
+ mag_lat = geod_mag(geo_lon, geo_lat);
+ /*
+ * Note: this function has been changed to use RA_TARG and DEC_TARG
+ * keywords, because the RA_APER and DEC_APER keywords are currently
+ * not being set.
+ */
+ FITS_read_key(outfits, TDOUBLE, "RA_TARG", &ra, comment, &status);
+ FITS_read_key(outfits, TDOUBLE, "DEC_TARG", &dec, comment, &status);
+
+ geo_vel= space_vel(vel, ra, dec);
+ helvel = helio_vel(mjd, ra, dec);
+ vlsr1 = lsrk_vel(ra, dec);
+ vlsr2 = lsrd_vel(ra, dec);
+
+ /* Calculate sun angle */
+ sun_ang = solar_ang(mjd, ra, dec);
+
+ /* Calculate moon angle */
+#ifdef CFORTRAN
+ slaRdplan(mjd, 3, geo_lon, geo_lat, ra_moon, dec_moon, dia_moon);
+#else
+ slaRdplan(mjd, 3, geo_lon, geo_lat, &ra_moon, &dec_moon, &dia_moon);
+#endif
+ moon_ang = slaDsep(ra*RADIAN, dec*RADIAN, ra_moon, dec_moon)/RADIAN;
+
+ /* Calculate sun angle */
+ pole_angle = pole_ang(pos, vel, ra, dec);
+
+ FITS_update_key(outfits, TDOUBLE, "SUNANGLE", &sun_ang, NULL, &status);
+ FITS_update_key(outfits, TDOUBLE, "MOONANGL", &moon_ang, NULL, &status);
+ FITS_update_key(outfits, TDOUBLE, "POLEANGL", &pole_angle, NULL, &status);
+ FITS_update_key(outfits, TDOUBLE, "GEO_LONG", &geo_lon, NULL, &status);
+ FITS_update_key(outfits, TDOUBLE, "GEO_LAT", &geo_lat, NULL, &status);
+ FITS_update_key(outfits, TDOUBLE, "MAG_LAT", &mag_lat, NULL, &status);
+ FITS_update_key(outfits, TDOUBLE, "V_GEOCEN", &geo_vel, NULL, &status);
+ FITS_update_key(outfits, TDOUBLE, "V_HELIO", &helvel, NULL, &status);
+ FITS_update_key(outfits, TDOUBLE, "V_LSRDYN", &vlsr2, NULL, &status);
+ FITS_update_key(outfits, TDOUBLE, "V_LSRSTD", &vlsr1, NULL, &status);
+}
+
+
+void cf_velang(fitsfile *outfits, double mjd)
+{
+ int status=0, hdunum=0, hdutype;
+ /*
+ * The outfits file pointer may be down in one of the extensions
+ * whereas the orbital info is in the primary hdu. Therefore,
+ * record the current position, move to the primary header,
+ * read the orbital data, and return to the previous hdu.
+ */
+ FITS_get_hdu_num(outfits, &hdunum);
+ FITS_movabs_hdu(outfits, 1, &hdutype, &status);
+
+ cf_velang_calc(outfits, mjd);
+
+ FITS_movabs_hdu(outfits, hdunum, &hdutype, &status);
+
+}
diff --git a/src/libcf/cf_write_extracted_spectrum.c b/src/libcf/cf_write_extracted_spectrum.c
new file mode 100644
index 0000000..90831c2
--- /dev/null
+++ b/src/libcf/cf_write_extracted_spectrum.c
@@ -0,0 +1,226 @@
+/**************************************************************************
+ * Johns Hopkins University
+ * Center for Astrophysical Sciences
+ * FUSE
+ *************************************************************************
+ *
+ * synopsis: cf_write_extracted_spectrum(infile, aperture, nout, wave_out,
+ * flux_out, var_out, counts_out, weights_out, bkgd_out,
+ * bpix_out, outrootname)
+ *
+ * Description: Write the results of the data reduction to a file.
+ * Information from the header is used to generate the name
+ * of the output file. Aperture coding is as follows:
+ *
+ * 1-8 -> LiF[HIRS,MDRS,LWRS,PINH], SiC[HIRS,MDRS,LWRS,PINH]
+ *
+ * Arguments: fitsfile infile : pointer to Intermediate Data File
+ * int aperture : Target aperture (values are 1 to 8)
+ * int valid_spectrum: True if spectrum contains photons.
+ * long nout : number of tabulated points
+ * float wave_out : Output wavelength array
+ * float flux_out : Output flux array
+ * float var_out : Output variance array
+ * long counts_out : Output counts array (in counts)
+ * float weights_out : Output weights array (in counts)
+ * float bkgd_out : Output background array (in counts)
+ * short bpix_out : Output bad pixel spectrum
+ * char outrootname: Optional output file name
+ *
+ * Returns: 0 if successful
+ *
+ * HISTORY: 02/10/03 1.1 RDR started work
+ * 02/28/03 1.2 peb Added header files to file and tidied
+ * code using lint.
+ * 03/12/03 1.3 wvd Output ERROR = sqrt(var_out)
+ * 03/31/03 1.4 wvd Update keywords FILENAME, FILETYPE,
+ * and APER_ACT.
+ * 05/22/03 1.5 wvd Direct cf_error_init to stderr.
+ * 05/29/03 1.6 rdr Added bad pixel column
+ * 06/03/03 1.7 rdr Change naming convention to allow
+ * hist data
+ * 07/17/03 1.9 wvd Test var_out before taking sqrt.
+ * Change calfusettag.h to calfuse.h
+ * 10/01/03 1.10 wvd Modify verbose levels.
+ * 11/10/03 1.11 wvd Correct numbering scheme for HIRS
+ * and MDRS apertures.
+ * 02/13/04 1.12 wvd Correct error in COMMENT lines.
+ * 03/03/04 1.13 rdr Populate archive search keywords
+ * 03/05/04 1.14 wvd Change name of POTHOLE column
+ * to QUALITY.
+ * 03/24/04 1.15 wvd If valid_spectrum=FALSE, write
+ * warning message to file header.
+ * 04/06/04 1.16 bjg Include string.h and ctype.h
+ * 04/09/04 1.17 wvd If valid_spectrum=FALSE, set
+ * EXPTIME = 0 in file header.
+ * 04/27/04 1.18 wvd Move conversion from variance
+ * to sigma from this routine to
+ * cf_optimal_extraction.
+ * 06/11/04 1.19 peb Added the -r option to override the
+ * default rootname
+ * 02/01/05 1.20 wvd Revert to standard FITS binary
+ * table format for output file.
+ * 04/11/05 1.21 wvd In HIST mode, don't write SIA
+ * table to output spectral file.
+ * 06/03/05 1.22 wvd Return status from cf_copy_hist_header.
+ * 06/03/05 1.23 wvd Change cf_copy_hist_header to type void.
+ * 08/11/05 1.24 wvd Delete FITS_write_date from
+ * cf_copy_hist_header.
+ *
+ **************************************************************************/
+
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include "calfuse.h"
+
+static char *ap_code[] = {"", "lif3", "lif2", "lif4", "lif1",
+ "sic3", "sic2", "sic4", "sic1"};
+
+static void
+cf_copy_hist_header(fitsfile *infits, fitsfile *outfits, int *status)
+{
+ char card[FLEN_CARD];
+ int i, nkeys, naxis=0, nextend=1;
+ long naxes[2];
+
+ naxes[0] = naxes[1] = 0;
+
+ FITS_create_img(outfits, SHORT_IMG, naxis, naxes, status);
+ FITS_delete_key(outfits, "COMMENT", status);
+ FITS_delete_key(outfits, "COMMENT", status);
+ FITS_write_key(outfits, TINT, "NEXTEND", &nextend,
+ "Number of standard extensions", status);
+ fits_get_hdrspace(infits, &nkeys, NULL, status);
+ cf_if_fits_error(*status);
+ for (i = 9; i <= nkeys; i++) {
+ FITS_read_record(infits, i, card, status);
+ FITS_write_record(outfits, card, status);
+ }
+}
+
+int
+cf_write_extracted_spectrum(fitsfile *infits, int aperture,
+ int valid_spectrum, long nout, float *wave_out,
+ float *flux_out, float *var_out, long *counts_out,
+ float *weights_out, float *bkgd_out, short *bpix_out,
+ char *outrootname)
+{
+ char CF_PRGM_ID[] = "cf_write_extracted_spectrum";
+ char CF_VER_NUM[] = "1.24";
+
+ fitsfile *outfits;
+ int nextend=1, status=0, tfields=7, timeref;
+ long i, frow=1, felem=1;
+ float bw=0., center=0., wmin=1200., wmax=0. ;
+ char comment[FLEN_CARD], datestr[FLEN_CARD];
+ char rootname[FLEN_VALUE]={'\0'}, instmode[FLEN_VALUE] ;
+ char det[FLEN_VALUE]={'\0'}, outfile[FLEN_VALUE]={'\0'};
+ char extname[]="SPECTRUM";
+ char *aperact[] = {"HIRS_LIF", "MDRS_LIF", "LWRS_LIF", "PINH_LIF",
+ "HIRS_SIC", "MDRS_SIC", "LWRS_SIC", "PINH_SIC"};
+ char *ttype[]={"WAVE","FLUX","ERROR","COUNTS","WEIGHTS","BKGD","QUALITY"};
+ char *tform[]={"1E", "1E", "1E", "1J", "1E", "1E", "1I"};
+ char *tunit[]={"ANGSTROMS","ERG CM^-2 S^-1 ANG^-1","ERG CM^-2 S^-1 ANG^-1",
+ "COUNTS", "COUNTS", "COUNTS","UNITLESS"};
+
+ /* Initialize error checking */
+ cf_error_init(CF_PRGM_ID, CF_VER_NUM, stderr);
+
+ /* Enter a time stamp into the log */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin Processing");
+
+ /*
+ * Read the observation ID from the input file and create an
+ * output file name.
+ */
+ FITS_movabs_hdu(infits, 1, NULL, &status);
+ FITS_read_key(infits, TSTRING, "ROOTNAME", rootname, NULL, &status);
+ FITS_read_key(infits, TSTRING, "DETECTOR", det, NULL, &status);
+ FITS_read_key(infits, TSTRING, "INSTMODE", instmode, NULL, &status);
+
+ /* Override the default rootname if the -r option is given. */
+ if (outrootname != NULL)
+ strcpy(rootname, outrootname);
+
+ /* Convert detector name to lower case */
+ det[1] = tolower(det[1]);
+
+ if (!strncmp(instmode,"T",1))
+ sprintf(outfile, "%11s%2s%4sttagfcal.fit",
+ rootname, det, ap_code[aperture]);
+ else if (!strncmp(instmode,"H",1))
+ sprintf(outfile, "%11s%2s%4shistfcal.fit",
+ rootname, det, ap_code[aperture]);
+ else {
+ cf_if_error("Cannot create name for the output files.") ;
+ return 1;
+ }
+ cf_verbose(3, "Output file name = %s", outfile);
+
+ /* Open the output file and copy the header info from the input file */
+ FITS_create_file(&outfits, outfile, &status);
+ if (!strncmp(instmode,"T",1))
+ FITS_copy_header(infits, outfits, &status);
+ else
+ cf_copy_hist_header(infits, outfits, &status);
+ FITS_update_key(outfits, TINT, "NEXTEND", &nextend, NULL, &status);
+ FITS_update_key(outfits, TSTRING, "FILENAME", outfile, NULL, &status);
+ FITS_update_key(outfits, TSTRING, "FILETYPE",
+ "CALIBRATED EXTRACTED SPECTRUM", NULL, &status);
+ FITS_update_key(outfits, TSTRING, "APER_ACT", aperact[aperture-1],
+ NULL, &status);
+
+ /* If valid_spectrum = FALSE, write warning to file header. */
+ if (valid_spectrum == FALSE) {
+ float exptime = 0.;
+ FITS_update_key(outfits, TFLOAT, "EXPTIME", &exptime, NULL, &status);
+ FITS_write_comment(outfits, " ", &status);
+ FITS_write_comment(outfits,
+ "No good data. Output arrays set to zero.", &status);
+ fits_get_system_time(datestr, &timeref, &status);
+ sprintf(comment, "CalFUSE v%s %.10s", CALFUSE_VERSION, datestr);
+ FITS_write_comment(outfits, comment, &status);
+ FITS_write_comment(outfits, " ", &status);
+ }
+
+ /* Populate archive search keywords */
+ for (i=0 ; i<nout ; i++) {
+ if (wave_out[i] < wmin) wmin=wave_out[i] ;
+ if (wave_out[i] > wmax) wmax=wave_out[i] ;
+ }
+ bw = wmax-wmin ;
+ center = (wmax + wmin) / 2. ;
+ FITS_update_key(outfits, TFLOAT, "BANDWID", &bw, NULL, &status);
+ FITS_update_key(outfits, TFLOAT, "CENTRWV", &center, NULL, &status);
+ FITS_update_key(outfits, TFLOAT, "WAVEMIN", &wmin, NULL, &status);
+ FITS_update_key(outfits, TFLOAT, "WAVEMAX", &wmax, NULL, &status);
+
+ /* Put the data into the table */
+ FITS_create_tbl(outfits, BINARY_TBL, nout, tfields, ttype, tform,
+ tunit, extname, &status);
+ FITS_write_col(outfits, TFLOAT, 1, frow, felem, nout, wave_out, &status);
+ FITS_write_col(outfits, TFLOAT, 2, frow, felem, nout, flux_out, &status);
+ FITS_write_col(outfits, TFLOAT, 3, frow, felem, nout, var_out, &status);
+ FITS_write_col(outfits, TLONG, 4, frow, felem, nout, counts_out, &status);
+ FITS_write_col(outfits, TFLOAT, 5, frow, felem, nout, weights_out, &status);
+ FITS_write_col(outfits, TFLOAT, 6, frow, felem, nout, bkgd_out, &status);
+ FITS_write_col(outfits, TSHORT, 7, frow, felem, nout, bpix_out, &status);
+
+ /* Write some comments into the header */
+ FITS_write_comment(outfits, " ", &status);
+ FITS_write_comment(outfits, "RAW COUNTS = COUNTS ",
+ &status);
+ FITS_write_comment(outfits, "TARGET COUNTS = WEIGHTS - BKGD ",
+ &status);
+ FITS_write_comment(outfits, "TARGET FLUX = TARGET COUNTS * HC / LAMBDA / "
+ "AEFF / EXPTIME / WPC", &status);
+ FITS_write_comment(outfits, " ", &status);
+
+ FITS_close_file(outfits, &status);
+
+ /* Enter a time stamp into the log */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Finished processing");
+
+ return (status);
+}
diff --git a/src/libcf/eclipse.c b/src/libcf/eclipse.c
new file mode 100644
index 0000000..a1e0b44
--- /dev/null
+++ b/src/libcf/eclipse.c
@@ -0,0 +1,148 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: int eclipse(double pos[3], double mjdate)
+ *
+ * Description: Determines if FUSE is in the day or night portion of the orbit.
+ *
+ *
+ * Arguments: double pos Cartesian vector xyz coord of satellite.
+ * double mjdate Julian date of observation
+ *
+ * Returns: int 0 if day portion of orbit
+ * 1 if night portion of orbit
+ *
+ * Calls: slaDcc2s dcc2s.f
+ * slaDranrm dranrm.f
+ * slaDsep dsep.f
+ * slaEvp evp.f
+ * slaPreces preces.f
+ *
+ * History: 03/10/98 E. Murphy Begin work.
+ * 03/10/98 E. Murphy Initial version working
+ * 07/07/99 E. Murphy Replaced x,y,z in call with pos
+ * 07/20/99 jm Removed duplicate define statements
+ * 08/26/99 E. Murphy ang_sep now returned.
+ * 05/31/00 peb Implemented cfortran.h calls for slalib
+ * functions.
+ * 12/18/03 bjg Change calfusettag.h to calfuse.h
+ * 06/17/04 bjg 1.4 Corrected cfortran call to sla_preces
+ *
+ * References: This routine makes use of the Starlink set of astronomical
+ * subroutines (SLALIB). More information can be found at
+ * http://star-www.rl.ac.uk.
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <math.h>
+
+#ifdef CFORTRAN
+#include "cfortran.h"
+PROTOCCALLSFSUB3(SLA_DCC2S, sla_dcc2s, DOUBLEV, PDOUBLE, PDOUBLE)
+#define slaDcc2s(V, A, B) \
+ CCALLSFSUB3(SLA_DCC2S, sla_dcc2s, DOUBLEV, PDOUBLE, PDOUBLE, V, A, B)
+PROTOCCALLSFFUN1(DOUBLE, SLA_DRANRM, sla_dranrm, DOUBLE)
+#define slaDranrm(ANGLE) \
+ CCALLSFFUN1(SLA_DRANRM, sla_dranrm, DOUBLE, ANGLE)
+PROTOCCALLSFFUN4(DOUBLE, SLA_DSEP, sla_dsep, DOUBLE, DOUBLE, DOUBLE, \
+ DOUBLE)
+#define slaDsep(A1, B1, A2, B2) \
+ CCALLSFFUN4(SLA_DSEP, sla_dsep, DOUBLE, DOUBLE, DOUBLE, DOUBLE, \
+ A1, B1, A2, B2)
+PROTOCCALLSFSUB6(SLA_EVP, sla_evp, DOUBLE, DOUBLE, DOUBLEV, DOUBLEV, \
+ DOUBLEV, DOUBLEV)
+#define slaEvp(DATE, DEQX, DVB, DPB, DVH, DPH) \
+ CCALLSFSUB6(SLA_EVP, sla_evp, DOUBLE, DOUBLE, DOUBLEV, DOUBLEV, \
+ DOUBLEV, DOUBLEV, DATE, DEQX, DVB, DPB, DVH, DPH)
+PROTOCCALLSFSUB5(SLA_PRECES, sla_preces, STRING, DOUBLE, DOUBLE, PDOUBLE, \
+ PDOUBLE)
+#define slaPreces(SYSTEM, EP0, EP1, RA, DC) \
+ CCALLSFSUB5(SLA_PRECES, sla_preces, STRING, DOUBLE, DOUBLE, PDOUBLE, \
+ PDOUBLE, SYSTEM, EP0, EP1, RA, DC)
+#else
+#include "slalib.h"
+#include "slamac.h"
+#endif
+
+#include "calfuse.h"
+
+int eclipse(double *pos, double mjdate, double *ang_sep)
+{
+ /* Define variables. */
+ int i, retcode;
+ double dvb[3], dpb[3], dvh[3], dph[3], epoch2;
+ double r, ra_earth, dec_earth, ra_sun, dec_sun, ang_earth, ang_sun;
+ char fk5_st[10]="FK5";
+
+ /* Calculate the J2000.0 apparent RA and DEC of the Earth */
+ r=sqrt(pos[0]*pos[0]+pos[1]*pos[1]+pos[2]*pos[2]);
+ ra_earth=atan2(-pos[1],-pos[0]);
+ dec_earth=asin(-pos[2]/r);
+
+ /* We must precess the J2000 RA and DEC to date of observation. This
+ * is a very small correction, and could probably be ignored.
+ */
+
+ epoch2=2000.0-((51544.0-mjdate)/365.25);
+
+#ifdef CFORTRAN
+ slaPreces(fk5_st, 2000.0, epoch2, ra_earth, dec_earth);
+#else
+ slaPreces(fk5_st, 2000.0, epoch2, &ra_earth, &dec_earth);
+#endif
+
+ /* Evp returns four 3-vectors containing the barycentric velocity and
+ * position (dvb,dpv) and the heliocentric velocity and position
+ * (dvh,dph) of the Earth on the date mjdate. It requires a modified
+ * Julian date as input. Output has units of AU for positions and
+ * AU/s for velocities.
+ */
+
+ slaEvp(mjdate, 2000.0, dvb, dpb, dvh, dph);
+
+ /* Convert the 3-vector position of the Sun into a J2000.0 RA and DEC */
+
+ for (i=0; i<3; i++) dph[i]*=-1.0;
+
+
+#ifdef CFORTRAN
+ slaDcc2s(dph, ra_sun, dec_sun);
+#else
+ slaDcc2s(dph, &ra_sun, &dec_sun);
+#endif
+
+ ra_sun=slaDranrm(ra_sun);
+
+ /* We must precess the J2000 RA and DEC to date of observation. This
+ * is a very small correction, and could probably be ignored.
+ */
+
+ epoch2=2000.0-((51544.0-mjdate)/365.25);
+
+#ifdef CFORTRAN
+ slaPreces(fk5_st, 2000.0, epoch2, ra_sun, dec_sun);
+#else
+ slaPreces(fk5_st, 2000.0, epoch2, &ra_sun, &dec_sun);
+#endif
+
+ /* Compute the angular sizes of the Earth and the Sun. */
+
+ ang_earth=asin(RE/r);
+
+ ang_sun=asin(RS/(sqrt(dph[0]*dph[0]+dph[1]*dph[1]+dph[2]*dph[2])*AU));
+
+ /* Compute the angular separation of the Earth and the Sun. */
+
+ *ang_sep=slaDsep(ra_earth, dec_earth, ra_sun, dec_sun);
+
+ retcode=0;
+ if (*ang_sep < ang_earth+ang_sun) retcode=1;
+
+ /* Convert ang_sep into degrees. */
+ *ang_sep /= RADIAN;
+
+ return retcode;
+}
diff --git a/src/libcf/geod_mag.c b/src/libcf/geod_mag.c
new file mode 100644
index 0000000..6750f9d
--- /dev/null
+++ b/src/libcf/geod_mag.c
@@ -0,0 +1,40 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: double geod_mag(double lon, double lat)
+ *
+ * Description: Computes the magnetic latitude of FUSE from the
+ * given geocentric longitude and latitude.
+ *
+ * Arguments: double lon,lat (deg) Geocentric longitude and latitude
+ *
+ * Returns: double Geomagnetic latitude.
+ *
+ * History: 03/11/98 E. Murphy Begin work.
+ * 03/11/98 E. Murphy Initial version working
+ * 04/13/99 E. Murphy Moved PI and RADIAN to calfuse.h
+ * 12/18/03 bjg Change calfusettag.h to calfuse.h
+ *
+ * Ake, T. 1998 in The Scientific Impact of the Goddard
+ * High Resolution Spectrograph, ed. J. C. Brandt et al.,
+ * ASP Conference Series, in preparation.
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <math.h>
+#include "calfuse.h"
+
+double geod_mag(double lon, double lat)
+{
+ double lat_rad, c1;
+
+ lat_rad=lat*RADIAN;
+
+ c1=sin(lat_rad)*cos(11.4*RADIAN)-
+ cos(lat_rad)*cos((lon+69.8)*RADIAN)*sin(11.4*RADIAN);
+
+ return asin(c1)/RADIAN;
+}
diff --git a/src/libcf/helio_vel.c b/src/libcf/helio_vel.c
new file mode 100644
index 0000000..bc069b6
--- /dev/null
+++ b/src/libcf/helio_vel.c
@@ -0,0 +1,82 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: double helio_vel(double mjdate, double ra, double dec)
+ *
+ * Description: Computes the Earth's orbital velocity in the direction
+ * of ra,dec to convert to heliocentric velocity.
+ *
+ * Arguments: double mjdate Mod. Julian date of observation
+ * double ra (deg) J2000.0 right ascension
+ * double dec (deg) J2000.0 declination
+ *
+ * Returns: double (km/s) velocity in direction of source
+ *
+ * Calls: slaEvp evp.f
+ * slaDcs2c dcs2c.f
+ * slaDvdv dvdv.f
+ *
+ * History: 03/05/98 E. Murphy Begin work.
+ * 03/05/98 E. Murphy Initial version working
+ * 05/31/00 peb Implemented cfortran.h calls for slalib
+ * functions.
+ * 06/15/01 V. Dixon Correct sign of return value to agree
+ * with astronomical convention.
+ * 12/18/03 bjg Change calfusettag.h to calfuse.h
+ *
+ * References: This routine makes use of the Starlink set of astronomical
+ * subroutines (SLALIB). More information can be found at
+ * http://star-www.rl.ac.uk.
+ ****************************************************************************/
+
+#include <stdio.h>
+
+#ifdef CFORTRAN
+#include "cfortran.h"
+
+PROTOCCALLSFSUB3(SLA_DCS2C, sla_dcs2c, DOUBLE, DOUBLE, DOUBLEV)
+#define slaDcs2c(A, B, V) \
+ CCALLSFSUB3(SLA_DCS2C, sla_dcs2c, DOUBLE, DOUBLE, DOUBLEV, A, B, V)
+
+PROTOCCALLSFFUN2(DOUBLE, SLA_DVDV, sla_dvdv, DOUBLEV, DOUBLEV)
+#define slaDvdv(VA, VB) \
+ CCALLSFFUN2(SLA_DVDV, sla_dvdv, DOUBLEV, DOUBLEV, VA, VB)
+
+PROTOCCALLSFSUB6(SLA_EVP, sla_evp, DOUBLE, DOUBLE, DOUBLEV, DOUBLEV, \
+ DOUBLEV, DOUBLEV)
+#define slaEvp(DATE, DEQX, DVB, DPB, DVH, DPH) \
+ CCALLSFSUB6(SLA_EVP, sla_evp, DOUBLE, DOUBLE, DOUBLEV, DOUBLEV, \
+ DOUBLEV, DOUBLEV, DATE, DEQX, DVB, DPB, DVH, DPH)
+#else
+#include "slalib.h"
+#include "slamac.h"
+#endif
+
+#include "calfuse.h"
+
+double helio_vel(double mjdate, double ra, double dec)
+{
+ /* Define variables. */
+ double dvb[3], dpb[3], dvh[3], dph[3], vect[3];
+ /*
+ * Evp returns four 3-vectors containing the barycentric velocity and
+ * position (dvb,dpb) and the heliocentric velocity and position
+ * (dvh,dph) of the Earth on the date mjdate. It requires a modified
+ * Julian date as input. Output has units of AU for positions and
+ * AU/s for velocities.
+ */
+ slaEvp(mjdate, 2000.0, dvb, dpb, dvh, dph);
+
+ /* Convert the J2000.0 RA and DEC into a 3-vector position */
+
+ slaDcs2c(RADIAN*ra, RADIAN*dec, vect);
+ /*
+ * Compute the dot product of the RA DEC position vector and the
+ * heliocentric velocity vector. Mutliply by km/AU to get velocity
+ * in km/s.
+ */
+ return slaDvdv(vect, dvh)*149.5978707E6;
+}
diff --git a/src/libcf/lsrd_vel.c b/src/libcf/lsrd_vel.c
new file mode 100644
index 0000000..a90c22b
--- /dev/null
+++ b/src/libcf/lsrd_vel.c
@@ -0,0 +1,51 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: double lsrd_vel(double ra, double dec)
+ *
+ * Description: Computes the Sun's velocity in the direction
+ * of ra,dec to convert to a LSR. In this case we
+ * are using the dynamical solar motion of
+ * 16.6 km/s towards l=53, b=+25.
+ *
+ * Arguments: double ra (deg) J2000.0 right ascension
+ * double dec (deg) J2000.0 declination
+ *
+ * Returns: double (km/s) velocity in direction of source
+ *
+ * Calls: slaRvlsrd rvslrd.f
+ *
+ * History: 03/05/98 E. Murphy Begin work.
+ * 03/05/98 E. Murphy Initial version working
+ * 05/31/00 peb Implemented cfortran.h calls for slalib
+ * functions.
+ * 06/15/01 V. Dixon Multiply return value by -1.0 to agree
+ * with astronomical convention.
+ * 12/18/03 bjg Change calfusettag.h to calfuse.h
+ *
+ * References: This routine makes use of the Starlink set of astronomical
+ * subroutines (SLALIB). More information can be found at
+ * http://star-www.rl.ac.uk.
+ ****************************************************************************/
+
+#include <stdio.h>
+
+#ifdef CFORTRAN
+#include "cfortran.h"
+PROTOCCALLSFFUN2(FLOAT, SLA_RVLSRD, sla_rvlsrd, FLOAT, FLOAT)
+#define slaRvlsrd(R2000, D2000) \
+ CCALLSFFUN2(SLA_RVLSRD, sla_rvlsrd, FLOAT, FLOAT, R2000, D2000)
+#else
+#include "slalib.h"
+#include "slamac.h"
+#endif
+
+#include "calfuse.h"
+
+double lsrd_vel(double ra, double dec)
+{
+ return -1.0 * slaRvlsrd((float) ra*RADIAN, (float) dec*RADIAN);
+}
diff --git a/src/libcf/lsrk_vel.c b/src/libcf/lsrk_vel.c
new file mode 100644
index 0000000..15f5a6c
--- /dev/null
+++ b/src/libcf/lsrk_vel.c
@@ -0,0 +1,52 @@
+/****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ ****************************************************************************
+ *
+ * Synopsis: double lsrk_vel(double ra, double dec)
+ *
+ * Description: Computes the Sun's velocity in the direction
+ * of ra,dec to convert to a LSR. In this case we
+ * are using standard solar motion of
+ * 20 km/s towards RA 18h Dec +30d (1900).
+ *
+ * Arguments: double ra (deg) J2000.0 right ascension
+ * double dec (deg) J2000.0 declination
+ *
+ * Returns: double (km/s) velocity in direction of source
+ *
+ * Calls: slaRvlsrk rvlsrk.f
+ *
+ * History: 03/05/98 E. Murphy Begin work.
+ * 03/05/98 E. Murphy Initial version working
+ * 04/13/99 E. Murphy Clean up a bit
+ * 05/31/00 peb Implemented cfortran.h calls for slalib
+ * functions.
+ * 06/15/01 V. Dixon Multiply return value by -1.0 to agree
+ * with astronomical convention.
+ * 12/18/03 bjg Change calfusettag.h to calfuse.h
+ *
+ * References: This routine makes use of the Starlink set of astronomical
+ * subroutines (SLALIB). More information can be found at
+ * http://star-www.rl.ac.uk.
+ ****************************************************************************/
+
+#include <stdio.h>
+
+#ifdef CFORTRAN
+#include "cfortran.h"
+PROTOCCALLSFFUN2(FLOAT, SLA_RVLSRK, sla_rvlsrk, FLOAT, FLOAT)
+#define slaRvlsrk(R2000, D2000) \
+ CCALLSFFUN2(SLA_RVLSRK, sla_rvlsrK, FLOAT, FLOAT, R2000, D2000)
+#else
+#include "slalib.h"
+#include "slamac.h"
+#endif
+
+#include "calfuse.h"
+
+double lsrk_vel(double ra, double dec)
+{
+ return -1.0 * slaRvlsrk((float) ra*RADIAN, (float) dec*RADIAN);
+}
diff --git a/src/libcf/month_day.c b/src/libcf/month_day.c
new file mode 100644
index 0000000..1ecfae1
--- /dev/null
+++ b/src/libcf/month_day.c
@@ -0,0 +1,19 @@
+/* month_day: return month,day from day of year */
+/* 07/21/04 1.4 wvd Add () to defn of leap */
+
+#include "calfuse.h"
+
+void month_day(int year, int yearday, int *pmonth, int *pday)
+{
+ static char daytab[2][13]={
+ {0,31,28,31,30,31,30,31,31,30,31,30,31},
+ {0,31,29,31,30,31,30,31,31,30,31,30,31}
+ };
+ int i, leap;
+
+ leap = ((year%4 == 0 && year%100 != 0) || year%400 == 0);
+ for (i=1; yearday>daytab[leap][i]; i++)
+ yearday-=daytab[leap][i];
+ *pmonth=i;
+ *pday=yearday;
+}
diff --git a/src/libcf/pole_ang.c b/src/libcf/pole_ang.c
new file mode 100644
index 0000000..ae90cc2
--- /dev/null
+++ b/src/libcf/pole_ang.c
@@ -0,0 +1,64 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: double pole_ang(double *pos, double *vel, double ra, double dec)
+ *
+ * Description: Computes the position of the orbit pole from the given state
+ * 6-vector. Returns the angle between the pole and the given
+ * RA and DEC.
+ *
+ * Arguments: double *pos (km) 3-vector position of satellite
+ * double *vel (km/s) 3-vector velocity of satellite
+ * double ra,dec (deg) position of source
+ *
+ * Returns: double (deg) angle from orbit pole to source
+ *
+ * History: 07/06/06 wvd 1.1 Adapted from space_vel and sun_ang
+ * 04/08/07 wvd 1.2 Cast SGP4_getPole as a void.
+ *
+ * References: This routine makes use of the Starlink set of astronomical
+ * subroutines (SLALIB). More information can be found at
+ * http://star-www.rl.ac.uk.
+ *****************************************************************************/
+
+#include <stdio.h>
+
+#ifdef CFORTRAN
+#include "cfortran.h"
+PROTOCCALLSFSUB3(SLA_DCC2S, sla_dcc2s, DOUBLEV, PDOUBLE, PDOUBLE)
+#define slaDcc2s(V, A, B) \
+ CCALLSFSUB3(SLA_DCC2S, sla_dcc2s, DOUBLEV, PDOUBLE, PDOUBLE, V, A, B)
+PROTOCCALLSFFUN1(DOUBLE, SLA_DRANRM, sla_dranrm, DOUBLE)
+#define slaDranrm(ANGLE) \
+ CCALLSFFUN1(SLA_DRANRM, sla_dranrm, DOUBLE, ANGLE)
+PROTOCCALLSFFUN4(DOUBLE, SLA_DSEP, sla_dsep, DOUBLE, DOUBLE, DOUBLE, DOUBLE)
+#define slaDsep(A1, B1, A2, B2) \
+ CCALLSFFUN4(SLA_DSEP, sla_dsep, DOUBLE, DOUBLE, DOUBLE, DOUBLE, \
+ A1, B1, A2, B2)
+#else
+#include "slalib.h"
+#include "slamac.h"
+#endif
+
+#include "calfuse.h"
+
+double pole_ang(double pos[3], double vel[3], double ra, double dec)
+{
+ double pole[3], ra_pole, dec_pole;
+
+ /* Get 3-vector containing the orbit pole. */
+ (void) SGP4_getPole(pos, vel, pole);
+
+ /* Convert 3-vector into J2000.0 RA and DEC */
+#ifdef CFORTRAN
+ slaDcc2s(pole, ra_pole, dec_pole);
+#else
+ slaDcc2s(pole, &ra_pole, &dec_pole);
+#endif
+
+ /* Compute the angular separation of RA, DEC and RA_POLE, DEC_POLE */
+ return slaDsep(ra*RADIAN, dec*RADIAN, slaDranrm(ra_pole), dec_pole)/RADIAN;
+}
diff --git a/src/libcf/read_tle.c b/src/libcf/read_tle.c
new file mode 100644
index 0000000..38db069
--- /dev/null
+++ b/src/libcf/read_tle.c
@@ -0,0 +1,303 @@
+/******************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ ******************************************************************************
+ *
+ * Synopsis: read_tle(fitsfile *fptr)
+ *
+ * Description: read_tle will read the standard tle format file FUSE.TLE
+ * and place the orbital elements into the header of the
+ * fitsfile. The FUSE.TLE file will contain multiple orbital
+ * element sets, so the set closest in time to the observation
+ * will be used.
+ *
+ * Arguments: fitsfile *fptr Pointer to input file
+ *
+ * History: 11/11/98 emurphy Begin work.
+ * 03/18/98 emurphy Added To do list.
+ * 04/08/99 peb Tidied code and added necessary
+ * include files
+ * 06/07/99 peb Added reporting of version control.
+ * 06/22/99 peb Added FITS_ wrappers.
+ * 08/05/99 emm Added statements to print date
+ * of obs and TLE if dtime > 5.
+ * Also modified program to work with
+ * FES keywords TEXPSTRT and TEXPEND
+ * 05/31/00 peb Implemented cfortran.h calls for slalib
+ * functions.
+ * 02/13/03 v1.4 wvd Change 0 to NULL in FITS_update_key
+ * 03/01/03 v1.5 wvd Correct use of pointer in FITS_read_key
+ * 04/01/03 v1.6 wvd Replace cf_errmsg with cf_if_warning,
+ * printf with cf_verbose
+ * 12/18/03 v1.7 bjg Change calfusettag.h to calfuse.h
+ * 04/07/07 v1.8 wvd Initialize min_mjd to zero to silence
+ * compiler warnings.
+ *
+ *****************************************************************************/
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef CFORTRAN
+#include "cfortran.h"
+PROTOCCALLSFSUB5(SLA_CLDJ, sla_cldj, INT, INT, INT, PDOUBLE, PINT)
+#define slaCldj(IY, IM, ID, DJM, J) \
+ CCALLSFSUB5(SLA_CLDJ, sla_cldj, INT, INT, INT, PDOUBLE, PINT, \
+ IY, IM, ID, DJM, J)
+#else
+#include "slalib.h"
+#include "slamac.h"
+#endif
+
+#include "calfuse.h"
+
+#define MAXCHARS 120
+static char CF_PRGM_ID[] = "read_tle";
+static char CF_VER_NUM[] = "1.8";
+
+void read_tle(fitsfile *fptr)
+{
+ char line1[MAXCHARS], line2[MAXCHARS], line3[MAXCHARS];
+ char sat_num[10], security_class[10], international_num[10];
+ char inchar[15][15], sp[15][2], sgn[4][2];
+ char comment[FLEN_CARD], eccen_str[20];
+ char n6_mant_str[20], drag_mant_str[20], instrument[FLEN_CARD];
+ int status=0, hdutype=0, card_num, epoch_year, n6_exponent;
+ int drag_exponent, ephemeris, elset_num, checksum1, checksum2;
+ int rev_num, e_month, e_day;
+ double n6_mantissa, drag_mantissa, epoch_day, n2, inclin, raan;
+ double aop, mean_anom, mean_mot, n6, drag, semimaj, revspersec;
+ double expstart, expend, expmiddle, eccentr, dtime=1.0e9;
+ double i_day, f_day, mjd, mjd_f, mjd_d, min_mjd=0;
+ FILE *ftle;
+
+ /* Enter a timestamp into the log. */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Begin reading TLE file");
+
+ /* Open the file with the standard two-line elements. */
+ ftle = NULL;
+ ftle = fopen(cf_cal_file("FUSE.TLE"),"r");
+ if (ftle == NULL)
+ cf_if_error("Cannot find file FUSE.TLE, which contains the"
+ " orbital elements.");
+
+ /* Rewind the fits file and get the start time of the exposure */
+ FITS_movabs_hdu(fptr, 1, &hdutype, &status);
+ FITS_read_key(fptr, TSTRING, "INSTRUME", instrument, comment, &status);
+
+ /* In the primary header, the FES files use keywords with a different
+ name. This is because the FES may have many exposures in a file.
+ The TEXPSTRT is the start of the first exposure and TEXPEND is the
+ end of the last exposure. */
+ if (!strncmp(instrument,"FES",3)) {
+ /* Read in FES keywords */
+ FITS_read_key(fptr, TDOUBLE, "TEXPSTRT", &expstart, comment, &status);
+ FITS_read_key(fptr, TDOUBLE, "TEXPEND", &expend, comment, &status);
+ } else {
+ /* Read in FUV keywords */
+ FITS_read_key(fptr, TDOUBLE, "EXPSTART", &expstart, comment, &status);
+ FITS_read_key(fptr, TDOUBLE, "EXPEND", &expend, comment, &status);
+ }
+
+ /* expmiddle is in mjd */
+ expmiddle = (expstart+expend)/2.0;
+
+ /* Read in FUSE.TLE file by getting one line at a time */
+ while (fgets(line1, MAXCHARS,ftle) != NULL) {
+ fgets(line2, MAXCHARS, ftle);
+ fgets(line3, MAXCHARS, ftle);
+
+#ifdef DEBUG
+ printf("\n\n%-80.80s\n",line1);
+ printf("%-80.80s\n",line2);
+ printf("%-80.80s\n",line3);
+#endif
+ /*
+ * This may look like a stupid way to do this, but it has to be
+ * done this way. You see, the fields can contain leading blanks
+ * which should be interpreted as zeros. However, c skips over
+ * leading blanks when reading %f and %d.
+ */
+ sscanf(line2,"%1c%1c%5c%1c%1c%8c%1c%2c%12c%1c%1c%9c%1c%1c%5c%2c%1c"
+ "%1c%5c%2c%1c%1c%1c%4c%1c",
+ inchar[1],sp[1],inchar[2],inchar[3],sp[2],inchar[4],sp[3],
+ inchar[5],inchar[6],sp[4],sgn[1],inchar[7],sp[5],sgn[2],
+ inchar[8],inchar[9],sp[6],sgn[3],inchar[10],inchar[11],
+ sp[7],inchar[12], sp[8],inchar[13],inchar[14]);
+
+ inchar[1][1]='\0';
+ card_num=atoi(inchar[1]);
+
+ inchar[2][5]='\0';
+ strncpy(sat_num,inchar[2],5);
+
+ inchar[3][1]='\0';
+ strncpy(security_class,inchar[3],1);
+
+ inchar[4][8]='\0';
+ strncpy(international_num,inchar[4],8);
+
+ inchar[5][2]='\0';
+ epoch_year=atoi(inchar[5]);
+ if (epoch_year < 50)
+ epoch_year+=2000;
+ else
+ epoch_year+=1900;
+
+ inchar[6][12]='\0';
+ epoch_day=atof(inchar[6]);
+
+ inchar[7][9]='\0';
+ n2=atof(inchar[7]);
+ if (sgn[1][0] == '-')
+ n2*=-1.0;
+
+ inchar[8][5]='\0';
+ strcpy(n6_mant_str,"0.");
+ strncat(n6_mant_str,inchar[8],7);
+#ifdef DEBUG
+ printf("n6=%-10.10s\n",n6_mant_str);
+#endif
+ n6_mantissa=atof(n6_mant_str);
+ if (sgn[2][0] == '-')
+ n6_mantissa*=-1.0;
+
+ inchar[9][2]='\0';
+ n6_exponent=atoi(inchar[9]);
+
+ inchar[10][5]='\0';
+ strcpy(drag_mant_str,"0.");
+ strncat(drag_mant_str,inchar[10],7);
+#ifdef DEBUG
+ printf("Drag=%-10.10s\n",drag_mant_str);
+#endif
+ drag_mantissa=atof(drag_mant_str);
+ if (sgn[3][0] == '-')
+ drag_mantissa*=-1.0;
+
+ inchar[11][2]='\0';
+ drag_exponent=atoi(inchar[11]);
+
+ inchar[12][1]='\0';
+ ephemeris=atoi(inchar[12]);
+
+ inchar[13][4]='\0';
+ elset_num=atoi(inchar[13]);
+
+ inchar[14][1]='\0';
+ checksum1=atoi(inchar[14]);
+
+ n6=n6_mantissa*pow(10.0,(double) n6_exponent);
+ drag=drag_mantissa*pow(10.0,(double) drag_exponent);
+
+#ifdef DEBUG
+ printf("n6=>%10.5f %5d %15.6E\n", n6_mantissa, n6_exponent, n6);
+ printf("drag=>%10.5f %5d %15.6E\n", drag_mantissa,
+ drag_exponent, drag);
+ for(i=1; i<=14; i++)
+ printf("%2d %-15.15s\n",i,inchar[i]);
+ for(i=1; i<=3; i++)
+ printf("%2d %-5.5s\n",i,sgn[i]);
+#endif
+ sscanf(line3,"%1c%1c%5c%1c%8c%1c%8c%1c%7c%1c%8c%1c%8c%1c%11c%5c%1c",
+ inchar[1],sp[1],inchar[2],sp[2],inchar[3],sp[3],inchar[4],
+ sp[4],inchar[5],sp[5],inchar[6],sp[6],inchar[7],sp[7],
+ inchar[8],inchar[9],inchar[10]);
+ inchar[1][1]='\0';
+ card_num=atoi(inchar[1]);
+
+ inchar[2][5]='\0';
+ strncpy(sat_num,inchar[2],5);
+
+ inchar[3][8]='\0';
+ inclin=atof(inchar[3]);
+
+ inchar[4][8]='\0';
+ raan=atof(inchar[4]);
+
+ inchar[5][7]='\0';
+ /*
+ * strcpy(eccen_str,"0.");
+ * strncat(eccen_str,inchar[5],8);
+ */
+ sprintf(eccen_str,"0.%-8s",inchar[5]);
+#ifdef DEBUG
+ printf("Ecstr=%-20.20s\n",eccen_str);
+#endif
+ eccentr=atof(eccen_str);
+
+ inchar[6][8]='\0';
+ aop=atof(inchar[6]);
+
+ inchar[7][8]='\0';
+ mean_anom=atof(inchar[7]);
+
+ inchar[8][11]='\0';
+ mean_mot=atof(inchar[8]);
+
+ inchar[9][5]='\0';
+ rev_num=atof(inchar[9]);
+
+ inchar[10][1]='\0';
+ checksum2=atoi(inchar[10]);
+
+#ifdef DEBUG
+ for(i=1; i<=10; i++)
+ printf("%2d %-15.15s\n",i,inchar[i]);
+ printf("%4d\n%12.8f\n%10.8f\n%10.5E\n%10.5E%5d%5d%5d\n",
+ epoch_year, epoch_day, n2, n6, drag, ephemeris,
+ elset_num, checksum1);
+ printf("\n%8.4f\n%8.4f\n%10.8f\n%8.4f\n%8.4f\n%11.8f\n%5d\n%1d\n",
+ inclin, raan, (float) eccentr, aop, mean_anom,
+ mean_mot, rev_num, checksum2);
+#endif
+ /* Convert the year and day of year into a year month day */
+
+ f_day = modf(epoch_day, &i_day);
+
+ month_day(epoch_year, (int) i_day, &e_month, &e_day);
+
+ /* Convert year month day into a Julian date. */
+#ifdef CFORTRAN
+ slaCldj(epoch_year, e_month, e_day, mjd, status);
+#else
+ slaCldj(epoch_year, e_month, e_day, &mjd, &status);
+#endif
+
+ mjd += f_day;
+
+ if (fabs(expmiddle-mjd) < dtime) {
+ dtime = fabs(expmiddle-mjd);
+ min_mjd=mjd;
+ revspersec = mean_mot/(24.0*60.0*60.0);
+ semimaj = pow(MU/(4.0*PI*PI*revspersec*revspersec),1.0/3.0);
+ mjd_f=modf(mjd, &mjd_d);
+ FITS_update_key(fptr, TDOUBLE, "EPCHTIMD", &mjd_d, NULL, &status);
+ FITS_update_key(fptr, TDOUBLE, "EPCHTIMF", &mjd_f, NULL, &status);
+ FITS_update_key(fptr, TDOUBLE, "INCLINAT", &inclin, NULL, &status);
+ FITS_update_key(fptr, TDOUBLE, "ECCENTRY", &eccentr, NULL, &status);
+ FITS_update_key(fptr, TDOUBLE, "MEANANOM", &mean_anom, NULL,&status);
+ FITS_update_key(fptr, TDOUBLE, "ARGPERIG", &aop, NULL, &status);
+ FITS_update_key(fptr, TDOUBLE, "RASCASCN", &raan, NULL, &status);
+ FITS_update_key(fptr, TDOUBLE, "SEMIMAJR", &semimaj, NULL, &status);
+ FITS_update_key(fptr, TDOUBLE, "MEANMOTN", &mean_mot, NULL, &status);
+ FITS_update_key(fptr, TDOUBLE, "FDM2COEF", &n2, NULL, &status);
+ FITS_update_key(fptr, TDOUBLE, "SDM6COEF", &n6, NULL, &status);
+ FITS_update_key(fptr, TDOUBLE, "DRAGCOEF", &drag, NULL, &status);
+ FITS_update_key(fptr, TSTRING, "PROPMODL", "SGP4", NULL, &status);
+ }
+ } /* endwhile */
+
+ if (dtime > 5.0) {
+ cf_verbose(2, "MJD OBS=%12.5f MJD TLE=%12.5f DT=%12.5f\n",
+ expmiddle, min_mjd, dtime);
+ cf_if_warning("Orbital elements are more than 5 days old.");
+ }
+
+ /* Enter a timestamp into the log. */
+ cf_timestamp(CF_PRGM_ID, CF_VER_NUM, "Done reading TLE file");
+}
+
diff --git a/src/libcf/saa.c b/src/libcf/saa.c
new file mode 100644
index 0000000..0d2d0e3
--- /dev/null
+++ b/src/libcf/saa.c
@@ -0,0 +1,74 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: int saa(saareg *saa, double lon, double lat)
+ *
+ * Description: Determines if FUSE is in the SAA for given models of the SAA
+ *
+ *
+ * Arguments: saareg *saa A structure containing data that roughly
+ * outlines the SAA region.
+ * (n_points: number of points
+ * lat: the latitude array
+ * lon: the longitude array)
+ * (currently we have only one model).
+ * double lon,lat Geocentric longitude and latitude of FUSE
+ *
+ * Returns: int 0 if not in SAA
+ * 1 if in SAA
+ *
+ * History: 07/17/98 E. Murphy Begin work.
+ * 06/22/99 peb Tidied code, added FITS_ wrappers,
+ * removed hardcoded variables.
+ * 07/02/99 peb Moved SAA data to a structure.
+ * 08/25/99 emm Fixed bug in setting maxlon
+ * 08/28/03 v1.3 bjg Adopt Bryce's algorithm for determining
+ * when FUSE is in the SAA.
+ * 08/28/03 v1.4 bjg v1.3 didn't compile but v1.4 does
+ * 08/29/03 v1.5 bjg slightly modified getDir
+ *
+ ****************************************************************************/
+
+#include "calfuse.h"
+
+int getDir(double lon0,double lat0,double lon1,double lat1,double lon2,double lat2)
+{
+
+double dx1, dx2, dy1, dy2, t1, t2;
+
+ dx1=lon1-lon0; dy1=lat1-lat0;
+ dx2=lon2-lon0; dy2=lat2-lat0;
+
+ t1=dx1*dy2; t2=dy1*dx2;
+
+ if (t1>t2)
+ return(-1);
+ if (t1<t2)
+ return(1);
+
+
+ return(1);
+
+}
+
+
+int
+saa(saareg *saa, double lon, double lat)
+{
+int i;
+int dir=0;
+int oldDir=0;
+
+for (i=0; i<saa->n_points-1; i++) {
+ dir=getDir(lon,lat,(double)(saa->lon[i]),(double)(saa->lat[i]),(double)(saa->lon[i+1]),(double)(saa->lat[i+1]));
+ if (dir*oldDir<0)
+ return(0);
+ else
+ oldDir=dir;
+ }
+
+ return(1);
+}
diff --git a/src/libcf/set_orbit_parms.c b/src/libcf/set_orbit_parms.c
new file mode 100644
index 0000000..65baca6
--- /dev/null
+++ b/src/libcf/set_orbit_parms.c
@@ -0,0 +1,86 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: SGP4 set_orbit_parms(fitsfile *infits)
+ *
+ * Description: Initialized the sgp4 structure and reads the orbital
+ * paramaters from the infits header and places the data
+ * in the sgp4 structure. It also calculates a number of
+ * time invariant quantities in the sgp4 structure.
+ *
+ * Arguments: fitsfile *infits Input FITS file.
+ *
+ * Return: SGP4 sgp4 Structure with orb parms.
+ *
+ * History: 07/07/99 emurphy Begin and finished work
+ * 07/14/99 emurphy Fixed minor bugs
+ * 07/21/99 peb Changed function to return SGP4
+ * pointer. (This fixes a bug.)
+ * 08/05/99 emurphy Converted to use set_orbit_parms_calc
+ * so that FES pipeline can use these
+ * routines.
+ * 12/18/03 bjg Change calfusettag.h to calfuse.h
+ * 04/07/07 1.4 wvd Delete CF_PRGM_ID and CF_VER_NUM,
+ * as they are not used.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include "calfuse.h"
+#include "sgp4.h"
+
+SGP4 set_orbit_parms_calc(fitsfile *infits)
+{
+ int status=0;
+ char comment[FLEN_CARD];
+ double epchtime, td, tf, inclinat, eccentry, meananom, argperig, rascascn;
+ double n0dt, n0dt2, bstar, mean_motion;
+ SGP4 sgp4;
+
+ /* Get the orbital data from the header of the infits file. */
+ FITS_read_key(infits, TDOUBLE, "EPCHTIMD", &td, comment, &status);
+ FITS_read_key(infits, TDOUBLE, "EPCHTIMF", &tf, comment, &status);
+ epchtime=td+tf;
+ FITS_read_key(infits, TDOUBLE, "INCLINAT", &inclinat, comment, &status);
+ FITS_read_key(infits, TDOUBLE, "ECCENTRY", &eccentry, comment, &status);
+ FITS_read_key(infits, TDOUBLE, "MEANANOM", &meananom, comment, &status);
+ FITS_read_key(infits, TDOUBLE, "ARGPERIG", &argperig, comment, &status);
+ FITS_read_key(infits, TDOUBLE, "RASCASCN", &rascascn, comment, &status);
+ FITS_read_key(infits, TDOUBLE, "FDM2COEF", &n0dt, comment, &status);
+ FITS_read_key(infits, TDOUBLE, "SDM6COEF", &n0dt2, comment, &status);
+ FITS_read_key(infits, TDOUBLE, "DRAGCOEF", &bstar, comment, &status);
+ FITS_read_key(infits, TDOUBLE, "MEANMOTN", &mean_motion, comment, &status);
+
+ sgp4 = SGP4_create();
+
+ SGP4_set(sgp4, epchtime, n0dt, n0dt2, bstar, inclinat,
+ rascascn, eccentry, argperig, meananom, mean_motion);
+
+ /* do time invariant initializations */
+ SGP4_init(sgp4);
+ return sgp4;
+}
+
+SGP4 set_orbit_parms(fitsfile *infits)
+{
+ int status=0, hdunum=0, hdutype;
+ SGP4 sgp4;
+ /*
+ * The infits file pointer may be down in one of the extensions
+ * whereas the orbital info is in the primary hdu. Therefore,
+ * record the current position, move to the primary header,
+ * set the orbital data, and return to the previous hdu.
+ */
+ FITS_get_hdu_num(infits, &hdunum);
+ FITS_movabs_hdu(infits, 1, &hdutype, &status);
+
+ sgp4=set_orbit_parms_calc(infits);
+
+ FITS_movabs_hdu(infits, hdunum, &hdutype, &status);
+
+ return sgp4;
+
+}
diff --git a/src/libcf/sgp4.c b/src/libcf/sgp4.c
new file mode 100644
index 0000000..b14dc45
--- /dev/null
+++ b/src/libcf/sgp4.c
@@ -0,0 +1,426 @@
+/* H+
+ * Title : sgp4.c
+ * Author : Bryce A. Roberts
+ * Date : 23 February 1999
+ * Synopsis : implementation of SGP4 propagation routines
+ * SCCS : @(#)sgp4.c 1.6 05/14/00
+ * Revisions :
+ * mm/dd/yy name description
+ * 01/14/00 ma get rid of unused function get_time (May 14 2000)
+ * 12/18/03 bjg Change calfusettag.h to calfuse.h
+* H-
+ */
+
+#include <string.h>
+#include <time.h>
+#include "calfuse.h"
+#include "sgp4.h"
+
+/* define some absolute constants used by SGP4 */
+#define MIN_PER_DAY (1440.0)
+#define KM_PER_EARTH (6378.135)
+#define TWOBYTHREE (2.0/3.0)
+#define Q0 (120.0)
+#define S0 (78.0)
+#define THREEPIBYTWO (PIBY2*3.0)
+#define KE (0.074366916)
+#define AE (1.0)
+#define S (AE*(1.0+S0/KM_PER_EARTH))
+#define Q0MS4 pow((Q0-S0)*AE/KM_PER_EARTH, 4)
+#define CK2 (0.5*J2*AE*AE)
+#define CK4 (-0.375*J4*AE*AE*AE*AE)
+#define A30 (-J3/AE*AE*AE)
+
+#ifndef J2
+#define J2 (1.082616E-3)
+#endif
+
+#ifndef J3
+#define J3 (-0.253881E-5)
+#endif
+
+#ifndef J4
+#define J4 (-1.65597E-6)
+#endif
+
+#ifndef PI
+#define PI (3.141592653589793)
+#endif
+
+#ifndef TWOPI
+#define TWOPI (2.0*PI)
+#endif
+
+#define RAD2DEG(x) (((x)*180.0)/PI)
+#define DEG2RAD(x) ((PI/180.0)*(x))
+
+#ifndef false
+#define false (0)
+#endif
+
+#ifndef true
+#define true (1)
+#endif
+
+
+/*
+ * Name: SGP4_isLeap
+ * Purpose: determine if a given year is a leap year
+ * Input: int year (e.g. 1999)
+ * Output: returns 1 if leap year, 0 if not
+ */
+static int SGP4_isLeap(int year) {
+ return(((year%4==0 && year%100!=0) || year%400==0));
+ }
+
+
+/*
+ * Name: SGP4_doy2cal
+ * Purpose: convert year/day-of-year to Gregorian calendar month and day
+ * Input: int year, doy
+ * Output: int *month, int *day, returns 0 for fail, 1 for success
+ */
+static int SGP4_doy2cal(int year, int doy, int *month, int *day) {
+ /* number of days in leap and non-leap year months */
+ static int daytab[2][13]={{0,31,28,31,30,31,30,31,31,30,31,30,31},
+ {0,31,29,31,30,31,30,31,31,30,31,30,31}};
+ int leap;
+
+ /* do range checking */
+ if (doy<1 || doy>366)
+ return(false);
+
+ leap=SGP4_isLeap(year) ? 1 : 0;
+ *day=doy;
+ for (*month=1; *day>daytab[leap][*month]; (*month)++)
+ *day-=daytab[leap][*month];
+
+ return(true);
+ }
+
+
+/*
+ * Name: SGP4_create
+ * Purpose: creates an instance of SGP4
+ * Input: none
+ * Output: SGP4
+ */
+SGP4 SGP4_create(void) {
+ return((SGP4)calloc(1, sizeof(struct sgp4_st)));
+ }
+
+
+/*
+ * Name: SGP4_destroy
+ * Purpose: destroys an instance of SGP4
+ * Input: SGP4 sgp4
+ * Output: none
+ */
+void SGP4_destroy(SGP4 sgp4) {
+ if (sgp4)
+ free(sgp4);
+ }
+
+
+/*
+ * Name: SGP4_init
+ * Purpose: initializes time-invariant SGP4 variables
+ * Input: SGP4 sgp4
+ * Output: none
+ */
+void SGP4_init(SGP4 sgp4) {
+ double a1, del1, a0, del0, perigee, sStar, eta_2, eta_3, eta_4, pinvsq,
+ temp1, temp2, temp3, x3thetam1, x1m5th, xhdot1, theta_4, zeta, zeta_2,
+ zeta_3, zeta_5, beta0, beta0_3, beta0_4, C2;
+
+ /* recover original mean motion and semimajor axis from the elements */
+ a1=pow(KE/sgp4->n0, TWOBYTHREE);
+ del1=1.5 * (CK2/(a1*a1))*((3*cos(sgp4->i0)*cos(sgp4->i0)-1.0)/
+ pow(1-sgp4->e0*sgp4->e0, 1.5));
+ a0=a1*(1-del1/3.0-del1*del1-(134.0/81.0)*del1*del1*del1);
+ del0=(del1*a1*a1)/(a0*a0);
+
+ /* find original mean motion, n0dp (n0'') */
+ sgp4->n0dp=sgp4->n0/(1+del0);
+
+ /* find semimajor axis, a0dp (a0'') */
+ sgp4->a0dp=a0/(1-del0);
+
+ /* find the perigee (in km) */
+ perigee=(sgp4->a0dp*(1.0-sgp4->e0)-AE)*KM_PER_EARTH;
+
+ /* make decisions on what value of s to use based on perigee */
+ if (perigee<=156.0 && perigee>=98.0) {
+ sStar=sgp4->a0dp*(1.0-sgp4->e0)-S-AE;
+ sgp4->q0ms4=pow(pow(Q0MS4, 0.25) + S - sStar, 4);
+ }
+ else if (perigee<98.0) {
+ sStar=20/KM_PER_EARTH + AE;
+ sgp4->q0ms4=pow(pow(Q0MS4, 0.25) + S - sStar, 4);
+ }
+ else {
+ sStar=S;
+ sgp4->q0ms4=Q0MS4;
+ }
+
+ sgp4->theta=cos(sgp4->i0); sgp4->theta_2=sgp4->theta*sgp4->theta;
+ theta_4=sgp4->theta_2*sgp4->theta_2;
+ zeta=1/(sgp4->a0dp-sStar);
+
+ zeta_2=zeta*zeta, zeta_3=zeta_2*zeta;
+ sgp4->zeta_4=zeta_3*zeta;
+ zeta_5=sgp4->zeta_4*zeta;
+
+ sgp4->beta0_2=1-sgp4->e0*sgp4->e0;
+ beta0=sqrt(sgp4->beta0_2); beta0_3=sgp4->beta0_2*beta0;
+ beta0_4=beta0_3*beta0;
+
+ sgp4->eta=sgp4->a0dp*sgp4->e0*zeta;
+ eta_2=sgp4->eta*sgp4->eta, eta_3=eta_2*sgp4->eta, eta_4=eta_3*sgp4->eta;
+
+ C2=sgp4->q0ms4*(sgp4->zeta_4)*sgp4->n0dp*pow(1-eta_2, -7.0/2.0)*
+ (sgp4->a0dp*(1+ ((3.0/2.0)*eta_2) + 4*sgp4->e0*sgp4->eta +
+ sgp4->e0*eta_3) + (3.0/2.0)*(CK2*zeta/(1-eta_2))*(-0.5 +
+ 1.5*sgp4->theta_2)*(8.0+24*eta_2 + 3*eta_4));
+ sgp4->C1=sgp4->bstar*C2; sgp4->C1_2=sgp4->C1*sgp4->C1;
+ sgp4->C1_3=sgp4->C1_2*sgp4->C1; sgp4->C1_4=sgp4->C1_3*sgp4->C1;
+ sgp4->C3=(sgp4->q0ms4*(zeta_5)*A30*sgp4->n0dp*AE*sin(sgp4->i0))/
+ (CK2*sgp4->e0);
+ sgp4->C4=2*sgp4->n0dp*sgp4->q0ms4*(sgp4->zeta_4)*sgp4->a0dp*(sgp4->beta0_2)*
+ pow(1-eta_2, -7.0/2.0)*( (2*sgp4->eta*(1+sgp4->e0*sgp4->eta)+
+ 0.5*sgp4->e0+0.5*eta_3)-2*CK2*zeta/(sgp4->a0dp*(1-eta_2))*
+ (3*(1-3*sgp4->theta_2)*(1 + 1.5*eta_2 -2*sgp4->e0*sgp4->eta -
+ 0.5*sgp4->e0*eta_3) + 0.75*(1-sgp4->theta_2)*(2*eta_2 -
+ sgp4->e0*sgp4->eta - sgp4->e0*eta_3)*cos(2*sgp4->w0)));
+ sgp4->C5=2*sgp4->q0ms4*(sgp4->zeta_4)*sgp4->a0dp*(sgp4->beta0_2)*
+ pow(1-eta_2, -7.0/2.0)*(1 + (11.0/4.0)*sgp4->eta*(sgp4->eta+sgp4->e0)+
+ sgp4->e0*eta_3);
+
+ sgp4->D2=4*sgp4->a0dp*zeta*sgp4->C1_2;
+ sgp4->D3=(4.0/3.0)*sgp4->a0dp*zeta_2*(17*sgp4->a0dp +
+ sStar)*sgp4->C1_3;
+ sgp4->D4=TWOBYTHREE*sgp4->a0dp*zeta_3*(221*sgp4->a0dp+
+ 31*sStar)*sgp4->C1_4;
+
+ /* constants for secular effects calculation */
+ pinvsq=1.0/(sgp4->a0dp*sgp4->a0dp*beta0_4);
+ temp1=3.0*CK2*pinvsq*sgp4->n0dp;
+ temp2=temp1*CK2*pinvsq;
+ temp3=1.25*CK4*pinvsq*pinvsq*sgp4->n0dp;
+ x3thetam1=3.0*sgp4->theta_2 - 1.0;
+ x1m5th=1.0 - 5.0*sgp4->theta_2;
+ xhdot1=-temp1*sgp4->theta;
+
+ sgp4->mdot=sgp4->n0dp+0.5*temp1*beta0*x3thetam1+
+ 0.0625*temp2*beta0*(13.0 - 78.0*sgp4->theta_2+137.0*theta_4);
+
+ sgp4->wdot=-0.5*temp1*x1m5th+0.0625*temp2*(7.0-114.0*sgp4->theta_2+
+ 395.0*theta_4)+temp3*(3.0-36.0*sgp4->theta_2+49.0*theta_4);
+
+ sgp4->raandot=xhdot1+(0.5*temp2*(4.0-19.0*sgp4->theta_2)+2.0*temp3*
+ (3.0-7.0*sgp4->theta_2))*sgp4->theta;
+ }
+
+
+/*
+ * Name: SGP4_getStateVector
+ * Purpose: finds position, velocity at a time
+ * Input: SGP4 sgp4, double t
+ * Output: double pos[3], double vel[3]
+ */
+void SGP4_getStateVector(SGP4 sgp4, double t, double pos[3], double vel[3]) {
+ double mdf, wdf, raandf, del_w, del_m, mp, w, RAAN, e, a, IL,
+ beta_2, n, axn, ILl, aynl, ILt, ayn, U, initial, eCosE,
+ eSinE, eL_2, pL, r, rDot, rfDot, cosu, sinu, u, rk, uk, RAANk, ik,
+ rDotk, rfDotk, mx, my, ux, uy, uz, vx, vy, vz, tsince;
+ int i;
+
+ /* find the time since the epoch, in minutes */
+ tsince=(t-sgp4->epochTime)*1440;
+
+ /* secular effects of atmospheric drag and gravitation */
+ mdf=sgp4->M0+(sgp4->mdot*tsince);
+ wdf=sgp4->w0+(sgp4->wdot*tsince);
+ raandf=sgp4->raan+(sgp4->raandot*tsince);
+
+ del_w=sgp4->bstar*sgp4->C3*(cos(sgp4->w0))*tsince;
+ del_m=-TWOBYTHREE*sgp4->q0ms4*sgp4->bstar*sgp4->zeta_4*
+ (AE/(sgp4->e0*sgp4->eta))*(pow(1+sgp4->eta*cos(mdf), 3)-
+ pow(1+sgp4->eta*cos(sgp4->M0), 3));
+
+ mp=mdf+del_w+del_m;
+ w=wdf-del_w-del_m;
+
+ RAAN=raandf-(21.0/2.0)*((sgp4->n0dp*CK2*sgp4->theta)/(sgp4->a0dp*
+ sgp4->a0dp*sgp4->beta0_2))* sgp4->C1*tsince*tsince;
+ e=sgp4->e0-sgp4->bstar*sgp4->C4*tsince-sgp4->bstar*sgp4->C5*
+ (sin(mp)-sin(sgp4->M0));
+
+ a=sgp4->a0dp*pow((1+tsince*(-sgp4->C1+tsince*(-sgp4->D2+tsince*
+ (-sgp4->D3-sgp4->D4*tsince)))), 2);
+
+ IL=mp+w+RAAN+sgp4->n0dp*tsince*tsince*(1.5*sgp4->C1+tsince*
+ ((sgp4->D2+2*sgp4->C1_2) + tsince*(0.25*(3*sgp4->D3 +
+ 12*sgp4->C1*sgp4->D2 + 10*sgp4->C1_3)+tsince*(0.2*(3*sgp4->D4+12*
+ sgp4->C1*sgp4->D3+6*sgp4->D2*sgp4->D2+30*sgp4->C1_2*sgp4->D2+
+ 15*sgp4->C1_4)))));
+
+ beta_2=1-e*e;
+ n=KE/pow(a, 3.0/2.0);
+
+ /* find the long-period periodic terms */
+ axn=e*cos(w);
+ ILl=(A30*sin(sgp4->i0))/(8*CK2*a*beta_2)*(e*cos(w))*((3+5*sgp4->theta)/
+ (1+sgp4->theta));
+ aynl=(A30*sin(sgp4->i0))/(4*CK2*a*beta_2);
+ ILt=IL+ILl;
+ ayn=e*sin(w)+aynl;
+
+ /* iteratively solve Kepler's equation */
+ U=fmod(ILt-RAAN, TWOPI);
+
+ initial=U;
+ for (i=0; i<10; i++) {
+ U=(initial-ayn*cos(U)+axn*sin(U)-U)/(1.0-ayn*sin(U)-axn*cos(U))+U;
+ }
+
+ /* preliminary quantities for short period periodics */
+ eCosE=axn*cos(U)+ayn*sin(U);
+ eSinE=axn*sin(U)-ayn*cos(U);
+ eL_2=axn*axn+ayn*ayn;
+ pL=a*(1-eL_2);
+ r=a*(1-eCosE);
+ rDot=KE*(sqrt(a)/r)*eSinE;
+ rfDot=KE*sqrt(pL)/r;
+ cosu=(a/r)*(cos(U)-axn+(ayn*eSinE)/(1+sqrt(1-eL_2)));
+ sinu=(a/r)*(sin(U)-ayn-(axn*eSinE)/(1+sqrt(1-eL_2)));
+ u=fmod(atan2(sinu, cosu)+TWOPI, TWOPI);
+
+ /* update for short-period periodics */
+ rk=r*(1-1.5*CK2*sqrt(1-eL_2)/(pL*pL)*(3*sgp4->theta_2-1))+
+ (CK2/(2*pL))*(1-sgp4->theta_2)*cos(2*u);
+ uk=u+-(CK2/(4*pL*pL))*(7*sgp4->theta_2-1.0)*sin(2*u);;
+ RAANk=RAAN+((3*CK2*sgp4->theta)/(2*pL*pL))*sin(2*u);
+ ik=sgp4->i0+((3*CK2*sgp4->theta)/(2*pL*pL))*sin(sgp4->i0)*cos(2*u);
+ rDotk=rDot-((CK2*n)/pL)*(1-sgp4->theta_2)*sin(2*u);
+ rfDotk=rfDot+((CK2*n)/pL)*((1-sgp4->theta_2)*cos(2*u)-
+ 1.5*(1-3*sgp4->theta_2));
+
+ /* find the position and velocity components */
+ mx=-sin(RAANk)*cos(ik);
+ my=cos(RAANk)*cos(ik);
+
+ ux=mx*sin(uk)+cos(RAANk)*cos(uk);
+ uy=my*sin(uk)+sin(RAANk)*cos(uk);
+ uz=sin(ik)*sin(uk);
+
+ vx=mx*cos(uk)-cos(RAANk)*sin(uk);
+ vy=my*cos(uk)-sin(RAANk)*sin(uk);
+ vz=sin(ik)*cos(uk);
+
+ pos[0]=KM_PER_EARTH*rk*ux;
+ pos[1]=KM_PER_EARTH*rk*uy;
+ pos[2]=KM_PER_EARTH*rk*uz;
+
+ vel[0]=MIN_PER_DAY*KM_PER_EARTH*(rDotk*ux+rfDotk*vx)/(AE*86400.0);
+ vel[1]=MIN_PER_DAY*KM_PER_EARTH*(rDotk*uy+rfDotk*vy)/(AE*86400.0);
+ vel[2]=MIN_PER_DAY*KM_PER_EARTH*(rDotk*uz+rfDotk*vz)/(AE*86400.0);
+ }
+
+
+/*
+ * Name: SGP4_set
+ * Purpose: sets the elements of an instance of SGP4
+ * Input: SGP4 sgp4, double epochTime, double n0dt, double n0dt2,
+ * double bstar, double i0, double raan, double e0,
+ * double w0, double M0, double n0
+ * Output: none
+ */
+void SGP4_set(SGP4 sgp4, double epochTime, double n0dt, double n0dt2,
+ double bstar, double i0, double raan, double e0,
+ double w0, double M0, double n0) {
+
+ /* set the epoch time */
+ sgp4->epochTime=epochTime;
+
+ /* set the first time derivative of mean motion */
+ sgp4->n0dt=n0dt*(TWOPI/(MIN_PER_DAY*MIN_PER_DAY));
+
+ /* set the second time derivative of mean motion */
+ sgp4->n0dt2=n0dt2*(TWOPI/(MIN_PER_DAY*MIN_PER_DAY));
+
+ /* bstar drag term */
+ sgp4->bstar=bstar;
+
+ /* inclination */
+ sgp4->i0=DEG2RAD(i0);
+
+ /* right ascension of the ascending node */
+ sgp4->raan=DEG2RAD(raan);
+
+ /* eccentricity */
+ sgp4->e0=e0;
+
+ /* argument of perigee */
+ sgp4->w0=DEG2RAD(w0);
+
+ /* mean anomaly */
+ sgp4->M0=DEG2RAD(M0);
+
+ /* mean motion */
+ sgp4->n0=n0*(TWOPI/MIN_PER_DAY);
+ }
+
+
+/*
+ * Name: SGP4_getPole
+ * Purpose: finds the pole (orbit plane normal)
+ * Input: double pos[3], double vel[3]
+ * Output: double pole[3]
+ */
+void SGP4_getPole(double pos[3], double vel[3], double pole[3]) {
+ register double pNorm;
+
+ /* find the cross product between position and velocity */
+ pole[0]=pos[1]*vel[2] - vel[1]*pos[2];
+ pole[1]=pos[2]*vel[0] - vel[2]*pos[0];
+ pole[2]=pos[0]*vel[1] - vel[0]*pos[1];
+
+ /* normalize the resulting pole vector */
+ pNorm=sqrt(pole[0]*pole[0]+pole[1]*pole[1]+pole[2]*pole[2]);
+ pole[0]/=pNorm; pole[1]/=pNorm; pole[2]/=pNorm;
+ }
+
+
+/*
+ * Name: SGP4_precess
+ * Purpose: precesses a direction vector from t1 to t2
+ * Input: double v[3], double t1, double t2
+ * Output: double v[3]
+ */
+void SGP4_precess(double v[3], double t1, double t2) {
+ double t, st, a, b, c, temp[3], sina, sinb, sinc, cosa, cosb, cosc;
+
+ /* convert times to years */
+ t=0.001*(t2-t1)/365.25;
+ st=0.001*(t1-MJD2000)/365.25;
+
+ /* find the Euler angles */
+ a=DEG2RAD(t*(23062.181+st*(139.656+0.0139*st)+
+ +t*(30.188-0.344*st+17.998*t)))/3600.0;
+ b=DEG2RAD(t*t*(79.280+0.410*st+0.205*t)/3600.0)+a;
+ c=DEG2RAD(t*(20043.109-st*(85.33+0.217*st)+
+ +t*(-42.665-0.217*st-41.833*t)))/3600.0;
+
+ /* do the precession rotation */
+ sina=sin(a); sinb=sin(b); sinc=sin(c);
+ cosa=cos(a); cosb=cos(b); cosc=cos(c);
+ temp[0]=(cosa*cosb*cosc-sina*sinb)*v[0]+(-cosa*sinb-sina*cosb*cosc)*v[1]+
+ -cosb*sinc*v[2];
+ temp[1]=(sina*cosb+cosa*sinb*cosc)*v[0]+(cosa*cosb-sina*sinb*cosc)*v[1]+
+ -sinb*sinc*v[2];
+ temp[2]=cosa*sinc*v[0]-sina*sinc*v[1]+cosc*v[2];
+
+ /* replace existing direction vector */
+ v[0]=temp[0]; v[1]=temp[1]; v[2]=temp[2];
+ }
diff --git a/src/libcf/solar_ang.c b/src/libcf/solar_ang.c
new file mode 100644
index 0000000..421d460
--- /dev/null
+++ b/src/libcf/solar_ang.c
@@ -0,0 +1,90 @@
+/******************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ ******************************************************************************
+ *
+ * Synopsis: double solar_ang(double mjdate, double ra, double dec)
+ *
+ * Description: Computes the angle between the Sun and J2000.0 RA and DEC
+ * for a given date.
+ *
+ * Arguments: double mjdate Modified Julian date of observation
+ * double ra (deg) J2000.0 right ascension
+ * double dec (deg) J2000.0 declination
+ *
+ * Returns: double (deg) angle between Sun and RA,DEC
+ *
+ * Calls: slaDcc2s dcc2s.f
+ * slaDsep dsep.f
+ * slaDranrm dranrm.f
+ * slaEvp evp.f
+ *
+ * History: 03/10/98 E. Murphy Begin work.
+ * 03/10/98 E. Murphy Initial version working
+ * 05/31/00 peb Implemented cfortran.h calls for slalib
+ * functions.
+ * 12/18/03 bjg Change calfusettag.h to calfuse.h
+ * 07/21/04 1.4 wvd Comment out variable vect; unused.
+ *
+ * References: This routine makes use of the Starlink set of astronomical
+ * subroutines (SLALIB). More information can be found at
+ * http://star-www.rl.ac.uk.
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <math.h>
+
+#ifdef CFORTRAN
+#include "cfortran.h"
+PROTOCCALLSFSUB3(SLA_DCC2S, sla_dcc2s, DOUBLEV, PDOUBLE, PDOUBLE)
+#define slaDcc2s(V, A, B) \
+ CCALLSFSUB3(SLA_DCC2S, sla_dcc2s, DOUBLEV, PDOUBLE, PDOUBLE, V, A, B)
+PROTOCCALLSFFUN1(DOUBLE, SLA_DRANRM, sla_dranrm, DOUBLE)
+#define slaDranrm(ANGLE) \
+ CCALLSFFUN1(SLA_DRANRM, sla_dranrm, DOUBLE, ANGLE)
+PROTOCCALLSFFUN4(DOUBLE, SLA_DSEP, sla_dsep, DOUBLE, DOUBLE, DOUBLE, DOUBLE)
+#define slaDsep(A1, B1, A2, B2) \
+ CCALLSFFUN4(SLA_DSEP, sla_dsep, DOUBLE, DOUBLE, DOUBLE, DOUBLE, \
+ A1, B1, A2, B2)
+PROTOCCALLSFSUB6(SLA_EVP, sla_evp, DOUBLE, DOUBLE, DOUBLEV, DOUBLEV, DOUBLEV, \
+ DOUBLEV)
+#define slaEvp(DATE, DEQX, DVB, DPB, DVH, DPH) \
+ CCALLSFSUB6(SLA_EVP, sla_evp, DOUBLE, DOUBLE, DOUBLEV, DOUBLEV, DOUBLEV, \
+ DOUBLEV, DATE, DEQX, DVB, DPB, DVH, DPH)
+#else
+#include "slalib.h"
+#include "slamac.h"
+#endif
+
+#include "calfuse.h"
+
+double solar_ang(double mjdate, double ra, double dec)
+{
+ /* Define variables. */
+ int i;
+ /* double dvb[3], dpb[3], dvh[3], dph[3], vect[3], ra_sun, dec_sun; */
+ double dvb[3], dpb[3], dvh[3], dph[3], ra_sun, dec_sun;
+ /*
+ * Evp returns four 3-vectors containing the barycentric velocity and
+ * position (dvb,dpv) and the heliocentric velocity and position
+ * (dvh,dph) of the Earth on the date mjdate. It requires a modified
+ * Julian date as input. Output has units of AU for positions and
+ * AU/s for velocities.
+ */
+ slaEvp(mjdate, 2000.0, dvb, dpb, dvh, dph);
+
+ /* Convert the 3-vector position of the Sun into a J2000.0 RA and DEC */
+
+ for (i=0; i<3; i++) dph[i]*=-1.0;
+
+#ifdef CFORTRAN
+ slaDcc2s(dph, ra_sun, dec_sun);
+#else
+ slaDcc2s(dph, &ra_sun, &dec_sun);
+#endif
+
+ /* Compute the angular separation of RA,DEC and RA_SUN and DEC_SUN */
+
+ return slaDsep(ra*RADIAN, dec*RADIAN, slaDranrm(ra_sun), dec_sun)/RADIAN;
+}
diff --git a/src/libcf/space_vel.c b/src/libcf/space_vel.c
new file mode 100644
index 0000000..e472961
--- /dev/null
+++ b/src/libcf/space_vel.c
@@ -0,0 +1,46 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: double space_vel(double *vel, double ra, double dec)
+ *
+ * Description: Computes the projection of the spacecraft orbital velocity
+ * vector onto a unit vector in the direction ra, dec.
+ *
+ * Arguments: double *vel (km/s) 3-vector velocity of satellite
+ * double ra,dec (deg) position of target
+ *
+ * Returns: double (km/s) velocity in direction of target
+ *
+ * History: 03/04/98 E. Murphy Begin work.
+ * 03/04/98 E. Murphy Initial version working
+ * 07/07/99 E. Murphy Converted x,y,z and vx,vy,vz to pos,vel
+ * 06/15/01 V. Dixon Comment out calculation of r, not used.
+ * 12/18/03 bjg Change calfusettag.h to calfuse.h
+ * 07/21/04 1.4 wvd Delete defn of r, as it is not used.
+ * 03/04/07 1.5 wvd Rewrite program to compute dot product
+ * of target and velocity vectors.
+ *
+ ****************************************************************************/
+
+#include <math.h>
+#include "calfuse.h"
+
+double space_vel(double vel[3], double ra, double dec)
+{
+
+ double x, y, z, phi;
+
+ ra *= RADIAN;
+ dec *= RADIAN;
+
+ z = sin(dec);
+ phi = cos(dec);
+ y = phi * sin(ra);
+ x = phi * cos(ra);
+
+ return x*vel[0] + y*vel[1] + z*vel[2];
+
+}
diff --git a/src/libcf/state_geod.c b/src/libcf/state_geod.c
new file mode 100644
index 0000000..4e0c161
--- /dev/null
+++ b/src/libcf/state_geod.c
@@ -0,0 +1,107 @@
+/******************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ ******************************************************************************
+ *
+ * Synopsis: void state_geod(double pos,
+ * double mjdate, double lon, double lat)
+ *
+ * Description: Computes the geocentric longitude and latitude of FUSE
+ * from the given state 6-vector.
+ *
+ * Arguments: double pos (km) 3-vector xyz position of satellite
+ * double mjdate Modified Julian date of observation
+ *
+ * Returns: double lon, lat (deg) Geocentric longitude and latitude
+ *
+ * Calls: slaPreces preces.f
+ *
+ * History: 03/04/98 E. Murphy Begin work.
+ * 03/04/98 E. Murphy Initial version working
+ * 03/15/99 E. murphy Changed function definition to void
+ * (was double)
+ * 07/07/99 E. Murphy Changed call to use pos instead of x,y,z
+ * 05/31/00 peb Implemented cfortran.h calls for slalib
+ * functions.
+ * 12/18/03 bjg Change calfusettag.h to calfuse.h
+ * 06/17/04 bjg 1.4 Corrected cfortran call to sla_preces
+ *
+ * Ake, T. 1998 in The Scientific Impact of the Goddard
+ * High Resolution Spectrograph, ed. J. C. Brandt et al.,
+ * ASP Conference Series, in preparation.
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <math.h>
+
+#ifdef CFORTRAN
+#include "cfortran.h"
+PROTOCCALLSFSUB5(SLA_PRECES, sla_preces, STRING, DOUBLE, DOUBLE, PDOUBLE, \
+ PDOUBLE)
+#define slaPreces(SYSTEM, EP0, EP1, RA, DC) \
+ CCALLSFSUB5(SLA_PRECES, sla_preces, STRING, DOUBLE, DOUBLE, PDOUBLE, \
+ PDOUBLE, SYSTEM, EP0, EP1, RA, DC)
+#else
+#include "slalib.h"
+#include "slamac.h"
+#endif
+
+#include "calfuse.h"
+
+void state_geod(double pos[3], double mjdate,
+ double *lon, double *lat, double *gmst)
+{
+ double ra, dec, epoch2, c1, r, intmjd, frcmjd;
+ char fk5_st[10]="FK5";
+
+ /* pos[0]=x
+ pos[1]=y
+ pos[2]=z
+ */
+
+ r=sqrt(pos[0]*pos[0]+pos[1]*pos[1]+pos[2]*pos[2]);
+ ra=atan2(pos[1],pos[0]);
+ dec=asin(pos[2]/r);
+
+ /* We must precess the J2000 RA and DEC to date of observation. This
+ * is a very small correction, and could probably be ignored.
+ */
+
+ epoch2=2000.0-((51544.0-mjdate)/365.25);
+
+#ifdef CFORTRAN
+ slaPreces(fk5_st, 2000.0, epoch2, ra, dec);
+#else
+ slaPreces(fk5_st, 2000.0, epoch2, &ra, &dec);
+#endif
+
+ /* Calculate the Greenwich Mean Sidereal Time in seconds
+ * Astronomical Almanac, 1998, p. B6.
+ */
+
+ frcmjd=modf(mjdate, &intmjd);
+
+ c1=(intmjd-51544.5)/36525.0;
+
+ /* The following line gives the GMST at 0 hr UT */
+ *gmst=24110.54841 + 8640184.812866*c1+0.093104*c1*c1-6.2E-6*c1*c1*c1;
+ /* The following adds on the number of seconds since the UT above */
+ *gmst+=frcmjd*1.00273791*86400.0;
+
+ *gmst*=(2*M_PI)/86400.0;
+
+ while (*gmst < 0.0) *gmst+=2*M_PI;
+
+ while (*gmst > 2*M_PI) *gmst-=2*M_PI;
+
+ *lon=(ra-*gmst)/RADIAN;
+
+ while (*lon > 180.0) *lon-=360.0;
+
+ while (*lon < -180.0) *lon+=360.0;
+
+ *lat=dec/RADIAN;
+
+ *gmst*=24.0/(2*M_PI);
+}
diff --git a/src/libcf/state_limb.c b/src/libcf/state_limb.c
new file mode 100644
index 0000000..9aacfb6
--- /dev/null
+++ b/src/libcf/state_limb.c
@@ -0,0 +1,159 @@
+/*****************************************************************************
+ * Johns Hopkins University
+ * Center For Astrophysical Sciences
+ * FUSE
+ *****************************************************************************
+ *
+ * Synopsis: double state_limb(double pos[3], double mjdate,
+ * double ra, double dec, double *zdis,tint day_limb)
+ *
+ * Description: Computes the earth limb angle from J2000 RA and DEC given
+ * the 3-vector position of FUSE.
+ *
+ * Arguments: double pos (km) 3-vector position of satellite
+ * double mjdate (days) Modified Julian date
+ * double ra,dec (deg) J2000.0 RA and DEC
+ * double zdist (deg) zenith angle
+ * integer day_limb output : 1 if bright limb, 0 if dark limb
+ *
+ * Returns: double (deg) Earth limb angle
+ * integer day_limb will have the value 1 or 0 when
+ * returning in the main program.
+ *
+ * Calls: slaPreces preces.f
+ * slaDcc2s
+ * slaDranrm
+ * slaEvp
+ *
+ * History: 03/10/98 E. Murphy Begin work.
+ * 03/10/98 E. Murphy Initial version working
+ * 07/07/99 E. Murphy Changed call to use pos instead of x,y,z
+ * 08/26/99 E. Murphy Added zenith distance.
+ * 05/31/00 peb Implemented cfortran.h calls for slalib
+ * functions.
+ * Ake, T. 1998 in The Scientific Impact of the Goddard
+ * High Resolution Spectrograph, ed. J. C. Brandt et al.,
+ * ASP Conference Series, in preparation.
+ *
+ * 09/23/00 v1.5 ma Now this function determine bright or
+ * dark limb
+ * To do so, the procedure has a new
+ * input argument: day_limb.
+ * day_limb = 1 if bright, 0 if dark.
+ * 09/24/00 v1.6 ma Fixed a bug in the input argument
+ * 09/27/00 v1.7 ma Fixed bug in limbvec calc.
+ * 10/02/00 v1.8 jwk Add fortran wrappers for slaDcc2s,
+ * slaEvp, and slaDranrm
+ * 12/18/03 bjg 1.3 Change calfusettag.h to calfuse.h
+ * 06/17/04 bjg Corrected cfortran call to sla_preces
+ * 07/22/04 bjg 1.4 Remove unused variables
+ ****************************************************************************/
+
+#include <math.h>
+
+#ifdef CFORTRAN
+#include "cfortran.h"
+PROTOCCALLSFSUB5(SLA_PRECES, sla_preces, STRING, DOUBLE, DOUBLE, PDOUBLE, \
+ PDOUBLE)
+#define slaPreces(SYSTEM, EP0, EP1, RA, DC) \
+ CCALLSFSUB5(SLA_PRECES, sla_preces, STRING, DOUBLE, DOUBLE, PDOUBLE, \
+ PDOUBLE, SYSTEM, EP0, EP1, RA, DC)
+PROTOCCALLSFSUB3(SLA_DCC2S, sla_dcc2s, DOUBLEV, PDOUBLE, PDOUBLE)
+#define slaDcc2s(V, A, B) \
+ CCALLSFSUB3(SLA_DCC2S, sla_dcc2s, DOUBLEV, PDOUBLE, PDOUBLE, V, A, B)
+PROTOCCALLSFFUN1(DOUBLE, SLA_DRANRM, sla_dranrm, DOUBLE)
+#define slaDranrm(ANGLE) \
+ CCALLSFFUN1(SLA_DRANRM, sla_dranrm, DOUBLE, ANGLE)
+PROTOCCALLSFSUB6(SLA_EVP, sla_evp, DOUBLE, DOUBLE, DOUBLEV, DOUBLEV, DOUBLEV, \
+ DOUBLEV)
+#define slaEvp(DATE, DEQX, DVB, DPB, DVH, DPH) \
+ CCALLSFSUB6(SLA_EVP, sla_evp, DOUBLE, DOUBLE, DOUBLEV, DOUBLEV, DOUBLEV, \
+ DOUBLEV, DATE, DEQX, DVB, DPB, DVH, DPH)
+#else
+#include "slalib.h"
+#include "slamac.h"
+#endif
+
+#include "calfuse.h"
+
+double state_limb(double pos[3], double mjdate,
+ double ra, double dec, double *zdist,int *day_limb)
+{
+ double dvb[3], dpb[3], dvh[3], dph[3], limbvec[3], epoch2;
+ double ra_earth, dec_earth, r, ndist;
+ double ra_sun, dec_sun;
+ int i;
+ char fk5_st[10]="FK5";
+
+ ra*=RADIAN;
+ dec*=RADIAN;
+
+ r=sqrt(pos[0]*pos[0]+pos[1]*pos[1]+pos[2]*pos[2]);
+ ra_earth=atan2(-pos[1],-pos[0]);
+ dec_earth=asin(-pos[2]/r);
+ /*
+ * We must precess the J2000 RA and DEC to date of observation. This
+ * is a very small correction, and could probably be ignored.
+ */
+ epoch2=2000.0-((51544.0-mjdate)/365.25);
+
+#ifdef CFORTRAN
+ slaPreces(fk5_st, 2000.0, epoch2, ra_earth, dec_earth);
+#else
+ slaPreces(fk5_st, 2000.0, epoch2, &ra_earth, &dec_earth);
+#endif
+
+ /* Now we need the position of the sun (same code as in eclipse.c) */
+
+ slaEvp(mjdate, 2000.0, dvb, dpb, dvh, dph);
+
+ /* Convert the 3-vector position of the Sun into a J2000.0 RA and DEC */
+
+ for (i=0; i<3; i++) dph[i]*=-1.0;
+
+
+#ifdef CFORTRAN
+ slaDcc2s(dph, ra_sun, dec_sun);
+#else
+ slaDcc2s(dph, &ra_sun, &dec_sun);
+#endif
+
+ ra_sun=slaDranrm(ra_sun);
+
+ /* We must precess the J2000 RA and DEC to date of observation. This
+ * is a very small correction, and could probably be ignored.
+ */
+
+ epoch2=2000.0-((51544.0-mjdate)/365.25);
+
+#ifdef CFORTRAN
+ slaPreces(fk5_st, 2000.0, epoch2, ra_sun, dec_sun);
+#else
+ slaPreces(fk5_st, 2000.0, epoch2, &ra_sun, &dec_sun);
+#endif
+
+ /* ndist is the angular distance (RADIAN) from the nadir to the target */
+ ndist = (acos(sin(dec)*sin(dec_earth)+
+ cos(dec)*cos(dec_earth)*cos(ra-ra_earth)));
+
+ /* Now we want to calculate the limb vector (originating at the center
+ of Earth and crossing the target-FUSE line perpendicularily)*/
+
+ limbvec[2]=pos[2]+r*cos(ndist)*sin(dec);
+ limbvec[1]=pos[1]+r*cos(ndist)*cos(dec)*sin(ra);
+ limbvec[0]=pos[0]+r*cos(ndist)*cos(dec)*cos(ra);
+
+ /* Now we do the scalar product with the sun vector; then
+ if the result is <0 the limb is in the day zone (less
+ than 90 degrees between sun vector and limb vector)*/
+
+ if ((limbvec[0]*cos(dec_sun)*cos(ra_sun) +
+ limbvec[1]*cos(dec_sun)*sin(ra_sun)+limbvec[2]*sin(dec_sun)) >0) {
+ *day_limb=1;
+ } else {
+ *day_limb=0;
+ }
+ *zdist = 180.0 - ndist/RADIAN;
+
+ return ndist/RADIAN-(asin(RE/r)/RADIAN);
+}