backscattermeter_analyzer 2.1.0__tar.gz → 2.2.0__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 (73) hide show
  1. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/PKG-INFO +2 -2
  2. backscattermeter_analyzer-2.2.0/mise.toml +6 -0
  3. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/pyproject.toml +2 -2
  4. backscattermeter_analyzer-2.2.0/src/backscattermeter_analyzer/__init__.py +25 -0
  5. backscattermeter_analyzer-2.2.0/src/backscattermeter_analyzer/characterize/__init__.py +9 -0
  6. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/characterize/distance/__init__.py +0 -4
  7. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/characterize/distance/ratio.py +3 -2
  8. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/characterize/distance/result.py +8 -7
  9. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/characterize/distance/vector.py +8 -4
  10. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/hardware/motor/jump.py +3 -1
  11. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/hardware/motor/path.py +8 -5
  12. backscattermeter_analyzer-2.2.0/src/backscattermeter_analyzer/io/hdf5/measurement/__init__.py +9 -0
  13. backscattermeter_analyzer-2.1.0/src/backscattermeter_analyzer/io/hdf5/measurement.py → backscattermeter_analyzer-2.2.0/src/backscattermeter_analyzer/io/hdf5/measurement/main.py +105 -65
  14. backscattermeter_analyzer-2.2.0/src/backscattermeter_analyzer/io/hdf5/measurement/motor.py +230 -0
  15. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/io/hdf5/utils.py +25 -0
  16. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/io/registry/interface/unified_read_write.py +6 -4
  17. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/io/registry/interface/unified_read_write_method.py +4 -1
  18. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/io/registry/unified_input_registry.py +4 -2
  19. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/io/registry/unified_io_registry.py +4 -2
  20. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/io/registry/unified_io_registry_base.py +11 -9
  21. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/io/registry/unified_output_registry.py +4 -2
  22. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/distance/distance.py +6 -4
  23. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/distance/motor/state.py +1 -1
  24. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/distance/motor/yes.py +4 -3
  25. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/distance/state.py +3 -3
  26. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/measurement/angle/state.py +1 -1
  27. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/measurement/angle/yes.py +6 -5
  28. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/measurement/camera/state.py +1 -1
  29. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/measurement/camera/yes.py +4 -3
  30. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/measurement/connect.py +43 -2
  31. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/measurement/measurement.py +13 -8
  32. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/measurement/motor/state.py +10 -1
  33. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/measurement/motor/yes.py +4 -3
  34. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/measurement/state.py +10 -10
  35. backscattermeter_analyzer-2.1.0/mise.toml +0 -6
  36. backscattermeter_analyzer-2.1.0/src/backscattermeter_analyzer/__init__.py +0 -15
  37. backscattermeter_analyzer-2.1.0/src/backscattermeter_analyzer/characterize/__init__.py +0 -1
  38. backscattermeter_analyzer-2.1.0/src/backscattermeter_analyzer/characterize/distance/distance.py +0 -22
  39. backscattermeter_analyzer-2.1.0/src/backscattermeter_analyzer/characterize/distance/measurement.py +0 -23
  40. backscattermeter_analyzer-2.1.0/src/backscattermeter_analyzer/characterize/distance/utils.py +0 -178
  41. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/LICENSE +0 -0
  42. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/README.md +0 -0
  43. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/display.py +0 -0
  44. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/justfile +0 -0
  45. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/axis.py +0 -0
  46. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/characterize/angle/__init__.py +0 -0
  47. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/characterize/angle/result.py +0 -0
  48. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/direction.py +0 -0
  49. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/hardware/__init__.py +0 -0
  50. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/hardware/backscattermeter.py +0 -0
  51. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/hardware/camera/__init__.py +0 -0
  52. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/hardware/motor/__init__.py +0 -0
  53. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/hardware/motor/step_numbers.py +0 -0
  54. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/hardware/optic/__init__.py +0 -0
  55. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/io/__init__.py +0 -0
  56. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/io/hdf5/__init__.py +0 -0
  57. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/io/registry/__init__.py +0 -0
  58. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/io/registry/default.py +0 -0
  59. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/io/registry/interface/__init__.py +0 -0
  60. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/scale.py +0 -0
  61. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/__init__.py +0 -0
  62. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/distance/__init__.py +0 -0
  63. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/distance/motor/__init__.py +0 -0
  64. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/distance/motor/no.py +0 -0
  65. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/measurement/__init__.py +0 -0
  66. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/measurement/angle/__init__.py +0 -0
  67. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/measurement/angle/no.py +0 -0
  68. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/measurement/camera/__init__.py +0 -0
  69. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/measurement/camera/no.py +0 -0
  70. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/measurement/motor/__init__.py +0 -0
  71. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/measurement/motor/no.py +0 -0
  72. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/src/backscattermeter_analyzer/utils/utils.py +0 -0
  73. {backscattermeter_analyzer-2.1.0 → backscattermeter_analyzer-2.2.0}/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: backscattermeter_analyzer
3
- Version: 2.1.0
3
+ Version: 2.2.0
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
@@ -15,7 +15,7 @@ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (G
15
15
  Classifier: Operating System :: OS Independent
16
16
  Classifier: Programming Language :: Python :: 3
17
17
  Classifier: Topic :: Scientific/Engineering
18
- Requires-Python: >=3.12
18
+ Requires-Python: >=3.14.7
19
19
  Requires-Dist: gwpy>=3.0.12
20
20
  Requires-Dist: lalsuite>=7.26.15
21
21
  Requires-Dist: pyqt6>=6.9.1
@@ -0,0 +1,6 @@
1
+ [tools]
2
+ python = "3.14.7"
3
+ uv = "0.11.33"
4
+ just = "1.51.0"
5
+ hatch = "1.16.5"
6
+ markdownlint-cli2 = "0.22.1"
@@ -1,10 +1,10 @@
1
1
  [project]
2
2
  name = "backscattermeter_analyzer"
3
- version = "2.1.0"
3
+ version = "2.2.0"
4
4
  description = "Tool to work on the backscattermeter at LAPP"
5
5
  authors = [{ name = "linarphy", email = "linarphy@linarphy.net" }]
6
6
  readme = "README.md"
7
- requires-python = ">=3.12"
7
+ requires-python = ">=3.14.7"
8
8
  classifiers = [
9
9
  "Programming Language :: Python :: 3",
10
10
  "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
@@ -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,9 @@
1
+ """Sub-package focused on setup chacterization."""
2
+
3
+ from .distance import Ratio as DistanceRatio
4
+ from .distance import Result as DistanceResult
5
+
6
+ __all__ = [
7
+ "DistanceRatio",
8
+ "DistanceResult",
9
+ ]
@@ -103,15 +103,11 @@ We have l, and j via (6), which can be used to check consistency
103
103
  To get the distance associated to each step, we have to
104
104
  """
105
105
 
106
- from .distance import Distance
107
- from .measurement import Measurement
108
106
  from .ratio import Ratio
109
107
  from .result import Result
110
108
  from .vector import Vector
111
109
 
112
110
  __all__ = [
113
- "Distance",
114
- "Measurement",
115
111
  "Ratio",
116
112
  "Result",
117
113
  "Vector",
@@ -11,12 +11,13 @@ from numpy.linalg import norm
11
11
  from backscattermeter_analyzer import Axis, Direction
12
12
  from backscattermeter_analyzer.utils.distance.motor import WithMotorJump
13
13
 
14
- from .result import Result
15
14
  from .vector import Vector
16
15
 
17
16
  if TYPE_CHECKING:
18
17
  from backscattermeter_analyzer.utils.distance import Distance
19
18
 
19
+ from .result import Result
20
+
20
21
 
21
22
  @dataclass
22
23
  class Ratio:
@@ -36,7 +37,7 @@ class Ratio:
36
37
  @staticmethod
37
38
  def compute(
38
39
  results: list[Result], reference: tuple[Axis, Direction]
39
- ) -> "Ratio":
40
+ ) -> Ratio:
40
41
  """Compute the distance ratio from multiple results."""
41
42
  distances: dict[tuple[Axis, Direction], list[Distance]] = {
42
43
  (Axis.X, Direction.POSITIVE): [],
@@ -1,6 +1,7 @@
1
1
  """Result class."""
2
2
 
3
3
  from dataclasses import dataclass, field
4
+ from typing import TYPE_CHECKING
4
5
  from uuid import uuid4
5
6
 
6
7
  from astropy.time import TimeDelta
@@ -8,12 +9,12 @@ from astropy.uncertainty import normal
8
9
  from numpy import mean, std
9
10
 
10
11
  from backscattermeter_analyzer.utils.distance import Distance
11
- from backscattermeter_analyzer.utils.measurement import Measurement
12
12
  from backscattermeter_analyzer.utils.measurement.motor import (
13
13
  WithMotorPath,
14
14
  )
15
15
 
16
- from .utils import split_stable_segment
16
+ if TYPE_CHECKING:
17
+ from backscattermeter_analyzer.utils.measurement import Measurement
17
18
 
18
19
 
19
20
  @dataclass
@@ -25,7 +26,7 @@ class Result:
25
26
  identifier: str = field(default_factory=lambda: str(uuid4()))
26
27
 
27
28
  @staticmethod
28
- def analyze(measurement: Measurement) -> "Result":
29
+ def analyze(measurement: Measurement) -> Result:
29
30
  """Generate a result from a measurement by analysing it."""
30
31
  motor_state = measurement.state.motor
31
32
  if not isinstance(motor_state, WithMotorPath):
@@ -40,10 +41,10 @@ class Result:
40
41
 
41
42
  transition_duration = mean(durations) / 4
42
43
 
43
- segments = split_stable_segment(
44
- transition_duration=transition_duration.to("s"),
45
- data=measurement.data,
46
- transition_number=len(jumps),
44
+ segments = measurement.split(
45
+ number=len(jumps),
46
+ duration=transition_duration,
47
+ transformation=lambda _, x: abs(x.diff()),
47
48
  )
48
49
 
49
50
  positions = [
@@ -1,16 +1,20 @@
1
1
  """Vector class."""
2
2
 
3
3
  from dataclasses import dataclass, field
4
+ from typing import TYPE_CHECKING
4
5
  from uuid import uuid4
5
6
 
6
7
  from astropy.uncertainty import Distribution, normal
7
- from gwpy.detector.channel import Channel
8
8
  from numpy import mean, sqrt
9
9
 
10
- from backscattermeter_analyzer import Axis, Direction
11
- from backscattermeter_analyzer.utils.distance import Distance
12
10
  from backscattermeter_analyzer.utils.distance.motor import WithMotorJump
13
11
 
12
+ if TYPE_CHECKING:
13
+ from gwpy.detector.channel import Channel
14
+
15
+ from backscattermeter_analyzer import Axis, Direction
16
+ from backscattermeter_analyzer.utils.distance import Distance
17
+
14
18
 
15
19
  @dataclass
16
20
  class Vector:
@@ -28,7 +32,7 @@ class Vector:
28
32
  step: dict[str | Channel, Distribution] = field(
29
33
  init=False,
30
34
  doc="displacement for one step",
31
- ) # type: ignore[ty:no-matching-overload]
35
+ )
32
36
  identifier: str = field(default_factory=lambda: str(uuid4()))
33
37
 
34
38
  def __post_init__(self) -> None:
@@ -1,8 +1,10 @@
1
1
  """Jump class."""
2
2
 
3
3
  from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING
4
5
 
5
- from backscattermeter_analyzer import Axis, Direction
6
+ if TYPE_CHECKING:
7
+ from backscattermeter_analyzer import Axis, Direction
6
8
 
7
9
 
8
10
  @dataclass
@@ -1,16 +1,19 @@
1
1
  """Path class."""
2
2
 
3
3
  from collections.abc import Iterable
4
+ from typing import TYPE_CHECKING
4
5
 
5
6
  from astropy.time import TimeDelta
6
7
 
7
8
  from backscattermeter_analyzer import Axis, Direction
8
- from backscattermeter_analyzer.hardware.motor.step_numbers import (
9
- StepNumbers,
10
- )
11
9
 
12
10
  from .jump import Jump
13
11
 
12
+ if TYPE_CHECKING:
13
+ from backscattermeter_analyzer.hardware.motor.step_numbers import (
14
+ StepNumbers,
15
+ )
16
+
14
17
 
15
18
  class Path(Iterable):
16
19
  """A sequence of MotorJump."""
@@ -30,7 +33,7 @@ class Path(Iterable):
30
33
  self._inner = data
31
34
  self._idx = 0
32
35
 
33
- def __iter__(self) -> "Path":
36
+ def __iter__(self) -> Path:
34
37
  """Default __iter__ function."""
35
38
 
36
39
  return self
@@ -63,7 +66,7 @@ class Path(Iterable):
63
66
  duration: TimeDelta,
64
67
  speed: int,
65
68
  step_numbers: StepNumbers,
66
- ) -> "Path":
69
+ ) -> Path:
67
70
  """Generate a motor path with a snail structure."""
68
71
 
69
72
  x_p = Jump(
@@ -0,0 +1,9 @@
1
+ """Sub-package managing HDF5 conversion of Measurement."""
2
+
3
+ from .main import read, register_measurement, write
4
+
5
+ __all__ = [
6
+ "read",
7
+ "register_measurement",
8
+ "write",
9
+ ]
@@ -1,4 +1,4 @@
1
- """Hdf5 conversion for Measurement."""
1
+ """Hdf5 conversion for the main metadata of a Measurement."""
2
2
 
3
3
  from pathlib import Path
4
4
  from typing import TYPE_CHECKING
@@ -6,22 +6,59 @@ from typing import TYPE_CHECKING
6
6
  from h5py import File, Group
7
7
 
8
8
  from backscattermeter_analyzer.hardware import Backscattermeter
9
- from backscattermeter_analyzer.io.registry.default import (
10
- default_registry,
11
- )
12
-
13
- from .utils import (
9
+ from backscattermeter_analyzer.io.hdf5.utils import (
10
+ get_group,
14
11
  group_to_timeseriesdict,
15
12
  is_hdf5,
16
13
  timeseriesdict_to_group,
17
14
  )
15
+ from backscattermeter_analyzer.io.registry.default import (
16
+ default_registry,
17
+ )
18
+
19
+ from .motor import read as read_motor
20
+ from .motor import write as write_motor
18
21
 
19
22
  if TYPE_CHECKING:
20
- from backscattermeter_analyzer.utils.measurement.measurement import ( # noqa:E501,TC004
23
+ from backscattermeter_analyzer.utils.measurement.measurement import ( # noqa: E501
21
24
  Measurement,
22
25
  )
23
26
 
24
27
 
28
+ def _read(input_: Group, path: str | None = None) -> Measurement:
29
+ """Read a Measurement object from a group, internal function."""
30
+ from backscattermeter_analyzer.utils.measurement.measurement import ( # noqa: PLC0415,E501
31
+ Measurement,
32
+ )
33
+ from backscattermeter_analyzer.utils.measurement.motor.state import ( # noqa: PLC0415,E501
34
+ State as MotorState,
35
+ )
36
+
37
+ # input_ is now a Group (a File is a Group)
38
+ item = get_group(input_, path)
39
+
40
+ group_data = item.get("data", default=None)
41
+ if group_data is None:
42
+ message = f"No 'data' group in {path} from {input_}."
43
+ raise ValueError(message)
44
+ if not isinstance(group_data, Group):
45
+ message = f"'data' must be a group in {path} from {input_}."
46
+ raise TypeError(message)
47
+
48
+ measurement = Measurement(
49
+ data=group_to_timeseriesdict(group_data),
50
+ backscattermeter=Backscattermeter(),
51
+ )
52
+
53
+ measurement.state.motor = MotorState.read( # type: ignore[ty:call-non-callable]
54
+ input_=item,
55
+ measurement=measurement,
56
+ format_="hdf5",
57
+ )
58
+
59
+ return measurement
60
+
61
+
25
62
  def read(input_: Path | Group, path: str | None = None) -> Measurement:
26
63
  """
27
64
  Read a Measurement object from an HDF5 component/file.
@@ -29,7 +66,7 @@ def read(input_: Path | Group, path: str | None = None) -> Measurement:
29
66
  Parameters
30
67
  ----------
31
68
  input_: Path | Group
32
- If a string or a Path, the filename to read the table from.
69
+ If a Path, the filename to read the measurement from.
33
70
  If an h5py object, either the file or the group object to read
34
71
  the Measurement from.
35
72
  path: str | None
@@ -37,45 +74,52 @@ def read(input_: Path | Group, path: str | None = None) -> Measurement:
37
74
  This should be relative to the input if the input is a Group.
38
75
 
39
76
  """
40
- from backscattermeter_analyzer.utils.measurement.measurement import ( # noqa: PLC0415,E501
41
- Measurement,
42
- )
43
77
 
44
78
  if isinstance(input_, Path):
45
- input_ = File(input_)
79
+ with File(input_) as file:
80
+ return _read(input_=file, path=path)
46
81
 
47
- # input_ is now a Group (a File is a Group)
82
+ return _read(input_=input_, path=path)
83
+
84
+
85
+ def _write(
86
+ measurement: Measurement,
87
+ output: Group,
88
+ path: str | None = None,
89
+ *,
90
+ append: bool = False,
91
+ overwrite: bool = False,
92
+ ) -> None:
93
+ """Write a Measurement object to a group, internal function."""
94
+
95
+ # output is now a Group (a File is a Group)
48
96
  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):
97
+ item = output.get(path, default=None)
98
+ if item is not None and not (append and overwrite):
54
99
  message = (
55
- f"{path} from {input_} is a {type(item)}, not a Group."
100
+ f"{path} already exists in {output}, add `append=True` "
101
+ "and `overwrite=True` to proceed anyway."
56
102
  )
57
- raise TypeError(message)
103
+ raise ValueError(message)
104
+ if item is not None:
105
+ if not isinstance(item, Group):
106
+ message = (
107
+ f"{path} from {output} is a {type(item)}"
108
+ ", not a Group."
109
+ )
110
+ raise TypeError(message)
111
+ item.clear()
112
+ else:
113
+ item = output.create_group(name=path)
58
114
  else:
59
- item = input_
115
+ item = output
60
116
 
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)
117
+ timeseriesdict_to_group(item, "data", measurement.data)
68
118
 
69
- measurement = Measurement(
70
- data=group_to_timeseriesdict(group_data),
71
- backscattermeter=Backscattermeter(),
119
+ measurement.state.motor.write( # type: ignore[ty:call-non-callable]
120
+ output=item, append=True, overwrite=True, format_="hdf5"
72
121
  )
73
122
 
74
- if isinstance(input_, File):
75
- input_.close()
76
-
77
- return measurement
78
-
79
123
 
80
124
  def write(
81
125
  measurement: Measurement,
@@ -97,7 +141,7 @@ def write(
97
141
  a Group, the group object to write the measurement to, depending
98
142
  on `path` value.
99
143
  path: str | None
100
- The path to which to write the table inside the HDF5 file.
144
+ The path to which to write the measurement inside the HDF5 file.
101
145
  This should be relative to the output Group (or the root Group
102
146
  if output is a Path). Default to None.
103
147
  append: bool
@@ -111,38 +155,28 @@ def write(
111
155
  """
112
156
 
113
157
  if isinstance(output, Path):
158
+ mode = "x"
114
159
  if overwrite and not append:
115
- output = File(output, mode="w")
160
+ mode = "w"
116
161
  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."
162
+ mode = "r+"
163
+
164
+ with File(output, mode=mode) as file:
165
+ return _write(
166
+ measurement=measurement,
167
+ output=file,
168
+ path=path,
169
+ append=append,
170
+ overwrite=overwrite,
130
171
  )
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
172
 
144
- if isinstance(output, File):
145
- output.close()
173
+ return _write(
174
+ measurement=measurement,
175
+ output=output,
176
+ path=path,
177
+ append=append,
178
+ overwrite=overwrite,
179
+ )
146
180
 
147
181
 
148
182
  def register_measurement() -> None:
@@ -150,7 +184,13 @@ def register_measurement() -> None:
150
184
  from backscattermeter_analyzer.utils.measurement.measurement import ( # noqa: PLC0415,E501
151
185
  Measurement,
152
186
  )
187
+ from backscattermeter_analyzer.utils.measurement.motor.state import ( # noqa: PLC0415, E501
188
+ State as MotorState,
189
+ )
153
190
 
154
191
  default_registry.register_identifier("hdf5", Measurement, is_hdf5)
155
192
  default_registry.register_reader("hdf5", Measurement, read)
156
193
  default_registry.register_writer("hdf5", Measurement, write)
194
+ default_registry.register_identifier("hdf5", MotorState, is_hdf5)
195
+ default_registry.register_reader("hdf5", MotorState, read_motor)
196
+ default_registry.register_writer("hdf5", MotorState, write_motor)