summaryrefslogtreecommitdiff
path: root/updatewcs/makewcs.py
diff options
context:
space:
mode:
Diffstat (limited to 'updatewcs/makewcs.py')
-rw-r--r--updatewcs/makewcs.py260
1 files changed, 260 insertions, 0 deletions
diff --git a/updatewcs/makewcs.py b/updatewcs/makewcs.py
new file mode 100644
index 0000000..451da13
--- /dev/null
+++ b/updatewcs/makewcs.py
@@ -0,0 +1,260 @@
+#from .. mappings import DEGTORAD, RADTODEG
+from hstwcs.mappings import DEGTORAD, RADTODEG
+import numpy
+from numpy import math
+from math import sin, sqrt, pow, cos, asin, atan2,pi
+
+class MakeWCS(object):
+ """
+ Purpose
+ =======
+ Recompute WCS keywords based on PA_V3 and distortion model.
+
+ Algorithm
+ =========
+ - update reference chip wcs
+ - update extension wcs
+ - update extension header
+
+ """
+
+ def __init__(self, owcs, refwcs):
+ self.wcsobj = owcs
+ self.refwcs = refwcs
+ self.updateWCS()
+ self.DOMakeWCS = 'COMPLETE'
+
+ def updateWCS(self):
+ """
+ recomputes the basic WCS kw
+ """
+ backup_wcs = self.wcsobj.restore()
+ ltvoff, offshift = self.getOffsets(backup_wcs)
+
+ self.uprefwcs(ltvoff, offshift)
+ self.upextwcs(ltvoff, offshift)
+ self.updatehdr()
+
+ def upextwcs(self, loff, offsh):
+ """
+ updates an extension wcs
+ """
+ ltvoffx, ltvoffy = loff[0], loff[1]
+ offshiftx, offshifty = offsh[0], offsh[1]
+ backup_wcs = self.wcsobj.restore()
+ ltv1 = self.wcsobj.ltv1
+ ltv2 = self.wcsobj.ltv2
+
+
+
+ if ltv1 != 0. or ltv2 != 0.:
+ offsetx = backup_wcs['CRPIX1'] - ltv1 - self.wcsobj.idcmodel.refpix['XREF']
+ offsety = backup_wcs['CRPIX2'] - ltv2 - self.wcsobj.idcmodel.refpix['YREF']
+ fx,fy = self.wcsobj.idcmodel.shift(self.wcsobj.idcmodel.cx,self.wcsobj.idcmodel.cy,offsetx,offsety)
+ else:
+ fx, fy = self.wcsobj.idcmodel.cx, self.wcsobj.idcmodel.cy
+
+ ridcmodel = self.refwcs.idcmodel
+ v2 = self.wcsobj.idcmodel.refpix['V2REF']
+ v3 = self.wcsobj.idcmodel.refpix['V3REF']
+ v2ref = self.refwcs.idcmodel.refpix['V2REF']
+
+ v3ref = self.refwcs.idcmodel.refpix['V3REF']
+ R_scale = self.refwcs.idcmodel.refpix['PSCALE']/3600.0
+ off = sqrt((v2-v2ref)**2 + (v3-v3ref)**2)/(R_scale*3600.0)
+
+ # Here we must include the PARITY
+ if v3 == v3ref:
+ theta=0.0
+ else:
+ theta = atan2(self.wcsobj.parity[0][0]*(v2-v2ref),self.wcsobj.parity[1][1]*(v3-v3ref))
+
+ if self.refwcs.idcmodel.refpix['THETA']: theta += self.refwcs.idcmodel.refpix['THETA']*pi/180.0
+
+ dX=(off*sin(theta)) + offshiftx
+ dY=(off*cos(theta)) + offshifty
+ px = numpy.array([[dX,dY]])
+ newcrval = self.refwcs.pixel2world_fits(px)[0]
+ newcrpix = numpy.array([self.wcsobj.idcmodel.refpix['XREF'] + ltvoffx,
+ self.wcsobj.idcmodel.refpix['YREF'] + ltvoffy])
+
+ self.wcsobj.crval = newcrval
+ self.wcsobj.crpix = newcrpix
+
+ # Account for subarray offset
+ # Angle of chip relative to chip
+ if self.wcsobj.idcmodel.refpix['THETA']:
+ dtheta = self.wcsobj.idcmodel.refpix['THETA'] - self.refwcs.idcmodel.refpix['THETA']
+ else:
+ dtheta = 0.0
+
+
+ # Create a small vector, in reference image pixel scale
+ # There is no parity effect here ???
+
+ delXX=fx[1,1]/R_scale/3600.
+ delYX=fy[1,1]/R_scale/3600.
+ delXY=fx[1,0]/R_scale/3600.
+ delYY=fy[1,0]/R_scale/3600.
+
+
+ # Convert to radians
+ rr=dtheta*pi/180.0
+
+ # Rotate the vectors
+ dXX = cos(rr)*delXX - sin(rr)*delYX
+ dYX = sin(rr)*delXX + cos(rr)*delYX
+
+ dXY = cos(rr)*delXY - sin(rr)*delYY
+ dYY = sin(rr)*delXY + cos(rr)*delYY
+ px = numpy.array([[dX+dXX,dY+dYX]])
+
+ # Transform to sky coordinates
+
+ wc = self.refwcs.pixel2world_fits(px)
+
+ a = wc[0,0]
+ b = wc[0,1]
+ px = numpy.array([[dX+dXY,dY+dYY]])
+
+ wc = self.refwcs.pixel2world_fits(px)
+ c = wc[0,0]
+ d = wc[0,1]
+
+ # Calculate the new CDs and convert to degrees
+ cd11 = diff_angles(a,newcrval[0])*cos(newcrval[1]*pi/180.0)
+ cd12 = diff_angles(c,newcrval[0])*cos(newcrval[1]*pi/180.0)
+ cd21 = diff_angles(b,newcrval[1])
+ cd22 = diff_angles(d,newcrval[1])
+
+ cd = numpy.array([[cd11, cd12], [cd21, cd22]])
+ self.wcsobj.cd = cd
+
+ def uprefwcs(self, loff, offsh):
+ """
+ Updates the reference chip
+ """
+ ltvoffx, ltvoffy = loff[0], loff[1]
+ offshift = offsh
+ backup_refwcs = self.refwcs.restore()
+ dec = backup_refwcs['CRVAL2']
+
+ # Get an approximate reference position on the sky
+ rref = numpy.array([[self.refwcs.idcmodel.refpix['XREF']+ltvoffx,
+ self.refwcs.idcmodel.refpix['YREF']+ltvoffy]])
+
+
+ crval = self.refwcs.pixel2world_fits(rref)
+
+ # Convert the PA_V3 orientation to the orientation at the aperture
+ # This is for the reference chip only - we use this for the
+ # reference tangent plane definition
+ # It has the same orientation as the reference chip
+ pv = troll(self.wcsobj.pav3,dec,self.refwcs.idcmodel.refpix['V2REF'],
+ self.refwcs.idcmodel.refpix['V3REF'])
+ # Add the chip rotation angle
+ if self.refwcs.idcmodel.refpix['THETA']:
+ pv += self.refwcs.idcmodel.refpix['THETA']
+
+
+ # Set values for the rest of the reference WCS
+ self.refwcs.crval = crval[0]
+ self.refwcs.crpix = numpy.array([0.0,0.0])+offsh
+ parity = self.refwcs.parity
+ R_scale = self.refwcs.idcmodel.refpix['PSCALE']/3600.0
+ cd11 = parity[0][0] * cos(pv*pi/180.0)*R_scale
+ cd12 = parity[0][0] * -sin(pv*pi/180.0)*R_scale
+ cd21 = parity[1][1] * sin(pv*pi/180.0)*R_scale
+ cd22 = parity[1][1] * cos(pv*pi/180.0)*R_scale
+ rcd = numpy.array([[cd11, cd12], [cd21, cd22]])
+ self.refwcs.cd = rcd
+ self.refwcs.set()
+
+
+
+ def updatehdr(self):
+ """
+ Keywords to update:
+ CD1_1, CD1_2, CD2_1, CD2_2, CRPIX1, CRPIX2, CRVAL1, CRVAL2
+ """
+
+ self.wcsobj.hdr.update('CD1_1', self.wcsobj.cd[0,0])
+ self.wcsobj.hdr.update('CD1_2', self.wcsobj.cd[0,1])
+ self.wcsobj.hdr.update('CD2_1', self.wcsobj.cd[1,0])
+ self.wcsobj.hdr.update('CD2_2', self.wcsobj.cd[1,1])
+ self.wcsobj.hdr.update('CRVAL1', self.wcsobj.crval[0])
+ self.wcsobj.hdr.update('CRVAL2', self.wcsobj.crval[1])
+ self.wcsobj.hdr.update('CRPIX1', self.wcsobj.crpix[0])
+ self.wcsobj.hdr.update('CRPIX2', self.wcsobj.crpix[1])
+ self.wcsobj.hdr.update('ORIENTAT', self.wcsobj.orientat)
+
+ def getOffsets(self, backup_wcs):
+ ltv1 = self.wcsobj.ltv1
+ ltv2 = self.wcsobj.ltv2
+
+ offsetx = backup_wcs['CRPIX1'] - ltv1 - self.wcsobj.idcmodel.refpix['XREF']
+ offsety = backup_wcs['CRPIX2'] - ltv2 - self.wcsobj.idcmodel.refpix['YREF']
+
+ shiftx = self.wcsobj.idcmodel.refpix['XREF'] + ltv1
+ shifty = self.wcsobj.idcmodel.refpix['YREF'] + ltv2
+ if ltv1 != 0. or ltv2 != 0.:
+ ltvoffx = ltv1 + offsetx
+ ltvoffy = ltv2 + offsety
+ offshiftx = offsetx + shiftx
+ offshifty = offsety + shifty
+ else:
+ ltvoffx = 0.
+ ltvoffy = 0.
+ offshiftx = 0.
+ offshifty = 0.
+
+ ltvoff = numpy.array([ltvoffx, ltvoffy])
+ offshift = numpy.array([offshiftx, offshifty])
+ return ltvoff, offshift
+
+def diff_angles(a,b):
+ """
+ Perform angle subtraction a-b taking into account
+ small-angle differences across 360degree line.
+ """
+
+ diff = a - b
+
+ if diff > 180.0:
+ diff -= 360.0
+
+ if diff < -180.0:
+ diff += 360.0
+
+ return diff
+
+def troll(roll, dec, v2, v3):
+ """ Computes the roll angle at the target position based on:
+ the roll angle at the V1 axis(roll),
+ the dec of the target(dec), and
+ the V2/V3 position of the aperture (v2,v3) in arcseconds.
+
+ Based on the algorithm provided by Colin Cox that is used in
+ Generic Conversion at STScI.
+ """
+ # Convert all angles to radians
+ _roll = DEGTORAD(roll)
+ _dec = DEGTORAD(dec)
+ _v2 = DEGTORAD(v2 / 3600.)
+ _v3 = DEGTORAD(v3 / 3600.)
+
+ # compute components
+ sin_rho = sqrt((pow(sin(_v2),2)+pow(sin(_v3),2)) - (pow(sin(_v2),2)*pow(sin(_v3),2)))
+ rho = asin(sin_rho)
+ beta = asin(sin(_v3)/sin_rho)
+ if _v2 < 0: beta = pi - beta
+ gamma = asin(sin(_v2)/sin_rho)
+ if _v3 < 0: gamma = pi - gamma
+ A = pi/2. + _roll - beta
+ B = atan2( sin(A)*cos(_dec), (sin(_dec)*sin_rho - cos(_dec)*cos(rho)*cos(A)))
+
+ # compute final value
+ troll = RADTODEG(pi - (gamma+B))
+
+ return troll
+