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 test_v2v3todet_roundtrips(): s2c = (Scale(1.0 / 3600.0) & Scale(1.0 / 3600.0)) | SphericalToCartesian(wrap_lon_at=180) s = 1.0e-5 crpix = np.random.random(2) alpha = 0.25 * np.pi * np.random.random() x, y = 1024 * np.random.random(2) v2, v3 = 45 * np.random.random(2) cd = [[s * np.cos(alpha), -s * np.sin(alpha)], [s * np.sin(alpha), s * np.cos(alpha)]] d2v = create_DetToV2V3(v2ref=123.0, v3ref=500.0, roll=15.0, crpix=crpix, cd=cd) v2d = create_V2V3ToDet(v2ref=123.0, v3ref=500.0, roll=15.0, crpix=crpix, cd=cd) assert np.allclose(d2v.inverse(*d2v(x, y)), (x, y), rtol=1e3 * _ATOL, atol=1e3 * _ATOL) assert (np.allclose(s2c(*v2d.inverse(*v2d(v2, v3))), s2c(v2, v3), rtol=1e3 * _ATOL, atol=_ATOL) or np.allclose(-np.asanyarray(s2c(*v2d.inverse(*v2d(v2, v3)))), s2c(v2, v3), rtol=1e3 * _ATOL, atol=_ATOL)) assert np.allclose(v2d(*d2v(x, y)), (x, y), rtol=1e5 * _ATOL, atol=1e3 * _ATOL) assert (np.allclose(s2c(*d2v(*v2d(v2, v3))), s2c(v2, v3), rtol=1e3 * _ATOL, atol=1e3 * _ATOL) or np.allclose(-np.asanyarray(s2c(*d2v(*v2d(v2, v3)))), s2c(v2, v3), rtol=1e3 * _ATOL, atol=1e3 * _ATOL))
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)
) from astropy.modeling.fitting import LinearLSQFitter from gwcs import wcstools, WCS from gwcs import coordinate_frames as cf from gwcs.geometry import SphericalToCartesian from ..assign_wcs.util import wrap_ra from .. import datamodels from . import resample_utils from .resample import ResampleData log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) _S2C = SphericalToCartesian() class ResampleSpecData(ResampleData): """ This is the controlling routine for the resampling process. Notes ----- This routine performs the following operations:: 1. Extracts parameter settings from input model, such as pixfrac, weight type, exposure time (if relevant), and kernel, and merges them with any user-provided values. 2. Creates output WCS based on input images and define mapping function between all input arrays and the output array.
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
A module containing unit tests for the `wcsutil` module. 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)