import katpoint
import numpy as np
from astropy import units as u
from astropy.coordinates import Angle, CartesianRepresentation, EarthLocation
from astropy.time import Time
from .uvw_engine import UVWEngine
[docs]
class KatpointUVWEngine(UVWEngine):
"""
A derived class that uses katpoint to determine the UVW of
the antennas.
One complication is that katpoint prefers topocentric antenna
reference positions. You may not that the UVWEngine creates
a geocentric antenna UVW so all baselines can be generated by
subtractions. This will need another reference position
"""
# building an antenna at the centre of the Earth
geo_location = EarthLocation.from_geocentric(0.0 * u.m, 0.0 * u.m, 0.0 * u.m)
ref_longitude, ref_latitude, ref_height = geo_location.to_geodetic(ellipsoid="WGS84")
# pylint: disable=consider-using-f-string
desc = "GEOCENTRE,{lat},{long},{ht},12.0,0,0,0,0)".format(
lat=ref_latitude.to_string(sep=":"),
long=ref_longitude.to_string(sep=":"),
ht=ref_height.value,
)
geo_antenna = katpoint.Antenna(desc)
[docs]
@staticmethod
def get_antenna_uvw(
antenna_location: np.array,
epoch: Time,
ra: Angle,
dec: Angle,
position_frame="itrf",
epoch_frame="icrs",
swap_baselines=False,
) -> np.ndarray:
"""
Internal class that uses the Engine to determine the UVW
This is specific to the Katpoint engine.
This must have a reference position. For the basic engine this
is the geocentre - so we replicate it here
This overloads a more general method. There are some fixed assumptions here:
The position is geocentric (in metres) The RA and DEC are ICRS
:param list antenna_location: position of the antenna (vector geocentric XYZ in m)
:param astropy.Time epoch: Time for which to calculate the UVW
:param astropy.Angle ra: Right Ascension (ICRS)
:param astropy.Angle dec: Declination (ICRS)
:param str position_frame: The frame of the input positions (currently assumes geocentric itrf)
:param str epoch_frame: NOT USED.
:param Bool swap_baselines: NOT USED.
:returns: The geocentric UVW baseline
:rtype: numpy.ndarray [u,v,w]
"""
# We need to set up the target direction
tgt_katpoint = katpoint.construct_radec_target(ra.rad, dec.rad)
# We also need a timestamp
# In the katpoint docs an astropy.Time object is supposed to work
# but it doesn't seem to.
# This seems to be related to the katpoint version and 1.0 is more reliable
# still getting issues due to the possibiltiy of epoch being a sequence
# of some sort but @rtobar introduced me to numpy.squeeze
# epoch.format = "iso"
# desc = "{t}".format(t=epoch[0])
#
# or
#
# if hasattr(epoch,"__len__"):
# epoch = epoch[0]
#
# or neater
epoch = np.squeeze(epoch)
timestamp = katpoint.Timestamp(epoch)
# we need the antenna location in katpoint format
ant1_location = EarthLocation.from_geocentric(
antenna_location[0] * u.m,
antenna_location[1] * u.m,
antenna_location[2] * u.m,
)
# the constructor wants a geodetic
longitude, latitude, height = ant1_location.to_geodetic(ellipsoid="WGS84")
# pylint: disable=consider-using-f-string
desc = "a1,{lat},{long},{ht},12.0,0,0,0,0)".format(
lat=latitude.to_string(sep=":"),
long=longitude.to_string(sep=":"),
ht=height.value,
)
ant = katpoint.Antenna(desc)
# Now we have all we need
# This evaluates the UVW <at> the location od ant in the direction of geo_ant.
uvw = tgt_katpoint.uvw(ant, timestamp, KatpointUVWEngine.geo_antenna)
# the inversion is becuase I want the baseline to point from CoM to the
# antenna.
if isinstance(uvw, CartesianRepresentation):
return (np.array(uvw.get_xyz())).squeeze()
else:
return np.array(uvw)