xradio 0.0.55__py3-none-any.whl → 0.0.58__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.
Files changed (64) hide show
  1. xradio/__init__.py +2 -2
  2. xradio/_utils/_casacore/casacore_from_casatools.py +1001 -0
  3. xradio/_utils/_casacore/tables.py +6 -1
  4. xradio/_utils/coord_math.py +22 -23
  5. xradio/_utils/dict_helpers.py +76 -11
  6. xradio/_utils/schema.py +5 -2
  7. xradio/_utils/zarr/common.py +1 -73
  8. xradio/image/_util/_casacore/common.py +11 -3
  9. xradio/image/_util/_casacore/xds_from_casacore.py +59 -35
  10. xradio/image/_util/_casacore/xds_to_casacore.py +47 -16
  11. xradio/image/_util/_fits/xds_from_fits.py +172 -77
  12. xradio/image/_util/casacore.py +9 -4
  13. xradio/image/_util/common.py +4 -4
  14. xradio/image/_util/image_factory.py +8 -8
  15. xradio/image/image.py +45 -5
  16. xradio/measurement_set/__init__.py +19 -9
  17. xradio/measurement_set/_utils/__init__.py +1 -3
  18. xradio/measurement_set/_utils/_msv2/__init__.py +0 -0
  19. xradio/measurement_set/_utils/_msv2/_tables/read.py +35 -90
  20. xradio/measurement_set/_utils/_msv2/_tables/read_main_table.py +6 -686
  21. xradio/measurement_set/_utils/_msv2/_tables/table_query.py +13 -3
  22. xradio/measurement_set/_utils/_msv2/conversion.py +129 -145
  23. xradio/measurement_set/_utils/_msv2/create_antenna_xds.py +9 -16
  24. xradio/measurement_set/_utils/_msv2/create_field_and_source_xds.py +125 -221
  25. xradio/measurement_set/_utils/_msv2/msv2_to_msv4_meta.py +1 -2
  26. xradio/measurement_set/_utils/_msv2/msv4_info_dicts.py +13 -8
  27. xradio/measurement_set/_utils/_msv2/msv4_sub_xdss.py +27 -72
  28. xradio/measurement_set/_utils/_msv2/partition_queries.py +5 -262
  29. xradio/measurement_set/_utils/_msv2/subtables.py +0 -107
  30. xradio/measurement_set/_utils/_utils/interpolate.py +60 -0
  31. xradio/measurement_set/_utils/_zarr/encoding.py +2 -7
  32. xradio/measurement_set/convert_msv2_to_processing_set.py +0 -2
  33. xradio/measurement_set/load_processing_set.py +2 -2
  34. xradio/measurement_set/measurement_set_xdt.py +14 -14
  35. xradio/measurement_set/open_processing_set.py +1 -3
  36. xradio/measurement_set/processing_set_xdt.py +41 -835
  37. xradio/measurement_set/schema.py +96 -123
  38. xradio/schema/check.py +91 -97
  39. xradio/schema/dataclass.py +159 -22
  40. xradio/schema/export.py +99 -0
  41. xradio/schema/metamodel.py +51 -16
  42. xradio/schema/typing.py +5 -5
  43. {xradio-0.0.55.dist-info → xradio-0.0.58.dist-info}/METADATA +43 -11
  44. xradio-0.0.58.dist-info/RECORD +65 -0
  45. {xradio-0.0.55.dist-info → xradio-0.0.58.dist-info}/WHEEL +1 -1
  46. xradio/image/_util/fits.py +0 -13
  47. xradio/measurement_set/_utils/_msv2/_tables/load.py +0 -63
  48. xradio/measurement_set/_utils/_msv2/_tables/load_main_table.py +0 -487
  49. xradio/measurement_set/_utils/_msv2/_tables/read_subtables.py +0 -395
  50. xradio/measurement_set/_utils/_msv2/_tables/write.py +0 -320
  51. xradio/measurement_set/_utils/_msv2/_tables/write_exp_api.py +0 -385
  52. xradio/measurement_set/_utils/_msv2/chunks.py +0 -115
  53. xradio/measurement_set/_utils/_msv2/descr.py +0 -165
  54. xradio/measurement_set/_utils/_msv2/msv2_msv3.py +0 -7
  55. xradio/measurement_set/_utils/_msv2/partitions.py +0 -392
  56. xradio/measurement_set/_utils/_utils/cds.py +0 -40
  57. xradio/measurement_set/_utils/_utils/xds_helper.py +0 -404
  58. xradio/measurement_set/_utils/_zarr/read.py +0 -263
  59. xradio/measurement_set/_utils/_zarr/write.py +0 -329
  60. xradio/measurement_set/_utils/msv2.py +0 -106
  61. xradio/measurement_set/_utils/zarr.py +0 -133
  62. xradio-0.0.55.dist-info/RECORD +0 -77
  63. {xradio-0.0.55.dist-info → xradio-0.0.58.dist-info}/licenses/LICENSE.txt +0 -0
  64. {xradio-0.0.55.dist-info → xradio-0.0.58.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,10 @@
1
- from casacore import tables
1
+ try:
2
+ from casacore import tables
3
+ except ImportError:
4
+ import xradio._utils._casacore.casacore_from_casatools as tables
5
+
2
6
  from contextlib import contextmanager
7
+ import os
3
8
  from typing import Dict, Generator
4
9
 
5
10
  # common casacore table handling code
@@ -63,38 +63,37 @@ def wrap_to_pi(angles):
63
63
  return (angles + np.pi) % (2 * np.pi) - np.pi
64
64
 
65
65
 
66
- def wrap_to_pi(angles):
67
- return (angles + np.pi) % (2 * np.pi) - np.pi
66
+ def convert_to_si_units(xds):
67
+ import xarray as xr
68
68
 
69
+ with xr.set_options(keep_attrs=True):
70
+ for data_var in xds.data_vars:
71
+ if "units" in xds[data_var].attrs:
72
+ u = xds[data_var].attrs["units"]
69
73
 
70
- def convert_to_si_units(xds):
71
- for data_var in xds.data_vars:
72
- if "units" in xds[data_var].attrs:
73
- for u_i, u in enumerate(xds[data_var].attrs["units"]):
74
74
  if u == "km":
75
- xds[data_var][..., u_i] = xds[data_var][..., u_i] * 1e3
76
- xds[data_var].attrs["units"][u_i] = "m"
75
+ xds[data_var] = xds[data_var] * 1e3
76
+ xds[data_var].attrs["units"] = "m"
77
77
  if u == "km/s":
78
- xds[data_var][..., u_i] = xds[data_var][..., u_i] * 1e3
79
- xds[data_var].attrs["units"][u_i] = "m/s"
78
+ xds[data_var] = xds[data_var] * 1e3
79
+ xds[data_var].attrs["units"] = "m/s"
80
80
  if u == "deg":
81
- xds[data_var][..., u_i] = xds[data_var][..., u_i] * np.pi / 180
82
- xds[data_var].attrs["units"][u_i] = "rad"
81
+ xds[data_var] = xds[data_var] * np.pi / 180
82
+ xds[data_var].attrs["units"] = "rad"
83
83
  if u == "Au" or u == "AU":
84
- xds[data_var][..., u_i] = xds[data_var][..., u_i] * 149597870700
85
- xds[data_var].attrs["units"][u_i] = "m"
84
+ xds[data_var] = xds[data_var] * 149597870700
85
+ xds[data_var].attrs["units"] = "m"
86
86
  if u == "Au/d" or u == "AU/d":
87
- xds[data_var][..., u_i] = (
88
- xds[data_var][..., u_i] * 149597870700 / 86400
89
- )
90
- xds[data_var].attrs["units"][u_i] = "m/s"
87
+ xds[data_var] = xds[data_var] * 149597870700 / 86400
88
+ xds[data_var].attrs["units"] = "m/s"
91
89
  if u == "arcsec":
92
- xds[data_var][..., u_i] = xds[data_var][..., u_i] * np.pi / 648000
93
- xds[data_var].attrs["units"][u_i] = "rad"
90
+ xds[data_var] = xds[data_var] * np.pi / 648000
91
+ xds[data_var].attrs["units"] = "rad"
94
92
  if u == "hPa":
95
- xds[data_var][..., u_i] = xds[data_var][..., u_i] * 100.0
96
- xds[data_var].attrs["units"][u_i] = "Pa"
93
+ xds[data_var] = xds[data_var] * 100.0
94
+ xds[data_var].attrs["units"] = "Pa"
97
95
  if u == "m-2":
98
96
  # IONOS_ELECTRON sometimes has "m-2" instead of "/m^2"
99
- xds[data_var].attrs["units"][u_i] = "/m^2"
97
+ xds[data_var].attrs["units"] = "/m^2"
98
+
100
99
  return xds
@@ -1,49 +1,114 @@
1
1
  def make_quantity(value, units: str, dims: list = []) -> dict:
2
2
  """
3
3
  create a quantity dictionary given value and units
4
+
4
5
  Parameters
5
6
  ----------
6
7
  value : numeric or array of numerics
7
8
  Quantity value
8
9
  units: str
9
10
  Quantity units
11
+
10
12
  Returns
11
13
  -------
12
14
  dict
13
15
  """
14
- u = units if isinstance(units, list) else [units]
15
- return {"data": value, "dims": dims, "attrs": {"units": u, "type": "quantity"}}
16
+ return {"data": value, "dims": dims, "attrs": make_quantity_attrs(units)}
17
+
18
+
19
+ def ensure_units_are_consistent(units):
20
+ if isinstance(units, str):
21
+ return units
22
+ else:
23
+ u0 = units[0]
24
+ for u in units:
25
+ assert u0 == u, f"Units are not consistent: {u0} != {u}. "
26
+ return u0
16
27
 
17
28
 
18
- def make_frequency_reference_dict(
29
+ def make_quantity_attrs(units: str) -> dict:
30
+ """
31
+ Creates the dict of attributes of a quantity
32
+
33
+ Parameters
34
+ ----------
35
+ units: str
36
+ Quantity units
37
+
38
+ Returns
39
+ -------
40
+ dict
41
+ """
42
+ return {"units": ensure_units_are_consistent(units), "type": "quantity"}
43
+
44
+
45
+ def make_spectral_coord_reference_dict(
19
46
  value: float, units: str, observer: str = "lsrk"
20
47
  ) -> dict:
21
- u = units if isinstance(units, list) else [units]
48
+ """
49
+ creates a spectral_coord measure dict given the value, units, and observer
50
+
51
+ Parameters
52
+ ----------
53
+ value : numeric or array of numerics
54
+ measure value
55
+ units : str
56
+ measure units
57
+ observer :
58
+ observer reference frame
59
+
60
+ Returns
61
+ -------
62
+ dict
63
+ """
64
+ u = ensure_units_are_consistent(units)
22
65
  return {
23
- "attrs": {"units": u, "observer": observer.lower(), "type": "frequency"},
66
+ "attrs": make_spectral_coord_measure_attrs(
67
+ u,
68
+ observer.lower() if observer not in ["TOPO", "BARY", "REST"] else observer,
69
+ ),
24
70
  "data": value,
25
71
  "dims": [],
26
72
  }
27
73
 
28
74
 
29
- def make_skycoord_dict(data: list[float], units: list[str], frame: str) -> dict:
75
+ def make_spectral_coord_measure_attrs(units: str, observer: str = "lsrk") -> dict:
76
+ """
77
+ Creates a spectral_coord measure attrs dict given units and observer
78
+
79
+ Parameters
80
+ ----------
81
+ units: str or list of str
82
+ Spectral coordinate units
83
+ observer: str
84
+ Spectral reference frame
85
+ Returns
86
+ -------
87
+ dict
88
+ Attrs dict for a spectral_coord measure
89
+ """
90
+ u = ensure_units_are_consistent(units)
91
+ return {"units": u, "observer": observer, "type": "spectral_coord"}
92
+
93
+
94
+ def make_skycoord_dict(data: list[float], units: str, frame: str) -> dict:
30
95
  return {
31
96
  "attrs": {
32
97
  "frame": frame.lower(),
33
98
  "type": "sky_coord",
34
- "units": units,
99
+ "units": ensure_units_are_consistent(units),
35
100
  },
36
101
  "data": data,
37
102
  "dims": ["l", "m"],
38
103
  }
39
104
 
40
105
 
41
- def make_time_measure_attrs(units=["s"], scale="utc", time_format="mjd") -> dict:
42
- u = units if isinstance(units, list) else [units]
106
+ def make_time_measure_attrs(units="s", scale="utc", time_format="mjd") -> dict:
107
+ u = ensure_units_are_consistent(units)
43
108
  return {"units": u, "scale": scale, "format": time_format, "type": "time"}
44
109
 
45
110
 
46
- def make_time_measure_dict(data, units=["s"], scale="utc", time_format="mjd") -> dict:
111
+ def make_time_measure_dict(data, units="s", scale="utc", time_format="mjd") -> dict:
47
112
  """
48
113
  create a time measure dictionary given value and units
49
114
  Parameters
@@ -67,7 +132,7 @@ def make_time_measure_dict(data, units=["s"], scale="utc", time_format="mjd") ->
67
132
  return x
68
133
 
69
134
 
70
- def make_time_coord_attrs(units=["s"], scale="utc", time_format="mjd") -> dict:
135
+ def make_time_coord_attrs(units="s", scale="utc", time_format="mjd") -> dict:
71
136
  """
72
137
  create a time measure dictionary given value and units
73
138
  Parameters
xradio/_utils/schema.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import toolviper.utils.logger as logger
2
2
  import xarray as xr
3
3
  from typing import Union
4
+ from xradio._utils.dict_helpers import ensure_units_are_consistent
4
5
 
5
6
 
6
7
  def convert_generic_xds_to_xradio_schema(
@@ -108,7 +109,7 @@ def column_description_casacore_to_msv4_measure(
108
109
 
109
110
  # Convert type, copy unit
110
111
  msv4_measure["type"] = msv4_measure_conversion["type"]
111
- msv4_measure["units"] = list(
112
+ msv4_measure["units"] = ensure_units_are_consistent(
112
113
  casacore_column_description["keywords"]["QuantumUnits"]
113
114
  )
114
115
 
@@ -154,7 +155,9 @@ def column_description_casacore_to_msv4_measure(
154
155
  elif "QuantumUnits" in casacore_column_description["keywords"]:
155
156
  msv4_measure = {
156
157
  "type": "quantity",
157
- "units": list(casacore_column_description["keywords"]["QuantumUnits"]),
158
+ "units": ensure_units_are_consistent(
159
+ casacore_column_description["keywords"]["QuantumUnits"]
160
+ ),
158
161
  }
159
162
 
160
163
  return msv4_measure
@@ -1,6 +1,4 @@
1
- import copy
2
1
  import xarray as xr
3
- import zarr
4
2
  import s3fs
5
3
  import os
6
4
  from botocore.exceptions import NoCredentialsError
@@ -34,7 +32,7 @@ def _get_file_system_and_items(ps_store: str):
34
32
  for bd in file_system.listdir(ps_store, detail=False)
35
33
  ]
36
34
 
37
- except (NoCredentialsError, PermissionError) as e:
35
+ except (NoCredentialsError, PermissionError):
38
36
  # only public, read-only buckets will be accessible
39
37
  # we will want to add messaging and error handling here
40
38
  file_system = s3fs.S3FileSystem(anon=True)
@@ -97,73 +95,3 @@ def _open_dataset(
97
95
  with dask.config.set(scheduler="synchronous"):
98
96
  xds = xds.load()
99
97
  return xds
100
-
101
-
102
- # Code to depricate:
103
- def _get_attrs(zarr_obj):
104
- """
105
- get attributes of zarr obj (groups or arrays)
106
- """
107
- return {k: v for k, v in zarr_obj.attrs.asdict().items() if not k.startswith("_NC")}
108
-
109
-
110
- def _load_no_dask_zarr(zarr_name, slice_dict={}):
111
- """
112
- Alternative to xarray open_zarr where the arrays are not Dask Arrays.
113
-
114
- slice_dict: A dictionary of slice objects for which values to read form a dimension.
115
- For example silce_dict={'time':slice(0,10)} would select the first 10 elements in the time dimension.
116
- If a dim is not specified all values are retruned.
117
- return:
118
- xarray.Dataset()
119
-
120
- #Should go into general utils.
121
- """
122
- # Used by xarray to store array labeling info in zarr meta data.
123
- DIMENSION_KEY = "_ARRAY_DIMENSIONS"
124
-
125
- # logger = _get_logger()
126
- zarr_group = zarr.open_group(store=zarr_name, mode="r")
127
- group_attrs = _get_attrs(zarr_group)
128
-
129
- slice_dict_complete = copy.deepcopy(slice_dict)
130
- coords = {}
131
- xds = xr.Dataset()
132
- for var_name, var in zarr_group.arrays():
133
- print("Hallo 3", var_name, var.shape)
134
- var_attrs = _get_attrs(var)
135
-
136
- for dim in var_attrs[DIMENSION_KEY]:
137
- if dim not in slice_dict_complete:
138
- slice_dict_complete[dim] = slice(None) # No slicing.
139
-
140
- if (var_attrs[DIMENSION_KEY][0] == var_name) and (
141
- len(var_attrs[DIMENSION_KEY]) == 1
142
- ):
143
- coord = var[
144
- slice_dict_complete[var_attrs[DIMENSION_KEY][0]]
145
- ] # Dimension coordinates.
146
- del var_attrs["_ARRAY_DIMENSIONS"]
147
- xds = xds.assign_coords({var_name: coord})
148
- xds[var_name].attrs = var_attrs
149
- else:
150
- # Construct slicing
151
- slicing_list = []
152
- for dim in var_attrs[DIMENSION_KEY]:
153
- slicing_list.append(slice_dict_complete[dim])
154
- slicing_tuple = tuple(slicing_list)
155
-
156
- print(var_attrs[DIMENSION_KEY])
157
-
158
- xds[var_name] = xr.DataArray(
159
- var[slicing_tuple], dims=var_attrs[DIMENSION_KEY]
160
- )
161
-
162
- if "coordinates" in var_attrs:
163
- del var_attrs["coordinates"]
164
- del var_attrs["_ARRAY_DIMENSIONS"]
165
- xds[var_name].attrs = var_attrs
166
-
167
- xds.attrs = group_attrs
168
-
169
- return xds
@@ -1,7 +1,10 @@
1
- from casacore import images
1
+ try:
2
+ from casacore import images
3
+ except ImportError:
4
+ import xradio._utils._casacore.casacore_from_casatools as images
5
+
2
6
  from contextlib import contextmanager
3
- import numpy as np
4
- from typing import Dict, Generator, List, Union
7
+ from typing import Generator, List
5
8
 
6
9
 
7
10
  @contextmanager
@@ -12,6 +15,7 @@ def _open_image_ro(infile: str) -> Generator[images.image, None, None]:
12
15
  finally:
13
16
  # there is no obvious way to close a python-casacore image, so
14
17
  # just delete the object to clear it from the table cache
18
+ # image.unlock() # not necessary
15
19
  del image
16
20
 
17
21
 
@@ -31,6 +35,10 @@ def _create_new_image(
31
35
  try:
32
36
  yield image
33
37
  finally:
38
+ # Explicitly calling unlock() is important for downstream parallel reads across multiple processes.
39
+ # Without it, Dask workers may mistakenly consider a freshly written image from another process
40
+ # as invalid—even if the image directory appears to be fully written and present.
41
+ image.unlock()
34
42
  del image
35
43
 
36
44
 
@@ -9,8 +9,15 @@ import toolviper.utils.logger as logger
9
9
  import numpy as np
10
10
  import xarray as xr
11
11
  from astropy import units as u
12
- from casacore import tables
13
- from casacore.images import coordinates, image as casa_image
12
+
13
+ try:
14
+ from casacore import tables
15
+ from casacore.images import coordinates, image as casa_image
16
+ except ImportError:
17
+ import xradio._utils._casacore.casacore_from_casatools as tables
18
+ import xradio._utils._casacore.casacore_from_casatools as coordinates
19
+ from xradio._utils._casacore.casacore_from_casatools import image as casa_image
20
+
14
21
 
15
22
  from .common import (
16
23
  _active_mask,
@@ -18,6 +25,7 @@ from .common import (
18
25
  _open_image_ro,
19
26
  _pointing_center,
20
27
  )
28
+
21
29
  from ..common import (
22
30
  _compute_linear_world_values,
23
31
  _compute_velocity_values,
@@ -33,7 +41,7 @@ from ...._utils._casacore.tables import extract_table_attributes, open_table_ro
33
41
  from xradio._utils.coord_math import _deg_to_rad
34
42
  from xradio._utils.dict_helpers import (
35
43
  _casacore_q_to_xradio_q,
36
- make_frequency_reference_dict,
44
+ make_spectral_coord_reference_dict,
37
45
  make_quantity,
38
46
  make_skycoord_dict,
39
47
  make_time_measure_dict,
@@ -77,34 +85,50 @@ def _casa_image_to_xds_image_attrs(image: casa_image, history: bool = True) -> d
77
85
  attrs["units"] = image.unit()
78
86
  attrs["telescope"] = {}
79
87
  telescope = attrs["telescope"]
88
+
80
89
  for k in ("observer", "obsdate", "telescope", "telescopeposition"):
81
90
  if k.startswith("telescope"):
82
91
  if k == "telescope":
83
92
  telescope["name"] = coord_dict[k]
84
93
  elif k in coord_dict:
85
94
  casa_pos = coord_dict[k]
86
- location = {}
87
- loc_attrs = {}
88
- loc_attrs["type"] = "location"
89
- loc_attrs["frame"] = casa_pos["refer"]
90
- """
91
- if casa_pos["refer"] == "ITRF":
92
- loc_attrs["ellipsoid"] = "GRS80"
93
- """
94
- loc_attrs["units"] = [
95
- casa_pos["m0"]["unit"],
96
- casa_pos["m1"]["unit"],
97
- casa_pos["m2"]["unit"],
98
- ]
99
- loc_attrs["coordinate_system"] = "geocentric"
100
- loc_attrs["origin_object_name"] = "earth"
101
- location["attrs"] = loc_attrs
102
- location["data"] = [
103
- casa_pos["m0"]["value"],
104
- casa_pos["m1"]["value"],
105
- casa_pos["m2"]["value"],
106
- ]
107
- telescope["location"] = location
95
+ telescope["direction"] = {
96
+ "attrs": {
97
+ "coordinate_system": "geocentric",
98
+ "frame": casa_pos["refer"],
99
+ "origin_object_name": "earth",
100
+ "type": "location",
101
+ "units": casa_pos["m0"]["unit"],
102
+ },
103
+ "data": [casa_pos["m0"]["value"], casa_pos["m1"]["value"]],
104
+ "dims": ["ellipsoid_dir_label"],
105
+ "coords": {
106
+ "ellipsoid_dir_label": {
107
+ "dims": ["ellipsoid_dir_label"],
108
+ "data": ["lon", "lat"],
109
+ }
110
+ },
111
+ }
112
+ telescope["distance"] = {
113
+ "attrs": {
114
+ "coordinate_system": "geocentric",
115
+ "frame": casa_pos["refer"],
116
+ "origin_object_name": "earth",
117
+ "type": "location",
118
+ "units": casa_pos["m2"]["unit"],
119
+ },
120
+ "data": [casa_pos["m2"]["value"]],
121
+ "dims": ["ellipsoid_dis_label"],
122
+ "coords": {
123
+ "ellipsoid_dis_label": {
124
+ "dims": ["ellipsoid_dis_label"],
125
+ "data": [
126
+ "dist",
127
+ ],
128
+ }
129
+ },
130
+ }
131
+
108
132
  """
109
133
  del (
110
134
  telescope["position"]["refer"],
@@ -147,7 +171,7 @@ def _casa_image_to_xds_image_attrs(image: casa_image, history: bool = True) -> d
147
171
  # associated with it.
148
172
  # point_center = coord_dict["pointingcenter"]
149
173
  attrs[_pointing_center] = make_skycoord_dict(
150
- coord_dict["pointingcenter"]["value"].tolist(), ["rad", "rad"], frame
174
+ coord_dict["pointingcenter"]["value"].tolist(), "rad", frame
151
175
  )
152
176
  imageinfo = meta_dict["imageinfo"]
153
177
  obj = "objectname"
@@ -213,7 +237,7 @@ def _add_time_attrs(xds: xr.Dataset, coord_dict: dict) -> xr.Dataset:
213
237
 
214
238
  def _add_vel_attrs(xds: xr.Dataset, coord_dict: dict) -> xr.Dataset:
215
239
  vel_coord = xds["velocity"]
216
- meta = {"units": ["m/s"]}
240
+ meta = {"units": "m/s"}
217
241
  for k in coord_dict:
218
242
  if k.startswith("spectral"):
219
243
  sd = coord_dict[k]
@@ -251,7 +275,7 @@ def _casa_image_to_xds_attrs(img_full_path: str) -> dict:
251
275
  dir_dict = {}
252
276
 
253
277
  dir_dict["reference"] = make_skycoord_dict(
254
- data=[0.0, 0.0], units=["rad", "rad"], frame=ap_system
278
+ data=[0.0, 0.0], units="rad", frame=ap_system
255
279
  )
256
280
  if ap_equinox:
257
281
  dir_dict["reference"]["attrs"]["equinox"] = ap_equinox
@@ -526,14 +550,14 @@ def _get_freq_values_attrs(
526
550
  cdelt=wcs["cdelt"],
527
551
  )
528
552
  attrs["rest_frequency"] = make_quantity(sd["restfreq"], "Hz")
529
- # attrs["type"] = "frequency"
530
- # attrs["units"] = sd["unit"]
531
- # attrs["frame"] = sd["system"]
532
- attrs["wave_unit"] = [sd["waveUnit"]]
553
+ attrs["type"] = "spectral_coord"
554
+ attrs["units"] = sd["unit"]
555
+ attrs["frame"] = sd["system"]
556
+ attrs["wave_units"] = sd["waveUnit"]
533
557
  # attrs["crval"] = sd["wcs"]["crval"]
534
558
  # attrs["cdelt"] = sd["wcs"]["cdelt"]
535
559
 
536
- attrs["reference_value"] = make_frequency_reference_dict(
560
+ attrs["reference_frequency"] = make_spectral_coord_reference_dict(
537
561
  value=sd["wcs"]["crval"],
538
562
  units=sd["unit"],
539
563
  observer=sd["system"],
@@ -664,7 +688,7 @@ def _get_time_values_attrs(cimage_coord_dict: dict) -> Tuple[List[float], dict]:
664
688
  attrs["type"] = "time"
665
689
  attrs["scale"] = cimage_coord_dict["obsdate"]["refer"]
666
690
  unit = cimage_coord_dict["obsdate"]["m0"]["unit"]
667
- attrs["units"] = unit if isinstance(unit, list) else [unit]
691
+ attrs["units"] = unit
668
692
  time_val = cimage_coord_dict["obsdate"]["m0"]["value"]
669
693
  attrs["format"] = _get_time_format(time_val, unit)
670
694
  return ([time_val], copy.deepcopy(attrs))
@@ -794,7 +818,7 @@ def _get_velocity_values_attrs(
794
818
  if not attrs:
795
819
  attrs["doppler_type"] = _doppler_types[0]
796
820
 
797
- attrs["units"] = ["m/s"]
821
+ attrs["units"] = "m/s"
798
822
  attrs["type"] = "doppler"
799
823
  return (
800
824
  _compute_velocity_values(