2018-05-06 08:45:51 -06:00
|
|
|
#!/usr/bin/env python
|
|
|
|
from __future__ import print_function
|
|
|
|
import os
|
|
|
|
import numpy as np
|
|
|
|
import astropy.units as u
|
|
|
|
from astropy.io import fits
|
|
|
|
from astropy import wcs
|
|
|
|
from astropy.coordinates import SkyCoord, FK5, ICRS
|
|
|
|
from astropy.time import Time
|
|
|
|
from scipy import optimize
|
|
|
|
|
|
|
|
|
|
|
|
# Class for the Tycho 2 catalog
|
|
|
|
class tycho2_catalog:
|
|
|
|
"""Tycho2 catalog"""
|
|
|
|
|
|
|
|
def __init__(self, maxmag=9.0):
|
|
|
|
hdu = fits.open(os.path.join(os.getenv("ST_DATADIR"),
|
|
|
|
"data/tyc2.fits"))
|
|
|
|
|
|
|
|
ra = hdu[1].data.field('RA')*u.deg
|
|
|
|
dec = hdu[1].data.field('DEC')*u.deg
|
|
|
|
mag = hdu[1].data.field('MAG_VT')
|
|
|
|
|
|
|
|
c = mag < maxmag
|
|
|
|
self.ra = ra[c]
|
|
|
|
self.dec = dec[c]
|
|
|
|
self.mag = mag[c]
|
|
|
|
|
|
|
|
|
|
|
|
# Estimate the WCS from a reference file
|
|
|
|
def estimate_wcs_from_reference(ref, fname):
|
|
|
|
# Read header of reference
|
|
|
|
hdu = fits.open(ref)
|
|
|
|
hdu[0].header["NAXIS"] = 2
|
|
|
|
w = wcs.WCS(hdu[0].header)
|
|
|
|
|
|
|
|
# Get time and position from reference
|
|
|
|
tref = Time(hdu[0].header["MJD-OBS"], format="mjd", scale="utc")
|
|
|
|
pref = SkyCoord(ra=w.wcs.crval[0],
|
|
|
|
dec=w.wcs.crval[1],
|
|
|
|
unit="deg",
|
|
|
|
frame="icrs").transform_to(FK5(equinox=tref))
|
|
|
|
|
|
|
|
# Read time from target
|
|
|
|
hdu = fits.open(fname)
|
|
|
|
t = Time(hdu[0].header["MJD-OBS"], format="mjd", scale="utc")
|
|
|
|
|
|
|
|
# Correct wcs
|
|
|
|
dra = (t.sidereal_time("mean", "greenwich")
|
|
|
|
- tref.sidereal_time("mean", "greenwich"))
|
|
|
|
p = FK5(ra=pref.ra+dra, dec=pref.dec, equinox=t).transform_to(ICRS)
|
|
|
|
w.wcs.crval = np.array([p.ra.degree, p.dec.degree])
|
|
|
|
|
|
|
|
return w
|
|
|
|
|
|
|
|
|
|
|
|
# Match the astrometry and pixel catalog
|
|
|
|
def match_catalogs(ast_catalog, pix_catalog, w, rmin):
|
|
|
|
# Select stars towards pointing center
|
|
|
|
ra, dec = w.wcs.crval*u.deg
|
|
|
|
d = np.arccos(np.sin(dec)
|
|
|
|
* np.sin(ast_catalog.dec)
|
|
|
|
+ np.cos(dec)
|
|
|
|
* np.cos(ast_catalog.dec)
|
|
|
|
* np.cos(ra-ast_catalog.ra))
|
|
|
|
c = (d < 30.0*u.deg)
|
|
|
|
ra, dec, mag = ast_catalog.ra[c], ast_catalog.dec[c], ast_catalog.mag[c]
|
|
|
|
|
|
|
|
# Convert RA/Dec to pixels
|
|
|
|
pix = w.wcs_world2pix(np.stack((ra, dec), axis=-1), 0)
|
|
|
|
xs, ys = pix[:, 0], pix[:, 1]
|
|
|
|
|
|
|
|
# Loop over stars
|
|
|
|
nmatch = 0
|
|
|
|
for i in range(len(pix_catalog.x)):
|
|
|
|
dx = xs-pix_catalog.x[i]
|
|
|
|
dy = ys-pix_catalog.y[i]
|
|
|
|
r = np.sqrt(dx*dx+dy*dy)
|
|
|
|
if np.min(r) < rmin:
|
|
|
|
j = np.argmin(r)
|
|
|
|
pix_catalog.ra[i] = ra[j].value
|
|
|
|
pix_catalog.dec[i] = dec[j].value
|
|
|
|
pix_catalog.imag[i] = mag[j]
|
|
|
|
pix_catalog.flag[i] = 1
|
|
|
|
nmatch += 1
|
|
|
|
|
|
|
|
return nmatch
|
|
|
|
|
|
|
|
|
|
|
|
# Residual function
|
|
|
|
def residual(a, x, y, z):
|
|
|
|
return z-(a[0]+a[1]*x+a[2]*y)
|
|
|
|
|
|
|
|
|
|
|
|
# Fit transformation
|
|
|
|
def fit_wcs(w, pix_catalog):
|
|
|
|
x0, y0 = w.wcs.crpix
|
|
|
|
ra0, dec0 = w.wcs.crval
|
|
|
|
dx, dy = pix_catalog.x-x0, pix_catalog.y-y0
|
|
|
|
|
|
|
|
# Iterate to remove outliers
|
|
|
|
nstars = np.sum(pix_catalog.flag == 1)
|
|
|
|
for j in range(10):
|
|
|
|
w = wcs.WCS(naxis=2)
|
|
|
|
w.wcs.crpix = np.array([0.0, 0.0])
|
|
|
|
w.wcs.cd = np.array([[1.0, 0.0], [0.0, 1.0]])
|
|
|
|
w.wcs.ctype = ["RA---TAN", "DEC--TAN"]
|
|
|
|
w.wcs.set_pv([(2, 1, 45.0)])
|
|
|
|
c = pix_catalog.flag == 1
|
|
|
|
|
|
|
|
# Iterate to move crval to crpix location
|
|
|
|
for i in range(5):
|
|
|
|
w.wcs.crval = np.array([ra0, dec0])
|
|
|
|
|
|
|
|
world = np.stack((pix_catalog.ra, pix_catalog.dec), axis=-1)
|
|
|
|
pix = w.wcs_world2pix(world, 1)
|
|
|
|
rx, ry = pix[:, 0], pix[:, 1]
|
|
|
|
|
|
|
|
ax, cov_q, infodict, mesg, ierr = optimize.leastsq(residual,
|
|
|
|
[0.0, 0.0, 0.0],
|
|
|
|
args=(dx[c],
|
|
|
|
dy[c],
|
|
|
|
rx[c]),
|
|
|
|
full_output=1)
|
|
|
|
ay, cov_q, infodict, mesg, ierr = optimize.leastsq(residual,
|
|
|
|
[0.0, 0.0, 0.0],
|
|
|
|
args=(dx[c],
|
|
|
|
dy[c],
|
|
|
|
ry[c]),
|
|
|
|
full_output=1)
|
|
|
|
|
|
|
|
ra0, dec0 = w.wcs_pix2world([[ax[0], ay[0]]], 1)[0]
|
|
|
|
|
|
|
|
# Compute residuals
|
|
|
|
drx = ax[0]+ax[1]*dx+ax[2]*dy-rx
|
|
|
|
dry = ay[0]+ay[1]*dx+ay[2]*dy-ry
|
|
|
|
dr = np.sqrt(drx*drx+dry*dry)
|
|
|
|
rms = np.sqrt(np.sum(dr[c]**2)/len(dr[c]))
|
|
|
|
|
|
|
|
dr[~c] = 1.0
|
|
|
|
c = (dr < 2.0*rms)
|
|
|
|
pix_catalog.flag[~c] = 0
|
|
|
|
|
|
|
|
# Break if converged
|
|
|
|
if np.sum(c) == nstars:
|
|
|
|
break
|
|
|
|
nstars = np.sum(c)
|
|
|
|
|
|
|
|
# Compute residuals
|
|
|
|
rmsx = np.sqrt(np.sum(drx[c]**2)/len(drx[c]))
|
|
|
|
rmsy = np.sqrt(np.sum(dry[c]**2)/len(dry[c]))
|
|
|
|
|
|
|
|
# Store header
|
|
|
|
w = wcs.WCS(naxis=2)
|
|
|
|
w.wcs.crpix = np.array([x0, y0])
|
|
|
|
w.wcs.crval = np.array([ra0, dec0])
|
|
|
|
w.wcs.cd = np.array([[ax[1], ax[2]], [ay[1], ay[2]]])
|
|
|
|
w.wcs.ctype = ["RA---TAN", "DEC--TAN"]
|
|
|
|
w.wcs.set_pv([(2, 1, 45.0)])
|
|
|
|
|
|
|
|
return w, rmsx, rmsy, rms
|
|
|
|
|
|
|
|
|
|
|
|
def add_wcs(fname, w, rmsx, rmsy):
|
|
|
|
# Read fits
|
|
|
|
hdu = fits.open(fname)
|
|
|
|
|
|
|
|
whdr = {"CRPIX1": w.wcs.crpix[0], "CRPIX2": w.wcs.crpix[1],
|
|
|
|
"CRVAL1": w.wcs.crval[0], "CRVAL2": w.wcs.crval[1],
|
|
|
|
"CD1_1": w.wcs.cd[0, 0], "CD1_2": w.wcs.cd[0, 1],
|
|
|
|
"CD2_1": w.wcs.cd[1, 0], "CD2_2": w.wcs.cd[1, 1],
|
|
|
|
"CTYPE1": "RA---TAN", "CTYPE2": "DEC--TAN", "CUNIT1": "DEG",
|
|
|
|
"CUNIT2": "DEG", "CRRES1": rmsx, "CRRES2": rmsy}
|
|
|
|
|
|
|
|
# Add keywords
|
|
|
|
hdr = hdu[0].header
|
|
|
|
for k, v in whdr.items():
|
|
|
|
hdr[k] = v
|
|
|
|
hdu = fits.PrimaryHDU(header=hdr, data=hdu[0].data)
|
|
|
|
hdu.writeto(fname, overwrite=True, output_verify="ignore")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def calibrate_from_reference(fname, ref, pix_catalog):
|
|
|
|
# Estimated WCS
|
|
|
|
w = estimate_wcs_from_reference(ref, fname)
|
|
|
|
|
|
|
|
# Default rms values
|
|
|
|
rmsx = 0.0
|
|
|
|
rmsy = 0.0
|
|
|
|
|
|
|
|
# Read catalogs
|
|
|
|
if (pix_catalog.nstars > 4):
|
|
|
|
ast_catalog = tycho2_catalog(10.0)
|
|
|
|
|
|
|
|
# Match catalogs
|
|
|
|
nmatch = match_catalogs(ast_catalog, pix_catalog, w, 10.0)
|
|
|
|
|
|
|
|
# Fit transformation
|
|
|
|
if nmatch > 4:
|
|
|
|
w, rmsx, rmsy, rms = fit_wcs(w, pix_catalog)
|
|
|
|
|
|
|
|
# Add wcs
|
|
|
|
add_wcs(fname, w, rmsx, rmsy)
|
|
|
|
|
|
|
|
return w, rmsx, rmsy
|
2019-05-04 15:05:24 -06:00
|
|
|
|
2019-05-12 08:11:15 -06:00
|
|
|
|
2019-05-04 15:05:24 -06:00
|
|
|
def is_calibrated(ff):
|
|
|
|
if (3600.0*ff.crres[0] < 1e-3) | \
|
|
|
|
(3600.0*ff.crres[1] < 1e-3) | \
|
|
|
|
(ff.crres[0]/ff.sx > 2.0) | \
|
|
|
|
(ff.crres[1]/ff.sy > 2.0):
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return True
|