package Subs::BATDB;
##############################################################################
#
# DESCRIPTION:
#
# HISTORY: $Log: BATDB.pm,v $
# HISTORY: Revision 1.10 2007/02/10 22:28:33 apsop
# HISTORY: Set orig obs to seq obs for tdrss failed triggers.
# HISTORY:
# HISTORY: Revision 1.9 2007/02/08 14:31:34 apsop
# HISTORY: Fix bug in calculation of original observation id, segment was being set to wrong value.
# HISTORY:
# HISTORY: Revision 1.8 2007/01/31 20:46:10 apsop
# HISTORY: Add entries for the failed trigger event data.
# HISTORY:
# HISTORY: Revision 1.7 2006/08/02 19:46:00 apsop
# HISTORY: If aspect correction fails for individual snapshots, change from level 2 to level 1 error.
# HISTORY:
# HISTORY: Revision 1.6 2005/12/22 18:03:38 apsop
# HISTORY: Give module a version number to make the install scripts happy.
# HISTORY:
# HISTORY: Revision 1.5 2005/11/14 20:21:34 apsop
# HISTORY: Wrote out map built-in in long-hand in response to inexplicable behavior.
# HISTORY: Possible perl bug(?)
# HISTORY:
# HISTORY: Revision 1.4 2005/11/08 17:07:59 apsop
# HISTORY: Use Filename calls to get bat db file names. Use caldb for alignfile in prefilter. Set RATE_CODE to INDEF for non-rate modes. Clean up temp $gtifile.
# HISTORY:
# HISTORY: Revision 1.3 2005/08/30 13:55:51 apsop
# HISTORY: Change alignfile param to make in compatible with old version of aspect. Change rate_code column to ratecode.
# HISTORY:
# HISTORY: Revision 1.2 2005/08/16 22:23:33 apsop
# HISTORY: Propagate original/true target, segment and observation numbers from
# HISTORY: Rich's database HK file instead of taking from job.par.
# HISTORY:
# HISTORY: Revision 1.1 2005/08/16 21:36:37 apsop
# HISTORY: Module for creating HEASARC BAT exposure database file.
# HISTORY:
#
# VERSION: 0.0
#
##############################################################################
use Subs::Sub;
@ISA = ("Subs::Sub");
use strict;
use Astro::FITS::CFITSIO qw(:constants);
use Util::HEAdas;
use Util::FITStable;
use Util::CoreTags;
sub new
{
my $proto=shift;
my $self=$proto->SUPER::new();
$self->{DESCRIPTION} = 'Update BAT exposure database';
return $self;
}
##################
# METHODS:
##################
sub body
{
my $self=shift;
my $log = $self->log();
my $filename = $self->filename();
my $procpar = $self->procpar();
my $jobpar = $self->jobpar();
# set up the database columns
my @db = (
{ name => 'name', # source name
type => '80A',
comment => 'Designation of the Pointed Source',
# $jobpar->read('object')
},
{ name => 'orig_target_id', # Target_id
type => '1J',
null => -1,
disp => 'I8',
comment => 'Trigger Number as Originally Assigned',
# $jobpar->read('target')
},
{ name => 'target_id', # True target_id
type => '1J',
null => -1,
disp => 'I8',
comment => 'Unique Trigger Number with Any Degeneracy Removed',
# substr(0, 8, $jobpar->read('sequence'))
},
{ name => 'ra', # R.A.
type => '1E',
unit => 'deg',
disp => 'F10.5',
comment => 'Right Ascension (Pointing Position)',
# $jobpar->read('ra')
},
{ name => 'dec', # Dec.
type => '1e',
unit => 'deg',
disp => 'F10.5',
comment => 'Declination (Pointing Position)',
# $jobpar->read('dec')
},
{ name => 'roll_angle', # Roll
type => '1E',
unit => 'deg',
disp => 'F10.5',
comment => 'Roll Angle (degree)',
# $jobpar->read('roll')
},
{ name => 'start_time', # Start time
type => '24A',
comment => 'Start Time of the Observation',
},
{ name => 'stop_time', # Stop time
type => '24A',
comment => 'Stop Time of the Observation',
},
{ name => 'orig_obs_segment', # Obs seg
type => '1J',
disp => 'I3',
null => -1,
comment => 'Observation Segment as Originally Assigned',
# $jobpar->read('obs')
},
{ name => 'obs_segment', # True Obs seg
type => '1J',
disp => 'I3',
null => -1,
comment => 'True Observation Segment (Corrected Value)',
# substr(8, $jobpar->read('sequence'))
},
{ name => 'orig_obsid', # Obs number
type => '11A',
comment => 'Observation Number as Originally Assigned (OrigTarget_ID + Orig_Obs_Segment)',
# $jobpar->read('target') . $jobpar->read('obs');
},
{ name => 'obsid', # True Obs num
type => '11A',
comment => 'Unique Observation Number (Target_ID + Obs_Segment)',
# $jobpar->read('sequence')
},
{ name => 'exposure',
type => '1E',
comment => 'Total time in seconds for this record',
},
{ name => 'ratecode', # ratecode
type => '4A',
comment => 'Flag for rate modes.',
},
{ name => 'catnum', # Catnum
type => '1J',
disp => 'I8',
comment => 'Target_Id number in the BAT catalog (Mask Tag & Pulsar mode)',
},
{ name => 'operation_mode', # Observing mode
type => '80A',
comment => 'Indicates Operating Mode of Instrument',
# from DATAMODE
},
{ name => 'pointing_mode', # Spacecraft obs mode
type => '80A',
comment => 'Indicates Pointing Mode of Spacecraft',
# from OBS_MODE
},
{ name => 'filename', # Filename
type => '80A',
comment => 'Name of the File Containing the Data for this Interval',
},
);
my $attfile = $filename->get('attitude', 'swift');
if (not $attfile) {
$log->error(2, 'Unable to create BAT exposure database without attitude');
return;
}
my $dbfile = $filename->get('badb', 'proc', '', 0);
# print "BAT exposure database will be named $dbfile\n";
my $hkfile = $filename->get('hk', 'proc', 'badb', 0);
unless (-f $hkfile) {
$log->entry("Unable to locate BAT exposure database housekeeping file $hkfile");
return;
}
my $batdb = Util::SimpleFITS->readonly($hkfile);
my $status = $batdb->status;
if (not $batdb or $status) {
$log->error(2, "unable to open BAT catalog $hkfile [$status]");
return;
}
$batdb->move(2);
if ($batdb->status) {
$log->error(2, 'unable to move to $hkfile database extension');
return;
}
# slurp the BAT2FITS version
my @records;
$status = $batdb
->loadTable(\@records)
->status;
if ($status) {
$log->error(2, 'unable to read data');
return;
}
my $db = Util::FITStable->new(\@db,
log => $log,
which => 'bat',
);
################################################
# Add entries for the failed trigger event data
################################################
foreach my $tdfile ($filename->get('tdunfilter', 'bat', '*', '*')){
my %new_rec;
my $tdfits = Util::FITSfile->new($tdfile, 0);
$new_rec{CATNUM} = 0;
$new_rec{SOURCE_NAME} = 'Failed Trigger';
$new_rec{RA} = 'INDEF';
$new_rec{DEC} = 'INDEF';
$new_rec{ROLL_ANGLE} = 'INDEF';
$new_rec{ORIG_TARGET_ID} = $jobpar->read('target');
$new_rec{TARGET_ID} = $tdfits->keyword('TARG_ID');
$new_rec{ORIG_OBS_SEGMENT} = $jobpar->read('obs');
$new_rec{OBS_SEGMENT} = $tdfits->keyword('SEG_NUM');
$new_rec{OPERATION_MODE} = 'Event';
$new_rec{POINTING_MODE} = 'SLEW_POINTING';
$new_rec{FILENAME} = $tdfile;
$tdfits->ext(1);
$new_rec{EXP_START} = $tdfits->keyword('TSTART');
$new_rec{EXP_STOP} = $tdfits->keyword('TSTOP');
$new_rec{START_TIME} = $new_rec{EXP_START};
$new_rec{STOP_TIME} = $new_rec{EXP_STOP};
$new_rec{INTEGRATION_TIME} = $tdfits->keyword('EXPOSURE');
$new_rec{INTERVAL} = $tdfits->keyword('TELAPSE');
push @records, \%new_rec;
}
###################################################
# Compile the data needed for the BAT database file
###################################################
my $rows = @records;
my @indef = ('INDEF') x $rows;
$db->{rows} = $rows;
# observation values
my $object = $jobpar->read('object');
# this is what the pipeline writes for RA_PNT, DEC_PNT...
my $nomra = $jobpar->read('ra');
my $nomdec = $jobpar->read('dec');
foreach my $e (@records) {
if ($e->{CATNUM} == 0) {
$e->{SOURCE_NAME} = $object;
$e->{RA} = $nomra;
$e->{DEC} = $nomdec;
}
# if($e->{POINTING_MODE} eq 'SLEW'){
# $e->{RA} = 'INDEF';
# $e->{DEC} = 'INDEF';
# $e->{ROLL_ANGLE} = 'INDEF';
# }
}
my @object = map { "'$_->{SOURCE_NAME}'" } @records;
$db->set(name => \@object);
$db->set(orig_target_id => [ map { $_->{ORIG_TARGET_ID} } @records ]);
$db->set(target_id => [ map { $_->{TARGET_ID} } @records ]);
# find mean roll for each snapshot
my $aspect = Util::HEAdas->new('aspect')
->params({
attfile => $attfile,
alignfile => 'CALDB'
});
my $solved;
my @gtidb = (
{ name => 'START',
type => '1D',
},
{ name => 'STOP',
type => '1D',
},
);
my $gtifile = 'aspect.gti';
foreach my $e (@records) {
if (defined($solved) and $solved->{INTERVAL} == $e->{INTERVAL}) {
$e->{ROLL_ANGLE} = $solved->{ROLL_ANGLE};
# if($e->{POINTING_MODE} eq 'SLEW'){
# $e->{RA} = $solved->{RA};
# $e->{DEC} = $solved->{DEC};
# }
next;
}
my $gti = Util::FITStable->new(\@gtidb,
log => $log,
which => 'GTI',
);
$gti->set(START => [ $e->{EXP_START} ]);
$gti->set(STOP => [ $e->{EXP_STOP} ]);
unlink($gtifile);
$gti->write($gtifile);
$aspect->params({ gtis => $gtifile });
$aspect->seriousness(1);
$aspect->run();
if ($aspect->had_error) {
$log->error([ 1, ASPECT_FAILED ],
'unable to determine mean roll for snapshot');
}
else {
$e->{ROLL_ANGLE} = $aspect->parfile()->read('roll');
# if($e->{POINTING_MODE} eq 'SLEW'){
# $e->{RA} = $aspect->parfile()->read('ra');
# $e->{DEC} = $aspect->parfile()->read('dec');
# }
$solved = $e;
# print "roll from $e->{EXP_START} to $e->{EXP_STOP} is $e->{ROLL_ANGLE}\n";
}
}
unlink($gtifile);
my @ra = map { $_->{RA} } @records;
my @dec = map { $_->{DEC} } @records;
my @roll = map { $_->{ROLL_ANGLE} } @records;
##################################################
# Set rate code to 'INDEF' if not in a Rates mode
##################################################
foreach my $rec (@records){
$rec->{RATE_CODE} = 'INDEF' unless $rec->{OPERATION_MODE} =~ /Rates/;
}
####################################################################
# Early observations (before PPSTs) had multiple pointings, so set
# pointing to indef
####################################################################
if( $jobpar->read('tstart') > 124383600 ){
$db->set(ra => \@ra);
$db->set(dec => \@dec);
$db->set(roll_angle => \@roll);
}
else{
$db->set(ra => \@indef);
$db->set(dec => \@indef);
$db->set(roll_angle => \@indef);
}
# my @start = map { $db->timeString($_->{START_TIME}) } @records;
# my @stop = map { $db->timeString($_->{STOP_TIME}) } @records;
my @start;
foreach my $e (@records) {
push(@start, $db->timeString($e->{START_TIME}));
}
my @stop;
foreach my $e (@records) {
push(@stop, $db->timeString($e->{STOP_TIME}));
}
$db->set(start_time => \@start);
$db->set(stop_time => \@stop);
$db->set(orig_obs_segment => [ map { $_->{ORIG_OBS_SEGMENT} } @records]);
$db->set(obs_segment => [ map { $_->{OBS_SEGMENT} } @records]);
$db->set(orig_obsid => [ map { makeSequence($_) } @records ]);
$db->set(obsid => [ map { makeSequence($_, 1) } @records ]);
my @exposure = map { $_->{INTEGRATION_TIME} } @records;
$db->set(exposure => \@exposure);
my @catnum = map { $_->{CATNUM} } @records;
$db->set(catnum => \@catnum);
my @obs_mode = map { $_->{OPERATION_MODE} } @records;
$db->set(operation_mode => \@obs_mode);
my @rate_code = map { $_->{RATE_CODE} } @records;
$db->set(ratecode => \@rate_code);
my @pointing_mode = map { $_->{POINTING_MODE} } @records;
$db->set(pointing_mode => \@pointing_mode);
my @filename = map { $_->{FILENAME} } @records;
$db->set(filename => \@filename);
$db->write($dbfile);
} # end of body method
sub makeSequence
{
my ($href, $true) = @_;
my $key1 = $true ? 'TARGET_ID' : 'ORIG_TARGET_ID';
my $key2 = $true ? 'OBS_SEGMENT' : 'ORIG_OBS_SEGMENT';
sprintf('%08d%03d', $_->{$key1}, $_->{$key2});
}