Skip to content

Commit abb2bce

Browse files
committed
Implement basic uvw-by-target tracking.
TO DO: - Flatten uvw-map, producing matched utc / lha arrays. - Output all as table.
1 parent a974c50 commit abb2bce

File tree

4 files changed

+82
-21
lines changed

4 files changed

+82
-21
lines changed

src/fastimgproto/coords.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import numpy as np
33

44

5-
def time_of_next_transit(start_time, observer_longitude, target_ra,
5+
def time_of_next_transit(observer_longitude, target_ra, start_time,
66
tolerance=0.02 * u.arcsec):
77
"""
88
Calculate time, ``t`` when the local-hour-angle of target will be 0.
@@ -15,11 +15,11 @@ def time_of_next_transit(start_time, observer_longitude, target_ra,
1515
time-period.
1616
1717
Args:
18-
start_time (astropy.time.Time): Time to start from.
1918
observer_longitude (astropy.coordinates.Longitude): Longitude
2019
of position on Earth
2120
target_ra (astropy.coordinates.Longitude): Right ascension of
2221
sky-target.
22+
start_time (astropy.time.Time): Time to start from.
2323
tolerance (astropy.units.Quantity): If target's LHA is within
2424
``tolerance`` of zero at ``start_time``, simply return
2525
``start_time``. Otherwise look for the next transit.

src/fastimgproto/telescope/base.py

+45-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import print_function
22

3+
from collections import OrderedDict
34
from itertools import combinations
45

56
import astropy.io.ascii as ascii
@@ -9,10 +10,10 @@
910
from astropy.coordinates import (Latitude, Longitude, EarthLocation, )
1011
from astropy.table import Table
1112
from attr import attrib, attrs
12-
1313
from fastimgproto.coords import (
14+
time_of_next_transit,
1415
xyz_to_uvw_rotation_matrix,
15-
z_rotation_matrix
16+
z_rotation_matrix,
1617
)
1718

1819
_validator_optional_ndarray = attr.validators.optional(
@@ -98,6 +99,19 @@ def from_itrf(ant_itrf_xyz, ant_labels):
9899
ant_local_xyz=ant_local_xyz,
99100
)
100101

102+
def lha(self, ra, time):
103+
"""
104+
Calculate the local hour angle of a target-RA at a given time
105+
106+
Args:
107+
ra (astropy.coordinates.Longitude): Right ascension
108+
time (astropy.time.Time): Timestamp
109+
110+
Returns:
111+
astropy.coordinates.Longitude: Local Hour Angle of target-RA.
112+
"""
113+
return self.lst(time) - ra
114+
101115
def lst(self, time):
102116
"""
103117
Calculate the local sidereal time at the telescope
@@ -106,8 +120,29 @@ def lst(self, time):
106120
time (astropy.time.Time): Global timestamp
107121
108122
Returns:
123+
astropy.coordinates.Longitude: Local sidereal time expressed as
124+
an angle-Quantity.
125+
"""
126+
return time.sidereal_time('apparent',
127+
longitude=self.longitude)
128+
129+
def next_transit(self, target_ra, start_time, ):
130+
"""
131+
Wrapper around :py:func:`.time_of_next_transit`
132+
133+
See :py:func:`.time_of_next_transit` for details.
134+
135+
Args:
136+
target_ra (astropy.coordinates.Longitude): Right ascension of
137+
sky-target.
138+
start_time (astropy.time.Time): Time to start from.
139+
Returns:
140+
astropy.time.Time: Approximate time of the next transit
109141
110142
"""
143+
return time_of_next_transit(observer_longitude=self.longitude,
144+
target_ra=target_ra,
145+
start_time=start_time)
111146

112147
def uvw_at_local_hour_angle(self, local_hour_angle, dec):
113148
"""
@@ -128,8 +163,14 @@ def uvw_at_local_hour_angle(self, local_hour_angle, dec):
128163
rotation = xyz_to_uvw_rotation_matrix(local_hour_angle, dec)
129164
return np.dot(rotation, self.baseline_local_xyz.T).T
130165

131-
def uvw_at_skycoord_and_time(self, pointing_centre, utc_time):
132-
pass
166+
def uvw_tracking_skycoord(self, pointing_centre, obs_times):
167+
lha_uvw_map = OrderedDict()
168+
for time in obs_times:
169+
lha = self.lha(pointing_centre.ra, time)
170+
lha_uvw_map[lha] = self.uvw_at_local_hour_angle(
171+
local_hour_angle=lha, dec=pointing_centre.dec
172+
)
173+
return lha_uvw_map
133174

134175

135176
def generate_baselines_and_labels(antenna_positions, antenna_labels):
@@ -238,4 +279,3 @@ def parse_itrf_ascii_to_xyz_and_labels(tabledata):
238279
xyz = hstack_table_columns_as_ndarray(tbl.columns[:3]) * u.m
239280
labels = tbl['label'].data
240281
return xyz, labels
241-

tests/test_coords/test_transit_calcs.py

+5-9
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,23 @@ def test_basic_functionality():
1717
tol = 0.02 * u.arcsec
1818

1919
# Next transit-time at lon=0
20-
tt_lon0 = time_of_next_transit(start_time=t0,
21-
observer_longitude=lon0,
22-
target_ra=ra,
23-
tolerance=tol)
20+
tt_lon0 = time_of_next_transit(observer_longitude=lon0, target_ra=ra,
21+
start_time=t0, tolerance=tol)
2422

2523
tt1_lst = tt_lon0.sidereal_time('apparent', longitude=lon0)
2624
assert np.fabs(tt1_lst) < tol
2725

2826
# If we provide a close-enough-to-transit time, just return it, don't
2927
# endlessly cycle by a sidereal day:
30-
tt_lon0_tnt = time_of_next_transit(tt_lon0, lon0, ra, tol)
28+
tt_lon0_tnt = time_of_next_transit(lon0, ra, tt_lon0, tol)
3129
assert tt_lon0_tnt == tt_lon0
3230

3331
# Longitude is positive east. So a negative longitude observer (West) should
3432
# see a later transit:
3533

3634
lon_m30 = Longitude(-30 * u.deg)
37-
tt_lon30 = time_of_next_transit(start_time=t0,
38-
observer_longitude=lon_m30,
39-
target_ra=ra,
40-
tolerance=tol)
35+
tt_lon30 = time_of_next_transit(observer_longitude=lon_m30, target_ra=ra,
36+
start_time=t0, tolerance=tol)
4137
assert tt_lon30 > tt_lon0
4238
# Difference should be 30 / 360 = 2 sidereal hours
4339
shour = 1./24.*u.sday # Sidereal hour

tests/test_telescope/test_telescope.py

+30-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from __future__ import print_function
22
import numpy as np
33
import astropy.units as u
4-
from astropy.coordinates import Latitude, Longitude
4+
from astropy.coordinates import Latitude, Longitude, SkyCoord
5+
from astropy.time import Time
56
from fastimgproto.telescope import Telescope
67
from fastimgproto.telescope.base import parse_itrf_ascii_to_xyz_and_labels
78
from fastimgproto.telescope.data import meerkat_itrf_filepath
9+
from fastimgproto.telescope.readymade import Meerkat
810

911

1012
def test_telescope_from_itrf():
@@ -38,9 +40,9 @@ def test_xyz_antennae_to_uvw_baselines():
3840
]
3941

4042
expected_uvw_baselines = np.array([
41-
[1., 0., 0.], #origin,1-east
42-
[0., 1., 0.], #origin,1-north
43-
[-1., 1., 0.]]) #1-east,1-north
43+
[1., 0., 0.], # origin,1-east
44+
[0., 1., 0.], # origin,1-north
45+
[-1., 1., 0.]]) # 1-east,1-north
4446

4547
tel = Telescope(latitude=Latitude(0 * u.deg),
4648
longitude=Longitude(0 * u.deg),
@@ -53,7 +55,30 @@ def test_xyz_antennae_to_uvw_baselines():
5355
lha = 0 * u.deg
5456
dec = 0 * u.deg
5557
uvw = tel.uvw_at_local_hour_angle(lha, dec)
56-
assert (uvw==expected_uvw_baselines).all()
58+
assert (uvw == expected_uvw_baselines).all()
5759
# print("UVW:")
5860
# print(repr(uvw))
5961
# print(tel.baseline_labels)
62+
63+
64+
def test_uvw_tracking_skyposn():
65+
"""
66+
Check UVW calculations for Time+RA+Dec vs LHA+Dec produce consistent results
67+
"""
68+
telescope = Meerkat()
69+
azimuth_target = SkyCoord(ra=0 * u.deg, dec=telescope.latitude)
70+
71+
t0 = Time('2017-01-01 12:00:00', format='iso', scale='utc')
72+
tt = telescope.next_transit(azimuth_target.ra, t0)
73+
tt_lha = telescope.lha(azimuth_target.ra, tt)
74+
assert np.fabs(tt_lha) < 0.2 * u.arcsec
75+
76+
obs_times = [tt - 10*u.second, tt, tt + 10*u.second]
77+
uvw_by_tracking = telescope.uvw_tracking_skycoord(
78+
azimuth_target,
79+
obs_times=obs_times)
80+
assert len(uvw_by_tracking) == len(obs_times)
81+
82+
uvw_by_lha = telescope.uvw_at_local_hour_angle(tt_lha, azimuth_target.dec)
83+
assert uvw_by_tracking.keys()[1] == tt_lha
84+
assert (uvw_by_lha == uvw_by_tracking.values()[1]).all()

0 commit comments

Comments
 (0)