xradio 0.0.52__tar.gz → 0.0.53__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. {xradio-0.0.52/src/xradio.egg-info → xradio-0.0.53}/PKG-INFO +1 -1
  2. {xradio-0.0.52 → xradio-0.0.53}/pyproject.toml +1 -1
  3. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/image/_util/_casacore/xds_to_casacore.py +3 -1
  4. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/image/_util/_fits/xds_from_fits.py +17 -26
  5. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/image/_util/_zarr/xds_to_zarr.py +17 -0
  6. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/image/image.py +20 -8
  7. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/conversion.py +12 -0
  8. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/msv4_sub_xdss.py +116 -1
  9. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/schema.py +104 -1
  10. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/schema/check.py +3 -2
  11. {xradio-0.0.52 → xradio-0.0.53/src/xradio.egg-info}/PKG-INFO +1 -1
  12. {xradio-0.0.52 → xradio-0.0.53}/LICENSE.txt +0 -0
  13. {xradio-0.0.52 → xradio-0.0.53}/MANIFEST.in +0 -0
  14. {xradio-0.0.52 → xradio-0.0.53}/README.md +0 -0
  15. {xradio-0.0.52 → xradio-0.0.53}/setup.cfg +0 -0
  16. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/__init__.py +0 -0
  17. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/_utils/__init__.py +0 -0
  18. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/_utils/_casacore/tables.py +0 -0
  19. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/_utils/coord_math.py +0 -0
  20. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/_utils/dict_helpers.py +0 -0
  21. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/_utils/list_and_array.py +0 -0
  22. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/_utils/schema.py +0 -0
  23. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/_utils/zarr/__init__.py +0 -0
  24. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/_utils/zarr/common.py +0 -0
  25. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/image/__init__.py +0 -0
  26. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/image/_util/__init__.py +0 -0
  27. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/image/_util/_casacore/__init__.py +0 -0
  28. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/image/_util/_casacore/common.py +0 -0
  29. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/image/_util/_casacore/xds_from_casacore.py +0 -0
  30. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/image/_util/_zarr/common.py +0 -0
  31. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/image/_util/_zarr/xds_from_zarr.py +0 -0
  32. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/image/_util/_zarr/zarr_low_level.py +0 -0
  33. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/image/_util/casacore.py +0 -0
  34. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/image/_util/common.py +0 -0
  35. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/image/_util/fits.py +0 -0
  36. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/image/_util/image_factory.py +0 -0
  37. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/image/_util/zarr.py +0 -0
  38. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/__init__.py +0 -0
  39. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/__init__.py +0 -0
  40. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/_tables/load.py +0 -0
  41. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/_tables/load_main_table.py +0 -0
  42. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/_tables/read.py +0 -0
  43. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/_tables/read_main_table.py +0 -0
  44. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/_tables/read_subtables.py +0 -0
  45. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/_tables/table_query.py +0 -0
  46. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/_tables/write.py +0 -0
  47. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/_tables/write_exp_api.py +0 -0
  48. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/chunks.py +0 -0
  49. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/create_antenna_xds.py +0 -0
  50. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/create_field_and_source_xds.py +0 -0
  51. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/descr.py +0 -0
  52. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/msv2_msv3.py +0 -0
  53. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/msv2_to_msv4_meta.py +0 -0
  54. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/msv4_info_dicts.py +0 -0
  55. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/optimised_functions.py +0 -0
  56. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/partition_queries.py +0 -0
  57. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/partitions.py +0 -0
  58. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_msv2/subtables.py +0 -0
  59. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_utils/cds.py +0 -0
  60. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_utils/partition_attrs.py +0 -0
  61. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_utils/stokes_types.py +0 -0
  62. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_utils/xds_helper.py +0 -0
  63. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_zarr/encoding.py +0 -0
  64. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_zarr/read.py +0 -0
  65. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/_zarr/write.py +0 -0
  66. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/msv2.py +0 -0
  67. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/_utils/zarr.py +0 -0
  68. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/convert_msv2_to_processing_set.py +0 -0
  69. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/load_processing_set.py +0 -0
  70. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/measurement_set_xdt.py +0 -0
  71. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/open_processing_set.py +0 -0
  72. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/measurement_set/processing_set_xdt.py +0 -0
  73. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/schema/__init__.py +0 -0
  74. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/schema/bases.py +0 -0
  75. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/schema/dataclass.py +0 -0
  76. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/schema/metamodel.py +0 -0
  77. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/schema/typing.py +0 -0
  78. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/sphinx/__init__.py +0 -0
  79. {xradio-0.0.52 → xradio-0.0.53}/src/xradio/sphinx/schema_table.py +0 -0
  80. {xradio-0.0.52 → xradio-0.0.53}/src/xradio.egg-info/SOURCES.txt +0 -0
  81. {xradio-0.0.52 → xradio-0.0.53}/src/xradio.egg-info/dependency_links.txt +0 -0
  82. {xradio-0.0.52 → xradio-0.0.53}/src/xradio.egg-info/requires.txt +0 -0
  83. {xradio-0.0.52 → xradio-0.0.53}/src/xradio.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xradio
3
- Version: 0.0.52
3
+ Version: 0.0.53
4
4
  Summary: Xarray Radio Astronomy Data IO
5
5
  Author-email: Jan-Willem Steeb <jsteeb@nrao.edu>, Federico Montesino Pouzols <pouzols@eso.edu>, Dave Mehringer <dmehring@nrao.edu>, Peter Wortmann <peter.wortmann@skao.int>
6
6
  License: BSD 3-Clause License
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "xradio"
3
- version = "0.0.52"
3
+ version = "0.0.53"
4
4
  description = " Xarray Radio Astronomy Data IO"
5
5
  authors = [
6
6
  {name = "Jan-Willem Steeb", email="jsteeb@nrao.edu"},
@@ -198,7 +198,9 @@ def _imageinfo_dict_from_xds(xds: xr.Dataset) -> dict:
198
198
  ii["image_type"] = (
199
199
  xds[ap_sky].attrs["image_type"] if "image_type" in xds[ap_sky].attrs else ""
200
200
  )
201
- ii["objectname"] = xds[ap_sky].attrs[_object_name]
201
+ ii["objectname"] = (
202
+ xds[ap_sky].attrs[_object_name] if _object_name in xds[ap_sky].attrs else ""
203
+ )
202
204
  if "BEAM" in xds.data_vars:
203
205
  # multi beam
204
206
  pp = {}
@@ -82,8 +82,6 @@ def _add_freq_attrs(xds: xr.Dataset, helpers: dict) -> xr.Dataset:
82
82
  if helpers["has_freq"]:
83
83
  meta["rest_frequency"] = make_quantity(helpers["restfreq"], "Hz")
84
84
  meta["rest_frequencies"] = [meta["rest_frequency"]]
85
- # meta["frame"] = helpers["specsys"]
86
- # meta["units"] = "Hz"
87
85
  meta["type"] = "frequency"
88
86
  meta["wave_unit"] = "mm"
89
87
  freq_axis = helpers["freq_axis"]
@@ -123,16 +121,6 @@ def _add_l_m_attrs(xds: xr.Dataset, helpers: dict) -> xr.Dataset:
123
121
 
124
122
 
125
123
  def _add_lin_attrs(xds: xr.Dataset, helpers: dict) -> xr.Dataset:
126
- """
127
- if helpers["sphr_dims"]:
128
- for i, name in zip(helpers["dir_axes"], helpers["sphr_axis_names"]):
129
- meta = {
130
- "units": "rad",
131
- "crval": helpers["crval"][i],
132
- "cdelt": helpers["cdelt"][i],
133
- }
134
- xds.coords[name].attrs = meta
135
- """
136
124
  if not helpers["sphr_dims"]:
137
125
  for i, j in zip(helpers["dir_axes"], ("u", "v")):
138
126
  meta = {
@@ -162,7 +150,7 @@ def _xds_direction_attrs_from_header(helpers: dict, header) -> dict:
162
150
  direction["projection"] = p0
163
151
  helpers["projection"] = p0
164
152
  ref_sys = header["RADESYS"]
165
- ref_eqx = header["EQUINOX"]
153
+ ref_eqx = None if ref_sys.upper() == "ICRS" else header["EQUINOX"]
166
154
  if ref_sys == "FK5" and ref_eqx == 2000:
167
155
  ref_eqx = "J2000.0"
168
156
  helpers["ref_sys"] = ref_sys
@@ -180,9 +168,10 @@ def _xds_direction_attrs_from_header(helpers: dict, header) -> dict:
180
168
  ddata.append(x.value)
181
169
  # direction["reference"]["value"][i] = x.value
182
170
  x = helpers["cdelt"][i] * u.Unit(_get_unit(helpers["cunit"][i]))
183
- dunits.append(x.to("rad"))
171
+ dunits.append("rad")
184
172
  direction["reference"] = make_skycoord_dict(ddata, units=dunits, frame=ref_sys)
185
- direction["reference"]["attrs"]["equinox"] = ref_eqx.lower()
173
+ if ref_eqx is not None:
174
+ direction["reference"]["attrs"]["equinox"] = ref_eqx.lower()
186
175
  direction["latpole"] = make_quantity(
187
176
  header["LATPOLE"] * _deg_to_rad, "rad", dims=["l", "m"]
188
177
  )
@@ -285,6 +274,9 @@ def _user_attrs_from_header(header) -> dict:
285
274
  "ALTRPIX",
286
275
  "ALTRVAL",
287
276
  "BITPIX",
277
+ "BMAJ",
278
+ "BMIN",
279
+ "BPA",
288
280
  "BSCALE",
289
281
  "BTYPE",
290
282
  "BUNIT",
@@ -310,21 +302,20 @@ def _user_attrs_from_header(header) -> dict:
310
302
  ]
311
303
  regex = r"|".join(
312
304
  [
313
- "^NAXIS\\d?$",
314
- "^CRVAL\\d$",
315
- "^CRPIX\\d$",
316
- "^CTYPE\\d$",
317
- "^CDELT\\d$",
318
- "^CUNIT\\d$",
319
- "^OBSGEO-(X|Y|Z)$",
320
- "^P(C|V)\\d_\\d$",
305
+ r"^NAXIS\d?$",
306
+ r"^CRVAL\d$",
307
+ r"^CRPIX\d$",
308
+ r"^CTYPE\d$",
309
+ r"^CDELT\d$",
310
+ r"^CUNIT\d$",
311
+ r"^OBSGEO-(X|Y|Z)$",
312
+ r"^P(C|V)0?\d_0?\d",
321
313
  ]
322
314
  )
323
315
  user = {}
324
316
  for k, v in header.items():
325
- if re.search(regex, k) or k in exclude:
326
- continue
327
- user[k.lower()] = v
317
+ if not (re.search(regex, k) or k in exclude):
318
+ user[k.lower()] = v
328
319
  return user
329
320
 
330
321
 
@@ -1,3 +1,4 @@
1
+ import dask.array as da
1
2
  import logging
2
3
  import numpy as np
3
4
  import xarray as xr
@@ -6,6 +7,22 @@ from .common import _np_types, _top_level_sub_xds
6
7
 
7
8
 
8
9
  def _write_zarr(xds: xr.Dataset, zarr_store: str):
10
+ max_chunk_size = 0.95 * 2**30
11
+ for dv in xds.data_vars:
12
+ obj = xds[dv]
13
+ if isinstance(obj, xr.core.dataarray.DataArray) and isinstance(
14
+ obj.data, da.Array
15
+ ):
16
+ # get chunk size to make sure it is small enough to be compressed
17
+ ary = obj.data
18
+ chunk_size_bytes = np.prod(ary.chunksize) * np.dtype(ary.dtype).itemsize
19
+ if chunk_size_bytes > max_chunk_size:
20
+ raise ValueError(
21
+ f"Chunk size of {chunk_size_bytes/1e9} GB for data variable {dv} "
22
+ "bytes is too large for compression. To fix this, "
23
+ "reduce the chunk size of the dask array in the data variable "
24
+ f"by at least a factor of {chunk_size_bytes/max_chunk_size}."
25
+ )
9
26
  xds_copy = xds.copy(deep=True)
10
27
  sub_xds_dict = _encode(xds_copy, zarr_store)
11
28
  z_obj = xds_copy.to_zarr(store=zarr_store, compute=True)
@@ -7,6 +7,9 @@ import warnings
7
7
  from typing import List, Union
8
8
  import copy
9
9
  import numpy as np
10
+ import os
11
+ import shutil
12
+ import toolviper.utils.logger as logger
10
13
  import xarray as xr
11
14
 
12
15
  # from .._utils.zarr.common import _load_no_dask_zarr
@@ -182,27 +185,36 @@ def load_image(infile: str, block_des: dict = {}, do_sky_coords=True) -> xr.Data
182
185
 
183
186
 
184
187
  def write_image(
185
- xds: xr.Dataset, imagename: str, out_format: str = "casa", overwrite=False
188
+ xds: xr.Dataset, imagename: str, out_format: str = "casa", overwrite: bool = False
186
189
  ) -> None:
187
190
  """
188
191
  Convert an xds image to CASA or zarr image.
189
192
  xds : xarray.Dataset
190
193
  XDS to convert
191
194
  imagename : str
192
- Path to output CASA image
195
+ Path to output image
193
196
  out_format : str
194
197
  Format of output image, currently "casa" and "zarr" are supported
198
+ overwrite : bool
199
+ If True, overwrite existing image. Default is False.
195
200
  Returns
196
201
  -------
197
202
  None
198
203
  """
204
+ if os.path.exists(imagename):
205
+ if overwrite:
206
+ logger.warning(
207
+ f"Because overwrite=True, removing existing path {imagename}"
208
+ )
209
+ if os.path.isdir(imagename):
210
+ shutil.rmtree(imagename)
211
+ else:
212
+ os.remove(imagename)
213
+ else:
214
+ raise FileExistsError(
215
+ f"Path {imagename} already exists. Set overwrite=True to remove it."
216
+ )
199
217
  my_format = out_format.lower()
200
-
201
- if overwrite:
202
- import os
203
-
204
- os.system("rm -rf " + imagename)
205
-
206
218
  if my_format == "casa":
207
219
  _xds_to_casa_image(xds, imagename)
208
220
  elif my_format == "zarr":
@@ -16,6 +16,7 @@ from xradio.measurement_set._utils._msv2.msv4_sub_xdss import (
16
16
  create_pointing_xds,
17
17
  create_system_calibration_xds,
18
18
  create_weather_xds,
19
+ create_phased_array_xds,
19
20
  )
20
21
  from .msv4_info_dicts import create_info_dicts
21
22
  from xradio.measurement_set.schema import MSV4_SCHEMA_VERSION
@@ -1243,6 +1244,14 @@ def convert_and_write_partition(
1243
1244
  + str(time.time() - start)
1244
1245
  )
1245
1246
 
1247
+ # Create phased array xds
1248
+ phased_array_xds = create_phased_array_xds(
1249
+ in_file,
1250
+ ant_xds.antenna_name,
1251
+ ant_xds.receptor_label,
1252
+ ant_xds.polarization_type,
1253
+ )
1254
+
1246
1255
  start = time.time()
1247
1256
 
1248
1257
  # Time and frequency should always be increasing
@@ -1358,6 +1367,9 @@ def convert_and_write_partition(
1358
1367
  if weather_xds:
1359
1368
  ms_xdt["/weather_xds"] = weather_xds
1360
1369
 
1370
+ if phased_array_xds:
1371
+ ms_xdt["/phased_array_xds"] = phased_array_xds
1372
+
1361
1373
  if storage_backend == "zarr":
1362
1374
  ms_xdt.to_zarr(store=os.path.join(out_file, ms_v4_name))
1363
1375
  elif storage_backend == "netcdf":
@@ -1,10 +1,11 @@
1
1
  import toolviper.utils.logger as logger
2
2
  import os
3
3
  import time
4
- from typing import Tuple, Union
4
+ from typing import Optional, Tuple, Union
5
5
 
6
6
  import numpy as np
7
7
  import xarray as xr
8
+ from numpy.typing import ArrayLike
8
9
 
9
10
  from xradio._utils.coord_math import convert_to_si_units
10
11
  from xradio._utils.schema import (
@@ -741,3 +742,117 @@ def create_system_calibration_xds(
741
742
  sys_cal_xds[data_var] = sys_cal_xds[data_var].astype(np.float64)
742
743
 
743
744
  return sys_cal_xds
745
+
746
+
747
+ def create_phased_array_xds(
748
+ in_file: str,
749
+ antenna_names: list[str],
750
+ receptor_label: list[str],
751
+ polarization_type: ArrayLike,
752
+ ) -> Optional[xr.Dataset]:
753
+ """
754
+ Create an Xarray Dataset containing phased array information.
755
+
756
+ Parameters
757
+ ----------
758
+ in_file : str
759
+ Path to the input MSv2.
760
+ antenna_names: DataArray or Sequence[str]
761
+ Content of the antenna_name coordinate of the antenna_xds.
762
+ receptor_label: DataArray or Sequence[str]
763
+ Content of the receptor_label coordinate of the antenna_xds. Used to
764
+ label the corresponding axis of ELEMENT_FLAG.
765
+ polarization_type: DataArray or ArrayLike
766
+ Contents of the polarization_type coordinate of the antenna_xds.
767
+ Array-like of shape (num_antennas, 2) containing the polarization
768
+ hands for each antenna.
769
+
770
+ Returns
771
+ ----------
772
+ xr.Dataset or None: If the input MS contains a PHASED_ARRAY table,
773
+ returns the Xarray Dataset containing the phased array information.
774
+ Otherwise, return None.
775
+ """
776
+
777
+ def extract_data(dataarray_or_sequence):
778
+ if hasattr(dataarray_or_sequence, "data"):
779
+ return dataarray_or_sequence.data.tolist()
780
+ return dataarray_or_sequence
781
+
782
+ antenna_names = extract_data(antenna_names)
783
+ receptor_label = extract_data(receptor_label)
784
+ polarization_type = extract_data(polarization_type)
785
+
786
+ # NOTE: We cannot use the dimension renaming option of `load_generic_table`
787
+ # here, because it leads to a dimension name collision. This is caused by
788
+ # the presence of two dimensions of size 3 in multiple arrays.
789
+ # Instead, we do the renaming manually below.
790
+ try:
791
+ raw_xds = load_generic_table(
792
+ in_file,
793
+ "PHASED_ARRAY",
794
+ # Some MSes carry COORDINATE_SYSTEM as a copy of COORDINATE_AXES
795
+ # due to a past ambiguity on the PHASED_ARRAY schema
796
+ ignore=["COORDINATE_SYSTEM", "ANTENNA_ID"],
797
+ )
798
+ except ValueError:
799
+ return None
800
+
801
+ # Defend against empty PHASED_ARRAY table.
802
+ # The test MS "AA2-Mid-sim_00000.ms" has that problem.
803
+ required_keys = {"COORDINATE_AXES", "ELEMENT_OFFSET", "ELEMENT_FLAG"}
804
+ if not all(k in raw_xds for k in required_keys):
805
+ return None
806
+
807
+ def msv4_measure(raw_name: str) -> dict:
808
+ coldesc = raw_xds.attrs["other"]["msv2"]["ctds_attrs"]["column_descriptions"]
809
+ return column_description_casacore_to_msv4_measure(coldesc[raw_name])
810
+
811
+ def make_data_variable(raw_name: str, dim_names: list[str]) -> xr.DataArray:
812
+ da = raw_xds[raw_name]
813
+ da = xr.DataArray(da.data, dims=tuple(dim_names))
814
+ return da.assign_attrs(msv4_measure(raw_name))
815
+
816
+ raw_datavar_names_and_dims = [
817
+ (
818
+ "COORDINATE_AXES",
819
+ ("antenna_name", "cartesian_pos_label_local", "cartesian_pos_label"),
820
+ ),
821
+ ("ELEMENT_OFFSET", ("antenna_name", "cartesian_pos_label_local", "element_id")),
822
+ ("ELEMENT_FLAG", ("antenna_name", "receptor_label", "element_id")),
823
+ ]
824
+
825
+ data_vars = {
826
+ name: make_data_variable(name, dims)
827
+ for name, dims in raw_datavar_names_and_dims
828
+ }
829
+ data_vars["COORDINATE_AXES"].attrs = {
830
+ "type": "rotation_matrix",
831
+ "units": ["undimensioned", "undimensioned", "undimensioned"],
832
+ }
833
+ # Remove the "frame" attribute if it exists, because ELEMENT_OFFSET is
834
+ # defined in a station-local frame for which no standard name exists
835
+ data_vars["ELEMENT_OFFSET"].attrs.pop("frame", None)
836
+ data_vars["ELEMENT_OFFSET"].attrs.update(
837
+ {
838
+ "coordinate_system": "topocentric",
839
+ "origin": "ANTENNA_POSITION",
840
+ }
841
+ )
842
+
843
+ num_elements = data_vars["ELEMENT_OFFSET"].sizes["element_id"]
844
+
845
+ data_vars = {"PHASED_ARRAY_" + key: val for key, val in data_vars.items()}
846
+ coords = {
847
+ "antenna_name": antenna_names,
848
+ "element_id": np.arange(num_elements),
849
+ "receptor_label": receptor_label,
850
+ "polarization_type": (
851
+ ("antenna_name", "receptor_label"),
852
+ polarization_type,
853
+ ),
854
+ "cartesian_pos_label": ["x", "y", "z"],
855
+ "cartesian_pos_label_local": ["p", "q", "r"],
856
+ }
857
+ attrs = {"type": "phased_array"}
858
+ return xr.Dataset(data_vars, coords, attrs)
@@ -26,6 +26,8 @@ TimeWeather = Literal["time_weather"]
26
26
  """ time dimension of weather dataset (when not interpolated to main time) """
27
27
  AntennaName = Literal["antenna_name"]
28
28
  """ Antenna name dimension """
29
+ ElementId = Literal["element_id"]
30
+ """ Element Id dimension of phased_array_xds"""
29
31
  StationName = Literal["station_name"]
30
32
  """ Station name dimension """
31
33
  ReceptorLabel = Literal["receptor_label"]
@@ -56,6 +58,8 @@ EllipsoidPosLabel = Literal["ellipsoid_pos_label"]
56
58
  """ Coordinate labels of geodetic earth location data (typically shape 3 and 'lon', 'lat', 'height')"""
57
59
  CartesianPosLabel = Literal["cartesian_pos_label"]
58
60
  """ Coordinate labels of geocentric earth location data (typically shape 3 and 'x', 'y', 'z')"""
61
+ CartesianPosLabelLocal = Literal["cartesian_pos_label_local"]
62
+ """ Coordinate labels for phased array elements positions relative to their parent station position; defined in a station-local frame (typically shape 3 and 'p', 'q', 'r')"""
59
63
  nPolynomial = Literal["n_polynomial"]
60
64
  """ For data that is represented as variable in time using Taylor expansion """
61
65
  PolyTerm = Literal["poly_term"]
@@ -76,9 +80,12 @@ SkyCoord = Literal["sky_coord"]
76
80
  SpectralCoord = Literal["spectral_coord"]
77
81
  Location = Literal["location"]
78
82
  Doppler = Literal["doppler"]
79
-
83
+ RotationMatrix = Literal["rotation_matrix"]
80
84
 
81
85
  # Units of quantities and measures
86
+ UnitsUndimensioned = list[
87
+ Literal["undimensioned"]
88
+ ] # name consistent with casacore measures
82
89
  UnitsSeconds = list[Literal["s"]]
83
90
  UnitsHertz = list[Literal["Hz"]]
84
91
  UnitsMeters = list[Literal["m"]]
@@ -562,6 +569,7 @@ AllowedLocationCoordinateSystems = Literal[
562
569
  "geodetic",
563
570
  "planetodetic",
564
571
  "orbital",
572
+ "topocentric",
565
573
  ]
566
574
 
567
575
 
@@ -2217,3 +2225,98 @@ class SpectrumXds:
2217
2225
  """The channel bandwidth that includes the effects of missing data."""
2218
2226
  FREQUENCY_CENTROID: Optional[Dataof[FrequencyCentroidArray]] = None
2219
2227
  """Includes the effects of missing data unlike ``frequency``."""
2228
+
2229
+
2230
+ @xarray_dataarray_schema
2231
+ class PhasedArrayElementOffsetArray:
2232
+ """
2233
+ Schema for PHASED_ARRAY_ELEMENT_OFFSET.
2234
+ """
2235
+
2236
+ data: Data[
2237
+ tuple[AntennaName, CartesianPosLabelLocal, ElementId],
2238
+ float,
2239
+ ]
2240
+
2241
+ units: Attr[list[Literal["m"]]]
2242
+
2243
+ type: Attr[Location]
2244
+ """ Measure type. Should be ``"location"``."""
2245
+
2246
+ coordinate_system: Attr[Literal["topocentric"]]
2247
+ """
2248
+ Coordinate system in which the element offsets are expressed.
2249
+ Should be ``"topocentric"``.
2250
+ """
2251
+
2252
+ origin: Attr[Literal["ANTENNA_POSITION"]]
2253
+ """ Origin of the coordinate system. Should be ``"ANTENNA_POSITION"``."""
2254
+
2255
+
2256
+ @xarray_dataarray_schema
2257
+ class PhasedArrayCoordinateAxesArray:
2258
+ """
2259
+ Schema for PHASED_ARRAY_COORDINATE_AXES
2260
+ """
2261
+
2262
+ data: Data[tuple[AntennaName, CartesianPosLabelLocal, CartesianPosLabel], float]
2263
+
2264
+ units: Attr[UnitsUndimensioned]
2265
+
2266
+ type: Attr[RotationMatrix]
2267
+ """ Measure type. Should be ``"rotation_matrix"``."""
2268
+
2269
+
2270
+ @xarray_dataset_schema
2271
+ class PhasedArrayXds:
2272
+ """
2273
+ Phased array dataset: define stations made of multiple receiver elements.
2274
+ """
2275
+
2276
+ # Coordinates
2277
+ antenna_name: Coordof[AntennaNameArray]
2278
+ """ Antenna name """
2279
+
2280
+ element_id: Coord[ElementId, Union[numpy.int64, numpy.int32]]
2281
+ """ Element Id within a station/antenna """
2282
+
2283
+ receptor_label: Coord[ReceptorLabel, str]
2284
+ """ Names of receptors, i.e. polarization hands. """
2285
+
2286
+ polarization_type: Coord[tuple[AntennaName, ReceptorLabel], str]
2287
+ """ Polarization type to which each receptor responds (e.g. ”R”,”L”,”X” or ”Y”).
2288
+ This is the receptor polarization type as recorded in the final correlated data (e.g. ”RR”); i.e.
2289
+ as measured after all polarization combiners. ['X','Y'], ['R','L'] """
2290
+
2291
+ cartesian_pos_label: Coord[CartesianPosLabel, str]
2292
+ """ (x,y,z) - either cartesian or ellipsoid """
2293
+
2294
+ cartesian_pos_label_local: Coord[CartesianPosLabelLocal, str]
2295
+ """ (p,q,r) - cartesian station-local frame of reference """
2296
+
2297
+ # Data variables
2298
+ PHASED_ARRAY_COORDINATE_AXES: Dataof[PhasedArrayCoordinateAxesArray]
2299
+ """
2300
+ 3x3 Rotation M such that X_geo = M X_local.
2301
+ Used to convert PHASED_ARRAY_ELEMENT_OFFSET coordinates from a station-local
2302
+ frame to a geocentric frame.
2303
+ """
2304
+
2305
+ PHASED_ARRAY_ELEMENT_OFFSET: Dataof[PhasedArrayElementOffsetArray]
2306
+ """
2307
+ Offsets of each array element from its parent station position, expressed
2308
+ in a station-local frame. Station positions are stored in
2309
+ antenna_xds.ANTENNA_POSITION.
2310
+ """
2311
+
2312
+ PHASED_ARRAY_ELEMENT_FLAG: Data[tuple[AntennaName, ReceptorLabel, ElementId], bool]
2313
+ """
2314
+ Boolean flag set to True if the data from a given polarisation receptor of a station element
2315
+ should be ignored.
2316
+ """
2317
+
2318
+ # Attributes
2319
+ type: Attr[Literal["phased_array"]]
2320
+ """
2321
+ Type of dataset. Expected to be ``phased_array``
2322
+ """
@@ -628,14 +628,15 @@ def check_datatree(
628
628
  continue
629
629
 
630
630
  # Look up schema
631
- schema = _DATASET_TYPES.get(node.attrs.get("type"))
631
+ typ = node.attrs.get("type")
632
+ schema = _DATASET_TYPES.get(typ)
632
633
  if schema is None:
633
634
  issues.add(
634
635
  SchemaIssue(
635
636
  [("", xds_name)],
636
637
  message="Unknown dataset type!",
637
638
  found=typ,
638
- expected=list(schemas.keys()),
639
+ expected=None,
639
640
  )
640
641
  )
641
642
  continue
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xradio
3
- Version: 0.0.52
3
+ Version: 0.0.53
4
4
  Summary: Xarray Radio Astronomy Data IO
5
5
  Author-email: Jan-Willem Steeb <jsteeb@nrao.edu>, Federico Montesino Pouzols <pouzols@eso.edu>, Dave Mehringer <dmehring@nrao.edu>, Peter Wortmann <peter.wortmann@skao.int>
6
6
  License: BSD 3-Clause License
File without changes
File without changes
File without changes
File without changes
File without changes