backscattermeter_analyzer 2.0.0__tar.gz → 2.1.1__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.
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/PKG-INFO +1 -1
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/pyproject.toml +1 -1
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/__init__.py +25 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/characterize/__init__.py +9 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/io/__init__.py +15 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/io/hdf5/__init__.py +7 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/io/hdf5/measurement.py +156 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/io/hdf5/utils.py +322 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/io/registry/__init__.py +16 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/io/registry/default.py +5 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/io/registry/interface/__init__.py +9 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/io/registry/interface/unified_read_write.py +108 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/io/registry/interface/unified_read_write_method.py +27 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/io/registry/unified_input_registry.py +226 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/io/registry/unified_io_registry.py +43 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/io/registry/unified_io_registry_base.py +586 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/io/registry/unified_output_registry.py +183 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/scale.py +10 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/__init__.py +4 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/utils/measurement/connect.py +50 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/utils/measurement/measurement.py +137 -0
- backscattermeter_analyzer-2.1.1/src/backscattermeter_analyzer/utils/utils.py +124 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/__init__.py +0 -9
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/characterize/__init__.py +0 -1
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/measurement.py +0 -34
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/LICENSE +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/README.md +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/display.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/justfile +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/mise.toml +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/axis.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/characterize/angle/__init__.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/characterize/angle/result.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/characterize/distance/__init__.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/characterize/distance/distance.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/characterize/distance/measurement.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/characterize/distance/ratio.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/characterize/distance/result.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/characterize/distance/utils.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/characterize/distance/vector.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/direction.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/hardware/__init__.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/hardware/backscattermeter.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/hardware/camera/__init__.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/hardware/motor/__init__.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/hardware/motor/jump.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/hardware/motor/path.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/hardware/motor/step_numbers.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/hardware/optic/__init__.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/distance/__init__.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/distance/distance.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/distance/motor/__init__.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/distance/motor/no.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/distance/motor/state.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/distance/motor/yes.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/distance/state.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/measurement/__init__.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/measurement/angle/__init__.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/measurement/angle/no.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/measurement/angle/state.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/measurement/angle/yes.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/measurement/camera/__init__.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/measurement/camera/no.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/measurement/camera/state.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/measurement/camera/yes.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/measurement/motor/__init__.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/measurement/motor/no.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/measurement/motor/state.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/measurement/motor/yes.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/src/backscattermeter_analyzer/utils/measurement/state.py +0 -0
- {backscattermeter_analyzer-2.0.0 → backscattermeter_analyzer-2.1.1}/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: backscattermeter_analyzer
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: Tool to work on the backscattermeter at LAPP
|
|
5
5
|
Project-URL: Documentation, https://git.linarphy.net/linarphy/backscattermeter_analyzer/wiki
|
|
6
6
|
Project-URL: Homepage, https://git.linarphy.net/linarphy/backscattermeter_analyzer
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Entry-point package."""
|
|
2
|
+
|
|
3
|
+
from .axis import Axis # noqa: I001
|
|
4
|
+
from .direction import Direction
|
|
5
|
+
|
|
6
|
+
from .hardware import Backscattermeter, Camera, MotorJump, MotorPath
|
|
7
|
+
from .io import register_measurement_hdf5
|
|
8
|
+
|
|
9
|
+
from .characterize import DistanceRatio, DistanceResult
|
|
10
|
+
from .utils import Distance, Measurement
|
|
11
|
+
|
|
12
|
+
register_measurement_hdf5()
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"Axis",
|
|
16
|
+
"Backscattermeter",
|
|
17
|
+
"Camera",
|
|
18
|
+
"Direction",
|
|
19
|
+
"Distance",
|
|
20
|
+
"DistanceRatio",
|
|
21
|
+
"DistanceResult",
|
|
22
|
+
"Measurement",
|
|
23
|
+
"MotorJump",
|
|
24
|
+
"MotorPath",
|
|
25
|
+
]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""I/O package."""
|
|
2
|
+
|
|
3
|
+
from .hdf5 import register_measurement_hdf5
|
|
4
|
+
from .registry import (
|
|
5
|
+
UnifiedIORegistry,
|
|
6
|
+
UnifiedReadWrite,
|
|
7
|
+
UnifiedReadWriteMethod,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"UnifiedIORegistry",
|
|
12
|
+
"UnifiedReadWrite",
|
|
13
|
+
"UnifiedReadWriteMethod",
|
|
14
|
+
"register_measurement_hdf5",
|
|
15
|
+
]
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""Hdf5 conversion for Measurement."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from h5py import File, Group
|
|
7
|
+
|
|
8
|
+
from backscattermeter_analyzer.hardware import Backscattermeter
|
|
9
|
+
from backscattermeter_analyzer.io.registry.default import (
|
|
10
|
+
default_registry,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from .utils import (
|
|
14
|
+
group_to_timeseriesdict,
|
|
15
|
+
is_hdf5,
|
|
16
|
+
timeseriesdict_to_group,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from backscattermeter_analyzer.utils.measurement.measurement import ( # noqa:E501,TC004
|
|
21
|
+
Measurement,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def read(input_: Path | Group, path: str | None = None) -> Measurement:
|
|
26
|
+
"""
|
|
27
|
+
Read a Measurement object from an HDF5 component/file.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
input_: Path | Group
|
|
32
|
+
If a string or a Path, the filename to read the table from.
|
|
33
|
+
If an h5py object, either the file or the group object to read
|
|
34
|
+
the Measurement from.
|
|
35
|
+
path: str | None
|
|
36
|
+
The path from which to read the table inside the HDF5 file.
|
|
37
|
+
This should be relative to the input if the input is a Group.
|
|
38
|
+
|
|
39
|
+
"""
|
|
40
|
+
from backscattermeter_analyzer.utils.measurement.measurement import ( # noqa: PLC0415,E501
|
|
41
|
+
Measurement,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
if isinstance(input_, Path):
|
|
45
|
+
input_ = File(input_)
|
|
46
|
+
|
|
47
|
+
# input_ is now a Group (a File is a Group)
|
|
48
|
+
if path is not None:
|
|
49
|
+
item = input_.get(path, default=None)
|
|
50
|
+
if item is None:
|
|
51
|
+
message = f"{path} is not a valid path in {input_}."
|
|
52
|
+
raise ValueError(message)
|
|
53
|
+
if not isinstance(item, Group):
|
|
54
|
+
message = (
|
|
55
|
+
f"{path} from {input_} is a {type(item)}, not a Group."
|
|
56
|
+
)
|
|
57
|
+
raise TypeError(message)
|
|
58
|
+
else:
|
|
59
|
+
item = input_
|
|
60
|
+
|
|
61
|
+
group_data = item.get("data", default=None)
|
|
62
|
+
if group_data is None:
|
|
63
|
+
message = f"No 'data' group in {path} from {input_}."
|
|
64
|
+
raise ValueError(message)
|
|
65
|
+
if not isinstance(group_data, Group):
|
|
66
|
+
message = f"'data' must be a group in {path} from {input_}."
|
|
67
|
+
raise TypeError(message)
|
|
68
|
+
|
|
69
|
+
measurement = Measurement(
|
|
70
|
+
data=group_to_timeseriesdict(group_data),
|
|
71
|
+
backscattermeter=Backscattermeter(),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if isinstance(input_, File):
|
|
75
|
+
input_.close()
|
|
76
|
+
|
|
77
|
+
return measurement
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def write(
|
|
81
|
+
measurement: Measurement,
|
|
82
|
+
output: Path | Group,
|
|
83
|
+
path: str | None = None,
|
|
84
|
+
*,
|
|
85
|
+
append: bool = False,
|
|
86
|
+
overwrite: bool = False,
|
|
87
|
+
) -> None:
|
|
88
|
+
"""
|
|
89
|
+
Write a Measurement object to an HDF5 file.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
measurement: Measurement
|
|
94
|
+
Measurement that is to be written to file.
|
|
95
|
+
output: Path | Group
|
|
96
|
+
If a Path, the path of the file to write the measurement to. If
|
|
97
|
+
a Group, the group object to write the measurement to, depending
|
|
98
|
+
on `path` value.
|
|
99
|
+
path: str | None
|
|
100
|
+
The path to which to write the table inside the HDF5 file.
|
|
101
|
+
This should be relative to the output Group (or the root Group
|
|
102
|
+
if output is a Path). Default to None.
|
|
103
|
+
append: bool
|
|
104
|
+
Whether to append the Measurement to an existing HDF5 file.
|
|
105
|
+
Default to False. This can delete a previous measurement.
|
|
106
|
+
overwrite: bool
|
|
107
|
+
Whether to overwrite any existing file without warning. Default
|
|
108
|
+
to False. If `append=True` and `overwrite=True`, then only the
|
|
109
|
+
group will be replaced.
|
|
110
|
+
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
if isinstance(output, Path):
|
|
114
|
+
if overwrite and not append:
|
|
115
|
+
output = File(output, mode="w")
|
|
116
|
+
elif append:
|
|
117
|
+
output = File(output, mode="r+")
|
|
118
|
+
else:
|
|
119
|
+
output = File(output, mode="x")
|
|
120
|
+
|
|
121
|
+
# output is now a Group (a File is a Group)
|
|
122
|
+
if path is not None:
|
|
123
|
+
item = output.get(path, default=None)
|
|
124
|
+
if item is None:
|
|
125
|
+
message = f"{path} is not a valid path in {output}."
|
|
126
|
+
raise ValueError(message)
|
|
127
|
+
if not isinstance(item, Group):
|
|
128
|
+
message = (
|
|
129
|
+
f"{path} from {output} is a {type(item)}, not a Group."
|
|
130
|
+
)
|
|
131
|
+
raise TypeError(message)
|
|
132
|
+
else:
|
|
133
|
+
item = output
|
|
134
|
+
|
|
135
|
+
element_number = len(item.keys())
|
|
136
|
+
if element_number != 0 and append:
|
|
137
|
+
item.clear()
|
|
138
|
+
elif element_number != 0 and not append:
|
|
139
|
+
message = f"{path} from {output} is not an empty group."
|
|
140
|
+
raise ValueError(message)
|
|
141
|
+
|
|
142
|
+
timeseriesdict_to_group(item, "data", measurement.data)
|
|
143
|
+
|
|
144
|
+
if isinstance(output, File):
|
|
145
|
+
output.close()
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def register_measurement() -> None:
|
|
149
|
+
"""Register Measurement hdf5 conversion in Unified I/O."""
|
|
150
|
+
from backscattermeter_analyzer.utils.measurement.measurement import ( # noqa: PLC0415,E501
|
|
151
|
+
Measurement,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
default_registry.register_identifier("hdf5", Measurement, is_hdf5)
|
|
155
|
+
default_registry.register_reader("hdf5", Measurement, read)
|
|
156
|
+
default_registry.register_writer("hdf5", Measurement, write)
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
"""Utils."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, IO, Literal
|
|
5
|
+
|
|
6
|
+
from astropy.io.misc.hdf5 import HDF5_SIGNATURE
|
|
7
|
+
from astropy.units import Quantity
|
|
8
|
+
from gwpy.detector.channel import Channel
|
|
9
|
+
from gwpy.frequencyseries import FrequencySeries
|
|
10
|
+
from gwpy.timeseries import TimeSeries, TimeSeriesDict
|
|
11
|
+
from h5py import Dataset, Group
|
|
12
|
+
from numpy import array, geomspace
|
|
13
|
+
|
|
14
|
+
from backscattermeter_analyzer.io.registry.default import (
|
|
15
|
+
default_registry,
|
|
16
|
+
)
|
|
17
|
+
from backscattermeter_analyzer.scale import Scale
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def dataset_to_frequencyseries(dataset: Dataset) -> FrequencySeries:
|
|
21
|
+
"""
|
|
22
|
+
Convert a hdf5 dataset to a FrequencySeries.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
dataset: Dataset
|
|
27
|
+
Dataset to convert.
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
FrequencySeries
|
|
32
|
+
FrequencySeries.
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
unit = None
|
|
36
|
+
scale = Scale.LINEAR
|
|
37
|
+
if "units" in dataset.attrs:
|
|
38
|
+
unit = dataset.attrs["units"]
|
|
39
|
+
|
|
40
|
+
if "scale" in dataset.attrs:
|
|
41
|
+
scale = Scale(dataset.attrs["scale"])
|
|
42
|
+
|
|
43
|
+
if scale is Scale.LINEAR:
|
|
44
|
+
if "f0" not in dataset.attrs:
|
|
45
|
+
message = f"f0 not defined in dataset {dataset.name}"
|
|
46
|
+
raise ValueError(message)
|
|
47
|
+
if "df" not in dataset.attrs:
|
|
48
|
+
message = f"df not defined in dataset {dataset.name}"
|
|
49
|
+
raise ValueError(message)
|
|
50
|
+
|
|
51
|
+
f0 = Quantity(dataset.attrs["f0"])
|
|
52
|
+
df = Quantity(dataset.attrs["df"])
|
|
53
|
+
|
|
54
|
+
return FrequencySeries(
|
|
55
|
+
data=array(dataset),
|
|
56
|
+
unit=unit,
|
|
57
|
+
f0=f0,
|
|
58
|
+
df=df,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
if "start" not in dataset.attrs:
|
|
62
|
+
message = f"start not defined in dataset {dataset.name}"
|
|
63
|
+
raise ValueError(message)
|
|
64
|
+
if "stop" not in dataset.attrs:
|
|
65
|
+
message = f"stop not defined in dataset {dataset.name}"
|
|
66
|
+
raise ValueError(message)
|
|
67
|
+
if "num" not in dataset.attrs:
|
|
68
|
+
message = f"num not defined in dataset {dataset.name}"
|
|
69
|
+
raise ValueError(message)
|
|
70
|
+
|
|
71
|
+
start = Quantity(dataset.attrs["start"])
|
|
72
|
+
stop = Quantity(dataset.attrs["stop"])
|
|
73
|
+
num = int(dataset.attrs["num"])
|
|
74
|
+
|
|
75
|
+
return FrequencySeries(
|
|
76
|
+
data=array(dataset),
|
|
77
|
+
unit=unit,
|
|
78
|
+
frequencies=geomspace(start=start, stop=stop, num=num),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def dataset_to_timeseries(dataset: Dataset) -> TimeSeries:
|
|
83
|
+
"""
|
|
84
|
+
Convert a hdf5 dataset to a TimeSeries.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
dataset: Dataset
|
|
89
|
+
Dataset to convert.
|
|
90
|
+
|
|
91
|
+
Returns
|
|
92
|
+
-------
|
|
93
|
+
TimeSeries
|
|
94
|
+
TimeSeries.
|
|
95
|
+
|
|
96
|
+
"""
|
|
97
|
+
unit = None
|
|
98
|
+
scale = Scale.LINEAR
|
|
99
|
+
if "units" in dataset.attrs:
|
|
100
|
+
unit = dataset.attrs["units"]
|
|
101
|
+
|
|
102
|
+
if "scale" in dataset.attrs:
|
|
103
|
+
scale = Scale(dataset.attrs["scale"])
|
|
104
|
+
|
|
105
|
+
if scale is Scale.LINEAR:
|
|
106
|
+
if "t0" not in dataset.attrs:
|
|
107
|
+
message = f"t0 not defined in dataset {dataset.name}."
|
|
108
|
+
raise ValueError(message)
|
|
109
|
+
|
|
110
|
+
if "dt" not in dataset.attrs:
|
|
111
|
+
message = f"dt not defined in dataset {dataset.name}."
|
|
112
|
+
raise ValueError(message)
|
|
113
|
+
|
|
114
|
+
t0 = Quantity(dataset.attrs["t0"])
|
|
115
|
+
dt = Quantity(dataset.attrs["dt"])
|
|
116
|
+
|
|
117
|
+
return TimeSeries(
|
|
118
|
+
data=array(dataset),
|
|
119
|
+
unit=unit,
|
|
120
|
+
t0=t0,
|
|
121
|
+
dt=dt,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
if "start" not in dataset.attrs:
|
|
125
|
+
message = f"start not defined in dataset {dataset.name}"
|
|
126
|
+
raise ValueError(message)
|
|
127
|
+
if "stop" not in dataset.attrs:
|
|
128
|
+
message = f"stop not defined in dataset {dataset.name}"
|
|
129
|
+
raise ValueError(message)
|
|
130
|
+
if "num" not in dataset.attrs:
|
|
131
|
+
message = f"num not defined in dataset {dataset.name}"
|
|
132
|
+
raise ValueError(message)
|
|
133
|
+
|
|
134
|
+
start = Quantity(dataset.attrs["start"])
|
|
135
|
+
stop = Quantity(dataset.attrs["stop"])
|
|
136
|
+
num = int(dataset.attrs["num"])
|
|
137
|
+
|
|
138
|
+
return TimeSeries(
|
|
139
|
+
data=array(dataset),
|
|
140
|
+
unit=unit,
|
|
141
|
+
times=Quantity(
|
|
142
|
+
geomspace(
|
|
143
|
+
start=start.to_value(unit="s"),
|
|
144
|
+
stop=stop.to_value(unit="s"),
|
|
145
|
+
num=num,
|
|
146
|
+
),
|
|
147
|
+
"s",
|
|
148
|
+
),
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def group_to_timeseriesdict(group: Group) -> TimeSeriesDict: # type: ignore[ty:invalid-type-form]
|
|
153
|
+
"""
|
|
154
|
+
Convert a hdf5 group to a TimeSeriesDict.
|
|
155
|
+
|
|
156
|
+
Parameters
|
|
157
|
+
----------
|
|
158
|
+
group: Group
|
|
159
|
+
Group to convert.
|
|
160
|
+
|
|
161
|
+
Returns
|
|
162
|
+
-------
|
|
163
|
+
TimeSeriesDict
|
|
164
|
+
TimeSeriesDict.
|
|
165
|
+
|
|
166
|
+
"""
|
|
167
|
+
dict_data = {}
|
|
168
|
+
for name, dataset in group.items():
|
|
169
|
+
if not isinstance(dataset, Dataset):
|
|
170
|
+
continue
|
|
171
|
+
|
|
172
|
+
dict_data[name] = dataset_to_timeseries(dataset)
|
|
173
|
+
|
|
174
|
+
return TimeSeriesDict(dict_data)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def frequencyseries_to_dataset(
|
|
178
|
+
store: Group, name: str, frequencyseries: FrequencySeries
|
|
179
|
+
) -> Dataset:
|
|
180
|
+
"""
|
|
181
|
+
Convert a FrequencySeries to an hdf5 dataset.
|
|
182
|
+
|
|
183
|
+
It adds the dataset to the `store`.
|
|
184
|
+
|
|
185
|
+
Parameters
|
|
186
|
+
----------
|
|
187
|
+
store: Group
|
|
188
|
+
Hdf5 element which will store the dataset.
|
|
189
|
+
name: str
|
|
190
|
+
Name of the dataset.
|
|
191
|
+
frequencyseries: FrequencySeries
|
|
192
|
+
Data to store.
|
|
193
|
+
|
|
194
|
+
Returns
|
|
195
|
+
-------
|
|
196
|
+
Dataset
|
|
197
|
+
Created dataset.
|
|
198
|
+
|
|
199
|
+
"""
|
|
200
|
+
dataset = store.create_dataset(name, data=frequencyseries.data)
|
|
201
|
+
|
|
202
|
+
if frequencyseries.unit is not None:
|
|
203
|
+
dataset.attrs["units"] = frequencyseries.unit.to_string()
|
|
204
|
+
|
|
205
|
+
try:
|
|
206
|
+
f0 = frequencyseries.f0
|
|
207
|
+
df = frequencyseries.df
|
|
208
|
+
dataset.attrs["scale"] = Scale.LINEAR
|
|
209
|
+
dataset.attrs["f0"] = f0.to_string()
|
|
210
|
+
dataset.attrs["df"] = df.to_string()
|
|
211
|
+
except AttributeError:
|
|
212
|
+
start = frequencyseries.f0
|
|
213
|
+
stop = frequencyseries.frequencies[-1]
|
|
214
|
+
num = len(frequencyseries.frequencies)
|
|
215
|
+
dataset.attrs["scale"] = Scale.LOG
|
|
216
|
+
dataset.attrs["start"] = start.to_string()
|
|
217
|
+
dataset.attrs["stop"] = stop.to_string()
|
|
218
|
+
dataset.attrs["num"] = num
|
|
219
|
+
|
|
220
|
+
return dataset
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def timeseries_to_dataset(
|
|
224
|
+
store: Group, name: str, timeseries: TimeSeries
|
|
225
|
+
) -> Dataset:
|
|
226
|
+
"""
|
|
227
|
+
Convert a TimeSeries to an hdf5 dataset.
|
|
228
|
+
|
|
229
|
+
It adds the dataset to the `store`.
|
|
230
|
+
|
|
231
|
+
Parameters
|
|
232
|
+
----------
|
|
233
|
+
store: Group
|
|
234
|
+
Hdf5 element which will store the dataset.
|
|
235
|
+
name: str
|
|
236
|
+
Name of the dataset.
|
|
237
|
+
timeseries: TimeSeries
|
|
238
|
+
Data to store.
|
|
239
|
+
|
|
240
|
+
Returns
|
|
241
|
+
-------
|
|
242
|
+
Dataset
|
|
243
|
+
Created dataset.
|
|
244
|
+
|
|
245
|
+
"""
|
|
246
|
+
dataset = store.create_dataset(name, data=timeseries.data)
|
|
247
|
+
|
|
248
|
+
if timeseries.unit is not None:
|
|
249
|
+
dataset.attrs["units"] = timeseries.unit.to_string()
|
|
250
|
+
|
|
251
|
+
try:
|
|
252
|
+
t0 = timeseries.t0
|
|
253
|
+
dt = timeseries.dt
|
|
254
|
+
dataset.attrs["scale"] = Scale.LINEAR.value
|
|
255
|
+
dataset.attrs["t0"] = t0.to_string()
|
|
256
|
+
dataset.attrs["dt"] = dt.to_string()
|
|
257
|
+
except AttributeError:
|
|
258
|
+
start = timeseries.t0
|
|
259
|
+
stop = timeseries.times[-1]
|
|
260
|
+
num = len(timeseries.times)
|
|
261
|
+
dataset.attrs["scale"] = Scale.LOG.value
|
|
262
|
+
dataset.attrs["start"] = start.to_string()
|
|
263
|
+
dataset.attrs["stop"] = stop.to_string()
|
|
264
|
+
dataset.attrs["num"] = num
|
|
265
|
+
|
|
266
|
+
return dataset
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def timeseriesdict_to_group(
|
|
270
|
+
store: Group, name: str, timeseriesdict: TimeSeriesDict # type: ignore[ty:invalid-type-form]
|
|
271
|
+
) -> Group:
|
|
272
|
+
"""
|
|
273
|
+
Convert a TimeSeriesDict to an hdf5 group.
|
|
274
|
+
|
|
275
|
+
It adds the group to the `store`.
|
|
276
|
+
|
|
277
|
+
Parameters
|
|
278
|
+
----------
|
|
279
|
+
store: Group
|
|
280
|
+
Hdf5 element which will store the group.
|
|
281
|
+
name: str
|
|
282
|
+
Name of the group.
|
|
283
|
+
timeseriesdict: TimeSeriesDict
|
|
284
|
+
Data to store.
|
|
285
|
+
|
|
286
|
+
Returns
|
|
287
|
+
-------
|
|
288
|
+
Group
|
|
289
|
+
Created group.
|
|
290
|
+
|
|
291
|
+
"""
|
|
292
|
+
group = store.create_group(name)
|
|
293
|
+
|
|
294
|
+
for key, timeseries in timeseriesdict.items():
|
|
295
|
+
channel = key
|
|
296
|
+
if isinstance(channel, Channel):
|
|
297
|
+
channel = str(key)
|
|
298
|
+
timeseries_to_dataset(group, channel, timeseries)
|
|
299
|
+
|
|
300
|
+
return group
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def is_hdf5(
|
|
304
|
+
origin: Literal["read", "write"],
|
|
305
|
+
path: Path | None,
|
|
306
|
+
fileobj: IO | None,
|
|
307
|
+
*args: Any,
|
|
308
|
+
**kwargs: Any,
|
|
309
|
+
) -> bool:
|
|
310
|
+
"""Check if data is hdf5."""
|
|
311
|
+
|
|
312
|
+
if fileobj is not None:
|
|
313
|
+
loc = fileobj.tell()
|
|
314
|
+
try:
|
|
315
|
+
signature = fileobj.read(8)
|
|
316
|
+
finally:
|
|
317
|
+
fileobj.seek(loc)
|
|
318
|
+
return signature == HDF5_SIGNATURE
|
|
319
|
+
if path is not None:
|
|
320
|
+
return path.suffix in ["hdf5", "h5"]
|
|
321
|
+
|
|
322
|
+
return isinstance(args[0], (Group, Dataset)) # File is a Group
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Unified I/O Registry (see astropy)."""
|
|
2
|
+
|
|
3
|
+
from .interface import UnifiedReadWrite, UnifiedReadWriteMethod
|
|
4
|
+
from .unified_input_registry import UnifiedInputRegistry
|
|
5
|
+
from .unified_io_registry import UnifiedIORegistry
|
|
6
|
+
from .unified_io_registry_base import UnifiedIORegistryBase
|
|
7
|
+
from .unified_output_registry import UnifiedOutputRegistry
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"UnifiedIORegistry",
|
|
11
|
+
"UnifiedIORegistryBase",
|
|
12
|
+
"UnifiedInputRegistry",
|
|
13
|
+
"UnifiedOutputRegistry",
|
|
14
|
+
"UnifiedReadWrite",
|
|
15
|
+
"UnifiedReadWriteMethod",
|
|
16
|
+
]
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""UnifiedReadWrite class."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from inspect import cleandoc
|
|
5
|
+
from os import linesep
|
|
6
|
+
from re import sub
|
|
7
|
+
from sys import flags
|
|
8
|
+
from typing import IO, Literal
|
|
9
|
+
|
|
10
|
+
from rich.pager import Pager
|
|
11
|
+
|
|
12
|
+
from backscattermeter_analyzer.io.registry.default import (
|
|
13
|
+
default_registry,
|
|
14
|
+
)
|
|
15
|
+
from backscattermeter_analyzer.io.registry.unified_io_registry import (
|
|
16
|
+
UnifiedIORegistry,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class UnifiedReadWrite[T]:
|
|
22
|
+
"""
|
|
23
|
+
Same goal than its astropy equivalent.
|
|
24
|
+
|
|
25
|
+
See astropy.io.registry.interface.UnifiedReadWrite
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
# Descriptor calling instance or None if no instance.
|
|
29
|
+
_instance: T | None
|
|
30
|
+
# Descriptor calling class (either owner class or instance class).
|
|
31
|
+
_class_: type[T]
|
|
32
|
+
# Method name (read or write).
|
|
33
|
+
_method_name: Literal["read", "write"]
|
|
34
|
+
# The IO Registry.
|
|
35
|
+
_registry: UnifiedIORegistry = field(init=False)
|
|
36
|
+
|
|
37
|
+
def __post_init__(self) -> None:
|
|
38
|
+
"""Post-initialization process."""
|
|
39
|
+
self._registry = default_registry
|
|
40
|
+
|
|
41
|
+
def help(
|
|
42
|
+
self, format_: str | None = None, out: IO | None = None
|
|
43
|
+
) -> None:
|
|
44
|
+
"""
|
|
45
|
+
Output help documentation for the specified unified I/O format.
|
|
46
|
+
|
|
47
|
+
By default the help output is printed to the console via
|
|
48
|
+
pydoc.pager. Instead one can supplied a file handle object as
|
|
49
|
+
`out` and the output will be written to that handle.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
format_: str | None
|
|
54
|
+
Unified I/O format name, e.g. 'fits' or 'ascii.ecsv'
|
|
55
|
+
out: IO | None
|
|
56
|
+
Output destination. If None, stdout via a pager. Default to
|
|
57
|
+
None.
|
|
58
|
+
|
|
59
|
+
"""
|
|
60
|
+
if flags.optimize >= 2: # noqa:PLR2004
|
|
61
|
+
message = (
|
|
62
|
+
"The help method is not available under Python's"
|
|
63
|
+
" optimized mode."
|
|
64
|
+
)
|
|
65
|
+
raise RuntimeError(message)
|
|
66
|
+
|
|
67
|
+
class_ = self._class_
|
|
68
|
+
method_name = self._method_name
|
|
69
|
+
|
|
70
|
+
# Get reader or writer function associated with the registry
|
|
71
|
+
get_func = (
|
|
72
|
+
self._registry.get_reader
|
|
73
|
+
if method_name == "read"
|
|
74
|
+
else self._registry.get_writer
|
|
75
|
+
)
|
|
76
|
+
try:
|
|
77
|
+
if format_:
|
|
78
|
+
read_write_func = get_func(format_, class_)
|
|
79
|
+
except ValueError as err:
|
|
80
|
+
reader_doc = "ERROR: " + str(err)
|
|
81
|
+
else:
|
|
82
|
+
if format_:
|
|
83
|
+
# Format-specific
|
|
84
|
+
header = (
|
|
85
|
+
f"{class_.__name__}.{method_name}(format_="
|
|
86
|
+
f"'{format_}') documentation\n"
|
|
87
|
+
)
|
|
88
|
+
doc = read_write_func.__doc__
|
|
89
|
+
else:
|
|
90
|
+
# General docs
|
|
91
|
+
header = (
|
|
92
|
+
f"{class_.__name__}.{method_name} general"
|
|
93
|
+
" documentation\n"
|
|
94
|
+
)
|
|
95
|
+
doc = getattr(class_, method_name).__doc__
|
|
96
|
+
|
|
97
|
+
reader_doc = sub(".", "=", header)
|
|
98
|
+
reader_doc += header
|
|
99
|
+
reader_doc += sub(".", "=", header)
|
|
100
|
+
reader_doc += linesep
|
|
101
|
+
if doc is not None:
|
|
102
|
+
reader_doc += cleandoc(doc)
|
|
103
|
+
|
|
104
|
+
if out is None:
|
|
105
|
+
pager = Pager()
|
|
106
|
+
pager.show(reader_doc)
|
|
107
|
+
else:
|
|
108
|
+
out.write(reader_doc)
|