xradio 0.0.34__py3-none-any.whl → 0.0.37__py3-none-any.whl
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.
- xradio/_utils/list_and_array.py +3 -1
- xradio/_utils/schema.py +190 -0
- xradio/_utils/zarr/common.py +11 -5
- xradio/image/_util/_zarr/xds_from_zarr.py +15 -2
- xradio/image/_util/_zarr/zarr_low_level.py +65 -14
- xradio/schema/bases.py +37 -8
- xradio/schema/check.py +15 -3
- xradio/schema/dataclass.py +2 -2
- xradio/vis/_processing_set.py +136 -10
- xradio/vis/_vis_utils/_ms/_tables/read.py +9 -0
- xradio/vis/_vis_utils/_ms/conversion.py +129 -103
- xradio/vis/_vis_utils/_ms/create_antenna_xds.py +482 -0
- xradio/vis/_vis_utils/_ms/create_field_and_source_xds.py +91 -45
- xradio/vis/_vis_utils/_ms/msv2_to_msv4_meta.py +1 -105
- xradio/vis/_vis_utils/_ms/msv4_sub_xdss.py +22 -235
- xradio/vis/_vis_utils/_utils/xds_helper.py +10 -2
- xradio/vis/convert_msv2_to_processing_set.py +6 -1
- xradio/vis/load_processing_set.py +2 -2
- xradio/vis/read_processing_set.py +6 -3
- xradio/vis/schema.py +584 -169
- {xradio-0.0.34.dist-info → xradio-0.0.37.dist-info}/METADATA +1 -1
- {xradio-0.0.34.dist-info → xradio-0.0.37.dist-info}/RECORD +25 -23
- {xradio-0.0.34.dist-info → xradio-0.0.37.dist-info}/WHEEL +1 -1
- {xradio-0.0.34.dist-info → xradio-0.0.37.dist-info}/LICENSE.txt +0 -0
- {xradio-0.0.34.dist-info → xradio-0.0.37.dist-info}/top_level.txt +0 -0
|
@@ -5,9 +5,7 @@ from typing import Tuple, Union
|
|
|
5
5
|
import numpy as np
|
|
6
6
|
import xarray as xr
|
|
7
7
|
|
|
8
|
-
from xradio.
|
|
9
|
-
column_description_casacore_to_msv4_measure,
|
|
10
|
-
)
|
|
8
|
+
from xradio._utils.schema import column_description_casacore_to_msv4_measure
|
|
11
9
|
from xradio.vis._vis_utils._ms.msv4_sub_xdss import interpolate_to_time
|
|
12
10
|
from xradio.vis._vis_utils._ms.subtables import subt_rename_ids
|
|
13
11
|
from xradio.vis._vis_utils._ms._tables.read import (
|
|
@@ -32,7 +30,7 @@ def create_field_and_source_xds(
|
|
|
32
30
|
is_single_dish: bool,
|
|
33
31
|
time_min_max: Tuple[np.float64, np.float64],
|
|
34
32
|
ephemeris_interp_time: Union[xr.DataArray, None] = None,
|
|
35
|
-
):
|
|
33
|
+
) -> tuple[xr.Dataset, int]:
|
|
36
34
|
"""
|
|
37
35
|
Create a field and source xarray dataset (xds) from the given input file, field ID, and spectral window ID.
|
|
38
36
|
Data is extracted from the FIELD and SOURCE tables and if there is ephemeris data, it is also extracted.
|
|
@@ -46,6 +44,10 @@ def create_field_and_source_xds(
|
|
|
46
44
|
The field ids to select.
|
|
47
45
|
spectral_window_id : int
|
|
48
46
|
The ID of the spectral window.
|
|
47
|
+
field_times: list
|
|
48
|
+
Time data for field. It is the same as the time axis in the main MSv4 dataset and is used if more than one field is present.
|
|
49
|
+
is_single_dish: bool
|
|
50
|
+
whether the main xds has single-dish (SPECTRUM) data
|
|
49
51
|
time_min_max : Tuple[np.float64, np.float46]
|
|
50
52
|
Min / max times to constrain loading (usually to the time range relevant to an MSv4)
|
|
51
53
|
ephemeris_interp_time : Union[xr.DataArray, None]
|
|
@@ -55,14 +57,16 @@ def create_field_and_source_xds(
|
|
|
55
57
|
-------
|
|
56
58
|
field_and_source_xds : xr.Dataset
|
|
57
59
|
The xarray dataset containing the field and source information.
|
|
60
|
+
num_lines : int
|
|
61
|
+
Sum of num_lines for all unique sources.
|
|
58
62
|
"""
|
|
59
63
|
|
|
60
64
|
start_time = time.time()
|
|
61
65
|
|
|
62
|
-
field_and_source_xds = xr.Dataset()
|
|
66
|
+
field_and_source_xds = xr.Dataset(attrs={"type": "field_and_source"})
|
|
63
67
|
|
|
64
68
|
field_and_source_xds, ephemeris_path, ephemeris_table_name, source_id = (
|
|
65
|
-
|
|
69
|
+
extract_field_info_and_check_ephemeris(
|
|
66
70
|
field_and_source_xds, in_file, field_id, field_times, is_single_dish
|
|
67
71
|
)
|
|
68
72
|
)
|
|
@@ -77,7 +81,7 @@ def create_field_and_source_xds(
|
|
|
77
81
|
ephemeris_interp_time,
|
|
78
82
|
)
|
|
79
83
|
|
|
80
|
-
field_and_source_xds = extract_source_info(
|
|
84
|
+
field_and_source_xds, num_lines = extract_source_info(
|
|
81
85
|
field_and_source_xds, in_file, source_id, spectral_window_id
|
|
82
86
|
)
|
|
83
87
|
|
|
@@ -95,7 +99,7 @@ def create_field_and_source_xds(
|
|
|
95
99
|
if np.unique(field_and_source_xds[center_dv], axis=0).shape[0] == 1:
|
|
96
100
|
field_and_source_xds = field_and_source_xds.isel(time=0).drop_vars("time")
|
|
97
101
|
|
|
98
|
-
return field_and_source_xds, source_id
|
|
102
|
+
return field_and_source_xds, source_id, num_lines
|
|
99
103
|
|
|
100
104
|
|
|
101
105
|
def extract_ephemeris_info(
|
|
@@ -119,7 +123,7 @@ def extract_ephemeris_info(
|
|
|
119
123
|
The name of the ephemeris table.
|
|
120
124
|
time_min_max : Tuple[np.float46, np.float64]
|
|
121
125
|
Min / max times to constrain loading (usually to the time range relevant to an MSv4)
|
|
122
|
-
|
|
126
|
+
interp_time : Union[xr.DataArray, None]
|
|
123
127
|
Time axis to interpolate the data vars to (usually main MSv4 time)
|
|
124
128
|
|
|
125
129
|
Returns:
|
|
@@ -172,18 +176,17 @@ def extract_ephemeris_info(
|
|
|
172
176
|
else:
|
|
173
177
|
unit_keyword = "QuantumUnits"
|
|
174
178
|
|
|
179
|
+
# We are using the "time_ephemeris_axis" label because it might not match the optional time axis of the source and field info. If ephemeris_interpolate=True then rename it to time.
|
|
175
180
|
coords = {
|
|
176
181
|
"ellipsoid_pos_label": ["lon", "lat", "dist"],
|
|
177
|
-
"
|
|
178
|
-
"time"
|
|
179
|
-
].data, # We are using the "ephem_time" label because it might not match the optional time axis of the source and field info. If ephemeris_interpolate=True then rename it to time.
|
|
182
|
+
"time_ephemeris_axis": ephemeris_xds["time"].data,
|
|
180
183
|
"sky_pos_label": ["ra", "dec", "dist"],
|
|
181
184
|
}
|
|
182
185
|
|
|
183
186
|
temp_xds = xr.Dataset()
|
|
184
187
|
|
|
185
|
-
# Add mandatory data:
|
|
186
|
-
temp_xds["
|
|
188
|
+
# Add mandatory data: SOURCE_LOCATION (POSITION / sky_pos_label)
|
|
189
|
+
temp_xds["SOURCE_LOCATION"] = xr.DataArray(
|
|
187
190
|
np.column_stack(
|
|
188
191
|
(
|
|
189
192
|
ephemeris_xds["RA"].data,
|
|
@@ -191,7 +194,7 @@ def extract_ephemeris_info(
|
|
|
191
194
|
ephemeris_xds["Rho"].data,
|
|
192
195
|
)
|
|
193
196
|
),
|
|
194
|
-
dims=["
|
|
197
|
+
dims=["time_ephemeris_axis", "sky_pos_label"],
|
|
195
198
|
)
|
|
196
199
|
# Have to use cast_to_str because the ephemeris table units are not consistently in a list or a string.
|
|
197
200
|
sky_coord_units = [
|
|
@@ -199,13 +202,13 @@ def extract_ephemeris_info(
|
|
|
199
202
|
cast_to_str(ephemris_column_description["DEC"]["keywords"][unit_keyword]),
|
|
200
203
|
cast_to_str(ephemris_column_description["Rho"]["keywords"][unit_keyword]),
|
|
201
204
|
]
|
|
202
|
-
temp_xds["
|
|
205
|
+
temp_xds["SOURCE_LOCATION"].attrs.update(
|
|
203
206
|
{"type": "sky_coord", "frame": sky_coord_frame, "units": sky_coord_units}
|
|
204
207
|
)
|
|
205
208
|
|
|
206
209
|
# Add mandatory data: SOURCE_RADIAL_VELOCITY
|
|
207
210
|
temp_xds["SOURCE_RADIAL_VELOCITY"] = xr.DataArray(
|
|
208
|
-
ephemeris_xds["RadVel"].data, dims=["
|
|
211
|
+
ephemeris_xds["RadVel"].data, dims=["time_ephemeris_axis"]
|
|
209
212
|
)
|
|
210
213
|
temp_xds["SOURCE_RADIAL_VELOCITY"].attrs.update(
|
|
211
214
|
{
|
|
@@ -241,7 +244,7 @@ def extract_ephemeris_info(
|
|
|
241
244
|
# Add optional data NORTH_POLE_POSITION_ANGLE and NORTH_POLE_ANGULAR_DISTANCE
|
|
242
245
|
if "NP_ang" in ephemeris_xds.data_vars:
|
|
243
246
|
temp_xds["NORTH_POLE_POSITION_ANGLE"] = xr.DataArray(
|
|
244
|
-
ephemeris_xds["NP_ang"].data, dims=["
|
|
247
|
+
ephemeris_xds["NP_ang"].data, dims=["time_ephemeris_axis"]
|
|
245
248
|
)
|
|
246
249
|
temp_xds["NORTH_POLE_POSITION_ANGLE"].attrs.update(
|
|
247
250
|
{
|
|
@@ -256,7 +259,7 @@ def extract_ephemeris_info(
|
|
|
256
259
|
|
|
257
260
|
if "NP_dist" in ephemeris_xds.data_vars:
|
|
258
261
|
temp_xds["NORTH_POLE_ANGULAR_DISTANCE"] = xr.DataArray(
|
|
259
|
-
ephemeris_xds["NP_dist"].data, dims=["
|
|
262
|
+
ephemeris_xds["NP_dist"].data, dims=["time_ephemeris_axis"]
|
|
260
263
|
)
|
|
261
264
|
temp_xds["NORTH_POLE_ANGULAR_DISTANCE"].attrs.update(
|
|
262
265
|
{
|
|
@@ -286,7 +289,7 @@ def extract_ephemeris_info(
|
|
|
286
289
|
np.zeros(ephemeris_xds[key_lon].shape),
|
|
287
290
|
)
|
|
288
291
|
),
|
|
289
|
-
dims=["
|
|
292
|
+
dims=["time_ephemeris_axis", "ellipsoid_pos_label"],
|
|
290
293
|
)
|
|
291
294
|
|
|
292
295
|
temp_xds["SUB_OBSERVER_POSITION"].attrs.update(
|
|
@@ -316,7 +319,7 @@ def extract_ephemeris_info(
|
|
|
316
319
|
ephemeris_xds["r"].data,
|
|
317
320
|
)
|
|
318
321
|
),
|
|
319
|
-
dims=["
|
|
322
|
+
dims=["time_ephemeris_axis", "ellipsoid_pos_label"],
|
|
320
323
|
)
|
|
321
324
|
temp_xds["SUB_SOLAR_POSITION"].attrs.update(
|
|
322
325
|
{
|
|
@@ -341,7 +344,7 @@ def extract_ephemeris_info(
|
|
|
341
344
|
# Add optional data: HELIOCENTRIC_RADIAL_VELOCITY
|
|
342
345
|
if "rdot" in ephemeris_xds.data_vars:
|
|
343
346
|
temp_xds["HELIOCENTRIC_RADIAL_VELOCITY"] = xr.DataArray(
|
|
344
|
-
ephemeris_xds["rdot"].data, dims=["
|
|
347
|
+
ephemeris_xds["rdot"].data, dims=["time_ephemeris_axis"]
|
|
345
348
|
)
|
|
346
349
|
temp_xds["HELIOCENTRIC_RADIAL_VELOCITY"].attrs.update(
|
|
347
350
|
{
|
|
@@ -357,7 +360,7 @@ def extract_ephemeris_info(
|
|
|
357
360
|
# Add optional data: OBSERVER_PHASE_ANGLE
|
|
358
361
|
if "phang" in ephemeris_xds.data_vars:
|
|
359
362
|
temp_xds["OBSERVER_PHASE_ANGLE"] = xr.DataArray(
|
|
360
|
-
ephemeris_xds["phang"].data, dims=["
|
|
363
|
+
ephemeris_xds["phang"].data, dims=["time_ephemeris_axis"]
|
|
361
364
|
)
|
|
362
365
|
temp_xds["OBSERVER_PHASE_ANGLE"].attrs.update(
|
|
363
366
|
{
|
|
@@ -371,24 +374,33 @@ def extract_ephemeris_info(
|
|
|
371
374
|
)
|
|
372
375
|
|
|
373
376
|
temp_xds = temp_xds.assign_coords(coords)
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
+
time_coord_attrs = {
|
|
378
|
+
"type": "time",
|
|
379
|
+
"units": ["s"],
|
|
380
|
+
"scale": "UTC",
|
|
381
|
+
"format": "UNIX",
|
|
382
|
+
}
|
|
383
|
+
temp_xds["time_ephemeris_axis"].attrs.update(time_coord_attrs)
|
|
377
384
|
|
|
378
385
|
# Convert to si units and interpolate if ephemeris_interpolate=True:
|
|
379
386
|
temp_xds = convert_to_si_units(temp_xds)
|
|
380
387
|
temp_xds = interpolate_to_time(
|
|
381
|
-
temp_xds, interp_time, "field_and_source_xds", time_name="
|
|
388
|
+
temp_xds, interp_time, "field_and_source_xds", time_name="time_ephemeris_axis"
|
|
382
389
|
)
|
|
383
390
|
|
|
384
|
-
# If we interpolate rename the
|
|
391
|
+
# If we interpolate rename the time_ephemeris_axis axis to time.
|
|
385
392
|
if interp_time is not None:
|
|
386
|
-
|
|
393
|
+
time_coord = {"time": ("time_ephemeris_axis", interp_time.data)}
|
|
394
|
+
temp_xds = temp_xds.assign_coords(time_coord)
|
|
395
|
+
temp_xds.coords["time"].attrs.update(time_coord_attrs)
|
|
396
|
+
temp_xds = temp_xds.swap_dims({"time_ephemeris_axis": "time"}).drop_vars(
|
|
397
|
+
"time_ephemeris_axis"
|
|
398
|
+
)
|
|
387
399
|
|
|
388
400
|
xds = xr.merge([xds, temp_xds])
|
|
389
401
|
|
|
390
|
-
# Add the
|
|
391
|
-
# We also need to add a distance dimension to the FIELD_PHASE_CENTER or FIELD_REFERENCE_CENTER to match the
|
|
402
|
+
# Add the SOURCE_LOCATION to the FIELD_PHASE_CENTER or FIELD_REFERENCE_CENTER. Ephemeris obs: When loaded from the MSv2 field table the FIELD_REFERENCE_CENTER or FIELD_PHASE_CENTER only contain an offset from the SOURCE_LOCATION.
|
|
403
|
+
# We also need to add a distance dimension to the FIELD_PHASE_CENTER or FIELD_REFERENCE_CENTER to match the SOURCE_LOCATION.
|
|
392
404
|
# FIELD_PHASE_CENTER is used for interferometer data and FIELD_REFERENCE_CENTER is used for single dish data.
|
|
393
405
|
if is_single_dish:
|
|
394
406
|
center_dv = "FIELD_REFERENCE_CENTER"
|
|
@@ -405,25 +417,27 @@ def extract_ephemeris_info(
|
|
|
405
417
|
np.column_stack(
|
|
406
418
|
(xds[center_dv].values, np.zeros(xds[center_dv].values.shape[0]))
|
|
407
419
|
),
|
|
408
|
-
xds["
|
|
420
|
+
xds["SOURCE_LOCATION"].values,
|
|
409
421
|
),
|
|
410
|
-
dims=[xds["
|
|
422
|
+
dims=[xds["SOURCE_LOCATION"].dims[0], "sky_pos_label"],
|
|
411
423
|
)
|
|
412
424
|
else:
|
|
413
425
|
xds[center_dv] = xr.DataArray(
|
|
414
426
|
add_position_offsets(
|
|
415
427
|
np.append(xds[center_dv].values, 0),
|
|
416
|
-
xds["
|
|
428
|
+
xds["SOURCE_LOCATION"].values,
|
|
417
429
|
),
|
|
418
|
-
dims=[xds["
|
|
430
|
+
dims=[xds["SOURCE_LOCATION"].dims[0], "sky_pos_label"],
|
|
419
431
|
)
|
|
420
432
|
|
|
421
|
-
xds[center_dv].attrs.update(xds["
|
|
433
|
+
xds[center_dv].attrs.update(xds["SOURCE_LOCATION"].attrs)
|
|
422
434
|
|
|
423
435
|
return xds
|
|
424
436
|
|
|
425
437
|
|
|
426
|
-
def extract_source_info(
|
|
438
|
+
def extract_source_info(
|
|
439
|
+
xds: xr.Dataset, path: str, source_id: int, spectral_window_id: int
|
|
440
|
+
) -> tuple[xr.Dataset, int]:
|
|
427
441
|
"""
|
|
428
442
|
Extracts source information from the given path and adds it to the xarray dataset.
|
|
429
443
|
|
|
@@ -442,6 +456,8 @@ def extract_source_info(xds, path, source_id, spectral_window_id):
|
|
|
442
456
|
-------
|
|
443
457
|
xds : xr.Dataset
|
|
444
458
|
The xarray dataset with the added source information.
|
|
459
|
+
num_lines : int
|
|
460
|
+
Sum of num_lines for all unique sources extracted.
|
|
445
461
|
"""
|
|
446
462
|
coords = {}
|
|
447
463
|
is_ephemeris = xds.attrs[
|
|
@@ -455,7 +471,14 @@ def extract_source_info(xds, path, source_id, spectral_window_id):
|
|
|
455
471
|
xds = xds.assign_coords(
|
|
456
472
|
{"source_name": "Unknown"}
|
|
457
473
|
) # Need to add this for ps.summary() to work.
|
|
458
|
-
return xds
|
|
474
|
+
return xds, 0
|
|
475
|
+
|
|
476
|
+
if not os.path.isdir(os.path.join(path, "SOURCE")):
|
|
477
|
+
logger.warning(
|
|
478
|
+
f"Could not find SOURCE table for source_id {source_id}. Source information will not be included in the field_and_source_xds."
|
|
479
|
+
)
|
|
480
|
+
xds = xds.assign_coords({"source_name": "Unknown"})
|
|
481
|
+
return xds, 0
|
|
459
482
|
|
|
460
483
|
unique_source_id = unique_1d(source_id)
|
|
461
484
|
taql_where = f"where (SOURCE_ID IN [{','.join(map(str, unique_source_id))}]) AND (SPECTRAL_WINDOW_ID = {spectral_window_id})"
|
|
@@ -474,7 +497,7 @@ def extract_source_info(xds, path, source_id, spectral_window_id):
|
|
|
474
497
|
xds = xds.assign_coords(
|
|
475
498
|
{"source_name": "Unknown"}
|
|
476
499
|
) # Need to add this for ps.summary() to work.
|
|
477
|
-
return xds
|
|
500
|
+
return xds, 0
|
|
478
501
|
|
|
479
502
|
assert (
|
|
480
503
|
len(source_xds.SPECTRAL_WINDOW_ID) == 1
|
|
@@ -530,8 +553,12 @@ def extract_source_info(xds, path, source_id, spectral_window_id):
|
|
|
530
553
|
else:
|
|
531
554
|
direction_var = source_xds[direction_msv2_col]
|
|
532
555
|
|
|
533
|
-
|
|
534
|
-
xds["
|
|
556
|
+
# SOURCE_LOCATION (DIRECTION / sky_dir_label)
|
|
557
|
+
xds["SOURCE_LOCATION"] = xr.DataArray(direction_var.data, dims=direction_dims)
|
|
558
|
+
location_msv4_measure = column_description_casacore_to_msv4_measure(
|
|
559
|
+
source_column_description[direction_msv2_col]
|
|
560
|
+
)
|
|
561
|
+
xds["SOURCE_LOCATION"].attrs.update(location_msv4_measure)
|
|
535
562
|
|
|
536
563
|
# Do we have line data:
|
|
537
564
|
if source_xds["NUM_LINES"].data.ndim == 0:
|
|
@@ -557,12 +584,19 @@ def extract_source_info(xds, path, source_id, spectral_window_id):
|
|
|
557
584
|
transition_var_data, max(transition_var_data.shape, vars_shape)
|
|
558
585
|
)
|
|
559
586
|
|
|
587
|
+
line_label_data = np.arange(coords_lines_data.shape[-1]).astype(str)
|
|
560
588
|
if len(source_id) == 1:
|
|
561
|
-
coords_lines = {
|
|
589
|
+
coords_lines = {
|
|
590
|
+
"line_name": ("line_label", coords_lines_data),
|
|
591
|
+
"line_label": line_label_data,
|
|
592
|
+
}
|
|
562
593
|
xds = xds.assign_coords(coords_lines)
|
|
563
594
|
line_dims = ["line_label"]
|
|
564
595
|
else:
|
|
565
|
-
coords_lines = {
|
|
596
|
+
coords_lines = {
|
|
597
|
+
"line_name": (("time", "line_label"), coords_lines_data),
|
|
598
|
+
"line_label": line_label_data,
|
|
599
|
+
}
|
|
566
600
|
xds = xds.assign_coords(coords_lines)
|
|
567
601
|
line_dims = ["time", "line_label"]
|
|
568
602
|
|
|
@@ -594,10 +628,13 @@ def extract_source_info(xds, path, source_id, spectral_window_id):
|
|
|
594
628
|
pass
|
|
595
629
|
|
|
596
630
|
xds = xds.assign_coords(coords)
|
|
597
|
-
return xds
|
|
598
631
|
|
|
632
|
+
_, unique_source_ids_indices = np.unique(source_xds.SOURCE_ID, return_index=True)
|
|
599
633
|
|
|
600
|
-
|
|
634
|
+
return xds, np.sum(num_lines[unique_source_ids_indices])
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def extract_field_info_and_check_ephemeris(
|
|
601
638
|
field_and_source_xds, in_file, field_id, field_times, is_single_dish
|
|
602
639
|
):
|
|
603
640
|
"""
|
|
@@ -738,4 +775,13 @@ def create_field_info_and_check_ephemeris(
|
|
|
738
775
|
field_and_source_xds[msv4_name].attrs["type"] = field_measures_type
|
|
739
776
|
|
|
740
777
|
field_and_source_xds = field_and_source_xds.assign_coords(coords)
|
|
778
|
+
if "time" in field_and_source_xds:
|
|
779
|
+
time_column_description = field_xds.attrs["other"]["msv2"]["ctds_attrs"][
|
|
780
|
+
"column_descriptions"
|
|
781
|
+
]["TIME"]
|
|
782
|
+
time_msv4_measure = column_description_casacore_to_msv4_measure(
|
|
783
|
+
time_column_description
|
|
784
|
+
)
|
|
785
|
+
field_and_source_xds.coords["time"].attrs.update(time_msv4_measure)
|
|
786
|
+
|
|
741
787
|
return field_and_source_xds, ephemeris_path, ephemeris_table_name, source_id
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import graphviper.utils.logger as logger
|
|
2
|
+
from xradio._utils.schema import column_description_casacore_to_msv4_measure
|
|
2
3
|
|
|
3
4
|
col_to_data_variable_names = {
|
|
4
5
|
"FLOAT_DATA": "SPECTRUM",
|
|
@@ -30,111 +31,6 @@ col_to_coord_names = {
|
|
|
30
31
|
"ANTENNA2": "baseline_ant2_id",
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
# Map casacore measures to astropy
|
|
34
|
-
casacore_to_msv4_measure_type = {
|
|
35
|
-
"quanta": {
|
|
36
|
-
"type": "quantity",
|
|
37
|
-
},
|
|
38
|
-
"direction": {"type": "sky_coord", "Ref": "frame", "Ref_map": {"J2000": "fk5"}},
|
|
39
|
-
"epoch": {"type": "time", "Ref": "scale", "Ref_map": {"UTC": "utc"}},
|
|
40
|
-
"frequency": {
|
|
41
|
-
"type": "spectral_coord",
|
|
42
|
-
"Ref": "frame",
|
|
43
|
-
"Ref_map": {
|
|
44
|
-
"REST": "REST",
|
|
45
|
-
"LSRK": "LSRK",
|
|
46
|
-
"LSRD": "LSRD",
|
|
47
|
-
"BARY": "BARY",
|
|
48
|
-
"GEO": "GEO",
|
|
49
|
-
"TOPO": "TOPO",
|
|
50
|
-
"GALACTO": "GALACTO",
|
|
51
|
-
"LGROUP": "LGROUP",
|
|
52
|
-
"CMB": "CMB",
|
|
53
|
-
"Undefined": "Undefined",
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
"position": {
|
|
57
|
-
"type": "earth_location",
|
|
58
|
-
"Ref": "ellipsoid",
|
|
59
|
-
"Ref_map": {"ITRF": "GRS80"},
|
|
60
|
-
},
|
|
61
|
-
"uvw": {"type": "uvw", "Ref": "frame", "Ref_map": {"ITRF": "GRS80"}},
|
|
62
|
-
"radialvelocity": {"type": "quantity"},
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
casa_frequency_frames = [
|
|
66
|
-
"REST",
|
|
67
|
-
"LSRK",
|
|
68
|
-
"LSRD",
|
|
69
|
-
"BARY",
|
|
70
|
-
"GEO",
|
|
71
|
-
"TOPO",
|
|
72
|
-
"GALACTO",
|
|
73
|
-
"LGROUP",
|
|
74
|
-
"CMB",
|
|
75
|
-
"Undefined",
|
|
76
|
-
]
|
|
77
|
-
|
|
78
|
-
casa_frequency_frames_codes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 64]
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def column_description_casacore_to_msv4_measure(
|
|
82
|
-
casacore_column_description, ref_code=None, time_format="UNIX"
|
|
83
|
-
):
|
|
84
|
-
import numpy as np
|
|
85
|
-
|
|
86
|
-
msv4_measure = {}
|
|
87
|
-
if "MEASINFO" in casacore_column_description["keywords"]:
|
|
88
|
-
measinfo = casacore_column_description["keywords"]["MEASINFO"]
|
|
89
|
-
|
|
90
|
-
# Get conversion information
|
|
91
|
-
msv4_measure_conversion = casacore_to_msv4_measure_type[measinfo["type"]]
|
|
92
|
-
|
|
93
|
-
# Convert type, copy unit
|
|
94
|
-
msv4_measure["type"] = msv4_measure_conversion["type"]
|
|
95
|
-
msv4_measure["units"] = list(
|
|
96
|
-
casacore_column_description["keywords"]["QuantumUnits"]
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
# Reference frame to convert?
|
|
100
|
-
if "Ref" in msv4_measure_conversion:
|
|
101
|
-
# Find reference frame
|
|
102
|
-
if "TabRefCodes" in measinfo:
|
|
103
|
-
ref_index = np.where(measinfo["TabRefCodes"] == ref_code)[0][0]
|
|
104
|
-
casa_ref = measinfo["TabRefTypes"][ref_index]
|
|
105
|
-
elif "Ref" in measinfo:
|
|
106
|
-
casa_ref = measinfo["Ref"]
|
|
107
|
-
elif measinfo["type"] == "frequency":
|
|
108
|
-
# Some MSv2 don't have the "TabRefCodes".
|
|
109
|
-
ref_index = np.where(casa_frequency_frames_codes == ref_code)[0][0]
|
|
110
|
-
casa_ref = casa_frequency_frames[ref_index]
|
|
111
|
-
else:
|
|
112
|
-
logger.debug(
|
|
113
|
-
f"Could not determine {measinfo['type']} measure "
|
|
114
|
-
"reference frame!"
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
# Convert into MSv4 representation of reference frame, warn if unknown
|
|
118
|
-
if casa_ref in msv4_measure_conversion.get("Ref_map", {}):
|
|
119
|
-
casa_ref = msv4_measure_conversion["Ref_map"][casa_ref]
|
|
120
|
-
else:
|
|
121
|
-
logger.debug(
|
|
122
|
-
f"Unknown reference frame for {measinfo['type']} "
|
|
123
|
-
f"measure, using verbatim: {casa_ref}"
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
msv4_measure[msv4_measure_conversion["Ref"]] = casa_ref
|
|
127
|
-
|
|
128
|
-
if msv4_measure["type"] == "time":
|
|
129
|
-
msv4_measure["format"] = time_format
|
|
130
|
-
elif "QuantumUnits" in casacore_column_description["keywords"]:
|
|
131
|
-
msv4_measure = {
|
|
132
|
-
"type": "quantity",
|
|
133
|
-
"units": list(casacore_column_description["keywords"]["QuantumUnits"]),
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return msv4_measure
|
|
137
|
-
|
|
138
34
|
|
|
139
35
|
def create_attribute_metadata(col, main_column_descriptions):
|
|
140
36
|
attrs_metadata = column_description_casacore_to_msv4_measure(
|