backscattermeter_analyzer 1.0.2__tar.gz → 2.0.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.
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/PKG-INFO +1 -1
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/pyproject.toml +1 -1
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/characterize/angle/__init__.py +1 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/characterize/angle/result.py +8 -0
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/src/backscattermeter_analyzer/characterize/distance/measurement.py +1 -1
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/src/backscattermeter_analyzer/characterize/distance/ratio.py +25 -12
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/src/backscattermeter_analyzer/characterize/distance/result.py +25 -8
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/src/backscattermeter_analyzer/characterize/distance/utils.py +14 -10
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/src/backscattermeter_analyzer/characterize/distance/vector.py +7 -4
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/src/backscattermeter_analyzer/hardware/__init__.py +4 -1
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/hardware/motor/__init__.py +11 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/hardware/motor/path.py +118 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/hardware/motor/step_numbers.py +12 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/hardware/optic/__init__.py +13 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/__init__.py +7 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/distance/__init__.py +7 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/distance/distance.py +27 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/distance/motor/__init__.py +11 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/distance/motor/no.py +16 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/distance/motor/state.py +20 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/distance/motor/yes.py +20 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/distance/state.py +31 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/__init__.py +7 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/angle/__init__.py +7 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/angle/no.py +14 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/angle/state.py +25 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/angle/yes.py +29 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/camera/__init__.py +7 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/camera/no.py +14 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/camera/state.py +20 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/camera/yes.py +20 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/measurement.py +34 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/motor/__init__.py +11 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/motor/no.py +14 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/motor/state.py +20 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/motor/yes.py +20 -0
- backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/state.py +92 -0
- backscattermeter_analyzer-1.0.2/src/backscattermeter_analyzer/hardware/motor/__init__.py +0 -7
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/LICENSE +0 -0
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/README.md +0 -0
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/display.py +0 -0
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/justfile +0 -0
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/mise.toml +0 -0
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/src/backscattermeter_analyzer/__init__.py +0 -0
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/src/backscattermeter_analyzer/axis.py +0 -0
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/src/backscattermeter_analyzer/characterize/__init__.py +0 -0
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/src/backscattermeter_analyzer/characterize/distance/__init__.py +0 -0
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/src/backscattermeter_analyzer/characterize/distance/distance.py +0 -0
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/src/backscattermeter_analyzer/direction.py +0 -0
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/src/backscattermeter_analyzer/hardware/backscattermeter.py +0 -0
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/src/backscattermeter_analyzer/hardware/camera/__init__.py +0 -0
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/src/backscattermeter_analyzer/hardware/motor/jump.py +0 -0
- {backscattermeter_analyzer-1.0.2 → backscattermeter_analyzer-2.0.0}/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: backscattermeter_analyzer
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Sub-package used to measure and analyze motor action on angle."""
|
|
@@ -16,7 +16,7 @@ from backscattermeter_analyzer.hardware import (
|
|
|
16
16
|
class Measurement:
|
|
17
17
|
"""Measurement of a distance scan with a camera."""
|
|
18
18
|
|
|
19
|
-
data: TimeSeriesDict #
|
|
19
|
+
data: TimeSeriesDict # type: ignore[ty:invalid-type-form]
|
|
20
20
|
camera: Camera
|
|
21
21
|
backscattermeter: Backscattermeter
|
|
22
22
|
motor_path: list[MotorJump]
|
|
@@ -5,15 +5,17 @@ from typing import TYPE_CHECKING
|
|
|
5
5
|
from uuid import uuid4
|
|
6
6
|
|
|
7
7
|
from astropy.uncertainty import Distribution, normal
|
|
8
|
+
from numpy import sqrt
|
|
8
9
|
from numpy.linalg import norm
|
|
9
10
|
|
|
10
11
|
from backscattermeter_analyzer import Axis, Direction
|
|
12
|
+
from backscattermeter_analyzer.utils.distance.motor import WithMotorJump
|
|
11
13
|
|
|
12
14
|
from .result import Result
|
|
13
15
|
from .vector import Vector
|
|
14
16
|
|
|
15
17
|
if TYPE_CHECKING:
|
|
16
|
-
from .distance import Distance
|
|
18
|
+
from backscattermeter_analyzer.utils.distance import Distance
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
@dataclass
|
|
@@ -44,15 +46,33 @@ class Ratio:
|
|
|
44
46
|
}
|
|
45
47
|
for result in results:
|
|
46
48
|
for distance in result.distances:
|
|
49
|
+
motor_state = distance.state.motor
|
|
50
|
+
if not isinstance(motor_state, WithMotorJump):
|
|
51
|
+
raise TypeError
|
|
47
52
|
distances[
|
|
48
|
-
|
|
53
|
+
motor_state.jump.axis, motor_state.jump.direction
|
|
49
54
|
].append(distance)
|
|
50
55
|
|
|
51
56
|
reference_vector = Vector(
|
|
52
57
|
axis=reference[0],
|
|
53
58
|
direction=reference[1],
|
|
54
59
|
distances=distances[reference],
|
|
55
|
-
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
reference_vector_norm = Distribution(
|
|
63
|
+
norm(
|
|
64
|
+
[
|
|
65
|
+
value.distribution
|
|
66
|
+
for value in reference_vector.step.values()
|
|
67
|
+
],
|
|
68
|
+
axis=0,
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
reference_vector.ratio = normal(
|
|
72
|
+
center=1.0,
|
|
73
|
+
std=reference_vector_norm.pdf_std()
|
|
74
|
+
/ sqrt(len(distances[reference])),
|
|
75
|
+
n_samples=1000,
|
|
56
76
|
)
|
|
57
77
|
|
|
58
78
|
vectors = [
|
|
@@ -65,22 +85,15 @@ class Ratio:
|
|
|
65
85
|
]
|
|
66
86
|
|
|
67
87
|
for vector in vectors:
|
|
68
|
-
vector.ratio = Distribution(
|
|
88
|
+
vector.ratio = reference_vector_norm / Distribution(
|
|
69
89
|
norm(
|
|
70
|
-
[
|
|
71
|
-
value.distribution
|
|
72
|
-
for value in reference_vector.step.values()
|
|
73
|
-
],
|
|
74
|
-
axis=0,
|
|
75
|
-
)
|
|
76
|
-
/ norm(
|
|
77
90
|
[
|
|
78
91
|
value.distribution
|
|
79
92
|
for value in vector.step.values()
|
|
80
93
|
],
|
|
81
94
|
axis=0,
|
|
82
95
|
)
|
|
83
|
-
)
|
|
96
|
+
) # type: ignore[ty:unsupported-operator]
|
|
84
97
|
|
|
85
98
|
return Ratio(
|
|
86
99
|
results=results,
|
|
@@ -3,12 +3,16 @@
|
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
4
|
from uuid import uuid4
|
|
5
5
|
|
|
6
|
+
from astropy.time import TimeDelta
|
|
6
7
|
from astropy.uncertainty import normal
|
|
7
|
-
from astropy.units import Quantity
|
|
8
8
|
from numpy import mean, std
|
|
9
9
|
|
|
10
|
-
from .distance import Distance
|
|
11
|
-
from .measurement import Measurement
|
|
10
|
+
from backscattermeter_analyzer.utils.distance import Distance
|
|
11
|
+
from backscattermeter_analyzer.utils.measurement import Measurement
|
|
12
|
+
from backscattermeter_analyzer.utils.measurement.motor import (
|
|
13
|
+
WithMotorPath,
|
|
14
|
+
)
|
|
15
|
+
|
|
12
16
|
from .utils import split_stable_segment
|
|
13
17
|
|
|
14
18
|
|
|
@@ -23,10 +27,23 @@ class Result:
|
|
|
23
27
|
@staticmethod
|
|
24
28
|
def analyze(measurement: Measurement) -> "Result":
|
|
25
29
|
"""Generate a result from a measurement by analysing it."""
|
|
30
|
+
motor_state = measurement.state.motor
|
|
31
|
+
if not isinstance(motor_state, WithMotorPath):
|
|
32
|
+
raise TypeError
|
|
33
|
+
|
|
34
|
+
jumps = [element[1] for element in motor_state.path]
|
|
35
|
+
times = [TimeDelta(0.0, format="sec")]
|
|
36
|
+
times.extend(element[0] for element in motor_state.path)
|
|
37
|
+
durations = [
|
|
38
|
+
times[i + 1] - times[i] for i in range(len(times) - 1)
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
transition_duration = mean(durations) / 4
|
|
42
|
+
|
|
26
43
|
segments = split_stable_segment(
|
|
27
|
-
|
|
28
|
-
measurement.data,
|
|
29
|
-
len(
|
|
44
|
+
transition_duration=transition_duration.to("s"),
|
|
45
|
+
data=measurement.data,
|
|
46
|
+
transition_number=len(jumps),
|
|
30
47
|
)
|
|
31
48
|
|
|
32
49
|
positions = [
|
|
@@ -49,10 +66,10 @@ class Result:
|
|
|
49
66
|
]
|
|
50
67
|
vectors = [
|
|
51
68
|
Distance(
|
|
52
|
-
jump=measurement.motor_path[i],
|
|
53
69
|
distance=distances[i],
|
|
70
|
+
motor_jump=jumps[i],
|
|
54
71
|
)
|
|
55
|
-
for i in range(len(
|
|
72
|
+
for i in range(len(jumps))
|
|
56
73
|
]
|
|
57
74
|
|
|
58
75
|
return Result(
|
|
@@ -44,8 +44,8 @@ def _find_transition_indices(
|
|
|
44
44
|
max_index = None
|
|
45
45
|
for index in sorted_transition_indices:
|
|
46
46
|
if (
|
|
47
|
-
index - half_transition_samples not in processed_indices
|
|
48
|
-
and index + half_transition_samples
|
|
47
|
+
int(index - half_transition_samples) not in processed_indices
|
|
48
|
+
and int(index + half_transition_samples)
|
|
49
49
|
not in processed_indices
|
|
50
50
|
):
|
|
51
51
|
max_index = index
|
|
@@ -61,6 +61,14 @@ def _find_transition_indices(
|
|
|
61
61
|
left_index = int(max_index - half_transition_samples)
|
|
62
62
|
right_index = int(max_index + half_transition_samples)
|
|
63
63
|
|
|
64
|
+
left_indices.append(left_index)
|
|
65
|
+
right_indices.append(right_index)
|
|
66
|
+
|
|
67
|
+
left_indices.sort()
|
|
68
|
+
right_indices.sort()
|
|
69
|
+
|
|
70
|
+
left_index_index = left_indices.index(left_index)
|
|
71
|
+
|
|
64
72
|
if left_index <= 0:
|
|
65
73
|
message = (
|
|
66
74
|
"The measurement does not have enough data before a "
|
|
@@ -76,16 +84,13 @@ def _find_transition_indices(
|
|
|
76
84
|
|
|
77
85
|
if (
|
|
78
86
|
len(right_indices) > 1
|
|
79
|
-
and left_index - right_indices[
|
|
87
|
+
and left_index - right_indices[left_index_index] < 2 # noqa: PLR2004
|
|
80
88
|
):
|
|
81
89
|
message = (
|
|
82
90
|
"Transitions are too close in the given measurement."
|
|
83
91
|
)
|
|
84
92
|
raise ValueError(message)
|
|
85
93
|
|
|
86
|
-
left_indices.append(left_index)
|
|
87
|
-
right_indices.append(right_index)
|
|
88
|
-
|
|
89
94
|
processed_indices.update(range(left_index, right_index + 1))
|
|
90
95
|
|
|
91
96
|
left_indices.append(data_length)
|
|
@@ -97,10 +102,10 @@ def _find_transition_indices(
|
|
|
97
102
|
|
|
98
103
|
|
|
99
104
|
def split_stable_segment(
|
|
100
|
-
transition_duration: Quantity[s], #
|
|
101
|
-
data: TimeSeriesDict, #
|
|
105
|
+
transition_duration: Quantity[s], #type: ignore[ty:not-subscriptable]
|
|
106
|
+
data: TimeSeriesDict, # type: ignore[ty:invalid-type-form]
|
|
102
107
|
transition_number: int,
|
|
103
|
-
) -> list[TimeSeriesDict]: #
|
|
108
|
+
) -> list[TimeSeriesDict]: # type: ignore[ty:invalid-type-form]
|
|
104
109
|
"""
|
|
105
110
|
Split N-dim time series into stable segments, excluding transition.
|
|
106
111
|
|
|
@@ -126,7 +131,6 @@ def split_stable_segment(
|
|
|
126
131
|
of segments is transition_number + 1.
|
|
127
132
|
|
|
128
133
|
"""
|
|
129
|
-
|
|
130
134
|
datum = next(iter(data.values())) # get a random TimeSeries in data
|
|
131
135
|
|
|
132
136
|
if len(datum) <= 1:
|
|
@@ -8,8 +8,8 @@ from gwpy.detector.channel import Channel
|
|
|
8
8
|
from numpy import mean, sqrt
|
|
9
9
|
|
|
10
10
|
from backscattermeter_analyzer import Axis, Direction
|
|
11
|
-
|
|
12
|
-
from .distance import
|
|
11
|
+
from backscattermeter_analyzer.utils.distance import Distance
|
|
12
|
+
from backscattermeter_analyzer.utils.distance.motor import WithMotorJump
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
@dataclass
|
|
@@ -28,7 +28,7 @@ class Vector:
|
|
|
28
28
|
step: dict[str | Channel, Distribution] = field(
|
|
29
29
|
init=False,
|
|
30
30
|
doc="displacement for one step",
|
|
31
|
-
) #
|
|
31
|
+
) # type: ignore[ty:no-matching-overload]
|
|
32
32
|
identifier: str = field(default_factory=lambda: str(uuid4()))
|
|
33
33
|
|
|
34
34
|
def __post_init__(self) -> None:
|
|
@@ -37,7 +37,10 @@ class Vector:
|
|
|
37
37
|
distances: dict[str | Channel, list[Distribution]] = {}
|
|
38
38
|
for distance in self.distances:
|
|
39
39
|
for key, length in distance.distance.items():
|
|
40
|
-
|
|
40
|
+
motor_state = distance.state.motor
|
|
41
|
+
if not isinstance(motor_state, WithMotorJump):
|
|
42
|
+
raise TypeError
|
|
43
|
+
real_distance = length / motor_state.jump.step_number # type: ignore[ty:unsupported-operator]
|
|
41
44
|
if key not in distances:
|
|
42
45
|
distances[key] = [real_distance]
|
|
43
46
|
else:
|
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
from .backscattermeter import Backscattermeter
|
|
4
4
|
from .camera import Camera
|
|
5
|
-
from .motor import MotorJump
|
|
5
|
+
from .motor import MotorJump, MotorPath
|
|
6
|
+
from .optic import Optic
|
|
6
7
|
|
|
7
8
|
__all__ = [
|
|
8
9
|
"Backscattermeter",
|
|
9
10
|
"Camera",
|
|
10
11
|
"MotorJump",
|
|
12
|
+
"MotorPath",
|
|
13
|
+
"Optic",
|
|
11
14
|
]
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Path class."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterable
|
|
4
|
+
|
|
5
|
+
from astropy.time import TimeDelta
|
|
6
|
+
|
|
7
|
+
from backscattermeter_analyzer import Axis, Direction
|
|
8
|
+
from backscattermeter_analyzer.hardware.motor.step_numbers import (
|
|
9
|
+
StepNumbers,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from .jump import Jump
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Path(Iterable):
|
|
16
|
+
"""A sequence of MotorJump."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, data: list[tuple[TimeDelta, Jump]]) -> None:
|
|
19
|
+
"""
|
|
20
|
+
Initialize a MotorPath.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
data: dict[Quantity[s], MotorJump]
|
|
25
|
+
Links between time in second from the start of the
|
|
26
|
+
TimeSeries and a motor jump.
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
self._inner = data
|
|
31
|
+
self._idx = 0
|
|
32
|
+
|
|
33
|
+
def __iter__(self) -> "Path":
|
|
34
|
+
"""Default __iter__ function."""
|
|
35
|
+
|
|
36
|
+
return self
|
|
37
|
+
|
|
38
|
+
def __next__(self) -> tuple[TimeDelta, Jump]:
|
|
39
|
+
"""Default __next__ function."""
|
|
40
|
+
|
|
41
|
+
self._idx += 1
|
|
42
|
+
try:
|
|
43
|
+
return self._inner[self._idx - 1]
|
|
44
|
+
except IndexError as exception:
|
|
45
|
+
self._idx = 0
|
|
46
|
+
raise StopIteration from exception
|
|
47
|
+
|
|
48
|
+
def __len__(self) -> int:
|
|
49
|
+
"""Default __len__ function."""
|
|
50
|
+
|
|
51
|
+
return len(self._inner)
|
|
52
|
+
|
|
53
|
+
def __getitem__(
|
|
54
|
+
self, subscript: slice | int
|
|
55
|
+
) -> tuple[TimeDelta, Jump] | list[tuple[TimeDelta, Jump]]:
|
|
56
|
+
"""Default __getitem__ function."""
|
|
57
|
+
|
|
58
|
+
return self._inner[subscript]
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def snail(
|
|
62
|
+
number: int,
|
|
63
|
+
duration: TimeDelta,
|
|
64
|
+
speed: int,
|
|
65
|
+
step_numbers: StepNumbers,
|
|
66
|
+
) -> "Path":
|
|
67
|
+
"""Generate a motor path with a snail structure."""
|
|
68
|
+
|
|
69
|
+
x_p = Jump(
|
|
70
|
+
direction=Direction.POSITIVE,
|
|
71
|
+
axis=Axis.X,
|
|
72
|
+
step_number=step_numbers.xp,
|
|
73
|
+
speed=speed,
|
|
74
|
+
)
|
|
75
|
+
x_n = Jump(
|
|
76
|
+
direction=Direction.NEGATIVE,
|
|
77
|
+
axis=Axis.X,
|
|
78
|
+
step_number=step_numbers.xn,
|
|
79
|
+
speed=speed,
|
|
80
|
+
)
|
|
81
|
+
y_p = Jump(
|
|
82
|
+
direction=Direction.POSITIVE,
|
|
83
|
+
axis=Axis.Y,
|
|
84
|
+
step_number=step_numbers.yp,
|
|
85
|
+
speed=speed,
|
|
86
|
+
)
|
|
87
|
+
y_n = Jump(
|
|
88
|
+
direction=Direction.NEGATIVE,
|
|
89
|
+
axis=Axis.Y,
|
|
90
|
+
step_number=step_numbers.yn,
|
|
91
|
+
speed=speed,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
result: list[tuple[TimeDelta, Jump]] = []
|
|
95
|
+
current_offset = TimeDelta(0.0, format="sec")
|
|
96
|
+
|
|
97
|
+
for i in range(number * 2):
|
|
98
|
+
for _ in range(i + 1):
|
|
99
|
+
current_offset += duration
|
|
100
|
+
if i % 2 == 0:
|
|
101
|
+
result.append((current_offset, x_p))
|
|
102
|
+
else:
|
|
103
|
+
result.append((current_offset, x_n))
|
|
104
|
+
for _ in range(i + 1):
|
|
105
|
+
current_offset += duration
|
|
106
|
+
if i % 2 == 0:
|
|
107
|
+
result.append((current_offset, y_p))
|
|
108
|
+
else:
|
|
109
|
+
result.append((current_offset, y_n))
|
|
110
|
+
|
|
111
|
+
for _ in range(number):
|
|
112
|
+
current_offset += duration
|
|
113
|
+
result.append((current_offset, x_p))
|
|
114
|
+
for _ in range(number):
|
|
115
|
+
current_offset += duration
|
|
116
|
+
result.append((current_offset, y_p))
|
|
117
|
+
|
|
118
|
+
return Path(data=result)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Distance class."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import InitVar, dataclass, field
|
|
4
|
+
from uuid import UUID, uuid4
|
|
5
|
+
|
|
6
|
+
from astropy.uncertainty import Distribution
|
|
7
|
+
from gwpy.detector.channel import Channel
|
|
8
|
+
|
|
9
|
+
from backscattermeter_analyzer.hardware import MotorJump
|
|
10
|
+
|
|
11
|
+
from .state import State
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class Distance:
|
|
16
|
+
"""Distances between two points from multiple channels/axis."""
|
|
17
|
+
|
|
18
|
+
distance: dict[str | Channel, Distribution]
|
|
19
|
+
state: State = field(init=False)
|
|
20
|
+
identifier: UUID = field(default_factory=uuid4)
|
|
21
|
+
|
|
22
|
+
motor_jump: InitVar[MotorJump | None] = None
|
|
23
|
+
|
|
24
|
+
def __post_init__(self, motor_jump: MotorJump | None) -> None:
|
|
25
|
+
"""Post initialization."""
|
|
26
|
+
|
|
27
|
+
self.state = State(motor_jump=motor_jump)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""WithoutMotorJump class."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
from .state import State
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class WithoutMotorJump(State):
|
|
11
|
+
"""The distance is not linked to a motor jump."""
|
|
12
|
+
|
|
13
|
+
def has_motor_jump(self) -> Literal[False]:
|
|
14
|
+
"""In this state, the distance is not linked to a motor jump."""
|
|
15
|
+
|
|
16
|
+
return False
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""State class."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from backscattermeter_analyzer.utils.distance.state import (
|
|
8
|
+
State as DistanceState,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class State:
|
|
14
|
+
"""State of the link between distance and motor jump."""
|
|
15
|
+
|
|
16
|
+
state: "DistanceState"
|
|
17
|
+
|
|
18
|
+
def has_motor_jump(self) -> bool:
|
|
19
|
+
"""Check the distance is linked to a motor jump."""
|
|
20
|
+
return NotImplemented
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""WithMotorJump class."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
from backscattermeter_analyzer.hardware import MotorJump
|
|
7
|
+
|
|
8
|
+
from .state import State
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class WithMotorJump(State):
|
|
13
|
+
"""The distance is linked to a motor jump."""
|
|
14
|
+
|
|
15
|
+
jump: MotorJump
|
|
16
|
+
|
|
17
|
+
def has_motor_jump(self) -> Literal[True]:
|
|
18
|
+
"""In this state, the distance is linked to a motor jump."""
|
|
19
|
+
|
|
20
|
+
return True
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""State class."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from backscattermeter_analyzer.hardware import MotorJump
|
|
6
|
+
|
|
7
|
+
from .motor import State as MotorState
|
|
8
|
+
from .motor import WithMotorJump, WithoutMotorJump
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from .distance import Distance
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class State:
|
|
15
|
+
"""Distance state holder."""
|
|
16
|
+
|
|
17
|
+
distance: "Distance"
|
|
18
|
+
motor: MotorState
|
|
19
|
+
|
|
20
|
+
def __init__(self, motor_jump: MotorJump | None = None) -> None:
|
|
21
|
+
"""Initialize distance state."""
|
|
22
|
+
|
|
23
|
+
if motor_jump is not None:
|
|
24
|
+
self.motor = WithMotorJump(state=self, jump=motor_jump)
|
|
25
|
+
else:
|
|
26
|
+
self.motor = WithoutMotorJump(state=self)
|
|
27
|
+
|
|
28
|
+
def has_motor_jump(self) -> bool:
|
|
29
|
+
"""Check if the distance si linked to a motor jump."""
|
|
30
|
+
|
|
31
|
+
return self.motor.has_motor_jump()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""WithoutManualAngle class."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from .state import State
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class WithoutManualAngle(State):
|
|
9
|
+
"""The measurement was done without any change of angle."""
|
|
10
|
+
|
|
11
|
+
def has_manual_angle_change(self) -> Literal[False]:
|
|
12
|
+
"""No manual change of angle in this state."""
|
|
13
|
+
|
|
14
|
+
return False
|
backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/angle/state.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""State class."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from backscattermeter_analyzer.utils.measurement.state import (
|
|
8
|
+
State as MeasurementState,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class State:
|
|
14
|
+
"""Angle state for this measurement."""
|
|
15
|
+
|
|
16
|
+
state: "MeasurementState"
|
|
17
|
+
|
|
18
|
+
def has_manual_angle_change(self) -> bool:
|
|
19
|
+
"""
|
|
20
|
+
Define if measurement is associated to a manual change of angle.
|
|
21
|
+
|
|
22
|
+
Check the measurement has been made during a manual change of
|
|
23
|
+
angle of an optic in the setup.
|
|
24
|
+
"""
|
|
25
|
+
return NotImplemented
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""WithManualAngle class."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
from astropy.uncertainty import Distribution
|
|
7
|
+
|
|
8
|
+
from backscattermeter_analyzer import Axis
|
|
9
|
+
from backscattermeter_analyzer.hardware import Optic
|
|
10
|
+
|
|
11
|
+
from .state import State
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class WithManualAngle(State):
|
|
16
|
+
"""The measurement was done with a manual change of angle."""
|
|
17
|
+
|
|
18
|
+
axis: Axis # axis of the angle
|
|
19
|
+
angle: Distribution # the angle difference
|
|
20
|
+
optic: Optic # the optic we changed the angle of
|
|
21
|
+
# we don't know exactly when the change of angle was made, so we
|
|
22
|
+
# cannot store even an approximation of the time during which the
|
|
23
|
+
# change of angle was done. This can be retrieved from the analysis
|
|
24
|
+
# through, but it's not very useful anyway.
|
|
25
|
+
|
|
26
|
+
def has_manual_angle_change(self) -> Literal[True]:
|
|
27
|
+
"""Manual change of angle in this state."""
|
|
28
|
+
|
|
29
|
+
return True
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""WithoutCamera class."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from .state import State
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class WithoutCamera(State):
|
|
9
|
+
"""The measurement was done without any camera."""
|
|
10
|
+
|
|
11
|
+
def has_camera(self) -> Literal[False]:
|
|
12
|
+
"""No camera if in this state."""
|
|
13
|
+
|
|
14
|
+
return False
|
backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/camera/state.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""State class."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from backscattermeter_analyzer.utils.measurement.state import (
|
|
8
|
+
State as MeasurementState,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class State:
|
|
14
|
+
"""Camera state for this measurement."""
|
|
15
|
+
|
|
16
|
+
state: "MeasurementState"
|
|
17
|
+
|
|
18
|
+
def has_camera(self) -> bool:
|
|
19
|
+
"""Check the measurement was made with a camera."""
|
|
20
|
+
return NotImplemented
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""WithCamera class."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
from backscattermeter_analyzer.hardware import Camera
|
|
7
|
+
|
|
8
|
+
from .state import State
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class WithCamera(State):
|
|
13
|
+
"""The measurement was done with a camera."""
|
|
14
|
+
|
|
15
|
+
camera: Camera
|
|
16
|
+
|
|
17
|
+
def have_camera(self) -> Literal[True]:
|
|
18
|
+
"""Camera used if in this state."""
|
|
19
|
+
|
|
20
|
+
return True
|
backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/measurement.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Measurement class."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import InitVar, dataclass, field
|
|
4
|
+
from uuid import UUID, uuid4
|
|
5
|
+
|
|
6
|
+
from gwpy.timeseries import TimeSeriesDict
|
|
7
|
+
|
|
8
|
+
from backscattermeter_analyzer.hardware import (
|
|
9
|
+
Backscattermeter,
|
|
10
|
+
Camera,
|
|
11
|
+
MotorPath,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from .state import State
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class Measurement:
|
|
19
|
+
"""A measurement on the backscattermeter."""
|
|
20
|
+
|
|
21
|
+
data: TimeSeriesDict # type: ignore[ty:invalid-type-form]
|
|
22
|
+
state: State = field(init=False)
|
|
23
|
+
backscattermeter: Backscattermeter
|
|
24
|
+
identifier: UUID = field(default_factory=uuid4)
|
|
25
|
+
|
|
26
|
+
camera: InitVar[Camera | None] = None
|
|
27
|
+
motor_path: InitVar[MotorPath | None] = None
|
|
28
|
+
|
|
29
|
+
def __post_init__(
|
|
30
|
+
self, camera: Camera | None, motor_path: MotorPath | None
|
|
31
|
+
) -> None:
|
|
32
|
+
"""Post initialization."""
|
|
33
|
+
|
|
34
|
+
self.state = State(camera=camera, motor_path=motor_path)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""WithoutMotorPath class."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from .state import State
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class WithoutMotorPath(State):
|
|
9
|
+
"""The measurement is not linked to a motor path."""
|
|
10
|
+
|
|
11
|
+
def is_linked_to_a_motor_path(self) -> Literal[False]:
|
|
12
|
+
"""In this state, measurement is not linked to a motor path."""
|
|
13
|
+
|
|
14
|
+
return False
|
backscattermeter_analyzer-2.0.0/src/backscattermeter_analyzer/utils/measurement/motor/state.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""State class."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from backscattermeter_analyzer.utils.measurement.state import (
|
|
8
|
+
State as MeasurementState,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class State:
|
|
14
|
+
"""State of the link between the measurement and a motor path."""
|
|
15
|
+
|
|
16
|
+
state: "MeasurementState"
|
|
17
|
+
|
|
18
|
+
def is_linked_to_a_motor_path(self) -> bool:
|
|
19
|
+
"""Answer: is this measurement linked to a motor path."""
|
|
20
|
+
return NotImplemented
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""WithMotorPath class."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
from backscattermeter_analyzer.hardware import MotorPath
|
|
7
|
+
|
|
8
|
+
from .state import State
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class WithMotorPath(State):
|
|
13
|
+
"""The measurement is linked to a motor path."""
|
|
14
|
+
|
|
15
|
+
path: MotorPath
|
|
16
|
+
|
|
17
|
+
def is_linked_to_a_motor_path(self) -> Literal[True]:
|
|
18
|
+
"""In this state, the measurement is linked to a motor path."""
|
|
19
|
+
|
|
20
|
+
return True
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""State class."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from astropy.uncertainty import Distribution
|
|
6
|
+
|
|
7
|
+
from backscattermeter_analyzer import Axis
|
|
8
|
+
from backscattermeter_analyzer.hardware import (
|
|
9
|
+
Camera,
|
|
10
|
+
MotorPath,
|
|
11
|
+
Optic,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from .angle import AngleState, WithManualAngle, WithoutManualAngle
|
|
15
|
+
from .camera import CameraState, WithCamera, WithoutCamera
|
|
16
|
+
from .motor import MotorState, WithMotorPath, WithoutMotorPath
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from .measurement import Measurement
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class State:
|
|
23
|
+
"""Measurement state holder."""
|
|
24
|
+
|
|
25
|
+
measurement: "Measurement"
|
|
26
|
+
angle: AngleState
|
|
27
|
+
camera: CameraState
|
|
28
|
+
motor: MotorState
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
camera: Camera | None,
|
|
33
|
+
motor_path: MotorPath | None = None,
|
|
34
|
+
angle: Distribution | None = None,
|
|
35
|
+
angle_axis: Axis | None = None,
|
|
36
|
+
angle_optic: Optic | None = None,
|
|
37
|
+
) -> None:
|
|
38
|
+
"""
|
|
39
|
+
Initialize measurement state.
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
camera: Camera | None
|
|
44
|
+
Camera used during the experiment, if it exists. Else, None.
|
|
45
|
+
motor_path: MotorPath | None
|
|
46
|
+
Chain of motor jump during the experiment, if it exists.
|
|
47
|
+
Else, None.
|
|
48
|
+
angle: Distribution | None
|
|
49
|
+
Estimation of the change in angle made during the
|
|
50
|
+
measurement, if it exisis. Else, None.
|
|
51
|
+
angle_axis: Axis | None
|
|
52
|
+
Must be set if angle is set. Define the axis of the angle.
|
|
53
|
+
angle_optic: Optic | None
|
|
54
|
+
Must be set if angle is set. Define the optic which was
|
|
55
|
+
tilted.
|
|
56
|
+
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
if camera is not None:
|
|
60
|
+
self.camera = WithCamera(state=self, camera=camera)
|
|
61
|
+
else:
|
|
62
|
+
self.camera = WithoutCamera(state=self)
|
|
63
|
+
if motor_path is not None:
|
|
64
|
+
self.motor = WithMotorPath(state=self, path=motor_path)
|
|
65
|
+
else:
|
|
66
|
+
self.motor = WithoutMotorPath(state=self)
|
|
67
|
+
if angle is not None:
|
|
68
|
+
if angle_axis is None or angle_optic is None:
|
|
69
|
+
raise ValueError
|
|
70
|
+
self.angle = WithManualAngle(
|
|
71
|
+
state=self,
|
|
72
|
+
axis=angle_axis,
|
|
73
|
+
angle=angle,
|
|
74
|
+
optic=angle_optic,
|
|
75
|
+
)
|
|
76
|
+
else:
|
|
77
|
+
self.angle = WithoutManualAngle(state=self)
|
|
78
|
+
|
|
79
|
+
def has_camera(self) -> bool:
|
|
80
|
+
"""Check if this measurement is associated to a camera."""
|
|
81
|
+
|
|
82
|
+
return self.camera.has_camera()
|
|
83
|
+
|
|
84
|
+
def is_linked_to_a_motor_path(self) -> bool:
|
|
85
|
+
"""Check if this measurement is linked to a motor path."""
|
|
86
|
+
|
|
87
|
+
return self.motor.is_linked_to_a_motor_path()
|
|
88
|
+
|
|
89
|
+
def has_manual_angle_change(self) -> bool:
|
|
90
|
+
"""Is measurement is associated to a tilt of an optic."""
|
|
91
|
+
|
|
92
|
+
return self.angle.has_manual_angle_change()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|