pytesdaqx 0.2.2__tar.gz → 0.2.4__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.
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/PKG-INFO +2 -2
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/developer/data_io.md +29 -9
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/user/cli/data_inspection.md +51 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/user/cli/index.md +1 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/user/configuration/acquisition_config.md +50 -4
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/user/configuration/run_setup_yaml.md +27 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/user/configuration/scans.md +2 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/README.md +15 -0
- pytesdaqx-0.2.4/examples/configuration/blinded/acquisition_blinded_75_25.yaml +117 -0
- pytesdaqx-0.2.4/examples/configuration/blinded/generate_blinded_sequence.py +165 -0
- pytesdaqx-0.2.4/examples/configuration/vibration/hardware_fake.yaml +15 -0
- pytesdaqx-0.2.4/examples/configuration/vibration/run_setup.yaml +45 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pyproject.toml +3 -2
- pytesdaqx-0.2.4/pytesdaqx/cli/repair_readout.py +168 -0
- pytesdaqx-0.2.4/pytesdaqx/cli/signal_generator.py +438 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/cli/view_traces.py +2 -1
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/config/resolver.py +37 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/config/schema.py +32 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/builtins.py +64 -9
- pytesdaqx-0.2.4/pytesdaqx/instruments/scpi_errors.py +44 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/signal_generator/agilent33500B.py +79 -8
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/io/hdf5_reader.py +10 -9
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/io/metadata_compat.py +104 -41
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/io/raw_data_writer.py +30 -2
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/io/zarr_reader.py +10 -11
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/naming.py +44 -1
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/recorder/hdf5.py +4 -1
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/recorder/zarr.py +38 -2
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/run/yaml_run.py +210 -16
- pytesdaqx-0.2.4/pytesdaqx/testing/check_33500b_pulse.py +243 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx.egg-info/PKG-INFO +2 -2
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx.egg-info/SOURCES.txt +13 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx.egg-info/entry_points.txt +1 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx.egg-info/requires.txt +1 -1
- pytesdaqx-0.2.4/tests/test_accelerometer_readout.py +175 -0
- pytesdaqx-0.2.4/tests/test_agilent33500B_pulse.py +298 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_raw_data_writer.py +30 -0
- pytesdaqx-0.2.4/tests/test_read_block_defaults.py +123 -0
- pytesdaqx-0.2.4/tests/test_repair_readout.py +186 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_repository_layout.py +1 -0
- pytesdaqx-0.2.4/tests/test_signal_generator_cli.py +441 -0
- pytesdaqx-0.2.4/tests/test_stream_id_uniqueness.py +167 -0
- pytesdaqx-0.2.4/tests/test_unit_conversion.py +80 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_zarr_chunking_and_sharding.py +48 -0
- pytesdaqx-0.2.2/pytesdaqx/cli/signal_generator.py +0 -217
- pytesdaqx-0.2.2/tests/test_signal_generator_cli.py +0 -189
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/MANIFEST.in +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/README.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/developer/acquisition_pipeline.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/developer/adding_hardware.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/developer/architecture.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/developer/configuration_pipeline.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/developer/hardware_support.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/developer/index.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/developer/package_structure.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/developer/release_process.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/developer/testing.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/index.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/installation/index.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/installation/rocky_linux_9.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/installation/troubleshooting.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/installation/ubuntu.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/installation/validation.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/installation/visa.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/user/cli/acquisition.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/user/cli/instrument_control.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/user/cli/lab_diagnostics.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/user/configuration/hardware_yaml.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/user/configuration/index.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/user/configuration/resolution.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/user/configuration/temperature_controllers.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/user/data_format.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/user/quickstart.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/user/terminology.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/user/troubleshooting.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/docs/user/workflows.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/basic/acquisition_background.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/basic/acquisition_didv_background.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/basic/acquisition_notebook_demo.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/basic/hardware_fake.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/basic/hardware_feb_signal_generator.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/basic/run_setup.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/calibration/acquisition_laser.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/pxi_loopback/README.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/pxi_loopback/acquisition_didv.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/pxi_loopback/acquisition_iv_restore.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/scans/acquisition_didv_ac.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/scans/acquisition_didv_frequency_focus.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/scans/acquisition_temperature_fake.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/scans/acquisition_tes_bias_iv_didv.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/scans/acquisition_transducer_vibration_scan.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/temperature/hardware_lakeshore372.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/temperature/hardware_macrt.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/temperature/run_setup_lakeshore372.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/configuration/temperature/run_setup_macrt.yaml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/simulation/README.md +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/examples/simulation/write_simulated_zarr.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/mkdocs.yml +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/acquisition/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/acquisition/fake.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/acquisition/nidaq_ai.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/acquisition/segment.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/acquisition/session.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/acquisition/streaming.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/analysis/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/analysis/continuity.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/cli/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/cli/build_manifest.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/cli/display_metadata.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/cli/laser.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/cli/run.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/cli/squid.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/cli/temperature.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/cli/tes_bias.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/cli/transducer.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/cli/validate_config.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/config/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/config/durations.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/config/models.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/config/scan_values.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/config/temperature.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/config/validators.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/control/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/control/coordinator.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/hardware/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/hardware/base.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/hardware/factory.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/hardware/legacy.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/hardware/registry.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/base.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/communication.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/external.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/remote.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/signal_generator/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/signal_generator/keithley2400.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/signal_generator/keysightDSOX1200.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/signal_generator/rigoldg800.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/temperature_controller/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/temperature_controller/base.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/temperature_controller/lakeshore.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/temperature_controller/macrt.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/temperature_controller/macrt_module.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/tes_squid/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/tes_squid/feb.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/instruments/tes_squid/magnicon.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/io/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/io/acquisition_catalog.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/io/catalog.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/io/hdf5_layout.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/io/raw_data_reader.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/io/reader_utils.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/io/stream_reader.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/io/zarr_utils.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/processing/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/processing/downsample.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/recorder/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/recorder/async_writer.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/recorder/base.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/run/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/run/runner.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/sequencer/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/sequencer/scan.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/sequencer/sequencer.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/services/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/services/laser.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/services/signal_generator_load.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/services/squid.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/services/temperature.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/services/tes_bias.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/services/transducer.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/testing/__init__.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/testing/analyze_continuity.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/testing/lab_check.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/testing/ni_cli_common.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/testing/real_ni_benchmark.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/testing/real_ni_continuity_test.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/testing/real_ni_run_continuous.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/testing/real_ni_smoke.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx/testing/real_ni_trigger_test.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx.egg-info/dependency_links.txt +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/pytesdaqx.egg-info/top_level.txt +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/setup.cfg +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_acquisition_catalog_api_cleanup.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_acquisition_catalog_naming.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_api_cleanup.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_cli_contract.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_code_cleanup_regressions.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_detector_settings_normalization.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_developer_documentation.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_distribution_readiness.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_facility_catalog_metadata.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_feb_driver.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_feb_signal_generator_didv.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_finite_record_metadata.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_io_coordinate_cleanup.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_lakeshore372_driver.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_laser_configuration_ownership.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_macrt_driver.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_magnicon_driver.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_manifest_and_zarr_metadata.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_metadata_canonical_schema.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_optional_hdf5_import.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_partition_planning.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_release_cleanup.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_run_cli_cleanup.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_scan_documentation.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_scan_values.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_signal_generator_drivers.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_signal_generator_load_policy.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_stream_reader_api_cleanup.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_temperature_acquisition_integration.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_temperature_cli.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_temperature_configuration.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_temperature_safety_cleanup.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_temperature_service.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_transducer_configuration_ownership.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_user_documentation.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_view_trace_partitions.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_zarr_compression.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_zarr_default_and_naming.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_zarr_manifest_sidecars.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_zarr_metadata_layout.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_zarr_native_backend.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_zarr_read_block_terminology.py +0 -0
- {pytesdaqx-0.2.2 → pytesdaqx-0.2.4}/tests/test_zarr_trace_labels.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytesdaqx
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: TES detector acquisition, instrument control, and raw-data recording
|
|
5
5
|
Author: TESSERACT DAQ developers
|
|
6
6
|
Classifier: Development Status :: 3 - Alpha
|
|
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
12
12
|
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
13
13
|
Requires-Python: <3.13,>=3.11
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
|
-
Requires-Dist: numpy
|
|
15
|
+
Requires-Dist: numpy>=1.26
|
|
16
16
|
Requires-Dist: PyYAML
|
|
17
17
|
Requires-Dist: h5py>=3.10
|
|
18
18
|
Requires-Dist: matplotlib
|
|
@@ -52,21 +52,41 @@ centered on its trigger.
|
|
|
52
52
|
`ZarrRecorder` defaults to zstd compression (level 9) and to sharded arrays for
|
|
53
53
|
both shapes:
|
|
54
54
|
|
|
55
|
-
- **Continuous** (`channel_sample`):
|
|
56
|
-
(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
count low for filesystems like NERSC/CC-IN2P3 that penalize very large
|
|
60
|
-
numbers of small files.
|
|
55
|
+
- **Continuous** (`channel_sample`): small chunks keep partial reads cheap
|
|
56
|
+
(decompression cost is paid per chunk touched, not per array), while
|
|
57
|
+
sharding keeps the on-disk object count low for filesystems like
|
|
58
|
+
NERSC/CC-IN2P3 that penalize very large numbers of small files.
|
|
61
59
|
- **Finite/trace** (`trace_channel_sample`): one trace per chunk, sharded
|
|
62
60
|
toward a 10s-equivalent target (`trace_sharding=True`,
|
|
63
61
|
`trace_shard_target_duration_s=10.0`).
|
|
64
62
|
|
|
63
|
+
The continuous geometry has two layers, because the useful sizes depend on
|
|
64
|
+
the acquisition and only the run layer knows it:
|
|
65
|
+
|
|
66
|
+
| | `ZarrRecorder` defaults | resolved by `pytesdaqx-run` from the YAML |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| chunk | `1s` | `0.25s`, grown towards 1 MiB at low data rates (capped 10s), then shortened to divide one read block |
|
|
69
|
+
| shard | `10s` | one read block: the longest block up to 60s that divides the step duration |
|
|
70
|
+
| shard bytes | 128 MiB | 8 GiB, so a whole block is never clamped |
|
|
71
|
+
| chunks per shard | `max_chunks_per_shard=100` | `continuous_max_chunks_per_shard` (high): one block fills a shard in a single write |
|
|
72
|
+
|
|
73
|
+
Constructing `ZarrRecorder` directly (including through `RawDataWriter`
|
|
74
|
+
without recorder options) therefore keeps the older 1s/10s geometry; pass
|
|
75
|
+
the options explicitly to match a run. See
|
|
76
|
+
[acquisition configuration](../user/configuration/acquisition_config.md)
|
|
77
|
+
for the user-facing rules.
|
|
78
|
+
|
|
79
|
+
A shard is rewritten whenever part of it is written, so a read block shorter
|
|
80
|
+
than the shard multiplies disk writes (measured: 5.5x for 1s blocks in 10s
|
|
81
|
+
shards, 30x for 1s blocks in 60s shards, at 3 channels and 1.25 MHz). Keeping
|
|
82
|
+
shard == read block writes each shard exactly once.
|
|
83
|
+
|
|
65
84
|
Both shard-sizing paths (`_continuous_chunking_metadata` and
|
|
66
85
|
`_trace_shard_traces` in `pytesdaqx/recorder/zarr.py`) apply a third
|
|
67
|
-
constraint, `max_chunks_per_shard
|
|
68
|
-
|
|
69
|
-
|
|
86
|
+
constraint, `max_chunks_per_shard`, in addition to the duration and
|
|
87
|
+
byte-size targets: `shard_chunk_count = min(by_duration, by_bytes,
|
|
88
|
+
max_chunks_per_shard)`. A chunk or shard reduced by any of these limits
|
|
89
|
+
emits a warning naming the binding one.
|
|
70
90
|
|
|
71
91
|
This cap exists because of a measured defect in zarr-python 3.1.5: the
|
|
72
92
|
per-write cost of an incremental `resize()` + append into a sharded array
|
|
@@ -110,6 +110,8 @@ pytesdaqx-view-traces acquisition_dir \
|
|
|
110
110
|
|
|
111
111
|
The default display units are detector amps when the required readout-circuit metadata are available. Other supported choices include ADC volts or raw ADC values, depending on stored read mode and CLI options.
|
|
112
112
|
|
|
113
|
+
Accelerometer channels can be displayed in g with `--units g`; this requires `readout_circuit.amplifier_gain` (and optionally `sensitivity_v_per_g`) in the run setup. See [Accelerometer readout](../configuration/run_setup_yaml.md#accelerometer-readout).
|
|
114
|
+
|
|
113
115
|
If amp conversion fails, inspect the channel `readout_circuit` values in stored metadata, especially output gain and resistance/turn-ratio fields.
|
|
114
116
|
|
|
115
117
|
### Plot decimation
|
|
@@ -140,3 +142,52 @@ pytesdaqx-build-manifest --acquisition-dir acquisition_I2_D20260727_T120000 --dr
|
|
|
140
142
|
Do not routinely rewrite the manifest of an active acquisition. Work on a completed copy unless a documented recovery procedure explicitly says otherwise.
|
|
141
143
|
|
|
142
144
|
The command summary reports the singular acquisition `storage_format` (`zarr` or `hdf5`) and acquisition timing when available, including `fridge_run`, `fridge_run_start`, `acquisition_start`, and the first/last `stream_start`. When rebuilding a Zarr manifest, missing acquisition-wide timing fields in individual stream stores are filled from the top-level acquisition `zarr.json`; each stream's own `stream_start` remains resource-specific. Older pytesdaqx/pytesdaq metadata using `group_*`/`series_*` and `group_start`/`series_start` is accepted on input and converted to the canonical acquisition/stream schema in the rebuilt manifest.
|
|
145
|
+
|
|
146
|
+
## `pytesdaqx-repair-readout`
|
|
147
|
+
|
|
148
|
+
Readout constants come from `run_setup.yaml` and are stored with every stream.
|
|
149
|
+
A channel recorded without them cannot be converted to physical units
|
|
150
|
+
afterwards, which stops processing with an error such as
|
|
151
|
+
`Cannot convert rigolTTL to amps: missing/invalid close_loop_norm=nan`.
|
|
152
|
+
This happens for a channel configured with an ADC assignment but no
|
|
153
|
+
`readout_circuit`, typically a TTL or other monitor line that has no SQUID
|
|
154
|
+
path.
|
|
155
|
+
|
|
156
|
+
Use this command to write the missing constants into the stored channel map of
|
|
157
|
+
an existing acquisition. Sample data are never modified.
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# Report what would change (default)
|
|
161
|
+
pytesdaqx-repair-readout /data/raw/calibration_I2_D20260924_T152603.zarr \
|
|
162
|
+
--channel rigolTTL --set close_loop_norm=1
|
|
163
|
+
|
|
164
|
+
# Write the change
|
|
165
|
+
pytesdaqx-repair-readout /data/raw/calibration_I2_D20260924_T152603.zarr \
|
|
166
|
+
--channel rigolTTL --set close_loop_norm=1 --apply
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Only streams that recorded the channel are updated. In a mixed acquisition the
|
|
170
|
+
IV, dIdV, and background streams usually do not contain an auxiliary channel,
|
|
171
|
+
and are left untouched; the summary reports how many streams were skipped for
|
|
172
|
+
that reason. Repeat `--set` for several constants. `--set` accepts the readout
|
|
173
|
+
keys listed in `pytesdaqx.io.metadata_compat.READOUT_SETTING_KEYS`; other keys
|
|
174
|
+
are rejected, because readers would ignore them.
|
|
175
|
+
|
|
176
|
+
For a monitor line digitized without a SQUID readout, `close_loop_norm=1` makes
|
|
177
|
+
the "amps" conversion return the measured volts, which keeps threshold settings
|
|
178
|
+
in the units the trigger was tuned against.
|
|
179
|
+
|
|
180
|
+
This command repairs data already on disk. Add the same constants to the
|
|
181
|
+
channel's `readout_circuit` in `run_setup.yaml` so future acquisitions record
|
|
182
|
+
them directly:
|
|
183
|
+
|
|
184
|
+
```yaml
|
|
185
|
+
rigolTTL:
|
|
186
|
+
adc:
|
|
187
|
+
device: adc1
|
|
188
|
+
channel: 4
|
|
189
|
+
record_for: [calibration]
|
|
190
|
+
readout_circuit:
|
|
191
|
+
close_loop_norm: 1
|
|
192
|
+
```
|
|
193
|
+
|
|
@@ -18,6 +18,7 @@ See [Acquisition and validation](acquisition.md).
|
|
|
18
18
|
| `pytesdaqx-display-metadata` | Display acquisition, stream, and legacy HDF5 metadata |
|
|
19
19
|
| `pytesdaqx-view-traces` | Plot continuous partitions or finite traces |
|
|
20
20
|
| `pytesdaqx-build-manifest` | Rebuild or inspect acquisition manifest information |
|
|
21
|
+
| `pytesdaqx-repair-readout` | Write missing readout constants into recorded metadata |
|
|
21
22
|
|
|
22
23
|
See [Data inspection](data_inspection.md).
|
|
23
24
|
|
|
@@ -53,9 +53,36 @@ sequence:
|
|
|
53
53
|
duration: 15m
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
### A step records whole read blocks
|
|
57
|
+
|
|
58
|
+
Acquisition always records complete read blocks, so `sequence[].duration` is
|
|
59
|
+
rounded **up** to a whole number of them. With a 60-second read block a
|
|
60
|
+
20-second step records 60 seconds of data, not 20. Keep the step duration a
|
|
61
|
+
multiple of the read-block duration; pytesdaqx warns when it is not, naming
|
|
62
|
+
the amount of data that will actually be taken.
|
|
63
|
+
|
|
56
64
|
### Automatic read-block duration
|
|
57
65
|
|
|
58
|
-
`read_block_duration` is optional. When omitted, pytesdaqx
|
|
66
|
+
`read_block_duration` is optional. When omitted, pytesdaqx uses the longest
|
|
67
|
+
block up to 60 seconds that divides the step duration exactly, so that a step
|
|
68
|
+
never takes more data than requested:
|
|
69
|
+
|
|
70
|
+
| Step duration | Automatic read block | Blocks recorded |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| 3 min | 60 s | 3 |
|
|
73
|
+
| 100 s | 50 s | 2 |
|
|
74
|
+
| 20 s | 20 s | 1 |
|
|
75
|
+
| 1 s (scan point) | 1 s | 1 |
|
|
76
|
+
|
|
77
|
+
One read block is written as one shard, so this also sets the file count: a
|
|
78
|
+
three-minute stream becomes three data objects instead of eighteen at the old
|
|
79
|
+
ten-second default. The cost is buffering: one block is held in memory before
|
|
80
|
+
it is written, which is `channels x sample_rate x 2 bytes x block` (1.2 GB for
|
|
81
|
+
8 channels at 1.25 MHz with a 60-second block), and an interrupted acquisition
|
|
82
|
+
loses the block in flight.
|
|
83
|
+
|
|
84
|
+
Steps driven by `n_read_blocks` rather than `duration` keep the historical
|
|
85
|
+
chunk-aligned block.
|
|
59
86
|
|
|
60
87
|
For example:
|
|
61
88
|
|
|
@@ -88,10 +115,29 @@ A **chunk** is a physical Zarr storage-layout unit. It is independent of a read
|
|
|
88
115
|
|
|
89
116
|
| Key | Default | Meaning |
|
|
90
117
|
|---|---:|---|
|
|
91
|
-
| `chunk_duration` | `
|
|
118
|
+
| `chunk_duration` | `0.25s`, grown at low data rates | Continuous chunk-duration target |
|
|
92
119
|
| `chunk_target_mb` | `128` | Maximum target chunk size |
|
|
93
|
-
|
|
94
|
-
|
|
120
|
+
| `shard_duration` | one read block | Continuous shard-duration target |
|
|
121
|
+
| `shard_target_mb` | `8192` (continuous), `128` (traces) | Maximum target shard size |
|
|
122
|
+
| `max_chunks_per_shard` | `100` (traces) | Chunks per trace shard |
|
|
123
|
+
|
|
124
|
+
A chunk is the unit a reader must decompress, so the default keeps it short
|
|
125
|
+
(about 5-8 MB at 20-32 MB/s) to make short random reads such as feature
|
|
126
|
+
windows cheap; at low data rates it grows towards 1 MiB so it stays worth
|
|
127
|
+
compressing, up to 10 seconds. A shard is one file and defaults to one read
|
|
128
|
+
block, which writes each shard exactly once.
|
|
129
|
+
|
|
130
|
+
The resolved chunk may be shorter than `chunk_duration`, and the resolved
|
|
131
|
+
shard shorter than `shard_duration`, when a byte cap or `max_chunks_per_shard`
|
|
132
|
+
is reached. Both cases emit a warning naming the binding limit.
|
|
133
|
+
|
|
134
|
+
### Shards, blocks and write cost
|
|
135
|
+
|
|
136
|
+
A shard is rewritten every time part of it is written, so a read block shorter
|
|
137
|
+
than the shard multiplies disk writes: at 3 channels and 1.25 MHz, one-second
|
|
138
|
+
blocks with 10-second shards write 4.2 GB for 764 MB of data (5.5x), and with
|
|
139
|
+
60-second shards 23 GB (30x). Leaving both at their defaults, or setting
|
|
140
|
+
`read_block_duration` equal to `shard_duration`, writes each shard once.
|
|
95
141
|
|
|
96
142
|
## Finite acquisition: traces
|
|
97
143
|
|
|
@@ -108,6 +108,33 @@ readout_circuit:
|
|
|
108
108
|
|
|
109
109
|
The correct constants depend on the installed readout circuit.
|
|
110
110
|
|
|
111
|
+
### Accelerometer readout
|
|
112
|
+
|
|
113
|
+
Auxiliary sensors such as accelerometers use the same `readout_circuit`
|
|
114
|
+
mapping. The conversion from ADC voltage to acceleration depends on the
|
|
115
|
+
sensor specification and its signal-conditioner gain:
|
|
116
|
+
|
|
117
|
+
```yaml
|
|
118
|
+
channels:
|
|
119
|
+
AccelerometerX:
|
|
120
|
+
adc:
|
|
121
|
+
device: adc1
|
|
122
|
+
channel: 0
|
|
123
|
+
readout_circuit:
|
|
124
|
+
sensor_type: accelerometer
|
|
125
|
+
amplifier_gain: 100 # signal conditioner gain (typically 1, 10 or 100)
|
|
126
|
+
sensitivity_v_per_g: 1.0 # sensor sensitivity in V/g (default 1)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Acceleration in g is `volts / (amplifier_gain * sensitivity_v_per_g)`.
|
|
130
|
+
`amplifier_gain` is required when `sensor_type: accelerometer`; both values
|
|
131
|
+
must be positive. The constants are saved with the stream metadata, exposed
|
|
132
|
+
by `get_detector_settings()` as `amplifier_gain`, `sensitivity_v_per_g`, and
|
|
133
|
+
`acceleration_norm_v_per_g`, and used when reading data with `units="g"`.
|
|
134
|
+
|
|
135
|
+
A complete example with three accelerometers and a vibration transducer is
|
|
136
|
+
in `examples/configuration/vibration/`.
|
|
137
|
+
|
|
111
138
|
## Other run-level mappings
|
|
112
139
|
|
|
113
140
|
A run setup may also define:
|
|
@@ -430,6 +430,8 @@ Cleanup is not implicit. Without `scan.cleanup.temperature`, the final scan setp
|
|
|
430
430
|
|
|
431
431
|
A vibration transducer is driven by a signal generator; `channels` names logical transducers from `run_setup.transducers`, not detector channels.
|
|
432
432
|
|
|
433
|
+
Accelerometer channels recorded during the scan should define their volts-to-g conversion in `run_setup.yaml` (see [Accelerometer readout](run_setup_yaml.md#accelerometer-readout)); `examples/configuration/vibration/run_setup.yaml` pairs with the vibration scan example below.
|
|
434
|
+
|
|
433
435
|
| Target | Values and units | Behavior |
|
|
434
436
|
|---|---|---|
|
|
435
437
|
| `transducer.frequency_hz` | Numeric drive frequency in Hz | Sets the transducer signal generator's frequency |
|
|
@@ -42,6 +42,10 @@ configuration/
|
|
|
42
42
|
README.md
|
|
43
43
|
acquisition_didv.yaml
|
|
44
44
|
acquisition_iv_restore.yaml
|
|
45
|
+
|
|
46
|
+
vibration/
|
|
47
|
+
hardware_fake.yaml
|
|
48
|
+
run_setup.yaml
|
|
45
49
|
```
|
|
46
50
|
|
|
47
51
|
The files under `basic/` form the common configuration foundation:
|
|
@@ -57,6 +61,17 @@ The files under `basic/` form the common configuration foundation:
|
|
|
57
61
|
|
|
58
62
|
The basic hardware and run-setup examples also include a fake temperature controller using separate `sensors` and `heaters` inventory mappings. The logical `mc` path is suitable for testing `pytesdaqx-temperature`, canonical `temperature.setpoint` scans, stability waiting, and opt-in stream snapshots with `acquisition_temperature_fake.yaml`.
|
|
59
63
|
|
|
64
|
+
The `vibration/` directory maps three accelerometers (`AccelerometerX`, `AccelerometerY`, `AccelerometerZ`) to `adc1` channels 0-2, with their volts-to-g conversion (`readout_circuit.amplifier_gain` and `sensitivity_v_per_g`), plus a vibration transducer. Use it with `scans/acquisition_transducer_vibration_scan.yaml`:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pytesdaqx-run \
|
|
68
|
+
--hardware examples/configuration/vibration/hardware_fake.yaml \
|
|
69
|
+
--run-setup examples/configuration/vibration/run_setup.yaml \
|
|
70
|
+
--acquisition-config examples/configuration/scans/acquisition_transducer_vibration_scan.yaml \
|
|
71
|
+
--backend fake \
|
|
72
|
+
--no-save-data
|
|
73
|
+
```
|
|
74
|
+
|
|
60
75
|
The `temperature/` directory contains the native MACRT inventory and logical temperature mappings used for real-controller CLI tests.
|
|
61
76
|
|
|
62
77
|
The scan examples cover the currently maintained scan patterns. Less common
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Blinded physics running: 75% restricted / 25% open, with a grouped
|
|
2
|
+
# IV+dIdV calibration block at the start and end of every cycle.
|
|
3
|
+
#
|
|
4
|
+
# One cycle (repeated by run.n_sequence_repeats):
|
|
5
|
+
#
|
|
6
|
+
# [ IV -> dIdV ] -> 4 data slots -> [ dIdV ]
|
|
7
|
+
# opening calib 3 restricted closing calib
|
|
8
|
+
# + 1 open
|
|
9
|
+
#
|
|
10
|
+
# Why the data block is split into slots instead of one restricted block
|
|
11
|
+
# followed by one open block: with a fixed "restricted then open" order the
|
|
12
|
+
# open sample always sits further from the calibration than the restricted
|
|
13
|
+
# sample does, so bias-point drift biases exactly the 25% used to develop
|
|
14
|
+
# cuts and efficiencies. Splitting into slots and moving the open slot
|
|
15
|
+
# around makes the time-since-dIdV distribution the same for both samples.
|
|
16
|
+
#
|
|
17
|
+
# The open slot's position is rotated by hand here (see the four cycle
|
|
18
|
+
# variants below). For real running, generate this file with a seeded RNG
|
|
19
|
+
# so the position is randomized rather than a fixed rotation, and record the
|
|
20
|
+
# seed -- a fixed rotation can alias with periodic systematics (diurnal
|
|
21
|
+
# temperature, cryocooler, fridge fills).
|
|
22
|
+
#
|
|
23
|
+
# Slot length (5m here) must be short compared to the drift timescale. The
|
|
24
|
+
# two bracketing dIdVs measure that drift directly: if the opening and
|
|
25
|
+
# closing dIdV disagree beyond tolerance, shorten the slots.
|
|
26
|
+
|
|
27
|
+
run:
|
|
28
|
+
file_format: zarr
|
|
29
|
+
save_data: true
|
|
30
|
+
channels: [TES_A, TES_B]
|
|
31
|
+
output_dir: /data/pytesdaqx
|
|
32
|
+
acquisition_label: blinded_physics
|
|
33
|
+
stop_policy: finish_current_step
|
|
34
|
+
# One cycle = 1 IV + 2 dIdV + 4 x 5m data = ~20m of physics data.
|
|
35
|
+
# 72 repeats ~= 24h of running.
|
|
36
|
+
n_sequence_repeats: 72
|
|
37
|
+
|
|
38
|
+
sequence:
|
|
39
|
+
# ---- opening calibration block (IV and dIdV grouped together) ----
|
|
40
|
+
- measurement: iv
|
|
41
|
+
label: iv
|
|
42
|
+
duration: 2m
|
|
43
|
+
|
|
44
|
+
- measurement: didv
|
|
45
|
+
label: didv_open
|
|
46
|
+
duration: 1m
|
|
47
|
+
|
|
48
|
+
# ---- settling window, discarded, applied equally to both samples ----
|
|
49
|
+
# The dIdV square wave injects heat. Taking this as its own labeled step
|
|
50
|
+
# means the transient lands in a stream both samples see identically,
|
|
51
|
+
# instead of being absorbed by whichever slot happens to run first.
|
|
52
|
+
- measurement: background
|
|
53
|
+
label: settle
|
|
54
|
+
duration: 30s
|
|
55
|
+
restricted: 0
|
|
56
|
+
|
|
57
|
+
# ---- data block: 4 slots, 3 restricted + 1 open ----
|
|
58
|
+
# This cycle places the open slot 2nd. Rotate/randomize per cycle.
|
|
59
|
+
- measurement: background
|
|
60
|
+
label: physics
|
|
61
|
+
duration: 5m
|
|
62
|
+
restricted: 1
|
|
63
|
+
|
|
64
|
+
- measurement: background
|
|
65
|
+
label: physics
|
|
66
|
+
duration: 5m
|
|
67
|
+
restricted: 0
|
|
68
|
+
|
|
69
|
+
- measurement: background
|
|
70
|
+
label: physics
|
|
71
|
+
duration: 5m
|
|
72
|
+
restricted: 1
|
|
73
|
+
|
|
74
|
+
- measurement: background
|
|
75
|
+
label: physics
|
|
76
|
+
duration: 5m
|
|
77
|
+
restricted: 1
|
|
78
|
+
|
|
79
|
+
# ---- closing calibration: brackets the block for interpolation ----
|
|
80
|
+
- measurement: didv
|
|
81
|
+
label: didv_close
|
|
82
|
+
duration: 1m
|
|
83
|
+
|
|
84
|
+
measurements:
|
|
85
|
+
iv:
|
|
86
|
+
type: iv
|
|
87
|
+
adc:
|
|
88
|
+
mode: continuous
|
|
89
|
+
sample_rate: 1250000
|
|
90
|
+
read_block_duration: 2s
|
|
91
|
+
adc_input_range_v: [-10, 10]
|
|
92
|
+
read_mode: unscaled_int16
|
|
93
|
+
|
|
94
|
+
didv:
|
|
95
|
+
type: didv
|
|
96
|
+
waveform:
|
|
97
|
+
shape: square
|
|
98
|
+
frequency_hz: 50
|
|
99
|
+
amplitude_vpp: 0.01
|
|
100
|
+
adc:
|
|
101
|
+
mode: continuous
|
|
102
|
+
sample_rate: 1250000
|
|
103
|
+
trace_duration: 2s
|
|
104
|
+
adc_input_range_v: [-10, 10]
|
|
105
|
+
read_mode: unscaled_int16
|
|
106
|
+
|
|
107
|
+
background:
|
|
108
|
+
type: background
|
|
109
|
+
adc:
|
|
110
|
+
mode: continuous
|
|
111
|
+
sample_rate: 1250000
|
|
112
|
+
read_block_duration: 1s
|
|
113
|
+
adc_input_range_v: [-10, 10]
|
|
114
|
+
read_mode: unscaled_int16
|
|
115
|
+
recorder:
|
|
116
|
+
chunk_duration: 1s
|
|
117
|
+
shard_duration: 10s
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""Generate a blinded physics acquisition YAML with a randomized open slot.
|
|
2
|
+
|
|
3
|
+
Each cycle is:
|
|
4
|
+
|
|
5
|
+
IV -> dIdV -> settle -> [4 data slots: 3 restricted + 1 open] -> dIdV
|
|
6
|
+
|
|
7
|
+
The open slot's position inside the 4-slot unit is drawn with *stratified*
|
|
8
|
+
randomization (permuted blocks): every group of 4 consecutive cycles uses
|
|
9
|
+
each of the 4 positions exactly once, in random order. That gives exactly
|
|
10
|
+
25% open data AND an open/restricted "time since dIdV" distribution that is
|
|
11
|
+
balanced by construction, not just on average -- while staying random, so
|
|
12
|
+
it cannot alias with a periodic systematic the way a fixed rotation can.
|
|
13
|
+
|
|
14
|
+
The seed is recorded in the generated file so the layout is reproducible
|
|
15
|
+
and auditable after unblinding.
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
python generate_blinded_sequence.py --cycles 72 --seed 20260918 \
|
|
19
|
+
--output acquisition_blinded_generated.yaml
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import argparse
|
|
25
|
+
import random
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
SLOTS_PER_CYCLE = 4 # 3 restricted + 1 open -> 25% open
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def open_slot_positions(n_cycles: int, seed: int, slots: int = SLOTS_PER_CYCLE) -> list[int]:
|
|
32
|
+
"""Open-slot position per cycle, stratified over every block of `slots`."""
|
|
33
|
+
rng = random.Random(seed)
|
|
34
|
+
positions: list[int] = []
|
|
35
|
+
while len(positions) < n_cycles:
|
|
36
|
+
block = list(range(slots))
|
|
37
|
+
rng.shuffle(block)
|
|
38
|
+
positions.extend(block)
|
|
39
|
+
return positions[:n_cycles]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def render_cycle(cycle: int, open_slot: int, slot_duration: str) -> str:
|
|
43
|
+
lines = [
|
|
44
|
+
f" # ---- cycle {cycle}: open slot in position {open_slot + 1} of {SLOTS_PER_CYCLE} ----",
|
|
45
|
+
" - measurement: iv",
|
|
46
|
+
" label: iv",
|
|
47
|
+
" duration: 2m",
|
|
48
|
+
"",
|
|
49
|
+
" - measurement: didv",
|
|
50
|
+
" label: didv_open",
|
|
51
|
+
" duration: 1m",
|
|
52
|
+
"",
|
|
53
|
+
" - measurement: background",
|
|
54
|
+
" label: settle",
|
|
55
|
+
" duration: 30s",
|
|
56
|
+
" restricted: 0",
|
|
57
|
+
"",
|
|
58
|
+
]
|
|
59
|
+
for slot in range(SLOTS_PER_CYCLE):
|
|
60
|
+
restricted = 0 if slot == open_slot else 1
|
|
61
|
+
lines += [
|
|
62
|
+
" - measurement: background",
|
|
63
|
+
" label: physics",
|
|
64
|
+
f" duration: {slot_duration}",
|
|
65
|
+
f" restricted: {restricted}",
|
|
66
|
+
"",
|
|
67
|
+
]
|
|
68
|
+
lines += [
|
|
69
|
+
" - measurement: didv",
|
|
70
|
+
" label: didv_close",
|
|
71
|
+
" duration: 1m",
|
|
72
|
+
"",
|
|
73
|
+
]
|
|
74
|
+
return "\n".join(lines)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
HEADER = """# GENERATED FILE -- do not edit by hand.
|
|
78
|
+
# Regenerate with:
|
|
79
|
+
# python generate_blinded_sequence.py --cycles {cycles} --seed {seed}
|
|
80
|
+
#
|
|
81
|
+
# Blinded physics running, {open_pct}% open / {restricted_pct}% restricted.
|
|
82
|
+
# Open-slot positions (1-based, one per cycle), stratified random with the
|
|
83
|
+
# seed below:
|
|
84
|
+
# {positions}
|
|
85
|
+
#
|
|
86
|
+
# blinding_seed: {seed}
|
|
87
|
+
|
|
88
|
+
run:
|
|
89
|
+
file_format: zarr
|
|
90
|
+
save_data: true
|
|
91
|
+
channels: [TES_A, TES_B]
|
|
92
|
+
output_dir: /data/pytesdaqx
|
|
93
|
+
acquisition_label: blinded_physics
|
|
94
|
+
stop_policy: finish_current_step
|
|
95
|
+
# Cycles are unrolled below, so the sequence runs once.
|
|
96
|
+
n_sequence_repeats: 1
|
|
97
|
+
|
|
98
|
+
sequence:
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
FOOTER = """measurements:
|
|
102
|
+
iv:
|
|
103
|
+
type: iv
|
|
104
|
+
adc:
|
|
105
|
+
mode: continuous
|
|
106
|
+
sample_rate: 1250000
|
|
107
|
+
read_block_duration: 2s
|
|
108
|
+
adc_input_range_v: [-10, 10]
|
|
109
|
+
read_mode: unscaled_int16
|
|
110
|
+
|
|
111
|
+
didv:
|
|
112
|
+
type: didv
|
|
113
|
+
waveform:
|
|
114
|
+
shape: square
|
|
115
|
+
frequency_hz: 50
|
|
116
|
+
amplitude_vpp: 0.01
|
|
117
|
+
adc:
|
|
118
|
+
mode: continuous
|
|
119
|
+
sample_rate: 1250000
|
|
120
|
+
trace_duration: 2s
|
|
121
|
+
adc_input_range_v: [-10, 10]
|
|
122
|
+
read_mode: unscaled_int16
|
|
123
|
+
|
|
124
|
+
background:
|
|
125
|
+
type: background
|
|
126
|
+
adc:
|
|
127
|
+
mode: continuous
|
|
128
|
+
sample_rate: 1250000
|
|
129
|
+
read_block_duration: 1s
|
|
130
|
+
adc_input_range_v: [-10, 10]
|
|
131
|
+
read_mode: unscaled_int16
|
|
132
|
+
recorder:
|
|
133
|
+
chunk_duration: 1s
|
|
134
|
+
shard_duration: 10s
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def main() -> None:
|
|
139
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
140
|
+
parser.add_argument("--cycles", type=int, default=72)
|
|
141
|
+
parser.add_argument("--seed", type=int, required=True)
|
|
142
|
+
parser.add_argument("--slot-duration", default="5m")
|
|
143
|
+
parser.add_argument("--output", type=Path, default=Path("acquisition_blinded_generated.yaml"))
|
|
144
|
+
args = parser.parse_args()
|
|
145
|
+
|
|
146
|
+
positions = open_slot_positions(args.cycles, args.seed)
|
|
147
|
+
body = "\n".join(
|
|
148
|
+
render_cycle(cycle, slot, args.slot_duration) for cycle, slot in enumerate(positions)
|
|
149
|
+
)
|
|
150
|
+
header = HEADER.format(
|
|
151
|
+
cycles=args.cycles,
|
|
152
|
+
seed=args.seed,
|
|
153
|
+
open_pct=int(100 / SLOTS_PER_CYCLE),
|
|
154
|
+
restricted_pct=int(100 - 100 / SLOTS_PER_CYCLE),
|
|
155
|
+
positions=", ".join(str(p + 1) for p in positions),
|
|
156
|
+
)
|
|
157
|
+
args.output.write_text(header + body + FOOTER, encoding="utf-8")
|
|
158
|
+
|
|
159
|
+
counts = {slot + 1: positions.count(slot) for slot in range(SLOTS_PER_CYCLE)}
|
|
160
|
+
print(f"wrote {args.output}: {args.cycles} cycles, seed {args.seed}")
|
|
161
|
+
print(f"open-slot position counts: {counts}")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
if __name__ == "__main__":
|
|
165
|
+
main()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Fake hardware inventory for vibration studies: one ADC reading three
|
|
2
|
+
# accelerometers and one signal generator driving a vibration transducer.
|
|
3
|
+
|
|
4
|
+
instruments:
|
|
5
|
+
adc1:
|
|
6
|
+
driver: fake
|
|
7
|
+
type: pci_adc
|
|
8
|
+
device_alias: Dev1
|
|
9
|
+
model: fake_adc
|
|
10
|
+
channels: [0, 1, 2, 3, 4, 5, 6, 7]
|
|
11
|
+
|
|
12
|
+
agilent1:
|
|
13
|
+
driver: fake
|
|
14
|
+
type: signal_generator
|
|
15
|
+
channels: [1, 2]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
facility: 2
|
|
2
|
+
fridge_run: 123
|
|
3
|
+
fridge_run_start: "2026-04-12 19:00:00"
|
|
4
|
+
|
|
5
|
+
# Example run setup for vibration studies: three accelerometer axes on adc1
|
|
6
|
+
# channels 0-2 and one vibration transducer.
|
|
7
|
+
#
|
|
8
|
+
# Acceleration in g = volts / (amplifier_gain * sensitivity_v_per_g).
|
|
9
|
+
# amplifier_gain is the signal-conditioner gain (typically 1, 10 or 100);
|
|
10
|
+
# sensitivity_v_per_g is the accelerometer sensitivity (default 1 V/g).
|
|
11
|
+
|
|
12
|
+
channels:
|
|
13
|
+
AccelerometerX:
|
|
14
|
+
adc:
|
|
15
|
+
device: adc1
|
|
16
|
+
channel: 0
|
|
17
|
+
readout_circuit:
|
|
18
|
+
sensor_type: accelerometer
|
|
19
|
+
amplifier_gain: 100
|
|
20
|
+
sensitivity_v_per_g: 1.0
|
|
21
|
+
|
|
22
|
+
AccelerometerY:
|
|
23
|
+
adc:
|
|
24
|
+
device: adc1
|
|
25
|
+
channel: 1
|
|
26
|
+
readout_circuit:
|
|
27
|
+
sensor_type: accelerometer
|
|
28
|
+
amplifier_gain: 100
|
|
29
|
+
sensitivity_v_per_g: 1.0
|
|
30
|
+
|
|
31
|
+
AccelerometerZ:
|
|
32
|
+
adc:
|
|
33
|
+
device: adc1
|
|
34
|
+
channel: 2
|
|
35
|
+
readout_circuit:
|
|
36
|
+
sensor_type: accelerometer
|
|
37
|
+
amplifier_gain: 100
|
|
38
|
+
sensitivity_v_per_g: 1.0
|
|
39
|
+
|
|
40
|
+
# Topology only; drive frequency/amplitude are scan.parameters targets
|
|
41
|
+
# (see examples/configuration/scans/acquisition_transducer_vibration_scan.yaml).
|
|
42
|
+
transducers:
|
|
43
|
+
transducer1:
|
|
44
|
+
device: agilent1
|
|
45
|
+
channel: 2
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pytesdaqx"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.4"
|
|
8
8
|
description = "TES detector acquisition, instrument control, and raw-data recording"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11,<3.13"
|
|
@@ -21,7 +21,7 @@ classifiers = [
|
|
|
21
21
|
"Topic :: Scientific/Engineering :: Physics",
|
|
22
22
|
]
|
|
23
23
|
dependencies = [
|
|
24
|
-
"numpy>=1.26
|
|
24
|
+
"numpy>=1.26",
|
|
25
25
|
"PyYAML",
|
|
26
26
|
"h5py>=3.10",
|
|
27
27
|
"matplotlib",
|
|
@@ -82,6 +82,7 @@ pytesdaqx-validate-config = "pytesdaqx.cli.validate_config:main"
|
|
|
82
82
|
pytesdaqx-display-metadata = "pytesdaqx.cli.display_metadata:main"
|
|
83
83
|
pytesdaqx-view-traces = "pytesdaqx.cli.view_traces:main"
|
|
84
84
|
pytesdaqx-build-manifest = "pytesdaqx.cli.build_manifest:main"
|
|
85
|
+
pytesdaqx-repair-readout = "pytesdaqx.cli.repair_readout:main"
|
|
85
86
|
[tool.pytest.ini_options]
|
|
86
87
|
testpaths = ["tests"]
|
|
87
88
|
python_files = ["test_*.py"]
|