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,445 @@
|
|
|
1
|
+
"""Analysis session management.
|
|
2
|
+
|
|
3
|
+
This module provides session save/restore functionality for TraceKit.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> session = Session()
|
|
8
|
+
>>> session.load_trace('capture.wfm')
|
|
9
|
+
>>> session.save('debug_session.tks')
|
|
10
|
+
>>>
|
|
11
|
+
>>> # Later...
|
|
12
|
+
>>> session = load_session('debug_session.tks')
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import gzip
|
|
18
|
+
import pickle
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from datetime import datetime
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
import numpy as np
|
|
25
|
+
|
|
26
|
+
from oscura.session.annotations import AnnotationLayer
|
|
27
|
+
from oscura.session.history import OperationHistory
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class Session:
|
|
32
|
+
"""Analysis session container.
|
|
33
|
+
|
|
34
|
+
Manages traces, annotations, measurements, and history for a complete
|
|
35
|
+
analysis session. Sessions can be saved and restored.
|
|
36
|
+
|
|
37
|
+
Attributes:
|
|
38
|
+
name: Session name
|
|
39
|
+
traces: Dictionary of loaded traces (name -> trace)
|
|
40
|
+
annotation_layers: Annotation layers
|
|
41
|
+
measurements: Recorded measurements
|
|
42
|
+
history: Operation history
|
|
43
|
+
metadata: Session metadata
|
|
44
|
+
created_at: Creation timestamp
|
|
45
|
+
modified_at: Last modification timestamp
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
name: str = "Untitled Session"
|
|
49
|
+
traces: dict[str, Any] = field(default_factory=dict)
|
|
50
|
+
annotation_layers: dict[str, AnnotationLayer] = field(default_factory=dict)
|
|
51
|
+
measurements: dict[str, Any] = field(default_factory=dict)
|
|
52
|
+
history: OperationHistory = field(default_factory=OperationHistory)
|
|
53
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
54
|
+
created_at: datetime = field(default_factory=datetime.now)
|
|
55
|
+
modified_at: datetime = field(default_factory=datetime.now)
|
|
56
|
+
_file_path: Path | None = None
|
|
57
|
+
|
|
58
|
+
def __post_init__(self) -> None:
|
|
59
|
+
"""Initialize default annotation layer."""
|
|
60
|
+
if "default" not in self.annotation_layers:
|
|
61
|
+
self.annotation_layers["default"] = AnnotationLayer("Default")
|
|
62
|
+
|
|
63
|
+
def load_trace(
|
|
64
|
+
self,
|
|
65
|
+
path: str | Path,
|
|
66
|
+
name: str | None = None,
|
|
67
|
+
**load_kwargs: Any,
|
|
68
|
+
) -> Any:
|
|
69
|
+
"""Load a trace into the session.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
path: Path to trace file.
|
|
73
|
+
name: Name for trace in session (default: filename).
|
|
74
|
+
**load_kwargs: Additional arguments for load().
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Loaded trace.
|
|
78
|
+
"""
|
|
79
|
+
from oscura.loaders import load
|
|
80
|
+
|
|
81
|
+
path = Path(path)
|
|
82
|
+
trace = load(str(path), **load_kwargs)
|
|
83
|
+
|
|
84
|
+
if name is None:
|
|
85
|
+
name = path.stem
|
|
86
|
+
|
|
87
|
+
self.traces[name] = trace
|
|
88
|
+
self._mark_modified()
|
|
89
|
+
|
|
90
|
+
self.history.record(
|
|
91
|
+
"load_trace",
|
|
92
|
+
{"path": str(path), "name": name},
|
|
93
|
+
result=f"Loaded {name}",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
return trace
|
|
97
|
+
|
|
98
|
+
def add_trace(
|
|
99
|
+
self,
|
|
100
|
+
name: str,
|
|
101
|
+
trace: Any,
|
|
102
|
+
) -> None:
|
|
103
|
+
"""Add an in-memory trace to the session.
|
|
104
|
+
|
|
105
|
+
This method allows adding traces that were created programmatically
|
|
106
|
+
or loaded separately, rather than loading from a file.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
name: Name for the trace in the session.
|
|
110
|
+
trace: Trace object (WaveformTrace, DigitalTrace, etc.).
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
ValueError: If name is empty or already exists.
|
|
114
|
+
TypeError: If trace doesn't have expected attributes.
|
|
115
|
+
|
|
116
|
+
Example:
|
|
117
|
+
>>> session = Session()
|
|
118
|
+
>>> data = np.sin(np.linspace(0, 2*np.pi, 1000))
|
|
119
|
+
>>> trace = tk.WaveformTrace(data=data, metadata=tk.TraceMetadata(sample_rate=1e6))
|
|
120
|
+
>>> session.add_trace("my_trace", trace)
|
|
121
|
+
"""
|
|
122
|
+
if not name:
|
|
123
|
+
raise ValueError("Trace name cannot be empty")
|
|
124
|
+
|
|
125
|
+
if not hasattr(trace, "data"):
|
|
126
|
+
raise TypeError("Trace must have a 'data' attribute")
|
|
127
|
+
|
|
128
|
+
self.traces[name] = trace
|
|
129
|
+
self._mark_modified()
|
|
130
|
+
|
|
131
|
+
self.history.record(
|
|
132
|
+
"add_trace",
|
|
133
|
+
{"name": name, "type": type(trace).__name__},
|
|
134
|
+
result=f"Added {name}",
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
def remove_trace(self, name: str) -> None:
|
|
138
|
+
"""Remove a trace from the session.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
name: Name of the trace to remove.
|
|
142
|
+
|
|
143
|
+
Raises:
|
|
144
|
+
KeyError: If trace not found.
|
|
145
|
+
"""
|
|
146
|
+
if name not in self.traces:
|
|
147
|
+
raise KeyError(f"Trace '{name}' not found in session")
|
|
148
|
+
|
|
149
|
+
del self.traces[name]
|
|
150
|
+
self._mark_modified()
|
|
151
|
+
|
|
152
|
+
self.history.record(
|
|
153
|
+
"remove_trace",
|
|
154
|
+
{"name": name},
|
|
155
|
+
result=f"Removed {name}",
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
def get_trace(self, name: str) -> Any:
|
|
159
|
+
"""Get trace by name.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
name: Trace name.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Trace object.
|
|
166
|
+
"""
|
|
167
|
+
return self.traces[name]
|
|
168
|
+
|
|
169
|
+
def list_traces(self) -> list[str]:
|
|
170
|
+
"""List all trace names."""
|
|
171
|
+
return list(self.traces.keys())
|
|
172
|
+
|
|
173
|
+
def annotate(
|
|
174
|
+
self,
|
|
175
|
+
text: str,
|
|
176
|
+
*,
|
|
177
|
+
time: float | None = None,
|
|
178
|
+
time_range: tuple[float, float] | None = None,
|
|
179
|
+
layer: str = "default",
|
|
180
|
+
**kwargs: Any,
|
|
181
|
+
) -> None:
|
|
182
|
+
"""Add annotation to session.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
text: Annotation text.
|
|
186
|
+
time: Time point for annotation.
|
|
187
|
+
time_range: Time range for annotation.
|
|
188
|
+
layer: Annotation layer name.
|
|
189
|
+
**kwargs: Additional annotation parameters.
|
|
190
|
+
"""
|
|
191
|
+
if layer not in self.annotation_layers:
|
|
192
|
+
self.annotation_layers[layer] = AnnotationLayer(layer)
|
|
193
|
+
|
|
194
|
+
self.annotation_layers[layer].add(
|
|
195
|
+
text=text,
|
|
196
|
+
time=time,
|
|
197
|
+
time_range=time_range,
|
|
198
|
+
**kwargs,
|
|
199
|
+
)
|
|
200
|
+
self._mark_modified()
|
|
201
|
+
|
|
202
|
+
self.history.record(
|
|
203
|
+
"annotate",
|
|
204
|
+
{"text": text, "time": time, "layer": layer},
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
def get_annotations(
|
|
208
|
+
self,
|
|
209
|
+
layer: str | None = None,
|
|
210
|
+
time_range: tuple[float, float] | None = None,
|
|
211
|
+
) -> list[Any]:
|
|
212
|
+
"""Get annotations.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
layer: Filter by layer name (None for all layers).
|
|
216
|
+
time_range: Filter by time range.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
List of annotations.
|
|
220
|
+
"""
|
|
221
|
+
annotations = []
|
|
222
|
+
|
|
223
|
+
layers = [self.annotation_layers[layer]] if layer else self.annotation_layers.values()
|
|
224
|
+
|
|
225
|
+
for ann_layer in layers:
|
|
226
|
+
if time_range:
|
|
227
|
+
annotations.extend(ann_layer.find_in_range(time_range[0], time_range[1]))
|
|
228
|
+
else:
|
|
229
|
+
annotations.extend(ann_layer.annotations)
|
|
230
|
+
|
|
231
|
+
return annotations
|
|
232
|
+
|
|
233
|
+
def record_measurement(
|
|
234
|
+
self,
|
|
235
|
+
name: str,
|
|
236
|
+
value: Any,
|
|
237
|
+
unit: str = "",
|
|
238
|
+
trace_name: str | None = None,
|
|
239
|
+
**metadata: Any,
|
|
240
|
+
) -> None:
|
|
241
|
+
"""Record a measurement result.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
name: Measurement name (e.g., 'rise_time').
|
|
245
|
+
value: Measurement value.
|
|
246
|
+
unit: Unit of measurement.
|
|
247
|
+
trace_name: Associated trace name.
|
|
248
|
+
**metadata: Additional metadata.
|
|
249
|
+
"""
|
|
250
|
+
self.measurements[name] = {
|
|
251
|
+
"value": value,
|
|
252
|
+
"unit": unit,
|
|
253
|
+
"trace": trace_name,
|
|
254
|
+
"timestamp": datetime.now().isoformat(),
|
|
255
|
+
**metadata,
|
|
256
|
+
}
|
|
257
|
+
self._mark_modified()
|
|
258
|
+
|
|
259
|
+
self.history.record(
|
|
260
|
+
f"measure_{name}",
|
|
261
|
+
{"trace": trace_name},
|
|
262
|
+
result=f"{value} {unit}".strip(),
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
def get_measurements(self) -> dict[str, Any]:
|
|
266
|
+
"""Get all recorded measurements."""
|
|
267
|
+
return self.measurements.copy()
|
|
268
|
+
|
|
269
|
+
def save(
|
|
270
|
+
self,
|
|
271
|
+
path: str | Path | None = None,
|
|
272
|
+
*,
|
|
273
|
+
include_traces: bool = True,
|
|
274
|
+
compress: bool = True,
|
|
275
|
+
) -> Path:
|
|
276
|
+
"""Save session to file.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
path: Output path (default: use existing or generate).
|
|
280
|
+
include_traces: Include trace data in session file.
|
|
281
|
+
compress: Compress session file with gzip.
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
Path to saved file.
|
|
285
|
+
|
|
286
|
+
Example:
|
|
287
|
+
>>> session.save('analysis.tks')
|
|
288
|
+
|
|
289
|
+
Security Note:
|
|
290
|
+
Session files use pickle serialization for flexibility. Share
|
|
291
|
+
session files only with trusted parties. For secure data exchange
|
|
292
|
+
with untrusted parties, use JSON or HDF5 export formats instead.
|
|
293
|
+
"""
|
|
294
|
+
if path is None:
|
|
295
|
+
path = self._file_path or Path(f"{self.name.replace(' ', '_')}.tks")
|
|
296
|
+
else:
|
|
297
|
+
path = Path(path)
|
|
298
|
+
|
|
299
|
+
self._file_path = path
|
|
300
|
+
self._mark_modified()
|
|
301
|
+
|
|
302
|
+
# Build session data
|
|
303
|
+
data = self._to_dict(include_traces=include_traces)
|
|
304
|
+
|
|
305
|
+
# Serialize
|
|
306
|
+
if compress:
|
|
307
|
+
with gzip.open(path, "wb") as f:
|
|
308
|
+
pickle.dump(data, f)
|
|
309
|
+
else:
|
|
310
|
+
with open(path, "wb") as f:
|
|
311
|
+
pickle.dump(data, f)
|
|
312
|
+
|
|
313
|
+
self.history.record("save", {"path": str(path)})
|
|
314
|
+
|
|
315
|
+
return path
|
|
316
|
+
|
|
317
|
+
def _to_dict(self, include_traces: bool = True) -> dict[str, Any]:
|
|
318
|
+
"""Convert session to dictionary."""
|
|
319
|
+
data: dict[str, Any] = {
|
|
320
|
+
"version": "1.0",
|
|
321
|
+
"name": self.name,
|
|
322
|
+
"created_at": self.created_at.isoformat(),
|
|
323
|
+
"modified_at": self.modified_at.isoformat(),
|
|
324
|
+
"annotation_layers": {
|
|
325
|
+
name: layer.to_dict() for name, layer in self.annotation_layers.items()
|
|
326
|
+
},
|
|
327
|
+
"measurements": self.measurements,
|
|
328
|
+
"history": self.history.to_dict(),
|
|
329
|
+
"metadata": self.metadata,
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if include_traces:
|
|
333
|
+
# Store traces with their data
|
|
334
|
+
data["traces"] = {}
|
|
335
|
+
for name, trace in self.traces.items():
|
|
336
|
+
trace_data = {
|
|
337
|
+
"type": type(trace).__name__,
|
|
338
|
+
"data": trace.data.tolist() if hasattr(trace, "data") else None,
|
|
339
|
+
"sample_rate": (
|
|
340
|
+
trace.metadata.sample_rate if hasattr(trace, "metadata") else None
|
|
341
|
+
),
|
|
342
|
+
}
|
|
343
|
+
data["traces"][name] = trace_data
|
|
344
|
+
else:
|
|
345
|
+
data["traces"] = {}
|
|
346
|
+
|
|
347
|
+
return data
|
|
348
|
+
|
|
349
|
+
@classmethod
|
|
350
|
+
def _from_dict(cls, data: dict[str, Any]) -> Session:
|
|
351
|
+
"""Create session from dictionary."""
|
|
352
|
+
session = cls(
|
|
353
|
+
name=data.get("name", "Untitled Session"),
|
|
354
|
+
metadata=data.get("metadata", {}),
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
if "created_at" in data:
|
|
358
|
+
session.created_at = datetime.fromisoformat(data["created_at"])
|
|
359
|
+
if "modified_at" in data:
|
|
360
|
+
session.modified_at = datetime.fromisoformat(data["modified_at"])
|
|
361
|
+
|
|
362
|
+
# Restore annotation layers
|
|
363
|
+
for name, layer_data in data.get("annotation_layers", {}).items():
|
|
364
|
+
session.annotation_layers[name] = AnnotationLayer.from_dict(layer_data)
|
|
365
|
+
|
|
366
|
+
# Restore measurements
|
|
367
|
+
session.measurements = data.get("measurements", {})
|
|
368
|
+
|
|
369
|
+
# Restore history
|
|
370
|
+
if "history" in data:
|
|
371
|
+
session.history = OperationHistory.from_dict(data["history"])
|
|
372
|
+
|
|
373
|
+
# Restore traces (if included)
|
|
374
|
+
if "traces" in data:
|
|
375
|
+
from oscura.core.types import WaveformTrace
|
|
376
|
+
|
|
377
|
+
for name, trace_data in data["traces"].items():
|
|
378
|
+
if trace_data.get("data") is not None:
|
|
379
|
+
session.traces[name] = WaveformTrace( # type: ignore[call-arg]
|
|
380
|
+
data=np.array(trace_data["data"]),
|
|
381
|
+
sample_rate=trace_data.get("sample_rate", 1.0),
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
return session
|
|
385
|
+
|
|
386
|
+
def _mark_modified(self) -> None:
|
|
387
|
+
"""Update modification timestamp."""
|
|
388
|
+
self.modified_at = datetime.now()
|
|
389
|
+
|
|
390
|
+
def summary(self) -> str:
|
|
391
|
+
"""Get session summary."""
|
|
392
|
+
lines = [
|
|
393
|
+
f"Session: {self.name}",
|
|
394
|
+
f"Created: {self.created_at.strftime('%Y-%m-%d %H:%M')}",
|
|
395
|
+
f"Modified: {self.modified_at.strftime('%Y-%m-%d %H:%M')}",
|
|
396
|
+
f"Traces: {len(self.traces)}",
|
|
397
|
+
f"Annotations: {sum(len(l.annotations) for l in self.annotation_layers.values())}", # noqa: E741
|
|
398
|
+
f"Measurements: {len(self.measurements)}",
|
|
399
|
+
f"History entries: {len(self.history.entries)}",
|
|
400
|
+
]
|
|
401
|
+
return "\n".join(lines)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def load_session(path: str | Path) -> Session:
|
|
405
|
+
"""Load session from file.
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
path: Path to session file (.tks).
|
|
409
|
+
|
|
410
|
+
Returns:
|
|
411
|
+
Loaded Session object.
|
|
412
|
+
|
|
413
|
+
Example:
|
|
414
|
+
>>> session = load_session('debug_session.tks')
|
|
415
|
+
>>> print(session.list_traces())
|
|
416
|
+
|
|
417
|
+
Security Warning:
|
|
418
|
+
Session files use pickle serialization. Only load session files from
|
|
419
|
+
trusted sources. Loading a malicious .tks file could execute arbitrary
|
|
420
|
+
code. Never load session files from untrusted or unknown sources.
|
|
421
|
+
|
|
422
|
+
For secure data exchange, consider exporting to JSON or HDF5 formats
|
|
423
|
+
instead of using pickle-based session files.
|
|
424
|
+
"""
|
|
425
|
+
path = Path(path)
|
|
426
|
+
|
|
427
|
+
try:
|
|
428
|
+
# Try gzip compressed first
|
|
429
|
+
with gzip.open(path, "rb") as f:
|
|
430
|
+
data = pickle.load(f)
|
|
431
|
+
except gzip.BadGzipFile:
|
|
432
|
+
# Fall back to uncompressed
|
|
433
|
+
with open(path, "rb") as f:
|
|
434
|
+
data = pickle.load(f)
|
|
435
|
+
|
|
436
|
+
session = Session._from_dict(data)
|
|
437
|
+
session._file_path = path
|
|
438
|
+
|
|
439
|
+
return session
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
__all__ = [
|
|
443
|
+
"Session",
|
|
444
|
+
"load_session",
|
|
445
|
+
]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Streaming APIs for memory-efficient large file processing.
|
|
2
|
+
|
|
3
|
+
This package provides chunk-by-chunk processing capabilities for huge
|
|
4
|
+
waveform files that don't fit in memory, plus real-time streaming APIs.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .chunked import (
|
|
8
|
+
StreamingAnalyzer,
|
|
9
|
+
chunked_fft,
|
|
10
|
+
chunked_spectrogram,
|
|
11
|
+
load_trace_chunks,
|
|
12
|
+
)
|
|
13
|
+
from .progressive import (
|
|
14
|
+
ProgressiveAnalyzer,
|
|
15
|
+
StreamingConfig,
|
|
16
|
+
StreamingProgress,
|
|
17
|
+
create_progressive_analyzer,
|
|
18
|
+
)
|
|
19
|
+
from .realtime import (
|
|
20
|
+
RealtimeAnalyzer,
|
|
21
|
+
RealtimeBuffer,
|
|
22
|
+
RealtimeConfig,
|
|
23
|
+
RealtimeSource,
|
|
24
|
+
RealtimeStream,
|
|
25
|
+
SimulatedSource,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"ProgressiveAnalyzer",
|
|
30
|
+
"RealtimeAnalyzer",
|
|
31
|
+
"RealtimeBuffer",
|
|
32
|
+
"RealtimeConfig",
|
|
33
|
+
"RealtimeSource",
|
|
34
|
+
"RealtimeStream",
|
|
35
|
+
"SimulatedSource",
|
|
36
|
+
"StreamingAnalyzer",
|
|
37
|
+
"StreamingConfig",
|
|
38
|
+
"StreamingProgress",
|
|
39
|
+
"chunked_fft",
|
|
40
|
+
"chunked_spectrogram",
|
|
41
|
+
"create_progressive_analyzer",
|
|
42
|
+
"load_trace_chunks",
|
|
43
|
+
]
|