oscura 0.5.1__py3-none-any.whl → 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- oscura/__init__.py +169 -167
- oscura/analyzers/__init__.py +3 -0
- oscura/analyzers/classification.py +659 -0
- oscura/analyzers/digital/edges.py +325 -65
- oscura/analyzers/digital/quality.py +293 -166
- oscura/analyzers/digital/timing.py +260 -115
- oscura/analyzers/digital/timing_numba.py +334 -0
- oscura/analyzers/entropy.py +605 -0
- oscura/analyzers/eye/diagram.py +176 -109
- oscura/analyzers/eye/metrics.py +5 -5
- oscura/analyzers/jitter/__init__.py +6 -4
- oscura/analyzers/jitter/ber.py +52 -52
- oscura/analyzers/jitter/classification.py +156 -0
- oscura/analyzers/jitter/decomposition.py +163 -113
- oscura/analyzers/jitter/spectrum.py +80 -64
- oscura/analyzers/ml/__init__.py +39 -0
- oscura/analyzers/ml/features.py +600 -0
- oscura/analyzers/ml/signal_classifier.py +604 -0
- oscura/analyzers/packet/daq.py +246 -158
- oscura/analyzers/packet/parser.py +12 -1
- oscura/analyzers/packet/payload.py +50 -2110
- oscura/analyzers/packet/payload_analysis.py +361 -181
- oscura/analyzers/packet/payload_patterns.py +133 -70
- oscura/analyzers/packet/stream.py +84 -23
- oscura/analyzers/patterns/__init__.py +26 -5
- oscura/analyzers/patterns/anomaly_detection.py +908 -0
- oscura/analyzers/patterns/clustering.py +169 -108
- oscura/analyzers/patterns/clustering_optimized.py +227 -0
- oscura/analyzers/patterns/discovery.py +1 -1
- oscura/analyzers/patterns/matching.py +581 -197
- oscura/analyzers/patterns/pattern_mining.py +778 -0
- oscura/analyzers/patterns/periodic.py +121 -38
- oscura/analyzers/patterns/sequences.py +175 -78
- oscura/analyzers/power/conduction.py +1 -1
- oscura/analyzers/power/soa.py +6 -6
- oscura/analyzers/power/switching.py +250 -110
- oscura/analyzers/protocol/__init__.py +17 -1
- oscura/analyzers/protocols/base.py +6 -6
- oscura/analyzers/protocols/ble/__init__.py +38 -0
- oscura/analyzers/protocols/ble/analyzer.py +809 -0
- oscura/analyzers/protocols/ble/uuids.py +288 -0
- oscura/analyzers/protocols/can.py +257 -127
- oscura/analyzers/protocols/can_fd.py +107 -80
- oscura/analyzers/protocols/flexray.py +139 -80
- oscura/analyzers/protocols/hdlc.py +93 -58
- oscura/analyzers/protocols/i2c.py +247 -106
- oscura/analyzers/protocols/i2s.py +138 -86
- oscura/analyzers/protocols/industrial/__init__.py +40 -0
- oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
- oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
- oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
- oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
- oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
- oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
- oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
- oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
- oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
- oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
- oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
- oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
- oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
- oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
- oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
- oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
- oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
- oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
- oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
- oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
- oscura/analyzers/protocols/jtag.py +180 -98
- oscura/analyzers/protocols/lin.py +219 -114
- oscura/analyzers/protocols/manchester.py +4 -4
- oscura/analyzers/protocols/onewire.py +253 -149
- oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
- oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
- oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
- oscura/analyzers/protocols/spi.py +192 -95
- oscura/analyzers/protocols/swd.py +321 -167
- oscura/analyzers/protocols/uart.py +267 -125
- oscura/analyzers/protocols/usb.py +235 -131
- oscura/analyzers/side_channel/power.py +17 -12
- oscura/analyzers/signal/__init__.py +15 -0
- oscura/analyzers/signal/timing_analysis.py +1086 -0
- oscura/analyzers/signal_integrity/__init__.py +4 -1
- oscura/analyzers/signal_integrity/sparams.py +2 -19
- oscura/analyzers/spectral/chunked.py +129 -60
- oscura/analyzers/spectral/chunked_fft.py +300 -94
- oscura/analyzers/spectral/chunked_wavelet.py +100 -80
- oscura/analyzers/statistical/checksum.py +376 -217
- oscura/analyzers/statistical/classification.py +229 -107
- oscura/analyzers/statistical/entropy.py +78 -53
- oscura/analyzers/statistics/correlation.py +407 -211
- oscura/analyzers/statistics/outliers.py +2 -2
- oscura/analyzers/statistics/streaming.py +30 -5
- oscura/analyzers/validation.py +216 -101
- oscura/analyzers/waveform/measurements.py +9 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
- oscura/analyzers/waveform/spectral.py +500 -228
- oscura/api/__init__.py +31 -5
- oscura/api/dsl/__init__.py +582 -0
- oscura/{dsl → api/dsl}/commands.py +43 -76
- oscura/{dsl → api/dsl}/interpreter.py +26 -51
- oscura/{dsl → api/dsl}/parser.py +107 -77
- oscura/{dsl → api/dsl}/repl.py +2 -2
- oscura/api/dsl.py +1 -1
- oscura/{integrations → api/integrations}/__init__.py +1 -1
- oscura/{integrations → api/integrations}/llm.py +201 -102
- oscura/api/operators.py +3 -3
- oscura/api/optimization.py +144 -30
- oscura/api/rest_server.py +921 -0
- oscura/api/server/__init__.py +17 -0
- oscura/api/server/dashboard.py +850 -0
- oscura/api/server/static/README.md +34 -0
- oscura/api/server/templates/base.html +181 -0
- oscura/api/server/templates/export.html +120 -0
- oscura/api/server/templates/home.html +284 -0
- oscura/api/server/templates/protocols.html +58 -0
- oscura/api/server/templates/reports.html +43 -0
- oscura/api/server/templates/session_detail.html +89 -0
- oscura/api/server/templates/sessions.html +83 -0
- oscura/api/server/templates/waveforms.html +73 -0
- oscura/automotive/__init__.py +8 -1
- oscura/automotive/can/__init__.py +10 -0
- oscura/automotive/can/checksum.py +3 -1
- oscura/automotive/can/dbc_generator.py +590 -0
- oscura/automotive/can/message_wrapper.py +121 -74
- oscura/automotive/can/patterns.py +98 -21
- oscura/automotive/can/session.py +292 -56
- oscura/automotive/can/state_machine.py +6 -3
- oscura/automotive/can/stimulus_response.py +97 -75
- oscura/automotive/dbc/__init__.py +10 -2
- oscura/automotive/dbc/generator.py +84 -56
- oscura/automotive/dbc/parser.py +6 -6
- oscura/automotive/dtc/data.json +17 -102
- oscura/automotive/dtc/database.py +2 -2
- oscura/automotive/flexray/__init__.py +31 -0
- oscura/automotive/flexray/analyzer.py +504 -0
- oscura/automotive/flexray/crc.py +185 -0
- oscura/automotive/flexray/fibex.py +449 -0
- oscura/automotive/j1939/__init__.py +45 -8
- oscura/automotive/j1939/analyzer.py +605 -0
- oscura/automotive/j1939/spns.py +326 -0
- oscura/automotive/j1939/transport.py +306 -0
- oscura/automotive/lin/__init__.py +47 -0
- oscura/automotive/lin/analyzer.py +612 -0
- oscura/automotive/loaders/blf.py +13 -2
- oscura/automotive/loaders/csv_can.py +143 -72
- oscura/automotive/loaders/dispatcher.py +50 -2
- oscura/automotive/loaders/mdf.py +86 -45
- oscura/automotive/loaders/pcap.py +111 -61
- oscura/automotive/uds/__init__.py +4 -0
- oscura/automotive/uds/analyzer.py +725 -0
- oscura/automotive/uds/decoder.py +140 -58
- oscura/automotive/uds/models.py +7 -1
- oscura/automotive/visualization.py +1 -1
- oscura/cli/analyze.py +348 -0
- oscura/cli/batch.py +142 -122
- oscura/cli/benchmark.py +275 -0
- oscura/cli/characterize.py +137 -82
- oscura/cli/compare.py +224 -131
- oscura/cli/completion.py +250 -0
- oscura/cli/config_cmd.py +361 -0
- oscura/cli/decode.py +164 -87
- oscura/cli/export.py +286 -0
- oscura/cli/main.py +115 -31
- oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
- oscura/{onboarding → cli/onboarding}/help.py +80 -58
- oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
- oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
- oscura/cli/progress.py +147 -0
- oscura/cli/shell.py +157 -135
- oscura/cli/validate_cmd.py +204 -0
- oscura/cli/visualize.py +158 -0
- oscura/convenience.py +125 -79
- oscura/core/__init__.py +4 -2
- oscura/core/backend_selector.py +3 -3
- oscura/core/cache.py +126 -15
- oscura/core/cancellation.py +1 -1
- oscura/{config → core/config}/__init__.py +20 -11
- oscura/{config → core/config}/defaults.py +1 -1
- oscura/{config → core/config}/loader.py +7 -5
- oscura/{config → core/config}/memory.py +5 -5
- oscura/{config → core/config}/migration.py +1 -1
- oscura/{config → core/config}/pipeline.py +99 -23
- oscura/{config → core/config}/preferences.py +1 -1
- oscura/{config → core/config}/protocol.py +3 -3
- oscura/{config → core/config}/schema.py +426 -272
- oscura/{config → core/config}/settings.py +1 -1
- oscura/{config → core/config}/thresholds.py +195 -153
- oscura/core/correlation.py +5 -6
- oscura/core/cross_domain.py +0 -2
- oscura/core/debug.py +9 -5
- oscura/{extensibility → core/extensibility}/docs.py +158 -70
- oscura/{extensibility → core/extensibility}/extensions.py +160 -76
- oscura/{extensibility → core/extensibility}/logging.py +1 -1
- oscura/{extensibility → core/extensibility}/measurements.py +1 -1
- oscura/{extensibility → core/extensibility}/plugins.py +1 -1
- oscura/{extensibility → core/extensibility}/templates.py +73 -3
- oscura/{extensibility → core/extensibility}/validation.py +1 -1
- oscura/core/gpu_backend.py +11 -7
- oscura/core/log_query.py +101 -11
- oscura/core/logging.py +126 -54
- oscura/core/logging_advanced.py +5 -5
- oscura/core/memory_limits.py +108 -70
- oscura/core/memory_monitor.py +2 -2
- oscura/core/memory_progress.py +7 -7
- oscura/core/memory_warnings.py +1 -1
- oscura/core/numba_backend.py +13 -13
- oscura/{plugins → core/plugins}/__init__.py +9 -9
- oscura/{plugins → core/plugins}/base.py +7 -7
- oscura/{plugins → core/plugins}/cli.py +3 -3
- oscura/{plugins → core/plugins}/discovery.py +186 -106
- oscura/{plugins → core/plugins}/lifecycle.py +1 -1
- oscura/{plugins → core/plugins}/manager.py +7 -7
- oscura/{plugins → core/plugins}/registry.py +3 -3
- oscura/{plugins → core/plugins}/versioning.py +1 -1
- oscura/core/progress.py +16 -1
- oscura/core/provenance.py +8 -2
- oscura/{schemas → core/schemas}/__init__.py +2 -2
- oscura/{schemas → core/schemas}/device_mapping.json +2 -8
- oscura/{schemas → core/schemas}/packet_format.json +4 -24
- oscura/{schemas → core/schemas}/protocol_definition.json +2 -12
- oscura/core/types.py +4 -0
- oscura/core/uncertainty.py +3 -3
- oscura/correlation/__init__.py +52 -0
- oscura/correlation/multi_protocol.py +811 -0
- oscura/discovery/auto_decoder.py +117 -35
- oscura/discovery/comparison.py +191 -86
- oscura/discovery/quality_validator.py +155 -68
- oscura/discovery/signal_detector.py +196 -79
- oscura/export/__init__.py +18 -8
- oscura/export/kaitai_struct.py +513 -0
- oscura/export/scapy_layer.py +801 -0
- oscura/export/wireshark/generator.py +1 -1
- oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
- oscura/export/wireshark_dissector.py +746 -0
- oscura/guidance/wizard.py +207 -111
- oscura/hardware/__init__.py +19 -0
- oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
- oscura/{acquisition → hardware/acquisition}/file.py +2 -2
- oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
- oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
- oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
- oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
- oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
- oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
- oscura/hardware/firmware/__init__.py +29 -0
- oscura/hardware/firmware/pattern_recognition.py +874 -0
- oscura/hardware/hal_detector.py +736 -0
- oscura/hardware/security/__init__.py +37 -0
- oscura/hardware/security/side_channel_detector.py +1126 -0
- oscura/inference/__init__.py +4 -0
- oscura/inference/active_learning/observation_table.py +4 -1
- oscura/inference/alignment.py +216 -123
- oscura/inference/bayesian.py +113 -33
- oscura/inference/crc_reverse.py +101 -55
- oscura/inference/logic.py +6 -2
- oscura/inference/message_format.py +342 -183
- oscura/inference/protocol.py +95 -44
- oscura/inference/protocol_dsl.py +180 -82
- oscura/inference/signal_intelligence.py +1439 -706
- oscura/inference/spectral.py +99 -57
- oscura/inference/state_machine.py +810 -158
- oscura/inference/stream.py +270 -110
- oscura/iot/__init__.py +34 -0
- oscura/iot/coap/__init__.py +32 -0
- oscura/iot/coap/analyzer.py +668 -0
- oscura/iot/coap/options.py +212 -0
- oscura/iot/lorawan/__init__.py +21 -0
- oscura/iot/lorawan/crypto.py +206 -0
- oscura/iot/lorawan/decoder.py +801 -0
- oscura/iot/lorawan/mac_commands.py +341 -0
- oscura/iot/mqtt/__init__.py +27 -0
- oscura/iot/mqtt/analyzer.py +999 -0
- oscura/iot/mqtt/properties.py +315 -0
- oscura/iot/zigbee/__init__.py +31 -0
- oscura/iot/zigbee/analyzer.py +615 -0
- oscura/iot/zigbee/security.py +153 -0
- oscura/iot/zigbee/zcl.py +349 -0
- oscura/jupyter/display.py +125 -45
- oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
- oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
- oscura/jupyter/exploratory/fuzzy.py +746 -0
- oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
- oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
- oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
- oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
- oscura/jupyter/exploratory/sync.py +612 -0
- oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
- oscura/jupyter/magic.py +4 -4
- oscura/{ui → jupyter/ui}/__init__.py +2 -2
- oscura/{ui → jupyter/ui}/formatters.py +3 -3
- oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
- oscura/loaders/__init__.py +183 -67
- oscura/loaders/binary.py +88 -1
- oscura/loaders/chipwhisperer.py +153 -137
- oscura/loaders/configurable.py +208 -86
- oscura/loaders/csv_loader.py +458 -215
- oscura/loaders/hdf5_loader.py +278 -119
- oscura/loaders/lazy.py +87 -54
- oscura/loaders/mmap_loader.py +1 -1
- oscura/loaders/numpy_loader.py +253 -116
- oscura/loaders/pcap.py +226 -151
- oscura/loaders/rigol.py +110 -49
- oscura/loaders/sigrok.py +201 -78
- oscura/loaders/tdms.py +81 -58
- oscura/loaders/tektronix.py +291 -174
- oscura/loaders/touchstone.py +182 -87
- oscura/loaders/tss.py +456 -0
- oscura/loaders/vcd.py +215 -117
- oscura/loaders/wav.py +155 -68
- oscura/reporting/__init__.py +9 -0
- oscura/reporting/analyze.py +352 -146
- oscura/reporting/argument_preparer.py +69 -14
- oscura/reporting/auto_report.py +97 -61
- oscura/reporting/batch.py +131 -58
- oscura/reporting/chart_selection.py +57 -45
- oscura/reporting/comparison.py +63 -17
- oscura/reporting/content/executive.py +76 -24
- oscura/reporting/core_formats/multi_format.py +11 -8
- oscura/reporting/engine.py +312 -158
- oscura/reporting/enhanced_reports.py +949 -0
- oscura/reporting/export.py +86 -43
- oscura/reporting/formatting/numbers.py +69 -42
- oscura/reporting/html.py +139 -58
- oscura/reporting/index.py +137 -65
- oscura/reporting/output.py +158 -67
- oscura/reporting/pdf.py +67 -102
- oscura/reporting/plots.py +191 -112
- oscura/reporting/sections.py +88 -47
- oscura/reporting/standards.py +104 -61
- oscura/reporting/summary_generator.py +75 -55
- oscura/reporting/tables.py +138 -54
- oscura/reporting/templates/enhanced/protocol_re.html +525 -0
- oscura/sessions/__init__.py +14 -23
- oscura/sessions/base.py +3 -3
- oscura/sessions/blackbox.py +106 -10
- oscura/sessions/generic.py +2 -2
- oscura/sessions/legacy.py +783 -0
- oscura/side_channel/__init__.py +63 -0
- oscura/side_channel/dpa.py +1025 -0
- oscura/utils/__init__.py +15 -1
- oscura/utils/bitwise.py +118 -0
- oscura/{builders → utils/builders}/__init__.py +1 -1
- oscura/{comparison → utils/comparison}/__init__.py +6 -6
- oscura/{comparison → utils/comparison}/compare.py +202 -101
- oscura/{comparison → utils/comparison}/golden.py +83 -63
- oscura/{comparison → utils/comparison}/limits.py +313 -89
- oscura/{comparison → utils/comparison}/mask.py +151 -45
- oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
- oscura/{comparison → utils/comparison}/visualization.py +147 -89
- oscura/{component → utils/component}/__init__.py +3 -3
- oscura/{component → utils/component}/impedance.py +122 -58
- oscura/{component → utils/component}/reactive.py +165 -168
- oscura/{component → utils/component}/transmission_line.py +3 -3
- oscura/{filtering → utils/filtering}/__init__.py +6 -6
- oscura/{filtering → utils/filtering}/base.py +1 -1
- oscura/{filtering → utils/filtering}/convenience.py +2 -2
- oscura/{filtering → utils/filtering}/design.py +169 -93
- oscura/{filtering → utils/filtering}/filters.py +2 -2
- oscura/{filtering → utils/filtering}/introspection.py +2 -2
- oscura/utils/geometry.py +31 -0
- oscura/utils/imports.py +184 -0
- oscura/utils/lazy.py +1 -1
- oscura/{math → utils/math}/__init__.py +2 -2
- oscura/{math → utils/math}/arithmetic.py +114 -48
- oscura/{math → utils/math}/interpolation.py +139 -106
- oscura/utils/memory.py +129 -66
- oscura/utils/memory_advanced.py +92 -9
- oscura/utils/memory_extensions.py +10 -8
- oscura/{optimization → utils/optimization}/__init__.py +1 -1
- oscura/{optimization → utils/optimization}/search.py +2 -2
- oscura/utils/performance/__init__.py +58 -0
- oscura/utils/performance/caching.py +889 -0
- oscura/utils/performance/lsh_clustering.py +333 -0
- oscura/utils/performance/memory_optimizer.py +699 -0
- oscura/utils/performance/optimizations.py +675 -0
- oscura/utils/performance/parallel.py +654 -0
- oscura/utils/performance/profiling.py +661 -0
- oscura/{pipeline → utils/pipeline}/base.py +1 -1
- oscura/{pipeline → utils/pipeline}/composition.py +1 -1
- oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
- oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
- oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
- oscura/{search → utils/search}/__init__.py +3 -3
- oscura/{search → utils/search}/anomaly.py +188 -58
- oscura/utils/search/context.py +294 -0
- oscura/{search → utils/search}/pattern.py +138 -10
- oscura/utils/serial.py +51 -0
- oscura/utils/storage/__init__.py +61 -0
- oscura/utils/storage/database.py +1166 -0
- oscura/{streaming → utils/streaming}/chunked.py +302 -143
- oscura/{streaming → utils/streaming}/progressive.py +1 -1
- oscura/{streaming → utils/streaming}/realtime.py +3 -2
- oscura/{triggering → utils/triggering}/__init__.py +6 -6
- oscura/{triggering → utils/triggering}/base.py +6 -6
- oscura/{triggering → utils/triggering}/edge.py +2 -2
- oscura/{triggering → utils/triggering}/pattern.py +2 -2
- oscura/{triggering → utils/triggering}/pulse.py +115 -74
- oscura/{triggering → utils/triggering}/window.py +2 -2
- oscura/utils/validation.py +32 -0
- oscura/validation/__init__.py +121 -0
- oscura/{compliance → validation/compliance}/__init__.py +5 -5
- oscura/{compliance → validation/compliance}/advanced.py +5 -5
- oscura/{compliance → validation/compliance}/masks.py +1 -1
- oscura/{compliance → validation/compliance}/reporting.py +127 -53
- oscura/{compliance → validation/compliance}/testing.py +114 -52
- oscura/validation/compliance_tests.py +915 -0
- oscura/validation/fuzzer.py +990 -0
- oscura/validation/grammar_tests.py +596 -0
- oscura/validation/grammar_validator.py +904 -0
- oscura/validation/hil_testing.py +977 -0
- oscura/{quality → validation/quality}/__init__.py +4 -4
- oscura/{quality → validation/quality}/ensemble.py +251 -171
- oscura/{quality → validation/quality}/explainer.py +3 -3
- oscura/{quality → validation/quality}/scoring.py +1 -1
- oscura/{quality → validation/quality}/warnings.py +4 -4
- oscura/validation/regression_suite.py +808 -0
- oscura/validation/replay.py +788 -0
- oscura/{testing → validation/testing}/__init__.py +2 -2
- oscura/{testing → validation/testing}/synthetic.py +5 -5
- oscura/visualization/__init__.py +9 -0
- oscura/visualization/accessibility.py +1 -1
- oscura/visualization/annotations.py +64 -67
- oscura/visualization/colors.py +7 -7
- oscura/visualization/digital.py +180 -81
- oscura/visualization/eye.py +236 -85
- oscura/visualization/interactive.py +320 -143
- oscura/visualization/jitter.py +587 -247
- oscura/visualization/layout.py +169 -134
- oscura/visualization/optimization.py +103 -52
- oscura/visualization/palettes.py +1 -1
- oscura/visualization/power.py +427 -211
- oscura/visualization/power_extended.py +626 -297
- oscura/visualization/presets.py +2 -0
- oscura/visualization/protocols.py +495 -181
- oscura/visualization/render.py +79 -63
- oscura/visualization/reverse_engineering.py +171 -124
- oscura/visualization/signal_integrity.py +460 -279
- oscura/visualization/specialized.py +190 -100
- oscura/visualization/spectral.py +670 -255
- oscura/visualization/thumbnails.py +166 -137
- oscura/visualization/waveform.py +150 -63
- oscura/workflows/__init__.py +3 -0
- oscura/{batch → workflows/batch}/__init__.py +5 -5
- oscura/{batch → workflows/batch}/advanced.py +150 -75
- oscura/workflows/batch/aggregate.py +531 -0
- oscura/workflows/batch/analyze.py +236 -0
- oscura/{batch → workflows/batch}/logging.py +2 -2
- oscura/{batch → workflows/batch}/metrics.py +1 -1
- oscura/workflows/complete_re.py +1144 -0
- oscura/workflows/compliance.py +44 -54
- oscura/workflows/digital.py +197 -51
- oscura/workflows/legacy/__init__.py +12 -0
- oscura/{workflow → workflows/legacy}/dag.py +4 -1
- oscura/workflows/multi_trace.py +9 -9
- oscura/workflows/power.py +42 -62
- oscura/workflows/protocol.py +82 -49
- oscura/workflows/reverse_engineering.py +351 -150
- oscura/workflows/signal_integrity.py +157 -82
- oscura-0.7.0.dist-info/METADATA +661 -0
- oscura-0.7.0.dist-info/RECORD +591 -0
- oscura/batch/aggregate.py +0 -300
- oscura/batch/analyze.py +0 -139
- oscura/dsl/__init__.py +0 -73
- oscura/exceptions.py +0 -59
- oscura/exploratory/fuzzy.py +0 -513
- oscura/exploratory/sync.py +0 -384
- oscura/exporters/__init__.py +0 -94
- oscura/exporters/csv.py +0 -303
- oscura/exporters/exporters.py +0 -44
- oscura/exporters/hdf5.py +0 -217
- oscura/exporters/html_export.py +0 -701
- oscura/exporters/json_export.py +0 -291
- oscura/exporters/markdown_export.py +0 -367
- oscura/exporters/matlab_export.py +0 -354
- oscura/exporters/npz_export.py +0 -219
- oscura/exporters/spice_export.py +0 -210
- oscura/search/context.py +0 -149
- oscura/session/__init__.py +0 -34
- oscura/session/annotations.py +0 -289
- oscura/session/history.py +0 -313
- oscura/session/session.py +0 -520
- oscura/workflow/__init__.py +0 -13
- oscura-0.5.1.dist-info/METADATA +0 -583
- oscura-0.5.1.dist-info/RECORD +0 -481
- /oscura/core/{config.py → config/legacy.py} +0 -0
- /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
- /oscura/{extensibility → core/extensibility}/registry.py +0 -0
- /oscura/{plugins → core/plugins}/isolation.py +0 -0
- /oscura/{schemas → core/schemas}/bus_configuration.json +0 -0
- /oscura/{builders → utils/builders}/signal_builder.py +0 -0
- /oscura/{optimization → utils/optimization}/parallel.py +0 -0
- /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
- /oscura/{streaming → utils/streaming}/__init__.py +0 -0
- {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/WHEEL +0 -0
- {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/entry_points.txt +0 -0
- {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"""J1939 Suspect Parameter Number (SPN) definitions.
|
|
2
|
+
|
|
3
|
+
This module provides standard SPN definitions for common J1939 parameters
|
|
4
|
+
according to SAE J1939/71 (Vehicle Application Layer).
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> from oscura.automotive.j1939.spns import get_standard_spns
|
|
8
|
+
>>> spns = get_standard_spns()
|
|
9
|
+
>>> engine_speed = next(s for s in spns[61444] if s.spn == 190)
|
|
10
|
+
>>> engine_speed.name
|
|
11
|
+
'Engine Speed'
|
|
12
|
+
>>> engine_speed.unit
|
|
13
|
+
'rpm'
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from oscura.automotive.j1939.analyzer import J1939SPN
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"STANDARD_SPNS",
|
|
22
|
+
"get_standard_spns",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_standard_spns() -> dict[int, list[J1939SPN]]:
|
|
27
|
+
"""Get standard SPN definitions for common PGNs.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Dictionary mapping PGN to list of SPNs.
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
>>> spns = get_standard_spns()
|
|
34
|
+
>>> 61444 in spns # EEC1
|
|
35
|
+
True
|
|
36
|
+
>>> len(spns[61444])
|
|
37
|
+
8
|
|
38
|
+
"""
|
|
39
|
+
return STANDARD_SPNS.copy()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# Standard SPN definitions per SAE J1939/71
|
|
43
|
+
STANDARD_SPNS: dict[int, list[J1939SPN]] = {
|
|
44
|
+
# PGN 61444 - Electronic Engine Controller 1 (EEC1)
|
|
45
|
+
61444: [
|
|
46
|
+
J1939SPN(
|
|
47
|
+
spn=190,
|
|
48
|
+
name="Engine Speed",
|
|
49
|
+
start_bit=24,
|
|
50
|
+
bit_length=16,
|
|
51
|
+
resolution=0.125,
|
|
52
|
+
offset=0.0,
|
|
53
|
+
unit="rpm",
|
|
54
|
+
data_range=(0.0, 8031.875),
|
|
55
|
+
),
|
|
56
|
+
J1939SPN(
|
|
57
|
+
spn=899,
|
|
58
|
+
name="Engine Torque Mode",
|
|
59
|
+
start_bit=0,
|
|
60
|
+
bit_length=4,
|
|
61
|
+
resolution=1.0,
|
|
62
|
+
offset=0.0,
|
|
63
|
+
unit="",
|
|
64
|
+
data_range=(0.0, 15.0),
|
|
65
|
+
),
|
|
66
|
+
J1939SPN(
|
|
67
|
+
spn=512,
|
|
68
|
+
name="Driver's Demand Engine Percent Torque",
|
|
69
|
+
start_bit=8,
|
|
70
|
+
bit_length=8,
|
|
71
|
+
resolution=1.0,
|
|
72
|
+
offset=-125.0,
|
|
73
|
+
unit="%",
|
|
74
|
+
data_range=(-125.0, 125.0),
|
|
75
|
+
),
|
|
76
|
+
J1939SPN(
|
|
77
|
+
spn=513,
|
|
78
|
+
name="Actual Engine Percent Torque",
|
|
79
|
+
start_bit=16,
|
|
80
|
+
bit_length=8,
|
|
81
|
+
resolution=1.0,
|
|
82
|
+
offset=-125.0,
|
|
83
|
+
unit="%",
|
|
84
|
+
data_range=(-125.0, 125.0),
|
|
85
|
+
),
|
|
86
|
+
J1939SPN(
|
|
87
|
+
spn=1483,
|
|
88
|
+
name="Source Address of Controlling Device",
|
|
89
|
+
start_bit=40,
|
|
90
|
+
bit_length=8,
|
|
91
|
+
resolution=1.0,
|
|
92
|
+
offset=0.0,
|
|
93
|
+
unit="",
|
|
94
|
+
data_range=(0.0, 255.0),
|
|
95
|
+
),
|
|
96
|
+
J1939SPN(
|
|
97
|
+
spn=1675,
|
|
98
|
+
name="Engine Starter Mode",
|
|
99
|
+
start_bit=4,
|
|
100
|
+
bit_length=4,
|
|
101
|
+
resolution=1.0,
|
|
102
|
+
offset=0.0,
|
|
103
|
+
unit="",
|
|
104
|
+
data_range=(0.0, 15.0),
|
|
105
|
+
),
|
|
106
|
+
J1939SPN(
|
|
107
|
+
spn=2432,
|
|
108
|
+
name="Engine Demand Percent Torque",
|
|
109
|
+
start_bit=48,
|
|
110
|
+
bit_length=8,
|
|
111
|
+
resolution=1.0,
|
|
112
|
+
offset=-125.0,
|
|
113
|
+
unit="%",
|
|
114
|
+
data_range=(-125.0, 125.0),
|
|
115
|
+
),
|
|
116
|
+
],
|
|
117
|
+
# PGN 61443 - Electronic Engine Controller 2 (EEC2)
|
|
118
|
+
61443: [
|
|
119
|
+
J1939SPN(
|
|
120
|
+
spn=91,
|
|
121
|
+
name="Accelerator Pedal Position 1",
|
|
122
|
+
start_bit=0,
|
|
123
|
+
bit_length=8,
|
|
124
|
+
resolution=0.4,
|
|
125
|
+
offset=0.0,
|
|
126
|
+
unit="%",
|
|
127
|
+
data_range=(0.0, 100.0),
|
|
128
|
+
),
|
|
129
|
+
J1939SPN(
|
|
130
|
+
spn=92,
|
|
131
|
+
name="Engine Percent Load At Current Speed",
|
|
132
|
+
start_bit=8,
|
|
133
|
+
bit_length=8,
|
|
134
|
+
resolution=1.0,
|
|
135
|
+
offset=0.0,
|
|
136
|
+
unit="%",
|
|
137
|
+
data_range=(0.0, 250.0),
|
|
138
|
+
),
|
|
139
|
+
J1939SPN(
|
|
140
|
+
spn=974,
|
|
141
|
+
name="Remote Accelerator Pedal Position",
|
|
142
|
+
start_bit=16,
|
|
143
|
+
bit_length=8,
|
|
144
|
+
resolution=0.4,
|
|
145
|
+
offset=0.0,
|
|
146
|
+
unit="%",
|
|
147
|
+
data_range=(0.0, 100.0),
|
|
148
|
+
),
|
|
149
|
+
J1939SPN(
|
|
150
|
+
spn=29,
|
|
151
|
+
name="Accelerator Pedal Position 2",
|
|
152
|
+
start_bit=24,
|
|
153
|
+
bit_length=8,
|
|
154
|
+
resolution=0.4,
|
|
155
|
+
offset=0.0,
|
|
156
|
+
unit="%",
|
|
157
|
+
data_range=(0.0, 100.0),
|
|
158
|
+
),
|
|
159
|
+
J1939SPN(
|
|
160
|
+
spn=3357,
|
|
161
|
+
name="Vehicle Acceleration Rate Limit Status",
|
|
162
|
+
start_bit=32,
|
|
163
|
+
bit_length=2,
|
|
164
|
+
resolution=1.0,
|
|
165
|
+
offset=0.0,
|
|
166
|
+
unit="",
|
|
167
|
+
data_range=(0.0, 3.0),
|
|
168
|
+
),
|
|
169
|
+
],
|
|
170
|
+
# PGN 65265 - Cruise Control/Vehicle Speed (CCVS1)
|
|
171
|
+
65265: [
|
|
172
|
+
J1939SPN(
|
|
173
|
+
spn=84,
|
|
174
|
+
name="Wheel-Based Vehicle Speed",
|
|
175
|
+
start_bit=8,
|
|
176
|
+
bit_length=16,
|
|
177
|
+
resolution=0.00390625,
|
|
178
|
+
offset=0.0,
|
|
179
|
+
unit="km/h",
|
|
180
|
+
data_range=(0.0, 250.996),
|
|
181
|
+
),
|
|
182
|
+
J1939SPN(
|
|
183
|
+
spn=595,
|
|
184
|
+
name="Cruise Control Active",
|
|
185
|
+
start_bit=0,
|
|
186
|
+
bit_length=2,
|
|
187
|
+
resolution=1.0,
|
|
188
|
+
offset=0.0,
|
|
189
|
+
unit="",
|
|
190
|
+
data_range=(0.0, 3.0),
|
|
191
|
+
),
|
|
192
|
+
J1939SPN(
|
|
193
|
+
spn=596,
|
|
194
|
+
name="Cruise Control Enable Switch",
|
|
195
|
+
start_bit=2,
|
|
196
|
+
bit_length=2,
|
|
197
|
+
resolution=1.0,
|
|
198
|
+
offset=0.0,
|
|
199
|
+
unit="",
|
|
200
|
+
data_range=(0.0, 3.0),
|
|
201
|
+
),
|
|
202
|
+
J1939SPN(
|
|
203
|
+
spn=597,
|
|
204
|
+
name="Brake Switch",
|
|
205
|
+
start_bit=4,
|
|
206
|
+
bit_length=2,
|
|
207
|
+
resolution=1.0,
|
|
208
|
+
offset=0.0,
|
|
209
|
+
unit="",
|
|
210
|
+
data_range=(0.0, 3.0),
|
|
211
|
+
),
|
|
212
|
+
J1939SPN(
|
|
213
|
+
spn=598,
|
|
214
|
+
name="Clutch Switch",
|
|
215
|
+
start_bit=6,
|
|
216
|
+
bit_length=2,
|
|
217
|
+
resolution=1.0,
|
|
218
|
+
offset=0.0,
|
|
219
|
+
unit="",
|
|
220
|
+
data_range=(0.0, 3.0),
|
|
221
|
+
),
|
|
222
|
+
J1939SPN(
|
|
223
|
+
spn=599,
|
|
224
|
+
name="Cruise Control Set Switch",
|
|
225
|
+
start_bit=24,
|
|
226
|
+
bit_length=2,
|
|
227
|
+
resolution=1.0,
|
|
228
|
+
offset=0.0,
|
|
229
|
+
unit="",
|
|
230
|
+
data_range=(0.0, 3.0),
|
|
231
|
+
),
|
|
232
|
+
J1939SPN(
|
|
233
|
+
spn=600,
|
|
234
|
+
name="Cruise Control Coast Switch",
|
|
235
|
+
start_bit=26,
|
|
236
|
+
bit_length=2,
|
|
237
|
+
resolution=1.0,
|
|
238
|
+
offset=0.0,
|
|
239
|
+
unit="",
|
|
240
|
+
data_range=(0.0, 3.0),
|
|
241
|
+
),
|
|
242
|
+
J1939SPN(
|
|
243
|
+
spn=601,
|
|
244
|
+
name="Cruise Control Resume Switch",
|
|
245
|
+
start_bit=28,
|
|
246
|
+
bit_length=2,
|
|
247
|
+
resolution=1.0,
|
|
248
|
+
offset=0.0,
|
|
249
|
+
unit="",
|
|
250
|
+
data_range=(0.0, 3.0),
|
|
251
|
+
),
|
|
252
|
+
J1939SPN(
|
|
253
|
+
spn=602,
|
|
254
|
+
name="Cruise Control Accelerate Switch",
|
|
255
|
+
start_bit=30,
|
|
256
|
+
bit_length=2,
|
|
257
|
+
resolution=1.0,
|
|
258
|
+
offset=0.0,
|
|
259
|
+
unit="",
|
|
260
|
+
data_range=(0.0, 3.0),
|
|
261
|
+
),
|
|
262
|
+
J1939SPN(
|
|
263
|
+
spn=1633,
|
|
264
|
+
name="Cruise Control Set Speed",
|
|
265
|
+
start_bit=32,
|
|
266
|
+
bit_length=8,
|
|
267
|
+
resolution=1.0,
|
|
268
|
+
offset=0.0,
|
|
269
|
+
unit="km/h",
|
|
270
|
+
data_range=(0.0, 250.0),
|
|
271
|
+
),
|
|
272
|
+
J1939SPN(
|
|
273
|
+
spn=976,
|
|
274
|
+
name="PTO State",
|
|
275
|
+
start_bit=40,
|
|
276
|
+
bit_length=5,
|
|
277
|
+
resolution=1.0,
|
|
278
|
+
offset=0.0,
|
|
279
|
+
unit="",
|
|
280
|
+
data_range=(0.0, 31.0),
|
|
281
|
+
),
|
|
282
|
+
],
|
|
283
|
+
# PGN 65226 - Active Diagnostic Trouble Codes (DM1)
|
|
284
|
+
65226: [
|
|
285
|
+
J1939SPN(
|
|
286
|
+
spn=1213,
|
|
287
|
+
name="Malfunction Indicator Lamp Status",
|
|
288
|
+
start_bit=0,
|
|
289
|
+
bit_length=2,
|
|
290
|
+
resolution=1.0,
|
|
291
|
+
offset=0.0,
|
|
292
|
+
unit="",
|
|
293
|
+
data_range=(0.0, 3.0),
|
|
294
|
+
),
|
|
295
|
+
J1939SPN(
|
|
296
|
+
spn=623,
|
|
297
|
+
name="Red Stop Lamp Status",
|
|
298
|
+
start_bit=2,
|
|
299
|
+
bit_length=2,
|
|
300
|
+
resolution=1.0,
|
|
301
|
+
offset=0.0,
|
|
302
|
+
unit="",
|
|
303
|
+
data_range=(0.0, 3.0),
|
|
304
|
+
),
|
|
305
|
+
J1939SPN(
|
|
306
|
+
spn=624,
|
|
307
|
+
name="Amber Warning Lamp Status",
|
|
308
|
+
start_bit=4,
|
|
309
|
+
bit_length=2,
|
|
310
|
+
resolution=1.0,
|
|
311
|
+
offset=0.0,
|
|
312
|
+
unit="",
|
|
313
|
+
data_range=(0.0, 3.0),
|
|
314
|
+
),
|
|
315
|
+
J1939SPN(
|
|
316
|
+
spn=987,
|
|
317
|
+
name="Protect Lamp Status",
|
|
318
|
+
start_bit=6,
|
|
319
|
+
bit_length=2,
|
|
320
|
+
resolution=1.0,
|
|
321
|
+
offset=0.0,
|
|
322
|
+
unit="",
|
|
323
|
+
data_range=(0.0, 3.0),
|
|
324
|
+
),
|
|
325
|
+
],
|
|
326
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""J1939 Transport Protocol (TP) implementation.
|
|
2
|
+
|
|
3
|
+
This module implements J1939 transport protocol for multi-packet message
|
|
4
|
+
transfer, including:
|
|
5
|
+
- TP.CM (Connection Management): RTS, CTS, EOM_ACK, BAM, ABORT
|
|
6
|
+
- TP.DT (Data Transfer): Sequence-based packet delivery
|
|
7
|
+
- Session management and reassembly
|
|
8
|
+
|
|
9
|
+
References:
|
|
10
|
+
SAE J1939/21 - Data Link Layer (Transport Protocol)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"TransportProtocol",
|
|
20
|
+
"TransportSession",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class TransportSession:
|
|
26
|
+
"""Active transport protocol session.
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
source_address: Source address initiating transfer.
|
|
30
|
+
dest_address: Destination address (0xFF for broadcast).
|
|
31
|
+
data_pgn: PGN of the data being transferred.
|
|
32
|
+
total_size: Total message size in bytes.
|
|
33
|
+
total_packets: Total number of packets.
|
|
34
|
+
max_packets: Maximum packets per CTS (connection mode only).
|
|
35
|
+
packets: Received packets (sequence -> data).
|
|
36
|
+
started_at: Session start timestamp.
|
|
37
|
+
last_timestamp: Last packet timestamp.
|
|
38
|
+
is_broadcast: True for BAM (broadcast), False for RTS/CTS.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
source_address: int
|
|
42
|
+
dest_address: int
|
|
43
|
+
data_pgn: int
|
|
44
|
+
total_size: int
|
|
45
|
+
total_packets: int
|
|
46
|
+
max_packets: int = 0xFF
|
|
47
|
+
packets: dict[int, bytes] = field(default_factory=dict)
|
|
48
|
+
started_at: float = 0.0
|
|
49
|
+
last_timestamp: float = 0.0
|
|
50
|
+
is_broadcast: bool = False
|
|
51
|
+
|
|
52
|
+
def is_complete(self) -> bool:
|
|
53
|
+
"""Check if all packets received.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
True if all packets received.
|
|
57
|
+
|
|
58
|
+
Example:
|
|
59
|
+
>>> session = TransportSession(0, 0xFF, 61444, 32, 5)
|
|
60
|
+
>>> session.is_complete()
|
|
61
|
+
False
|
|
62
|
+
>>> for i in range(1, 6):
|
|
63
|
+
... session.packets[i] = b'\\x00' * 7
|
|
64
|
+
>>> session.is_complete()
|
|
65
|
+
True
|
|
66
|
+
"""
|
|
67
|
+
return len(self.packets) == self.total_packets
|
|
68
|
+
|
|
69
|
+
def reassemble(self) -> bytes:
|
|
70
|
+
"""Reassemble complete message from packets.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Reassembled message data.
|
|
74
|
+
|
|
75
|
+
Raises:
|
|
76
|
+
ValueError: If session is incomplete.
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
>>> session = TransportSession(0, 0xFF, 61444, 14, 2)
|
|
80
|
+
>>> session.packets[1] = b'\\x00\\x01\\x02\\x03\\x04\\x05\\x06'
|
|
81
|
+
>>> session.packets[2] = b'\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d'
|
|
82
|
+
>>> data = session.reassemble()
|
|
83
|
+
>>> len(data)
|
|
84
|
+
14
|
|
85
|
+
"""
|
|
86
|
+
if not self.is_complete():
|
|
87
|
+
raise ValueError(f"Session incomplete: {len(self.packets)}/{self.total_packets}")
|
|
88
|
+
|
|
89
|
+
# Reassemble in sequence order
|
|
90
|
+
complete_data = b"".join(self.packets[i] for i in range(1, self.total_packets + 1))
|
|
91
|
+
|
|
92
|
+
# Trim to actual size (last packet may be padded)
|
|
93
|
+
return complete_data[: self.total_size]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class TransportProtocol:
|
|
97
|
+
"""J1939 Transport Protocol handler.
|
|
98
|
+
|
|
99
|
+
Manages multi-packet message transfer sessions for messages
|
|
100
|
+
larger than 8 bytes using TP.CM and TP.DT.
|
|
101
|
+
|
|
102
|
+
Example:
|
|
103
|
+
>>> tp = TransportProtocol()
|
|
104
|
+
>>> # Parse TP.CM RTS
|
|
105
|
+
>>> cm_info = tp.parse_cm(b'\\x10\\x20\\x00\\x05\\xff\\xf0\\x04\\x00')
|
|
106
|
+
>>> cm_info['control']
|
|
107
|
+
'RTS'
|
|
108
|
+
>>> cm_info['total_size']
|
|
109
|
+
32
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
# PGN for transport protocol
|
|
113
|
+
PGN_TP_CM = 60160 # Connection Management
|
|
114
|
+
PGN_TP_DT = 60416 # Data Transfer
|
|
115
|
+
|
|
116
|
+
# Connection Management control bytes
|
|
117
|
+
CM_RTS = 16 # Request To Send
|
|
118
|
+
CM_CTS = 17 # Clear To Send
|
|
119
|
+
CM_EOM_ACK = 19 # End of Message Acknowledgment
|
|
120
|
+
CM_BAM = 32 # Broadcast Announce Message
|
|
121
|
+
CM_ABORT = 255 # Connection Abort
|
|
122
|
+
|
|
123
|
+
def __init__(self) -> None:
|
|
124
|
+
"""Initialize transport protocol handler."""
|
|
125
|
+
# Active sessions: (source_addr, dest_addr) -> session
|
|
126
|
+
self.sessions: dict[tuple[int, int], TransportSession] = {}
|
|
127
|
+
|
|
128
|
+
def parse_cm(self, data: bytes) -> dict[str, Any] | None:
|
|
129
|
+
"""Parse TP.CM (Connection Management) message.
|
|
130
|
+
|
|
131
|
+
TP.CM format (8 bytes):
|
|
132
|
+
- Byte 0: Control byte (RTS/CTS/EOM_ACK/BAM/ABORT)
|
|
133
|
+
- Byte 1-2: Total message size (little-endian)
|
|
134
|
+
- Byte 3: Total packets
|
|
135
|
+
- Byte 4: Max packets (CTS) or Reserved (RTS/BAM)
|
|
136
|
+
- Byte 5-7: PGN of data (little-endian, 3 bytes)
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
data: TP.CM message data (8 bytes).
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Parsed CM metadata or None if invalid.
|
|
143
|
+
|
|
144
|
+
Example:
|
|
145
|
+
>>> tp = TransportProtocol()
|
|
146
|
+
>>> # RTS message
|
|
147
|
+
>>> info = tp.parse_cm(b'\\x10\\x20\\x00\\x05\\xff\\xf0\\x04\\x00')
|
|
148
|
+
>>> info['control']
|
|
149
|
+
'RTS'
|
|
150
|
+
>>> info['data_pgn']
|
|
151
|
+
61680
|
|
152
|
+
"""
|
|
153
|
+
if len(data) < 8:
|
|
154
|
+
return None
|
|
155
|
+
|
|
156
|
+
control = data[0]
|
|
157
|
+
total_size = int.from_bytes(data[1:3], "little")
|
|
158
|
+
total_packets = data[3]
|
|
159
|
+
max_packets = data[4]
|
|
160
|
+
data_pgn = int.from_bytes(data[5:8], "little")
|
|
161
|
+
|
|
162
|
+
cm_types = {
|
|
163
|
+
self.CM_RTS: "RTS",
|
|
164
|
+
self.CM_CTS: "CTS",
|
|
165
|
+
self.CM_EOM_ACK: "EOM_ACK",
|
|
166
|
+
self.CM_BAM: "BAM",
|
|
167
|
+
self.CM_ABORT: "ABORT",
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
"control": cm_types.get(control, f"Unknown ({control})"),
|
|
172
|
+
"control_byte": control,
|
|
173
|
+
"total_size": total_size,
|
|
174
|
+
"total_packets": total_packets,
|
|
175
|
+
"max_packets": max_packets,
|
|
176
|
+
"data_pgn": data_pgn,
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
def parse_dt(self, data: bytes) -> dict[str, Any] | None:
|
|
180
|
+
"""Parse TP.DT (Data Transfer) message.
|
|
181
|
+
|
|
182
|
+
TP.DT format:
|
|
183
|
+
- Byte 0: Sequence number (1-255)
|
|
184
|
+
- Byte 1-7: Data (7 bytes per packet)
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
data: TP.DT message data (8 bytes).
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Parsed DT metadata or None if invalid.
|
|
191
|
+
|
|
192
|
+
Example:
|
|
193
|
+
>>> tp = TransportProtocol()
|
|
194
|
+
>>> info = tp.parse_dt(b'\\x01\\x00\\x01\\x02\\x03\\x04\\x05\\x06')
|
|
195
|
+
>>> info['sequence']
|
|
196
|
+
1
|
|
197
|
+
>>> info['data']
|
|
198
|
+
b'\\x00\\x01\\x02\\x03\\x04\\x05\\x06'
|
|
199
|
+
"""
|
|
200
|
+
if len(data) < 1:
|
|
201
|
+
return None
|
|
202
|
+
|
|
203
|
+
sequence = data[0]
|
|
204
|
+
packet_data = data[1:]
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
"sequence": sequence,
|
|
208
|
+
"data": packet_data,
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
def start_session(
|
|
212
|
+
self,
|
|
213
|
+
source_address: int,
|
|
214
|
+
dest_address: int,
|
|
215
|
+
cm_info: dict[str, Any],
|
|
216
|
+
timestamp: float,
|
|
217
|
+
) -> TransportSession | None:
|
|
218
|
+
"""Start transport protocol session from TP.CM.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
source_address: Source address.
|
|
222
|
+
dest_address: Destination address.
|
|
223
|
+
cm_info: Parsed TP.CM metadata.
|
|
224
|
+
timestamp: Session start timestamp.
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Created session or None if invalid.
|
|
228
|
+
|
|
229
|
+
Example:
|
|
230
|
+
>>> tp = TransportProtocol()
|
|
231
|
+
>>> cm_info = tp.parse_cm(b'\\x10\\x20\\x00\\x05\\xff\\xf0\\x04\\x00')
|
|
232
|
+
>>> session = tp.start_session(0, 0xFF, cm_info, 1.0)
|
|
233
|
+
>>> session.total_size
|
|
234
|
+
32
|
|
235
|
+
"""
|
|
236
|
+
control = cm_info.get("control_byte")
|
|
237
|
+
if control not in (self.CM_RTS, self.CM_BAM):
|
|
238
|
+
return None
|
|
239
|
+
|
|
240
|
+
session = TransportSession(
|
|
241
|
+
source_address=source_address,
|
|
242
|
+
dest_address=dest_address,
|
|
243
|
+
data_pgn=cm_info["data_pgn"],
|
|
244
|
+
total_size=cm_info["total_size"],
|
|
245
|
+
total_packets=cm_info["total_packets"],
|
|
246
|
+
max_packets=cm_info.get("max_packets", 0xFF),
|
|
247
|
+
started_at=timestamp,
|
|
248
|
+
last_timestamp=timestamp,
|
|
249
|
+
is_broadcast=(control == self.CM_BAM),
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
session_key = (source_address, dest_address)
|
|
253
|
+
self.sessions[session_key] = session
|
|
254
|
+
|
|
255
|
+
return session
|
|
256
|
+
|
|
257
|
+
def add_packet(
|
|
258
|
+
self,
|
|
259
|
+
source_address: int,
|
|
260
|
+
dest_address: int,
|
|
261
|
+
dt_info: dict[str, Any],
|
|
262
|
+
timestamp: float,
|
|
263
|
+
) -> bytes | None:
|
|
264
|
+
"""Add TP.DT packet to session.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
source_address: Source address.
|
|
268
|
+
dest_address: Destination address.
|
|
269
|
+
dt_info: Parsed TP.DT metadata.
|
|
270
|
+
timestamp: Packet timestamp.
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
Complete reassembled message if session complete, None otherwise.
|
|
274
|
+
|
|
275
|
+
Example:
|
|
276
|
+
>>> tp = TransportProtocol()
|
|
277
|
+
>>> cm = tp.parse_cm(b'\\x20\\x0e\\x00\\x02\\xff\\xf0\\x04\\x00')
|
|
278
|
+
>>> tp.start_session(0, 0xFF, cm, 1.0)
|
|
279
|
+
<...>
|
|
280
|
+
>>> dt1 = tp.parse_dt(b'\\x01\\x00\\x01\\x02\\x03\\x04\\x05\\x06')
|
|
281
|
+
>>> tp.add_packet(0, 0xFF, dt1, 1.1)
|
|
282
|
+
>>> dt2 = tp.parse_dt(b'\\x02\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d')
|
|
283
|
+
>>> data = tp.add_packet(0, 0xFF, dt2, 1.2)
|
|
284
|
+
>>> len(data)
|
|
285
|
+
14
|
|
286
|
+
"""
|
|
287
|
+
session_key = (source_address, dest_address)
|
|
288
|
+
|
|
289
|
+
if session_key not in self.sessions:
|
|
290
|
+
return None
|
|
291
|
+
|
|
292
|
+
session = self.sessions[session_key]
|
|
293
|
+
sequence = dt_info["sequence"]
|
|
294
|
+
packet_data = dt_info["data"]
|
|
295
|
+
|
|
296
|
+
# Add packet
|
|
297
|
+
session.packets[sequence] = packet_data
|
|
298
|
+
session.last_timestamp = timestamp
|
|
299
|
+
|
|
300
|
+
# Check if complete
|
|
301
|
+
if session.is_complete():
|
|
302
|
+
complete_data = session.reassemble()
|
|
303
|
+
del self.sessions[session_key]
|
|
304
|
+
return complete_data
|
|
305
|
+
|
|
306
|
+
return None
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""LIN (Local Interconnect Network) protocol analysis.
|
|
2
|
+
|
|
3
|
+
This module provides comprehensive LIN protocol analysis capabilities including:
|
|
4
|
+
- LIN 2.x frame parsing with enhanced checksum support
|
|
5
|
+
- Protected ID calculation with parity bits
|
|
6
|
+
- Signal decoding and extraction
|
|
7
|
+
- Diagnostic services (master request/slave response)
|
|
8
|
+
- Schedule table inference from captured traffic
|
|
9
|
+
- LDF (LIN Description File) generation
|
|
10
|
+
|
|
11
|
+
Key features:
|
|
12
|
+
- Both classic and enhanced checksum validation
|
|
13
|
+
- Diagnostic frame parsing (0x3C, 0x3D)
|
|
14
|
+
- Event-triggered frame handling
|
|
15
|
+
- Signal mapping and decoding
|
|
16
|
+
- Automatic LDF generation from traffic
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
>>> from oscura.automotive.lin import LINAnalyzer, LINSignal
|
|
20
|
+
>>> analyzer = LINAnalyzer()
|
|
21
|
+
>>> # Parse captured LIN frame
|
|
22
|
+
>>> frame = analyzer.parse_frame(data=b'\\x55\\x80\\x01\\x02\\x03\\xFA', timestamp=1.0)
|
|
23
|
+
>>> print(f"Frame ID: {frame.frame_id}, Checksum: {frame.checksum_type}")
|
|
24
|
+
Frame ID: 0, Checksum: enhanced
|
|
25
|
+
>>> # Add signal definition
|
|
26
|
+
>>> analyzer.add_signal(LINSignal("Speed", frame_id=0, start_bit=0, bit_length=16))
|
|
27
|
+
>>> # Generate LDF from captured traffic
|
|
28
|
+
>>> analyzer.generate_ldf(Path("output.ldf"), baudrate=19200)
|
|
29
|
+
|
|
30
|
+
References:
|
|
31
|
+
LIN Specification 2.2A
|
|
32
|
+
ISO 17987 (LIN standard)
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"LINAnalyzer",
|
|
37
|
+
"LINFrame",
|
|
38
|
+
"LINScheduleEntry",
|
|
39
|
+
"LINSignal",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
from oscura.automotive.lin.analyzer import (
|
|
43
|
+
LINAnalyzer,
|
|
44
|
+
LINFrame,
|
|
45
|
+
LINScheduleEntry,
|
|
46
|
+
LINSignal,
|
|
47
|
+
)
|