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/decode.py
CHANGED
|
@@ -25,44 +25,44 @@ from oscura.core.types import DigitalTrace, ProtocolPacket, WaveformTrace
|
|
|
25
25
|
logger = logging.getLogger("oscura.cli.decode")
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
@click.command()
|
|
29
|
-
@click.argument("file", type=click.Path(exists=True))
|
|
30
|
-
@click.option(
|
|
28
|
+
@click.command()
|
|
29
|
+
@click.argument("file", type=click.Path(exists=True))
|
|
30
|
+
@click.option(
|
|
31
31
|
"--protocol",
|
|
32
32
|
type=click.Choice(["uart", "spi", "i2c", "can", "auto"], case_sensitive=False),
|
|
33
33
|
default="auto",
|
|
34
34
|
help="Protocol type (default: auto-detect).",
|
|
35
35
|
)
|
|
36
|
-
@click.option(
|
|
36
|
+
@click.option(
|
|
37
37
|
"--baud-rate",
|
|
38
38
|
type=int,
|
|
39
39
|
default=None,
|
|
40
40
|
help="Baud rate for UART (auto-detect if not specified).",
|
|
41
41
|
)
|
|
42
|
-
@click.option(
|
|
42
|
+
@click.option(
|
|
43
43
|
"--parity",
|
|
44
44
|
type=click.Choice(["none", "even", "odd"], case_sensitive=False),
|
|
45
45
|
default="none",
|
|
46
46
|
help="Parity for UART (default: none).",
|
|
47
47
|
)
|
|
48
|
-
@click.option(
|
|
48
|
+
@click.option(
|
|
49
49
|
"--stop-bits",
|
|
50
50
|
type=click.Choice(["1", "2"]),
|
|
51
51
|
default="1",
|
|
52
52
|
help="Stop bits for UART (default: 1).",
|
|
53
53
|
)
|
|
54
|
-
@click.option(
|
|
54
|
+
@click.option(
|
|
55
55
|
"--show-errors",
|
|
56
56
|
is_flag=True,
|
|
57
57
|
help="Show only errors with context.",
|
|
58
58
|
)
|
|
59
|
-
@click.option(
|
|
59
|
+
@click.option(
|
|
60
60
|
"--output",
|
|
61
61
|
type=click.Choice(["json", "csv", "html", "table"], case_sensitive=False),
|
|
62
62
|
default="table",
|
|
63
63
|
help="Output format (default: table).",
|
|
64
64
|
)
|
|
65
|
-
@click.pass_context
|
|
65
|
+
@click.pass_context
|
|
66
66
|
def decode(
|
|
67
67
|
ctx: click.Context,
|
|
68
68
|
file: str,
|
|
@@ -198,80 +198,113 @@ def _perform_decoding(
|
|
|
198
198
|
Returns:
|
|
199
199
|
Dictionary of decoding results.
|
|
200
200
|
"""
|
|
201
|
-
#
|
|
202
|
-
|
|
201
|
+
# Build base results
|
|
202
|
+
results = _build_base_results(trace)
|
|
203
|
+
|
|
204
|
+
# Auto-detect protocol if needed
|
|
205
|
+
detected_protocol, baud_rate = _detect_protocol_if_auto(trace, protocol, baud_rate, results)
|
|
206
|
+
results["protocol"] = detected_protocol.upper()
|
|
207
|
+
|
|
208
|
+
# Decode with appropriate decoder
|
|
209
|
+
digital_trace = _to_digital(trace)
|
|
210
|
+
packets, errors = _dispatch_protocol_decode(
|
|
211
|
+
digital_trace, detected_protocol, baud_rate, parity, stop_bits, show_errors, results
|
|
212
|
+
)
|
|
203
213
|
|
|
214
|
+
# Filter and format results
|
|
215
|
+
if show_errors:
|
|
216
|
+
packets = [p for p in packets if p.errors]
|
|
217
|
+
|
|
218
|
+
_add_packet_summary(results, packets, errors)
|
|
219
|
+
return results
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _build_base_results(trace: WaveformTrace | DigitalTrace) -> dict[str, Any]:
|
|
223
|
+
"""Build base results dictionary with trace metadata."""
|
|
204
224
|
sample_rate = trace.metadata.sample_rate
|
|
205
225
|
duration_ms = len(trace.data) / sample_rate * 1e3
|
|
206
|
-
|
|
207
|
-
results: dict[str, Any] = {
|
|
226
|
+
return {
|
|
208
227
|
"sample_rate": f"{sample_rate / 1e6:.1f} MHz",
|
|
209
228
|
"samples": len(trace.data),
|
|
210
229
|
"duration": f"{duration_ms:.3f} ms",
|
|
211
230
|
}
|
|
212
231
|
|
|
213
|
-
# Auto-detect protocol if requested
|
|
214
|
-
detected_protocol = protocol
|
|
215
|
-
detection_confidence = 1.0
|
|
216
232
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
{"protocol": c["protocol"], "confidence": f"{c['confidence']:.1%}"}
|
|
227
|
-
for c in detection.get("candidates", [])[:3]
|
|
228
|
-
],
|
|
229
|
-
}
|
|
230
|
-
# Extract config suggestions
|
|
231
|
-
if "config" in detection:
|
|
232
|
-
if detected_protocol == "uart" and baud_rate is None:
|
|
233
|
-
baud_rate = detection["config"].get("baud_rate")
|
|
234
|
-
except Exception as e:
|
|
235
|
-
logger.warning(f"Auto-detection failed: {e}, defaulting to UART")
|
|
236
|
-
detected_protocol = "uart"
|
|
237
|
-
detection_confidence = 0.0
|
|
233
|
+
def _detect_protocol_if_auto(
|
|
234
|
+
trace: WaveformTrace | DigitalTrace,
|
|
235
|
+
protocol: str,
|
|
236
|
+
baud_rate: int | None,
|
|
237
|
+
results: dict[str, Any],
|
|
238
|
+
) -> tuple[str, int | None]:
|
|
239
|
+
"""Auto-detect protocol if requested, otherwise return protocol as-is."""
|
|
240
|
+
if protocol.lower() != "auto":
|
|
241
|
+
return protocol, baud_rate
|
|
238
242
|
|
|
239
|
-
|
|
243
|
+
from oscura.inference.protocol import detect_protocol
|
|
240
244
|
|
|
241
|
-
|
|
242
|
-
|
|
245
|
+
try:
|
|
246
|
+
detection = detect_protocol(trace, min_confidence=0.5, return_candidates=True) # type: ignore[arg-type]
|
|
247
|
+
detected_protocol = detection["protocol"].lower()
|
|
248
|
+
results["auto_detection"] = _format_detection_results(detection)
|
|
249
|
+
|
|
250
|
+
# Extract config suggestions
|
|
251
|
+
if "config" in detection and detected_protocol == "uart" and baud_rate is None:
|
|
252
|
+
baud_rate = detection["config"].get("baud_rate")
|
|
253
|
+
|
|
254
|
+
return detected_protocol, baud_rate
|
|
255
|
+
except Exception as e:
|
|
256
|
+
logger.warning(f"Auto-detection failed: {e}, defaulting to UART")
|
|
257
|
+
return "uart", baud_rate
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _format_detection_results(detection: dict[str, Any]) -> dict[str, Any]:
|
|
261
|
+
"""Format auto-detection results for display."""
|
|
262
|
+
return {
|
|
263
|
+
"protocol": detection["protocol"],
|
|
264
|
+
"confidence": f"{detection['confidence']:.1%}",
|
|
265
|
+
"candidates": [
|
|
266
|
+
{"protocol": c["protocol"], "confidence": f"{c['confidence']:.1%}"}
|
|
267
|
+
for c in detection.get("candidates", [])[:3]
|
|
268
|
+
],
|
|
269
|
+
}
|
|
243
270
|
|
|
244
|
-
# Decode based on protocol
|
|
245
|
-
packets: list[ProtocolPacket] = []
|
|
246
|
-
errors: list[dict[str, Any]] = []
|
|
247
271
|
|
|
248
|
-
|
|
272
|
+
def _dispatch_protocol_decode(
|
|
273
|
+
digital_trace: DigitalTrace,
|
|
274
|
+
protocol: str,
|
|
275
|
+
baud_rate: int | None,
|
|
276
|
+
parity: str,
|
|
277
|
+
stop_bits: int,
|
|
278
|
+
show_errors: bool,
|
|
279
|
+
results: dict[str, Any],
|
|
280
|
+
) -> tuple[list[ProtocolPacket], list[dict[str, Any]]]:
|
|
281
|
+
"""Dispatch to appropriate protocol decoder."""
|
|
282
|
+
if protocol == "uart":
|
|
249
283
|
packets, errors, protocol_info = _decode_uart(
|
|
250
284
|
digital_trace, baud_rate, parity, stop_bits, show_errors
|
|
251
285
|
)
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
elif detected_protocol == "spi":
|
|
286
|
+
elif protocol == "spi":
|
|
255
287
|
packets, errors, protocol_info = _decode_spi(digital_trace, show_errors)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
elif detected_protocol == "i2c":
|
|
288
|
+
elif protocol == "i2c":
|
|
259
289
|
packets, errors, protocol_info = _decode_i2c(digital_trace, show_errors)
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
elif detected_protocol == "can":
|
|
290
|
+
elif protocol == "can":
|
|
263
291
|
packets, errors, protocol_info = _decode_can(digital_trace, baud_rate, show_errors)
|
|
264
|
-
|
|
292
|
+
else:
|
|
293
|
+
packets, errors, protocol_info = [], [], {}
|
|
265
294
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
packets = [p for p in packets if p.errors]
|
|
295
|
+
results.update(protocol_info)
|
|
296
|
+
return packets, errors
|
|
269
297
|
|
|
270
|
-
|
|
298
|
+
|
|
299
|
+
def _add_packet_summary(
|
|
300
|
+
results: dict[str, Any],
|
|
301
|
+
packets: list[ProtocolPacket],
|
|
302
|
+
errors: list[dict[str, Any]],
|
|
303
|
+
) -> None:
|
|
304
|
+
"""Add packet summary and details to results."""
|
|
271
305
|
results["packets_decoded"] = len(packets)
|
|
272
306
|
results["errors_found"] = len(errors)
|
|
273
307
|
|
|
274
|
-
# Add packet details
|
|
275
308
|
results["packets"] = [
|
|
276
309
|
{
|
|
277
310
|
"index": i,
|
|
@@ -280,17 +313,14 @@ def _perform_decoding(
|
|
|
280
313
|
"errors": p.errors,
|
|
281
314
|
**{k: v for k, v in (p.annotations or {}).items() if k != "data_bits"},
|
|
282
315
|
}
|
|
283
|
-
for i, p in enumerate(packets[:100])
|
|
316
|
+
for i, p in enumerate(packets[:100])
|
|
284
317
|
]
|
|
285
318
|
|
|
286
319
|
if len(packets) > 100:
|
|
287
320
|
results["note"] = f"Showing first 100 of {len(packets)} packets"
|
|
288
321
|
|
|
289
|
-
# Add error details if any
|
|
290
322
|
if errors:
|
|
291
|
-
results["error_details"] = errors[:20]
|
|
292
|
-
|
|
293
|
-
return results
|
|
323
|
+
results["error_details"] = errors[:20]
|
|
294
324
|
|
|
295
325
|
|
|
296
326
|
def _decode_uart(
|
|
@@ -498,29 +528,60 @@ def _decode_can(
|
|
|
498
528
|
"""
|
|
499
529
|
from oscura.analyzers.protocols.can import CANDecoder
|
|
500
530
|
|
|
501
|
-
# Try common CAN baud rates if not specified
|
|
502
531
|
if baud_rate is None:
|
|
503
|
-
|
|
504
|
-
best_rate = 500000
|
|
505
|
-
max_packets = 0
|
|
506
|
-
|
|
507
|
-
for rate in common_rates:
|
|
508
|
-
try:
|
|
509
|
-
decoder = CANDecoder(bitrate=rate)
|
|
510
|
-
test_packets = list(decoder.decode(trace))
|
|
511
|
-
if len(test_packets) > max_packets:
|
|
512
|
-
max_packets = len(test_packets)
|
|
513
|
-
best_rate = rate
|
|
514
|
-
except Exception:
|
|
515
|
-
continue
|
|
516
|
-
|
|
517
|
-
baud_rate = best_rate
|
|
532
|
+
baud_rate = _detect_can_baud_rate(trace)
|
|
518
533
|
|
|
519
534
|
decoder = CANDecoder(bitrate=baud_rate)
|
|
520
535
|
packets = list(decoder.decode(trace))
|
|
521
|
-
errors = []
|
|
522
536
|
|
|
537
|
+
errors, arbitration_ids = _extract_can_errors(packets)
|
|
538
|
+
protocol_info = _build_can_protocol_info(baud_rate, packets, arbitration_ids)
|
|
539
|
+
|
|
540
|
+
return packets, errors, protocol_info
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
def _detect_can_baud_rate(trace: DigitalTrace) -> int:
|
|
544
|
+
"""Detect CAN baud rate by trying common rates.
|
|
545
|
+
|
|
546
|
+
Args:
|
|
547
|
+
trace: Digital trace to analyze.
|
|
548
|
+
|
|
549
|
+
Returns:
|
|
550
|
+
Best matching baud rate.
|
|
551
|
+
"""
|
|
552
|
+
from oscura.analyzers.protocols.can import CANDecoder
|
|
553
|
+
|
|
554
|
+
common_rates = [500000, 250000, 125000, 1000000]
|
|
555
|
+
best_rate = 500000
|
|
556
|
+
max_packets = 0
|
|
557
|
+
|
|
558
|
+
for rate in common_rates:
|
|
559
|
+
try:
|
|
560
|
+
decoder = CANDecoder(bitrate=rate)
|
|
561
|
+
test_packets = list(decoder.decode(trace))
|
|
562
|
+
if len(test_packets) > max_packets:
|
|
563
|
+
max_packets = len(test_packets)
|
|
564
|
+
best_rate = rate
|
|
565
|
+
except Exception:
|
|
566
|
+
continue
|
|
567
|
+
|
|
568
|
+
return best_rate
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def _extract_can_errors(
|
|
572
|
+
packets: list[ProtocolPacket],
|
|
573
|
+
) -> tuple[list[dict[str, Any]], set[int]]:
|
|
574
|
+
"""Extract errors and arbitration IDs from CAN packets.
|
|
575
|
+
|
|
576
|
+
Args:
|
|
577
|
+
packets: Decoded packets.
|
|
578
|
+
|
|
579
|
+
Returns:
|
|
580
|
+
Tuple of (errors list, arbitration ID set).
|
|
581
|
+
"""
|
|
582
|
+
errors = []
|
|
523
583
|
arbitration_ids: set[int] = set()
|
|
584
|
+
|
|
524
585
|
for i, pkt in enumerate(packets):
|
|
525
586
|
arb_id = pkt.annotations.get("arbitration_id", 0) if pkt.annotations else 0
|
|
526
587
|
arbitration_ids.add(arb_id)
|
|
@@ -536,16 +597,32 @@ def _decode_can(
|
|
|
536
597
|
}
|
|
537
598
|
)
|
|
538
599
|
|
|
539
|
-
|
|
600
|
+
return errors, arbitration_ids
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
def _build_can_protocol_info(
|
|
604
|
+
baud_rate: int, packets: list[ProtocolPacket], arbitration_ids: set[int]
|
|
605
|
+
) -> dict[str, Any]:
|
|
606
|
+
"""Build CAN protocol info dictionary.
|
|
607
|
+
|
|
608
|
+
Args:
|
|
609
|
+
baud_rate: Detected baud rate.
|
|
610
|
+
packets: Decoded packets.
|
|
611
|
+
arbitration_ids: Set of arbitration IDs.
|
|
612
|
+
|
|
613
|
+
Returns:
|
|
614
|
+
Protocol info dictionary.
|
|
615
|
+
"""
|
|
616
|
+
extended_count = sum(1 for p in packets if p.annotations and p.annotations.get("is_extended"))
|
|
617
|
+
|
|
618
|
+
info = {
|
|
540
619
|
"bit_rate": f"{baud_rate / 1000:.0f} kbps",
|
|
541
620
|
"messages": len(packets),
|
|
542
621
|
"arbitration_ids": [f"0x{a:03X}" for a in sorted(arbitration_ids)[:10]],
|
|
543
|
-
"extended_frames":
|
|
544
|
-
1 for p in packets if p.annotations and p.annotations.get("is_extended")
|
|
545
|
-
),
|
|
622
|
+
"extended_frames": extended_count,
|
|
546
623
|
}
|
|
547
624
|
|
|
548
625
|
if len(arbitration_ids) > 10:
|
|
549
|
-
|
|
626
|
+
info["note"] = f"Showing first 10 of {len(arbitration_ids)} arbitration IDs"
|
|
550
627
|
|
|
551
|
-
return
|
|
628
|
+
return info
|
oscura/cli/export.py
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"""Oscura Export Command - Export Analysis Results.
|
|
2
|
+
|
|
3
|
+
Provides CLI for exporting analysis sessions and results to various formats.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
$ oscura export json session.tks --output results.json
|
|
8
|
+
$ oscura export html session.tks --output report.html
|
|
9
|
+
$ oscura export wireshark session.tks --output dissector.lua
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import logging
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
import click
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger("oscura.cli.export")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@click.command()
|
|
24
|
+
@click.argument(
|
|
25
|
+
"format",
|
|
26
|
+
type=click.Choice(
|
|
27
|
+
["json", "html", "csv", "matlab", "wireshark", "scapy", "kaitai"], case_sensitive=False
|
|
28
|
+
),
|
|
29
|
+
)
|
|
30
|
+
@click.argument("session", type=click.Path(exists=True))
|
|
31
|
+
@click.option(
|
|
32
|
+
"--output",
|
|
33
|
+
"-o",
|
|
34
|
+
type=click.Path(),
|
|
35
|
+
required=True,
|
|
36
|
+
help="Output file path.",
|
|
37
|
+
)
|
|
38
|
+
@click.option(
|
|
39
|
+
"--include-traces",
|
|
40
|
+
is_flag=True,
|
|
41
|
+
default=True,
|
|
42
|
+
help="Include trace data in export (default: True).",
|
|
43
|
+
)
|
|
44
|
+
@click.pass_context
|
|
45
|
+
def export(
|
|
46
|
+
ctx: click.Context,
|
|
47
|
+
format: str,
|
|
48
|
+
session: str,
|
|
49
|
+
output: str,
|
|
50
|
+
include_traces: bool,
|
|
51
|
+
) -> None:
|
|
52
|
+
"""Export analysis session to various formats.
|
|
53
|
+
|
|
54
|
+
Supports exporting to JSON, HTML, CSV, MATLAB, Wireshark dissector,
|
|
55
|
+
Scapy layer, and Kaitai struct formats.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
ctx: Click context object.
|
|
59
|
+
format: Export format.
|
|
60
|
+
session: Path to session file (.tks).
|
|
61
|
+
output: Output file path.
|
|
62
|
+
include_traces: Include trace data in export.
|
|
63
|
+
|
|
64
|
+
Examples:
|
|
65
|
+
|
|
66
|
+
\b
|
|
67
|
+
# Export to JSON
|
|
68
|
+
$ oscura export json session.tks --output results.json
|
|
69
|
+
|
|
70
|
+
\b
|
|
71
|
+
# Generate HTML report
|
|
72
|
+
$ oscura export html session.tks --output report.html
|
|
73
|
+
|
|
74
|
+
\b
|
|
75
|
+
# Generate Wireshark dissector
|
|
76
|
+
$ oscura export wireshark session.tks --output protocol.lua
|
|
77
|
+
"""
|
|
78
|
+
verbose = ctx.obj.get("verbose", 0)
|
|
79
|
+
|
|
80
|
+
if verbose:
|
|
81
|
+
logger.info(f"Exporting session {session} to {format}")
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
# Note: Session loading functionality has been redesigned
|
|
85
|
+
# For now, this is a placeholder - session export needs to be reimplemented
|
|
86
|
+
# using the new AnalysisSession architecture
|
|
87
|
+
raise NotImplementedError(
|
|
88
|
+
"Session export has been redesigned. Use the new AnalysisSession API instead."
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
except Exception as e:
|
|
92
|
+
logger.error(f"Export failed: {e}")
|
|
93
|
+
if verbose > 1:
|
|
94
|
+
raise
|
|
95
|
+
click.echo(f"Error: {e}", err=True)
|
|
96
|
+
ctx.exit(1)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _export_json(session: Any, output_path: Path, include_traces: bool) -> None:
|
|
100
|
+
"""Export session to JSON.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
session: Session object.
|
|
104
|
+
output_path: Output file path.
|
|
105
|
+
include_traces: Include trace data.
|
|
106
|
+
"""
|
|
107
|
+
import json
|
|
108
|
+
|
|
109
|
+
data = session._to_dict(include_traces=include_traces)
|
|
110
|
+
|
|
111
|
+
with open(output_path, "w") as f:
|
|
112
|
+
json.dump(data, f, indent=2, default=str)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _export_html(session: Any, output_path: Path) -> None:
|
|
116
|
+
"""Export session to HTML report.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
session: Session object.
|
|
120
|
+
output_path: Output file path.
|
|
121
|
+
"""
|
|
122
|
+
html_content = f"""<!DOCTYPE html>
|
|
123
|
+
<html>
|
|
124
|
+
<head>
|
|
125
|
+
<meta charset='utf-8'>
|
|
126
|
+
<title>Oscura Analysis Report - {session.name}</title>
|
|
127
|
+
<style>
|
|
128
|
+
body {{ font-family: Arial, sans-serif; margin: 20px; }}
|
|
129
|
+
h1 {{ color: #4CAF50; }}
|
|
130
|
+
table {{ border-collapse: collapse; width: 100%; margin: 20px 0; }}
|
|
131
|
+
th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
|
|
132
|
+
th {{ background-color: #4CAF50; color: white; }}
|
|
133
|
+
tr:nth-child(even) {{ background-color: #f2f2f2; }}
|
|
134
|
+
.metadata {{ background: #f9f9f9; padding: 10px; margin: 10px 0; }}
|
|
135
|
+
</style>
|
|
136
|
+
</head>
|
|
137
|
+
<body>
|
|
138
|
+
<h1>Oscura Analysis Report</h1>
|
|
139
|
+
<div class="metadata">
|
|
140
|
+
<h2>Session: {session.name}</h2>
|
|
141
|
+
<p>Created: {session.created_at}</p>
|
|
142
|
+
<p>Modified: {session.modified_at}</p>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<h2>Traces ({len(session.traces)})</h2>
|
|
146
|
+
<ul>
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
for trace_name in session.list_traces():
|
|
150
|
+
html_content += f" <li>{trace_name}</li>\n"
|
|
151
|
+
|
|
152
|
+
html_content += """ </ul>
|
|
153
|
+
|
|
154
|
+
<h2>Measurements</h2>
|
|
155
|
+
<table>
|
|
156
|
+
<tr><th>Measurement</th><th>Value</th><th>Unit</th></tr>
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
for name, meas in session.get_measurements().items():
|
|
160
|
+
value = meas.get("value", "N/A")
|
|
161
|
+
unit = meas.get("unit", "")
|
|
162
|
+
html_content += f" <tr><td>{name}</td><td>{value}</td><td>{unit}</td></tr>\n"
|
|
163
|
+
|
|
164
|
+
html_content += """ </table>
|
|
165
|
+
</body>
|
|
166
|
+
</html>
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
with open(output_path, "w") as f:
|
|
170
|
+
f.write(html_content)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _export_csv(session: Any, output_path: Path) -> None:
|
|
174
|
+
"""Export measurements to CSV.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
session: Session object.
|
|
178
|
+
output_path: Output file path.
|
|
179
|
+
"""
|
|
180
|
+
import csv
|
|
181
|
+
|
|
182
|
+
with open(output_path, "w", newline="") as f:
|
|
183
|
+
writer = csv.writer(f)
|
|
184
|
+
writer.writerow(["Measurement", "Value", "Unit", "Trace"])
|
|
185
|
+
|
|
186
|
+
for name, meas in session.get_measurements().items():
|
|
187
|
+
writer.writerow(
|
|
188
|
+
[
|
|
189
|
+
name,
|
|
190
|
+
meas.get("value", ""),
|
|
191
|
+
meas.get("unit", ""),
|
|
192
|
+
meas.get("trace", ""),
|
|
193
|
+
]
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _export_matlab(session: Any, output_path: Path) -> None:
|
|
198
|
+
"""Export to MATLAB format.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
session: Session object.
|
|
202
|
+
output_path: Output file path.
|
|
203
|
+
"""
|
|
204
|
+
# MATLAB export has been redesigned - use new AnalysisSession API
|
|
205
|
+
raise NotImplementedError("MATLAB export needs reimplementation with new API")
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _export_wireshark(session: Any, output_path: Path) -> None:
|
|
209
|
+
"""Export to Wireshark dissector.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
session: Session object.
|
|
213
|
+
output_path: Output file path.
|
|
214
|
+
"""
|
|
215
|
+
# Placeholder - requires protocol specification
|
|
216
|
+
lua_template = """-- Wireshark Dissector for Unknown Protocol
|
|
217
|
+
-- Generated by Oscura
|
|
218
|
+
|
|
219
|
+
local proto = Proto("unknown", "Unknown Protocol")
|
|
220
|
+
|
|
221
|
+
function proto.dissector(buffer, pinfo, tree)
|
|
222
|
+
pinfo.cols.protocol = "UNKNOWN"
|
|
223
|
+
local subtree = tree:add(proto, buffer(), "Unknown Protocol Data")
|
|
224
|
+
subtree:add(buffer(0), "Raw data: " .. buffer():bytes():tohex())
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
-- Register dissector
|
|
228
|
+
local udp_table = DissectorTable.get("udp.port")
|
|
229
|
+
-- udp_table:add(YOUR_PORT, proto)
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
with open(output_path, "w") as f:
|
|
233
|
+
f.write(lua_template)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def _export_scapy(session: Any, output_path: Path) -> None:
|
|
237
|
+
"""Export to Scapy layer.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
session: Session object.
|
|
241
|
+
output_path: Output file path.
|
|
242
|
+
"""
|
|
243
|
+
# Placeholder - requires protocol specification
|
|
244
|
+
scapy_template = """# Scapy Layer for Unknown Protocol
|
|
245
|
+
# Generated by Oscura
|
|
246
|
+
|
|
247
|
+
from scapy.all import *
|
|
248
|
+
|
|
249
|
+
class UnknownProtocol(Packet):
|
|
250
|
+
name = "UnknownProtocol"
|
|
251
|
+
fields_desc = [
|
|
252
|
+
# Define fields here
|
|
253
|
+
XByteField("data", 0x00),
|
|
254
|
+
]
|
|
255
|
+
|
|
256
|
+
# Bind to lower layer if needed
|
|
257
|
+
# bind_layers(UDP, UnknownProtocol, dport=YOUR_PORT)
|
|
258
|
+
"""
|
|
259
|
+
|
|
260
|
+
with open(output_path, "w") as f:
|
|
261
|
+
f.write(scapy_template)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _export_kaitai(session: Any, output_path: Path) -> None:
|
|
265
|
+
"""Export to Kaitai struct.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
session: Session object.
|
|
269
|
+
output_path: Output file path.
|
|
270
|
+
"""
|
|
271
|
+
# Placeholder - requires protocol specification
|
|
272
|
+
kaitai_template = """meta:
|
|
273
|
+
id: unknown_protocol
|
|
274
|
+
title: Unknown Protocol
|
|
275
|
+
file-extension: dat
|
|
276
|
+
endian: le
|
|
277
|
+
|
|
278
|
+
seq:
|
|
279
|
+
- id: header
|
|
280
|
+
type: u1
|
|
281
|
+
- id: data
|
|
282
|
+
size-eos: true
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
with open(output_path, "w") as f:
|
|
286
|
+
f.write(kaitai_template)
|