oscura 0.0.1__py3-none-any.whl → 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.
- oscura/__init__.py +813 -8
- oscura/__main__.py +392 -0
- oscura/analyzers/__init__.py +37 -0
- oscura/analyzers/digital/__init__.py +177 -0
- oscura/analyzers/digital/bus.py +691 -0
- oscura/analyzers/digital/clock.py +805 -0
- oscura/analyzers/digital/correlation.py +720 -0
- oscura/analyzers/digital/edges.py +632 -0
- oscura/analyzers/digital/extraction.py +413 -0
- oscura/analyzers/digital/quality.py +878 -0
- oscura/analyzers/digital/signal_quality.py +877 -0
- oscura/analyzers/digital/thresholds.py +708 -0
- oscura/analyzers/digital/timing.py +1104 -0
- oscura/analyzers/eye/__init__.py +46 -0
- oscura/analyzers/eye/diagram.py +434 -0
- oscura/analyzers/eye/metrics.py +555 -0
- oscura/analyzers/jitter/__init__.py +83 -0
- oscura/analyzers/jitter/ber.py +333 -0
- oscura/analyzers/jitter/decomposition.py +759 -0
- oscura/analyzers/jitter/measurements.py +413 -0
- oscura/analyzers/jitter/spectrum.py +220 -0
- oscura/analyzers/measurements.py +40 -0
- oscura/analyzers/packet/__init__.py +171 -0
- oscura/analyzers/packet/daq.py +1077 -0
- oscura/analyzers/packet/metrics.py +437 -0
- oscura/analyzers/packet/parser.py +327 -0
- oscura/analyzers/packet/payload.py +2156 -0
- oscura/analyzers/packet/payload_analysis.py +1312 -0
- oscura/analyzers/packet/payload_extraction.py +236 -0
- oscura/analyzers/packet/payload_patterns.py +670 -0
- oscura/analyzers/packet/stream.py +359 -0
- oscura/analyzers/patterns/__init__.py +266 -0
- oscura/analyzers/patterns/clustering.py +1036 -0
- oscura/analyzers/patterns/discovery.py +539 -0
- oscura/analyzers/patterns/learning.py +797 -0
- oscura/analyzers/patterns/matching.py +1091 -0
- oscura/analyzers/patterns/periodic.py +650 -0
- oscura/analyzers/patterns/sequences.py +767 -0
- oscura/analyzers/power/__init__.py +116 -0
- oscura/analyzers/power/ac_power.py +391 -0
- oscura/analyzers/power/basic.py +383 -0
- oscura/analyzers/power/conduction.py +314 -0
- oscura/analyzers/power/efficiency.py +297 -0
- oscura/analyzers/power/ripple.py +356 -0
- oscura/analyzers/power/soa.py +372 -0
- oscura/analyzers/power/switching.py +479 -0
- oscura/analyzers/protocol/__init__.py +150 -0
- oscura/analyzers/protocols/__init__.py +150 -0
- oscura/analyzers/protocols/base.py +500 -0
- oscura/analyzers/protocols/can.py +620 -0
- oscura/analyzers/protocols/can_fd.py +448 -0
- oscura/analyzers/protocols/flexray.py +405 -0
- oscura/analyzers/protocols/hdlc.py +399 -0
- oscura/analyzers/protocols/i2c.py +368 -0
- oscura/analyzers/protocols/i2s.py +296 -0
- oscura/analyzers/protocols/jtag.py +393 -0
- oscura/analyzers/protocols/lin.py +445 -0
- oscura/analyzers/protocols/manchester.py +333 -0
- oscura/analyzers/protocols/onewire.py +501 -0
- oscura/analyzers/protocols/spi.py +334 -0
- oscura/analyzers/protocols/swd.py +325 -0
- oscura/analyzers/protocols/uart.py +393 -0
- oscura/analyzers/protocols/usb.py +495 -0
- oscura/analyzers/signal_integrity/__init__.py +63 -0
- oscura/analyzers/signal_integrity/embedding.py +294 -0
- oscura/analyzers/signal_integrity/equalization.py +370 -0
- oscura/analyzers/signal_integrity/sparams.py +484 -0
- oscura/analyzers/spectral/__init__.py +53 -0
- oscura/analyzers/spectral/chunked.py +273 -0
- oscura/analyzers/spectral/chunked_fft.py +571 -0
- oscura/analyzers/spectral/chunked_wavelet.py +391 -0
- oscura/analyzers/spectral/fft.py +92 -0
- oscura/analyzers/statistical/__init__.py +250 -0
- oscura/analyzers/statistical/checksum.py +923 -0
- oscura/analyzers/statistical/chunked_corr.py +228 -0
- oscura/analyzers/statistical/classification.py +778 -0
- oscura/analyzers/statistical/entropy.py +1113 -0
- oscura/analyzers/statistical/ngrams.py +614 -0
- oscura/analyzers/statistics/__init__.py +119 -0
- oscura/analyzers/statistics/advanced.py +885 -0
- oscura/analyzers/statistics/basic.py +263 -0
- oscura/analyzers/statistics/correlation.py +630 -0
- oscura/analyzers/statistics/distribution.py +298 -0
- oscura/analyzers/statistics/outliers.py +463 -0
- oscura/analyzers/statistics/streaming.py +93 -0
- oscura/analyzers/statistics/trend.py +520 -0
- oscura/analyzers/validation.py +598 -0
- oscura/analyzers/waveform/__init__.py +36 -0
- oscura/analyzers/waveform/measurements.py +943 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
- oscura/analyzers/waveform/spectral.py +1689 -0
- oscura/analyzers/waveform/wavelets.py +298 -0
- oscura/api/__init__.py +62 -0
- oscura/api/dsl.py +538 -0
- oscura/api/fluent.py +571 -0
- oscura/api/operators.py +498 -0
- oscura/api/optimization.py +392 -0
- oscura/api/profiling.py +396 -0
- oscura/automotive/__init__.py +73 -0
- oscura/automotive/can/__init__.py +52 -0
- oscura/automotive/can/analysis.py +356 -0
- oscura/automotive/can/checksum.py +250 -0
- oscura/automotive/can/correlation.py +212 -0
- oscura/automotive/can/discovery.py +355 -0
- oscura/automotive/can/message_wrapper.py +375 -0
- oscura/automotive/can/models.py +385 -0
- oscura/automotive/can/patterns.py +381 -0
- oscura/automotive/can/session.py +452 -0
- oscura/automotive/can/state_machine.py +300 -0
- oscura/automotive/can/stimulus_response.py +461 -0
- oscura/automotive/dbc/__init__.py +15 -0
- oscura/automotive/dbc/generator.py +156 -0
- oscura/automotive/dbc/parser.py +146 -0
- oscura/automotive/dtc/__init__.py +30 -0
- oscura/automotive/dtc/database.py +3036 -0
- oscura/automotive/j1939/__init__.py +14 -0
- oscura/automotive/j1939/decoder.py +745 -0
- oscura/automotive/loaders/__init__.py +35 -0
- oscura/automotive/loaders/asc.py +98 -0
- oscura/automotive/loaders/blf.py +77 -0
- oscura/automotive/loaders/csv_can.py +136 -0
- oscura/automotive/loaders/dispatcher.py +136 -0
- oscura/automotive/loaders/mdf.py +331 -0
- oscura/automotive/loaders/pcap.py +132 -0
- oscura/automotive/obd/__init__.py +14 -0
- oscura/automotive/obd/decoder.py +707 -0
- oscura/automotive/uds/__init__.py +48 -0
- oscura/automotive/uds/decoder.py +265 -0
- oscura/automotive/uds/models.py +64 -0
- oscura/automotive/visualization.py +369 -0
- oscura/batch/__init__.py +55 -0
- oscura/batch/advanced.py +627 -0
- oscura/batch/aggregate.py +300 -0
- oscura/batch/analyze.py +139 -0
- oscura/batch/logging.py +487 -0
- oscura/batch/metrics.py +556 -0
- oscura/builders/__init__.py +41 -0
- oscura/builders/signal_builder.py +1131 -0
- oscura/cli/__init__.py +14 -0
- oscura/cli/batch.py +339 -0
- oscura/cli/characterize.py +273 -0
- oscura/cli/compare.py +775 -0
- oscura/cli/decode.py +551 -0
- oscura/cli/main.py +247 -0
- oscura/cli/shell.py +350 -0
- oscura/comparison/__init__.py +66 -0
- oscura/comparison/compare.py +397 -0
- oscura/comparison/golden.py +487 -0
- oscura/comparison/limits.py +391 -0
- oscura/comparison/mask.py +434 -0
- oscura/comparison/trace_diff.py +30 -0
- oscura/comparison/visualization.py +481 -0
- oscura/compliance/__init__.py +70 -0
- oscura/compliance/advanced.py +756 -0
- oscura/compliance/masks.py +363 -0
- oscura/compliance/reporting.py +483 -0
- oscura/compliance/testing.py +298 -0
- oscura/component/__init__.py +38 -0
- oscura/component/impedance.py +365 -0
- oscura/component/reactive.py +598 -0
- oscura/component/transmission_line.py +312 -0
- oscura/config/__init__.py +191 -0
- oscura/config/defaults.py +254 -0
- oscura/config/loader.py +348 -0
- oscura/config/memory.py +271 -0
- oscura/config/migration.py +458 -0
- oscura/config/pipeline.py +1077 -0
- oscura/config/preferences.py +530 -0
- oscura/config/protocol.py +875 -0
- oscura/config/schema.py +713 -0
- oscura/config/settings.py +420 -0
- oscura/config/thresholds.py +599 -0
- oscura/convenience.py +457 -0
- oscura/core/__init__.py +299 -0
- oscura/core/audit.py +457 -0
- oscura/core/backend_selector.py +405 -0
- oscura/core/cache.py +590 -0
- oscura/core/cancellation.py +439 -0
- oscura/core/confidence.py +225 -0
- oscura/core/config.py +506 -0
- oscura/core/correlation.py +216 -0
- oscura/core/cross_domain.py +422 -0
- oscura/core/debug.py +301 -0
- oscura/core/edge_cases.py +541 -0
- oscura/core/exceptions.py +535 -0
- oscura/core/gpu_backend.py +523 -0
- oscura/core/lazy.py +832 -0
- oscura/core/log_query.py +540 -0
- oscura/core/logging.py +931 -0
- oscura/core/logging_advanced.py +952 -0
- oscura/core/memoize.py +171 -0
- oscura/core/memory_check.py +274 -0
- oscura/core/memory_guard.py +290 -0
- oscura/core/memory_limits.py +336 -0
- oscura/core/memory_monitor.py +453 -0
- oscura/core/memory_progress.py +465 -0
- oscura/core/memory_warnings.py +315 -0
- oscura/core/numba_backend.py +362 -0
- oscura/core/performance.py +352 -0
- oscura/core/progress.py +524 -0
- oscura/core/provenance.py +358 -0
- oscura/core/results.py +331 -0
- oscura/core/types.py +504 -0
- oscura/core/uncertainty.py +383 -0
- oscura/discovery/__init__.py +52 -0
- oscura/discovery/anomaly_detector.py +672 -0
- oscura/discovery/auto_decoder.py +415 -0
- oscura/discovery/comparison.py +497 -0
- oscura/discovery/quality_validator.py +528 -0
- oscura/discovery/signal_detector.py +769 -0
- oscura/dsl/__init__.py +73 -0
- oscura/dsl/commands.py +246 -0
- oscura/dsl/interpreter.py +455 -0
- oscura/dsl/parser.py +689 -0
- oscura/dsl/repl.py +172 -0
- oscura/exceptions.py +59 -0
- oscura/exploratory/__init__.py +111 -0
- oscura/exploratory/error_recovery.py +642 -0
- oscura/exploratory/fuzzy.py +513 -0
- oscura/exploratory/fuzzy_advanced.py +786 -0
- oscura/exploratory/legacy.py +831 -0
- oscura/exploratory/parse.py +358 -0
- oscura/exploratory/recovery.py +275 -0
- oscura/exploratory/sync.py +382 -0
- oscura/exploratory/unknown.py +707 -0
- oscura/export/__init__.py +25 -0
- oscura/export/wireshark/README.md +265 -0
- oscura/export/wireshark/__init__.py +47 -0
- oscura/export/wireshark/generator.py +312 -0
- oscura/export/wireshark/lua_builder.py +159 -0
- oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
- oscura/export/wireshark/type_mapping.py +165 -0
- oscura/export/wireshark/validator.py +105 -0
- oscura/exporters/__init__.py +94 -0
- oscura/exporters/csv.py +303 -0
- oscura/exporters/exporters.py +44 -0
- oscura/exporters/hdf5.py +219 -0
- oscura/exporters/html_export.py +701 -0
- oscura/exporters/json_export.py +291 -0
- oscura/exporters/markdown_export.py +367 -0
- oscura/exporters/matlab_export.py +354 -0
- oscura/exporters/npz_export.py +219 -0
- oscura/exporters/spice_export.py +210 -0
- oscura/extensibility/__init__.py +131 -0
- oscura/extensibility/docs.py +752 -0
- oscura/extensibility/extensions.py +1125 -0
- oscura/extensibility/logging.py +259 -0
- oscura/extensibility/measurements.py +485 -0
- oscura/extensibility/plugins.py +414 -0
- oscura/extensibility/registry.py +346 -0
- oscura/extensibility/templates.py +913 -0
- oscura/extensibility/validation.py +651 -0
- oscura/filtering/__init__.py +89 -0
- oscura/filtering/base.py +563 -0
- oscura/filtering/convenience.py +564 -0
- oscura/filtering/design.py +725 -0
- oscura/filtering/filters.py +32 -0
- oscura/filtering/introspection.py +605 -0
- oscura/guidance/__init__.py +24 -0
- oscura/guidance/recommender.py +429 -0
- oscura/guidance/wizard.py +518 -0
- oscura/inference/__init__.py +251 -0
- oscura/inference/active_learning/README.md +153 -0
- oscura/inference/active_learning/__init__.py +38 -0
- oscura/inference/active_learning/lstar.py +257 -0
- oscura/inference/active_learning/observation_table.py +230 -0
- oscura/inference/active_learning/oracle.py +78 -0
- oscura/inference/active_learning/teachers/__init__.py +15 -0
- oscura/inference/active_learning/teachers/simulator.py +192 -0
- oscura/inference/adaptive_tuning.py +453 -0
- oscura/inference/alignment.py +653 -0
- oscura/inference/bayesian.py +943 -0
- oscura/inference/binary.py +1016 -0
- oscura/inference/crc_reverse.py +711 -0
- oscura/inference/logic.py +288 -0
- oscura/inference/message_format.py +1305 -0
- oscura/inference/protocol.py +417 -0
- oscura/inference/protocol_dsl.py +1084 -0
- oscura/inference/protocol_library.py +1230 -0
- oscura/inference/sequences.py +809 -0
- oscura/inference/signal_intelligence.py +1509 -0
- oscura/inference/spectral.py +215 -0
- oscura/inference/state_machine.py +634 -0
- oscura/inference/stream.py +918 -0
- oscura/integrations/__init__.py +59 -0
- oscura/integrations/llm.py +1827 -0
- oscura/jupyter/__init__.py +32 -0
- oscura/jupyter/display.py +268 -0
- oscura/jupyter/magic.py +334 -0
- oscura/loaders/__init__.py +526 -0
- oscura/loaders/binary.py +69 -0
- oscura/loaders/configurable.py +1255 -0
- oscura/loaders/csv.py +26 -0
- oscura/loaders/csv_loader.py +473 -0
- oscura/loaders/hdf5.py +9 -0
- oscura/loaders/hdf5_loader.py +510 -0
- oscura/loaders/lazy.py +370 -0
- oscura/loaders/mmap_loader.py +583 -0
- oscura/loaders/numpy_loader.py +436 -0
- oscura/loaders/pcap.py +432 -0
- oscura/loaders/preprocessing.py +368 -0
- oscura/loaders/rigol.py +287 -0
- oscura/loaders/sigrok.py +321 -0
- oscura/loaders/tdms.py +367 -0
- oscura/loaders/tektronix.py +711 -0
- oscura/loaders/validation.py +584 -0
- oscura/loaders/vcd.py +464 -0
- oscura/loaders/wav.py +233 -0
- oscura/math/__init__.py +45 -0
- oscura/math/arithmetic.py +824 -0
- oscura/math/interpolation.py +413 -0
- oscura/onboarding/__init__.py +39 -0
- oscura/onboarding/help.py +498 -0
- oscura/onboarding/tutorials.py +405 -0
- oscura/onboarding/wizard.py +466 -0
- oscura/optimization/__init__.py +19 -0
- oscura/optimization/parallel.py +440 -0
- oscura/optimization/search.py +532 -0
- oscura/pipeline/__init__.py +43 -0
- oscura/pipeline/base.py +338 -0
- oscura/pipeline/composition.py +242 -0
- oscura/pipeline/parallel.py +448 -0
- oscura/pipeline/pipeline.py +375 -0
- oscura/pipeline/reverse_engineering.py +1119 -0
- oscura/plugins/__init__.py +122 -0
- oscura/plugins/base.py +272 -0
- oscura/plugins/cli.py +497 -0
- oscura/plugins/discovery.py +411 -0
- oscura/plugins/isolation.py +418 -0
- oscura/plugins/lifecycle.py +959 -0
- oscura/plugins/manager.py +493 -0
- oscura/plugins/registry.py +421 -0
- oscura/plugins/versioning.py +372 -0
- oscura/py.typed +0 -0
- oscura/quality/__init__.py +65 -0
- oscura/quality/ensemble.py +740 -0
- oscura/quality/explainer.py +338 -0
- oscura/quality/scoring.py +616 -0
- oscura/quality/warnings.py +456 -0
- oscura/reporting/__init__.py +248 -0
- oscura/reporting/advanced.py +1234 -0
- oscura/reporting/analyze.py +448 -0
- oscura/reporting/argument_preparer.py +596 -0
- oscura/reporting/auto_report.py +507 -0
- oscura/reporting/batch.py +615 -0
- oscura/reporting/chart_selection.py +223 -0
- oscura/reporting/comparison.py +330 -0
- oscura/reporting/config.py +615 -0
- oscura/reporting/content/__init__.py +39 -0
- oscura/reporting/content/executive.py +127 -0
- oscura/reporting/content/filtering.py +191 -0
- oscura/reporting/content/minimal.py +257 -0
- oscura/reporting/content/verbosity.py +162 -0
- oscura/reporting/core.py +508 -0
- oscura/reporting/core_formats/__init__.py +17 -0
- oscura/reporting/core_formats/multi_format.py +210 -0
- oscura/reporting/engine.py +836 -0
- oscura/reporting/export.py +366 -0
- oscura/reporting/formatting/__init__.py +129 -0
- oscura/reporting/formatting/emphasis.py +81 -0
- oscura/reporting/formatting/numbers.py +403 -0
- oscura/reporting/formatting/standards.py +55 -0
- oscura/reporting/formatting.py +466 -0
- oscura/reporting/html.py +578 -0
- oscura/reporting/index.py +590 -0
- oscura/reporting/multichannel.py +296 -0
- oscura/reporting/output.py +379 -0
- oscura/reporting/pdf.py +373 -0
- oscura/reporting/plots.py +731 -0
- oscura/reporting/pptx_export.py +360 -0
- oscura/reporting/renderers/__init__.py +11 -0
- oscura/reporting/renderers/pdf.py +94 -0
- oscura/reporting/sections.py +471 -0
- oscura/reporting/standards.py +680 -0
- oscura/reporting/summary_generator.py +368 -0
- oscura/reporting/tables.py +397 -0
- oscura/reporting/template_system.py +724 -0
- oscura/reporting/templates/__init__.py +15 -0
- oscura/reporting/templates/definition.py +205 -0
- oscura/reporting/templates/index.html +649 -0
- oscura/reporting/templates/index.md +173 -0
- oscura/schemas/__init__.py +158 -0
- oscura/schemas/bus_configuration.json +322 -0
- oscura/schemas/device_mapping.json +182 -0
- oscura/schemas/packet_format.json +418 -0
- oscura/schemas/protocol_definition.json +363 -0
- oscura/search/__init__.py +16 -0
- oscura/search/anomaly.py +292 -0
- oscura/search/context.py +149 -0
- oscura/search/pattern.py +160 -0
- oscura/session/__init__.py +34 -0
- oscura/session/annotations.py +289 -0
- oscura/session/history.py +313 -0
- oscura/session/session.py +445 -0
- oscura/streaming/__init__.py +43 -0
- oscura/streaming/chunked.py +611 -0
- oscura/streaming/progressive.py +393 -0
- oscura/streaming/realtime.py +622 -0
- oscura/testing/__init__.py +54 -0
- oscura/testing/synthetic.py +808 -0
- oscura/triggering/__init__.py +68 -0
- oscura/triggering/base.py +229 -0
- oscura/triggering/edge.py +353 -0
- oscura/triggering/pattern.py +344 -0
- oscura/triggering/pulse.py +581 -0
- oscura/triggering/window.py +453 -0
- oscura/ui/__init__.py +48 -0
- oscura/ui/formatters.py +526 -0
- oscura/ui/progressive_display.py +340 -0
- oscura/utils/__init__.py +99 -0
- oscura/utils/autodetect.py +338 -0
- oscura/utils/buffer.py +389 -0
- oscura/utils/lazy.py +407 -0
- oscura/utils/lazy_imports.py +147 -0
- oscura/utils/memory.py +836 -0
- oscura/utils/memory_advanced.py +1326 -0
- oscura/utils/memory_extensions.py +465 -0
- oscura/utils/progressive.py +352 -0
- oscura/utils/windowing.py +362 -0
- oscura/visualization/__init__.py +321 -0
- oscura/visualization/accessibility.py +526 -0
- oscura/visualization/annotations.py +374 -0
- oscura/visualization/axis_scaling.py +305 -0
- oscura/visualization/colors.py +453 -0
- oscura/visualization/digital.py +337 -0
- oscura/visualization/eye.py +420 -0
- oscura/visualization/histogram.py +281 -0
- oscura/visualization/interactive.py +858 -0
- oscura/visualization/jitter.py +702 -0
- oscura/visualization/keyboard.py +394 -0
- oscura/visualization/layout.py +365 -0
- oscura/visualization/optimization.py +1028 -0
- oscura/visualization/palettes.py +446 -0
- oscura/visualization/plot.py +92 -0
- oscura/visualization/power.py +290 -0
- oscura/visualization/power_extended.py +626 -0
- oscura/visualization/presets.py +467 -0
- oscura/visualization/protocols.py +932 -0
- oscura/visualization/render.py +207 -0
- oscura/visualization/rendering.py +444 -0
- oscura/visualization/reverse_engineering.py +791 -0
- oscura/visualization/signal_integrity.py +808 -0
- oscura/visualization/specialized.py +553 -0
- oscura/visualization/spectral.py +811 -0
- oscura/visualization/styles.py +381 -0
- oscura/visualization/thumbnails.py +311 -0
- oscura/visualization/time_axis.py +351 -0
- oscura/visualization/waveform.py +367 -0
- oscura/workflow/__init__.py +13 -0
- oscura/workflow/dag.py +377 -0
- oscura/workflows/__init__.py +58 -0
- oscura/workflows/compliance.py +280 -0
- oscura/workflows/digital.py +272 -0
- oscura/workflows/multi_trace.py +502 -0
- oscura/workflows/power.py +178 -0
- oscura/workflows/protocol.py +492 -0
- oscura/workflows/reverse_engineering.py +639 -0
- oscura/workflows/signal_integrity.py +227 -0
- oscura-0.1.0.dist-info/METADATA +300 -0
- oscura-0.1.0.dist-info/RECORD +463 -0
- oscura-0.1.0.dist-info/entry_points.txt +2 -0
- {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/licenses/LICENSE +1 -1
- oscura-0.0.1.dist-info/METADATA +0 -63
- oscura-0.0.1.dist-info/RECORD +0 -5
- {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/WHEEL +0 -0
oscura/core/types.py
ADDED
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
"""Core data types for TraceKit signal analysis framework.
|
|
2
|
+
|
|
3
|
+
This module implements the fundamental data structures for oscilloscope
|
|
4
|
+
and logic analyzer data analysis.
|
|
5
|
+
|
|
6
|
+
Requirements addressed:
|
|
7
|
+
- CORE-001: TraceMetadata Data Class
|
|
8
|
+
- CORE-002: WaveformTrace Data Class
|
|
9
|
+
- CORE-003: DigitalTrace Data Class
|
|
10
|
+
- CORE-004: ProtocolPacket Data Class
|
|
11
|
+
- CORE-005: CalibrationInfo Data Class (regulatory compliance)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from typing import TYPE_CHECKING, Any
|
|
18
|
+
|
|
19
|
+
import numpy as np
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from datetime import datetime
|
|
23
|
+
|
|
24
|
+
from numpy.typing import NDArray
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class CalibrationInfo:
|
|
29
|
+
"""Calibration and instrument provenance information.
|
|
30
|
+
|
|
31
|
+
Stores traceability metadata for measurements performed on oscilloscope
|
|
32
|
+
or logic analyzer data. Essential for regulatory compliance and quality
|
|
33
|
+
assurance in DOD/aerospace/medical applications.
|
|
34
|
+
|
|
35
|
+
Attributes:
|
|
36
|
+
instrument: Instrument make and model (e.g., "Tektronix DPO7254C").
|
|
37
|
+
serial_number: Instrument serial number for traceability (optional).
|
|
38
|
+
calibration_date: Date of last calibration (optional).
|
|
39
|
+
calibration_due_date: Date when next calibration is due (optional).
|
|
40
|
+
firmware_version: Instrument firmware version (optional).
|
|
41
|
+
calibration_lab: Calibration lab name or accreditation (optional).
|
|
42
|
+
calibration_cert_number: Calibration certificate number (optional).
|
|
43
|
+
probe_attenuation: Probe attenuation factor (e.g., 10.0 for 10x probe) (optional).
|
|
44
|
+
coupling: Input coupling ("DC", "AC", "GND") (optional).
|
|
45
|
+
bandwidth_limit: Bandwidth limit in Hz, None if disabled (optional).
|
|
46
|
+
vertical_resolution: ADC resolution in bits (optional).
|
|
47
|
+
|
|
48
|
+
Example:
|
|
49
|
+
>>> from datetime import datetime
|
|
50
|
+
>>> cal_info = CalibrationInfo(
|
|
51
|
+
... instrument="Tektronix DPO7254C",
|
|
52
|
+
... serial_number="C012345",
|
|
53
|
+
... calibration_date=datetime(2024, 12, 15),
|
|
54
|
+
... probe_attenuation=10.0,
|
|
55
|
+
... vertical_resolution=8
|
|
56
|
+
... )
|
|
57
|
+
>>> print(f"Instrument: {cal_info.instrument}")
|
|
58
|
+
Instrument: Tektronix DPO7254C
|
|
59
|
+
|
|
60
|
+
References:
|
|
61
|
+
ISO/IEC 17025: General Requirements for Testing/Calibration Laboratories
|
|
62
|
+
NIST Handbook 150: Laboratory Accreditation Program Requirements
|
|
63
|
+
21 CFR Part 11: Electronic Records (FDA)
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
instrument: str
|
|
67
|
+
serial_number: str | None = None
|
|
68
|
+
calibration_date: datetime | None = None
|
|
69
|
+
calibration_due_date: datetime | None = None
|
|
70
|
+
firmware_version: str | None = None
|
|
71
|
+
calibration_lab: str | None = None
|
|
72
|
+
calibration_cert_number: str | None = None
|
|
73
|
+
probe_attenuation: float | None = None
|
|
74
|
+
coupling: str | None = None
|
|
75
|
+
bandwidth_limit: float | None = None
|
|
76
|
+
vertical_resolution: int | None = None
|
|
77
|
+
|
|
78
|
+
def __post_init__(self) -> None:
|
|
79
|
+
"""Validate calibration info after initialization."""
|
|
80
|
+
if self.probe_attenuation is not None and self.probe_attenuation <= 0:
|
|
81
|
+
raise ValueError(f"probe_attenuation must be positive, got {self.probe_attenuation}")
|
|
82
|
+
if self.bandwidth_limit is not None and self.bandwidth_limit <= 0:
|
|
83
|
+
raise ValueError(f"bandwidth_limit must be positive, got {self.bandwidth_limit}")
|
|
84
|
+
if self.vertical_resolution is not None and self.vertical_resolution <= 0:
|
|
85
|
+
raise ValueError(
|
|
86
|
+
f"vertical_resolution must be positive, got {self.vertical_resolution}"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def is_calibration_current(self) -> bool | None:
|
|
91
|
+
"""Check if calibration is current.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
True if calibration is current, False if expired, None if dates not set.
|
|
95
|
+
"""
|
|
96
|
+
if self.calibration_date is None or self.calibration_due_date is None:
|
|
97
|
+
return None
|
|
98
|
+
from datetime import datetime
|
|
99
|
+
|
|
100
|
+
return datetime.now() < self.calibration_due_date
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def traceability_summary(self) -> str:
|
|
104
|
+
"""Generate a traceability summary string.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Human-readable summary of calibration traceability.
|
|
108
|
+
"""
|
|
109
|
+
parts = [f"Instrument: {self.instrument}"]
|
|
110
|
+
if self.serial_number:
|
|
111
|
+
parts.append(f"S/N: {self.serial_number}")
|
|
112
|
+
if self.calibration_date:
|
|
113
|
+
parts.append(f"Cal Date: {self.calibration_date.strftime('%Y-%m-%d')}")
|
|
114
|
+
if self.calibration_due_date:
|
|
115
|
+
parts.append(f"Due: {self.calibration_due_date.strftime('%Y-%m-%d')}")
|
|
116
|
+
if self.calibration_cert_number:
|
|
117
|
+
parts.append(f"Cert: {self.calibration_cert_number}")
|
|
118
|
+
return ", ".join(parts)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@dataclass
|
|
122
|
+
class TraceMetadata:
|
|
123
|
+
"""Metadata describing a captured trace.
|
|
124
|
+
|
|
125
|
+
Contains sample rate, scaling information, acquisition details,
|
|
126
|
+
and provenance information for a captured waveform or digital trace.
|
|
127
|
+
|
|
128
|
+
Attributes:
|
|
129
|
+
sample_rate: Sample rate in Hz (required).
|
|
130
|
+
vertical_scale: Vertical scale in volts/division (optional).
|
|
131
|
+
vertical_offset: Vertical offset in volts (optional).
|
|
132
|
+
acquisition_time: Time of acquisition (optional).
|
|
133
|
+
trigger_info: Trigger configuration dictionary (optional).
|
|
134
|
+
source_file: Path to source file (optional).
|
|
135
|
+
channel_name: Name of the channel (optional).
|
|
136
|
+
calibration_info: Calibration and instrument traceability information (optional).
|
|
137
|
+
|
|
138
|
+
Example:
|
|
139
|
+
>>> metadata = TraceMetadata(sample_rate=1e9) # 1 GSa/s
|
|
140
|
+
>>> print(f"Time base: {metadata.time_base} s/sample")
|
|
141
|
+
Time base: 1e-09 s/sample
|
|
142
|
+
|
|
143
|
+
Example with calibration info:
|
|
144
|
+
>>> from datetime import datetime
|
|
145
|
+
>>> cal = CalibrationInfo(
|
|
146
|
+
... instrument="Tektronix DPO7254C",
|
|
147
|
+
... calibration_date=datetime(2024, 12, 15)
|
|
148
|
+
... )
|
|
149
|
+
>>> metadata = TraceMetadata(sample_rate=1e9, calibration_info=cal)
|
|
150
|
+
>>> print(metadata.calibration_info.traceability_summary)
|
|
151
|
+
Instrument: Tektronix DPO7254C, Cal Date: 2024-12-15
|
|
152
|
+
|
|
153
|
+
References:
|
|
154
|
+
IEEE 181-2011: Standard for Transitional Waveform Definitions
|
|
155
|
+
ISO/IEC 17025: General Requirements for Testing/Calibration Laboratories
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
sample_rate: float
|
|
159
|
+
vertical_scale: float | None = None
|
|
160
|
+
vertical_offset: float | None = None
|
|
161
|
+
acquisition_time: datetime | None = None
|
|
162
|
+
trigger_info: dict[str, Any] | None = None
|
|
163
|
+
source_file: str | None = None
|
|
164
|
+
channel_name: str | None = None
|
|
165
|
+
calibration_info: CalibrationInfo | None = None
|
|
166
|
+
|
|
167
|
+
def __post_init__(self) -> None:
|
|
168
|
+
"""Validate metadata after initialization."""
|
|
169
|
+
if self.sample_rate <= 0:
|
|
170
|
+
raise ValueError(f"sample_rate must be positive, got {self.sample_rate}")
|
|
171
|
+
|
|
172
|
+
@property
|
|
173
|
+
def time_base(self) -> float:
|
|
174
|
+
"""Time between samples in seconds (derived from sample_rate).
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Time per sample in seconds (1 / sample_rate).
|
|
178
|
+
"""
|
|
179
|
+
return 1.0 / self.sample_rate
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@dataclass
|
|
183
|
+
class WaveformTrace:
|
|
184
|
+
"""Analog waveform data with metadata.
|
|
185
|
+
|
|
186
|
+
Stores sampled analog voltage data as a numpy array along with
|
|
187
|
+
associated metadata for timing and scaling.
|
|
188
|
+
|
|
189
|
+
Attributes:
|
|
190
|
+
data: Waveform samples as numpy float array.
|
|
191
|
+
metadata: Associated trace metadata.
|
|
192
|
+
|
|
193
|
+
Example:
|
|
194
|
+
>>> import numpy as np
|
|
195
|
+
>>> data = np.sin(2 * np.pi * 1e6 * np.linspace(0, 1e-3, 1000))
|
|
196
|
+
>>> trace = WaveformTrace(data=data, metadata=TraceMetadata(sample_rate=1e6))
|
|
197
|
+
>>> print(f"Duration: {trace.time_vector[-1]:.6f} seconds")
|
|
198
|
+
Duration: 0.000999 seconds
|
|
199
|
+
|
|
200
|
+
References:
|
|
201
|
+
IEEE 1241-2010: Standard for Terminology and Test Methods for ADCs
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
data: NDArray[np.floating[Any]]
|
|
205
|
+
metadata: TraceMetadata
|
|
206
|
+
|
|
207
|
+
def __post_init__(self) -> None:
|
|
208
|
+
"""Validate waveform data after initialization."""
|
|
209
|
+
if not isinstance(self.data, np.ndarray):
|
|
210
|
+
raise TypeError(f"data must be a numpy array, got {type(self.data).__name__}")
|
|
211
|
+
if not np.issubdtype(self.data.dtype, np.floating):
|
|
212
|
+
# Convert to float64 if not already floating point
|
|
213
|
+
self.data = self.data.astype(np.float64)
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def time_vector(self) -> NDArray[np.float64]:
|
|
217
|
+
"""Time axis in seconds.
|
|
218
|
+
|
|
219
|
+
Computes a time vector starting from 0, with intervals
|
|
220
|
+
determined by the sample rate.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
Array of time values in seconds, same length as data.
|
|
224
|
+
"""
|
|
225
|
+
n_samples = len(self.data)
|
|
226
|
+
return np.arange(n_samples, dtype=np.float64) * self.metadata.time_base
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def duration(self) -> float:
|
|
230
|
+
"""Total duration of the trace in seconds.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
Duration from first to last sample in seconds.
|
|
234
|
+
"""
|
|
235
|
+
if len(self.data) == 0:
|
|
236
|
+
return 0.0
|
|
237
|
+
return (len(self.data) - 1) * self.metadata.time_base
|
|
238
|
+
|
|
239
|
+
def __len__(self) -> int:
|
|
240
|
+
"""Return number of samples in the trace."""
|
|
241
|
+
return len(self.data)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@dataclass
|
|
245
|
+
class DigitalTrace:
|
|
246
|
+
"""Digital/logic signal data with metadata.
|
|
247
|
+
|
|
248
|
+
Stores sampled digital signal data as a boolean numpy array,
|
|
249
|
+
with optional edge timestamp information.
|
|
250
|
+
|
|
251
|
+
Attributes:
|
|
252
|
+
data: Digital samples as numpy boolean array.
|
|
253
|
+
metadata: Associated trace metadata.
|
|
254
|
+
edges: Optional list of (timestamp, is_rising) tuples.
|
|
255
|
+
|
|
256
|
+
Example:
|
|
257
|
+
>>> import numpy as np
|
|
258
|
+
>>> data = np.array([False, False, True, True, False], dtype=bool)
|
|
259
|
+
>>> trace = DigitalTrace(data=data, metadata=TraceMetadata(sample_rate=1e6))
|
|
260
|
+
>>> print(f"High samples: {np.sum(trace.data)}")
|
|
261
|
+
High samples: 2
|
|
262
|
+
|
|
263
|
+
References:
|
|
264
|
+
IEEE 1076.6-2004: Standard for VHDL Register Transfer Level Synthesis
|
|
265
|
+
"""
|
|
266
|
+
|
|
267
|
+
data: NDArray[np.bool_]
|
|
268
|
+
metadata: TraceMetadata
|
|
269
|
+
edges: list[tuple[float, bool]] | None = None
|
|
270
|
+
|
|
271
|
+
def __post_init__(self) -> None:
|
|
272
|
+
"""Validate digital data after initialization."""
|
|
273
|
+
if not isinstance(self.data, np.ndarray):
|
|
274
|
+
raise TypeError(f"data must be a numpy array, got {type(self.data).__name__}")
|
|
275
|
+
if self.data.dtype != np.bool_:
|
|
276
|
+
# Convert to boolean if not already
|
|
277
|
+
self.data = self.data.astype(np.bool_)
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def time_vector(self) -> NDArray[np.float64]:
|
|
281
|
+
"""Time axis in seconds.
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
Array of time values in seconds, same length as data.
|
|
285
|
+
"""
|
|
286
|
+
n_samples = len(self.data)
|
|
287
|
+
return np.arange(n_samples, dtype=np.float64) * self.metadata.time_base
|
|
288
|
+
|
|
289
|
+
@property
|
|
290
|
+
def duration(self) -> float:
|
|
291
|
+
"""Total duration of the trace in seconds.
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
Duration from first to last sample in seconds.
|
|
295
|
+
"""
|
|
296
|
+
if len(self.data) == 0:
|
|
297
|
+
return 0.0
|
|
298
|
+
return (len(self.data) - 1) * self.metadata.time_base
|
|
299
|
+
|
|
300
|
+
@property
|
|
301
|
+
def rising_edges(self) -> list[float]:
|
|
302
|
+
"""Timestamps of rising edges.
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
List of timestamps where signal transitions from low to high.
|
|
306
|
+
"""
|
|
307
|
+
if self.edges is None:
|
|
308
|
+
return []
|
|
309
|
+
return [ts for ts, is_rising in self.edges if is_rising]
|
|
310
|
+
|
|
311
|
+
@property
|
|
312
|
+
def falling_edges(self) -> list[float]:
|
|
313
|
+
"""Timestamps of falling edges.
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
List of timestamps where signal transitions from high to low.
|
|
317
|
+
"""
|
|
318
|
+
if self.edges is None:
|
|
319
|
+
return []
|
|
320
|
+
return [ts for ts, is_rising in self.edges if not is_rising]
|
|
321
|
+
|
|
322
|
+
def __len__(self) -> int:
|
|
323
|
+
"""Return number of samples in the trace."""
|
|
324
|
+
return len(self.data)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
@dataclass
|
|
328
|
+
class IQTrace:
|
|
329
|
+
"""I/Q (In-phase/Quadrature) waveform data with metadata.
|
|
330
|
+
|
|
331
|
+
Stores complex-valued signal data as separate I and Q components,
|
|
332
|
+
commonly used for RF and software-defined radio applications.
|
|
333
|
+
|
|
334
|
+
Attributes:
|
|
335
|
+
i_data: In-phase component samples as numpy float array.
|
|
336
|
+
q_data: Quadrature component samples as numpy float array.
|
|
337
|
+
metadata: Associated trace metadata.
|
|
338
|
+
|
|
339
|
+
Example:
|
|
340
|
+
>>> import numpy as np
|
|
341
|
+
>>> t = np.linspace(0, 1e-3, 1000)
|
|
342
|
+
>>> i_data = np.cos(2 * np.pi * 1e6 * t)
|
|
343
|
+
>>> q_data = np.sin(2 * np.pi * 1e6 * t)
|
|
344
|
+
>>> trace = IQTrace(i_data=i_data, q_data=q_data, metadata=TraceMetadata(sample_rate=1e6))
|
|
345
|
+
>>> print(f"Complex samples: {len(trace)}")
|
|
346
|
+
Complex samples: 1000
|
|
347
|
+
|
|
348
|
+
References:
|
|
349
|
+
IEEE Std 181-2011: Transitional Waveform Definitions
|
|
350
|
+
"""
|
|
351
|
+
|
|
352
|
+
i_data: NDArray[np.floating[Any]]
|
|
353
|
+
q_data: NDArray[np.floating[Any]]
|
|
354
|
+
metadata: TraceMetadata
|
|
355
|
+
|
|
356
|
+
def __post_init__(self) -> None:
|
|
357
|
+
"""Validate I/Q data after initialization."""
|
|
358
|
+
if not isinstance(self.i_data, np.ndarray):
|
|
359
|
+
raise TypeError(f"i_data must be a numpy array, got {type(self.i_data).__name__}")
|
|
360
|
+
if not isinstance(self.q_data, np.ndarray):
|
|
361
|
+
raise TypeError(f"q_data must be a numpy array, got {type(self.q_data).__name__}")
|
|
362
|
+
if len(self.i_data) != len(self.q_data):
|
|
363
|
+
raise ValueError(
|
|
364
|
+
f"I and Q data must have same length, got {len(self.i_data)} and {len(self.q_data)}"
|
|
365
|
+
)
|
|
366
|
+
# Convert to float64 if not already floating point
|
|
367
|
+
if not np.issubdtype(self.i_data.dtype, np.floating):
|
|
368
|
+
self.i_data = self.i_data.astype(np.float64)
|
|
369
|
+
if not np.issubdtype(self.q_data.dtype, np.floating):
|
|
370
|
+
self.q_data = self.q_data.astype(np.float64)
|
|
371
|
+
|
|
372
|
+
@property
|
|
373
|
+
def complex_data(self) -> NDArray[np.complex128]:
|
|
374
|
+
"""Return I/Q data as complex array.
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
Complex array where real=I, imag=Q.
|
|
378
|
+
"""
|
|
379
|
+
return self.i_data + 1j * self.q_data
|
|
380
|
+
|
|
381
|
+
@property
|
|
382
|
+
def magnitude(self) -> NDArray[np.float64]:
|
|
383
|
+
"""Magnitude (amplitude) of the complex signal.
|
|
384
|
+
|
|
385
|
+
Returns:
|
|
386
|
+
Array of magnitude values sqrt(I² + Q²).
|
|
387
|
+
"""
|
|
388
|
+
return np.sqrt(self.i_data**2 + self.q_data**2)
|
|
389
|
+
|
|
390
|
+
@property
|
|
391
|
+
def phase(self) -> NDArray[np.float64]:
|
|
392
|
+
"""Phase angle of the complex signal in radians.
|
|
393
|
+
|
|
394
|
+
Returns:
|
|
395
|
+
Array of phase values atan2(Q, I).
|
|
396
|
+
"""
|
|
397
|
+
return np.arctan2(self.q_data, self.i_data)
|
|
398
|
+
|
|
399
|
+
@property
|
|
400
|
+
def time_vector(self) -> NDArray[np.float64]:
|
|
401
|
+
"""Time axis in seconds.
|
|
402
|
+
|
|
403
|
+
Returns:
|
|
404
|
+
Array of time values in seconds, same length as data.
|
|
405
|
+
"""
|
|
406
|
+
n_samples = len(self.i_data)
|
|
407
|
+
return np.arange(n_samples, dtype=np.float64) * self.metadata.time_base
|
|
408
|
+
|
|
409
|
+
@property
|
|
410
|
+
def duration(self) -> float:
|
|
411
|
+
"""Total duration of the trace in seconds.
|
|
412
|
+
|
|
413
|
+
Returns:
|
|
414
|
+
Duration from first to last sample in seconds.
|
|
415
|
+
"""
|
|
416
|
+
if len(self.i_data) == 0:
|
|
417
|
+
return 0.0
|
|
418
|
+
return (len(self.i_data) - 1) * self.metadata.time_base
|
|
419
|
+
|
|
420
|
+
def __len__(self) -> int:
|
|
421
|
+
"""Return number of samples in the trace."""
|
|
422
|
+
return len(self.i_data)
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
@dataclass
|
|
426
|
+
class ProtocolPacket:
|
|
427
|
+
"""Decoded protocol packet data.
|
|
428
|
+
|
|
429
|
+
Represents a decoded packet from a serial protocol (UART, SPI, I2C, etc.)
|
|
430
|
+
with timing, data content, annotations, and error information.
|
|
431
|
+
|
|
432
|
+
Attributes:
|
|
433
|
+
timestamp: Start time of the packet in seconds.
|
|
434
|
+
protocol: Name of the protocol (e.g., "UART", "SPI", "I2C").
|
|
435
|
+
data: Decoded data bytes.
|
|
436
|
+
annotations: Multi-level annotations dictionary (optional).
|
|
437
|
+
errors: List of detected errors (optional).
|
|
438
|
+
end_timestamp: End time of the packet in seconds (optional).
|
|
439
|
+
|
|
440
|
+
Example:
|
|
441
|
+
>>> packet = ProtocolPacket(
|
|
442
|
+
... timestamp=1.23e-3,
|
|
443
|
+
... protocol="UART",
|
|
444
|
+
... data=b"Hello"
|
|
445
|
+
... )
|
|
446
|
+
>>> print(f"Received at {packet.timestamp}s: {packet.data.decode()}")
|
|
447
|
+
Received at 0.00123s: Hello
|
|
448
|
+
|
|
449
|
+
References:
|
|
450
|
+
sigrok Protocol Decoder API
|
|
451
|
+
"""
|
|
452
|
+
|
|
453
|
+
timestamp: float
|
|
454
|
+
protocol: str
|
|
455
|
+
data: bytes
|
|
456
|
+
annotations: dict[str, Any] = field(default_factory=dict)
|
|
457
|
+
errors: list[str] = field(default_factory=list)
|
|
458
|
+
end_timestamp: float | None = None
|
|
459
|
+
|
|
460
|
+
def __post_init__(self) -> None:
|
|
461
|
+
"""Validate packet data after initialization."""
|
|
462
|
+
if self.timestamp < 0:
|
|
463
|
+
raise ValueError(f"timestamp must be non-negative, got {self.timestamp}")
|
|
464
|
+
if not isinstance(self.data, bytes):
|
|
465
|
+
raise TypeError(f"data must be bytes, got {type(self.data).__name__}")
|
|
466
|
+
|
|
467
|
+
@property
|
|
468
|
+
def duration(self) -> float | None:
|
|
469
|
+
"""Duration of the packet in seconds.
|
|
470
|
+
|
|
471
|
+
Returns:
|
|
472
|
+
Duration if end_timestamp is set, None otherwise.
|
|
473
|
+
"""
|
|
474
|
+
if self.end_timestamp is None:
|
|
475
|
+
return None
|
|
476
|
+
return self.end_timestamp - self.timestamp
|
|
477
|
+
|
|
478
|
+
@property
|
|
479
|
+
def has_errors(self) -> bool:
|
|
480
|
+
"""Check if packet has any errors.
|
|
481
|
+
|
|
482
|
+
Returns:
|
|
483
|
+
True if errors list is non-empty.
|
|
484
|
+
"""
|
|
485
|
+
return len(self.errors) > 0
|
|
486
|
+
|
|
487
|
+
def __len__(self) -> int:
|
|
488
|
+
"""Return number of bytes in the packet."""
|
|
489
|
+
return len(self.data)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
# Type aliases for convenience
|
|
493
|
+
Trace = WaveformTrace | DigitalTrace | IQTrace
|
|
494
|
+
"""Union type for any trace type."""
|
|
495
|
+
|
|
496
|
+
__all__ = [
|
|
497
|
+
"CalibrationInfo",
|
|
498
|
+
"DigitalTrace",
|
|
499
|
+
"IQTrace",
|
|
500
|
+
"ProtocolPacket",
|
|
501
|
+
"Trace",
|
|
502
|
+
"TraceMetadata",
|
|
503
|
+
"WaveformTrace",
|
|
504
|
+
]
|