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
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
"""TraceKit data loaders for various file formats.
|
|
2
|
+
|
|
3
|
+
This module provides a unified load() function that auto-detects file formats
|
|
4
|
+
and delegates to the appropriate loader.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> import oscura as tk
|
|
9
|
+
>>> trace = tk.load("capture.wfm")
|
|
10
|
+
>>> print(f"Loaded {len(trace.data)} samples")
|
|
11
|
+
|
|
12
|
+
>>> # Load all channels from multi-channel file
|
|
13
|
+
>>> channels = tk.load_all_channels("multi_channel.wfm")
|
|
14
|
+
>>> for name, trace in channels.items():
|
|
15
|
+
... print(f"{name}: {len(trace.data)} samples")
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import logging
|
|
21
|
+
import warnings
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import TYPE_CHECKING, Any
|
|
24
|
+
|
|
25
|
+
from oscura.core.exceptions import LoaderError, UnsupportedFormatError
|
|
26
|
+
from oscura.core.types import DigitalTrace, WaveformTrace
|
|
27
|
+
|
|
28
|
+
# Import alias modules for DSL compatibility
|
|
29
|
+
from oscura.loaders import (
|
|
30
|
+
binary,
|
|
31
|
+
csv,
|
|
32
|
+
hdf5,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Import configurable binary loading functionality
|
|
36
|
+
from oscura.loaders.configurable import (
|
|
37
|
+
BitfieldDef,
|
|
38
|
+
BitfieldExtractor,
|
|
39
|
+
ConfigurablePacketLoader,
|
|
40
|
+
DeviceConfig,
|
|
41
|
+
DeviceInfo,
|
|
42
|
+
DeviceMapper,
|
|
43
|
+
HeaderFieldDef,
|
|
44
|
+
PacketFormatConfig,
|
|
45
|
+
ParsedPacket,
|
|
46
|
+
SampleFormatDef,
|
|
47
|
+
detect_source_type,
|
|
48
|
+
extract_channels,
|
|
49
|
+
load_binary_packets,
|
|
50
|
+
load_packets_streaming,
|
|
51
|
+
)
|
|
52
|
+
from oscura.loaders.lazy import LazyWaveformTrace, load_trace_lazy
|
|
53
|
+
from oscura.loaders.preprocessing import (
|
|
54
|
+
IdleRegion,
|
|
55
|
+
IdleStatistics,
|
|
56
|
+
IdleStats,
|
|
57
|
+
detect_idle_regions,
|
|
58
|
+
get_idle_statistics,
|
|
59
|
+
trim_idle,
|
|
60
|
+
)
|
|
61
|
+
from oscura.loaders.validation import (
|
|
62
|
+
PacketValidator,
|
|
63
|
+
SequenceGap,
|
|
64
|
+
SequenceValidation,
|
|
65
|
+
ValidationResult,
|
|
66
|
+
ValidationStats,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
if TYPE_CHECKING:
|
|
70
|
+
from os import PathLike
|
|
71
|
+
|
|
72
|
+
from oscura.core.types import Trace
|
|
73
|
+
|
|
74
|
+
# Logger for debug output
|
|
75
|
+
logger = logging.getLogger(__name__)
|
|
76
|
+
|
|
77
|
+
# Supported format extensions mapped to loader names
|
|
78
|
+
SUPPORTED_FORMATS: dict[str, str] = {
|
|
79
|
+
".wfm": "auto_wfm", # Auto-detect Tektronix vs Rigol
|
|
80
|
+
".npz": "numpy",
|
|
81
|
+
".csv": "csv",
|
|
82
|
+
".h5": "hdf5",
|
|
83
|
+
".hdf5": "hdf5",
|
|
84
|
+
".sr": "sigrok",
|
|
85
|
+
".pcap": "pcap",
|
|
86
|
+
".pcapng": "pcap",
|
|
87
|
+
".wav": "wav",
|
|
88
|
+
".vcd": "vcd",
|
|
89
|
+
".tdms": "tdms",
|
|
90
|
+
# Touchstone S-parameter formats
|
|
91
|
+
".s1p": "touchstone",
|
|
92
|
+
".s2p": "touchstone",
|
|
93
|
+
".s3p": "touchstone",
|
|
94
|
+
".s4p": "touchstone",
|
|
95
|
+
".s5p": "touchstone",
|
|
96
|
+
".s6p": "touchstone",
|
|
97
|
+
".s7p": "touchstone",
|
|
98
|
+
".s8p": "touchstone",
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# File size warning threshold for lazy loading suggestion (100 MB)
|
|
102
|
+
LARGE_FILE_WARNING_THRESHOLD = 100 * 1024 * 1024
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def load(
|
|
106
|
+
path: str | PathLike[str],
|
|
107
|
+
*,
|
|
108
|
+
format: str | None = None,
|
|
109
|
+
channel: str | int | None = None,
|
|
110
|
+
lazy: bool = False,
|
|
111
|
+
**kwargs: Any,
|
|
112
|
+
) -> Trace:
|
|
113
|
+
"""Load trace data from file with automatic format detection.
|
|
114
|
+
|
|
115
|
+
This is the primary entry point for loading oscilloscope and logic
|
|
116
|
+
analyzer data. The file format is auto-detected from the extension
|
|
117
|
+
unless explicitly specified.
|
|
118
|
+
|
|
119
|
+
Supports both analog waveforms (WaveformTrace) and digital waveforms
|
|
120
|
+
(DigitalTrace) from mixed-signal oscilloscopes.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
path: Path to the file to load.
|
|
124
|
+
format: Optional format override (e.g., "tektronix", "rigol", "csv").
|
|
125
|
+
If not specified, format is auto-detected from file extension.
|
|
126
|
+
channel: Optional channel name or index for multi-channel files.
|
|
127
|
+
lazy: If True, use lazy loading for huge files (see load_lazy).
|
|
128
|
+
**kwargs: Additional arguments passed to the specific loader.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
WaveformTrace or DigitalTrace depending on the file content.
|
|
132
|
+
|
|
133
|
+
Raises:
|
|
134
|
+
UnsupportedFormatError: If the file format is not recognized.
|
|
135
|
+
FileNotFoundError: If the file does not exist.
|
|
136
|
+
|
|
137
|
+
Example:
|
|
138
|
+
>>> import oscura as tk
|
|
139
|
+
>>> trace = tk.load("oscilloscope_capture.wfm")
|
|
140
|
+
>>> print(f"Loaded {len(trace.data)} samples at {trace.metadata.sample_rate} Hz")
|
|
141
|
+
|
|
142
|
+
>>> # Force specific loader
|
|
143
|
+
>>> trace = tk.load("data.bin", format="tektronix")
|
|
144
|
+
|
|
145
|
+
>>> # Check if digital trace
|
|
146
|
+
>>> if isinstance(trace, DigitalTrace):
|
|
147
|
+
... print("Loaded digital waveform")
|
|
148
|
+
"""
|
|
149
|
+
path = Path(path)
|
|
150
|
+
|
|
151
|
+
if not path.exists():
|
|
152
|
+
raise FileNotFoundError(f"File not found: {path}")
|
|
153
|
+
|
|
154
|
+
# Check file size and warn for large files
|
|
155
|
+
file_size = path.stat().st_size
|
|
156
|
+
if file_size > LARGE_FILE_WARNING_THRESHOLD and not lazy:
|
|
157
|
+
warnings.warn(
|
|
158
|
+
f"File is large ({file_size / 1024 / 1024:.1f} MB). "
|
|
159
|
+
"Consider using lazy=True for better memory efficiency.",
|
|
160
|
+
stacklevel=2,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Handle lazy loading request
|
|
164
|
+
if lazy:
|
|
165
|
+
return load_lazy(path, **kwargs) # type: ignore[return-value]
|
|
166
|
+
|
|
167
|
+
# Determine format
|
|
168
|
+
if format is not None:
|
|
169
|
+
loader_name = format.lower()
|
|
170
|
+
else:
|
|
171
|
+
ext = path.suffix.lower()
|
|
172
|
+
if ext not in SUPPORTED_FORMATS:
|
|
173
|
+
raise UnsupportedFormatError(
|
|
174
|
+
ext,
|
|
175
|
+
list(SUPPORTED_FORMATS.keys()),
|
|
176
|
+
file_path=str(path),
|
|
177
|
+
)
|
|
178
|
+
loader_name = SUPPORTED_FORMATS[ext]
|
|
179
|
+
|
|
180
|
+
# Dispatch to appropriate loader
|
|
181
|
+
if loader_name == "auto_wfm":
|
|
182
|
+
return _load_wfm_auto(path, channel=channel, **kwargs)
|
|
183
|
+
elif loader_name in ("tektronix", "tek"):
|
|
184
|
+
from oscura.loaders.tektronix import load_tektronix_wfm
|
|
185
|
+
|
|
186
|
+
return load_tektronix_wfm(path, **kwargs)
|
|
187
|
+
elif loader_name == "rigol":
|
|
188
|
+
from oscura.loaders.rigol import load_rigol_wfm
|
|
189
|
+
|
|
190
|
+
return load_rigol_wfm(path, **kwargs)
|
|
191
|
+
elif loader_name == "numpy":
|
|
192
|
+
from oscura.loaders.numpy_loader import load_npz
|
|
193
|
+
|
|
194
|
+
return load_npz(path, channel=channel, **kwargs)
|
|
195
|
+
elif loader_name == "csv":
|
|
196
|
+
from oscura.loaders.csv_loader import load_csv
|
|
197
|
+
|
|
198
|
+
return load_csv(path, **kwargs) # type: ignore[return-value]
|
|
199
|
+
elif loader_name == "hdf5":
|
|
200
|
+
from oscura.loaders.hdf5_loader import load_hdf5
|
|
201
|
+
|
|
202
|
+
return load_hdf5(path, channel=channel, **kwargs) # type: ignore[return-value]
|
|
203
|
+
elif loader_name == "sigrok":
|
|
204
|
+
from oscura.loaders.sigrok import load_sigrok
|
|
205
|
+
|
|
206
|
+
return load_sigrok(path, channel=channel, **kwargs)
|
|
207
|
+
elif loader_name == "vcd":
|
|
208
|
+
from oscura.loaders.vcd import load_vcd
|
|
209
|
+
|
|
210
|
+
return load_vcd(path, **kwargs)
|
|
211
|
+
elif loader_name == "pcap":
|
|
212
|
+
from oscura.loaders.pcap import load_pcap
|
|
213
|
+
|
|
214
|
+
return load_pcap(path, **kwargs) # type: ignore[return-value]
|
|
215
|
+
elif loader_name == "wav":
|
|
216
|
+
from oscura.loaders.wav import load_wav
|
|
217
|
+
|
|
218
|
+
return load_wav(path, channel=channel, **kwargs)
|
|
219
|
+
elif loader_name == "tdms":
|
|
220
|
+
from oscura.loaders.tdms import load_tdms
|
|
221
|
+
|
|
222
|
+
return load_tdms(path, channel=channel, **kwargs)
|
|
223
|
+
elif loader_name == "touchstone":
|
|
224
|
+
from oscura.analyzers.signal_integrity.sparams import load_touchstone
|
|
225
|
+
|
|
226
|
+
return load_touchstone(path) # type: ignore[return-value]
|
|
227
|
+
else:
|
|
228
|
+
raise UnsupportedFormatError(
|
|
229
|
+
loader_name,
|
|
230
|
+
list(SUPPORTED_FORMATS.keys()),
|
|
231
|
+
file_path=str(path),
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _load_wfm_auto(
|
|
236
|
+
path: Path,
|
|
237
|
+
*,
|
|
238
|
+
channel: str | int | None = None,
|
|
239
|
+
**kwargs: Any,
|
|
240
|
+
) -> Trace:
|
|
241
|
+
"""Auto-detect WFM format (Tektronix vs Rigol) and load.
|
|
242
|
+
|
|
243
|
+
Distinguishes between Tektronix and Rigol WFM formats by examining
|
|
244
|
+
the file's magic bytes.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
path: Path to the .wfm file.
|
|
248
|
+
channel: Optional channel for multi-channel files.
|
|
249
|
+
**kwargs: Additional arguments for the loader.
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
WaveformTrace or DigitalTrace from the detected loader.
|
|
253
|
+
|
|
254
|
+
Raises:
|
|
255
|
+
LoaderError: If the WFM format cannot be determined.
|
|
256
|
+
"""
|
|
257
|
+
# Read first bytes to detect format
|
|
258
|
+
try:
|
|
259
|
+
with open(path, "rb") as f:
|
|
260
|
+
magic = f.read(32)
|
|
261
|
+
except OSError as e:
|
|
262
|
+
raise LoaderError(
|
|
263
|
+
"Failed to read file for format detection",
|
|
264
|
+
file_path=str(path),
|
|
265
|
+
details=str(e),
|
|
266
|
+
) from e
|
|
267
|
+
|
|
268
|
+
# Tektronix WFM files typically start with specific patterns
|
|
269
|
+
# Rigol files have different magic bytes
|
|
270
|
+
# This is a simplified detection - real implementation would be more robust
|
|
271
|
+
|
|
272
|
+
# Check for Rigol signature (often starts with certain patterns)
|
|
273
|
+
if magic[:4] in (b"\x00\x00\x01\x00", b"RIGOL"):
|
|
274
|
+
from oscura.loaders.rigol import load_rigol_wfm
|
|
275
|
+
|
|
276
|
+
return load_rigol_wfm(path, **kwargs)
|
|
277
|
+
|
|
278
|
+
# Default to Tektronix
|
|
279
|
+
from oscura.loaders.tektronix import load_tektronix_wfm
|
|
280
|
+
|
|
281
|
+
return load_tektronix_wfm(path, **kwargs)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def load_all_channels(
|
|
285
|
+
path: str | PathLike[str],
|
|
286
|
+
*,
|
|
287
|
+
format: str | None = None,
|
|
288
|
+
) -> dict[str, WaveformTrace | DigitalTrace]:
|
|
289
|
+
"""Load all channels from a multi-channel waveform file.
|
|
290
|
+
|
|
291
|
+
Reads the file once and extracts all available channels (both analog
|
|
292
|
+
and digital). This is more efficient than loading each channel
|
|
293
|
+
separately when you need multiple channels.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
path: Path to the multi-channel waveform file.
|
|
297
|
+
format: Optional format override (e.g., "tektronix", "rigol").
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
Dictionary mapping channel names to traces.
|
|
301
|
+
Analog channels are named "ch1", "ch2", etc.
|
|
302
|
+
Digital channels are named "d1", "d2", etc.
|
|
303
|
+
|
|
304
|
+
Raises:
|
|
305
|
+
UnsupportedFormatError: If the file format is not recognized.
|
|
306
|
+
FileNotFoundError: If the file does not exist.
|
|
307
|
+
|
|
308
|
+
Example:
|
|
309
|
+
>>> import oscura as tk
|
|
310
|
+
>>> channels = tk.load_all_channels("multi_channel.wfm")
|
|
311
|
+
>>> for name, trace in channels.items():
|
|
312
|
+
... print(f"{name}: {len(trace.data)} samples")
|
|
313
|
+
ch1: 10000 samples
|
|
314
|
+
ch2: 10000 samples
|
|
315
|
+
d1: 10000 samples
|
|
316
|
+
|
|
317
|
+
>>> # Access specific channel
|
|
318
|
+
>>> analog_ch1 = channels["ch1"]
|
|
319
|
+
>>> digital_d1 = channels["d1"]
|
|
320
|
+
"""
|
|
321
|
+
path = Path(path)
|
|
322
|
+
|
|
323
|
+
if not path.exists():
|
|
324
|
+
raise FileNotFoundError(f"File not found: {path}")
|
|
325
|
+
|
|
326
|
+
# Determine format
|
|
327
|
+
if format is not None:
|
|
328
|
+
loader_name = format.lower()
|
|
329
|
+
else:
|
|
330
|
+
ext = path.suffix.lower()
|
|
331
|
+
if ext not in SUPPORTED_FORMATS:
|
|
332
|
+
raise UnsupportedFormatError(
|
|
333
|
+
ext,
|
|
334
|
+
list(SUPPORTED_FORMATS.keys()),
|
|
335
|
+
file_path=str(path),
|
|
336
|
+
)
|
|
337
|
+
loader_name = SUPPORTED_FORMATS[ext]
|
|
338
|
+
|
|
339
|
+
# Currently only supports Tektronix WFM for multi-channel loading
|
|
340
|
+
if loader_name in ("auto_wfm", "tektronix", "tek"):
|
|
341
|
+
return _load_all_channels_tektronix(path)
|
|
342
|
+
else:
|
|
343
|
+
# For other formats, try loading as single channel
|
|
344
|
+
trace = load(path, format=format)
|
|
345
|
+
channel_name = getattr(trace.metadata, "channel_name", None) or "ch1"
|
|
346
|
+
return {channel_name: trace} # type: ignore[dict-item]
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def _load_all_channels_tektronix(
|
|
350
|
+
path: Path,
|
|
351
|
+
) -> dict[str, WaveformTrace | DigitalTrace]:
|
|
352
|
+
"""Load all channels from a Tektronix WFM file.
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
path: Path to the Tektronix .wfm file.
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
Dictionary mapping channel names to traces.
|
|
359
|
+
|
|
360
|
+
Raises:
|
|
361
|
+
LoaderError: If the file cannot be read or parsed.
|
|
362
|
+
"""
|
|
363
|
+
try:
|
|
364
|
+
import tm_data_types # type: ignore[import-not-found, import-untyped]
|
|
365
|
+
except ImportError:
|
|
366
|
+
# Fall back to single channel loading
|
|
367
|
+
trace = load(path, format="tektronix")
|
|
368
|
+
channel_name = getattr(trace.metadata, "channel_name", None) or "ch1"
|
|
369
|
+
return {channel_name: trace} # type: ignore[dict-item]
|
|
370
|
+
|
|
371
|
+
try:
|
|
372
|
+
wfm = tm_data_types.read_file(str(path))
|
|
373
|
+
except Exception as e:
|
|
374
|
+
raise LoaderError(
|
|
375
|
+
"Failed to read Tektronix WFM file",
|
|
376
|
+
file_path=str(path),
|
|
377
|
+
details=str(e),
|
|
378
|
+
) from e
|
|
379
|
+
|
|
380
|
+
channels: dict[str, WaveformTrace | DigitalTrace] = {}
|
|
381
|
+
|
|
382
|
+
# Extract analog waveforms
|
|
383
|
+
if hasattr(wfm, "analog_waveforms") and wfm.analog_waveforms:
|
|
384
|
+
import numpy as np
|
|
385
|
+
|
|
386
|
+
from oscura.loaders.tektronix import _build_waveform_trace
|
|
387
|
+
|
|
388
|
+
for i, awfm in enumerate(wfm.analog_waveforms):
|
|
389
|
+
try:
|
|
390
|
+
data = np.array(awfm.y_data, dtype=np.float64)
|
|
391
|
+
x_increment = getattr(awfm, "x_increment", 1e-6)
|
|
392
|
+
sample_rate = 1.0 / x_increment if x_increment > 0 else 1e6
|
|
393
|
+
vertical_scale = getattr(awfm, "y_scale", None)
|
|
394
|
+
vertical_offset = getattr(awfm, "y_offset", None)
|
|
395
|
+
channel_name = getattr(awfm, "name", f"CH{i + 1}")
|
|
396
|
+
|
|
397
|
+
trace = _build_waveform_trace(
|
|
398
|
+
data=data,
|
|
399
|
+
sample_rate=sample_rate,
|
|
400
|
+
vertical_scale=vertical_scale,
|
|
401
|
+
vertical_offset=vertical_offset,
|
|
402
|
+
channel_name=channel_name,
|
|
403
|
+
path=path,
|
|
404
|
+
wfm=awfm,
|
|
405
|
+
)
|
|
406
|
+
channels[f"ch{i + 1}"] = trace
|
|
407
|
+
except Exception as e:
|
|
408
|
+
logger.warning("Failed to extract analog channel %d: %s", i + 1, e)
|
|
409
|
+
|
|
410
|
+
# Extract digital waveforms
|
|
411
|
+
if hasattr(wfm, "digital_waveforms") and wfm.digital_waveforms:
|
|
412
|
+
from oscura.loaders.tektronix import _load_digital_waveform
|
|
413
|
+
|
|
414
|
+
for i, dwfm in enumerate(wfm.digital_waveforms):
|
|
415
|
+
try:
|
|
416
|
+
trace = _load_digital_waveform(dwfm, path, i)
|
|
417
|
+
channels[f"d{i + 1}"] = trace
|
|
418
|
+
except Exception as e:
|
|
419
|
+
logger.warning("Failed to extract digital channel %d: %s", i + 1, e)
|
|
420
|
+
|
|
421
|
+
# Handle direct waveform formats (single file = single channel)
|
|
422
|
+
if not channels:
|
|
423
|
+
wfm_type = type(wfm).__name__
|
|
424
|
+
|
|
425
|
+
if wfm_type == "DigitalWaveform" or hasattr(wfm, "y_axis_byte_values"):
|
|
426
|
+
from oscura.loaders.tektronix import _load_digital_waveform
|
|
427
|
+
|
|
428
|
+
trace = _load_digital_waveform(wfm, path, 0)
|
|
429
|
+
channel_name = trace.metadata.channel_name or "d1"
|
|
430
|
+
channels[channel_name.lower()] = trace
|
|
431
|
+
|
|
432
|
+
elif hasattr(wfm, "y_axis_values") or hasattr(wfm, "y_data"):
|
|
433
|
+
# Direct analog waveform
|
|
434
|
+
trace = load(path, format="tektronix")
|
|
435
|
+
channel_name = trace.metadata.channel_name or "ch1"
|
|
436
|
+
channels[channel_name.lower()] = trace # type: ignore[assignment]
|
|
437
|
+
|
|
438
|
+
if not channels:
|
|
439
|
+
raise LoaderError(
|
|
440
|
+
"No channels found in file",
|
|
441
|
+
file_path=str(path),
|
|
442
|
+
fix_hint="File may be empty or use an unsupported format variant.",
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
return channels
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def get_supported_formats() -> list[str]:
|
|
449
|
+
"""Get list of supported file formats.
|
|
450
|
+
|
|
451
|
+
Returns:
|
|
452
|
+
List of supported file extensions.
|
|
453
|
+
|
|
454
|
+
Example:
|
|
455
|
+
>>> from oscura.loaders import get_supported_formats
|
|
456
|
+
>>> print(get_supported_formats())
|
|
457
|
+
['.wfm', '.npz', '.csv', '.h5', ...]
|
|
458
|
+
"""
|
|
459
|
+
return list(SUPPORTED_FORMATS.keys())
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def load_lazy(path: str | PathLike[str], **kwargs: Any) -> LazyWaveformTrace | WaveformTrace:
|
|
463
|
+
"""Load trace with lazy loading for huge files.
|
|
464
|
+
|
|
465
|
+
Convenience wrapper for lazy loading. See load_trace_lazy for details.
|
|
466
|
+
|
|
467
|
+
Args:
|
|
468
|
+
path: Path to the file.
|
|
469
|
+
**kwargs: Additional arguments (sample_rate, lazy=True, etc.).
|
|
470
|
+
|
|
471
|
+
Returns:
|
|
472
|
+
LazyWaveformTrace or WaveformTrace.
|
|
473
|
+
|
|
474
|
+
Example:
|
|
475
|
+
>>> trace = tk.loaders.load_lazy("huge_trace.npy", sample_rate=1e9)
|
|
476
|
+
>>> print(f"Length: {trace.length}") # Metadata available immediately
|
|
477
|
+
|
|
478
|
+
References:
|
|
479
|
+
API-017: Lazy Loading for Huge Files
|
|
480
|
+
"""
|
|
481
|
+
from oscura.loaders.lazy import load_trace_lazy
|
|
482
|
+
|
|
483
|
+
return load_trace_lazy(path, **kwargs) # type: ignore[arg-type]
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
__all__ = [
|
|
487
|
+
"LARGE_FILE_WARNING_THRESHOLD",
|
|
488
|
+
"SUPPORTED_FORMATS",
|
|
489
|
+
# Configurable binary loading
|
|
490
|
+
"BitfieldDef",
|
|
491
|
+
"BitfieldExtractor",
|
|
492
|
+
"ConfigurablePacketLoader",
|
|
493
|
+
"DeviceConfig",
|
|
494
|
+
"DeviceInfo",
|
|
495
|
+
"DeviceMapper",
|
|
496
|
+
"DigitalTrace",
|
|
497
|
+
"HeaderFieldDef",
|
|
498
|
+
"IdleRegion",
|
|
499
|
+
"IdleStatistics",
|
|
500
|
+
"IdleStats",
|
|
501
|
+
"LazyWaveformTrace",
|
|
502
|
+
"PacketFormatConfig",
|
|
503
|
+
"PacketValidator",
|
|
504
|
+
"ParsedPacket",
|
|
505
|
+
"SampleFormatDef",
|
|
506
|
+
"SequenceGap",
|
|
507
|
+
"SequenceValidation",
|
|
508
|
+
"ValidationResult",
|
|
509
|
+
"ValidationStats",
|
|
510
|
+
"WaveformTrace",
|
|
511
|
+
"binary",
|
|
512
|
+
"csv",
|
|
513
|
+
"detect_idle_regions",
|
|
514
|
+
"detect_source_type",
|
|
515
|
+
"extract_channels",
|
|
516
|
+
"get_idle_statistics",
|
|
517
|
+
"get_supported_formats",
|
|
518
|
+
"hdf5",
|
|
519
|
+
"load",
|
|
520
|
+
"load_all_channels",
|
|
521
|
+
"load_binary_packets",
|
|
522
|
+
"load_lazy",
|
|
523
|
+
"load_packets_streaming",
|
|
524
|
+
"load_trace_lazy",
|
|
525
|
+
"trim_idle",
|
|
526
|
+
]
|
oscura/loaders/binary.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Binary file loader for raw signal data.
|
|
2
|
+
|
|
3
|
+
Loads raw binary files containing signal data with user-specified format.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import TYPE_CHECKING, Any
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
from oscura.core.types import TraceMetadata, WaveformTrace
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from os import PathLike
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def load_binary(
|
|
20
|
+
path: str | PathLike[str],
|
|
21
|
+
*,
|
|
22
|
+
dtype: str | np.dtype[Any] = "float64",
|
|
23
|
+
sample_rate: float = 1.0,
|
|
24
|
+
channels: int = 1,
|
|
25
|
+
channel: int = 0,
|
|
26
|
+
offset: int = 0,
|
|
27
|
+
count: int = -1,
|
|
28
|
+
) -> WaveformTrace:
|
|
29
|
+
"""Load raw binary file as waveform trace.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
path: Path to the binary file.
|
|
33
|
+
dtype: NumPy dtype for the data (default: float64).
|
|
34
|
+
sample_rate: Sample rate in Hz.
|
|
35
|
+
channels: Number of interleaved channels.
|
|
36
|
+
channel: Channel index to load (0-based).
|
|
37
|
+
offset: Number of samples to skip from start.
|
|
38
|
+
count: Number of samples to read (-1 for all).
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
WaveformTrace containing the loaded data.
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
>>> from oscura.loaders.binary import load_binary
|
|
45
|
+
>>> trace = load_binary("signal.bin", dtype="int16", sample_rate=1e6)
|
|
46
|
+
"""
|
|
47
|
+
path = Path(path)
|
|
48
|
+
|
|
49
|
+
# Load raw data
|
|
50
|
+
data = np.fromfile(path, dtype=dtype, count=count, offset=offset * np.dtype(dtype).itemsize)
|
|
51
|
+
|
|
52
|
+
# Handle multi-channel data
|
|
53
|
+
if channels > 1:
|
|
54
|
+
# Reshape and select channel
|
|
55
|
+
samples_per_channel = len(data) // channels
|
|
56
|
+
data = data[: samples_per_channel * channels].reshape(-1, channels)
|
|
57
|
+
data = data[:, channel]
|
|
58
|
+
|
|
59
|
+
# Create metadata
|
|
60
|
+
metadata = TraceMetadata(
|
|
61
|
+
sample_rate=sample_rate,
|
|
62
|
+
source_file=str(path),
|
|
63
|
+
channel_name=f"Channel {channel}",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
return WaveformTrace(data=data.astype(np.float64), metadata=metadata)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
__all__ = ["load_binary"]
|