#!/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
#  08jul24  MM    Updated for new CCD

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;  <-- Old ccd, some delta PA
$paref = 0.0;  #   <-- New ccd, no delta PA

# Position of tangent point minus position listed in header
$dra = -6.833e-4;
$ddec = 2.2374555e-3;

$solution[1]=[ ("","",
		"-0.00301843240873",
		"-0.00206020495664",
		"9.74349263983e-07",
		"1.05141484876e-08",
		"-4.30492020782e-09",
		"9.71134900369e-07",
		"-3.26134524769e-12",
		"-8.17885971135e-13",
		"-2.21410452653e-12",
		"-2.19665247191e-12",
		"-1.1569659946e-12",
		"-1.71816940436e-12",
		"7.7544933424e-16",
		"-1.10012615145e-16",
		"3.70903897451e-16",
		"3.06482462347e-16",
		"5.50181092485e-16",
		"2.04962533306e-16",
		"1.55967576097e-16",
		"3.09949063524e-16") ];

$solution[2]=[ ("","",
		"-0.000943228145562",
		"-0.002042067746",
		"9.68213504296e-07",
		"7.25453248515e-09",
		"-6.76482094157e-09",
		"9.6844039679e-07",
		"-2.3341806125e-13",
		"-4.95658015499e-13",
		"-6.85856302838e-13",
		"-4.93768219093e-13",
		"-1.81642592454e-13",
		"-1.09262082133e-12",
		"-3.78510607975e-18",
		"1.93243479622e-16",
		"1.11524397178e-16",
		"2.57644759469e-16",
		"2.21879176719e-16",
		"7.15804997727e-17",
		"4.80347792136e-17",
		"2.53978955535e-16") ];

$solution[3]=[ ("","",
		"0.0011287585215",
		"-0.00202979382461",
		"9.68780518691e-07",
		"5.89235284754e-09",
		"-7.55208635687e-09",
		"9.68291577522e-07",
		"8.01374912167e-13",
		"-5.06836943922e-13",
		"-6.43878337339e-13",
		"6.73520109222e-13",
		"3.13984723268e-13",
		"-1.15816990787e-12",
		"7.08160588262e-17",
		"-6.46484091867e-17",
		"-1.09014065339e-16",
		"3.08498663145e-16",
		"2.08625414145e-16",
		"-1.80795287313e-16",
		"-5.72047654003e-17",
		"3.73530693968e-16") ];


$solution[4]=[ ("","",
		"0.003206740851",
		"-0.00201860203898",
		"9.75855079841e-07",
		"2.82898056669e-09",
		"-1.02814789488e-08",
		"9.71311398074e-07",
		"3.40684469843e-12",
		"-9.65803326897e-13",
		"-1.79358208506e-12",
		"2.59629964135e-12",
		"1.09442108735e-12",
		"-1.66707278297e-12",
		"9.75881529504e-16",
		"2.69654858819e-16",
		"-2.49121098393e-16",
		"5.0199221921e-16",
		"4.04845542074e-16",
		"-3.01848377579e-16",
		"-5.6981577668e-17",
		"3.90360690231e-16") ];


$solution[5]=[ ("","",
		"-0.000971647683056",
		"0.00198551807709",
		"-9.68028452293e-07",
		"-6.85585951049e-09",
		"7.86366749894e-09",
		"-9.67960199945e-07",
		"-1.9477592899e-13",
		"3.88878327238e-13",
		"6.35596060061e-13",
		"-3.44658441565e-13",
		"-2.25250405432e-13",
		"9.80857784022e-13",
		"-1.80881481585e-16",
		"-1.59834914155e-16",
		"9.84689104286e-17",
		"-2.95582159076e-16",
		"-2.53766162558e-16",
		"1.50986988674e-16",
		"6.03572237402e-18",
		"-3.38733110468e-16") ];

$solution[6]=[ ("","",
		"-0.00304767257272",
		"0.00197411757911",
		"-9.73002836224e-07",
		"-4.45919601076e-09",
		"9.87217107951e-09",
		"-9.70083722068e-07",
		"-3.22286346555e-12",
		"1.19626627661e-12",
		"1.67513438981e-12",
		"-2.0263193394e-12",
		"-9.93283535415e-13",
		"1.54192789618e-12",
		"-1.35641300504e-15",
		"7.44102535307e-16",
		"4.30887154461e-16",
		"-5.37000585735e-16",
		"-3.95542150395e-16",
		"3.46835299555e-16",
		"8.25832368572e-17",
		"-4.25879811171e-16") ];

$solution[7]=[ ("","",
		"0.00317915446273",
		"0.00201838561678",
		"-9.75980998897e-07",
		"-1.07720286379e-08",
		"3.97881290466e-09",
		"-9.71617142555e-07",
		"3.49355450675e-12",
		"1.24663521631e-12",
		"1.62239598278e-12",
		"2.44314159833e-12",
		"1.00348166021e-12",
		"1.72445067057e-12",
		"-1.07245538356e-15",
		"8.92303608214e-17",
		"-8.78863471447e-17",
		"-6.17396742803e-16",
		"-4.60392253235e-16",
		"-4.01624327579e-16",
		"-2.05656291378e-17",
		"-3.93984114551e-16") ];

$solution[8]=[ ("","",
		"0.0011002487131",
		"0.00199916077428",
		"-9.68916036327e-07",
		"-8.20124486369e-09",
		"6.85437204725e-09",
		"-9.68497213526e-07",
		"7.22904566864e-13",
		"4.15154642547e-13",
		"7.25475464383e-13",
		"7.49890647243e-13",
		"2.62088978152e-13",
		"1.13079448455e-12",
		"8.47072432824e-17",
		"-4.04071942459e-17",
		"-8.74962528084e-17",
		"-2.58870823696e-16",
		"-2.24727406308e-16",
		"-1.4843989434e-16",
		"-3.62033514802e-17",
		"-3.51083245512e-16") ];

#
# 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;  
	$paoffset[$i] = $rotangle[$i] - 43.75 + $dpa + $paref;  ## Fixed by MM - Feb 9, 2009
	                                                       
	
    } 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);
}
