oscura 0.5.1__py3-none-any.whl → 0.7.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 +169 -167
- oscura/analyzers/__init__.py +3 -0
- oscura/analyzers/classification.py +659 -0
- oscura/analyzers/digital/edges.py +325 -65
- oscura/analyzers/digital/quality.py +293 -166
- oscura/analyzers/digital/timing.py +260 -115
- oscura/analyzers/digital/timing_numba.py +334 -0
- oscura/analyzers/entropy.py +605 -0
- oscura/analyzers/eye/diagram.py +176 -109
- oscura/analyzers/eye/metrics.py +5 -5
- oscura/analyzers/jitter/__init__.py +6 -4
- oscura/analyzers/jitter/ber.py +52 -52
- oscura/analyzers/jitter/classification.py +156 -0
- oscura/analyzers/jitter/decomposition.py +163 -113
- oscura/analyzers/jitter/spectrum.py +80 -64
- oscura/analyzers/ml/__init__.py +39 -0
- oscura/analyzers/ml/features.py +600 -0
- oscura/analyzers/ml/signal_classifier.py +604 -0
- oscura/analyzers/packet/daq.py +246 -158
- oscura/analyzers/packet/parser.py +12 -1
- oscura/analyzers/packet/payload.py +50 -2110
- oscura/analyzers/packet/payload_analysis.py +361 -181
- oscura/analyzers/packet/payload_patterns.py +133 -70
- oscura/analyzers/packet/stream.py +84 -23
- oscura/analyzers/patterns/__init__.py +26 -5
- oscura/analyzers/patterns/anomaly_detection.py +908 -0
- oscura/analyzers/patterns/clustering.py +169 -108
- oscura/analyzers/patterns/clustering_optimized.py +227 -0
- oscura/analyzers/patterns/discovery.py +1 -1
- oscura/analyzers/patterns/matching.py +581 -197
- oscura/analyzers/patterns/pattern_mining.py +778 -0
- oscura/analyzers/patterns/periodic.py +121 -38
- oscura/analyzers/patterns/sequences.py +175 -78
- oscura/analyzers/power/conduction.py +1 -1
- oscura/analyzers/power/soa.py +6 -6
- oscura/analyzers/power/switching.py +250 -110
- oscura/analyzers/protocol/__init__.py +17 -1
- oscura/analyzers/protocols/base.py +6 -6
- oscura/analyzers/protocols/ble/__init__.py +38 -0
- oscura/analyzers/protocols/ble/analyzer.py +809 -0
- oscura/analyzers/protocols/ble/uuids.py +288 -0
- oscura/analyzers/protocols/can.py +257 -127
- oscura/analyzers/protocols/can_fd.py +107 -80
- oscura/analyzers/protocols/flexray.py +139 -80
- oscura/analyzers/protocols/hdlc.py +93 -58
- oscura/analyzers/protocols/i2c.py +247 -106
- oscura/analyzers/protocols/i2s.py +138 -86
- oscura/analyzers/protocols/industrial/__init__.py +40 -0
- oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
- oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
- oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
- oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
- oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
- oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
- oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
- oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
- oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
- oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
- oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
- oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
- oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
- oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
- oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
- oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
- oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
- oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
- oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
- oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
- oscura/analyzers/protocols/jtag.py +180 -98
- oscura/analyzers/protocols/lin.py +219 -114
- oscura/analyzers/protocols/manchester.py +4 -4
- oscura/analyzers/protocols/onewire.py +253 -149
- oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
- oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
- oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
- oscura/analyzers/protocols/spi.py +192 -95
- oscura/analyzers/protocols/swd.py +321 -167
- oscura/analyzers/protocols/uart.py +267 -125
- oscura/analyzers/protocols/usb.py +235 -131
- oscura/analyzers/side_channel/power.py +17 -12
- oscura/analyzers/signal/__init__.py +15 -0
- oscura/analyzers/signal/timing_analysis.py +1086 -0
- oscura/analyzers/signal_integrity/__init__.py +4 -1
- oscura/analyzers/signal_integrity/sparams.py +2 -19
- oscura/analyzers/spectral/chunked.py +129 -60
- oscura/analyzers/spectral/chunked_fft.py +300 -94
- oscura/analyzers/spectral/chunked_wavelet.py +100 -80
- oscura/analyzers/statistical/checksum.py +376 -217
- oscura/analyzers/statistical/classification.py +229 -107
- oscura/analyzers/statistical/entropy.py +78 -53
- oscura/analyzers/statistics/correlation.py +407 -211
- oscura/analyzers/statistics/outliers.py +2 -2
- oscura/analyzers/statistics/streaming.py +30 -5
- oscura/analyzers/validation.py +216 -101
- oscura/analyzers/waveform/measurements.py +9 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
- oscura/analyzers/waveform/spectral.py +500 -228
- oscura/api/__init__.py +31 -5
- oscura/api/dsl/__init__.py +582 -0
- oscura/{dsl → api/dsl}/commands.py +43 -76
- oscura/{dsl → api/dsl}/interpreter.py +26 -51
- oscura/{dsl → api/dsl}/parser.py +107 -77
- oscura/{dsl → api/dsl}/repl.py +2 -2
- oscura/api/dsl.py +1 -1
- oscura/{integrations → api/integrations}/__init__.py +1 -1
- oscura/{integrations → api/integrations}/llm.py +201 -102
- oscura/api/operators.py +3 -3
- oscura/api/optimization.py +144 -30
- oscura/api/rest_server.py +921 -0
- oscura/api/server/__init__.py +17 -0
- oscura/api/server/dashboard.py +850 -0
- oscura/api/server/static/README.md +34 -0
- oscura/api/server/templates/base.html +181 -0
- oscura/api/server/templates/export.html +120 -0
- oscura/api/server/templates/home.html +284 -0
- oscura/api/server/templates/protocols.html +58 -0
- oscura/api/server/templates/reports.html +43 -0
- oscura/api/server/templates/session_detail.html +89 -0
- oscura/api/server/templates/sessions.html +83 -0
- oscura/api/server/templates/waveforms.html +73 -0
- oscura/automotive/__init__.py +8 -1
- oscura/automotive/can/__init__.py +10 -0
- oscura/automotive/can/checksum.py +3 -1
- oscura/automotive/can/dbc_generator.py +590 -0
- oscura/automotive/can/message_wrapper.py +121 -74
- oscura/automotive/can/patterns.py +98 -21
- oscura/automotive/can/session.py +292 -56
- oscura/automotive/can/state_machine.py +6 -3
- oscura/automotive/can/stimulus_response.py +97 -75
- oscura/automotive/dbc/__init__.py +10 -2
- oscura/automotive/dbc/generator.py +84 -56
- oscura/automotive/dbc/parser.py +6 -6
- oscura/automotive/dtc/data.json +17 -102
- oscura/automotive/dtc/database.py +2 -2
- oscura/automotive/flexray/__init__.py +31 -0
- oscura/automotive/flexray/analyzer.py +504 -0
- oscura/automotive/flexray/crc.py +185 -0
- oscura/automotive/flexray/fibex.py +449 -0
- oscura/automotive/j1939/__init__.py +45 -8
- oscura/automotive/j1939/analyzer.py +605 -0
- oscura/automotive/j1939/spns.py +326 -0
- oscura/automotive/j1939/transport.py +306 -0
- oscura/automotive/lin/__init__.py +47 -0
- oscura/automotive/lin/analyzer.py +612 -0
- oscura/automotive/loaders/blf.py +13 -2
- oscura/automotive/loaders/csv_can.py +143 -72
- oscura/automotive/loaders/dispatcher.py +50 -2
- oscura/automotive/loaders/mdf.py +86 -45
- oscura/automotive/loaders/pcap.py +111 -61
- oscura/automotive/uds/__init__.py +4 -0
- oscura/automotive/uds/analyzer.py +725 -0
- oscura/automotive/uds/decoder.py +140 -58
- oscura/automotive/uds/models.py +7 -1
- oscura/automotive/visualization.py +1 -1
- oscura/cli/analyze.py +348 -0
- oscura/cli/batch.py +142 -122
- oscura/cli/benchmark.py +275 -0
- oscura/cli/characterize.py +137 -82
- oscura/cli/compare.py +224 -131
- oscura/cli/completion.py +250 -0
- oscura/cli/config_cmd.py +361 -0
- oscura/cli/decode.py +164 -87
- oscura/cli/export.py +286 -0
- oscura/cli/main.py +115 -31
- oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
- oscura/{onboarding → cli/onboarding}/help.py +80 -58
- oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
- oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
- oscura/cli/progress.py +147 -0
- oscura/cli/shell.py +157 -135
- oscura/cli/validate_cmd.py +204 -0
- oscura/cli/visualize.py +158 -0
- oscura/convenience.py +125 -79
- oscura/core/__init__.py +4 -2
- oscura/core/backend_selector.py +3 -3
- oscura/core/cache.py +126 -15
- oscura/core/cancellation.py +1 -1
- oscura/{config → core/config}/__init__.py +20 -11
- oscura/{config → core/config}/defaults.py +1 -1
- oscura/{config → core/config}/loader.py +7 -5
- oscura/{config → core/config}/memory.py +5 -5
- oscura/{config → core/config}/migration.py +1 -1
- oscura/{config → core/config}/pipeline.py +99 -23
- oscura/{config → core/config}/preferences.py +1 -1
- oscura/{config → core/config}/protocol.py +3 -3
- oscura/{config → core/config}/schema.py +426 -272
- oscura/{config → core/config}/settings.py +1 -1
- oscura/{config → core/config}/thresholds.py +195 -153
- oscura/core/correlation.py +5 -6
- oscura/core/cross_domain.py +0 -2
- oscura/core/debug.py +9 -5
- oscura/{extensibility → core/extensibility}/docs.py +158 -70
- oscura/{extensibility → core/extensibility}/extensions.py +160 -76
- oscura/{extensibility → core/extensibility}/logging.py +1 -1
- oscura/{extensibility → core/extensibility}/measurements.py +1 -1
- oscura/{extensibility → core/extensibility}/plugins.py +1 -1
- oscura/{extensibility → core/extensibility}/templates.py +73 -3
- oscura/{extensibility → core/extensibility}/validation.py +1 -1
- oscura/core/gpu_backend.py +11 -7
- oscura/core/log_query.py +101 -11
- oscura/core/logging.py +126 -54
- oscura/core/logging_advanced.py +5 -5
- oscura/core/memory_limits.py +108 -70
- oscura/core/memory_monitor.py +2 -2
- oscura/core/memory_progress.py +7 -7
- oscura/core/memory_warnings.py +1 -1
- oscura/core/numba_backend.py +13 -13
- oscura/{plugins → core/plugins}/__init__.py +9 -9
- oscura/{plugins → core/plugins}/base.py +7 -7
- oscura/{plugins → core/plugins}/cli.py +3 -3
- oscura/{plugins → core/plugins}/discovery.py +186 -106
- oscura/{plugins → core/plugins}/lifecycle.py +1 -1
- oscura/{plugins → core/plugins}/manager.py +7 -7
- oscura/{plugins → core/plugins}/registry.py +3 -3
- oscura/{plugins → core/plugins}/versioning.py +1 -1
- oscura/core/progress.py +16 -1
- oscura/core/provenance.py +8 -2
- oscura/{schemas → core/schemas}/__init__.py +2 -2
- oscura/{schemas → core/schemas}/device_mapping.json +2 -8
- oscura/{schemas → core/schemas}/packet_format.json +4 -24
- oscura/{schemas → core/schemas}/protocol_definition.json +2 -12
- oscura/core/types.py +4 -0
- oscura/core/uncertainty.py +3 -3
- oscura/correlation/__init__.py +52 -0
- oscura/correlation/multi_protocol.py +811 -0
- oscura/discovery/auto_decoder.py +117 -35
- oscura/discovery/comparison.py +191 -86
- oscura/discovery/quality_validator.py +155 -68
- oscura/discovery/signal_detector.py +196 -79
- oscura/export/__init__.py +18 -8
- oscura/export/kaitai_struct.py +513 -0
- oscura/export/scapy_layer.py +801 -0
- oscura/export/wireshark/generator.py +1 -1
- oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
- oscura/export/wireshark_dissector.py +746 -0
- oscura/guidance/wizard.py +207 -111
- oscura/hardware/__init__.py +19 -0
- oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
- oscura/{acquisition → hardware/acquisition}/file.py +2 -2
- oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
- oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
- oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
- oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
- oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
- oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
- oscura/hardware/firmware/__init__.py +29 -0
- oscura/hardware/firmware/pattern_recognition.py +874 -0
- oscura/hardware/hal_detector.py +736 -0
- oscura/hardware/security/__init__.py +37 -0
- oscura/hardware/security/side_channel_detector.py +1126 -0
- oscura/inference/__init__.py +4 -0
- oscura/inference/active_learning/observation_table.py +4 -1
- oscura/inference/alignment.py +216 -123
- oscura/inference/bayesian.py +113 -33
- oscura/inference/crc_reverse.py +101 -55
- oscura/inference/logic.py +6 -2
- oscura/inference/message_format.py +342 -183
- oscura/inference/protocol.py +95 -44
- oscura/inference/protocol_dsl.py +180 -82
- oscura/inference/signal_intelligence.py +1439 -706
- oscura/inference/spectral.py +99 -57
- oscura/inference/state_machine.py +810 -158
- oscura/inference/stream.py +270 -110
- oscura/iot/__init__.py +34 -0
- oscura/iot/coap/__init__.py +32 -0
- oscura/iot/coap/analyzer.py +668 -0
- oscura/iot/coap/options.py +212 -0
- oscura/iot/lorawan/__init__.py +21 -0
- oscura/iot/lorawan/crypto.py +206 -0
- oscura/iot/lorawan/decoder.py +801 -0
- oscura/iot/lorawan/mac_commands.py +341 -0
- oscura/iot/mqtt/__init__.py +27 -0
- oscura/iot/mqtt/analyzer.py +999 -0
- oscura/iot/mqtt/properties.py +315 -0
- oscura/iot/zigbee/__init__.py +31 -0
- oscura/iot/zigbee/analyzer.py +615 -0
- oscura/iot/zigbee/security.py +153 -0
- oscura/iot/zigbee/zcl.py +349 -0
- oscura/jupyter/display.py +125 -45
- oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
- oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
- oscura/jupyter/exploratory/fuzzy.py +746 -0
- oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
- oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
- oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
- oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
- oscura/jupyter/exploratory/sync.py +612 -0
- oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
- oscura/jupyter/magic.py +4 -4
- oscura/{ui → jupyter/ui}/__init__.py +2 -2
- oscura/{ui → jupyter/ui}/formatters.py +3 -3
- oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
- oscura/loaders/__init__.py +183 -67
- oscura/loaders/binary.py +88 -1
- oscura/loaders/chipwhisperer.py +153 -137
- oscura/loaders/configurable.py +208 -86
- oscura/loaders/csv_loader.py +458 -215
- oscura/loaders/hdf5_loader.py +278 -119
- oscura/loaders/lazy.py +87 -54
- oscura/loaders/mmap_loader.py +1 -1
- oscura/loaders/numpy_loader.py +253 -116
- oscura/loaders/pcap.py +226 -151
- oscura/loaders/rigol.py +110 -49
- oscura/loaders/sigrok.py +201 -78
- oscura/loaders/tdms.py +81 -58
- oscura/loaders/tektronix.py +291 -174
- oscura/loaders/touchstone.py +182 -87
- oscura/loaders/tss.py +456 -0
- oscura/loaders/vcd.py +215 -117
- oscura/loaders/wav.py +155 -68
- oscura/reporting/__init__.py +9 -0
- oscura/reporting/analyze.py +352 -146
- oscura/reporting/argument_preparer.py +69 -14
- oscura/reporting/auto_report.py +97 -61
- oscura/reporting/batch.py +131 -58
- oscura/reporting/chart_selection.py +57 -45
- oscura/reporting/comparison.py +63 -17
- oscura/reporting/content/executive.py +76 -24
- oscura/reporting/core_formats/multi_format.py +11 -8
- oscura/reporting/engine.py +312 -158
- oscura/reporting/enhanced_reports.py +949 -0
- oscura/reporting/export.py +86 -43
- oscura/reporting/formatting/numbers.py +69 -42
- oscura/reporting/html.py +139 -58
- oscura/reporting/index.py +137 -65
- oscura/reporting/output.py +158 -67
- oscura/reporting/pdf.py +67 -102
- oscura/reporting/plots.py +191 -112
- oscura/reporting/sections.py +88 -47
- oscura/reporting/standards.py +104 -61
- oscura/reporting/summary_generator.py +75 -55
- oscura/reporting/tables.py +138 -54
- oscura/reporting/templates/enhanced/protocol_re.html +525 -0
- oscura/sessions/__init__.py +14 -23
- oscura/sessions/base.py +3 -3
- oscura/sessions/blackbox.py +106 -10
- oscura/sessions/generic.py +2 -2
- oscura/sessions/legacy.py +783 -0
- oscura/side_channel/__init__.py +63 -0
- oscura/side_channel/dpa.py +1025 -0
- oscura/utils/__init__.py +15 -1
- oscura/utils/bitwise.py +118 -0
- oscura/{builders → utils/builders}/__init__.py +1 -1
- oscura/{comparison → utils/comparison}/__init__.py +6 -6
- oscura/{comparison → utils/comparison}/compare.py +202 -101
- oscura/{comparison → utils/comparison}/golden.py +83 -63
- oscura/{comparison → utils/comparison}/limits.py +313 -89
- oscura/{comparison → utils/comparison}/mask.py +151 -45
- oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
- oscura/{comparison → utils/comparison}/visualization.py +147 -89
- oscura/{component → utils/component}/__init__.py +3 -3
- oscura/{component → utils/component}/impedance.py +122 -58
- oscura/{component → utils/component}/reactive.py +165 -168
- oscura/{component → utils/component}/transmission_line.py +3 -3
- oscura/{filtering → utils/filtering}/__init__.py +6 -6
- oscura/{filtering → utils/filtering}/base.py +1 -1
- oscura/{filtering → utils/filtering}/convenience.py +2 -2
- oscura/{filtering → utils/filtering}/design.py +169 -93
- oscura/{filtering → utils/filtering}/filters.py +2 -2
- oscura/{filtering → utils/filtering}/introspection.py +2 -2
- oscura/utils/geometry.py +31 -0
- oscura/utils/imports.py +184 -0
- oscura/utils/lazy.py +1 -1
- oscura/{math → utils/math}/__init__.py +2 -2
- oscura/{math → utils/math}/arithmetic.py +114 -48
- oscura/{math → utils/math}/interpolation.py +139 -106
- oscura/utils/memory.py +129 -66
- oscura/utils/memory_advanced.py +92 -9
- oscura/utils/memory_extensions.py +10 -8
- oscura/{optimization → utils/optimization}/__init__.py +1 -1
- oscura/{optimization → utils/optimization}/search.py +2 -2
- oscura/utils/performance/__init__.py +58 -0
- oscura/utils/performance/caching.py +889 -0
- oscura/utils/performance/lsh_clustering.py +333 -0
- oscura/utils/performance/memory_optimizer.py +699 -0
- oscura/utils/performance/optimizations.py +675 -0
- oscura/utils/performance/parallel.py +654 -0
- oscura/utils/performance/profiling.py +661 -0
- oscura/{pipeline → utils/pipeline}/base.py +1 -1
- oscura/{pipeline → utils/pipeline}/composition.py +1 -1
- oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
- oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
- oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
- oscura/{search → utils/search}/__init__.py +3 -3
- oscura/{search → utils/search}/anomaly.py +188 -58
- oscura/utils/search/context.py +294 -0
- oscura/{search → utils/search}/pattern.py +138 -10
- oscura/utils/serial.py +51 -0
- oscura/utils/storage/__init__.py +61 -0
- oscura/utils/storage/database.py +1166 -0
- oscura/{streaming → utils/streaming}/chunked.py +302 -143
- oscura/{streaming → utils/streaming}/progressive.py +1 -1
- oscura/{streaming → utils/streaming}/realtime.py +3 -2
- oscura/{triggering → utils/triggering}/__init__.py +6 -6
- oscura/{triggering → utils/triggering}/base.py +6 -6
- oscura/{triggering → utils/triggering}/edge.py +2 -2
- oscura/{triggering → utils/triggering}/pattern.py +2 -2
- oscura/{triggering → utils/triggering}/pulse.py +115 -74
- oscura/{triggering → utils/triggering}/window.py +2 -2
- oscura/utils/validation.py +32 -0
- oscura/validation/__init__.py +121 -0
- oscura/{compliance → validation/compliance}/__init__.py +5 -5
- oscura/{compliance → validation/compliance}/advanced.py +5 -5
- oscura/{compliance → validation/compliance}/masks.py +1 -1
- oscura/{compliance → validation/compliance}/reporting.py +127 -53
- oscura/{compliance → validation/compliance}/testing.py +114 -52
- oscura/validation/compliance_tests.py +915 -0
- oscura/validation/fuzzer.py +990 -0
- oscura/validation/grammar_tests.py +596 -0
- oscura/validation/grammar_validator.py +904 -0
- oscura/validation/hil_testing.py +977 -0
- oscura/{quality → validation/quality}/__init__.py +4 -4
- oscura/{quality → validation/quality}/ensemble.py +251 -171
- oscura/{quality → validation/quality}/explainer.py +3 -3
- oscura/{quality → validation/quality}/scoring.py +1 -1
- oscura/{quality → validation/quality}/warnings.py +4 -4
- oscura/validation/regression_suite.py +808 -0
- oscura/validation/replay.py +788 -0
- oscura/{testing → validation/testing}/__init__.py +2 -2
- oscura/{testing → validation/testing}/synthetic.py +5 -5
- oscura/visualization/__init__.py +9 -0
- oscura/visualization/accessibility.py +1 -1
- oscura/visualization/annotations.py +64 -67
- oscura/visualization/colors.py +7 -7
- oscura/visualization/digital.py +180 -81
- oscura/visualization/eye.py +236 -85
- oscura/visualization/interactive.py +320 -143
- oscura/visualization/jitter.py +587 -247
- oscura/visualization/layout.py +169 -134
- oscura/visualization/optimization.py +103 -52
- oscura/visualization/palettes.py +1 -1
- oscura/visualization/power.py +427 -211
- oscura/visualization/power_extended.py +626 -297
- oscura/visualization/presets.py +2 -0
- oscura/visualization/protocols.py +495 -181
- oscura/visualization/render.py +79 -63
- oscura/visualization/reverse_engineering.py +171 -124
- oscura/visualization/signal_integrity.py +460 -279
- oscura/visualization/specialized.py +190 -100
- oscura/visualization/spectral.py +670 -255
- oscura/visualization/thumbnails.py +166 -137
- oscura/visualization/waveform.py +150 -63
- oscura/workflows/__init__.py +3 -0
- oscura/{batch → workflows/batch}/__init__.py +5 -5
- oscura/{batch → workflows/batch}/advanced.py +150 -75
- oscura/workflows/batch/aggregate.py +531 -0
- oscura/workflows/batch/analyze.py +236 -0
- oscura/{batch → workflows/batch}/logging.py +2 -2
- oscura/{batch → workflows/batch}/metrics.py +1 -1
- oscura/workflows/complete_re.py +1144 -0
- oscura/workflows/compliance.py +44 -54
- oscura/workflows/digital.py +197 -51
- oscura/workflows/legacy/__init__.py +12 -0
- oscura/{workflow → workflows/legacy}/dag.py +4 -1
- oscura/workflows/multi_trace.py +9 -9
- oscura/workflows/power.py +42 -62
- oscura/workflows/protocol.py +82 -49
- oscura/workflows/reverse_engineering.py +351 -150
- oscura/workflows/signal_integrity.py +157 -82
- oscura-0.7.0.dist-info/METADATA +661 -0
- oscura-0.7.0.dist-info/RECORD +591 -0
- oscura/batch/aggregate.py +0 -300
- oscura/batch/analyze.py +0 -139
- oscura/dsl/__init__.py +0 -73
- oscura/exceptions.py +0 -59
- oscura/exploratory/fuzzy.py +0 -513
- oscura/exploratory/sync.py +0 -384
- oscura/exporters/__init__.py +0 -94
- oscura/exporters/csv.py +0 -303
- oscura/exporters/exporters.py +0 -44
- oscura/exporters/hdf5.py +0 -217
- oscura/exporters/html_export.py +0 -701
- oscura/exporters/json_export.py +0 -291
- oscura/exporters/markdown_export.py +0 -367
- oscura/exporters/matlab_export.py +0 -354
- oscura/exporters/npz_export.py +0 -219
- oscura/exporters/spice_export.py +0 -210
- oscura/search/context.py +0 -149
- oscura/session/__init__.py +0 -34
- oscura/session/annotations.py +0 -289
- oscura/session/history.py +0 -313
- oscura/session/session.py +0 -520
- oscura/workflow/__init__.py +0 -13
- oscura-0.5.1.dist-info/METADATA +0 -583
- oscura-0.5.1.dist-info/RECORD +0 -481
- /oscura/core/{config.py → config/legacy.py} +0 -0
- /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
- /oscura/{extensibility → core/extensibility}/registry.py +0 -0
- /oscura/{plugins → core/plugins}/isolation.py +0 -0
- /oscura/{schemas → core/schemas}/bus_configuration.json +0 -0
- /oscura/{builders → utils/builders}/signal_builder.py +0 -0
- /oscura/{optimization → utils/optimization}/parallel.py +0 -0
- /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
- /oscura/{streaming → utils/streaming}/__init__.py +0 -0
- {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/WHEEL +0 -0
- {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/entry_points.txt +0 -0
- {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/licenses/LICENSE +0 -0
oscura/guidance/wizard.py
CHANGED
|
@@ -181,40 +181,22 @@ class AnalysisWizard:
|
|
|
181
181
|
"""
|
|
182
182
|
self._predefined_answers = answers
|
|
183
183
|
|
|
184
|
-
def
|
|
185
|
-
|
|
186
|
-
*,
|
|
187
|
-
preview_callback: Callable[[Any], None] | None = None,
|
|
188
|
-
) -> WizardResult:
|
|
189
|
-
"""Run the analysis wizard.
|
|
190
|
-
|
|
191
|
-
Guides user through analysis steps, auto-skipping where confident,
|
|
192
|
-
showing progress, and providing live previews.
|
|
184
|
+
def _execute_characterization_step(self, preview_callback: Callable[[Any], None] | None) -> Any:
|
|
185
|
+
"""Execute signal characterization step.
|
|
193
186
|
|
|
194
187
|
Args:
|
|
195
|
-
preview_callback: Optional callback for step
|
|
188
|
+
preview_callback: Optional callback for step preview.
|
|
196
189
|
|
|
197
190
|
Returns:
|
|
198
|
-
|
|
191
|
+
Characterization result object with signal_type and confidence.
|
|
199
192
|
|
|
200
193
|
Example:
|
|
201
|
-
>>>
|
|
202
|
-
>>> result
|
|
203
|
-
>>> print(result.summary)
|
|
204
|
-
|
|
205
|
-
References:
|
|
206
|
-
DISC-006: Interactive Analysis Wizard
|
|
194
|
+
>>> result = wizard._execute_characterization_step(None)
|
|
195
|
+
>>> print(result.signal_type)
|
|
207
196
|
"""
|
|
208
|
-
from oscura.discovery import
|
|
209
|
-
assess_data_quality,
|
|
210
|
-
characterize_signal,
|
|
211
|
-
decode_protocol,
|
|
212
|
-
find_anomalies,
|
|
213
|
-
)
|
|
214
|
-
from oscura.guidance import suggest_next_steps
|
|
197
|
+
from oscura.discovery import characterize_signal
|
|
215
198
|
|
|
216
|
-
|
|
217
|
-
step1 = WizardStep(
|
|
199
|
+
step = WizardStep(
|
|
218
200
|
number=1,
|
|
219
201
|
id="characterization",
|
|
220
202
|
question="What type of signal are you analyzing?",
|
|
@@ -228,31 +210,46 @@ class AnalysisWizard:
|
|
|
228
210
|
skip_if_confident=False,
|
|
229
211
|
)
|
|
230
212
|
|
|
231
|
-
# Always do auto-characterization first
|
|
232
213
|
char_result = characterize_signal(self.trace)
|
|
233
|
-
|
|
234
|
-
|
|
214
|
+
step.confidence_before = 0.0
|
|
215
|
+
step.confidence_after = char_result.confidence
|
|
235
216
|
self._current_state["characterization"] = char_result
|
|
236
217
|
|
|
237
218
|
if self.interactive and char_result.confidence < self.auto_detect_threshold:
|
|
238
|
-
|
|
239
|
-
step1.user_response = self._predefined_answers.get("signal_type", step1.default)
|
|
219
|
+
step.user_response = self._predefined_answers.get("signal_type", step.default)
|
|
240
220
|
self.questions_asked += 1
|
|
241
221
|
else:
|
|
242
|
-
# Auto-detected with high confidence
|
|
243
222
|
signal_type = getattr(char_result, "signal_type", "Unknown")
|
|
244
|
-
|
|
223
|
+
step.user_response = f"Auto-detected: {signal_type}"
|
|
245
224
|
self.questions_skipped += 1
|
|
246
225
|
|
|
247
|
-
self.step_history.append(
|
|
226
|
+
self.step_history.append(step)
|
|
248
227
|
self.steps_completed += 1
|
|
249
228
|
|
|
250
|
-
# Preview callback
|
|
251
229
|
if preview_callback and self.enable_preview:
|
|
252
230
|
preview_callback(char_result)
|
|
253
231
|
|
|
254
|
-
|
|
255
|
-
|
|
232
|
+
return char_result
|
|
233
|
+
|
|
234
|
+
def _execute_quality_assessment_step(
|
|
235
|
+
self, char_result: Any, preview_callback: Callable[[Any], None] | None
|
|
236
|
+
) -> Any:
|
|
237
|
+
"""Execute data quality assessment step.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
char_result: Result from characterization step.
|
|
241
|
+
preview_callback: Optional callback for step preview.
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
Quality assessment result with status and confidence.
|
|
245
|
+
|
|
246
|
+
Example:
|
|
247
|
+
>>> quality = wizard._execute_quality_assessment_step(char_result, None)
|
|
248
|
+
>>> print(quality.status)
|
|
249
|
+
"""
|
|
250
|
+
from oscura.discovery import assess_data_quality
|
|
251
|
+
|
|
252
|
+
step = WizardStep(
|
|
256
253
|
number=2,
|
|
257
254
|
id="quality",
|
|
258
255
|
question="Check data quality?",
|
|
@@ -262,98 +259,159 @@ class AnalysisWizard:
|
|
|
262
259
|
)
|
|
263
260
|
|
|
264
261
|
quality = assess_data_quality(self.trace)
|
|
265
|
-
|
|
266
|
-
|
|
262
|
+
step.confidence_before = char_result.confidence
|
|
263
|
+
step.confidence_after = quality.confidence
|
|
267
264
|
self._current_state["quality"] = quality
|
|
268
265
|
|
|
269
266
|
if self.interactive and self.questions_asked < self.max_questions:
|
|
270
|
-
|
|
271
|
-
if
|
|
267
|
+
step.user_response = self._predefined_answers.get("check_quality", "Yes")
|
|
268
|
+
if step.user_response == "Yes":
|
|
272
269
|
self.questions_asked += 1
|
|
273
270
|
else:
|
|
274
|
-
|
|
271
|
+
step.user_response = "Skipped (auto-assessed)"
|
|
275
272
|
self.questions_skipped += 1
|
|
276
273
|
|
|
277
|
-
self.step_history.append(
|
|
274
|
+
self.step_history.append(step)
|
|
278
275
|
self.steps_completed += 1
|
|
279
276
|
|
|
280
277
|
if preview_callback and self.enable_preview:
|
|
281
278
|
preview_callback(quality)
|
|
282
279
|
|
|
283
|
-
|
|
280
|
+
return quality
|
|
281
|
+
|
|
282
|
+
def _execute_protocol_decode_step(
|
|
283
|
+
self, char_result: Any, preview_callback: Callable[[Any], None] | None
|
|
284
|
+
) -> Any | None:
|
|
285
|
+
"""Execute protocol decoding step if applicable.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
char_result: Result from characterization step.
|
|
289
|
+
preview_callback: Optional callback for step preview.
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
Decode result if protocol detected, None otherwise.
|
|
293
|
+
|
|
294
|
+
Example:
|
|
295
|
+
>>> decode = wizard._execute_protocol_decode_step(char_result, None)
|
|
296
|
+
>>> if decode:
|
|
297
|
+
... print(len(decode.data))
|
|
298
|
+
"""
|
|
299
|
+
from oscura.discovery import decode_protocol
|
|
300
|
+
|
|
284
301
|
decode_result = None
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
302
|
+
|
|
303
|
+
if not (hasattr(char_result, "signal_type") and char_result.confidence >= 0.7):
|
|
304
|
+
return decode_result
|
|
305
|
+
|
|
306
|
+
signal_type = char_result.signal_type.lower()
|
|
307
|
+
if not any(proto in signal_type for proto in ["uart", "spi", "i2c", "can"]):
|
|
308
|
+
return decode_result
|
|
309
|
+
|
|
310
|
+
step = WizardStep(
|
|
311
|
+
number=3,
|
|
312
|
+
id="decode",
|
|
313
|
+
question=f"Auto-detected {char_result.signal_type}. Decode data?",
|
|
314
|
+
options=["Yes", "No"],
|
|
315
|
+
default="Yes",
|
|
316
|
+
skip_if_confident=True,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
if self.interactive and self.questions_asked < self.max_questions:
|
|
320
|
+
step.user_response = self._predefined_answers.get("decode_data", "Yes")
|
|
321
|
+
if step.user_response == "Yes":
|
|
322
|
+
decode_result = decode_protocol(self.trace)
|
|
323
|
+
self._current_state["decode"] = decode_result
|
|
324
|
+
self.questions_asked += 1
|
|
325
|
+
else:
|
|
326
|
+
decode_result = decode_protocol(self.trace)
|
|
327
|
+
self._current_state["decode"] = decode_result
|
|
328
|
+
step.user_response = "Auto-decoded"
|
|
329
|
+
self.questions_skipped += 1
|
|
330
|
+
|
|
331
|
+
step.confidence_before = char_result.confidence
|
|
332
|
+
step.confidence_after = decode_result.overall_confidence if decode_result else 0.0
|
|
333
|
+
|
|
334
|
+
self.step_history.append(step)
|
|
335
|
+
self.steps_completed += 1
|
|
336
|
+
|
|
337
|
+
if preview_callback and self.enable_preview and decode_result:
|
|
338
|
+
preview_callback(decode_result)
|
|
339
|
+
|
|
340
|
+
return decode_result
|
|
341
|
+
|
|
342
|
+
def _execute_anomaly_detection_step(
|
|
343
|
+
self, quality: Any, preview_callback: Callable[[Any], None] | None
|
|
344
|
+
) -> Any | None:
|
|
345
|
+
"""Execute anomaly detection step if quality issues exist.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
quality: Result from quality assessment step.
|
|
349
|
+
preview_callback: Optional callback for step preview.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Anomaly detection results if quality issues found, None otherwise.
|
|
353
|
+
|
|
354
|
+
Example:
|
|
355
|
+
>>> anomalies = wizard._execute_anomaly_detection_step(quality, None)
|
|
356
|
+
>>> if anomalies:
|
|
357
|
+
... print(len(anomalies))
|
|
358
|
+
"""
|
|
359
|
+
from oscura.discovery import find_anomalies
|
|
360
|
+
|
|
321
361
|
anomalies = None
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
else:
|
|
338
|
-
# Auto-check
|
|
362
|
+
|
|
363
|
+
if quality.status not in ["WARNING", "FAIL"]:
|
|
364
|
+
return anomalies
|
|
365
|
+
|
|
366
|
+
step = WizardStep(
|
|
367
|
+
number=self.steps_completed + 1,
|
|
368
|
+
id="anomalies",
|
|
369
|
+
question="Quality concerns detected. Check for anomalies?",
|
|
370
|
+
options=["Yes", "No"],
|
|
371
|
+
default="Yes",
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
if self.interactive and self.questions_asked < self.max_questions:
|
|
375
|
+
step.user_response = self._predefined_answers.get("check_anomalies", "Yes")
|
|
376
|
+
if step.user_response == "Yes":
|
|
339
377
|
anomalies = find_anomalies(self.trace)
|
|
340
378
|
self._current_state["anomalies"] = anomalies
|
|
341
|
-
|
|
342
|
-
|
|
379
|
+
self.questions_asked += 1
|
|
380
|
+
else:
|
|
381
|
+
anomalies = find_anomalies(self.trace)
|
|
382
|
+
self._current_state["anomalies"] = anomalies
|
|
383
|
+
step.user_response = "Auto-checked"
|
|
384
|
+
self.questions_skipped += 1
|
|
343
385
|
|
|
344
|
-
|
|
345
|
-
|
|
386
|
+
self.step_history.append(step)
|
|
387
|
+
self.steps_completed += 1
|
|
346
388
|
|
|
347
|
-
|
|
348
|
-
|
|
389
|
+
if preview_callback and self.enable_preview and anomalies:
|
|
390
|
+
preview_callback(anomalies)
|
|
349
391
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
392
|
+
return anomalies
|
|
393
|
+
|
|
394
|
+
def _build_summary(
|
|
395
|
+
self, char_result: Any, quality: Any, decode_result: Any | None, anomalies: Any | None
|
|
396
|
+
) -> str:
|
|
397
|
+
"""Build wizard result summary from analysis steps.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
char_result: Characterization result.
|
|
401
|
+
quality: Quality assessment result.
|
|
402
|
+
decode_result: Protocol decode result (optional).
|
|
403
|
+
anomalies: Anomaly detection results (optional).
|
|
355
404
|
|
|
356
|
-
|
|
405
|
+
Returns:
|
|
406
|
+
Multi-line summary string.
|
|
407
|
+
|
|
408
|
+
Example:
|
|
409
|
+
>>> summary = wizard._build_summary(char, quality, decode, anomalies)
|
|
410
|
+
>>> print(summary)
|
|
411
|
+
Signal type: UART
|
|
412
|
+
Quality: Good
|
|
413
|
+
Decoded: 1024 bytes
|
|
414
|
+
"""
|
|
357
415
|
summary_parts = []
|
|
358
416
|
|
|
359
417
|
if hasattr(char_result, "signal_type") and char_result.signal_type:
|
|
@@ -377,9 +435,47 @@ class AnalysisWizard:
|
|
|
377
435
|
if critical > 0:
|
|
378
436
|
summary_parts.append(f"Anomalies: {critical} critical issues")
|
|
379
437
|
|
|
380
|
-
|
|
438
|
+
return "\n".join(summary_parts)
|
|
439
|
+
|
|
440
|
+
def run(
|
|
441
|
+
self,
|
|
442
|
+
*,
|
|
443
|
+
preview_callback: Callable[[Any], None] | None = None,
|
|
444
|
+
) -> WizardResult:
|
|
445
|
+
"""Run the analysis wizard.
|
|
446
|
+
|
|
447
|
+
Guides user through analysis steps, auto-skipping where confident,
|
|
448
|
+
showing progress, and providing live previews.
|
|
449
|
+
|
|
450
|
+
Args:
|
|
451
|
+
preview_callback: Optional callback for step previews.
|
|
452
|
+
|
|
453
|
+
Returns:
|
|
454
|
+
WizardResult with analysis summary and findings.
|
|
455
|
+
|
|
456
|
+
Example:
|
|
457
|
+
>>> wizard = AnalysisWizard(trace)
|
|
458
|
+
>>> result = wizard.run()
|
|
459
|
+
>>> print(result.summary)
|
|
460
|
+
|
|
461
|
+
References:
|
|
462
|
+
DISC-006: Interactive Analysis Wizard
|
|
463
|
+
"""
|
|
464
|
+
from oscura.guidance import suggest_next_steps
|
|
465
|
+
|
|
466
|
+
# Execute analysis steps
|
|
467
|
+
char_result = self._execute_characterization_step(preview_callback)
|
|
468
|
+
quality = self._execute_quality_assessment_step(char_result, preview_callback)
|
|
469
|
+
decode_result = self._execute_protocol_decode_step(char_result, preview_callback)
|
|
470
|
+
anomalies = self._execute_anomaly_detection_step(quality, preview_callback)
|
|
471
|
+
|
|
472
|
+
# Generate recommendations and results
|
|
473
|
+
recommendations = suggest_next_steps(
|
|
474
|
+
self.trace,
|
|
475
|
+
current_state=self._current_state,
|
|
476
|
+
)
|
|
381
477
|
|
|
382
|
-
|
|
478
|
+
summary = self._build_summary(char_result, quality, decode_result, anomalies)
|
|
383
479
|
self.session_duration_seconds = (datetime.now() - self._start_time).total_seconds()
|
|
384
480
|
|
|
385
481
|
return WizardResult(
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Hardware abstraction layer analysis.
|
|
2
|
+
|
|
3
|
+
This module provides tools for analyzing hardware abstraction layers,
|
|
4
|
+
register access patterns, and peripheral drivers in firmware binaries.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from oscura.hardware.hal_detector import (
|
|
8
|
+
HALAnalysisResult,
|
|
9
|
+
HALDetector,
|
|
10
|
+
Peripheral,
|
|
11
|
+
RegisterAccess,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"HALAnalysisResult",
|
|
16
|
+
"HALDetector",
|
|
17
|
+
"Peripheral",
|
|
18
|
+
"RegisterAccess",
|
|
19
|
+
]
|
|
@@ -11,7 +11,7 @@ The Source protocol enables polymorphic data acquisition:
|
|
|
11
11
|
All sources implement the same interface, making them interchangeable:
|
|
12
12
|
|
|
13
13
|
Example:
|
|
14
|
-
>>> from oscura.acquisition import FileSource, HardwareSource, SyntheticSource
|
|
14
|
+
>>> from oscura.hardware.acquisition import FileSource, HardwareSource, SyntheticSource
|
|
15
15
|
>>> from oscura import SignalBuilder
|
|
16
16
|
>>>
|
|
17
17
|
>>> # All sources use the same interface
|
|
@@ -135,9 +135,9 @@ class Source(Protocol):
|
|
|
135
135
|
|
|
136
136
|
|
|
137
137
|
# Import concrete implementations
|
|
138
|
-
from oscura.acquisition.file import FileSource
|
|
139
|
-
from oscura.acquisition.hardware import HardwareSource
|
|
140
|
-
from oscura.acquisition.synthetic import SyntheticSource
|
|
138
|
+
from oscura.hardware.acquisition.file import FileSource
|
|
139
|
+
from oscura.hardware.acquisition.hardware import HardwareSource
|
|
140
|
+
from oscura.hardware.acquisition.synthetic import SyntheticSource
|
|
141
141
|
|
|
142
142
|
__all__ = [
|
|
143
143
|
"FileSource",
|
|
@@ -5,7 +5,7 @@ the unified Source protocol. FileSource makes file loading consistent with all
|
|
|
5
5
|
other acquisition methods (hardware, synthetic).
|
|
6
6
|
|
|
7
7
|
Example:
|
|
8
|
-
>>> from oscura.acquisition import FileSource
|
|
8
|
+
>>> from oscura.hardware.acquisition import FileSource
|
|
9
9
|
>>>
|
|
10
10
|
>>> # Basic usage
|
|
11
11
|
>>> source = FileSource("capture.wfm")
|
|
@@ -153,7 +153,7 @@ class FileSource:
|
|
|
153
153
|
|
|
154
154
|
# Try lazy/chunked loading if available
|
|
155
155
|
try:
|
|
156
|
-
from oscura.streaming import load_trace_chunks
|
|
156
|
+
from oscura.utils.streaming import load_trace_chunks
|
|
157
157
|
|
|
158
158
|
# load_trace_chunks expects Path-like and chunk_size
|
|
159
159
|
yield from load_trace_chunks(self.path, chunk_size=chunk_size)
|
|
@@ -11,7 +11,7 @@ Supported Hardware:
|
|
|
11
11
|
- PyVISA: Oscilloscopes and instruments (requires pyvisa)
|
|
12
12
|
|
|
13
13
|
Example:
|
|
14
|
-
>>> from oscura.acquisition import HardwareSource
|
|
14
|
+
>>> from oscura.hardware.acquisition import HardwareSource
|
|
15
15
|
>>>
|
|
16
16
|
>>> # SocketCAN source
|
|
17
17
|
>>> with HardwareSource.socketcan("can0", bitrate=500000) as source:
|
|
@@ -48,9 +48,9 @@ from __future__ import annotations
|
|
|
48
48
|
from typing import TYPE_CHECKING, Any
|
|
49
49
|
|
|
50
50
|
if TYPE_CHECKING:
|
|
51
|
-
from oscura.acquisition.saleae import SaleaeSource
|
|
52
|
-
from oscura.acquisition.socketcan import SocketCANSource
|
|
53
|
-
from oscura.acquisition.visa import VISASource
|
|
51
|
+
from oscura.hardware.acquisition.saleae import SaleaeSource
|
|
52
|
+
from oscura.hardware.acquisition.socketcan import SocketCANSource
|
|
53
|
+
from oscura.hardware.acquisition.visa import VISASource
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
class HardwareSource:
|
|
@@ -107,7 +107,7 @@ class HardwareSource:
|
|
|
107
107
|
Requires python-can library: pip install oscura[automotive]
|
|
108
108
|
Linux only - uses SocketCAN kernel module.
|
|
109
109
|
"""
|
|
110
|
-
from oscura.acquisition.socketcan import SocketCANSource
|
|
110
|
+
from oscura.hardware.acquisition.socketcan import SocketCANSource
|
|
111
111
|
|
|
112
112
|
return SocketCANSource(interface=interface, bitrate=bitrate, **kwargs)
|
|
113
113
|
|
|
@@ -142,7 +142,7 @@ class HardwareSource:
|
|
|
142
142
|
Requires saleae library: pip install saleae
|
|
143
143
|
Supports Logic 8, Logic Pro 8, Logic Pro 16.
|
|
144
144
|
"""
|
|
145
|
-
from oscura.acquisition.saleae import SaleaeSource
|
|
145
|
+
from oscura.hardware.acquisition.saleae import SaleaeSource
|
|
146
146
|
|
|
147
147
|
return SaleaeSource(device_id=device_id, **kwargs)
|
|
148
148
|
|
|
@@ -178,7 +178,7 @@ class HardwareSource:
|
|
|
178
178
|
Requires pyvisa and pyvisa-py: pip install pyvisa pyvisa-py
|
|
179
179
|
Supports Tektronix, Keysight, Rigol, and other SCPI oscilloscopes.
|
|
180
180
|
"""
|
|
181
|
-
from oscura.acquisition.visa import VISASource
|
|
181
|
+
from oscura.hardware.acquisition.visa import VISASource
|
|
182
182
|
|
|
183
183
|
return VISASource(resource=resource, **kwargs)
|
|
184
184
|
|
|
@@ -8,7 +8,7 @@ directly from the hardware, returning WaveformTrace (analog) or DigitalTrace
|
|
|
8
8
|
(digital) depending on channel configuration.
|
|
9
9
|
|
|
10
10
|
Example:
|
|
11
|
-
>>> from oscura.acquisition import HardwareSource
|
|
11
|
+
>>> from oscura.hardware.acquisition import HardwareSource
|
|
12
12
|
>>>
|
|
13
13
|
>>> # Basic digital acquisition
|
|
14
14
|
>>> with HardwareSource.saleae() as source:
|
|
@@ -41,10 +41,17 @@ References:
|
|
|
41
41
|
|
|
42
42
|
from __future__ import annotations
|
|
43
43
|
|
|
44
|
+
import time
|
|
44
45
|
from collections.abc import Iterator
|
|
45
46
|
from datetime import datetime
|
|
46
47
|
from typing import TYPE_CHECKING, Any
|
|
47
48
|
|
|
49
|
+
# Optional dependency - import at module level for mocking in tests
|
|
50
|
+
try:
|
|
51
|
+
import saleae
|
|
52
|
+
except ImportError:
|
|
53
|
+
saleae = None # type: ignore[assignment]
|
|
54
|
+
|
|
48
55
|
if TYPE_CHECKING:
|
|
49
56
|
from oscura.core.types import Trace
|
|
50
57
|
|
|
@@ -88,7 +95,7 @@ class SaleaeSource:
|
|
|
88
95
|
"""
|
|
89
96
|
self.device_id = device_id
|
|
90
97
|
self.kwargs = kwargs
|
|
91
|
-
self.saleae = None
|
|
98
|
+
self.saleae: Any = None # Saleae connection object (dynamic import)
|
|
92
99
|
self._closed = False
|
|
93
100
|
|
|
94
101
|
# Configuration
|
|
@@ -107,12 +114,10 @@ class SaleaeSource:
|
|
|
107
114
|
if self.saleae is not None:
|
|
108
115
|
return
|
|
109
116
|
|
|
110
|
-
|
|
111
|
-
import saleae
|
|
112
|
-
except ImportError as e:
|
|
117
|
+
if saleae is None:
|
|
113
118
|
raise ImportError(
|
|
114
119
|
"Saleae source requires saleae library. Install with: pip install saleae"
|
|
115
|
-
)
|
|
120
|
+
)
|
|
116
121
|
|
|
117
122
|
try:
|
|
118
123
|
self.saleae = saleae.Saleae()
|
|
@@ -164,12 +169,12 @@ class SaleaeSource:
|
|
|
164
169
|
self._ensure_connection()
|
|
165
170
|
|
|
166
171
|
# Set sample rate
|
|
167
|
-
self.saleae.set_sample_rate_by_minimum(sample_rate)
|
|
172
|
+
self.saleae.set_sample_rate_by_minimum(sample_rate)
|
|
168
173
|
|
|
169
174
|
# Enable/disable channels
|
|
170
175
|
for ch in range(16): # Max 16 digital channels
|
|
171
176
|
if ch in self.digital_channels:
|
|
172
|
-
self.saleae.set_capture_pretrigger_buffer_size(
|
|
177
|
+
self.saleae.set_capture_pretrigger_buffer_size(
|
|
173
178
|
int(sample_rate * duration), is_set=True
|
|
174
179
|
)
|
|
175
180
|
|
|
@@ -204,15 +209,13 @@ class SaleaeSource:
|
|
|
204
209
|
acquisition_start = datetime.now()
|
|
205
210
|
|
|
206
211
|
# Start capture
|
|
207
|
-
self.saleae.capture_start()
|
|
212
|
+
self.saleae.capture_start()
|
|
208
213
|
|
|
209
214
|
# Wait for capture to complete
|
|
210
|
-
import time
|
|
211
|
-
|
|
212
215
|
time.sleep(self.duration)
|
|
213
216
|
|
|
214
217
|
# Stop capture
|
|
215
|
-
self.saleae.capture_stop()
|
|
218
|
+
self.saleae.capture_stop()
|
|
216
219
|
|
|
217
220
|
# Export data
|
|
218
221
|
# Note: Actual Saleae API would save to file, then we'd load it.
|
|
@@ -9,7 +9,7 @@ CAN ID represented as a separate digital channel. This enables protocol analysis
|
|
|
9
9
|
and reverse engineering of CAN bus communications.
|
|
10
10
|
|
|
11
11
|
Example:
|
|
12
|
-
>>> from oscura.acquisition import HardwareSource
|
|
12
|
+
>>> from oscura.hardware.acquisition import HardwareSource
|
|
13
13
|
>>>
|
|
14
14
|
>>> # Basic usage
|
|
15
15
|
>>> with HardwareSource.socketcan("can0", bitrate=500000) as source:
|
|
@@ -8,8 +8,8 @@ Planned features:
|
|
|
8
8
|
- Buffering and backpressure management
|
|
9
9
|
|
|
10
10
|
Example (Future):
|
|
11
|
-
>>> from oscura.acquisition import HardwareSource
|
|
12
|
-
>>> from oscura.acquisition.streaming import LiveProcessor
|
|
11
|
+
>>> from oscura.hardware.acquisition import HardwareSource
|
|
12
|
+
>>> from oscura.hardware.acquisition.streaming import LiveProcessor
|
|
13
13
|
>>>
|
|
14
14
|
>>> # Real-time streaming from hardware
|
|
15
15
|
>>> processor = LiveProcessor(
|
|
@@ -5,7 +5,7 @@ the unified Source protocol. SyntheticSource makes synthetic signals consistent
|
|
|
5
5
|
with all other acquisition methods (files, hardware).
|
|
6
6
|
|
|
7
7
|
Example:
|
|
8
|
-
>>> from oscura.acquisition import SyntheticSource
|
|
8
|
+
>>> from oscura.hardware.acquisition import SyntheticSource
|
|
9
9
|
>>> from oscura import SignalBuilder
|
|
10
10
|
>>>
|
|
11
11
|
>>> # Create signal builder
|
|
@@ -41,8 +41,8 @@ from collections.abc import Iterator
|
|
|
41
41
|
from typing import TYPE_CHECKING
|
|
42
42
|
|
|
43
43
|
if TYPE_CHECKING:
|
|
44
|
-
from oscura.builders.signal_builder import SignalBuilder
|
|
45
44
|
from oscura.core.types import Trace, WaveformTrace
|
|
45
|
+
from oscura.utils.builders.signal_builder import SignalBuilder
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
class SyntheticSource:
|
|
@@ -57,7 +57,7 @@ class SyntheticSource:
|
|
|
57
57
|
|
|
58
58
|
Example:
|
|
59
59
|
>>> from oscura import SignalBuilder
|
|
60
|
-
>>> from oscura.acquisition import SyntheticSource
|
|
60
|
+
>>> from oscura.hardware.acquisition import SyntheticSource
|
|
61
61
|
>>>
|
|
62
62
|
>>> # Create builder
|
|
63
63
|
>>> builder = (SignalBuilder(sample_rate=1e6, duration=0.01)
|