#! /bin/sh
# This is the LHEA perl script: /cvmfs/extras-fp7.egi.eu/extras/heasoft/hitomi/x86_64-unknown-linux-gnu-libc2.19-0/bin/hxisgdshield.pl
# The purpose of this special block is to make this script work with
# the user's local perl, regardless of where that perl is installed.
# The variable LHEAPERL is set by the initialization script to
# point to the local perl installation.
#-------------------------------------------------------------------------------
eval '
if [ "x$LHEAPERL" = x ]; then
  echo "Please run standard LHEA initialization before attempting to run /cvmfs/extras-fp7.egi.eu/extras/heasoft/hitomi/x86_64-unknown-linux-gnu-libc2.19-0/bin/hxisgdshield.pl."
  exit 3
elif [ "$LHEAPERL" = noperl ]; then
  echo "During LHEA initialization, no acceptable version of Perl was found."
  echo "Cannot execute script /cvmfs/extras-fp7.egi.eu/extras/heasoft/hitomi/x86_64-unknown-linux-gnu-libc2.19-0/bin/hxisgdshield.pl."
  exit 3
elif [ `$LHEAPERL -v < /dev/null 2> /dev/null | grep -ic "perl"` -eq 0 ]; then
  echo "LHEAPERL variable does not point to a usable perl."
  exit 3
else
  # Force Perl into 32-bit mode (to match the binaries) if necessary:
  if [ "x$HD_BUILD_ARCH_32_BIT" = xyes ]; then
    if [ `$LHEAPERL -V 2> /dev/null | grep -ic "USE_64_BIT"` -ne 0 ]; then
      VERSIONER_PERL_PREFER_32_BIT=yes
      export VERSIONER_PERL_PREFER_32_BIT
    fi
  fi
  exec $LHEAPERL -x $0 ${1+"$@"}
fi
'
if(0);
# Do not delete anything above this comment from an installed LHEA script!
#-------------------------------------------------------------------------------
#! /usr/bin/perl
#-------------------------------------------------------------------------------
# File name: hxisgdshield.pl
#
# Task name: hxisgdshield.pl
#
# Description: compute light curves, scans, or spectra from HXI/SGD shield data.
#
# Author/Date: James Peachey NASA GSFC
#
# History: for full history, see CVS change log at bottom of file.
#   Initially, code was developed from trf_hxisgdshield_15-05-06.docx.
#   Then algorithm was made faster by making COUNT a vector column and
#   thus allowing counts for all energies to be computed in a single pass.
#   This was then fed back into trf_hxisgdshield_15-06-17.docx.
#-------------------------------------------------------------------------------

# Set up.
use strict;
use warnings;

use ahapp;
use ahgen;
use ahlog;

# Turn on AUTOFLUSH
$| = 1;

# Script globals.
#-------------------------------------------------------------------------------
# For ease of comparison to TRF, which uses the construction par.infile, use
# a hash for all parameters. Also this makes it clear in the code which
# variables come from parameters.
our %par; # Hash of all the user (Ape) parameters.
our $tstart; # Beginning of time range being processed.
our $tstop; # End of time range being processed.
our $emin; # Minimum energy.
our $emax; # Maximum energy.
our $eminlimit = 0; # Lower limit on energy.
our $emaxlimit = 0; # Upper limit on energy.
our $instrument; # Name of instrument, from input file.
our @column; # Columns to bin.
# SPEC and SCAN parameters.
our $exposure; # Exposure time = diff between user-supplied TSTART and TSTOP.
# SPEC-only parameters.
our $dec_emin; # Minimum energy for deconvolved spectrum.
our $dec_emax; # Maximum energy for deconvolved spectrum.
our $offset; # Location (channel) of data in the output spectrum.
our @bound; # Location (channel) where binning changes in the input spectrum.
our @bin_factor; # Binning factor for each range given by the @bound array.
our $shieldname; # Name of the shield "instrument" used in output PHA file.
#-------------------------------------------------------------------------------

#-------------------------------------------------------------------------------
# Main function execution. Call the top-level (main) subroutine.
#-------------------------------------------------------------------------------
exit &hxisgdshield();

sub hxisgdshield {
  #-------------------------------------------------------------------------------
  # Main script. Follow standard form for a tool.
  #-------------------------------------------------------------------------------
  my $status = &startup();

  if (!$status) {
    # Get user input parameters.
    $status = &getpar();
  }

  if (!$status) {
    # Perform initializations needed for the tool to run.
    $status = &initialize();
  }

  if (!$status) {
    # Do the main work of the tool (create the output LC/SPEC/SCAN product.
    $status = &dowork();
  }

  # Clean-up.
  if (&finalize($status)) { $status = 1; }
  if (&shutdown()) { $status = 1; }

  return $status;
}

#-------------------------------------------------------------------------------

sub startup {
  # Set the force_debug flag to non-zero (preferably 1) to force ouput of debugging messages.
  # Note that ahapp::startup performs no error checking of any kind, and does not return a status.
  my $force_debug = 0;
  &ahapp::startup($force_debug);
  return 0;
}

sub getpar {
  my $status = 0;
  # Prompt/construct hash in the order parameters appear in the file.
  foreach my $par (qw(infile outfile_root outfile_type datatype columnname
                      energybin tstart tstop deconvolve hkfile
                      hkext gticut_pre gticut_post cleanup clobber history)) {
    &ahgen::set_error_flag(0);
    $par{$par} = &ahapp::query_parameter($par);
    $status = &ahgen::get_error_flag();
    last if ($status);
  }

  # Issue some warnings.
  if ($par{'outfile_type'} !~ /^LC$/i and $par{'energybin'} !~ /^all$/i) {
    ahlog::ah_warn "HIGH", "The parameter energybin = $par{'energybin'} is ignored in $par{outfile_type} mode";
  } elsif ($par{'outfile_type'} !~ /^SPEC$/i and $par{'outfile_type'} !~ /^SCAN$/i) {
    if ($par{'tstart'} !~ /^TSTART$/i) {
      ahlog::ah_warn "HIGH", "The parameter tstart = $par{'tstart'} is used only in SPEC or SCAN mode";
    }
    if ($par{'tstop'} !~ /^TSTOP$/i) {
      ahlog::ah_warn "HIGH", "The parameter tstop = $par{'tstop'} is used only in SPEC or SCAN mode";
    }
  }
  return $status;
}

sub initialize {
  my $status = 0;

  # Pre-Processing
  &ahapp::begin_processing();

  # Set up for run: determine LC/SPEC/SCAN and set energy ranges etc.
  $status = &setrunmode();

  if (!$status) {
    # Choose columns for which to compute sums, based on instrument, and/or user parameter columnname.
    $status = &selectcolumns();
  }

  return $status;
}

sub dowork {
  # This code is consistent with TRF trf_hxisgdshield_15-06-17.docx.
  my $status = 0; # Error status.

  my $ii; # Loop index for looping over columns.
  # Step 4-3.
  # Loop over the columns selected based on user choice.
  for ($ii = 0; $ii < @column and !$status; $ii++) {
    my $infile = $par{'infile'}; # Input file supplied by user.
    my $column_ana = $column[$ii]; # Column name.
    my $ext; # Extension where this column is located.

    # Locate the extension where this column is located, based on the column name.
    $status = &setcolumnextension($column_ana, \$ext);

    # Set tstart/tstop and compute exposure.
    if (!$status) {
      $status = &set_time_range($infile, $ext);
    }
    
    if (!$status) {
      if ($par{'outfile_type'} =~ /^LC$/i) {
        $status = &createlc($infile, $ext, $column_ana);
      } elsif ($par{'outfile_type'} =~ /^SPEC$/i) {
        $status = &createspec($infile, $ext, $column_ana);
      } elsif ($par{'outfile_type'} =~ /^SCAN$/i) {
        $status = &createscan($infile, $ext, $column_ana);
      }
    }
  }
  return $status;
}

sub finalize {
  my $status = shift;
  # Note that end_processing calls ahapp::shutdown. It should not.
  # Note that end_processing has no means of indicating errors. It should.
  &ahapp::end_processing($status);
  return $status;
}

sub shutdown {
  my $status = 0;
  # Should call this, but end_processing already called it (in finalize).
  # Note that shutdown has no means of indicating errors. It should.
  # &ahapp::shutdown();
  return $status;
}

# Utility function for checking whether a value is in the range [min, max]. Returns 1 (true)
# if the value is *not* in the range, blank if it is.
sub outofrange { my ($val, $min, $max) = @_; defined $val and $val ne "" or $val = 0; return ($min > $val or $max < $val); }

sub setrunmode {
  my $status = 0;

  undef $emin; undef $emax;

  # Configure constants needed for each data type.
  if ($par{'datatype'} =~ /^SCL$/i) { $emaxlimit = 0; }
  elsif ($par{'datatype'} =~ /^HIST$/i) { $emaxlimit = 127; }
  elsif ($par{'datatype'} =~ /^GRB$/i) { $emaxlimit = 31; }

  # Interpret parameters to determine what type of data product to make (LC, SCAN, SPEC)
  # and configure the script to handle that data product properly.
  if ($par{'outfile_type'} =~ /^LC$/i) {
    # Note the parameter file has enumerated options SCL|HIST|GRB: this check is not strictly necessary.
    # Validate choices for datatype.
    if ($par{'datatype'} !~ /^SCL$/i && $par{'datatype'} !~ /^HIST$/i && $par{'datatype'} !~ /^GRB$/i) {
      ahlog::ah_err "Parameter \"datatype\" = \"$par{datatype}\" is invalid for LC mode; must be HIST, or GRB.";
      $status = 1;
    }

    # Parse energybin to get energy range.
    if (!$status) {
      if ($par{'energybin'} !~ /^all$/i) {
        # Parse parameter 7-96 into emin = 7, emax = 96. Use a regexp that requires both limits and the dash between.
        # At this stage, allow any integer for emin, emax, to keep this part general.
        if ($par{'energybin'} =~ /^\s*([+-]?\d+)\s*-\s*([+-]?\d+)\s*$/) { $emin = $1; $emax = $2; }
        else {
          ahlog::ah_err "Parameter \"energybin\" = \"$par{energybin}\" is invalid; must be \"all\" or a range, e.g., \"5-15\"";
          $status = 1;
        }
      }
    }

    # Validate selected energy limits.
    if (!$status) {
      # In case of "all", emin and emax will still be undefined at this point, so define them.
      defined $emin or $emin = $eminlimit; defined $emax or $emax = $emaxlimit;

      # Check whether chosen $emin is within the limits for this datatype.
      if (&outofrange($emin, $eminlimit, $emaxlimit)) {
        ahlog::ah_err "Lower energy limit $emin is outside of the allowed range [$eminlimit, $emaxlimit] for datatype $par{datatype}.";
        $status = 1;
      }

      # Check whether chosen $emax is within the limits for this datatype.
      if (&outofrange($emax, $eminlimit, $emaxlimit)) {
        ahlog::ah_err "Upper energy limit $emax is outside of the allowed range [$eminlimit, $emaxlimit] for datatype $par{datatype}.";
        $status = 1;
      }

      # Check whether emax is greater than or equal to emin.
      if (&outofrange($emax, $emin, $emax + 1)) {
        ahlog::ah_err "Energy limits [$emin, $emax] are not in ascending order.";
        $status = 1;
      }
    }

  } elsif ($par{'outfile_type'} =~ /^SPEC$/i) {
    # Validate choices for datatype.
    if ($par{'datatype'} !~ /^HIST$/i && $par{'datatype'} !~ /^GRB$/i) {
      ahlog::ah_err "Parameter \"datatype\" = \"$par{datatype}\" is invalid for SPEC mode; must be HIST, or GRB.";
      $status = 1;
    }

    if (!$status) {
      $emin = 0;
      $dec_emin = 0;
      $dec_emax = 4095;
      if ($par{'datatype'} =~ /^HIST$/i) {
        $emax = 127;
        $offset = 24;
        @bound = (68,80,112,116,124,128);
        @bin_factor = (2,8,16,64,128,512);
      } elsif ($par{'datatype'} =~ /^GRB$/i) {
        $emax = 31;
        $offset = 48;
        @bound = (9,17,22,26,29,31,32);
        @bin_factor = (16,32,64,128,256,512,1024);
      } # else { error already trapped above, see "Validate choices for datatype." }
    }
  } elsif ($par{'outfile_type'} =~ /^SCAN$/i) {
    # Validate choices for datatype.
    if ($par{'datatype'} !~ /^SCL$/i) {
      ahlog::ah_err "Parameter \"datatype\" = \"$par{datatype}\" is invalid for SCAN mode; must be SCL.";
      $status = 1;
    }
  } else {
    # Should not hit this case because outfile_type is enumerated to be either LC, SPEC, or SCAN.
    ahlog::ah_err "Parameter \"outfile_type\" = \"$par{outfile_type}\" is invalid; must be LC, SPEC, or SCAN.";
    $status = 1;
  }

  # If valid energy limits were not supplied, undefine them both. This is to prevent calling code from ignoring an error at this step.
  if ($status) { undef $emin; undef $emax; }

  return $status;
}

# Corresponds to TRF "Step 3: set column input"
sub selectcolumns {
  my $status = 0;

  $instrument = ahgen::get_keyword("$par{infile}", "1", "INSTRUME");

  # Clean up any previous column assignments.
  undef @column;

  if ($instrument !~ /^HXI[12]?$/i and $instrument !~ /^SGD[12]?$/i) {
    ahlog::ah_err "File given by parameter \"infile\": $par{infile} has data for instrument \"$instrument\", not HXI, HXI1, HXI2, SGD, SGD1, or SGD2";
    $status = 1;
  }

  if (!$status and $par{'columnname'} !~ /^all$/i) {
    # Select the columns based on the instrument if the columns were not already selected by the user parameter columnname.
    @column = split /[,\s]+/, $par{'columnname'};
  } elsif (!$status) {
    my @columnbase;

    # First set up all HXI cases (cases 1 through 3 in TRF).
    if ($instrument =~ /HXI/i) {
      if ($par{'datatype'} =~ /^SCL/i and $par{'outfile_type'} =~ /^LC$/i) {
        # Case 1-1.
        @columnbase = qw(SH_FBGO SH_HITPAT SH_UD SH_SUD);
      } elsif ($par{'datatype'} =~ /^SCL/i and $par{'outfile_type'} =~ /^SCAN$/i) {
        # Case 1-2.
        @columnbase = qw(SH_FBGO SH_HITPAT);
      } elsif ($par{'datatype'} =~ /^HIST$/i) {
        # Case 2.
        @columnbase = qw(SH_HIST);
      } elsif ($par{'datatype'} =~ /^GRB$/i) {
        # Case 3.
        @columnbase = qw(SH_GRB);
      } else {
        ahlog::ah_err "Data type $par{datatype} is not valid for HXI data.";
        $status = 1;
      }
    } else { # $instrument =~ /SGD/i.
      if ($par{'datatype'} =~ /^SCL/i and $par{'outfile_type'} =~ /^LC$/i) {
        # Case 4-1.
        @columnbase = qw(SH1_FBGO SH1_HITPAT SH1_UD SH1_SUD SH2_FBGO SH2_HITPAT SH2_UD SH2_SUD);
      } elsif ($par{'datatype'} =~ /^SCL/i and $par{'outfile_type'} =~ /^SCAN/i) {
        # Case 4-2.
        @columnbase = qw(SH1_FBGO SH1_HITPAT SH2_FBGO SH2_HITPAT);
      } elsif ($par{'datatype'} =~ /^HIST$/i) {
        # Case 5.
        @columnbase = qw(SH1_HIST SH2_HIST);
      } elsif ($par{'datatype'} =~ /^GRB$/i) {
        # Case 6.
        @columnbase = qw(SH1_GRB SH2_GRB);
      } else {
        ahlog::ah_err "Data type $par{datatype} is not valid for SGD data.";
        $status = 1;
      }
    }

    if (!$status) {
      # To each column name base numbers from 1 - 6 will be appended (GRB case) or 1 - 13 (other cases).
      my $maxcolnum;
      if ($par{'datatype'} =~ /^GRB$/i) {
        $maxcolnum = 6;
      } else {
        $maxcolnum = 13;
      }

      # Assign columns and extensions in which to find them.
      foreach my $ii (0..$#columnbase) {
        foreach my $jj (1..$maxcolnum) {
          my $column = $columnbase[$ii].$jj;
          push @column, $column;
        }
      }
    }
  }

  return $status;
}

sub setcolumnextension {
  my $status = 0;
  my $column = shift;
  my $ext = shift;
  # Corresponds to TRF "4-1: Determine extension to read".
  if ($column =~ /^SH_/i) {
    $shieldname = "SHIELD";
    if ($column =~ /HIST/i) {
      $$ext = 2;
    } else {
      $$ext = 1;
    }
  } elsif ($column =~ /^SH1_/i) {
    $shieldname = "SHIELD1";
    if ($column =~ /HIST/i) {
      $$ext = 2;
    } else {
      $$ext = 1;
    }
  } elsif ($column =~ /^SH2_/i) {
    $shieldname = "SHIELD2";
    if ($column =~ /HIST/i) {
      $$ext = 4;
    } elsif ($column =~ /GRB/i) {
      $$ext = 2;
    } else {
      $$ext = 3;
    }
  } else {
    ahlog::ah_err "Unable to determine extension containing column $column.";
    $status = 1;
  }
  return $status;
}

sub set_time_range {
  my $status = 0;
  my $infile = shift;
  my $ext = shift;

  if ($par{'outfile_type'} !~ /^LC$/i) {
    # Use the tstart parameter and/or the TSTART keyword to set the low end of time range.
    if ($par{'tstart'} =~ /^tstart$/i) {
      $tstart = ahgen::get_keyword($infile, $ext, 'TSTART');
      ahlog::ah_info "Using start time from TSTART keyword = $tstart.";
    } else {
      $tstart = $par{'tstart'};
      ahlog::ah_info "Using start time from tstart user parameter = $tstart.";
    }
  
    # Use the tstop parameter and/or the TSTOP keyword to set the upper end of time range.
    if ($par{'tstop'} =~ /^tstop$/i) {
      $tstop = ahgen::get_keyword($infile, $ext, 'TSTOP');
      ahlog::ah_info "Using stop time from TSTOP keyword = $tstop.";
    } else {
      $tstop = $par{'tstop'};
      ahlog::ah_info "Using stop time from tstop user parameter = $tstop.";
    }
  
    # Check for valid range, and compute exposure if all is well.
    if ($tstart >= $tstop) {
      ahlog::ah_err "TSTART = $tstart may not be greater than or equal to TSTOP = $tstop.";
      $status = 1;
    } else {
      $exposure = $tstop - $tstart;
    }
  }
  return $status;
}

sub setbitshrink {
  my $status = 0;
  my $column = shift;
  my $bit = shift;
  my $shrink = shift;

  # Corresponds to TRF "4-2: Set parameters".
  if ($column =~ /_FBGO/i) {
    $$bit = 16;
    $$shrink = 1;
  } elsif ($column =~ /_HITPAT/i) {
    $$bit = 16;
    $$shrink = 1;
  } elsif ($column =~ /_UD/i) {
    $$bit = 8;
    $$shrink = 2;
  } elsif ($column =~ /_SUD/i) {
    $$bit = 4;
    $$shrink = 1;
  } elsif ($column =~ /_HIST/i) {
    $$bit = 8;
    $$shrink = 1;
  } elsif ($column =~ /_GRB/i) {
    $$bit = 8;
    $$shrink = 1;
  } else {
    ahlog::ah_err "Unable to determine shrink factor and number of bits for column $column.";
    $status = 1;
  }
  return $status;
}

sub computecount {
  # This function computes the COUNTS column from a specified column in a
  # file+extension by taking the difference between subsequent rows, and adding 2^$bit
  # when a row "rolls over". The input column may be a scalar or a vector. The output
  # "COUNTS" column will match the format of the input column. This operation is
  # shared between the LC and SPEC modes.
  my $status = 0; # Error status.
  my $infile = shift; # Input file name.
  my $ext = shift; # Input file extension.
  my $column_ana = shift; # Input column from which to compute the counts.
  my $outfile = shift; # Name of output file.
  my $jj; # Top-level loop variable for looping over energy channels.

  # Clean up any previously-created temporary files. Do this even if cleanup is not enabled.
  unlink 'tmp1.fits'; unlink $outfile;

  my $bit; # Number of bits in the input channel.
  my $shrink; # Factor by which input data was "shrunk".
  # Set $bit and $shrink appropriately for the selected column.
  $status = &setbitshrink($column_ana, \$bit, \$shrink);

  if ($eminlimit < $emaxlimit) {
    if (!$status) {
      # Create an expression that will add a boolean "ERANGE" column. For energies
      # inside the range [emin, emax] the column will be T, otherwise F. This can
      # be used below to sum only energies inside the range.
      my $erangewidth = $emaxlimit - $eminlimit + 1;
      my $expr = "col *,ERANGE(${erangewidth}L)={";
      for ($jj = $eminlimit; $jj <= $emaxlimit; $jj++) {
        if (&outofrange($jj, $emin, $emax)) {
          $expr.= 'F,';
        } else {
          $expr.= 'T,';
        }
      }
      chop $expr; # Kill the trailing comma.
      $expr .= '}'; # Add the trailing brace.
  
      # Similarly, now add an expression that will add an "HSTSHRNK" column
      # to hold the value for hist_shrink for each energy. This can then be
      # used in the calculation expression to compute the COUNT column
      # correctly for each energy in one pass.
      $expr .= ",HSTSHRNK(${erangewidth}J)={";
      for ($jj = $eminlimit; $jj <= $emaxlimit; $jj++) {
        $expr .= ($column_ana =~ /HIST/i and ($jj <= 15)) ? 4 : 1;
        $expr .= ',';
      }
      chop $expr; # Kill the trailing comma.
      $expr.= '}'; # Add the trailing brace.
  
      # Use the expression constructed above in a call to ftcopy to add
      # these columns. The columns ERANGE and HSTSHRNK can then be used to
      # compute the COUNT vector-valued column in one pass. for all energies.
      $status = &ahgen::run_ftool("ftcopy", "infile=$infile+${ext}[$expr]", "outfile=$outfile");
    }
  
    if (!$status) {
      rename $outfile, 'tmp1.fits';
      $infile = 'tmp1.fits';
  
      # Use fcalc to create the COUNT vector column, using the
      # ERANGE column to set to 0 counts outside the energy range
      # and HSTSHRNK column to multiply by the correct value of
      # histshrink.
      my $expr = "ERANGE ? ".
              "(${column_ana}-${column_ana}{-1} >= 0 ? ".
              "(${column_ana}-${column_ana}{-1})*2^$shrink*HSTSHRNK: ".
              "(${column_ana}-${column_ana}{-1}+2^$bit)*2^$shrink*HSTSHRNK)".
              " : 0";
      $status = &ahgen::run_ftool("fcalc", "infile=$infile+$ext", "outfile=$outfile", "clname=COUNT", "expr=$expr");
    }
  } else {
      # Use fcalc to create the COUNT scalar column.
      my $expr = "${column_ana}-${column_ana}{-1} >= 0 ? ".
              "(${column_ana}-${column_ana}{-1})*2^$shrink: ".
              "(${column_ana}-${column_ana}{-1}+2^$bit)*2^$shrink";
      $status = &ahgen::run_ftool("fcalc", "infile=$infile+$ext", "outfile=$outfile", "clname=TOTCOUNT", "expr=$expr");
  }



  # for all the files just created, add CHECKSUM and DATASUM, and 
  # check that files are valid
  for my $currtmpfile ("tmp1.fits", "$outfile") {
    if (-e $currtmpfile) {
      &ahgen::update_checksum_and_verify($currtmpfile);
    }
  }

  return $status;
}

sub createlc {
  # This code is consistent with TRF trf_hxisgdshield_15-06-17.docx.
  my $status = 0; # Error status.
  my $infile = shift;
  my $ext = shift;
  my $column_ana = shift;

  # Compute the COUNTS column, stored in a temporary file.
  $status = &computecount($infile, $ext, $column_ana, 'tmp2.fits');

  if (!$status) {
    # Move the output file from the previous step to become the input to the next step.
    rename 'tmp2.fits', 'tmp1.fits';
    $infile = 'tmp1.fits';

    if ($eminlimit < $emaxlimit) {
      # Step 4-4.
      # Sum the COUNT vector column to produce a scalar TOTCOUNT column in a second temporary file.
      $status = &ahgen::run_ftool("fcalc", "infile=$infile+$ext", "outfile=tmp2.fits", "clname=TOTCOUNT", "expr=SUM(COUNT)");
    }
  }

  if (!$status) {
    # Move the output file from the previous step to become the input to the next step.
    rename 'tmp2.fits', $infile;

    # Step 4-5.
    # Clean up temporary files and output files that may have been created in a previous run.
    # Do this even if cleanup is "no".
    unlink 'timebin.fits', 'rate.fits', 'error.fits', 'fracexp.fits', "$par{outfile_root}_${column_ana}.lc";

    # Compute the TIMEBIN column from the difference in times between subsequent rows.
    $status = &ahgen::run_ftool('fcalc', "infile=$infile+$ext", 'outfile=timebin.fits',
      'clname=TIMEBIN', 'expr=TIME-TIME{-1}');
  }

  if (!$status) {
    $infile = 'timebin.fits'; # Output from last step becomes input to this step.
    # Compute the RATE column by dividing total counts by time difference.
    $status = &ahgen::run_ftool('fcalc', "infile=$infile+$ext", 'outfile=rate.fits',
      'clname=RATE', 'expr=TOTCOUNT/TIMEBIN');
  }

  if (!$status) {
    $infile = 'rate.fits'; # Output from last step becomes input to this step.
    # Compute the ERROR column by dividing square root of total counts by time difference.
    $status = &ahgen::run_ftool('fcalc', "infile=$infile+$ext", 'outfile=error.fits',
      'clname=ERROR', 'expr=sqrt(TOTCOUNT)/TIMEBIN');
  }

  if (!$status) {
    $infile = 'error.fits'; # Output from last step becomes input to this step.
    # Set the FRACEXP column to a constant value of 1.
    $status = &ahgen::run_ftool('fcalc', "infile=$infile+$ext", 'outfile=fracexp.fits',
      'clname=FRACEXP', 'expr=1');
  }

  if (!$status) {
    $infile = 'fracexp.fits'; # Output from last step becomes input to this step.
    # The light curve calculations were complete at the end of the previous step.
    # Now just create a clean output file containing only the standard light curve
    # columns: TIME, RATE, ERROR and FRACEXP.
    my $outfile = "$par{outfile_root}_${column_ana}.lc";
    $status = &ahgen::run_ftool('ftabcopy', "infile=$infile+$ext", "outfile=$outfile",
      'columns=TIME,RATE,ERROR,FRACEXP', 'rows=-', "clobber=$par{clobber}");

    # Write the parameters to the output file now that it exists.
    if ($par{'history'} =~ /^yes$/i) {
      &ahapp::write_parameters($outfile, 1);
    }
    
    # Add CHECKSUM and DATASUM, and check that file we just created is valid
    &ahgen::update_checksum_and_verify($outfile);
  }
  
  # for extra files created, and if user decided to keep temp files, 
  # add CHECKSUM and DATASUM, and check that files are valid.  
  if ($par{'cleanup'} =~ /^n/i) {
    for my $currtmpfile ('tmp1.fits', 'tmp2.fits', 'timebin.fits', 'rate.fits', 'error.fits', 'fracexp.fits', 'rate.fits', 'error.fits', 'fracexp.fits', "$par{outfile_root}_${column_ana}.lc") {
      if (-e $currtmpfile) {
        &ahgen::update_checksum_and_verify($currtmpfile);
      }
    }
  }

  if (!$status and $par{'cleanup'} =~ /^y/i) {
    # Clean up now if cleanup parameter set.
    unlink 'tmp1.fits', 'tmp2.fits', 'timebin.fits', 'rate.fits', 'error.fits', 'fracexp.fits';
  }

  return $status;
}

sub createspec {
  # This code is consistent with TRF trf_hxisgdshield_15-06-17.docx.
  my $status = 0; # Error status.
  my $infile = shift;
  my $ext = shift;
  my $column_ana = shift;

  # Clean up from any previous runs.
  unlink 'tmp_time.fits', 'tmp_spec.tbl';

  # Step 4-3: Calculate spectra
  $status = &ahgen::run_ftool("fselect", "infile=$infile+$ext", "outfile=tmp_time.fits",
    "expr=TIME>=$tstart && TIME<=$tstop");

  if (!$status) {
    $infile = 'tmp_time.fits';
    $status = &computecount($infile, $ext, $column_ana, 'tmp2.fits');
  }

  if (!$status) {
    rename 'tmp2.fits', $infile;

    $status = &ahgen::run_ftool("fsumrows", "infile=$infile+$ext", "outfile=tmp2.fits", "cols=COUNT", "rows=-",
      "operation=SUM", "sametype=yes");
  }

  if (!$status) {
    rename 'tmp2.fits', $infile;

    # Extension number is always 1 for the output from fsumrows.
    # Extract the summed data and write it to a text file to use as input to ascii2pha.
    $status = &ahgen::run_ftool("ftlist", "infile=$infile+1", "option=t", "outfile=tmp_spec.tbl",
      "rownum=no", "colheader=no");
  }

  if (!$status) {
    # Create the PHA file.
    my $detchans = $emax - $emin + 1;
    my $outfile = "$par{outfile_root}_${column_ana}.pha";
    $status = &ahgen::run_ftool("ascii2pha", "infile=tmp_spec.tbl", "outfile=$outfile",
      "chanpres=no", "dtype=1", "qerror=no", "rows=-", "fchan=$emin", "tlmin=0", "detchans=$detchans",
      "pois=no", "telescope=HITOMI", "instrume=$instrument", "detnam=$shieldname", "filter=NONE", "exposure=$exposure",
      "clobber=$par{clobber}");

    # Write the parameters to the output file now that it exists.
    if ($par{'history'} =~ /^yes$/i) {
      &ahapp::write_parameters($outfile, 1);
    }
    
    # Add CHECKSUM and DATASUM, and check that file we just created is valid
    &ahgen::update_checksum_and_verify($outfile);
  }
  
  # for all the files just created, add CHECKSUM and DATASUM, and 
  # check that files are valid
  for my $currtmpfile ("tmp_time.fits") {
    if (-e $currtmpfile) {
      &ahgen::update_checksum_and_verify($currtmpfile);
    }
  }

  if (!$status) {
    if ($par{'deconvolve'} =~ /y/i) {
      $status = &createdeconvolvedspec($column_ana, 'tmp_spec.tbl');
    }
  }

  return $status;
}

sub createdeconvolvedspec {
  # This code is consistent with TRF trf_hxisgdshield_15-06-17.docx.
  # This function operates on a text file containing a spectrum to compute a deconvolved spectrum.
  my $status = 0; # Error status.
  my $column_ana = shift; # Column that was the source for the input spectrum, used only to name the output file.
  my $inputspecfile = shift; # Name of the input spectrum file.
  my $deconvspecfile = 'spec_deconv.tbl'; # Name of temporary text file containing deconvolved spectrum.
  my @inputspec; # The input spectrum, before deconvolution.

  # Read input file.
  if (open INFILE, $inputspecfile) {
    @inputspec = <INFILE>; close INFILE;
    chomp @inputspec;
  } else {
    ahlog::ah_err "createdeconvolvedspec: unable to open file $inputspecfile.";
    $status = 1;
  }

  my @deconvspec; # The data for the deconvolved output spectrum.
  if (!$status) {
    unlink $deconvspecfile;

    # First section of the output spectrum consists of leading zeroes up
    # to the cutoff given by the $offset variable.
    my $ii;
    for ($ii = 0; $ii < $offset; $ii++) {
      my $line = sprintf("%.12f", 0.);
      push @deconvspec, $line;
    }

    # Compute the remainder of the output spectrum from the input spectrum.
    my $boundary_min = 0; # Lower boundary of section of the input spectrum currently being deconvolved.
    my $loop; # Loop variable for looping over the bounds: the points where the input file binning changes.

    # +++ 2015-07-27 JP: after checking with Hiroya, change to bounds, both code and TRF.
    # Loop over the bounds, each of which had a different binning applied.
    for ($loop = 0; $loop < @bound; $loop++) {
      my $boundary_max = $bound[$loop]; # The end point for this bound.
      my $bin = $bin_factor[$loop]; # The binning factor by which to deconvolve this bound.
      my $jj; # Loop variable for looping over the input channels, which are expanded into output channels.

      # Within this bound, loop over the input channels to expand them into the output channels.
      for ($jj = $boundary_min; $jj < $boundary_max; $jj++) {
        my $ii; # Loop variable for looping over output channels derived from the current input channel.
        # Loop over output channels derived from the input channel.
        # Each input channel with counts = X is turned into $bin output channels with rate X/$bin/$exposure.
        my $drate = $inputspec[$jj]/$bin/$exposure;
        for ($ii = 0; $ii < $bin; $ii++) {
          # Spread the counts in this input channel evenly over the output channel, and divide by the exposure
          # to produce "RATE" data.
          my $line = sprintf("%.12f", $drate);
          push @deconvspec, $line;
        }
      }
      # The next input channel will pick up right after the ($boundary_max - $boundary_min + 1) input channels
      # looped over $jj.
      $boundary_min = $boundary_max;
    }
    # Write the deconvolved spectrum in text form, to be used as input to ascii2pha.
    if (open OUTFILE, ">$deconvspecfile") {
      foreach my $line (@deconvspec) {
        print OUTFILE "$line\n";
      }
      close OUTFILE;
    } else {
      ahlog::ah_err "createdeconvolvedspec: unable to open for writing file $deconvspecfile";
      $status = 1;
    }
  }
  if (!$status) {
    # For clarity, compute the "detchans" for this case.
    my $detchans = $dec_emax - $dec_emin + 1;
    my $outfile = "$par{outfile_root}_${column_ana}_deconv.pha";
    # Use ascii2pha to create a PHA file from the text version of the deconvolved spectrum.
    $status = &ahgen::run_ftool("ascii2pha", "infile=$deconvspecfile", "outfile=$outfile",
      "chanpres=no", "dtype=2", "qerror=no", "rows=-", "fchan=$dec_emin", "tlmin=0", "detchans=$detchans",
      "pois=no", "telescope=HITOMI", "instrume=$instrument", "detnam=$shieldname", "filter=NONE", "exposure=$exposure",
      "clobber=$par{clobber}");

    # Write the parameters to the output file now that it exists.
    if ($par{'history'} =~ /^yes$/i) {
      &ahapp::write_parameters($outfile, 1);
    }
    
    # Add CHECKSUM and DATASUM, and check that file we just created is valid
    &ahgen::update_checksum_and_verify($outfile);
  }
  
  return $status;
}

sub createscan {
  # This code is consistent with TRF trf_hxisgdshield_15-06-17.docx.
  my $status = 0; # Error status.
  my $infile = shift;
  my $ext = shift;
  my $column_ana = shift;

  # Step 4-3.
  my $bit;
  my $shrink;
  $status = &setbitshrink($column_ana, \$bit, \$shrink);

  if (!$status) {
    unlink 'test_time.fits', 'test1.fits', 'test2.fits', 'test3.fits';
    $status = &ahgen::run_ftool("fcalc", "infile=$infile+$ext", "outfile=test_time.fits", "clname=TIMEBIN",
      "expr=TIME-TIME{-1}");
  }

  if (!$status) {
    $infile = 'test_time.fits';
    $status = &ahgen::run_ftool("fcalc", "infile=$infile+$ext", "outfile=test1.fits", "clname=RATE",
      "expr=${column_ana}-${column_ana}{-1} >= 0 ? ".
                 "(${column_ana}-${column_ana}{-1})*2^$shrink/TIMEBIN: ".
                 "(${column_ana}-${column_ana}{-1}+2^$bit)*2^$shrink/TIMEBIN");
  }

  if (!$status) {
    $infile = 'test1.fits';
    $status = &ahgen::run_ftool("fcalc", "infile=$infile+$ext", "outfile=test2.fits", "clname=ERROR",
      "expr=${column_ana}-${column_ana}{-1} >= 0 ? ".
                 "sqrt((${column_ana}-${column_ana}{-1})*2^$shrink)/TIMEBIN: ".
                 "sqrt((${column_ana}-${column_ana}{-1}+2^$bit)*2^$shrink)/TIMEBIN");
  }

  if (!$status) {
    $infile = 'test2.fits';
    $status = &ahgen::run_ftool("fcalc", "infile=$infile+$ext", "outfile=test3.fits", "clname=FRACEXP",
      "expr=1");
  }

  if (!$status) {
    $infile = 'test3.fits';
    my $outfile = "$par{outfile_root}_${column_ana}.lc";
    $status = &ahgen::run_ftool('ftabcopy', "infile=$infile+$ext", "outfile=$outfile",
      'columns=TIME,RATE,ERROR,FRACEXP', 'rows=-', "clobber=$par{clobber}");

    # Write the parameters to the output file now that it exists. Note that this set of history
    # will be propagated to the GTI-filtered file as well.
    if ($par{'history'} =~ /^yes$/i) {
      &ahapp::write_parameters($outfile, 1);
    }
    
    # Add CHECKSUM and DATASUM, and check that file we just created is valid
    &ahgen::update_checksum_and_verify($outfile);
  }

  if (!$status) {
    unlink 'test.fits', 'test_pre.fits', 'test_after.fits', 'test_gti.fits';
    $infile=$par{'hkfile'};
    $status = &ahgen::run_ftool('fselect', "infile=${infile}[$par{hkext}]", "outfile=test.fits",
      "expr=TIME>=$tstart&&TIME<=$tstop");
  }

  if (!$status) {
    $infile = 'test.fits';
    $status = &ahgen::run_ftool('fcalc', "infile=${infile}[$par{hkext}]", "outfile=test_pre.fits", "clname=START",
      "expr=TIME-$par{gticut_pre}");
  }

  if (!$status) {
    $infile = 'test_pre.fits';
    $status = &ahgen::run_ftool('fcalc', "infile=${infile}[$par{hkext}]", "outfile=test_after.fits", "clname=STOP",
      "expr=TIME+$par{gticut_post}");
  }

  if (!$status) {
    $infile = 'test_after.fits';
    $status = &ahgen::run_ftool('fappend', "infile=${infile}[$par{hkext}][col START,STOP]", "outfile=$par{outfile_root}_${column_ana}.lc");
  }

  if (!$status) {
    $infile = "$par{outfile_root}_${column_ana}.lc";
    $status = &ahgen::run_ftool('fthedit', "infile=${infile}[$par{hkext}]", "keyword=EXTNAME", "operation=add", "value=GTI");
  }

  if (!$status) {
    $infile="$par{outfile_root}_${column_ana}.lc";
    # GTI-filtered file will have this tool's parameter history block copied from the input file.
    $status = &ahgen::run_ftool('fselect', "infile=$infile", "outfile=$par{outfile_root}_${column_ana}_gti.lc",
      'expr=gtifilter()', "clobber=$par{clobber}");
  }
  
  # for all the files just created, add CHECKSUM and DATASUM, and 
  # check that files are valid
  for my $currtmpfile ("test_time.fits", "test1.fits", "test2.fits", "test3.fits", "$par{outfile_root}_${column_ana}_gti.lc", "$par{outfile_root}_${column_ana}.lc") {
    if (-e $currtmpfile) {
      &ahgen::update_checksum_and_verify($currtmpfile);
    }
  }
  
  return $status;
}
# $Log: hxisgdshield.pl,v $
# Revision 1.24  2016/02/29 18:46:24  klrutkow
# added call to ahgen::update_checksum_and_verify for all outfiles
#
# Revision 1.23  2016/02/19 00:22:39  klrutkow
# change mission to HITOMI
#
# Revision 1.22  2015/10/30 18:48:24  peachey
# Corrected a bug in which many extra channels with 0 counts were being
# added to all deconvolved spectra after the first one was created. The
# "offset" used to put in the initial 0s was being increased more with
# each file. There is no reason to change the offset value, the increment
# was probably a holdover from a previous version.
#
# Revision 1.21  2015/10/30 15:58:09  peachey
# Explicitly specify the housekeeping extension when performing fselect and fappend
# steps in SCAN mode.
#
# Revision 1.20  2015/08/24 19:41:13  peachey
# Temporarily change this script to work around a bug in ahlog which causes
# messages with "LOW" priority to be printed as errors. Thus for now,
# changing warnings to have "HIGH" priority, which mysteriously causes
# ahlog to print them properly, i.e., as warnings. Need to change this
# back when ahlog.pm behaves properly.
#
# Revision 1.19  2015/08/21 16:58:45  peachey
# Unrelated changes to sub getpar for processing parameters.
# 1. Check error status after each call to query_parameter, in order
#    to exit the loop the first time a parameter error occurs. Indicate
#    such errors by setting status.
# 2. Issue warnings for hidden parameters that are ignored because of the
#    mode of operation, i.e., energybin used for SPEC or SCAN, or
#    tstart/tstop used for LC.
#
# Revision 1.18  2015/08/19 18:48:34  peachey
# Write history keywords in output files.
#
# Revision 1.17  2015/08/12 15:31:07  peachey
# Accept user parameter tstart=TSTART and tstop=TSTOP, either/both of
# which cause the corresponding keywords to be read from the input file
# and used for time filtering.
#
# Revision 1.16  2015/08/07 13:59:23  peachey
# For SCL cases, do not bother with ERANGE, HSTSHRNK, or COUNTS intermediate columns.
#
# Revision 1.15  2015/08/07 13:10:21  peachey
# Remove unused functions setupcolumn and create_all_data_products.
#
# Revision 1.14  2015/08/07 13:02:43  peachey
# Change 'bond' to 'bound'
#
# Revision 1.13  2015/07/28 17:37:21  peachey
# Oops, forgot at the last moment to reset the channel
# to 0 in the convolved spectrum.
#
# Revision 1.12  2015/07/27 20:03:52  peachey
# Change spectra to start with channel 0 rather than 1. Additional
# miscellaneous fixes.
#
# Revision 1.11  2015/07/13 17:53:45  peachey
# Cosmetic improvements made while reconciling with TRF.
#
# Revision 1.10  2015/07/08 17:56:59  peachey
# Handle clobber for final output files consistently. Turn off the force_debug option.
#
# Revision 1.9  2015/07/08 17:27:50  peachey
# Final changes needed for SCAN mode: fix typos and add a call to fthedit
# to change the name of the GTI extension to GTI.
#
# Revision 1.8  2015/06/22 13:25:59  peachey
# 1. Add full SPEC mode, including deconvolved spectrum output.
# 2. Add full SCAN mode, but the last stage is commented out and remains
# untested since I do not yet have a HK file.
# Regarding SCAN and LC, there are some questions concerning the numbering
# of the channels, the way the channel numbers are added to the spectrum,
# and whether the TRF is correct as to the type of each spectrum
# (COUNTS v. RATE). Otherwise this version is pretty consistent with
# trf_hxisgdshield_15-06-17.docx.
#
# Revision 1.7  2015/06/17 17:14:41  peachey
# Rename createlc and createlc2 so that the current code is named createlc
# and the original version is createlc_trf_20150506.
#
# Revision 1.6  2015/06/17 16:04:07  peachey
# Some cosmetic code improvements and improvements to the comments.
# This version is consistent with TRF trf_hxisgdshield_15-06-17.docx.
#
# Revision 1.5  2015/06/05 20:16:21  peachey
# Improve comments.
#
# Revision 1.4  2015/06/05 19:53:50  peachey
# Fix a few small bugs. Enable debug mode always for now. Use the more efficient
# algorithm rather than the one in the TRF.
#
# Revision 1.3  2015/06/03 22:54:14  peachey
# Initial functional version of the script. Includes alternate version of the
# algorithm for lightcurves which is much more efficient.
#
# Revision 1.2  2015/05/22 14:10:33  peachey
# Restructure more along standard lines.
#
# Revision 1.1  2015/05/08 13:00:10  peachey
# Initial shell of hxisgdshield script.
#
