#!/usr/bin/perl
#
# Abstract
#
#   This is an example script illustrating how a SPICE CK containing
#   nominal MER-1/MER-2 orientation during cruise can be generated from
#   a pointing table providing the s/c spin axis (-Z) direction as a
#   function of time.
#
# Usage
#
#   This script requires the following arguments to be provided on the
#   command line:
#
#     > mer_cruisetable2ck.pl [sc] [lsk] [sclk] [table] [ck]
#
#   where:
#
#     [sc]    -- spacecraft name: MER-1 or MER-2
#     [lsk]   -- LSK file name
#     [sclk]  -- SCLK file name
#     [table] -- input pointing table file name
#     [ck]    -- output CK file name
#
#   Input LSK, SCLK, and pointing table files should exists and be
#   readable under the account running the script. Output CK file
#   should not exist prior to the script execution. The SCLK file should
#   for the s/c specified as the first argument.
#
#   All command line arguments are required and must be provided in the
#   exact order specified above.
#
# Input Pointing Table File
#
#   The input pointing table file should be a plain ASCII text file
#   containing a table with space-delimited columns specifying the s/c
#   -Z axis (spin axis) direction with respect to the J2000 inertial
#   frame. The table should have the following format:
#
#     UTC(1)   RA(1)   DEC(1)   SLEWDURATION(1)
#     ...      ...     ...      ...
#     UTC(n)   RA(n)   DEC(n)   SLEWDURATION(n)
#
#   where
#
#     UTC      is the time at which the s/c starts pointing its -Z axis in
#              the specified direction. The time must be in ISO format --
#              YYYY-MM-DDTHR:MN:SC.###.
#
#     RA, DEC  are the inertial J2000 RA and DEC along which -Z axis should
#              be pointed, in degrees.
#
#     SLEWDURATION is the duration of the slew that took the s/c from the
#              previous orientation to this orientation, in seconds.
#              This value is optional, and, if not present or too small,
#              is replaced with the default duration set by the $defslewd
#              variable.
#
#   Other details regarding pointing table format:
#
#     --  The fields on each line must be separated by spaces, tabs, or
#         combination of spaces and tabs.
#
#     --  The table must contain at least two data records.
#
#     --  Data records in the table must be time ordered. The time of
#         the next record should be greater than the time of the
#         previous record plus the slew duration from the next record.
#
#     --  Time tags are UTCs in ISO month/day format --
#         YYYY-MM-DDTHR:MN:SC.XXX -- with all tokens present (including
#         fractional seconds 'XXX') and padded with zeros ('02' is OK
#         in place of MM, DD, HR, MN, or SC while just '2' is not.)
#
#     --  RA, DEC, and SLEWDUR values must be numbers expressed in
#         engineering, "+/-ddd.ddd", or scientific,
#         "+/-d.dddddddE+/-ddd", notation with at least one digit
#         present in the whole part and the sign adjacent to it (i.e.
#         '-0.23' but not '- .23' or '- 0.23'.)
#
#     --  All blank lines present in the table are ignored.
#
#     --  Lines starting with '#' character are treated as comments and
#         ignored.
#
#   Pointing table example:
#
#     # DATE(UTC/ISOFORMAT)   RA(DEG)  DEC(DEG) SLEWDUR(SEC)
#     2003-06-25T00:00:00.000  108.400  -24.370     60.000
#     2003-07-04T00:00:00.000  107.920  -19.390     60.000
#     2003-07-17T00:00:00.000  155.360    0.200     60.000
#     2003-08-14T00:00:00.000  150.790    2.500     60.000
#     2003-09-16T00:00:00.000  154.470    7.680     60.000
#     2003-09-30T00:00:00.000  156.560    5.870     60.000
#     2003-10-29T00:00:00.000  164.830    3.760     60.000
#     2003-11-14T00:00:00.000  174.150    0.470     60.000
#     2003-12-03T00:00:00.000  179.500   -1.280     60.000
#     2003-12-13T00:00:00.000  184.220   -3.080     60.000
#     2003-12-24T00:00:00.000  189.450   -5.490     60.000
#     2004-01-01T00:00:00.000  193.600   -7.230     60.000
#     2004-01-13T00:00:00.000  198.970   -8.760     60.000
#     2004-01-18T00:00:00.000  204.460  -10.980     60.000
#     2004-01-25T00:00:00.000  204.460  -10.980     60.000
#
# Output CK
#
#   The output CK is a type 3 CK files that provides continuous coverage
#   for the whole interval of the input pointing table. The file has
#   comments explaining how the pointing profile data has been converted to
#   to the orientation stored in the CK. The comments also include the
#   input pointing table as an attachment.
#
# Temporary Files
#
#   The script creates and removes upon completion the following temporary
#   files:
#
#     --  MSOPCK input file that contains input orientation in the form
#         of rotational angles generated from RA and DEC pairs;
#
#     --  MSOPCK setup file containing keyword=value setup information
#         directing the program to make the right kind of the output CK
#         file;
#
#     --  CK comments file containing comments that are inserted into
#         the CK by the program.
#
#   All three files are created in the same directory as the output CK
#   file and have the same name as the CK file with additional
#   extensions ".msopck.input", ".msopck.setup", and ".msopck.comments"
#   correspondingly.
#
#   If MSOPCK fails to convert inputs generated by this script to a CK
#   file, the temporary files do not get deleted.
#
# Particulars
#
#   This script goes through the following steps:
#
#      1) gets and verifies command line arguments;
#
#      2) verifies the format of and buffers input pointing table data;
#
#      3) verifies consistency of input pointing table data, computes
#         start time for each record from the record time tag and slew
#         duration, and generates MSOPCK input file;
#
#      4) generates MSOPCK setup and comments files;
#
#      5) runs MSOPCK to make the output CK file;
#
#      6) removes temporary files;
#
# SPICE Utilities
#
#   The location of the MSOPCK program that this script invokes should
#   be specified in the variable below.
#
$msopck  = "/naif/toolkit/exe/msopck";

#
# Includes
#
#   The Time::Local module should be included because the script uses
#   "timegm" function.
#
use Time::Local;

#
# Version 2.0.0 -- February 13, 2003 -- BVS/NAIF
#
#   Replaced MER-A with MER-1 and MER-B with MER-2 to match project's
#   official name-id mapping.
#
# Version 1.0.0 -- May 4, 2002 -- BVS/NAIF
#
#   Initial release.
#
$scriptname = "mer_cruisetable2ck.pl";
$version    = "2.0.0 -- February 13, 2003 -- BVS/NAIF";

############################################################################
#
# Step 1:
#
# Get and verify command line inputs.
#
############################################################################

#
# Get command line arguments. There should be exactly 5 of them. If not,
# display usage and stop.
#
if ( @ARGV != 5 ) {

  print <<EndBlock;

$scriptname -- version $version

Usage:

    \% $scriptname [sc] [lsk] [sclk] [table] [ck]

where:

    [sc]    -- spacecraft name: MER-1 or MER-2
    [lsk]   -- LSK file name
    [sclk]  -- SCLK file name
    [table] -- input pointing table file name
    [ck]    -- output CK file name

All command line arguments are required and must be provided in the
exact order specified by the usage string above. See the script header
for more details.

EndBlock

  exit(1);

} else {

  #
  # Assign command-line argument to internal variables.
  #
  ( $scname, $lskfile, $sclkfile, $tablefile, $ckfile ) = @ARGV;

}

#
# Verify command line arguments. The s/c name must be MER-1 or MER-2
# (case insensitive), LSK, SCLK, and pointing table files must exist
# and be readable, and output CK should not exist.
#
if( ! ( $scname =~ /^MER-{0,1}(1|2)$/i ) ) {
  die "\nERROR: $scriptname: The s/c name '$scname' provided as the first ".
      "command line argument is not recognized. It should be 'MER-1' or ".
      "'MER-2'.\n\n";
} else {
  #
  # For recognized s/c we need to set correct NAIF IDs and NAMEs.
  #
  if ( $scname =~ /^MER-{0,1}1$/i ) {
    $scid   = "-253";
    $scname = "MER-1";
    $ckid   = "-253000";
    $frame  = "J2000";
  }
  if ( $scname =~ /^MER-{0,1}2$/i ) {
    $scid   = "-254";
    $scname = "MER-2";
    $ckid   = "-254000";
    $frame  = "J2000";
  }
}

if( ! ( -r $lskfile ) ) {
  die "\nERROR: $scriptname: The LSK file '$lskfile' specified as the ".
      "second command line argument does not exist or is not readable ".
      "under current account.\n\n";
}

if( ! ( -r $sclkfile ) ) {
  die "\nERROR: $scriptname: The SCSK file '$sclkfile' specified as the ".
      "third command line argument does not exist or is not readable ".
      "under current account.\n\n";
}

if( ! ( -r $tablefile ) ) {
  die "\nERROR: $scriptname: The pointing table file '$tablefile' ".
      "specified as the fourth command line argument does not exist or ".
      "is not readable under current account.\n\n";
}

if( -e $ckfile ) {
  die "\nERROR: $scriptname: The output ck file '$ckfile' ".
      "specified as the fifth command line argument already exists.\n\n";
}

#
# Set names of the temporary files -- MSOPCK setup, input, and comment
# files.
#
$inputfile   = $ckfile.".msopck.input";
$setupfile   = $ckfile.".msopck.setup";
$commentfile = $ckfile.".msopck.comments";
$keeptmpfile = "no";

#
# The default slew duration is set in the variable below. It is used if
# the slew duration is not specified in the pointing table or if it is
# specified but has value smaller than the default value.
#
# From common sense it is set to 1 second. It must not be set to less
# than 0.001 of a second because MSOPCK will not be able to process
# the input generated using smaller values.
#
$defslewd = "1.0";

#
# Get current date/time. It will be included into the CK comments.
#
$currdate = `date`;
chop( $currdate );


############################################################################
#
# Step 2:
#
# Buffer input pointing table data.
#
############################################################################

#
# Open pointing table file.
#
open ( INPUT, "<$tablefile" ) ||
  die "\nERROR: $scriptname: cannot open pointing table file '$tablefile' ".
      "for read access.\n\n";

#
# Buffer individual data records ("as is") in the @tablelines and the
# table image (exactly as it appear in the file) in the $tableimage. The
# lines will be used in further processing while the image will be
# included into the comments of the output CK file. Count the lines, just
# for sake of producing more meaningful error messages, along the way.
#
@tablelines = ();
$tableimage = "";
$count = 0;

while ( $line = <INPUT> ) {

  $tableimage = $tableimage."      ".$line;
  $count++;
  chop( $line );

  #
  # Ignore blank lines and comments lines (with '#' as the first character.)
  #
  if ( ! ( $line =~ /^\s*$/ || $line =~ /^\s*\#/ ) ) {

    #
    # Verify that the line is properly formatted, i.e. that it contains
    #
    #    TIME NUMBER NUMBER [NUMBER]
    #
    # where TIME should be in YYYY-MM-DDTHR:MN:SC.XXX format and the
    # NUMBERs should follow engineering, "+/-ddd.ddd", or scientific,
    # "+/-d.dddddddE+/-ddd", notation with at least one digit present
    # in the whole part and the sign adjacent to it.
    #
    # The third number shown in [] is optional slew duration in seconds.
    #
    # If it is, buffer it. Otherwise, complain and stop.
    #

    if ( $line =~ /^\s*(\d{4,4}-\d{2,2}-\d{2,2}T\d{2,2}\:\d{2,2}\:\d{2,2}\.\d{3,3})(\s+(\+|-)?\d+\.?\d*((e|E|d|D)(\+|-)?\d+)?){2,3}\s*$/ ) {

      @tablelines = ( @tablelines, $line );

    } else {

      die "\nERROR: $scriptname: the line \# $count, '$line', of ".
          "the pointing table file '$tablefile' is not properly ".
          "formatted. See the script header for the input file format ".
          "description.\n\n";

    }

  }

}

close ( INPUT ) ||
  die "\nERROR: $scriptname: cannot close pointing table file '$tablefile' ".
      "opened for read access.\n\n";

#
# Check that we have buffered at least two lines. If not, complain and stop.
#
if ( $#tablelines < 1 ) {
  $j = $#tablelines + 1;
  die "\nERROR: $scriptname: pointing table file '$tablefile' ".
      "must contains at least 2 data lines. It contains $j.\n\n";
}


############################################################################
#
# Step 3:
#
# Make MSOPCK input file.
#
############################################################################

#
# Open MSOPCK input file for write.
#
open ( MSINPUT, ">$inputfile" ) ||
  die "\nERROR: $scriptname: cannot open MSOPCK input file '$inputfile' ".
      "for write access.\n\n";

#
# Loop over the buffered table lines.
#
for( $i=0; $i<=$#tablelines; $i++ ) {

  #
  # Pull out time, RA/DEC, and duration from the line.
  #
  ( $time, $ra, $dec, $duration ) = split( ' ', $tablelines[$i] );

  #
  # Compute Euler angles from RA and DEC.
  #
  $angz = $ra  - 180.0;
  $angy = $dec +  90.0;
  $andx = 0.0;

  #
  # For all lines but the first something should be printed to the MSOPCK
  # input file.
  #
  if ( $i >= 1 ) {

    #
    # Re-set slew duration to the default value if the table's value is
    # to small.
    #
    if ( $duration eq "" || $duration < $defslewd ) {
       $duration = $defslewd
    }

    #
    # Compute time of the start of the slew -- split time tag into tokens,
    # convert tokens into perl internal time representation, subtract
    # the slew duration from it, and convert the result back into an ISO
    # formatted time tag.
    #
    ($year,$mon,$day,$hour,$min,$sec) = split ( /[-:T]+/, $time );
    $ttime = timegm($sec,$min,$hour,$day,$mon-1,$year-1900) - $duration;
    ($sec,$min,$hour,$day,$mon,$year,$wday,$yday) = gmtime($ttime);
    $btime = sprintf( "%04d-%02d-%02dT%02d:%02d:%06.3f",
                     $year+1900, $mon+1, $day, $hour, $min, $sec);

    #
    # Verify that the time of the start of the slew is greater than the
    # previous time. If not, complain and stop.
    #
    if ( $btime le $ptime ) {
      close  ( MSINPUT );
      unlink ( $inputfile );
      $j = $i + 1;
      die "\nERROR: $scriptname: start of the slew time '$btime' computed ".
          "from the time and slew duration provided on the $j-th data record ".
          "of the pointing table '$tablefile' is less than or equal to ".
          "the time of the previous record, '$ptime'.\n\n";
    }

    #
    # Print MSOPCK file records for the beginning and the end of the
    # constant orientation interval for the previous pointing table
    # record.
    #
    printf MSINPUT "%23s %8.3f %8.3f %8.3f\n", $ptime, $pangx, $pangy, $pangz;
    printf MSINPUT "%23s %8.3f %8.3f %8.3f\n", $btime, $pangx, $pangy, $pangz;

  }

  #
  # Save time and Euler angles for the current line.
  #
  $ptime = $time;
  $pangz = $angz;
  $pangy = $angy;
  $pangx = $angx;

}

#
# Print the end of the interval line for the last record.
#
printf MSINPUT "%23s %8.3f %8.3f %8.3f\n", $ptime, $pangx, $pangy, $pangz;

close ( MSINPUT ) ||
  die "\nERROR: $scriptname: cannot close MSOPCK output file '$inputfile' ".
      "opened for read access.\n\n";


############################################################################
#
# Step 4:
#
# Make MSOPCK setup and CK comments files.
#
############################################################################


#
# Open MSOPCK setup file for write access and make it with a single
# print block.
#
open ( MSSETUP, ">$setupfile" ) ||
  die "\nERROR: $scriptname: cannot open MSOPCK setup file '$setupfile' ".
      "for write access.\n\n";

#
# This setup is for a type 3 CK that forces interpolation between all
# input data points (MAXIMUM_VALID_INTERVAL = 31536000 means "interpolate
# over a year long periods") and tell MSOPCK to generate angular rates.
#
print MSSETUP <<EndBlock;

   \\begindata

      LSK_FILE_NAME           = '$lskfile'
      SCLK_FILE_NAME          = '$sclkfile'

      INTERNAL_FILE_NAME      = '$scname Nominal Cruise Attitude CK, $currdate'
      COMMENTS_FILE_NAME      = '$commentfile'

      CK_TYPE                 = 3
      CK_SEGMENT_ID           = '$scname Nominal Cruise Attitude'
      INSTRUMENT_ID           = $ckid
      REFERENCE_FRAME_NAME    = '$frame'
      ANGULAR_RATE_PRESENT    = 'MAKE UP/NO AVERAGING'

      MAXIMUM_VALID_INTERVAL  = 31536000

      INPUT_TIME_TYPE         = 'UTC'
      INPUT_DATA_TYPE         = 'EULER ANGLES'
      EULER_ANGLE_UNITS       = 'DEGREES'
      EULER_ROTATIONS_ORDER   = ( 'X' 'Y' 'Z' )

      PRODUCER_ID             = 'MER AACS'

   \\begintext

EndBlock

close ( MSSETUP ) ||
  die "\nERROR: $scriptname: cannot close MSOPCK setup file '$setupfile' ".
      "opened for read access.\n\n";

#
# Open MSOPCK comments file for write and make it with a single print
# block.
#
open ( MSCOMMENT, ">$commentfile" ) ||
  die "\nERROR: $scriptname: cannot open MSOPCK comments file ".
      "'$commentfile' for write access.\n\n";

print MSCOMMENT <<EndBlock;

$scname Nominal Cruise Orientation CK
===============================================================================

   This CK file contains nominal cruise orientation for $scname, NAIF ID
   $ckid, with respect the $frame inertial frame.


Orientation Data in the File
--------------------------------------------------------

   During all time covered by this file the orientation of $scname
   corresponds to the pointing table provided in "Appendix 1" of these
   comments. Each line of the table contains the $frame RA nd DEC
   direction along which the -Z axis is pointed between the time
   specified by that record and the time specified by the next record
   minus duration of the slew specified by the next record. For each
   RA/DEC pair the rotation from $frame to $scname frame is computed as
   follows:

      M ($frame->$scname) = [ DEC+90 ]y [ RA-180 ]z

   The slews are modeled as a uniform rotation from initial to final
   orientation over the slew duration interval. Any slew duration from the
   table that was less than $defslewd seconds has been replaced with
   $defslewd seconds before processing.

   While the s/c actually spins around the Z axis, the spinning is NOT
   modeled by the data in this file.


Pedigree
--------------------------------------------------------

   This file has been generated by ``$scriptname'' script,
   (Ver. $version.) that reformatted the $scname
   pointing table to be acceptable for input into the MSOPCK program,
   and them ran the program to generate this CK.


Version and Date
--------------------------------------------------------

   Version 1.0 -- $currdate


Contact Information
--------------------------------------------------------

   If you have any questions regarding this CK file, contact:

      MER AACS Team

   or

      Boris Semenov, NAIF, x4-8136, Boris.Semenov\@jpl.nasa.gov


Appendix 1: Input Pointing Table.
--------------------------------------------------------

   The table below was stored in the file

      $tablefile

   that was the input to the script that generated this CK:

$tableimage

Appendix 2: MSOPCK Setup File and Segment Coverage Information
--------------------------------------------------------

EndBlock

close ( MSCOMMENT ) ||
  die "\nERROR: $scriptname: cannot close MSOPCK comments file ".
      "'$commentfile' opened for read access.\n\n";


############################################################################
#
# Step 5:
#
# Run MSOPCK to make output CK file.
#
############################################################################

#
# Put together MSOPCK command line and run the program.
#
$command = "$msopck $setupfile $inputfile $ckfile 2>&1";
$result = `$command`;

#
# Check returned status and complain and stop if the program failed.
#
$status = ( $? >> 8 );
if ( $status ) {
    die "\nERROR: $scriptname: MSOPCK failed. Messages were:\n\n$result.";
}

############################################################################
#
# Step 6:
#
# Clean up.
#
############################################################################

#
# Delete temporary files if keeping them has not been requested.
#
if ( ! ( $keeptmpfile =~ /^yes$/i ) ) {
  unlink( $inputfile ) ||
    die "\nERROR: $scriptname: cannot delete temporary MSOPCK input file ".
        "'$inputfile'.\n\n";
  unlink( $setupfile ) ||
    die "\nERROR: $scriptname: cannot delete temporary MSOPCK setup file ".
        "'$setupfile'.\n\n";
  unlink( $commentfile ) ||
    die "\nERROR: $scriptname: cannot delete temporary MSOPCK comments file ".
        "'$commentfile'.\n\n";
}

#
# All done.
#
exit(0);
