pyuvdata
pyuvdata copied to clipboard
Add support for feed position angles
Currently, the UVData object does not support arbitrary feed polarization angles. This would be useful, and UVFITS/MS files already support this (see below). My personal motivation is that the upcoming SKA-Low stations are all rotated at different angles for "polarization diversity" and to improve sidelobe response between antenna pairs.
Currently, when writing to UVFITS the polarization angle keywords are hardcoded. I can't see anything similar for MS, which uses the RECEPTOR_ANGLE keyword -- presumably casacore fills these with default values.
To make this possible, I think the following would have to happen:
- An attribute is added to
UVData, e.g.UVData.feed_polarization_angleand__init__is updated accordingly (with (90, 0) degrees as default values). - All writers are updated to use these values.
- All readers are updated to read the relevant keywords when creating a
UVDataobject. - Tests are written (roundtrip conversion, e.g. UVData -> UVFITS -> UVData would exercise both read and write).
- Documentation is updated.
I don't have enough free cycles to take all this on, but I have some example code for UVFITS + MS below.
UVFITS keywords
From AIPS Memo 117.
In UVFITS, AIPS AN antenna table, the columns POLAA and POLAB should be updated:
POLAA 1E degrees Position angle feed A
POLAB 1E degrees Position angle feed B
The value of the
POLAAcolumn shall be the orientation of feed A, assumed independent of IF, given in degrees. Similarly, thePOLABcolumn shall contain the feed orientation for feed B.
MS keywords
Need to update RECEPTOR_ANGLE column of the FEED table of a measurement set:
https://safe.nrao.edu/wiki/bin/view/ALMA/ReceptorAngles
These angles are stored in radians. If there are multiple receiver bands in a measurement set, then multiple angles will be shown.
Miriad
No idea what keywords would need to change.
Proto-code
def write_ms(uv: UVData, filename: str, *args, **kwargs):
"""Write UVData to MeasurementSet.
Notes:
Calls uv.write_ms(), then applies station rotation patch.
Args:
uv (UVData): pyuvdata object to write to file.
filename (str): Name of output filename.
args (list): Arguments to pass to uv.write_ms
kwargs (dict): Keyword arguments to pass to uv.write_ms
"""
tables = import_optional_dependency('casacore.tables', errors='raise')
uv.write_ms(filename, *args, **kwargs)
# Patch RECEPTOR_ANGLE column
with tables.table(f'{filename}/FEED', readonly=False) as t:
logger.debug('Applying station rotation (RECEPTOR_ANGLE)')
r_ang = np.zeros(shape=t.getcol('RECEPTOR_ANGLE').shape, dtype='float64')
x_ang = -np.pi/180 * uv.receptor_angle
r_ang[:, 0] = x_ang
r_ang[:, 1] = x_ang + np.pi
t.putcol('RECEPTOR_ANGLE', r_ang)
def write_uvfits(uv: UVData, filename: str, *args, **kwargs):
"""Write UVData to UVFITS.
Notes:
Calls uv.write_uvfits(), then applies station rotation patch.
Args:
uv (UVData): pyuvdata object to write to file.
filename (str): Name of output filename.
args (list): Arguments to pass to uv.write_uvfits
kwargs (dict): Keyword arguments to pass to uv.write_uvfits
"""
uv.write_uvfits(filename, *args, **kwargs)
# Patch POLAA/POLAB columns
with pf.open(filename, mode='update') as hdu:
logger.debug('Applying station rotation (POLAA/POLAB)')
hdu[1].data['POLAA'] = uv.receptor_angle
hdu[1].data['POLAB'] = uv.receptor_angle + 90