#!/usr/bin/perl

# Assign a WCS to an IMACS exposure.

# History
#  07sep03  DSNR  created
#  08mar02  DSNR  generalized
#  08mar05  DSNR  added rotation parameter
#  08mar12  DSNR  worked out kinks

#  08jun11  MM    corrected PA, corrected WCS calculation for dec
#  08jun23  MM    added help switch

use Getopt::Long;
use constant PI => 4*atan2(1,1);

@usage = ("v1.2  MM  17June2008\n\nUsage: imacswcs File [--list=FileList --dpa=Angle --norot] \n\n");
@help = (
         "\t---------------\n ",
         "\tREQUIRED INPUTS\n ",
         "\t---------------\n ",
         "\tFile is the image base name, minus the chip ID and .fits extension\n",
	 "\t(e.g. ssfccd0001). To run in batch mode, specify a list of sky-\n",
	 "\tsubtracted, flat-fielded files of the form: \n\n",
         "\tssfccd0001\n",
	 "\tssfccd0002\n",
	 "\tssfccd0003\n",
	 "\t...\n\n",
         "\t------\n ",
         "\tOUTPUT\n ",
         "\t------\n ",
         "\tThe output is an IRAF CL script named with the convention:\n",
	 "\t'imacswcs_<filename/list>.cl'; run this file at the cl> prompt\n",
         "\twith the following syntax: cl> cl < imacswcs_<filename/list>.cl\n",
	 "\t--------\n",
	 "\tSWITCHES\n",
	 "\t--------\n",
	 "\t--list=<char> : Provide list of input files.\n",
	 "\t--dpa=<angle> : Offset computed position angles by some amount.\n",
	 "\t                (default dpa=0.0)\n",
	 "\t--norot       : No rotation of the default solution is performed.\n",
         );

if ($#ARGV lt 1) {
    if ($#ARGV eq 0) {
	if ($ARGV[0] eq "--help"){
	    print @usage;
	    print @help;
	    print "\n\n";
	    exit;
	}
    } else {
	die "@usage\ For detailed help: imacsreg --help\n\n";
    }
}

#######################################
#
# Distortion mapping from Dan Kelson
# Computed from Palomar 5 observation
# Referenced to exposure 0693 from March 2007 MMTF run
# Solution housed in 8x20 element array of arrays
#  8 chips
#  18 polynomial coefficients (first two array elements are historical dummies):
#    a00,b00
#    a10,b10
#    a01,b01
#    a20,b20
#    a11,b11
#    a02,b02
#    a30,b30
#    a21,b21
#    a12,b12
#    a03,b03
#
#  Solution is
#    RA(x,y) =  RA,c + a00 + a10(x-xc) + a01(y-yc) + a11x'y' + a20x'^2 + ...
#   dec(x,y) = dec,c + b00 + b10 ...
#
#  where RA,c and dec,c are the tangent points
#        x and y are the chip coordinates for each chip
#        xc and yc are 1024 and 2048
#        x' = (x-xc), y' = (y-yc)
#
# In IRAF parlance, this is a CCMAP solution with
#    xxterms = "half", xyterms = "half"
#    xxorder = 4, xyorder = 4, yxorder = 4, yyorder = 4
#

# Reference image parameters
# Position angle, in degrees E of N
$paref = 270.09;
# Position of tangent point minus position listed in header
$dra = -6.833e-4;
$ddec = 2.2374555e-3;

$solution[1]=[ ("","",
		"-0.00201908221",
		"0.00300652948",
		"8.02410618e-09",
		"-9.82431076e-07",
		"9.792697e-07",
		"2.16185984e-09",
		"-7.99845802e-13",
		"3.24282275e-12",
		"-1.8800642e-12",
		"2.01984887e-12",
		"-1.4964422e-12",
		"8.82751511e-13",
		"3.92561527e-16",
		"-5.84228131e-16",
		"2.832385e-16",
		"-4.83219319e-16",
		"1.1853397e-16",
		"-4.80758197e-16",
		"2.58836016e-16",
		"-3.12726682e-17") ];

$solution[2]=[ ("","",
		"-0.00199850441",
		"0.000953942965",
		"5.39818381e-09",
		"-9.75728006e-07",
		"9.76671675e-07",
		"4.17962268e-09",
		"-4.64748186e-13",
		"6.43092355e-13",
		"-6.17982102e-13",
		"7.85400347e-13",
		"-1.23022481e-12",
		"2.08343674e-13",
		"1.08581081e-17",
		"-3.31011434e-16",
		"1.42285e-16",
		"-1.26395819e-16",
		"1.36818852e-16",
		"-2.99647215e-16",
		"3.08321246e-16",
		"-3.00104633e-17") ];

$solution[3]=[ ("","",
		"-0.00198551326",
		"-0.00109719987",
		"5.39232555e-09",
		"-9.7530686e-07",
		"9.76283547e-07",
		"6.64801332e-09",
		"-2.55659712e-13",
		"-4.81503404e-13",
		"5.13554534e-13",
		"8.24654412e-13",
		"-1.22443191e-12",
		"-3.15799996e-13",
		"-7.51795689e-17",
		"-4.34717946e-16",
		"2.03685965e-16",
		"1.70230517e-16",
		"-1.51407256e-16",
		"-2.59310857e-16",
		"3.277636e-16",
		"3.26339603e-17") ];

$solution[4]=[ ("","",
		"-0.00197230335",
		"-0.00314925467",
		"3.20189778e-09",
		"-9.81403836e-07",
		"9.78561439e-07",
		"9.54500807e-09",
		"-7.79266001e-13",
		"-3.16254032e-12",
		"1.89585588e-12",
		"2.17323917e-12",
		"-1.73364583e-12",
		"-1.14040234e-12",
		"-9.77973805e-17",
		"-7.41350755e-16",
		"4.08710481e-16",
		"4.0993581e-16",
		"-2.10511351e-16",
		"-6.31104779e-16",
		"3.81115565e-16",
		"1.14661735e-16") ];

$solution[5]=[ ("","",
		"0.00204187503",
		"0.000977523239",
		"-4.41247867e-09",
		"9.76160358e-07",
		"-9.77274179e-07",
		"-6.44210407e-09",
		"4.54963701e-13",
		"6.11180758e-13",
		"-7.3488321e-13",
		"-1.01642229e-12",
		"1.5211763e-12",
		"2.62436435e-13",
		"1.05476262e-16",
		"3.30579602e-16",
		"-3.25185478e-16",
		"-4.58097793e-17",
		"1.40686795e-16",
		"2.33355575e-16",
		"-3.99530343e-16",
		"-8.18044242e-19") ];

$solution[6]=[ ("","",
		"0.00201334402",
		"0.00303252102",
		"-9.16131904e-10",
		"9.83246265e-07",
		"-9.80317797e-07",
		"-8.8270229e-09",
		"9.15197041e-13",
		"3.34551592e-12",
		"-2.36242356e-12",
		"-2.22557593e-12",
		"1.98953457e-12",
		"1.08054036e-12",
		"-2.31524962e-16",
		"3.77405112e-16",
		"-4.75385233e-16",
		"-3.7807315e-16",
		"3.13092302e-16",
		"4.481232e-16",
		"-3.43278928e-16",
		"-6.38922542e-17") ];

$solution[7]=[ ("","",
		"0.00208034876",
		"-0.00312588623",
		"-7.47188548e-09",
		"9.82294554e-07",
		"-9.79886592e-07",
		"-5.21757679e-10",
		"6.79016413e-13",
		"-2.77750898e-12",
		"1.73239523e-12",
		"-1.77440888e-12",
		"1.94592978e-12",
		"-7.89198604e-13",
		"-3.31667532e-16",
		"6.14949708e-17",
		"-1.75403226e-16",
		"2.37961936e-16",
		"-1.2602296e-16",
		"2.03732466e-16",
		"-3.65430571e-16",
		"-5.52775747e-17") ];

$solution[8]=[ ("","",
		"0.00206132409",
		"-0.00107103828",
		"-6.17325544e-09",
		"9.75651576e-07",
		"-9.77221879e-07",
		"-5.14783417e-09",
		"6.2540871e-13",
		"-5.80498473e-13",
		"4.94811445e-13",
		"-1.03943082e-12",
		"1.54375785e-12",
		"-2.7958468e-13",
		"-5.00843574e-17",
		"5.70341829e-16",
		"-3.14011963e-16",
		"1.71542594e-16",
		"-1.8129298e-16",
		"2.50047628e-16",
		"-3.58228087e-16",
		"6.25202207e-17") ];

#
# End of distortion mapping
#
#######################################


##############
# INITIALIZE #
##############

#
# Filenames
#
$list = '';
$dpa = 0;
$norot = '';
GetOptions ('list=s' => \$list,
	    'dpa=f' => \$dpa,
	    'norot' => \$norot);
$files[0]=$ARGV[0];
if ($list ne '') {
    open IN, $list;
    chomp(@files = <IN>);
    close IN;
}
#
# Get RA, dec, and Nasmyth rotator offset angle
#
for $i (0..$#files) {
    open IN, "$files[$i]c1.fits";
    chomp(@in = <IN>);
    # Get header information
    # This is not elegant, but elegant would require CFITSIO libraries
    foreach (@in) {
	# Check for header line
	if (/SIMPLE/) {
	    # Parse header line
	    @line = parse($_);
	    for $j (0..$#line) {
		# Get RA and dec in decimal degree format
		if ($line[$j] =~ /RA-D/) {$RAc[$i] = $line[$j+2]}
		if ($line[$j] =~ /DEC-D/) {$decc[$i] = $line[$j+2]}
		if ($line[$j] =~ /ROTANGLE/) {$rotangle[$i] = $line[$j+1]}
	    }
	}
    }
    close IN;	
    # Compute position angle from rotation angle
    # (see http://www.lco.cl/telescopes-information/magellan/instruments-1/observing-catalogs-1)
    if ($norot eq '') {       

	$skypa[$i] = $rotangle[$i] + $dpa + 46.15;
	$paoffset[$i] = $skypa[$i] - $paref;

	# From Dave Osip, added by MM

	# IROA_d = -46.25
	# north = 0.0 - (rotange - (90.0 + IROA_d)) + 0.0
	# ==> north = -rotangle + 90 + IROA_d
	# ==> north = -rotangle +43.75

	$paoffset[$i] = 43.75 - $rotangle[$i] + $dpa + $paref;  
	                                                       
	
    } else {
	$skypa[$i] = $paref;
	$paoffset[$i] = 0;
    }
    $cospa[$i] = cos($paoffset[$i]*PI/180);
    $sinpa[$i] = sin($paoffset[$i]*PI/180);
    # Correct for reference image parameters
    print,$RAc[$i],$decc[$i];
    $RAc[$i] += $dra;
    $decc[$i] += $ddec;
    print,$RAc[$i],$decc[$i];
    # Compute RA and dec in hms and dms formats
    $RAcsex[$i] = deg2sex($RAc[$i],"ra");
    $deccsex[$i] = deg2sex($decc[$i],"dec");
}
#
# Increment for pixel grid
#
$dpix=10;
#
# 8 IMACS chips
#
@chips=qw(1 2 3 4 5 6 7 8);

########
# MAIN #
########

#
# Cycle thru files
#
@outcl=();
for $m (0..$#files) {
    #
    print "Running base image $files[$m]:\n";
    $goodfile = 1;
    $n = 0;
    #
    # Check for presence of file
    #
    while ($goodfile && $n le 7) {
	$n+=1;
	$cfile = "$files[$m]c$n";
	unless (-e "$cfile.fits") {
	    print "IMACSWCS: $cfile.fits does not exist; ignoring this base name.\n";
	    $goodfile = 0;
	}
    }
    if ($goodfile) {
	#
	# print basic info
	#
	print "\tRA (tangent point) = $RAcsex[$m]\n";
	print "\tdec (tangent point) = $deccsex[$m]\n";
	printf("\t%s%7.2f\n","Sky PA (degrees E of N) = ",$skypa[$m]);

	# Compute mapping from [x,y] to [RA,dec] for each chip

	# We use a (mostly) regular grid.  Start with the first row
	# and column, and increment by $dpix each time.  However, make
	# sure to include the last row and column (even if they don't
	# fall on an integer multiple of the increment).

	# Cycle thru chips

	for (@chips) {
	    #
	    print "\tComputing [x,y] --> [RA,dec] mapping for chip $_.\n";
	    #
	    # Cycle through rows and columns.  Make sure to:
	    #  (a) convert coefficients from radians to degrees
	    #  (b) print 10 significant digits in coordinates
	    #
	    @out=();
	    for ($i=1; $i<2048; $i+=$dpix) {
		$x=$i-1024;
		for ($j=1; $j<4096; $j+=$dpix) {
		    $y=$j-2048;
		    $delta_ra =($solution[$_][2]+$solution[$_][4]*$x+$solution[$_][6]*$y+$solution[$_][8]*$x*$x+$solution[$_][10]*$x*$y+$solution[$_][12]*$y*$y+$solution[$_][14]*$x*$x*$x+$solution[$_][16]*$x*$x*$y+$solution[$_][18]*$x*$y*$y+$solution[$_][20]*$y*$y*$y);
		    $delta_dec=$solution[$_][3]+$solution[$_][5]*$x+$solution[$_][7]*$y+$solution[$_][9]*$x*$x+$solution[$_][11]*$x*$y+$solution[$_][13]*$y*$y+$solution[$_][15]*$x*$x*$x+$solution[$_][17]*$x*$x*$y+$solution[$_][19]*$x*$y*$y+$solution[$_][21]*$y*$y*$y;
#	    $RA =$RAc +$delta_ra *180/PI;
#	    $dec=$decc+$delta_dec*180/PI;
		    $RA =$RAc[$m] +( $cospa[$m]*$delta_ra+$sinpa[$m]*$delta_dec)/cos($decc[$m]*PI/180)*180/PI; # MM added 06/13
		    $dec=$decc[$m]+(-$sinpa[$m]*$delta_ra+$cospa[$m]*$delta_dec)*180/PI;
		    push @out, sprintf("%6d%6d%16.10f%16.10f\n",$x+1024,$y+2048,$RA,$dec);
		}
		# the last row
		$y=2048;
		$delta_ra =($solution[$_][2]+$solution[$_][4]*$x+$solution[$_][6]*$y+$solution[$_][8]*$x*$x+$solution[$_][10]*$x*$y+$solution[$_][12]*$y*$y+$solution[$_][14]*$x*$x*$x+$solution[$_][16]*$x*$x*$y+$solution[$_][18]*$x*$y*$y+$solution[$_][20]*$y*$y*$y);
		$delta_dec=$solution[$_][3]+$solution[$_][5]*$x+$solution[$_][7]*$y+$solution[$_][9]*$x*$x+$solution[$_][11]*$x*$y+$solution[$_][13]*$y*$y+$solution[$_][15]*$x*$x*$x+$solution[$_][17]*$x*$x*$y+$solution[$_][19]*$x*$y*$y+$solution[$_][21]*$y*$y*$y;
		$RA =$RAc[$m] +( $cospa[$m]*$delta_ra+$sinpa[$m]*$delta_dec)/cos($decc[$m]*PI/180)*180/PI; # MM added 06/13
		$dec=$decc[$m]+(-$sinpa[$m]*$delta_ra+$cospa[$m]*$delta_dec)*180/PI;
		push @out, sprintf("%6d%6d%16.10f%16.10f\n",$x+1024,$y+2048,$RA,$dec);
	    }
	    # the last column
	    $x=1024;
	    for ($j=1; $j<4096; $j+=$dpix) {
		$y=$j-2048;
		$delta_ra =($solution[$_][2]+$solution[$_][4]*$x+$solution[$_][6]*$y+$solution[$_][8]*$x*$x+$solution[$_][10]*$x*$y+$solution[$_][12]*$y*$y+$solution[$_][14]*$x*$x*$x+$solution[$_][16]*$x*$x*$y+$solution[$_][18]*$x*$y*$y+$solution[$_][20]*$y*$y*$y);
		$delta_dec=$solution[$_][3]+$solution[$_][5]*$x+$solution[$_][7]*$y+$solution[$_][9]*$x*$x+$solution[$_][11]*$x*$y+$solution[$_][13]*$y*$y+$solution[$_][15]*$x*$x*$x+$solution[$_][17]*$x*$x*$y+$solution[$_][19]*$x*$y*$y+$solution[$_][21]*$y*$y*$y;
		$RA =$RAc[$m] +( $cospa[$m]*$delta_ra+$sinpa[$m]*$delta_dec)/cos($decc[$m]*PI/180)*180/PI; # MM added 06/13
		$dec=$decc[$m]+(-$sinpa[$m]*$delta_ra+$cospa[$m]*$delta_dec)*180/PI;
		push @out, sprintf("%6d%6d%16.10f%16.10f\n",$x+1024,$y+2048,$RA,$dec);
	    }
	    # the last column and row
	    $y=2048;
	    $delta_ra =($solution[$_][2]+$solution[$_][4]*$x+$solution[$_][6]*$y+$solution[$_][8]*$x*$x+$solution[$_][10]*$x*$y+$solution[$_][12]*$y*$y+$solution[$_][14]*$x*$x*$x+$solution[$_][16]*$x*$x*$y+$solution[$_][18]*$x*$y*$y+$solution[$_][20]*$y*$y*$y);
	    $delta_dec=$solution[$_][3]+$solution[$_][5]*$x+$solution[$_][7]*$y+$solution[$_][9]*$x*$x+$solution[$_][11]*$x*$y+$solution[$_][13]*$y*$y+$solution[$_][15]*$x*$x*$x+$solution[$_][17]*$x*$x*$y+$solution[$_][19]*$x*$y*$y+$solution[$_][21]*$y*$y*$y;
	    $RA =$RAc[$m] +( $cospa[$m]*$delta_ra+$sinpa[$m]*$delta_dec)/cos($decc[$m]*PI/180)*180/PI; # MM added 06/13
	    $dec=$decc[$m]+(-$sinpa[$m]*$delta_ra+$cospa[$m]*$delta_dec)*180/PI;
	    push @out, sprintf("%6d%6d%16.10f%16.10f\n",$x+1024,$y+2048,$RA,$dec);
	    #
	    # Print pixel/coordinate pairs to a file
	    #
	    $cfile = "$files[$m]c$_";
	    $outfile = "$cfile.ccmap.in";
	    print "\tWriting chip $_ solution to $outfile.\n";
	    open OUT, ">$outfile";
	    print OUT @out;
	    close OUT;
            #
	    # Write IRAF commands to a script
	    #
	    push @outcl, "ccmap $outfile database/$files[$m].ccmap solutions=chip$_ images=$cfile.fits lngunits=degrees latunits=degrees refpoint=user lngref=$RAc[$m] latref=$decc[$m] lngrefunits=degrees latrefunits=degrees projection=tnx xxorder=4 xyorder=4 yxorder=4 yyorder=4 xxterms=half yxterms=half pixsystem=physical update+ inter-\n";
	}
    }
}

# Check for database directory
unless (-e "database") {print "IMACSWCS: Need to create directory \"database\" in which ccmap files can reside, before running IRAF script.\n"}

#
# Print IRAF script to file
#
if ($#files ne 0) {$outscript = "imacswcs_$list.cl"} else {$outscript = "imacswcs_$files[0].cl"}
open OUT, ">$outscript";
print OUT @outcl;
close OUT;
print "\nNow in IRAF:\ncl> cl < $outscript\n\n";


###############
# SUBROUTINES #
###############

########################################
# $string = DEG2SEX($number,"ra" or "dec")
# Converts coordinates in degree format to coordinates 
#   in sexagesimal (HMS or DMS) format.
# Specify "ra" or "dec".
# Returns a string.
# NOTE: Assumes 0.1s accuracy for RA and 1" accuracy for DEC.  Not tested on negative RA/DEC.
# History
#  19oct05  DSR  created
#  19jun07  DSNR modified for higher precision
############################

sub deg2sex {
    my $deg = $_[0];
    my $radec = $_[1];
    if ($radec eq "ra") {$deg /= 15}
    my $dsex = rounddown($deg,0);
    $deg -= $dsex;
    $deg *= 60;
    my $msex = rounddown($deg,0);
    $deg -= $msex;
    $deg *= 60;
    my $asex = $deg;
    # This part converts to strings and pads with zeros when necessary.
    if ($dsex < 10) {$dsex = sprintf("0%0d",$dsex)} else {$dsex = sprintf("%0d",$dsex)}
    if ($msex < 10) {$msex = sprintf("0%0d",$msex)} else {$msex = sprintf("%0d",$msex)}
    if ($radec eq "ra") {
	if ($asex < 10) {$asex = sprintf("0%0.3f",$asex)} else {$asex = sprintf("%0.3f",$asex)}
    } else {
	if ($asex < 10) {$asex = sprintf("0%0.2f",$asex)} else {$asex = sprintf("%0.2f",$asex)}
    }
    my $sex = "$dsex:$msex:$asex";
    return $sex;
}

#######################
# @array = PARSE($line)
# Parse line of data into an array
# Assumes data separator is whitespace
# ??/??/02  DSR  created
# 09/04/02  DSR  changed "if" statement to "while"
# 08/04/03  DSR  removed $line variable
#######################

sub parse {
  my(@parsed);
  @parsed = split(/\s+/,$_[0]);
  chomp(@parsed);
  #remove whitespace at beginning of line, if any
  while ($parsed[0] eq "") {shift(@parsed)}
  return(@parsed);
}

##########################################
# $rounded_no = ROUNDDOWN($number,$sigdig)
# Round $number downward to $sigdig digits after the decimal place.
# If $sigdig is negative, rounds above the decimal place.
# E.g. Rounddown(5.675,2) => 5.67
#      Rounddown(55,-1) => 50
# History
#  08/04/03  DSR  created
##########################################

sub rounddown {
  use POSIX;
  my($number,$sigdig);
  $number = $_[0];
  $sigdig = 10**$_[1];
  $number *= $sigdig;
  $number = floor($number);
  $number /= $sigdig;
  return($number);
}
