#!/usr/bin/perl
#

$version = 'lucierrors.040.pl';

# lucierrors - test a LUCI script for common errors in
#
# Usage: luckerrors.pl [slit_type]
#
# where:
#   slit_type = one of: mask, phot, 2.0, 1.5, 1.0, 0.75, 0.5, 0.25
#
# Overview
#
# lucierrors scans all LUCI scripts in the current working directory
# and searches for the most common operational or syntax errors.
#
# It works assuming that a uniform naming convention has been adopted
# (cue laughter).  We make the following associations based on the
# file extension:
#
#    XX.acq -- target acquistion script
#    XX.spc/.spec/.obs -- spectroscopic science observation script
#    XX.img  -- imaging script
#    XX.cal  -- calibration script
#    XX.flat -- flat field calibration script
#    XX.arc  -- arc (comparison) lamp calibration script
#    XX.dark -- dark calibration script
#    XX.cal  -- generic calibration script
#
# It is hard to guess all possible naming variations, so at least among
# the OSU/RC partners we will start enforcing a uniform naming convention
# going forward to try vainly to reduce the guesswork.
#
# Author:
#  CS Kochanek, OSU Astronomy Department
#  ckochanek@astronomy.ohio-state.edu
#
# Modification History:
#   2012 December 5 -- initial version
#   2012 December 7 -- errors/warnings to push standard naming convention
#                      collect errors/warnings to print as summary
#                      generate a dark script for program
#   2012 December 15 - Fix bad name for 0.5 arcsec slit
#                      flag long script names/funny characters as warning
#   2013 January  2  - If a slit is used in an acq script, be sure it is consistent with the spec scripts
#                      check guide star magnitude if available
#                      check guide star geometry
#   2013 January 28  - Error in reading posangle -- did not allow for sign (fixed)
# 
#
# Testing Rules (update as needed)
#   -- DIT must be >= 4
#   -- Double checks standard slit masks (prompts user for desired slit)
#   -- Rejects timed Lamp commands
#   -- Checks camera use
#   -- Forces absolute dithers for imaging, relative for spectroscopy
#   -- Warns of acquistion scripts that do not contain a dithered image pair
#   -- Tries to identify targets with multiple acquistions (i.e. where you
#         want to acquire once, then run several additional observation 
#         scripts)
#   -- Reports all the possible darks that may be required (depends on 
#         observer religion) -- creates a dark script "program.dark.cal"
#
#---------------------------------------------------------------------------

# everyone's favorite number
$pi = 3.14159265;
# guide star magnitude limits
$guideRmin = 12.0;   # too bright
$guideRwarn= 15.0;   # reconsider
$guideRmax = 16.0;   # definitely too faint
# guide patrol field geometry parameters
$gregradmax = 330.0;               # radius of the focal plane
$m3vign     = 240.0;               # wavefront sensor partly vigneted by M3
$lucimin    = 120.0;               # half width of lucifer
$lucivign   = $lucimin + 60.0;     # vignetting limit
$radswing   = 1020.8;              # radius of the swing arm limit
$angswing   = 18.0;                # swing arm angle limit
$padding    = 30.0;                # amount of padding to try to maintain

# yes, perl can do this, but date provides a nice format with no further work
open(GETTIME,"date |");
$date = <GETTIME>;
chomp($date);
close(GETTIME);

$slitwant = 'UNKNOWN';
if ($#ARGV == 0) { 
  if ($ARGV[0] =~ /mask/i) { $slitwant = 'MASK'; printf "Set desired slit to MOS\n"; }
  if ($ARGV[0] =~ /phot/i) { $slitwant = 'PHOT'; printf "Set desired slit to Sepectrophotometric\n";}
  if (abs($ARGV[0]-2.00) < 0.001) { $slitwant = '2.00arcsec'; printf "Set desired slit to 2.00 arcsec\n";} 
  if (abs($ARGV[0]-1.50) < 0.001) { $slitwant = '1.50arcsec'; printf "Set desired slit to 1.50 arcsec\n";} 
  if (abs($ARGV[0]-1.00) < 0.001) { $slitwant = '1.00arcsec'; printf "Set desired slit to 1.00 arcsec\n";} 
  if (abs($ARGV[0]-0.75) < 0.001) { $slitwant = '0.75arcsec'; printf "Set desired slit to 0.75 arcsec\n";} 
  if (abs($ARGV[0]-0.50) < 0.001) { $slitwant = '0.50arcsec'; printf "Set desired slit to 0.50 arcsec\n";} 
  if (abs($ARGV[0]-0.25) < 0.001) { $slitwant = '0.25arcsec'; printf "Set desired slit to 0.25 arcsec\n";} 
  if ($slitwant =~ /UNKNOWN/) {
    printf "slit name $ARGV[0] unknown \n";
    printf "enter one of {mask, phot, 2.0, 1.5, 1.0, 0.75, 0.50 0.25}\n";
    exit(-1);
    }
  }

# clear the lists of accumulated warnings and errors
@warnings = ();
@errors   = ();
@nobsname = ();  # number of times an object of a given name is acquired
@nobscoord= ();  # number of times an object of a given coordinate is acquired

open(INFILE,"file * | grep ASCII |");
while(<INFILE>) {
  $line = $_;
  @entries = split(' ',$line);
  $file = $entries[0];
  $file =~ s/://;
  $type = 'UNKNOWN';
  open(CHECKLUCI,"grep -i start_readout $file |");
  $line = <CHECKLUCI>;
  close(CHECKLUCI);
  if ( !($line =~ /start_readout/i)) {
    printf "file $file is not a lucifer script\n\n";
  } else { 
    if ($file =~ /acq/i)  { $type = 'ACQ'; }
    if ($file =~ /obs/i)  { $type = 'SPEC'; }
    if ($file =~ /spec/i) { $type = 'SPEC'; }
    if ($file =~ /spc/i)  { $type = 'SPEC'; }
    if ($file =~ /img/i)  { $type = 'IMG'; }
    if ($file =~ /flat/i) { $type = 'CAL'; }
    if ($file =~ /cal/i)  { $type = 'CAL'; }
    if ($file =~ /dark/i) { $type = 'CAL'; }
    if ($file =~ /arc/i)  { $type = 'CAL'; }
    printf "DOING $file of type $type\n";
    if ($type =~ /UNKNOWN/) {
      $error = "    ERROR: file " . $file . " has a name which cannot be converted into a file type -- use standard naming convention\n      script.acq, script.spec, script.img, script.flat.cal, script.arc.cal, script.dark.cal \n";
      printf "\n\n$error\n";
      $errors{$file} = $errors{$file} . $error;
      }
    $savetype{$file} = $type;
    # flag non-standard script names
    if ( !($file =~ /\.acq$/) &&  !($file =~ /\.spec$/) &&  !($file =~ /\.img$/) &&  !($file =~ /\.arc.cal$/) &&  !($file =~ /\.flat.cal$/) &&  !($file =~ /\.dark.cal$/) ) {
      $warn = "    WARNING: file " . $file . " follows a non-standard naming convention -- use standard naming convention\n      script.acq, script.spec, script.img, script.flat.cal, script.arc.cal, script.dark.cal \n";
      printf "\n\n$warn\n";
      $warnings{$file} = $warnings{$file} . $warn;
      }
    # flag long scripts or those with special characters
    if ( (length($file)>20) || ($file =~ /[_\+\-]/) ) {
      $warn = "    WARNING: file " . $file . ":  Observers report strong preferences for shorter script names with minimal non-alphanumeric characters\n";
      printf "\n\n$warn\n";
      $warnings{$file} = $warnings{$file} . $warn;
      }
    # bad cal name
    if ( $type =~ /CAL/ ) {
      if ( !($file =~ /flat.cal/) && !($file =~ /dark.cal/) && !($file =~ /arc.cal/) ) {
        $warn = "    WARNING: calibration file " . $file . " follows a non-standard naming convention -- use standard calibration naming convention\n      script.flat.cal, script.arc.cal, script.dark.cal \n";
        printf "\n\n$warn\n";
        $warnings{$file} = $warnings{$file} . $warn;
        }
      } 
    # if there is a spectroscopic observation force a check of the slit
    if ($type =~ /SPEC/) {
      while ($slitwant =~ /UNKNOWN/) { 
        printf "Enter the slit expected: mask, phot, 2.0, 1.5, 1.0, 0.75, 0.50 0.25\n";
        $inslit = <STDIN>; $inslit =~ s/\n//; $inslit =~ s/^\s*//g; $inslit =~ s/\s*$//g;
        if ( $inslit =~ /mask/i ) {
          $slitwant = 'MASK';
          $warn     = "    WARNING: this script has no means of checking if the right mask is used for MOS observations \n";
          printf "\n$warn\n"; 
          $warnings{$file} = $warnings{$file} . $warn;
        } else {
          if ($inslit =~ /phot/i)        { $slatwant = 'PHOT'; printf "Set slit to 2.00 arcsec\n";}
          if (abs($inslit-2.00) < 0.001) { $slitwant = '2.00arcsec'; printf "Set slit to 2.00 arcsec\n";} 
          if (abs($inslit-1.50) < 0.001) { $slitwant = '1.50arcsec'; printf "Set slit to 1.50 arcsec\n";} 
          if (abs($inslit-1.00) < 0.001) { $slitwant = '1.00arcsec'; printf "Set slit to 1.00 arcsec\n";} 
          if (abs($inslit-0.75) < 0.001) { $slitwant = '0.75arcsec'; printf "Set slit to 0.75 arcsec\n";} 
          if (abs($inslit-0.50) < 0.001) { $slitwant = '0.50arcsec'; printf "Set slit to 0.50 arcsec\n";} 
          if (abs($inslit-0.25) < 0.001) { $slitwant = '0.25arcsec'; printf "Set slit to 0.25 arcsec\n";} 
          }
        if ($slitwant =~ /UNKNOWN/) { printf "entry $slit was not interpretable\n"; }
        }
      }
    printf "    checking $file of type $type\n";
    # flags/variables to set by scanning script
    $doesacq   = 0;  # does the script do a target acquisition 1/0
    $doesimg   = 0;  # does the script take an img (1) or a spectrum (0)
    $dark      = 0;  # is this a dark script (called a dark script if the script contains a DARK command);
    $offset    = 'UNKNOWN'; 
    $mask      = 'UNKNOWN';
    $roemode   = 'UNKNOWN';
    $dit       = 'UNKNOWN';
    $ndit      = 'UNKNOWN';
    $targname  = 'UNKNOWN';
    $targcoord = 'UNKNOWN';
    $guidecoord= 'UNKNOWN';
    $targmag   = 'UNKNOWN';
    $guidemag  = 'UNKNOWN';
    $posangle  = 'UNKNOWN';
    $noff      = 0;
    open(INPUT,"<$file");
    while(<INPUT>) {
      $line = $_;
      chomp($line);
      $line =~ s/\r//g; # Clean Ctrl-Ms if present (sigh)
      # look for the meta-commands to embed the target/guide magnitudes
      # consider double pounds (#\s*#) as a commented out case
      if (!($line =~ /\#\s*\#/) ) {
        if ($line =~ /\#\s*TARGET_MAG\s*=\s*(\d+\.?\d*\s*[JHK])\s*mag/i) { $targmag  = $1; } 
        if ($line =~ /\#\s*GUIDE_MAG\s*=\s*(\d+\.?\d*)\s*R\s*mag/i)   { $guidemag = $1; } 
        }
      if (!($line =~ /^\s*#/)) {  # ignore comment lines
        $line =~ s/#.*//g;   #strip any trailing comments
        if ($line =~ /PI_NAME\s*=\s*(.*)/i)  { $piname = $1; $piname =~ s/\s//g; }
        if ($line =~ /TARGET_NAME\s*=\s*(.*)/i)  { $targname = $1; $targname =~ s/\s//g; $nobsname{$targname} += 1; }
        if ($line =~ /TARGET_COORD\s*=\s*(.*)/i) { $targcoord = $1; $ccode=$targcoord; $ccode =~ s/\s//g; $nobscoord{$ccode} += 1; }
        if ($line =~ /GUIDE_COORD\s*=\s*(.*)/i)  { $guidecoord = $1; }
        if ($line =~ /POSANGLE\s*=\s*([+-]*\d+)/i)    { $posangle = $1; }
        if ($line =~ /CAMERA/i && $line =~ /CAMERA\s*=\s*N1\.8/i) { $camera = 'N18'; }
        if ($line =~ /CAMERA/i && $line =~ /CAMERA\s*=\s*N3\.7/i) { $camera = 'N375'; }
        if ($line =~ /START_TELESCOPE_SETUP/i) { $doesacq = 1; }
        if ($line =~ /OFFSET_TYPE\s*=\s*relative/i)  { $offset = 'RELATIVE'; } 
        if ($line =~ /OFFSET_TYPE\s*=\s*absolute/i)  { $offset = 'ABSOLUTE'; } 
        if ( ($line =~ /FLEXURE_COMP\s*=\s*off/i) && !($type =~ /CAL/) )  { $error = "    ERROR: script has flexure compensation off -- it should be on\n"; 
                                                   printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; }
        if ($line =~ /MASK\s*=\s*ID(\d*)/i) { $mask = $1; }
        if ($line =~ /MASK\s*=\s*NB(\d*)/i) { $warn = "    WARNING: Refer to masks by their ID number - this is much safer than using the cabinet (NB) number\n"; 
                                              printf "\n$warn\n"; $warnings{$file} = $warnings{$file} . $warn; }
        if ($line =~ /OFFSET\s*=/i)      { $noff++; }
        if ($line =~ /ACQUISITION\s*=/i) { $noff++; }
        if ( !($line =~ /NDIT/i) && ($line =~ /DIT\s*=\s*(\d*)/i) ) { $dit   = $1;} 
        if ( $line =~ /NDIT\s*=\s*(\d*)/i )                         { $ndit  = $1;} 
        if ( $line =~ /NEXPO\s*=\s*(\d*)/i )                        { $nexpo = $1;} 
        if ( ($line =~ /ROE_MODE/i) && ($line =~ /mer/i) )   { $roemode = 'mer';}
        if ( ($line =~ /ROE_MODE/i) && ($line =~ /o2dcr/i) ) { $roemode = 'o2dcr';}
        if ( $line =~ /LAMP\s*=\s*[A-Za-z0-9]*\s+\d+/i ) { $error = "    ERROR: script specifies a lamp on time in line \"" . $line . "\", which should not be done\n";  
                                                           printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; }
        if ( $line =~ /DARK/i) { $dark = 1;}
        if ( ($type =~ /SPEC/) && ($line =~ /no_mask_in_use/g) ) { $error = "    ERROR: based on name belived this was a spectroscopic script, but no mask is used, probably a naming issue\n      script.acq, script.spec, script.img, script.flat.cal, script.arc.cal, script.dark.cal \n"; printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; }
        if ( ($type =~ /IMG/) && ($line =~ /MASK_POSITION/i) && !($line =~ /no_mask_in_use/g) ) { $error = "    ERROR: based on name belived this was an imaging script, but a mask is being used, probably a naming issue\n      script.acq, script.spec, script.img, script.flat.cal, script.arc.cal, script.dark.cal \n"; printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; }
        }
      }
    close(INPUT);
    # shortest allowed DIT is 4
    if ($dit < 4) { $eror = "    ERROR: shortest allowed DIT = 4 script has " . $dit . "\n"; printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; }
    # check for things that should be true of an acquisition script
    if ($type =~ /ACQ/)  {
      $acqmask{$mask} = 1;
      if (!($camera =~ /N18/) )  { $error = "    ERROR: script is a bad acquistion script -- not using the N1.8 camera, using " . $camera ."\n"; printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; }
      #this is not obviously true
      #if ($offset =~ /ABSOLUTE/) { $error = "    ERROR: script is a bad acquistion script -- doing absolute offsets\n"; printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error;  }
      if ($noff > 2) { 
        $error = "    ERROR: acquisition script with >2 (found $noff) acquisition images -- 1 for bright, 1 + 1 offset for faint\n"; 
        printf "\n\n$error\n"; 
        $warnings{$file} = $warnings{$file} . $error;
      } else { 
        $warn = "    WARNING: use one acquisition image for bright targets, one plus offset for faint targets \n        -- need TARGMAG to evaluate choice\n"; 
        $warnings{$file} = $warnings{$file} . $warn;
        }
# EVALUATE THE GUIDE STAR MAGNITUDE
      if ( $guidemag =~ /UNKNOWN/ ) {  
        $warn = "    WARNING: acquisition scripts should include a metacommand\n         \"#GUIDE_MAG = xx.x R mag\" \n        providing the guide star magnitude -- THIS WILL BECOME MANDATORY\n";
        printf "\n\n$warn\n";  $warnings{$file} = $warnings{$file} . $warn;
      } else {
        if ($guidemag < $guideRmin) {
          $error = "    ERROR: guide mag $guidemag is too bright, limit >$guideRmin mag\n";
          printf "\n\n$error\n"; 
          $errors{$file} = $errors{$file} . $error;
          }
        if ($guidemag > $guideRmax) {
          $error = "    ERROR: guide mag $guidemag is too faint, limit <$guideRmax mag\n";
          printf "\n\n$error\n"; 
          $errors{$file} = $errors{$file} . $error;
        } else { 
          if ($guidemag > $guideRwarn) {
            $warn = "    WARNING: guide mag $guidemag is getting faint, <$guideRwarn is preferred\n";
            printf "\n\n$warn\n"; 
            $warnings{$file} = $warnings{$file} . $warn;
            }
          }
        } 
# EVALUATE THE TARGET MAGNITUDE
      if ( $targmag =~ /UNKNOWN/ ) {  
        $warn = "    WARNING: acquisition scripts should include a metacommand\n         \"#TARGET_MAG = xx.x [JHK] mag\" \n        providing the target magnitude -- THIS WILL BECOME MANDATORY\n";
        printf "\n\n$warn\n";  $warnings{$file} = $warnings{$file} . $warn;
        } 
# CHECK THE GUIDE STAR POSITION
      $targcoord =~ /(\d+)\s+(\d+)\s+(\d+\.?\d*)\s+([+\-]?\d+)\s+(\d+)\s+(\d+\.?\d*)/;
      $ra1 = $1; $ra2 = $2; $ra3 = $3; $dc1 = $4; $dc2 = $5; $dc3 = $6;
      $tra       = $ra1+$ra2/60.0+$ra3/3600.0;
      if ($dc1 =~ /\-/) {
        $tdec = $dc1-$dc2/60.0-$dc3/3600.0;
      } else {
        $tdec = $dc1+$dc2/60.0+$dc3/3600.0;
        }
      $guidecoord =~ /(\d+)\s+(\d+)\s+(\d+\.?\d*)\s+([+\-]?\d+)\s+(\d+)\s+(\d+\.?\d*)/;
      $ra1 = $1; $ra2 = $2; $ra3 = $3; $dc1 = $4; $dc2 = $5; $dc3 = $6;
      $gra       = $ra1+$ra2/60.0+$ra3/3600.0;
      if ($dc1 =~ /\-/) {
        $gdec = $dc1-$dc2/60.0-$dc3/3600.0;
      } else {
        $gdec = $dc1+$dc2/60.0+$dc3/3600.0;
        }
      # make a theory (x,y) vector to the guide star in arcsec
      $cosd = cos($pi*$tdec/180.0);
      $dx   = -3600.0*15.0*$cosd*($gra-$tra);
      $dy   =  3600.0*($gdec-$tdec);
      #printf "debug dx,dy $dx,$dy\n";
      # make a theory vector for the rotator angle
      $px   = cos(($posangle+90.0)*$pi/180.0);
      $py   = sin(($posangle+90.0)*$pi/180.0);
      #printf "debug px,py $px,$py posangle $posangle pi $pi \n";
      # guide star position in lucifer coordinates
      $lx   = $dx*$px+$dy*$py;  
      $ly   = $dx*$py-$dy*$px;
      #printf "debug lx,ly $lx,$ly\n";
      $lxa  = ($lx > 0) ? $lx : -$lx;
      $lya  = ($ly > 0) ? $ly : -$ly;
      $m3vigncode   = '';  # get a flag ready for being in the M3 vign region
      $lucivigncode = '';  # get a flag ready for being in the luci vign region
      $edgedist     = 1.e32; # estimated distance to edge
      # check against the radius of the gregorian field
      $lr = sqrt($lx*$lx+$ly*$ly);
      if ($lr > $gregradmax) {
        $error = sprintf("    ERROR: guide star radius (%3d arcsec) beyond radius of focal plane (%3d arcsec)\n",$lr,$gregradmax);
        printf "\n\n$error\n"; 
        $errors{$file} = $errors{$file} . $error; 
      } else {
        $gregradwarn = $gregradmax - $padding;
        $dtemp       = $gregradmax-$lr;
        if ($dtemp < $edgedist) {
          $edgedist = $dtemp;
          }
        if ($lr > $gregradwarn) {
          $warn = sprintf("    WARNING: guide star radius (%3d arcsec) very close (%3d arcsec) to edge of focal plane (%3d arcsec)\n",$lr,$gregradmax-$lr,$gregradmax);
          printf "\n\n$warn\n"; 
          $warnings{$file} = $warnings{$file} . $warn; 
          }
        if ($lr > $m3vign) {
          $warn = sprintf("    WARNING: guide star radius (%3d arcsec) in M3 vignetted region (>%3d arcsec)\n",$lr,$m3vign);
          printf "\n\n$warn\n"; 
          $warnings{$file} = $warnings{$file} . $warn;
          $m3vigncode      = '(in M3 vignetted region)';
          }
        }
      $dtempx = $lxa-$lucimin;
      $dtempy = $lya-$lucimin;
      if ($lxa <= $lucimin) {
        if ($dtempy < $edgedist) { $edgedist = $dtempy; }
        } 
      if ($lya <= $lucimin) {
        if ($dtempx < $edgedist) { $edgedist = $dtempx; }
        } 
      if ( ($lxa >= $lucimin) && ($lya >= $lucimin)) {
        $dtemp = sqrt($dtempx*$dtempx+$dtempy*$dtempy);
        if ($dtemp < $edgedist) { $edgedist = $dtemp; }
        }  
      if ( ($lxa < $lucimin) && ($lya < $lucimin)) {
        $error = sprintf("    ERROR: guide star lies inside Lucifer focal plane (x,y)=(%3d,%3d) should be more than %3d\n",$lx,$ly,$lucimin);
        printf "\n\n$error\n"; 
        $errors{$file} = $errors{$file} . $error; 
        }
      if ( ($lxa < $lucivign) && ($lya < $lucivign)) {
        $warn = sprintf("    WARNING: guide star will vignette Lucifer focal plane (x,y)=(%3d,%3d) should be more than %3d\n",$lx,$ly,$lucivign);
        printf "\n\n$warn\n"; 
        $warnings{$file} = $warnings{$file} . $warn; 
        $lucivigncode = '(in Luci vignetting region)';
        }
      # check against the arm movements convert to position relative to arm "center"
      $ax = $radswing - $lx;
      $ay = $lya;  #NOTE USE OF ABSOLUTE VALUE
      $ra = sqrt($ax*$ax+$ay*$ay);
      $dtemp = $radswing-$ra;
      if ($dtemp < $edgedist) { $edgedist = $dtemp;}
      if ( $ra > $radswing ) {
        #printf "lx, ly $lx, $ly, ax, ay $ax, $ay radswing $radswing radius $ra \n";
        $error = "    ERROR: guide star lies beyond radial travel range of guide probe\n";
        printf "\n\n$error\n"; 
        $errors{$file} = $errors{$file} . $error; 
      } else {
        $radswingwarn = $radswing - $padding;
        if ($ra > $radswingwarn) { 
          $warn = sprintf("    WARNING: guide star lies very close (%2d arcsec) to the limiting radial travel range of guide probe\n",$radswing-$ra);
          printf "\n\n$warn\n"; 
          $warnings{$file} = $warnings{$file} . $warn; 
          }
        }  
      $sang = (180.0/$pi)*atan2($ay,$ax);
      if ($sang > $angswing) {
        #printf "angle $sang andswing $angswing\n";
        $error = "    ERROR: guide star lies beyond the angular travel range of guide probe\n";
        printf "\n\n$error\n"; 
        $errors{$file} = $errors{$file} . $error; 
      } else {
        $dest = $ra*($angswing-$sang)*$pi/180.0;  # use small angles to estimate distance from edge
        if ($dest < $edgedist) { $edgedist = $dest;}
        if ($dest < $padding) {
          $warn = sprintf("    WARNING: guide star lies very close (%2d arcsec) to the limiting angular travel range of guide probe\n",$dest);
          printf "\n\n$warn\n"; 
          $warnings{$file} = $warnings{$file} . $warn; 
          }
        }
      $guidestat{$file} = 'Nearest guide region edge ' . int($edgedist+0.5) . ' arcsec ' . $lucivigncode . $m3vigncode;
# end basic guide star position check
      }
    # check for things that should be true of a spectroscopic script
    if ($type =~ /SPEC/)  {
      $specmask{$mask} = 1;
      printf "checking slit\n";
      if (!($camera =~ /N18/) ) { $error = "    ERROR: bad spectroscopic script -- not using the N1.8 camera, using " . $camera . "\n";  
                                  printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; }
      if ($doesacq == 1)        { $error = "    ERROR: bad spectroscopic script -- script is doing a target acquisition\n";  
                                  printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error;}
      if ($offset =~ /ABSOLUTE/) { $error = "    ERROR: bad spectroscopic script -- doing absolute offsets instead of relative\n";  
                                   printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; }
      if ( ($slitwant =~ /PHOT/)       && !($mask =~ /995623/i) ) { $error = "    ERROR: bad spectroscopic script -- mask ID" . $mask . " does not correspond to " . $slitwant . "\n";  
                                                                    printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; }
      if ( ($slitwant =~ /2.00arcsec/) && !($mask =~ /990018/i) ) { $error = "    ERROR: bad spectroscopic script -- mask ID" . $mask . " does not correspond to " . $slitwant . "\n";  
                                                                    printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; }
      if ( ($slitwant =~ /1.50arcsec/) && !($mask =~ /990021/i) ) { $error = "    ERROR: bad spectroscopic script -- mask ID" . $mask . " does not correspond to " . $slitwant . "\n";  
                                                                    printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; }
      if ( ($slitwant =~ /1.00arcsec/) && !($mask =~ /990034/i) ) { $error = "    ERROR: bad spectroscopic script -- mask ID" . $mask . " does not correspond to " . $slitwant . "\n";  
                                                                    printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; }
      if ( ($slitwant =~ /0.75arcsec/) && !($mask =~ /990029/i) ) { $error = "    ERROR: bad spectroscopic script -- mask ID" . $mask . " does not correspond to " . $slitwant . "\n";  
                                                                    printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; }
      if ( ($slitwant =~ /0.50arcsec/) && !($mask =~ /990032/i) ) { $error = "    ERROR: bad spectroscopic script -- mask ID" . $mask . " does not correspond to " . $slitwant . "\n";  
                                                                    printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; }
      if ( ($slitwant =~ /0.25arcsec/) && !($mask =~ /990065/i) ) { $error = "    ERROR: bad spectroscopic script -- mask ID" . $mask . " does not correspond to " . $slitwant . "\n";  
                                                                    printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; }
      if ( $targmag =~ /UNKNOWN/ ) {  
        $warn = "    WARNING: spectroscopic scripts should include a metacommand \"#TARGET_MAG = xx.x [JHK] mag\" providing the target magnitude\n             THIS WILL BECOME MANDATORY\n";
        printf "\n\n$warn\n";  $warnings{$file} = $warnings{$file} . $warn;
        } 
      }
    # check for things that should be true of an imaging script
    if ($type =~ /IMG/) {
      if (!($camera =~ /N375/) ) { $error = "    ERROR: bad imaging script -- not using the N3.75 camera, using " . $camera . "\n"; printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; }
      if ($offset =~ /RELATIVE/) { $error = "    ERROR: bad imaging script -- doing relative offsets\n"; printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; }
      if ( $targmag =~ /UNKNOWN/ ) {  
        $warn = "    WARNING: imaging scripts should include a metacommand \"#TARGET_MAG = xx.x [JHK] mag\" providing the target magnitude\n             THIS WILL BECOME MANDATORY\n";
        printf "\n\n$warn\n";  $warnings{$file} = $warnings{$file} . $warn;
        } 
      if ( ($guidemag =~ /UNKNOWN/) && ($doesacq == 1)  ) {  
        $warn = "    WARNING: imaging scripts doing an acquisition should include a metacommand \"#GUIDE_MAG = xx.x R mag\" providing the guide star magnitude\n             THIS WILL BECOME MANDATORY\n";
        printf "\n\n$warn\n";  $warnings{$file} = $warnings{$file} . $warn;
        } 
      }
    # type of dark
    if ( !($type =~ /ACQ/) && ($dark == 0)) {
      if ( ($roemode =~ /UNKNOWN/) || ($dit =~ /UNKNOWN/) ) {
        printf "    ERROR: integration time and/or mode are undefined: ROE_MODE=" . $roemode . "DIT=" . $dit . "\n"; 
        printf "\n\n$error\n"; $errors{$file} = $errors{$file} . $error; 
      } else { 
        $darkname = $roemode . $dit;
        $darks{$darkname}++;
        $darkfile{$darkname} = $darkfile{$darkname} . $file . ' ';
        }
      }
    printf "  CHECK COMPLETE\n\n";
    # update checklog
    open(OUTLOG,">.lucierrors.$file");
    printf OUTLOG "Completed $version analysis of $file on $date\n";
    if ( (keys(%errors) == 0) && (keys(%warnings) == 0) ){
      printf OUTLOG "   No errors or warnings\n";
    } else {
      if ( !(keys(%errors) == 0  ) ) { printf OUTLOG "$errors{$file}";  }
      if ( !(keys(%warnings) == 0) ) { printf OUTLOG "$warnings{$file}";}
      } 
    close(OUTLOG);
    }
  }


#spit out the accumulated warnings
printf "\n\nERROR MESSAGES \n";
if (keys(%errors) == 0) {
  printf "  no errors were identified\n";
} else {
  foreach $file (sort(keys %errors)) {
    printf "  for $file of type $savetype{$file} (could be ACQ, SPEC, IMG, CAL) \n";
    printf "$errors{$file}\n";
    }
  printf "BECAUSE THERE ARE ERRORS, QUITTING NOW\n";
  exit(-1);
  }

#spit out the accumulated warnings
printf "\n\nWARNING MESSAGES \n";
if (keys(%warnings) == 0) {
  printf "  no warnings were identified\n";
} else { 
  foreach $file (sort(keys %warnings)) {
    printf "  for $file of type $savetype{$file} (could be ACQ, SPEC, IMG, CAL) \n";
    printf "$warnings{$file}\n";
    }
  }

#checked for mixed masks in acquisistion scripts
if ( keys(%specmask) > 0) {
  printf "\n\nCHECKING FOR MASK DIFFERENCES BETWEEN SPECTROSCOPIC AND ACQUISTION SCRIPTS\n";
  $nfound = 0;
  foreach $mask (sort(keys %acqmask)) {
    if ($specmask{$mask} != 1) {
      $nfound++;
      printf "  ERROR:  An acquistion script has mask $mask that does not appear in any spectroscpic script\n";
      }
    }
  if ($nfound == 0) { printf "   No problems found\n";}
  }


#summary of guide stars
if (keys(%guidestat) > 0) {
  printf "\n\nSUMMARY OF GUIDE STAR GEOMETRIES\n";
  $nfound = 0;
  foreach $file (sort(keys %guidestat)) {
    printf "  $file: $guidestat{$file}\n";
    }
  }

#flag possible multiple acquisitions of the same target
printf "\n\nCHECKING FOR MULTIPLE ACQUISTIONS\n";
printf "  (in almost all circumstances you should only have one script that is acquiring any given target)\n";
printf "\n  CHECK #1: SUMMARY OF TARGETS WITH MULTIPLE ACQUISTIONS BY NAME @nobsname \n";
if (keys(%nobsname) == 0) {
  printf "  none were identified\n";
} else { 
  $nfound = 0;
  foreach $targname (sort(keys %nobsname)) {
    if ($nobsname{$targname}>1) {
      $nfound++;
      printf "    WARNING $targname seems to be acquired $nobsname{$targname} times, which may be inefficient\n"; 
      }
    }
  if ($nfound == 0) { printf "    No problems found\n"; }
  }
printf "\n  CHECK #2: SUMMARY OF TARGETS WITH MULTIPLE ACQUISTIONS BY COORDINATE\n     (all spaces removed from coordinate string) \n";
if (keys(%nobscoord) == 0) { 
  printf "    none were identified\n";
} else {
  $nfound = 0;
  foreach $targcoord (sort(keys %nobscoord)) {
    if ($nobscoord{$targcoord}>1) {
      $nfound++;
      printf "    WARNING $targcoord seems to be acquired $nobscoord{$targcoord} times, which may be inefficient\n"; 
      }
    }
  if ($nfound == 0) { printf "     No problems found\n"; }
  }
 

# print out the darks all these scripts might require
printf "\n\nCREATING DARK SCRIPT program.dark.cal WITH THE FOLLOWING DARKS\n";
open(OUTDARK,">program.dark.cal");

# the ARCHIVE BLOCK
printf OUTDARK  "[START_ARCHIVE_INFORMATION]\n";
printf OUTDARK  "    PARTNER                 = LBTB,INAF,AZ,OSURC,LBTO\n";
printf OUTDARK  "    PI_NAME                 = $piname\n";
printf OUTDARK  "    PROP_ID                 = CALIBRATION\n";
printf OUTDARK  "    [END_ARCHIVE_INFORMATION]\n\n";

# INSTRUMENT SET UP -- as blind as you can be.....
printf OUTDARK "[START_INSTRUMENT_SETUP]\n";
printf OUTDARK "    CAMERA                  = N1.8 \n";
printf OUTDARK "    FILTER                  = blind blind\n";  #blind filters
printf OUTDARK "    #GRATING_UNIT           = mirror\n";       #this is the default "resting" entry       
printf OUTDARK "    MASK                    = ID990031\n";     #blind mask
printf OUTDARK "    MASK_POSITION           = mask_in_fpu \n"; 
printf OUTDARK "    FLEXURE_COMP            = off  \n";
printf OUTDARK "    [END_INSTRUMENT_SETUP] \n\n";

# READOUT -- ONLY RELEVANT PARAMETER IS SAVE_MODE SINCE THE REST ARE SET BY THE DARK COMMAND
printf OUTDARK "[START_READOUT_SETUP]\n";
printf OUTDARK "    DIT               =4 \n";            
printf OUTDARK "    NDIT              =1   \n";
printf OUTDARK "    NEXPO             =5   \n";
printf OUTDARK "    ROE_MODE          = o2dcr  \n";      
printf OUTDARK "    SAVE_MODE         = normal \n";      
printf OUTDARK "    [END_READOUT_SETUP]\n\n";

# OBSERVING
# basically should not include flush_detector
printf OUTDARK "[START_OBSERVING_SETUP]\n";
foreach $darkname (sort(keys %darks)) {
  $darkname =~ /(.*[a-zA-Z])(\d*)/;
  $mode     = $1;
  $time     = $2;
  printf OUTDARK "    DARK = $mode $time 1 5\n";
  printf         "    DARK = $mode $time 1 5 for files $darkfile{$darkname}\n";
  }
printf OUTDARK "    [END_OBSERVING_SETUP]\n";
close(OUTDARK);


