Subs::UvotAttitude (version $)


package Subs::UvotAttitude;
##############################################################################
#
# DESCRIPTION: Generates UVOT Attitude file
# DESCRIPTION: The creation of such attitude file will take place
# DESCRIPTION: IF and ONLY IF the sequence is "FINAL FOR ARCHIVE"
#
# HISTORY: $Log: UvotAttitude.pm,v $
# HISTORY: Revision 1.6  2014/03/28 08:57:26  apsop
# HISTORY: Change chatter in uvotimage and uvotskycorr from 5 to 3.
# HISTORY:
# HISTORY: Revision 1.5  2014/02/28 11:31:29  apsop
# HISTORY: Check for final in jobpar object rather than job_title parameter.
# HISTORY:
# HISTORY: Revision 1.4  2014/02/28 11:23:28  apsop
# HISTORY: New sub get_attfile_name to pick correct attitude file.
# HISTORY: Informative comment string for the ATTFLAG keyword.
# HISTORY: Ensure ATTFLAG keyword is a string.
# HISTORY:
# HISTORY: Revision 1.3  2013/07/16 07:15:45  apsop
# HISTORY: Don't include grism images when calling uvotskycorr,
# HISTORY: by getting the filenames by calling getNonGrismSkyImages.
# HISTORY:
# HISTORY: Revision 1.2  2012/01/12 06:52:03  apsop
# HISTORY: Changes going to proc3.15.03
# HISTORY:
# HISTORY: 2011-12-05 Jeff Guerber: Call uvotmodmap ($umodmap) with ncell=16
# HISTORY:   instead of =2 (supposedly uvotmodmap has been fixed so its speed
# HISTORY:   is no longer an issue).
# HISTORY:
# HISTORY: 2011-11-10 Jeff Guerber as apsop
# HISTORY:   Added refattopt and alignfile params to uvotexpmap call.
# HISTORY:   Read refattopt value from $procpar (sw0.par).
# HISTORY:
# HISTORY: 2011-11-03 Jeff Guerber as apsop
# HISTORY:   Added refattopt to parameter lists for swiftxform and uvotimage,
# HISTORY:   and alignfile for swiftxform.
# HISTORY:
#
# VERSION: $Revision: 1.6 $
#
##############################################################################


use Subs::Sub;
use Subs::Images;
use Util::Xanadu;
use Subs::UvotNames;

@ISA = ("Subs::Images");
use strict;

sub new {
    my $proto=shift;
    my $self=$proto->SUPER::new();

    $self->{DESCRIPTION}="Extracting attitude for UVOT";

    return $self;
}

##################
# METHODS:
##################

sub body {
    my $self=shift;
    my $log     =$self->log();
    my $filename=$self->filename();

    my $jobpar  = $self->jobpar();
    my $jobtitle = $jobpar->read('job_title');


    if ( ! $jobpar->{TIMELIST}->{final} ){
      $log->entry("Process skipped since sequence is not \"FINAL FOR ARCHIVE\"");
      return;
    }

    my $catfile = $filename->get('hk', 'uvot', 'ct', '*');
    unless( -f $catfile ){
      $log->entry("No uvot catalogue file indicates no uvot data");
      return;
    }

    $self->create_images();

    $self->image_raw_to_det();

    $self->create_exposure_maps();

    $self->aspect_correct_sky_images();


} # end of body method


#############################################################################
# Extract images from filtered event data and raw images files
#############################################################################
sub create_images
{
	my $self = shift;

	my $log     =$self->log();
	my $filename=$self->filename();
	my $procpar =$self->procpar();
	my $jobpar  =$self->jobpar();

	$log->entry('Creating images from UVOT event and image mode data');

	my $attfile = $self->get_attfile_name();
	if ($attfile eq 'NONE') {
	  $log->error(2, 'no attitude file available, exiting');
	  return;
	}

	my @eventFiles = $filename->get('unfiltered', 'uvot', '*', '*');
	@eventFiles = grep !/[0-9]ubl/, @eventFiles;
	my @imageFiles = $filename->get('rawimage', 'uvot', '*', '*');
	@imageFiles = grep !/[0-9]ubl/, @imageFiles;
	@imageFiles = grep !/[0-9]udi/, @imageFiles;


	unless( @eventFiles || @imageFiles ){
	  $log->error(1, 'No UVOT image or event files to process.');
	  return;
	}

	my $catfile = $filename->get('hk', 'uvot', 'ct', '*');
	if (not -f $catfile) {
	  $log->entry('No UVOT exposure catalogue to update');
	}else {
	  $log->entry('UVOT exposure catalogue update not implemented');
	}

	my $prefix = 'Qz3'; # a unique string

	my $tool = Util::HEAdas->new('uvotimage')->is_script(1);
	$tool->params({
		       prefix => $prefix,
		       attfile => $attfile,
		       teldeffile => 'CALDB',
		       alignfile => 'CALDB',
		       ra => $jobpar->read('ra'),
		       dec => $jobpar->read('dec'),
		       roll => $jobpar->read('roll'),
		       flatfield => 'no',
		       mod8corr => 'no',
		       refattopt => $procpar->read('refattopt'),
		       # badpix => 'no',
		       # catfile => $catfile,
		       chatter => 3
		      });



	$tool->params({mod8corr => 'yes'})
	  if $jobpar->{TIMELIST}->{final};

	my $swobsid = $filename->sequence_specific;

	my @dataFiles = (@eventFiles, @imageFiles);
	while( @dataFiles ){
	  my $type = $dataFiles[0] =~ /\.evt/ ? 'unfiltered' : 'rawimage';
	  my ($mode, $index) = ($filename->parse($dataFiles[0], $type))[1,2];
	  $mode = substr($mode, 0, 2);
	  my @infiles = ( $filename->get('rawimage', 'uvot', "$mode*", $index),
			  $filename->get('unfiltered', 'uvot', "$mode*", $index) );

	  my $expr = '('. join('|',@infiles) .')';
	  @dataFiles = grep !/${expr}/, @dataFiles;


	  $tool->params({infile => join(',', @infiles)})->run();

	  # move each file ${prefix}xxx to sw<obsid>xxx
	  foreach my $name (glob($prefix . '*')) {
	    my $xxx = substr($name, length($prefix));
	    my $fixed = $swobsid . $xxx;
	    if($index!=0){
	      $index = sprintf('%02d', $index) if length($index)<2;
	      $fixed =~ s/\./_${index}./;
	    }
	    if (-f $fixed) {
	      unlink($fixed);
	    }
	    rename($name, $fixed);
	  }
	}

} # end create images method



###############################################################################
# convert raw coordinate images to detector coordinates
###############################################################################
sub image_raw_to_det
{
    my $self=shift;

    my $log     =$self->log();
    my $filename=$self->filename();
    my $procpar =$self->procpar();
    my $jobpar  =$self->jobpar();

    $log->entry("Converting raw grism images to detector coordinates");

    my $attfile = $self->get_attfile_name();

    $log->entry("Processing raw uvot images");

    my $ubadpix = Util::HEAdas->new('uvotbadpix');

    my $umodmap = Util::HEAdas->new('uvotmodmap');

    my $uflatfield = Util::HEAdas->new('uvotflatfield');
    $uflatfield->params({flatfile => 'CALDB'});

    $log->entry("setting {RA,DEC,PA}_PNT in all raw images");
    my $ra = $jobpar->read("ra");
    my $dec = $jobpar->read("dec");
    my $roll = $jobpar->read("roll");


    #########################################
    # create the swiftxform tool
    #########################################
    my $swiftxform = Util::HEAdas->new('swiftxform');

    $swiftxform->params({
			 teldeffile => 'CALDB',
			 alignfile  => 'CALDB',
			 to         => 'DET',
			 attfile    => $attfile,
			 ra         => $jobpar->read('ra'),
			 dec        => $jobpar->read('dec'),
			 roll       => $jobpar->read('roll'),
			 aberration => 'no',
			 seed       => $procpar->read('seed'),
			 refattopt  => $procpar->read('refattopt'),
			 chatter    => 4,
			 clobber    => 'yes',
			})
                ->is_script(1);

    ######################
    # loop over _GRISM_ files
    #   since only they are converted to DET coordinates
    ######################
    foreach my $rawFile ($filename->get('rawimage', 'uvot', 'g*', '*') ) {


      {
 	my $imageFits = Util::FITSfile->new($rawFile);
	for my $i (1 .. $imageFits->nhdus - 1) {
	  $imageFits->ext($i);
	  $imageFits->keyword('RA_PNT', $ra);
	  $imageFits->keyword('DEC_PNT', $dec);
	  $imageFits->keyword('PA_PNT', $roll);
 	}
      }

      my $badFile = $filename->corresponding('rawimage', 'badimage', $rawFile);
      my $corrFile = $filename->corresponding('rawimage', 'corrimage', $rawFile);
      my $mod8File = 'mod.tmp';


      $log->entry("running uvotbadpix on $rawFile");
      $ubadpix->params({infile => $rawFile,
                        badpixlist => 'CALDB',
			outfile => $badFile,
			clobber => 'yes',
			chatter => 3})
	      ->run();

      $log->entry("running uvotmodmap on $rawFile");
      $umodmap->params({infile => $rawFile,
                        badpixfile => $badFile,
			outfile => $mod8File,
			nsig => 3,
			clobber => 'yes',
			ncell => 16,
			chatter => 3})
              ->run();

      # uflatfield will go here
      $uflatfield->params({
            infile => $mod8File,
	    clobber    => 'yes',
	    infile => $rawFile,
            outfile => $corrFile})
            ->run();

#      unlink($mod8File);








      #########################################
      # determine the DET coordinate filename
      #########################################
      my $detFile = $filename->corresponding("rawimage", "detimage", $rawFile);
      $log->entry("converting $rawFile to $detFile");


      ######################
      # do the conversion
      ######################
      $swiftxform->params({
            infile   => $mod8File,
            outfile  => $detFile,
            })
         ->run();

      unlink($mod8File);

    } # end of loop over files

} # end of image_raw_to_det method


###############################################################################
# generate exposure maps
###############################################################################
sub create_exposure_maps {
    my $self=shift;

    my $log     = $self->log();
    my $filename= $self->filename();
    my $procpar = $self->procpar();
    my $jobpar  = $self->jobpar();

    $log->entry("Creating exposure maps");


    ######################################################
    # make sure there is an attitude file
    ######################################################
    my $attitude = $self->get_attfile_name();
    if ($attitude eq 'NONE') {
        $log->entry("No attitude data available - can't make exposure maps");
        return;
    }

    #########################################
    # create the uvotexpmap tool
    #########################################
    ##					attdelta   => 100,
    my $uexpmap = Util::HEAdas->new('uvotexpmap')
                              ->params({attfile    => $attitude,
				     teldeffile => 'CALDB',
				     alignfile  => 'CALDB',
				     method     => 'SHIFTADD',
				     attdelta   => 0.1,
				     aberration => 'no',
				     refattopt  => $procpar->read('refattopt'),
				     chatter    => 4});

    my $aspect_follow_file = $filename->get('hk', 'uvot', 'af', 0);
    if( -f $aspect_follow_file){
      $uexpmap->params({trackfile => $aspect_follow_file});
    }else{
      $uexpmap->params({trackfile => 'NONE'});
    }

    ######################
    # loop over files
    ######################
    foreach my $skyFile ($filename->get('filterimg', 'uvot', '*', '*') ) {

      # exclude grism files
      next if $filename->is_grism($skyFile, 'skyimage');

      #########################################
      # determine the exposure map filename
      #########################################
      my ($expFile, $maskFile) = ($skyFile) x 2;
      $expFile =~ s/_sk([\._])/_ex$1/;
      $maskFile =~ s/_sk([\._])/_mk$1/;
      $log->entry("building exposure map $expFile for $skyFile");


      my $badFile = $skyFile;
      $badFile =~ s/_sk([\._])/_bp$1/;
      if (not -f $badFile) {
         $log->entry("no bad pixel file for $skyFile");
         $badFile = 'NONE';
      }


      #######################
      # make the exposure map
      #######################
      $uexpmap->params({
            infile => $skyFile,
            badpixfile => $badFile,
            outfile => $expFile,
	    maskfile => $maskFile
            })
         ->is_script(1)
         ->run();


    } # end of loop over files

} # end of create_exposure_maps method



sub aspect_correct_sky_images
{
	my $self = shift;

	my $log     =$self->log();
	my $filename=$self->filename();
	my $procpar =$self->procpar();
	my $jobpar  =$self->jobpar();
	$log->entry('Performing aspect correction on UVOT images');

	my $attfile = $self->get_attfile_name();
	if ($attfile eq 'NONE') {
	    $log->error(2, 'no attitude file available');
	    return;
	}

	my @skyFiles = $filename->getNonGrismSkyImages;
	if (not @skyFiles) {
		$log->entry("no sky images to correct");
		return;
	}

	my $infile = 'uvotskycorr.files';
	my $fh = FileHandle->new($infile, 'w');
	if (not $fh) {
		$log->error(2, "unable to create $infile [$!]");
		return;
	}

	foreach my $name (@skyFiles) {
		$fh->print($name . "\n");
	}

	$fh->close;

	my $corrfile = $filename->get('hk', 'u', 'ac', 0);
	my $catspec = $procpar->read('starcatalog');

	unlink($corrfile);

	$log->entry('finding corrections');
	my $find = Util::HEAdas->new('uvotskycorr')->is_script(1);
	$find->params({
				skyfile => '@' . $infile,
				what => 'ID',
				outfile => $corrfile,
				corrfile => 'NONE',
				attfile => $attfile,
				catspec => $catspec,
				starid => 'n.reference=50 n.observation=30 max.rate=1000',
				chatter => 3
			})
			->run;

	unlink $infile;
	return if not -f $corrfile;

	$log->entry('applying corrections');

	$fh = FileHandle->new($infile, 'w');
	if (not $fh) {
	  $log->error(2, "unable to create $infile [$!]");
	  return;
	}

	foreach my $name (@skyFiles) {
	  $fh->print($name . "\n") if -f $name;
	  $name =~ s/_sk([\._])/_ex$1/;
	  $fh->print($name . "\n") if -f $name;
	}

	$fh->close;

	my $apply = Util::HEAdas->new('uvotskycorr')->is_script(1);
	$apply->params({
				skyfile => '@' . $infile,
				what => 'SKY',
				outfile => 'NONE',
				corrfile => $corrfile,
				attfile => $attfile,
				catspec => $catspec,
				chatter => 3
			})
			->run;

	#######################################################################
	# Apply aspect corrections to attitude file.  First check if there are
	# any corrections to apply.
	#######################################################################
	my $corrfits = Util::FITSfile->new($corrfile)->cols('ASPCORR');
	my @corrections = $corrfits->table();
	if( grep(/1/, @corrections) ){
	  $log->entry('applying corrections to attitude file');

	  my $scatt = $filename->get('attitude', 's');
	  my $jumpatt = $filename->get('attcorr', 'p');
  	  my $uvotatt = $filename->get('attcorr', 'u');

#	  print "About to make Attitude file $uvotatt\n";

	  my $att_corr = Util::HEAdas->new('uvotattcorr')->is_script(1);
	  $att_corr->params({attfile => $attfile,
			     corrfile => $corrfile,
			     clobber  => 'yes',
			     outfile => $uvotatt})
	            ->run();



	  my $sattfits = Util::FITSfile->new($scatt, 0);
	  my $attstatus = $sattfits->keyword('ATTSTATU');
	  if ( $att_corr->had_error() ) {
	      $attstatus += 2;
	      unlink $uvotatt;
	  } else {
	      # Sets new uat's ATTFLAG to 111, or to 101 if not jump
	      # corrected (pat file does NOT exist), in the 1st 3 HDUs.
	      $attstatus += 1;
	      my $flags = '111';
	      $flags = '101' unless -f $jumpatt;
	      my $uattfits = Util::FITSfile->new($uvotatt);
	      for (my $i=0; $i<3; $i++) {
		  $uattfits->ext($i);
		  $uattfits->keyword('ATTFLAG', "'$flags'",
			    'Attitude corrections: 101=uvot, 111=jump+uvot');
	      }
#	    my $natt = $uvotatt.'_1stcor';
#	    system("cp $uvotatt $natt");
	  }
	  $sattfits->keyword('ATTSTATU', $attstatus, 'Status of corrected attitude files');
	}

} # end of aspect_correct_sky_images method


#########################################################################
# get_attfile_name: Returns the name of the attitude file to use (pat or
# sat), with logging.  Returns 'NONE' if couldn't find either.
#########################################################################
sub get_attfile_name {
    my $self     = shift;
    my $log      = $self->log();
    my $filename = $self->filename();

    # First try pat file
    my $attfile = $filename->get('attcorr', 'p');
    if (-e $attfile) {
	$log->entry("Got pat attitude file $attfile");

    } else {

	# No pat, is there a sat?
	$log->entry("$attfile pat attitude file does not exist, trying sat file");
	$attfile = $filename->get('attitude', 's');

	if (-e $attfile) {
	    $log->entry("Got sat attitude file $attfile");
	} else {
	    # No pat or sat, return NONE
	    $log->entry("Unable to find attitude file");
	    $attfile="NONE";
	}
    }

    return $attfile;

} # end of get_attfile_name method


1;