#!/usr/bin/perl

# Given MIPS command parameters, this script computes scan mirror positions
# and can plot or print out those positions as a function of time.  The script
# will warn you if you violate the hard stop or fine DAC range constraints.
# You can enter all the parameters on the command line, or the script will
# prompt you for them if it doesn't find them.  The idea is that you can
# enter the same parameters you'd like to use for a MIPS command sequence
# and the script will tell you where the scan mirror will go during
# that sequence and warn you before you do something evil to the scan
# mirror.

# The parameters are:
# SCANABS SCANMODE RAMPDIR SCANPOS2 RELPOS1 RELPOS2 STEPOFFSET RAMPSLOPE
# STIMCYCLE NUMDCE FRAMECNT

# send complaints to cengelbracht@as.arizona.edu
# last updated 21 August 2003


# indicate whether you want to print out CSMM positions, plot them, or both
$print = 1;		# 0 = don't print; 1 = print
$plot = 0;		# 0 = don't plot; 1 = plot (must have PGPLOT module)


# constants
$hardstop1 = 610;
$hardstop2 = 3486;


# get command parameters

if ($#ARGV >= 0) {$scanabs = int ($ARGV[0]);}
else {
   print "Please enter SCANABS (0 - 4095): ";
   $scanabs = int (<STDIN>);
   }
unless (($scanabs >= 0) && ($scanabs <= 4095)) {
   print STDERR "SCANABS must be between 0 and 4095.\n";
   exit;
}
   

if ($#ARGV >= 1) {$scanmode = $ARGV[1];}
else {
   print "Please enter SCANMODE (0 = chop, 1 = ramp): ";
   chop ($scanmode = <STDIN>);
   }
unless (($scanmode == 0) || ($scanmode == 1)) {
   print STDERR "SCANMODE must be 0 (chop) or 1 (ramp)\n";
   exit;
}

if ($#ARGV >= 2) {$rampdircmd = $ARGV[2];}
else {
   print "Please enter RAMPDIR (0 = positive, 1 = negative): ";
   chop ($rampdircmd = <STDIN>);
   }
if ($scanmode) {
   unless (($rampdircmd == 0) || ($rampdircmd == 1)) {
      print STDERR "RAMPDIR must be 0 (positive) or 1 (negative)\n";
      exit;
   }
}

if ($#ARGV >= 3) {$scanpos2cmd = int ($ARGV[3]);}
else {
   print "Please enter SCANPOS2 (0 - 4095): ";
   $scanpos2cmd = int (<STDIN>);
   }
unless (($scanpos2 >= 0) && ($scanpos2 <= 4095)) {
   print STDERR "SCANPOS2 must be between 0 and 4095.\n";
   exit;
}

if ($#ARGV >= 4) {$relpos1 = int ($ARGV[4]);}
else {
   print "Please enter RELPOS1 (0 - 4095): ";
   $relpos1 = int (<STDIN>);
   }
unless (($relpos1 >= 0) && ($relpos1 <= 4095)) {
   print STDERR "RELPOS1 must be between 0 and 4095.\n";
   exit;
}

if ($#ARGV >= 5) {$relpos2 = int ($ARGV[5]);}
else {
   print "Please enter RELPOS2 (0 - 4095): ";
   $relpos2 = int (<STDIN>);
   }
unless (($relpos2 >= 0) && ($relpos2 <= 4095)) {
   print STDERR "RELPOS2 must be between 0 and 4095.\n";
   exit;
}

if ($#ARGV >= 6) {$stepoffset = int ($ARGV[6]);}
else {
   print "Please enter STEPOFFSET (-2047 - 2047): ";
   $stepoffset = int (<STDIN>);
   }
unless (($stepoffset >= -2047) && ($stepoffset <= 2047)) {
   print STDERR "STEPOFFSET must be between -2047 and 2047.\n";
   exit;
}

# a value of 27 below is as fast as the spacecraft can slew, and 310 will give
# you the slowest MIPS scan rate - about 2"/second on the sky
if ($#ARGV >= 7) {$rampslope = int ($ARGV[7]);}
else {
   print "Please enter RAMPSLOPE (0 - 511): ";
   $rampslope = int (<STDIN>);
   }
if ($scanmode) {
   unless (($rampslope >= 0) && ($rampslope <= 511)) {
      print STDERR "RAMPSLOPE must be between 0 and 511.\n";
      exit;
   }
}

if ($#ARGV >= 8) {$stimcycle = int ($ARGV[8]);}
else {
   print "Please enter STIMCYCLE (1 - 63): ";
   $stimcycle = int (<STDIN>);
   }
unless (($stimcycle >= 1) && ($stimcycle <= 63)) {
   print STDERR "STIMCYCLE must be between 1 and 63.\n";
   exit;
}

if ($#ARGV >= 9) {$numdce = int ($ARGV[9]);}
else {
   print "Please enter NUMDCE (1 - 1176): ";
   $numdce = int (<STDIN>);
   }
unless (($numdce >= 1) && ($numdce <= 1176)) {
   print STDERR "NUMDCE must be between 1 and 1176.\n";
   exit;
}

if ($#ARGV >= 10) {$framenum = int ($ARGV[10]);}
else {
   print "Please enter FRAMENUM (1 - 61): ";
   $framenum = int (<STDIN>);
   }
unless (($framenum >= 1) && ($framenum <= 61)) {
   print STDERR "FRAMNUM must be between 1 and 61.\n";
   exit;
}


# derive new parameters from command parameters for use in algorithms below

$dcetime = $framenum + 1.5;			# DCE time, in MIPS seconds
$freq = 1 / $dcetime;

$scanpos1 = $scanabs;

if ($scanpos2cmd == 0) {$scanpos2 = $scanpos1;}
else {$scanpos2 = $scanpos2cmd;}

# set sign of ramp motion (1 = negative direction, 0 = positive direction)
if ($rampdircmd == 1) {$rampdir = -1;}
else {$rampdir = 1;}

if ($scanmode) {
   $slope = 1e4 / $rampslope;	# units = fine DAC counts / sec.
   $freq = 8;
}


# initialize min and max csmm position trackers to first csmm position
# note that the fine DAC is 8 times finer than the coarse DAC and that relpos
#    2048 is the center of the fine (and coarse) DAC
$posmin = $scanpos1 + ($relpos1 - 2048) / 8 + $stepoffset / 8 ;
$posmax = $scanpos1 + ($relpos1 - 2048) / 8 + $stepoffset / 8 ;


# main loop to compute scan mirror positions
for ($i = 0; $i < $numdce; $i++) {
   for ($j = 0; $j < $dcetime*$freq; $j++) {

      # compute position in stimcyle
      $cyclepos = $i % $stimcycle;

      # compute elapsed time, in MIPS seconds
      $time[$index] = $i * $dcetime + $j / $freq;

      # compute csmm position for odd numbered DCEs
      if ($cyclepos % 2) {
         $coarse[$index] = $scanpos2;
         $fine[$index] = $relpos2 + $stepoffset * ($cyclepos + 1) + $rampdir *
            $scanmode * $slope * $j / $freq;
         if (($fine[$index] < 0) || ($fine[$index] > 4095))
            {print STDERR "range violation: fine DAC = $fine[$index] at DCE $cyclepos\n";}
         $pos[$index] = $coarse[$index] + ($fine[$index] - 2048) / 8;
      }

      # compute csmm position for even numbered DCEs
      else {
         $coarse[$index] = $scanpos1;
         $fine[$index] = $relpos1 + $stepoffset * ($cyclepos + 1) + $rampdir *
            $scanmode * $slope * $j / $freq;
         if (($fine[$index] < 0) || ($fine[$index] > 4095))
            {print STDERR "range violation: fine DAC = $fine[$index] at DCE $cyclepos\n";}
         $pos[$index] = $coarse[$index] + ($fine[$index] - 2048) / 8;
      }

      if ($pos[$index] < $hardstop1) {
         print STDERR "Hardstop violation: CSMM index $pos[$index] at DCE $cyclepos\n";
      }
      if ($pos[$index] > $hardstop2) {
         print STDERR "Hardstop violation: CSMM index $pos[$index] at DCE $cyclepos\n";
      }

      if ($pos[$index] > $posmax) {$posmax = $pos[$index];}
      if ($pos[$index] < $posmin) {$posmin = $pos[$index];}

      $index++;
   }
}


# print scan mirror positions vs. time
if ($print) {
   print "arcsec. (on the sky) computed using CE1 gain (computed each DCE)\n";
   printf "%10s%10s%10s%10s\n", "time (s)", "SCANABS", "FINE DAC", "arcsec.";
   for ($i=0; $i<=$#time; $i++) {
      $gain =  2.619e1 - 8.828e-3 * $pos[$i] + 2.213e-6 * $pos[$i]**2;
      printf "%10.3f%10.0f%10.0f%10.3f\n", $time[$i], $pos[$i], $fine[$i],
         ($pos[0]-$pos[$i])*$gain/32.635;
   }
}


# plot scan mirror positions

if ($plot) {
   use PGPLOT;

   # expand the plot limits a little bit
   $posmin = $posmin - 0.1 * ($posmax - $posmin);
   $posmax = $posmax + 0.1 * ($posmax - $posmin);

   # draw most of the plot
   pgbeg (0, "?", 0, 0);
   pgqinf ('type', $device, $junk);
   if ($device ne "XSERVE") {pgpap (6, 1);}
   pgsch (1.5);
   pgenv (0, $time[$#time], $posmin, $posmax, 0, -2);
   pgbox (bcnts, 0, 0, bnts, 0, 0);
   pglab ("MIPS seconds", "coarse DAC position", "");
   pgsci (2);
   pgslw (5);
   if ($scanmode) {pgline ($#time+1, \@time, \@pos);}
   else {pgpt ($#time+1, \@time, \@pos, 0);}
   pgsls (2);
   pgline (2, [$time[0],$time[$#time]], [$hardstop1,$hardstop1]);
   pgline (2, [$time[0],$time[$#time]], [$hardstop2,$hardstop2]);

   # add a fine DAC scale
   pgslw (1);
   $finemin = $fine[0] + ($posmin - $pos[0]) * 8;
   $finemax = $fine[0] + ($posmax - $pos[0]) * 8;
   pgsci (1);
   pgsls (1);
   pgswin (0, $time[$#time], $finemin, $finemax);
   pgbox (0, 0, 0, cmts, 0, 0);
   pgmtxt (r, 2.5, 0.5, 0.5, "fine DAC position");

   pgend;
}


# print command-line parameters
printf "\n%7s%7s%7s%7s%7s%7s%7s%7s%7s%7s%7s\n", "scnabs", "scnmod", "rmpdir",
   "scnps2", "relps1", "relps2", "stpoff", "rmpslp", "stmcyc", "numdce",
   "frmcnt";
printf "%7d%7d%7d%7d%7d%7d%7d%7d%7d%7d%7d\n\n", $scanabs, $scanmode,
   $rampdircmd, $scanpos2cmd, $relpos1, $relpos2, $stepoffset, $rampslope,
   $stimcycle, $numdce, $framenum;
