def _make_reference_gwcs_wcs(fits_hdr): hdr = fits.Header.fromfile(get_pkg_data_filename(fits_hdr)) fw = fitswcs.WCS(hdr) unit_conv = Scale(1.0 / 3600.0, name='arcsec_to_deg_1D') unit_conv = unit_conv & unit_conv unit_conv.name = 'arcsec_to_deg_2D' unit_conv_inv = Scale(3600.0, name='deg_to_arcsec_1D') unit_conv_inv = unit_conv_inv & unit_conv_inv unit_conv_inv.name = 'deg_to_arcsec_2D' c2s = CartesianToSpherical(name='c2s', wrap_lon_at=180) s2c = SphericalToCartesian(name='s2c', wrap_lon_at=180) c2tan = ((Mapping((0, 1, 2), name='xyz') / Mapping( (0, 0, 0), n_inputs=3, name='xxx')) | Mapping((1, 2), name='xtyt')) c2tan.name = 'Cartesian 3D to TAN' tan2c = (Mapping((0, 0, 1), n_inputs=2, name='xtyt2xyz') | (Const1D(1, name='one') & Identity(2, name='I(2D)'))) tan2c.name = 'TAN to cartesian 3D' tan2c.inverse = c2tan c2tan.inverse = tan2c aff = AffineTransformation2D(matrix=fw.wcs.cd) offx = Shift(-fw.wcs.crpix[0]) offy = Shift(-fw.wcs.crpix[1]) s = 5e-6 scale = Scale(s) & Scale(s) det2tan = (offx & offy) | scale | tan2c | c2s | unit_conv_inv taninv = s2c | c2tan tan = Pix2Sky_TAN() n2c = RotateNative2Celestial(fw.wcs.crval[0], fw.wcs.crval[1], 180) wcslin = unit_conv | taninv | scale.inverse | aff | tan | n2c sky_frm = cf.CelestialFrame(reference_frame=coord.ICRS()) det_frm = cf.Frame2D(name='detector') v2v3_frm = cf.Frame2D(name="v2v3", unit=(u.arcsec, u.arcsec), axes_names=('x', 'y'), axes_order=(0, 1)) pipeline = [(det_frm, det2tan), (v2v3_frm, wcslin), (sky_frm, None)] gw = gwcs.WCS(input_frame=det_frm, output_frame=sky_frm, forward_transform=pipeline) gw.crpix = fw.wcs.crpix gw.crval = fw.wcs.crval gw.bounding_box = ((-0.5, fw.pixel_shape[0] - 0.5), (-0.5, fw.pixel_shape[1] - 0.5)) return gw
def compute_va_effects(velocity_x, velocity_y, velocity_z, ra, dec): """ Computes constant scale factor due to velocity aberration as well as corrected ``RA`` and ``DEC`` values. Parameters ---------- velocity_x, velocity_y, velocity_z: float The components of the velocity of JWST, in km / s with respect to the Sun. These are celestial coordinates, with x toward the vernal equinox, y toward right ascension 90 degrees and declination 0, z toward the north celestial pole. ra, dec: float The right ascension and declination of the target (or some other point, such as the center of a detector) in the barycentric coordinate system. The equator and equinox should be the same as the coordinate system for the velocity. Returns ------- scale_factor: float Multiply the nominal image scale (e.g. in degrees per pixel) by this value to obtain the image scale corrected for the "aberration of starlight" due to the velocity of JWST with respect to the Sun. apparent_ra: float Apparent star position in the moving telescope frame. apparent_dec: float Apparent star position in the moving telescope frame. """ beta = np.array([velocity_x, velocity_y, velocity_z]) / SPEED_OF_LIGHT beta2 = np.dot(beta, beta) # |beta|^2 if beta2 == 0.0: logger.warning('Observatory speed is zero. Setting VA scale to 1.0') return 1.0, ra, dec u = np.asanyarray(SphericalToCartesian()(ra, dec)) beta_u = np.dot(beta, u) igamma = np.sqrt(1.0 - beta2) # inverse of usual gamma scale_factor = (1.0 + beta_u) / igamma # Algorithm below is from Colin Cox notebook. # Also see: Instrument Science Report OSG-CAL-97-06 by Colin Cox (1997). u_corr = (igamma * u + beta * (1.0 + (1.0 - igamma) * beta_u / beta2)) / (1.0 + beta_u) apparent_ra, apparent_dec = CartesianToSpherical()(*u_corr) return scale_factor, apparent_ra, apparent_dec
def v23tosky(input_model, wrap_v2_at=180, wrap_lon_at=360): v2_ref = input_model.meta.wcsinfo.v2_ref / 3600 v3_ref = input_model.meta.wcsinfo.v3_ref / 3600 roll_ref = input_model.meta.wcsinfo.roll_ref ra_ref = input_model.meta.wcsinfo.ra_ref dec_ref = input_model.meta.wcsinfo.dec_ref angles = np.array([v2_ref, -v3_ref, roll_ref, dec_ref, -ra_ref]) axes = "zyxyz" rot = RotationSequence3D(angles, axes_order=axes) # The sky rotation expects values in deg. # This should be removed when models work with quantities. m = ((Scale(1 / 3600) & Scale(1 / 3600)) | SphericalToCartesian(wrap_lon_at=wrap_v2_at) | rot | CartesianToSpherical(wrap_lon_at=wrap_lon_at)) m.name = 'v23tosky' return m
def _tpcorr_init(v2_ref, v3_ref, roll_ref): s2c = SphericalToCartesian(name='s2c', wrap_lon_at=180) c2s = CartesianToSpherical(name='c2s', wrap_lon_at=180) unit_conv = Scale(1.0 / 3600.0, name='arcsec_to_deg_1D') unit_conv = unit_conv & unit_conv unit_conv.name = 'arcsec_to_deg_2D' unit_conv_inv = Scale(3600.0, name='deg_to_arcsec_1D') unit_conv_inv = unit_conv_inv & unit_conv_inv unit_conv_inv.name = 'deg_to_arcsec_2D' affine = AffineTransformation2D(name='tp_affine') affine_inv = AffineTransformation2D(name='tp_affine_inv') rot = RotationSequence3D([v2_ref, -v3_ref, roll_ref], 'zyx', name='det_to_optic_axis') rot_inv = rot.inverse rot_inv.name = 'optic_axis_to_det' # projection submodels: c2tan = ((Mapping((0, 1, 2), name='xyz') / Mapping( (0, 0, 0), n_inputs=3, name='xxx')) | Mapping((1, 2), name='xtyt')) c2tan.name = 'Cartesian 3D to TAN' tan2c = (Mapping((0, 0, 1), n_inputs=2, name='xtyt2xyz') | (Const1D(1, name='one') & Identity(2, name='I(2D)'))) tan2c.name = 'TAN to cartesian 3D' total_corr = (unit_conv | s2c | rot | c2tan | affine | tan2c | rot_inv | c2s | unit_conv_inv) total_corr.name = 'JWST tangent-plane linear correction. v1' inv_total_corr = (unit_conv | s2c | rot | c2tan | affine_inv | tan2c | rot_inv | c2s | unit_conv_inv) inv_total_corr.name = 'Inverse JWST tangent-plane linear correction. v1' # TODO # re-enable circular inverse definitions once # https://github.com/spacetelescope/asdf/issues/744 or # https://github.com/spacetelescope/asdf/issues/745 are resolved. # # inv_total_corr.inverse = total_corr total_corr.inverse = inv_total_corr return total_corr
def test_v23_to_sky(): """ Test taken from INS report. """ ra_ref = 165 # in deg dec_ref = 54 # in deg v2_ref = -503.654472 / 3600 # in deg v3_ref = -318.742464 / 3600 # in deg r0 = 37 # in deg v2 = 210 # in deg v3 = -75 # in deg expected_ra_dec = (107.12810484789563, -35.97940247128502) # in deg angles = [v2_ref, -v3_ref, r0, dec_ref, -ra_ref] axes = "zyxyz" rot = RotationSequence3D(angles, axes_order=axes) v2s = SphericalToCartesian() | rot | CartesianToSpherical() radec = v2s(v2, v3) assert_allclose(radec, expected_ra_dec, atol=1e-10)
def _make_gwcs_wcs(fits_hdr): hdr = fits.Header.fromfile(get_pkg_data_filename(fits_hdr)) fw = fitswcs.WCS(hdr) a_order = hdr['A_ORDER'] a_coeff = {} for i in range(a_order + 1): for j in range(a_order + 1 - i): key = 'A_{:d}_{:d}'.format(i, j) if key in hdr: a_coeff[key] = hdr[key] b_order = hdr['B_ORDER'] b_coeff = {} for i in range(b_order + 1): for j in range(b_order + 1 - i): key = 'B_{:d}_{:d}'.format(i, j) if key in hdr: b_coeff[key] = hdr[key] cx = {"c" + k[2:]: v for k, v in a_coeff.items()} cy = {"c" + k[2:]: v for k, v in b_coeff.items()} sip_distortion = ((Shift(-fw.wcs.crpix[0]) & Shift(-fw.wcs.crpix[1])) | Mapping((0, 1, 0, 1)) | (polynomial.Polynomial2D(a_order, **cx, c1_0=1) & polynomial.Polynomial2D(b_order, **cy, c0_1=1)) | (Shift(fw.wcs.crpix[0]) & Shift(fw.wcs.crpix[1]))) y, x = np.indices(fw.array_shape) unit_conv = Scale(1.0 / 3600.0, name='arcsec_to_deg_1D') unit_conv = unit_conv & unit_conv unit_conv.name = 'arcsec_to_deg_2D' unit_conv_inv = Scale(3600.0, name='deg_to_arcsec_1D') unit_conv_inv = unit_conv_inv & unit_conv_inv unit_conv_inv.name = 'deg_to_arcsec_2D' c2s = CartesianToSpherical(name='c2s', wrap_lon_at=180) s2c = SphericalToCartesian(name='s2c', wrap_lon_at=180) c2tan = ((Mapping((0, 1, 2), name='xyz') / Mapping( (0, 0, 0), n_inputs=3, name='xxx')) | Mapping((1, 2), name='xtyt')) c2tan.name = 'Cartesian 3D to TAN' tan2c = (Mapping((0, 0, 1), n_inputs=2, name='xtyt2xyz') | (Const1D(1, name='one') & Identity(2, name='I(2D)'))) tan2c.name = 'TAN to cartesian 3D' tan2c.inverse = c2tan c2tan.inverse = tan2c aff = AffineTransformation2D(matrix=fw.wcs.cd) offx = Shift(-fw.wcs.crpix[0]) offy = Shift(-fw.wcs.crpix[1]) s = 5e-6 scale = Scale(s) & Scale(s) sip_distortion |= (offx & offy) | scale | tan2c | c2s | unit_conv_inv taninv = s2c | c2tan tan = Pix2Sky_TAN() n2c = RotateNative2Celestial(fw.wcs.crval[0], fw.wcs.crval[1], 180) wcslin = unit_conv | taninv | scale.inverse | aff | tan | n2c sky_frm = cf.CelestialFrame(reference_frame=coord.ICRS()) det_frm = cf.Frame2D(name='detector') v2v3_frm = cf.Frame2D(name="v2v3", unit=(u.arcsec, u.arcsec), axes_names=('x', 'y'), axes_order=(0, 1)) pipeline = [(det_frm, sip_distortion), (v2v3_frm, wcslin), (sky_frm, None)] gw = gwcs.WCS(input_frame=det_frm, output_frame=sky_frm, forward_transform=pipeline) gw.crpix = fw.wcs.crpix gw.crval = fw.wcs.crval gw.bounding_box = ((-0.5, fw.pixel_shape[0] - 0.5), (-0.5, fw.pixel_shape[1] - 0.5)) # sanity check: for _ in range(100): x = np.random.randint(1, fw.pixel_shape[0]) y = np.random.randint(1, fw.pixel_shape[1]) assert np.allclose(gw(x, y), fw.all_pix2world(x, y, 1), rtol=0, atol=1e-11) return gw
Licensed under a 3-clause BSD style license - see LICENSE.rst """ import math import numpy as np from astropy.modeling import Model, Parameter from astropy.modeling.models import AffineTransformation2D, Identity from astropy import coordinates as coord from astropy import units as u import gwcs from gwcs.geometry import CartesianToSpherical, SphericalToCartesian from tweakwcs.tpwcs import TPWCS, JWSTgWCS _S2C = SphericalToCartesian(name='s2c', wrap_lon_at=180) _C2S = CartesianToSpherical(name='c2s', wrap_lon_at=180) class DummyTPWCS(TPWCS): def set_correction(self, matrix=[[1, 0], [0, 1]], shift=[0, 0], ref_tpwcs=None, meta=None, **kwargs): super().set_correction(matrix=matrix, shift=shift, ref_tpwcs=ref_tpwcs, meta=meta, **kwargs)