oscura 0.5.0__py3-none-any.whl → 0.6.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/__init__.py +0 -48
- oscura/analyzers/digital/edges.py +325 -65
- oscura/analyzers/digital/extraction.py +0 -195
- 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/__init__.py +1 -22
- 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 +2763 -0
- 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/core/schemas/bus_configuration.json +322 -0
- oscura/core/schemas/device_mapping.json +182 -0
- oscura/core/schemas/packet_format.json +418 -0
- oscura/core/schemas/protocol_definition.json +363 -0
- 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 -20
- oscura/export/kaitai_struct.py +513 -0
- oscura/export/scapy_layer.py +801 -0
- oscura/export/wireshark/README.md +15 -15
- 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/README.md +7 -7
- 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 +171 -63
- 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/vcd.py +215 -117
- oscura/loaders/wav.py +155 -68
- oscura/reporting/__init__.py +9 -7
- 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/reporting/templates/index.md +13 -13
- 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/autodetect.py +1 -5
- 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 +11 -3
- 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.6.0.dist-info/METADATA +643 -0
- oscura-0.6.0.dist-info/RECORD +590 -0
- oscura/analyzers/digital/ic_database.py +0 -498
- oscura/analyzers/digital/timing_paths.py +0 -339
- oscura/analyzers/digital/vintage.py +0 -377
- oscura/analyzers/digital/vintage_result.py +0 -148
- oscura/analyzers/protocols/parallel_bus.py +0 -449
- 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/export/wavedrom.py +0 -430
- 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 -338
- 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/exporters/vintage_logic_csv.py +0 -247
- oscura/reporting/vintage_logic_report.py +0 -523
- 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/visualization/digital_advanced.py +0 -718
- oscura/visualization/figure_manager.py +0 -156
- oscura/workflow/__init__.py +0 -13
- oscura-0.5.0.dist-info/METADATA +0 -407
- oscura-0.5.0.dist-info/RECORD +0 -486
- /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/{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.0.dist-info → oscura-0.6.0.dist-info}/WHEEL +0 -0
- {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/entry_points.txt +0 -0
- {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/licenses/LICENSE +0 -0
oscura/cli/main.py
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
|
-
"""Oscura Core CLI Framework implementing CLI-001.
|
|
1
|
+
"""Oscura Core CLI Framework implementing CLI-001 (Enhanced Edition).
|
|
2
2
|
|
|
3
3
|
Provides the main entry point for the oscura command-line interface with
|
|
4
|
-
|
|
4
|
+
comprehensive subcommands, interactive mode, batch processing, configuration
|
|
5
|
+
management, and shell completion support.
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
Example:
|
|
8
9
|
$ oscura --help
|
|
9
|
-
$ oscura
|
|
10
|
-
$ oscura decode uart.wfm -
|
|
10
|
+
$ oscura analyze signal.wfm --output json
|
|
11
|
+
$ oscura decode uart capture.wfm --baud-rate 115200
|
|
11
12
|
$ oscura shell # Interactive REPL
|
|
13
|
+
$ oscura config --show # View configuration
|
|
14
|
+
$ oscura plugins list # Manage plugins
|
|
12
15
|
"""
|
|
13
16
|
|
|
14
17
|
import json
|
|
15
18
|
import logging
|
|
16
19
|
import sys
|
|
20
|
+
from pathlib import Path
|
|
17
21
|
from typing import Any
|
|
18
22
|
|
|
19
23
|
import click
|
|
@@ -126,37 +130,98 @@ def format_output(data: dict[str, Any], format_type: str) -> str:
|
|
|
126
130
|
return formatter(data)
|
|
127
131
|
|
|
128
132
|
|
|
129
|
-
|
|
130
|
-
|
|
133
|
+
def load_config_file(config_path: Path | None = None) -> dict[str, Any]:
|
|
134
|
+
"""Load configuration from YAML file.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
config_path: Path to config file. If None, searches for config in:
|
|
138
|
+
1. .oscura.yaml in current directory
|
|
139
|
+
2. ~/.config/oscura/config.yaml
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Configuration dictionary.
|
|
143
|
+
"""
|
|
144
|
+
import yaml
|
|
145
|
+
|
|
146
|
+
if config_path is None:
|
|
147
|
+
# Search for config files
|
|
148
|
+
candidates = [
|
|
149
|
+
Path(".oscura.yaml"),
|
|
150
|
+
Path.home() / ".config" / "oscura" / "config.yaml",
|
|
151
|
+
]
|
|
152
|
+
for candidate in candidates:
|
|
153
|
+
if candidate.exists():
|
|
154
|
+
config_path = candidate
|
|
155
|
+
break
|
|
156
|
+
|
|
157
|
+
if config_path is None or not config_path.exists():
|
|
158
|
+
return {}
|
|
159
|
+
|
|
160
|
+
with open(config_path) as f:
|
|
161
|
+
return yaml.safe_load(f) or {}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@click.group()
|
|
165
|
+
@click.option(
|
|
131
166
|
"-v",
|
|
132
167
|
"--verbose",
|
|
133
168
|
count=True,
|
|
134
169
|
help="Increase verbosity (-v for INFO, -vv for DEBUG).",
|
|
135
170
|
)
|
|
136
|
-
@click.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
171
|
+
@click.option(
|
|
172
|
+
"--config",
|
|
173
|
+
type=click.Path(exists=True),
|
|
174
|
+
default=None,
|
|
175
|
+
help="Path to configuration file (YAML).",
|
|
176
|
+
)
|
|
177
|
+
@click.option(
|
|
178
|
+
"--quiet",
|
|
179
|
+
"-q",
|
|
180
|
+
is_flag=True,
|
|
181
|
+
help="Quiet mode (suppress non-error output).",
|
|
182
|
+
)
|
|
183
|
+
@click.option(
|
|
184
|
+
"--json",
|
|
185
|
+
"json_output",
|
|
186
|
+
is_flag=True,
|
|
187
|
+
help="JSON output mode for scripting.",
|
|
188
|
+
)
|
|
189
|
+
@click.version_option(prog_name="oscura") # Version auto-detected from package metadata
|
|
190
|
+
@click.pass_context
|
|
191
|
+
def cli(
|
|
192
|
+
ctx: click.Context,
|
|
193
|
+
verbose: int,
|
|
194
|
+
config: str | None,
|
|
195
|
+
quiet: bool,
|
|
196
|
+
json_output: bool,
|
|
197
|
+
) -> None:
|
|
198
|
+
"""Oscura - Hardware Reverse Engineering Framework.
|
|
199
|
+
|
|
200
|
+
Unified framework for extracting all information from hardware systems through
|
|
201
|
+
signals and data. Features unknown protocol discovery, state machine extraction,
|
|
202
|
+
CRC recovery, and security analysis.
|
|
143
203
|
|
|
144
204
|
Args:
|
|
145
205
|
ctx: Click context object.
|
|
146
206
|
verbose: Verbosity level (0=WARNING, 1=INFO, 2+=DEBUG).
|
|
207
|
+
config: Path to configuration file.
|
|
208
|
+
quiet: Quiet mode flag.
|
|
209
|
+
json_output: JSON output mode flag.
|
|
147
210
|
|
|
148
211
|
Examples:
|
|
149
|
-
oscura
|
|
212
|
+
oscura analyze signal.wfm
|
|
150
213
|
oscura decode uart.wfm --protocol auto
|
|
151
214
|
oscura batch '*.wfm' --analysis characterize
|
|
152
|
-
oscura
|
|
215
|
+
oscura visualize trace.wfm
|
|
153
216
|
oscura shell # Interactive REPL
|
|
154
217
|
"""
|
|
155
218
|
# Ensure ctx.obj exists
|
|
156
219
|
ctx.ensure_object(dict)
|
|
157
220
|
|
|
158
221
|
# Set logging level based on verbosity
|
|
159
|
-
if
|
|
222
|
+
if quiet:
|
|
223
|
+
logger.setLevel(logging.ERROR)
|
|
224
|
+
elif verbose == 0:
|
|
160
225
|
logger.setLevel(logging.WARNING)
|
|
161
226
|
elif verbose == 1:
|
|
162
227
|
logger.setLevel(logging.INFO)
|
|
@@ -166,9 +231,28 @@ def cli(ctx: click.Context, verbose: int) -> None:
|
|
|
166
231
|
logger.debug("Debug mode enabled")
|
|
167
232
|
|
|
168
233
|
ctx.obj["verbose"] = verbose
|
|
234
|
+
ctx.obj["quiet"] = quiet
|
|
235
|
+
ctx.obj["json_output"] = json_output
|
|
236
|
+
|
|
237
|
+
# Load configuration
|
|
238
|
+
config_path = Path(config) if config else None
|
|
239
|
+
ctx.obj["config"] = load_config_file(config_path)
|
|
169
240
|
|
|
170
241
|
|
|
171
|
-
|
|
242
|
+
# Enhanced subcommands - imported below
|
|
243
|
+
from oscura.cli.analyze import analyze
|
|
244
|
+
from oscura.cli.batch import batch
|
|
245
|
+
from oscura.cli.benchmark import benchmark
|
|
246
|
+
from oscura.cli.characterize import characterize
|
|
247
|
+
from oscura.cli.compare import compare
|
|
248
|
+
from oscura.cli.config_cmd import config as config_cmd
|
|
249
|
+
from oscura.cli.decode import decode
|
|
250
|
+
from oscura.cli.export import export
|
|
251
|
+
from oscura.cli.validate_cmd import validate
|
|
252
|
+
from oscura.cli.visualize import visualize
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
@click.command()
|
|
172
256
|
def shell() -> None:
|
|
173
257
|
"""Start an interactive Oscura shell.
|
|
174
258
|
|
|
@@ -177,7 +261,7 @@ def shell() -> None:
|
|
|
177
261
|
|
|
178
262
|
Example:
|
|
179
263
|
$ oscura shell
|
|
180
|
-
Oscura Shell v0.
|
|
264
|
+
Oscura Shell v0.6.0
|
|
181
265
|
>>> trace = load("signal.wfm")
|
|
182
266
|
>>> rise_time(trace)
|
|
183
267
|
"""
|
|
@@ -186,9 +270,9 @@ def shell() -> None:
|
|
|
186
270
|
start_shell()
|
|
187
271
|
|
|
188
272
|
|
|
189
|
-
@click.command()
|
|
190
|
-
@click.argument("tutorial_id", required=False, default=None)
|
|
191
|
-
@click.option("--list", "list_tutorials", is_flag=True, help="List available tutorials")
|
|
273
|
+
@click.command()
|
|
274
|
+
@click.argument("tutorial_id", required=False, default=None)
|
|
275
|
+
@click.option("--list", "list_tutorials", is_flag=True, help="List available tutorials")
|
|
192
276
|
def tutorial(tutorial_id: str | None, list_tutorials: bool) -> None:
|
|
193
277
|
"""Run an interactive tutorial.
|
|
194
278
|
|
|
@@ -202,8 +286,8 @@ def tutorial(tutorial_id: str | None, list_tutorials: bool) -> None:
|
|
|
202
286
|
oscura tutorial --list # List available tutorials
|
|
203
287
|
oscura tutorial getting_started # Run the getting started tutorial
|
|
204
288
|
"""
|
|
205
|
-
from oscura.onboarding import list_tutorials as list_tut
|
|
206
|
-
from oscura.onboarding import run_tutorial
|
|
289
|
+
from oscura.cli.onboarding import list_tutorials as list_tut
|
|
290
|
+
from oscura.cli.onboarding import run_tutorial
|
|
207
291
|
|
|
208
292
|
if list_tutorials or tutorial_id is None:
|
|
209
293
|
tutorials = list_tut()
|
|
@@ -217,15 +301,15 @@ def tutorial(tutorial_id: str | None, list_tutorials: bool) -> None:
|
|
|
217
301
|
run_tutorial(tutorial_id, interactive=True)
|
|
218
302
|
|
|
219
303
|
|
|
220
|
-
#
|
|
221
|
-
|
|
222
|
-
from oscura.cli.characterize import characterize # noqa: E402
|
|
223
|
-
from oscura.cli.compare import compare # noqa: E402
|
|
224
|
-
from oscura.cli.decode import decode # noqa: E402
|
|
225
|
-
|
|
226
|
-
# Register subcommands
|
|
227
|
-
cli.add_command(characterize) # type: ignore[has-type]
|
|
304
|
+
# Register all subcommands
|
|
305
|
+
cli.add_command(analyze) # type: ignore[has-type]
|
|
228
306
|
cli.add_command(decode) # type: ignore[has-type]
|
|
307
|
+
cli.add_command(export)
|
|
308
|
+
cli.add_command(visualize)
|
|
309
|
+
cli.add_command(benchmark)
|
|
310
|
+
cli.add_command(validate)
|
|
311
|
+
cli.add_command(config_cmd, name="config")
|
|
312
|
+
cli.add_command(characterize) # type: ignore[has-type]
|
|
229
313
|
cli.add_command(batch) # type: ignore[has-type]
|
|
230
314
|
cli.add_command(compare) # type: ignore[has-type]
|
|
231
315
|
cli.add_command(shell)
|
|
@@ -4,20 +4,20 @@ This package provides interactive tutorials, context-sensitive help,
|
|
|
4
4
|
and guided analysis features for new users.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from oscura.onboarding.help import (
|
|
7
|
+
from oscura.cli.onboarding.help import (
|
|
8
8
|
explain_result,
|
|
9
9
|
get_example,
|
|
10
10
|
get_help,
|
|
11
11
|
suggest_commands,
|
|
12
12
|
)
|
|
13
|
-
from oscura.onboarding.tutorials import (
|
|
13
|
+
from oscura.cli.onboarding.tutorials import (
|
|
14
14
|
Tutorial,
|
|
15
15
|
TutorialStep,
|
|
16
16
|
get_tutorial,
|
|
17
17
|
list_tutorials,
|
|
18
18
|
run_tutorial,
|
|
19
19
|
)
|
|
20
|
-
from oscura.onboarding.wizard import (
|
|
20
|
+
from oscura.cli.onboarding.wizard import (
|
|
21
21
|
AnalysisWizard,
|
|
22
22
|
WizardStep,
|
|
23
23
|
run_wizard,
|
|
@@ -5,7 +5,7 @@ context, and result explanations for non-expert users.
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
Example:
|
|
8
|
-
>>> from oscura.onboarding import get_help, suggest_commands
|
|
8
|
+
>>> from oscura.cli.onboarding import get_help, suggest_commands
|
|
9
9
|
>>> get_help("rise_time")
|
|
10
10
|
>>> suggest_commands(trace)
|
|
11
11
|
"""
|
|
@@ -252,22 +252,7 @@ def suggest_commands(trace: Any = None, context: str | None = None) -> list[dict
|
|
|
252
252
|
suggestions = []
|
|
253
253
|
|
|
254
254
|
if trace is None:
|
|
255
|
-
|
|
256
|
-
suggestions.append(
|
|
257
|
-
{
|
|
258
|
-
"command": "trace = load('file.csv')",
|
|
259
|
-
"description": "Load a trace file to get started",
|
|
260
|
-
"reason": "No trace loaded yet",
|
|
261
|
-
}
|
|
262
|
-
)
|
|
263
|
-
suggestions.append(
|
|
264
|
-
{
|
|
265
|
-
"command": "formats = get_supported_formats()",
|
|
266
|
-
"description": "See what file formats are supported",
|
|
267
|
-
"reason": "Helpful for knowing what files you can load",
|
|
268
|
-
}
|
|
269
|
-
)
|
|
270
|
-
return suggestions
|
|
255
|
+
return _suggest_loading_commands()
|
|
271
256
|
|
|
272
257
|
# Trace is loaded - suggest measurements
|
|
273
258
|
suggestions.append(
|
|
@@ -278,7 +263,48 @@ def suggest_commands(trace: Any = None, context: str | None = None) -> list[dict
|
|
|
278
263
|
}
|
|
279
264
|
)
|
|
280
265
|
|
|
281
|
-
#
|
|
266
|
+
# Add signal type-specific suggestions
|
|
267
|
+
_add_signal_type_suggestions(suggestions, trace)
|
|
268
|
+
|
|
269
|
+
# Always suggest filtering for noisy signals
|
|
270
|
+
suggestions.append(
|
|
271
|
+
{
|
|
272
|
+
"command": "filtered = low_pass(trace, cutoff_hz)",
|
|
273
|
+
"description": "Apply low-pass filter to remove noise",
|
|
274
|
+
"reason": "Clean up high-frequency noise",
|
|
275
|
+
}
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
# Add context-specific suggestions
|
|
279
|
+
if context:
|
|
280
|
+
_add_context_suggestions(suggestions, context)
|
|
281
|
+
|
|
282
|
+
return suggestions
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _suggest_loading_commands() -> list[dict[str, str]]:
|
|
286
|
+
"""Get suggestions when no trace is loaded."""
|
|
287
|
+
return [
|
|
288
|
+
{
|
|
289
|
+
"command": "trace = load('file.csv')",
|
|
290
|
+
"description": "Load a trace file to get started",
|
|
291
|
+
"reason": "No trace loaded yet",
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
"command": "formats = get_supported_formats()",
|
|
295
|
+
"description": "See what file formats are supported",
|
|
296
|
+
"reason": "Helpful for knowing what files you can load",
|
|
297
|
+
},
|
|
298
|
+
]
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _add_signal_type_suggestions(suggestions: list[dict[str, str]], trace: Any) -> None:
|
|
302
|
+
"""Add signal type-specific suggestions.
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
suggestions: List to append suggestions to.
|
|
306
|
+
trace: Trace object to analyze.
|
|
307
|
+
"""
|
|
282
308
|
if hasattr(trace, "data"):
|
|
283
309
|
import numpy as np
|
|
284
310
|
|
|
@@ -318,47 +344,43 @@ def suggest_commands(trace: Any = None, context: str | None = None) -> list[dict
|
|
|
318
344
|
}
|
|
319
345
|
)
|
|
320
346
|
|
|
321
|
-
# Always suggest filtering for noisy signals
|
|
322
|
-
suggestions.append(
|
|
323
|
-
{
|
|
324
|
-
"command": "filtered = low_pass(trace, cutoff_hz)",
|
|
325
|
-
"description": "Apply low-pass filter to remove noise",
|
|
326
|
-
"reason": "Clean up high-frequency noise",
|
|
327
|
-
}
|
|
328
|
-
)
|
|
329
347
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
context_lower = context.lower()
|
|
333
|
-
if "uart" in context_lower or "serial" in context_lower:
|
|
334
|
-
suggestions.insert(
|
|
335
|
-
0,
|
|
336
|
-
{
|
|
337
|
-
"command": "packets = decode_uart(trace)",
|
|
338
|
-
"description": "Decode UART serial data",
|
|
339
|
-
"reason": "You mentioned UART/serial",
|
|
340
|
-
},
|
|
341
|
-
)
|
|
342
|
-
elif "spi" in context_lower:
|
|
343
|
-
suggestions.insert(
|
|
344
|
-
0,
|
|
345
|
-
{
|
|
346
|
-
"command": "packets = decode_spi(clk_trace, data_trace)",
|
|
347
|
-
"description": "Decode SPI bus",
|
|
348
|
-
"reason": "You mentioned SPI",
|
|
349
|
-
},
|
|
350
|
-
)
|
|
351
|
-
elif "i2c" in context_lower:
|
|
352
|
-
suggestions.insert(
|
|
353
|
-
0,
|
|
354
|
-
{
|
|
355
|
-
"command": "packets = decode_i2c(scl_trace, sda_trace)",
|
|
356
|
-
"description": "Decode I2C bus",
|
|
357
|
-
"reason": "You mentioned I2C",
|
|
358
|
-
},
|
|
359
|
-
)
|
|
348
|
+
def _add_context_suggestions(suggestions: list[dict[str, str]], context: str) -> None:
|
|
349
|
+
"""Add context-specific suggestions based on user intent.
|
|
360
350
|
|
|
361
|
-
|
|
351
|
+
Args:
|
|
352
|
+
suggestions: List to prepend suggestions to.
|
|
353
|
+
context: User context string.
|
|
354
|
+
"""
|
|
355
|
+
context_lower = context.lower()
|
|
356
|
+
|
|
357
|
+
if "uart" in context_lower or "serial" in context_lower:
|
|
358
|
+
suggestions.insert(
|
|
359
|
+
0,
|
|
360
|
+
{
|
|
361
|
+
"command": "packets = decode_uart(trace)",
|
|
362
|
+
"description": "Decode UART serial data",
|
|
363
|
+
"reason": "You mentioned UART/serial",
|
|
364
|
+
},
|
|
365
|
+
)
|
|
366
|
+
elif "spi" in context_lower:
|
|
367
|
+
suggestions.insert(
|
|
368
|
+
0,
|
|
369
|
+
{
|
|
370
|
+
"command": "packets = decode_spi(clk_trace, data_trace)",
|
|
371
|
+
"description": "Decode SPI bus",
|
|
372
|
+
"reason": "You mentioned SPI",
|
|
373
|
+
},
|
|
374
|
+
)
|
|
375
|
+
elif "i2c" in context_lower:
|
|
376
|
+
suggestions.insert(
|
|
377
|
+
0,
|
|
378
|
+
{
|
|
379
|
+
"command": "packets = decode_i2c(scl_trace, sda_trace)",
|
|
380
|
+
"description": "Decode I2C bus",
|
|
381
|
+
"reason": "You mentioned I2C",
|
|
382
|
+
},
|
|
383
|
+
)
|
|
362
384
|
|
|
363
385
|
|
|
364
386
|
def explain_result(
|
|
@@ -389,7 +411,7 @@ def explain_result(
|
|
|
389
411
|
}
|
|
390
412
|
|
|
391
413
|
if measurement.lower() in explanations:
|
|
392
|
-
return explanations[measurement.lower()](value)
|
|
414
|
+
return explanations[measurement.lower()](value)
|
|
393
415
|
|
|
394
416
|
# Generic explanation
|
|
395
417
|
return f"{measurement}: {value}"
|
|
@@ -9,7 +9,7 @@ covering common analysis workflows.
|
|
|
9
9
|
- Progress tracking
|
|
10
10
|
|
|
11
11
|
Example:
|
|
12
|
-
>>> from oscura.onboarding import run_tutorial
|
|
12
|
+
>>> from oscura.cli.onboarding import run_tutorial
|
|
13
13
|
>>> run_tutorial("getting_started")
|
|
14
14
|
Welcome to Oscura!
|
|
15
15
|
Step 1/5: Loading a trace file
|
|
@@ -71,16 +71,47 @@ TUTORIALS: dict[str, Tutorial] = {}
|
|
|
71
71
|
def _register_getting_started() -> None:
|
|
72
72
|
"""Register the getting started tutorial."""
|
|
73
73
|
steps = [
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
_create_loading_step(),
|
|
75
|
+
_create_measurements_step(),
|
|
76
|
+
_create_spectral_step(),
|
|
77
|
+
_create_protocol_step(),
|
|
78
|
+
_create_discovery_step(),
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
tutorial = Tutorial(
|
|
82
|
+
id="getting_started",
|
|
83
|
+
title="Getting Started with Oscura",
|
|
84
|
+
description="""
|
|
85
|
+
Welcome to Oscura! This tutorial will teach you the basics of
|
|
86
|
+
signal analysis in 5 easy steps:
|
|
87
|
+
|
|
88
|
+
1. Loading trace files
|
|
89
|
+
2. Making basic measurements
|
|
90
|
+
3. Spectral analysis
|
|
91
|
+
4. Protocol decoding
|
|
92
|
+
5. Auto-discovery
|
|
93
|
+
|
|
94
|
+
No prior signal analysis experience required!
|
|
95
|
+
""",
|
|
96
|
+
steps=steps,
|
|
97
|
+
difficulty="beginner",
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
TUTORIALS[tutorial.id] = tutorial
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _create_loading_step() -> TutorialStep:
|
|
104
|
+
"""Create loading trace file tutorial step."""
|
|
105
|
+
return TutorialStep(
|
|
106
|
+
title="Loading a Trace File",
|
|
107
|
+
description="""
|
|
77
108
|
Oscura can load waveform data from many file formats.
|
|
78
109
|
The simplest way is to use the load() function, which auto-detects the format.
|
|
79
110
|
|
|
80
111
|
Think of a trace like a recording of an electrical signal over time -
|
|
81
112
|
similar to how an audio file stores sound waves.
|
|
82
113
|
""",
|
|
83
|
-
|
|
114
|
+
code="""
|
|
84
115
|
import oscura as osc
|
|
85
116
|
|
|
86
117
|
# Load a waveform file (replace with your file path)
|
|
@@ -90,15 +121,19 @@ trace = osc.load("signal.csv")
|
|
|
90
121
|
print(f"Loaded {len(trace.data)} samples")
|
|
91
122
|
print(f"Sample rate: {trace.metadata.sample_rate} Hz")
|
|
92
123
|
""",
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
124
|
+
expected_output="Loaded 10000 samples\nSample rate: 1000000.0 Hz",
|
|
125
|
+
hints=[
|
|
126
|
+
"Try loading a CSV file with two columns: time and voltage",
|
|
127
|
+
"Supported formats: .csv, .wfm, .npz, .hdf5, and more",
|
|
128
|
+
],
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _create_measurements_step() -> TutorialStep:
|
|
133
|
+
"""Create measurements tutorial step."""
|
|
134
|
+
return TutorialStep(
|
|
135
|
+
title="Making Basic Measurements",
|
|
136
|
+
description="""
|
|
102
137
|
Once you have a trace, you can measure things like:
|
|
103
138
|
- Rise time: How fast a signal goes from low to high
|
|
104
139
|
- Frequency: How many times per second the signal repeats
|
|
@@ -106,7 +141,7 @@ Once you have a trace, you can measure things like:
|
|
|
106
141
|
|
|
107
142
|
These are the same measurements an oscilloscope would show you!
|
|
108
143
|
""",
|
|
109
|
-
|
|
144
|
+
code="""
|
|
110
145
|
import oscura as osc
|
|
111
146
|
|
|
112
147
|
trace = osc.load("signal.csv")
|
|
@@ -124,15 +159,19 @@ results = osc.measure(trace)
|
|
|
124
159
|
for name, value in results.items():
|
|
125
160
|
print(f"{name}: {value}")
|
|
126
161
|
""",
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
162
|
+
expected_output="Rise time: 2.50 nanoseconds\nFrequency: 10.00 MHz",
|
|
163
|
+
hints=[
|
|
164
|
+
"rise_time() measures 10%-90% transition by default",
|
|
165
|
+
"Use measure() to get all measurements in one call",
|
|
166
|
+
],
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _create_spectral_step() -> TutorialStep:
|
|
171
|
+
"""Create spectral analysis tutorial step."""
|
|
172
|
+
return TutorialStep(
|
|
173
|
+
title="Spectral Analysis (Frequency Domain)",
|
|
174
|
+
description="""
|
|
136
175
|
Spectral analysis shows you what frequencies are present in your signal.
|
|
137
176
|
This is useful for:
|
|
138
177
|
- Finding the main frequency of a clock signal
|
|
@@ -141,7 +180,7 @@ This is useful for:
|
|
|
141
180
|
|
|
142
181
|
It's like looking at a music equalizer that shows bass, mid, and treble!
|
|
143
182
|
""",
|
|
144
|
-
|
|
183
|
+
code="""
|
|
145
184
|
import oscura as osc
|
|
146
185
|
|
|
147
186
|
trace = osc.load("signal.csv")
|
|
@@ -160,21 +199,25 @@ snr_value = osc.snr(trace)
|
|
|
160
199
|
print(f"THD: {thd_value:.1f} dB")
|
|
161
200
|
print(f"SNR: {snr_value:.1f} dB")
|
|
162
201
|
""",
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
202
|
+
expected_output="Dominant frequency: 10.00 MHz\nTHD: -45.2 dB\nSNR: 52.3 dB",
|
|
203
|
+
hints=[
|
|
204
|
+
"THD (Total Harmonic Distortion) should be negative in dB - more negative is better",
|
|
205
|
+
"SNR (Signal-to-Noise Ratio) should be positive - higher is better",
|
|
206
|
+
],
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _create_protocol_step() -> TutorialStep:
|
|
211
|
+
"""Create protocol decoding tutorial step."""
|
|
212
|
+
return TutorialStep(
|
|
213
|
+
title="Protocol Decoding",
|
|
214
|
+
description="""
|
|
172
215
|
If your signal is a digital communication protocol like UART, SPI, or I2C,
|
|
173
216
|
Oscura can decode it to show you the actual data being transmitted.
|
|
174
217
|
|
|
175
218
|
Think of it like translating Morse code back into text!
|
|
176
219
|
""",
|
|
177
|
-
|
|
220
|
+
code="""
|
|
178
221
|
import oscura as osc
|
|
179
222
|
|
|
180
223
|
# Load a UART signal
|
|
@@ -188,15 +231,19 @@ packets = decode_uart(trace)
|
|
|
188
231
|
for pkt in packets[:5]: # First 5 packets
|
|
189
232
|
print(f"Time: {pkt.timestamp:.6f}s, Data: 0x{pkt.data:02X} ('{chr(pkt.data)}')")
|
|
190
233
|
""",
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
234
|
+
expected_output="Time: 0.000001s, Data: 0x48 ('H')\nTime: 0.000086s, Data: 0x65 ('e')",
|
|
235
|
+
hints=[
|
|
236
|
+
"UART baud rate is auto-detected by default",
|
|
237
|
+
"Supported protocols: UART, SPI, I2C, CAN, and many more",
|
|
238
|
+
],
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _create_discovery_step() -> TutorialStep:
|
|
243
|
+
"""Create auto-discovery tutorial step."""
|
|
244
|
+
return TutorialStep(
|
|
245
|
+
title="Auto-Discovery for Beginners",
|
|
246
|
+
description="""
|
|
200
247
|
Not sure what your signal is? Oscura can analyze it automatically!
|
|
201
248
|
|
|
202
249
|
The characterize_signal() function examines your trace and tells you:
|
|
@@ -206,7 +253,7 @@ The characterize_signal() function examines your trace and tells you:
|
|
|
206
253
|
|
|
207
254
|
It's like having an expert look at your signal and give you hints!
|
|
208
255
|
""",
|
|
209
|
-
|
|
256
|
+
code="""
|
|
210
257
|
import oscura as osc
|
|
211
258
|
from oscura.discovery import characterize_signal
|
|
212
259
|
|
|
@@ -226,35 +273,13 @@ else:
|
|
|
226
273
|
for alt in result.alternatives:
|
|
227
274
|
print(f" - {alt.signal_type}: {alt.confidence:.1%}")
|
|
228
275
|
""",
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
),
|
|
235
|
-
]
|
|
236
|
-
|
|
237
|
-
tutorial = Tutorial(
|
|
238
|
-
id="getting_started",
|
|
239
|
-
title="Getting Started with Oscura",
|
|
240
|
-
description="""
|
|
241
|
-
Welcome to Oscura! This tutorial will teach you the basics of
|
|
242
|
-
signal analysis in 5 easy steps:
|
|
243
|
-
|
|
244
|
-
1. Loading trace files
|
|
245
|
-
2. Making basic measurements
|
|
246
|
-
3. Spectral analysis
|
|
247
|
-
4. Protocol decoding
|
|
248
|
-
5. Auto-discovery
|
|
249
|
-
|
|
250
|
-
No prior signal analysis experience required!
|
|
251
|
-
""",
|
|
252
|
-
steps=steps,
|
|
253
|
-
difficulty="beginner",
|
|
276
|
+
expected_output="Signal type: digital\nConfidence: 94.0%",
|
|
277
|
+
hints=[
|
|
278
|
+
"Confidence >= 80% means high confidence in the detection",
|
|
279
|
+
"Low confidence? Check the alternatives for other possibilities",
|
|
280
|
+
],
|
|
254
281
|
)
|
|
255
282
|
|
|
256
|
-
TUTORIALS[tutorial.id] = tutorial
|
|
257
|
-
|
|
258
283
|
|
|
259
284
|
def _register_spectral_analysis() -> None:
|
|
260
285
|
"""Register the spectral analysis tutorial."""
|