pytesdaqx 0.1.0__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.
- pytesdaqx/__init__.py +10 -0
- pytesdaqx/acquisition/__init__.py +21 -0
- pytesdaqx/acquisition/fake.py +101 -0
- pytesdaqx/acquisition/nidaq_ai.py +723 -0
- pytesdaqx/acquisition/segment.py +39 -0
- pytesdaqx/acquisition/session.py +117 -0
- pytesdaqx/acquisition/streaming.py +167 -0
- pytesdaqx/analysis/__init__.py +3 -0
- pytesdaqx/analysis/continuity.py +244 -0
- pytesdaqx/cli/__init__.py +1 -0
- pytesdaqx/cli/build_manifest.py +149 -0
- pytesdaqx/cli/display_metadata.py +381 -0
- pytesdaqx/cli/laser.py +148 -0
- pytesdaqx/cli/run.py +184 -0
- pytesdaqx/cli/signal_generator.py +217 -0
- pytesdaqx/cli/squid.py +150 -0
- pytesdaqx/cli/temperature.py +127 -0
- pytesdaqx/cli/tes_bias.py +324 -0
- pytesdaqx/cli/transducer.py +73 -0
- pytesdaqx/cli/validate_config.py +48 -0
- pytesdaqx/cli/view_traces.py +964 -0
- pytesdaqx/config/__init__.py +30 -0
- pytesdaqx/config/durations.py +32 -0
- pytesdaqx/config/models.py +158 -0
- pytesdaqx/config/resolver.py +302 -0
- pytesdaqx/config/scan_values.py +141 -0
- pytesdaqx/config/schema.py +1052 -0
- pytesdaqx/config/temperature.py +419 -0
- pytesdaqx/config/validators.py +55 -0
- pytesdaqx/control/__init__.py +3 -0
- pytesdaqx/control/coordinator.py +1742 -0
- pytesdaqx/hardware/__init__.py +18 -0
- pytesdaqx/hardware/base.py +163 -0
- pytesdaqx/hardware/factory.py +48 -0
- pytesdaqx/hardware/legacy.py +157 -0
- pytesdaqx/hardware/registry.py +109 -0
- pytesdaqx/instruments/__init__.py +19 -0
- pytesdaqx/instruments/base.py +35 -0
- pytesdaqx/instruments/builtins.py +1632 -0
- pytesdaqx/instruments/communication.py +269 -0
- pytesdaqx/instruments/external.py +49 -0
- pytesdaqx/instruments/remote.py +443 -0
- pytesdaqx/instruments/signal_generator/__init__.py +0 -0
- pytesdaqx/instruments/signal_generator/agilent33500B.py +274 -0
- pytesdaqx/instruments/signal_generator/keithley2400.py +122 -0
- pytesdaqx/instruments/signal_generator/keysightDSOX1200.py +140 -0
- pytesdaqx/instruments/signal_generator/rigoldg800.py +253 -0
- pytesdaqx/instruments/temperature_controller/__init__.py +25 -0
- pytesdaqx/instruments/temperature_controller/base.py +40 -0
- pytesdaqx/instruments/temperature_controller/lakeshore.py +641 -0
- pytesdaqx/instruments/temperature_controller/macrt.py +195 -0
- pytesdaqx/instruments/temperature_controller/macrt_module.py +406 -0
- pytesdaqx/instruments/tes_squid/__init__.py +0 -0
- pytesdaqx/instruments/tes_squid/feb.py +657 -0
- pytesdaqx/instruments/tes_squid/magnicon.py +1340 -0
- pytesdaqx/io/__init__.py +69 -0
- pytesdaqx/io/acquisition_catalog.py +2205 -0
- pytesdaqx/io/catalog.py +17 -0
- pytesdaqx/io/hdf5_layout.py +93 -0
- pytesdaqx/io/hdf5_reader.py +405 -0
- pytesdaqx/io/metadata_compat.py +668 -0
- pytesdaqx/io/raw_data_reader.py +193 -0
- pytesdaqx/io/raw_data_writer.py +640 -0
- pytesdaqx/io/reader_utils.py +8 -0
- pytesdaqx/io/stream_reader.py +1372 -0
- pytesdaqx/io/zarr_reader.py +650 -0
- pytesdaqx/io/zarr_utils.py +550 -0
- pytesdaqx/naming.py +296 -0
- pytesdaqx/processing/__init__.py +3 -0
- pytesdaqx/processing/downsample.py +119 -0
- pytesdaqx/recorder/__init__.py +38 -0
- pytesdaqx/recorder/async_writer.py +84 -0
- pytesdaqx/recorder/base.py +31 -0
- pytesdaqx/recorder/hdf5.py +356 -0
- pytesdaqx/recorder/zarr.py +837 -0
- pytesdaqx/run/__init__.py +4 -0
- pytesdaqx/run/runner.py +107 -0
- pytesdaqx/run/yaml_run.py +2834 -0
- pytesdaqx/sequencer/__init__.py +10 -0
- pytesdaqx/sequencer/scan.py +98 -0
- pytesdaqx/sequencer/sequencer.py +117 -0
- pytesdaqx/services/__init__.py +24 -0
- pytesdaqx/services/laser.py +374 -0
- pytesdaqx/services/signal_generator_load.py +161 -0
- pytesdaqx/services/squid.py +489 -0
- pytesdaqx/services/temperature.py +472 -0
- pytesdaqx/services/tes_bias.py +1191 -0
- pytesdaqx/services/transducer.py +185 -0
- pytesdaqx/testing/__init__.py +0 -0
- pytesdaqx/testing/analyze_continuity.py +67 -0
- pytesdaqx/testing/lab_check.py +66 -0
- pytesdaqx/testing/ni_cli_common.py +82 -0
- pytesdaqx/testing/real_ni_benchmark.py +270 -0
- pytesdaqx/testing/real_ni_continuity_test.py +132 -0
- pytesdaqx/testing/real_ni_run_continuous.py +147 -0
- pytesdaqx/testing/real_ni_smoke.py +115 -0
- pytesdaqx/testing/real_ni_trigger_test.py +108 -0
- pytesdaqx-0.1.0.dist-info/METADATA +93 -0
- pytesdaqx-0.1.0.dist-info/RECORD +102 -0
- pytesdaqx-0.1.0.dist-info/WHEEL +5 -0
- pytesdaqx-0.1.0.dist-info/entry_points.txt +19 -0
- pytesdaqx-0.1.0.dist-info/top_level.txt +1 -0
pytesdaqx/__init__.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Python acquisition and instrument-control framework for TES detector readout."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
__version__ = version("pytesdaqx")
|
|
7
|
+
except PackageNotFoundError: # Source tree imported before installation.
|
|
8
|
+
__version__ = "0+unknown"
|
|
9
|
+
|
|
10
|
+
__all__ = ["__version__"]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from .fake import FakeAcquisitionBackend
|
|
2
|
+
from .nidaq_ai import NIDAQAIBackend, NIDAQAIConfig, NIDAQAIError
|
|
3
|
+
from .segment import Segment
|
|
4
|
+
from .session import DAQSession
|
|
5
|
+
from .streaming import (
|
|
6
|
+
SegmentRingBuffer,
|
|
7
|
+
StreamingAcquisitionLoop,
|
|
8
|
+
StreamingResult,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"Segment",
|
|
13
|
+
"FakeAcquisitionBackend",
|
|
14
|
+
"DAQSession",
|
|
15
|
+
"NIDAQAIBackend",
|
|
16
|
+
"NIDAQAIConfig",
|
|
17
|
+
"NIDAQAIError",
|
|
18
|
+
"SegmentRingBuffer",
|
|
19
|
+
"StreamingResult",
|
|
20
|
+
"StreamingAcquisitionLoop",
|
|
21
|
+
]
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Deterministic, hardware-free acquisition backend for development and tests.
|
|
2
|
+
|
|
3
|
+
``FakeAcquisitionBackend`` generates synthetic segments (noise around
|
|
4
|
+
zero, seeded for reproducibility) with the same shape/timing contract as
|
|
5
|
+
``NIDAQAIBackend``, so configuration resolution and acquisition
|
|
6
|
+
orchestration can be exercised end to end without contacting hardware.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import itertools
|
|
12
|
+
import time
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
|
|
17
|
+
from pytesdaqx.config.durations import parse_duration
|
|
18
|
+
|
|
19
|
+
from .segment import Segment
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class FakeAcquisitionBackend:
|
|
23
|
+
"""Synthetic acquisition backend producing seeded noise segments.
|
|
24
|
+
|
|
25
|
+
Drop-in stand-in for ``NIDAQAIBackend`` behind ``DAQSession``: same
|
|
26
|
+
``configure``/acquire contract, no hardware access.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, *, dtype=np.float32, noise_rms=0.01, seed=12345):
|
|
30
|
+
self.dtype = np.dtype(dtype)
|
|
31
|
+
self.noise_rms = noise_rms
|
|
32
|
+
self.rng = np.random.default_rng(seed)
|
|
33
|
+
self._counter = itertools.count(1)
|
|
34
|
+
|
|
35
|
+
def configure(
|
|
36
|
+
self,
|
|
37
|
+
*,
|
|
38
|
+
measurement_name: str,
|
|
39
|
+
measurement_config: dict[str, Any],
|
|
40
|
+
detector_channels: list[str],
|
|
41
|
+
) -> None:
|
|
42
|
+
self.measurement_name = measurement_name
|
|
43
|
+
self.measurement_config = measurement_config
|
|
44
|
+
self.detector_channels = list(detector_channels)
|
|
45
|
+
|
|
46
|
+
adc = measurement_config.get("adc", {}) or {}
|
|
47
|
+
self.sample_rate_hz = float(adc.get("sample_rate", adc.get("sample_rate_hz", 1000.0)))
|
|
48
|
+
self.n_samples = int(
|
|
49
|
+
adc.get(
|
|
50
|
+
"n_samples",
|
|
51
|
+
adc.get(
|
|
52
|
+
"samples_per_read_block",
|
|
53
|
+
adc.get("samples_per_trace", adc.get("samples_per_segment", 0)),
|
|
54
|
+
),
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
if self.n_samples <= 0:
|
|
58
|
+
duration = adc.get(
|
|
59
|
+
"read_block_duration",
|
|
60
|
+
adc.get("trace_duration", adc.get("segment_duration", "1s")),
|
|
61
|
+
)
|
|
62
|
+
self.n_samples = self._samples_from_duration(duration, self.sample_rate_hz)
|
|
63
|
+
|
|
64
|
+
def read_segment(self) -> Segment:
|
|
65
|
+
segment_id = next(self._counter)
|
|
66
|
+
n_channels = len(self.detector_channels)
|
|
67
|
+
times = np.arange(self.n_samples) / self.sample_rate_hz
|
|
68
|
+
data = np.zeros((n_channels, self.n_samples), dtype=float)
|
|
69
|
+
|
|
70
|
+
for index in range(n_channels):
|
|
71
|
+
data[index] = (
|
|
72
|
+
0.1 * index
|
|
73
|
+
+ 0.02 * np.sin(2 * np.pi * (3 + index) * times)
|
|
74
|
+
+ self.rng.normal(0, self.noise_rms, self.n_samples)
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
return Segment(
|
|
78
|
+
data=data.astype(self.dtype),
|
|
79
|
+
detector_channels=self.detector_channels,
|
|
80
|
+
measurement=self.measurement_name,
|
|
81
|
+
label=self.measurement_config.get("label", self.measurement_name),
|
|
82
|
+
sample_rate_hz=self.sample_rate_hz,
|
|
83
|
+
timestamp=time.time(),
|
|
84
|
+
segment_id=segment_id,
|
|
85
|
+
metadata={
|
|
86
|
+
"backend": "fake",
|
|
87
|
+
"adc_mode": self.measurement_config.get("adc", {}).get("mode"),
|
|
88
|
+
},
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def close(self) -> None:
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def _samples_from_duration(duration: Any, sample_rate: float) -> int:
|
|
96
|
+
parsed = parse_duration(duration)
|
|
97
|
+
if parsed == "auto_from_cycles":
|
|
98
|
+
parsed = 1.0
|
|
99
|
+
if parsed is None or isinstance(parsed, str) or parsed <= 0:
|
|
100
|
+
raise ValueError(f"Invalid duration {duration!r}")
|
|
101
|
+
return int(round(float(parsed) * sample_rate))
|