oscura 0.0.1__py3-none-any.whl → 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (465) hide show
  1. oscura/__init__.py +813 -8
  2. oscura/__main__.py +392 -0
  3. oscura/analyzers/__init__.py +37 -0
  4. oscura/analyzers/digital/__init__.py +177 -0
  5. oscura/analyzers/digital/bus.py +691 -0
  6. oscura/analyzers/digital/clock.py +805 -0
  7. oscura/analyzers/digital/correlation.py +720 -0
  8. oscura/analyzers/digital/edges.py +632 -0
  9. oscura/analyzers/digital/extraction.py +413 -0
  10. oscura/analyzers/digital/quality.py +878 -0
  11. oscura/analyzers/digital/signal_quality.py +877 -0
  12. oscura/analyzers/digital/thresholds.py +708 -0
  13. oscura/analyzers/digital/timing.py +1104 -0
  14. oscura/analyzers/eye/__init__.py +46 -0
  15. oscura/analyzers/eye/diagram.py +434 -0
  16. oscura/analyzers/eye/metrics.py +555 -0
  17. oscura/analyzers/jitter/__init__.py +83 -0
  18. oscura/analyzers/jitter/ber.py +333 -0
  19. oscura/analyzers/jitter/decomposition.py +759 -0
  20. oscura/analyzers/jitter/measurements.py +413 -0
  21. oscura/analyzers/jitter/spectrum.py +220 -0
  22. oscura/analyzers/measurements.py +40 -0
  23. oscura/analyzers/packet/__init__.py +171 -0
  24. oscura/analyzers/packet/daq.py +1077 -0
  25. oscura/analyzers/packet/metrics.py +437 -0
  26. oscura/analyzers/packet/parser.py +327 -0
  27. oscura/analyzers/packet/payload.py +2156 -0
  28. oscura/analyzers/packet/payload_analysis.py +1312 -0
  29. oscura/analyzers/packet/payload_extraction.py +236 -0
  30. oscura/analyzers/packet/payload_patterns.py +670 -0
  31. oscura/analyzers/packet/stream.py +359 -0
  32. oscura/analyzers/patterns/__init__.py +266 -0
  33. oscura/analyzers/patterns/clustering.py +1036 -0
  34. oscura/analyzers/patterns/discovery.py +539 -0
  35. oscura/analyzers/patterns/learning.py +797 -0
  36. oscura/analyzers/patterns/matching.py +1091 -0
  37. oscura/analyzers/patterns/periodic.py +650 -0
  38. oscura/analyzers/patterns/sequences.py +767 -0
  39. oscura/analyzers/power/__init__.py +116 -0
  40. oscura/analyzers/power/ac_power.py +391 -0
  41. oscura/analyzers/power/basic.py +383 -0
  42. oscura/analyzers/power/conduction.py +314 -0
  43. oscura/analyzers/power/efficiency.py +297 -0
  44. oscura/analyzers/power/ripple.py +356 -0
  45. oscura/analyzers/power/soa.py +372 -0
  46. oscura/analyzers/power/switching.py +479 -0
  47. oscura/analyzers/protocol/__init__.py +150 -0
  48. oscura/analyzers/protocols/__init__.py +150 -0
  49. oscura/analyzers/protocols/base.py +500 -0
  50. oscura/analyzers/protocols/can.py +620 -0
  51. oscura/analyzers/protocols/can_fd.py +448 -0
  52. oscura/analyzers/protocols/flexray.py +405 -0
  53. oscura/analyzers/protocols/hdlc.py +399 -0
  54. oscura/analyzers/protocols/i2c.py +368 -0
  55. oscura/analyzers/protocols/i2s.py +296 -0
  56. oscura/analyzers/protocols/jtag.py +393 -0
  57. oscura/analyzers/protocols/lin.py +445 -0
  58. oscura/analyzers/protocols/manchester.py +333 -0
  59. oscura/analyzers/protocols/onewire.py +501 -0
  60. oscura/analyzers/protocols/spi.py +334 -0
  61. oscura/analyzers/protocols/swd.py +325 -0
  62. oscura/analyzers/protocols/uart.py +393 -0
  63. oscura/analyzers/protocols/usb.py +495 -0
  64. oscura/analyzers/signal_integrity/__init__.py +63 -0
  65. oscura/analyzers/signal_integrity/embedding.py +294 -0
  66. oscura/analyzers/signal_integrity/equalization.py +370 -0
  67. oscura/analyzers/signal_integrity/sparams.py +484 -0
  68. oscura/analyzers/spectral/__init__.py +53 -0
  69. oscura/analyzers/spectral/chunked.py +273 -0
  70. oscura/analyzers/spectral/chunked_fft.py +571 -0
  71. oscura/analyzers/spectral/chunked_wavelet.py +391 -0
  72. oscura/analyzers/spectral/fft.py +92 -0
  73. oscura/analyzers/statistical/__init__.py +250 -0
  74. oscura/analyzers/statistical/checksum.py +923 -0
  75. oscura/analyzers/statistical/chunked_corr.py +228 -0
  76. oscura/analyzers/statistical/classification.py +778 -0
  77. oscura/analyzers/statistical/entropy.py +1113 -0
  78. oscura/analyzers/statistical/ngrams.py +614 -0
  79. oscura/analyzers/statistics/__init__.py +119 -0
  80. oscura/analyzers/statistics/advanced.py +885 -0
  81. oscura/analyzers/statistics/basic.py +263 -0
  82. oscura/analyzers/statistics/correlation.py +630 -0
  83. oscura/analyzers/statistics/distribution.py +298 -0
  84. oscura/analyzers/statistics/outliers.py +463 -0
  85. oscura/analyzers/statistics/streaming.py +93 -0
  86. oscura/analyzers/statistics/trend.py +520 -0
  87. oscura/analyzers/validation.py +598 -0
  88. oscura/analyzers/waveform/__init__.py +36 -0
  89. oscura/analyzers/waveform/measurements.py +943 -0
  90. oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
  91. oscura/analyzers/waveform/spectral.py +1689 -0
  92. oscura/analyzers/waveform/wavelets.py +298 -0
  93. oscura/api/__init__.py +62 -0
  94. oscura/api/dsl.py +538 -0
  95. oscura/api/fluent.py +571 -0
  96. oscura/api/operators.py +498 -0
  97. oscura/api/optimization.py +392 -0
  98. oscura/api/profiling.py +396 -0
  99. oscura/automotive/__init__.py +73 -0
  100. oscura/automotive/can/__init__.py +52 -0
  101. oscura/automotive/can/analysis.py +356 -0
  102. oscura/automotive/can/checksum.py +250 -0
  103. oscura/automotive/can/correlation.py +212 -0
  104. oscura/automotive/can/discovery.py +355 -0
  105. oscura/automotive/can/message_wrapper.py +375 -0
  106. oscura/automotive/can/models.py +385 -0
  107. oscura/automotive/can/patterns.py +381 -0
  108. oscura/automotive/can/session.py +452 -0
  109. oscura/automotive/can/state_machine.py +300 -0
  110. oscura/automotive/can/stimulus_response.py +461 -0
  111. oscura/automotive/dbc/__init__.py +15 -0
  112. oscura/automotive/dbc/generator.py +156 -0
  113. oscura/automotive/dbc/parser.py +146 -0
  114. oscura/automotive/dtc/__init__.py +30 -0
  115. oscura/automotive/dtc/database.py +3036 -0
  116. oscura/automotive/j1939/__init__.py +14 -0
  117. oscura/automotive/j1939/decoder.py +745 -0
  118. oscura/automotive/loaders/__init__.py +35 -0
  119. oscura/automotive/loaders/asc.py +98 -0
  120. oscura/automotive/loaders/blf.py +77 -0
  121. oscura/automotive/loaders/csv_can.py +136 -0
  122. oscura/automotive/loaders/dispatcher.py +136 -0
  123. oscura/automotive/loaders/mdf.py +331 -0
  124. oscura/automotive/loaders/pcap.py +132 -0
  125. oscura/automotive/obd/__init__.py +14 -0
  126. oscura/automotive/obd/decoder.py +707 -0
  127. oscura/automotive/uds/__init__.py +48 -0
  128. oscura/automotive/uds/decoder.py +265 -0
  129. oscura/automotive/uds/models.py +64 -0
  130. oscura/automotive/visualization.py +369 -0
  131. oscura/batch/__init__.py +55 -0
  132. oscura/batch/advanced.py +627 -0
  133. oscura/batch/aggregate.py +300 -0
  134. oscura/batch/analyze.py +139 -0
  135. oscura/batch/logging.py +487 -0
  136. oscura/batch/metrics.py +556 -0
  137. oscura/builders/__init__.py +41 -0
  138. oscura/builders/signal_builder.py +1131 -0
  139. oscura/cli/__init__.py +14 -0
  140. oscura/cli/batch.py +339 -0
  141. oscura/cli/characterize.py +273 -0
  142. oscura/cli/compare.py +775 -0
  143. oscura/cli/decode.py +551 -0
  144. oscura/cli/main.py +247 -0
  145. oscura/cli/shell.py +350 -0
  146. oscura/comparison/__init__.py +66 -0
  147. oscura/comparison/compare.py +397 -0
  148. oscura/comparison/golden.py +487 -0
  149. oscura/comparison/limits.py +391 -0
  150. oscura/comparison/mask.py +434 -0
  151. oscura/comparison/trace_diff.py +30 -0
  152. oscura/comparison/visualization.py +481 -0
  153. oscura/compliance/__init__.py +70 -0
  154. oscura/compliance/advanced.py +756 -0
  155. oscura/compliance/masks.py +363 -0
  156. oscura/compliance/reporting.py +483 -0
  157. oscura/compliance/testing.py +298 -0
  158. oscura/component/__init__.py +38 -0
  159. oscura/component/impedance.py +365 -0
  160. oscura/component/reactive.py +598 -0
  161. oscura/component/transmission_line.py +312 -0
  162. oscura/config/__init__.py +191 -0
  163. oscura/config/defaults.py +254 -0
  164. oscura/config/loader.py +348 -0
  165. oscura/config/memory.py +271 -0
  166. oscura/config/migration.py +458 -0
  167. oscura/config/pipeline.py +1077 -0
  168. oscura/config/preferences.py +530 -0
  169. oscura/config/protocol.py +875 -0
  170. oscura/config/schema.py +713 -0
  171. oscura/config/settings.py +420 -0
  172. oscura/config/thresholds.py +599 -0
  173. oscura/convenience.py +457 -0
  174. oscura/core/__init__.py +299 -0
  175. oscura/core/audit.py +457 -0
  176. oscura/core/backend_selector.py +405 -0
  177. oscura/core/cache.py +590 -0
  178. oscura/core/cancellation.py +439 -0
  179. oscura/core/confidence.py +225 -0
  180. oscura/core/config.py +506 -0
  181. oscura/core/correlation.py +216 -0
  182. oscura/core/cross_domain.py +422 -0
  183. oscura/core/debug.py +301 -0
  184. oscura/core/edge_cases.py +541 -0
  185. oscura/core/exceptions.py +535 -0
  186. oscura/core/gpu_backend.py +523 -0
  187. oscura/core/lazy.py +832 -0
  188. oscura/core/log_query.py +540 -0
  189. oscura/core/logging.py +931 -0
  190. oscura/core/logging_advanced.py +952 -0
  191. oscura/core/memoize.py +171 -0
  192. oscura/core/memory_check.py +274 -0
  193. oscura/core/memory_guard.py +290 -0
  194. oscura/core/memory_limits.py +336 -0
  195. oscura/core/memory_monitor.py +453 -0
  196. oscura/core/memory_progress.py +465 -0
  197. oscura/core/memory_warnings.py +315 -0
  198. oscura/core/numba_backend.py +362 -0
  199. oscura/core/performance.py +352 -0
  200. oscura/core/progress.py +524 -0
  201. oscura/core/provenance.py +358 -0
  202. oscura/core/results.py +331 -0
  203. oscura/core/types.py +504 -0
  204. oscura/core/uncertainty.py +383 -0
  205. oscura/discovery/__init__.py +52 -0
  206. oscura/discovery/anomaly_detector.py +672 -0
  207. oscura/discovery/auto_decoder.py +415 -0
  208. oscura/discovery/comparison.py +497 -0
  209. oscura/discovery/quality_validator.py +528 -0
  210. oscura/discovery/signal_detector.py +769 -0
  211. oscura/dsl/__init__.py +73 -0
  212. oscura/dsl/commands.py +246 -0
  213. oscura/dsl/interpreter.py +455 -0
  214. oscura/dsl/parser.py +689 -0
  215. oscura/dsl/repl.py +172 -0
  216. oscura/exceptions.py +59 -0
  217. oscura/exploratory/__init__.py +111 -0
  218. oscura/exploratory/error_recovery.py +642 -0
  219. oscura/exploratory/fuzzy.py +513 -0
  220. oscura/exploratory/fuzzy_advanced.py +786 -0
  221. oscura/exploratory/legacy.py +831 -0
  222. oscura/exploratory/parse.py +358 -0
  223. oscura/exploratory/recovery.py +275 -0
  224. oscura/exploratory/sync.py +382 -0
  225. oscura/exploratory/unknown.py +707 -0
  226. oscura/export/__init__.py +25 -0
  227. oscura/export/wireshark/README.md +265 -0
  228. oscura/export/wireshark/__init__.py +47 -0
  229. oscura/export/wireshark/generator.py +312 -0
  230. oscura/export/wireshark/lua_builder.py +159 -0
  231. oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
  232. oscura/export/wireshark/type_mapping.py +165 -0
  233. oscura/export/wireshark/validator.py +105 -0
  234. oscura/exporters/__init__.py +94 -0
  235. oscura/exporters/csv.py +303 -0
  236. oscura/exporters/exporters.py +44 -0
  237. oscura/exporters/hdf5.py +219 -0
  238. oscura/exporters/html_export.py +701 -0
  239. oscura/exporters/json_export.py +291 -0
  240. oscura/exporters/markdown_export.py +367 -0
  241. oscura/exporters/matlab_export.py +354 -0
  242. oscura/exporters/npz_export.py +219 -0
  243. oscura/exporters/spice_export.py +210 -0
  244. oscura/extensibility/__init__.py +131 -0
  245. oscura/extensibility/docs.py +752 -0
  246. oscura/extensibility/extensions.py +1125 -0
  247. oscura/extensibility/logging.py +259 -0
  248. oscura/extensibility/measurements.py +485 -0
  249. oscura/extensibility/plugins.py +414 -0
  250. oscura/extensibility/registry.py +346 -0
  251. oscura/extensibility/templates.py +913 -0
  252. oscura/extensibility/validation.py +651 -0
  253. oscura/filtering/__init__.py +89 -0
  254. oscura/filtering/base.py +563 -0
  255. oscura/filtering/convenience.py +564 -0
  256. oscura/filtering/design.py +725 -0
  257. oscura/filtering/filters.py +32 -0
  258. oscura/filtering/introspection.py +605 -0
  259. oscura/guidance/__init__.py +24 -0
  260. oscura/guidance/recommender.py +429 -0
  261. oscura/guidance/wizard.py +518 -0
  262. oscura/inference/__init__.py +251 -0
  263. oscura/inference/active_learning/README.md +153 -0
  264. oscura/inference/active_learning/__init__.py +38 -0
  265. oscura/inference/active_learning/lstar.py +257 -0
  266. oscura/inference/active_learning/observation_table.py +230 -0
  267. oscura/inference/active_learning/oracle.py +78 -0
  268. oscura/inference/active_learning/teachers/__init__.py +15 -0
  269. oscura/inference/active_learning/teachers/simulator.py +192 -0
  270. oscura/inference/adaptive_tuning.py +453 -0
  271. oscura/inference/alignment.py +653 -0
  272. oscura/inference/bayesian.py +943 -0
  273. oscura/inference/binary.py +1016 -0
  274. oscura/inference/crc_reverse.py +711 -0
  275. oscura/inference/logic.py +288 -0
  276. oscura/inference/message_format.py +1305 -0
  277. oscura/inference/protocol.py +417 -0
  278. oscura/inference/protocol_dsl.py +1084 -0
  279. oscura/inference/protocol_library.py +1230 -0
  280. oscura/inference/sequences.py +809 -0
  281. oscura/inference/signal_intelligence.py +1509 -0
  282. oscura/inference/spectral.py +215 -0
  283. oscura/inference/state_machine.py +634 -0
  284. oscura/inference/stream.py +918 -0
  285. oscura/integrations/__init__.py +59 -0
  286. oscura/integrations/llm.py +1827 -0
  287. oscura/jupyter/__init__.py +32 -0
  288. oscura/jupyter/display.py +268 -0
  289. oscura/jupyter/magic.py +334 -0
  290. oscura/loaders/__init__.py +526 -0
  291. oscura/loaders/binary.py +69 -0
  292. oscura/loaders/configurable.py +1255 -0
  293. oscura/loaders/csv.py +26 -0
  294. oscura/loaders/csv_loader.py +473 -0
  295. oscura/loaders/hdf5.py +9 -0
  296. oscura/loaders/hdf5_loader.py +510 -0
  297. oscura/loaders/lazy.py +370 -0
  298. oscura/loaders/mmap_loader.py +583 -0
  299. oscura/loaders/numpy_loader.py +436 -0
  300. oscura/loaders/pcap.py +432 -0
  301. oscura/loaders/preprocessing.py +368 -0
  302. oscura/loaders/rigol.py +287 -0
  303. oscura/loaders/sigrok.py +321 -0
  304. oscura/loaders/tdms.py +367 -0
  305. oscura/loaders/tektronix.py +711 -0
  306. oscura/loaders/validation.py +584 -0
  307. oscura/loaders/vcd.py +464 -0
  308. oscura/loaders/wav.py +233 -0
  309. oscura/math/__init__.py +45 -0
  310. oscura/math/arithmetic.py +824 -0
  311. oscura/math/interpolation.py +413 -0
  312. oscura/onboarding/__init__.py +39 -0
  313. oscura/onboarding/help.py +498 -0
  314. oscura/onboarding/tutorials.py +405 -0
  315. oscura/onboarding/wizard.py +466 -0
  316. oscura/optimization/__init__.py +19 -0
  317. oscura/optimization/parallel.py +440 -0
  318. oscura/optimization/search.py +532 -0
  319. oscura/pipeline/__init__.py +43 -0
  320. oscura/pipeline/base.py +338 -0
  321. oscura/pipeline/composition.py +242 -0
  322. oscura/pipeline/parallel.py +448 -0
  323. oscura/pipeline/pipeline.py +375 -0
  324. oscura/pipeline/reverse_engineering.py +1119 -0
  325. oscura/plugins/__init__.py +122 -0
  326. oscura/plugins/base.py +272 -0
  327. oscura/plugins/cli.py +497 -0
  328. oscura/plugins/discovery.py +411 -0
  329. oscura/plugins/isolation.py +418 -0
  330. oscura/plugins/lifecycle.py +959 -0
  331. oscura/plugins/manager.py +493 -0
  332. oscura/plugins/registry.py +421 -0
  333. oscura/plugins/versioning.py +372 -0
  334. oscura/py.typed +0 -0
  335. oscura/quality/__init__.py +65 -0
  336. oscura/quality/ensemble.py +740 -0
  337. oscura/quality/explainer.py +338 -0
  338. oscura/quality/scoring.py +616 -0
  339. oscura/quality/warnings.py +456 -0
  340. oscura/reporting/__init__.py +248 -0
  341. oscura/reporting/advanced.py +1234 -0
  342. oscura/reporting/analyze.py +448 -0
  343. oscura/reporting/argument_preparer.py +596 -0
  344. oscura/reporting/auto_report.py +507 -0
  345. oscura/reporting/batch.py +615 -0
  346. oscura/reporting/chart_selection.py +223 -0
  347. oscura/reporting/comparison.py +330 -0
  348. oscura/reporting/config.py +615 -0
  349. oscura/reporting/content/__init__.py +39 -0
  350. oscura/reporting/content/executive.py +127 -0
  351. oscura/reporting/content/filtering.py +191 -0
  352. oscura/reporting/content/minimal.py +257 -0
  353. oscura/reporting/content/verbosity.py +162 -0
  354. oscura/reporting/core.py +508 -0
  355. oscura/reporting/core_formats/__init__.py +17 -0
  356. oscura/reporting/core_formats/multi_format.py +210 -0
  357. oscura/reporting/engine.py +836 -0
  358. oscura/reporting/export.py +366 -0
  359. oscura/reporting/formatting/__init__.py +129 -0
  360. oscura/reporting/formatting/emphasis.py +81 -0
  361. oscura/reporting/formatting/numbers.py +403 -0
  362. oscura/reporting/formatting/standards.py +55 -0
  363. oscura/reporting/formatting.py +466 -0
  364. oscura/reporting/html.py +578 -0
  365. oscura/reporting/index.py +590 -0
  366. oscura/reporting/multichannel.py +296 -0
  367. oscura/reporting/output.py +379 -0
  368. oscura/reporting/pdf.py +373 -0
  369. oscura/reporting/plots.py +731 -0
  370. oscura/reporting/pptx_export.py +360 -0
  371. oscura/reporting/renderers/__init__.py +11 -0
  372. oscura/reporting/renderers/pdf.py +94 -0
  373. oscura/reporting/sections.py +471 -0
  374. oscura/reporting/standards.py +680 -0
  375. oscura/reporting/summary_generator.py +368 -0
  376. oscura/reporting/tables.py +397 -0
  377. oscura/reporting/template_system.py +724 -0
  378. oscura/reporting/templates/__init__.py +15 -0
  379. oscura/reporting/templates/definition.py +205 -0
  380. oscura/reporting/templates/index.html +649 -0
  381. oscura/reporting/templates/index.md +173 -0
  382. oscura/schemas/__init__.py +158 -0
  383. oscura/schemas/bus_configuration.json +322 -0
  384. oscura/schemas/device_mapping.json +182 -0
  385. oscura/schemas/packet_format.json +418 -0
  386. oscura/schemas/protocol_definition.json +363 -0
  387. oscura/search/__init__.py +16 -0
  388. oscura/search/anomaly.py +292 -0
  389. oscura/search/context.py +149 -0
  390. oscura/search/pattern.py +160 -0
  391. oscura/session/__init__.py +34 -0
  392. oscura/session/annotations.py +289 -0
  393. oscura/session/history.py +313 -0
  394. oscura/session/session.py +445 -0
  395. oscura/streaming/__init__.py +43 -0
  396. oscura/streaming/chunked.py +611 -0
  397. oscura/streaming/progressive.py +393 -0
  398. oscura/streaming/realtime.py +622 -0
  399. oscura/testing/__init__.py +54 -0
  400. oscura/testing/synthetic.py +808 -0
  401. oscura/triggering/__init__.py +68 -0
  402. oscura/triggering/base.py +229 -0
  403. oscura/triggering/edge.py +353 -0
  404. oscura/triggering/pattern.py +344 -0
  405. oscura/triggering/pulse.py +581 -0
  406. oscura/triggering/window.py +453 -0
  407. oscura/ui/__init__.py +48 -0
  408. oscura/ui/formatters.py +526 -0
  409. oscura/ui/progressive_display.py +340 -0
  410. oscura/utils/__init__.py +99 -0
  411. oscura/utils/autodetect.py +338 -0
  412. oscura/utils/buffer.py +389 -0
  413. oscura/utils/lazy.py +407 -0
  414. oscura/utils/lazy_imports.py +147 -0
  415. oscura/utils/memory.py +836 -0
  416. oscura/utils/memory_advanced.py +1326 -0
  417. oscura/utils/memory_extensions.py +465 -0
  418. oscura/utils/progressive.py +352 -0
  419. oscura/utils/windowing.py +362 -0
  420. oscura/visualization/__init__.py +321 -0
  421. oscura/visualization/accessibility.py +526 -0
  422. oscura/visualization/annotations.py +374 -0
  423. oscura/visualization/axis_scaling.py +305 -0
  424. oscura/visualization/colors.py +453 -0
  425. oscura/visualization/digital.py +337 -0
  426. oscura/visualization/eye.py +420 -0
  427. oscura/visualization/histogram.py +281 -0
  428. oscura/visualization/interactive.py +858 -0
  429. oscura/visualization/jitter.py +702 -0
  430. oscura/visualization/keyboard.py +394 -0
  431. oscura/visualization/layout.py +365 -0
  432. oscura/visualization/optimization.py +1028 -0
  433. oscura/visualization/palettes.py +446 -0
  434. oscura/visualization/plot.py +92 -0
  435. oscura/visualization/power.py +290 -0
  436. oscura/visualization/power_extended.py +626 -0
  437. oscura/visualization/presets.py +467 -0
  438. oscura/visualization/protocols.py +932 -0
  439. oscura/visualization/render.py +207 -0
  440. oscura/visualization/rendering.py +444 -0
  441. oscura/visualization/reverse_engineering.py +791 -0
  442. oscura/visualization/signal_integrity.py +808 -0
  443. oscura/visualization/specialized.py +553 -0
  444. oscura/visualization/spectral.py +811 -0
  445. oscura/visualization/styles.py +381 -0
  446. oscura/visualization/thumbnails.py +311 -0
  447. oscura/visualization/time_axis.py +351 -0
  448. oscura/visualization/waveform.py +367 -0
  449. oscura/workflow/__init__.py +13 -0
  450. oscura/workflow/dag.py +377 -0
  451. oscura/workflows/__init__.py +58 -0
  452. oscura/workflows/compliance.py +280 -0
  453. oscura/workflows/digital.py +272 -0
  454. oscura/workflows/multi_trace.py +502 -0
  455. oscura/workflows/power.py +178 -0
  456. oscura/workflows/protocol.py +492 -0
  457. oscura/workflows/reverse_engineering.py +639 -0
  458. oscura/workflows/signal_integrity.py +227 -0
  459. oscura-0.1.0.dist-info/METADATA +300 -0
  460. oscura-0.1.0.dist-info/RECORD +463 -0
  461. oscura-0.1.0.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/licenses/LICENSE +1 -1
  463. oscura-0.0.1.dist-info/METADATA +0 -63
  464. oscura-0.0.1.dist-info/RECORD +0 -5
  465. {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,445 @@
1
+ """LIN protocol decoder.
2
+
3
+ This module provides Local Interconnect Network (LIN) automotive protocol
4
+ decoding for LIN 1.x and 2.x frames.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.analyzers.protocols.lin import LINDecoder
9
+ >>> decoder = LINDecoder(baudrate=19200)
10
+ >>> for packet in decoder.decode(trace):
11
+ ... print(f"ID: 0x{packet.annotations['frame_id']:02X}")
12
+
13
+ References:
14
+ LIN Specification 1.3, 2.0, 2.1, 2.2A
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from enum import Enum
20
+ from typing import TYPE_CHECKING, Literal
21
+
22
+ from oscura.analyzers.protocols.base import (
23
+ AnnotationLevel,
24
+ AsyncDecoder,
25
+ ChannelDef,
26
+ OptionDef,
27
+ )
28
+ from oscura.core.types import DigitalTrace, ProtocolPacket, WaveformTrace
29
+
30
+ if TYPE_CHECKING:
31
+ from collections.abc import Iterator
32
+
33
+ import numpy as np
34
+ from numpy.typing import NDArray
35
+
36
+
37
+ class LINVersion(Enum):
38
+ """LIN protocol version."""
39
+
40
+ LIN_1X = "1.x"
41
+ LIN_2X = "2.x"
42
+
43
+
44
+ class LINDecoder(AsyncDecoder):
45
+ """LIN protocol decoder.
46
+
47
+ Decodes LIN bus frames with sync field validation, identifier extraction,
48
+ and checksum validation for both LIN 1.x (classic) and 2.x (enhanced).
49
+
50
+ Attributes:
51
+ id: "lin"
52
+ name: "LIN"
53
+ channels: [bus] (required)
54
+
55
+ Example:
56
+ >>> decoder = LINDecoder(baudrate=19200, version="2.x")
57
+ >>> for packet in decoder.decode(trace):
58
+ ... print(f"ID: 0x{packet.annotations['frame_id']:02X}")
59
+ """
60
+
61
+ id = "lin"
62
+ name = "LIN"
63
+ longname = "Local Interconnect Network"
64
+ desc = "LIN automotive bus protocol decoder"
65
+
66
+ channels = [ # noqa: RUF012
67
+ ChannelDef("bus", "BUS", "LIN bus signal", required=True),
68
+ ]
69
+
70
+ optional_channels = [] # noqa: RUF012
71
+
72
+ options = [ # noqa: RUF012
73
+ OptionDef(
74
+ "baudrate",
75
+ "Baud rate",
76
+ "Bits per second",
77
+ default=19200,
78
+ values=[9600, 19200, 20000],
79
+ ),
80
+ OptionDef(
81
+ "version",
82
+ "LIN version",
83
+ "Protocol version",
84
+ default="2.x",
85
+ values=["1.x", "2.x"],
86
+ ),
87
+ ]
88
+
89
+ annotations = [ # noqa: RUF012
90
+ ("sync", "Sync field"),
91
+ ("pid", "Protected identifier"),
92
+ ("data", "Data bytes"),
93
+ ("checksum", "Checksum"),
94
+ ("error", "Error"),
95
+ ]
96
+
97
+ def __init__(
98
+ self,
99
+ baudrate: int = 19200,
100
+ version: Literal["1.x", "2.x"] = "2.x",
101
+ ) -> None:
102
+ """Initialize LIN decoder.
103
+
104
+ Args:
105
+ baudrate: Baud rate in bps (9600, 19200, 20000).
106
+ version: LIN version ("1.x" or "2.x").
107
+ """
108
+ super().__init__(baudrate=baudrate, version=version)
109
+ self._version = LINVersion.LIN_1X if version == "1.x" else LINVersion.LIN_2X
110
+
111
+ def decode(
112
+ self,
113
+ trace: DigitalTrace | WaveformTrace,
114
+ **channels: NDArray[np.bool_],
115
+ ) -> Iterator[ProtocolPacket]:
116
+ """Decode LIN frames from trace.
117
+
118
+ Args:
119
+ trace: Input digital trace.
120
+ **channels: Additional channel data.
121
+
122
+ Yields:
123
+ Decoded LIN frames as ProtocolPacket objects.
124
+
125
+ Example:
126
+ >>> decoder = LINDecoder(baudrate=19200)
127
+ >>> for packet in decoder.decode(trace):
128
+ ... print(f"Data: {packet.data.hex()}")
129
+ """
130
+ # Convert to digital if needed
131
+ if isinstance(trace, WaveformTrace):
132
+ from oscura.analyzers.digital.extraction import to_digital
133
+
134
+ digital_trace = to_digital(trace, threshold="auto")
135
+ else:
136
+ digital_trace = trace
137
+
138
+ data = digital_trace.data
139
+ sample_rate = digital_trace.metadata.sample_rate
140
+
141
+ bit_period = sample_rate / self._baudrate
142
+ half_bit = bit_period / 2
143
+
144
+ idx = 0
145
+ frame_num = 0
146
+
147
+ while idx < len(data):
148
+ # Look for break field (dominant for at least 13 bit times)
149
+ break_start = self._find_break_field(data, idx, bit_period)
150
+ if break_start is None:
151
+ break
152
+
153
+ # After break field, find the end of the dominant period (break)
154
+ # and skip the delimiter (recessive) to reach the sync byte
155
+ sync_start_idx = break_start
156
+ while sync_start_idx < len(data) and not data[sync_start_idx]:
157
+ sync_start_idx += 1
158
+
159
+ # Skip delimiter (recessive period) to reach sync byte start bit
160
+ # The delimiter is at least 1 bit time recessive
161
+ while sync_start_idx < len(data) and data[sync_start_idx]:
162
+ sync_start_idx += 1
163
+
164
+ if sync_start_idx >= len(data):
165
+ break
166
+
167
+ # Sync field is 0x55 (01010101)
168
+ sync_byte, sync_errors = self._decode_byte(data, sync_start_idx, bit_period, half_bit)
169
+
170
+ if sync_byte != 0x55:
171
+ sync_errors.append(f"Invalid sync field: 0x{sync_byte:02X} (expected 0x55)")
172
+
173
+ # Protected identifier (PID)
174
+ pid_start_idx = int(sync_start_idx + 10 * bit_period)
175
+ if pid_start_idx >= len(data):
176
+ break
177
+
178
+ pid_byte, pid_errors = self._decode_byte(data, pid_start_idx, bit_period, half_bit)
179
+
180
+ # Extract ID and parity
181
+ frame_id = pid_byte & 0x3F
182
+ parity = (pid_byte >> 6) & 0x03
183
+
184
+ # Validate parity
185
+ expected_parity = self._compute_parity(frame_id)
186
+ if parity != expected_parity:
187
+ pid_errors.append(f"Parity error: {parity} (expected {expected_parity})")
188
+
189
+ # Data length depends on frame ID
190
+ data_length = self._get_data_length(frame_id)
191
+
192
+ # Decode data bytes
193
+ data_bytes = []
194
+ data_errors = []
195
+ data_start_idx = int(pid_start_idx + 10 * bit_period)
196
+
197
+ for i in range(data_length):
198
+ byte_start_idx = int(data_start_idx + i * 10 * bit_period)
199
+ if byte_start_idx >= len(data):
200
+ break
201
+
202
+ byte_val, byte_errors = self._decode_byte(
203
+ data, byte_start_idx, bit_period, half_bit
204
+ )
205
+ data_bytes.append(byte_val)
206
+ data_errors.extend(byte_errors)
207
+
208
+ # Decode checksum
209
+ checksum_start_idx = int(data_start_idx + data_length * 10 * bit_period)
210
+ if checksum_start_idx < len(data):
211
+ checksum_byte, checksum_errors = self._decode_byte(
212
+ data, checksum_start_idx, bit_period, half_bit
213
+ )
214
+
215
+ # Validate checksum
216
+ expected_checksum = self._compute_checksum(frame_id, data_bytes)
217
+ if checksum_byte != expected_checksum:
218
+ checksum_errors.append(
219
+ f"Checksum error: 0x{checksum_byte:02X} (expected 0x{expected_checksum:02X})"
220
+ )
221
+ else:
222
+ checksum_byte = 0
223
+ checksum_errors = ["Missing checksum"]
224
+
225
+ # Calculate timestamps
226
+ start_time = break_start / sample_rate
227
+ end_time = (checksum_start_idx + 10 * bit_period) / sample_rate
228
+
229
+ # Collect all errors
230
+ errors = sync_errors + pid_errors + data_errors + checksum_errors
231
+
232
+ # Add annotations
233
+ self.put_annotation(
234
+ start_time,
235
+ end_time,
236
+ AnnotationLevel.PACKETS,
237
+ f"ID: 0x{frame_id:02X}",
238
+ data=bytes(data_bytes),
239
+ )
240
+
241
+ # Create packet
242
+ annotations = {
243
+ "frame_num": frame_num,
244
+ "frame_id": frame_id,
245
+ "pid": pid_byte,
246
+ "data_length": data_length,
247
+ "checksum": checksum_byte,
248
+ "version": self._version.value,
249
+ }
250
+
251
+ packet = ProtocolPacket(
252
+ timestamp=start_time,
253
+ protocol="lin",
254
+ data=bytes(data_bytes),
255
+ annotations=annotations,
256
+ errors=errors,
257
+ )
258
+
259
+ yield packet
260
+
261
+ frame_num += 1
262
+ idx = int(checksum_start_idx + 10 * bit_period)
263
+
264
+ def _find_break_field(
265
+ self,
266
+ data: NDArray[np.bool_],
267
+ start_idx: int,
268
+ bit_period: float,
269
+ ) -> int | None:
270
+ """Find LIN break field (dominant for >= 13 bits).
271
+
272
+ Args:
273
+ data: Digital data array.
274
+ start_idx: Index to start searching.
275
+ bit_period: Bit period in samples.
276
+
277
+ Returns:
278
+ Index of break field start, or None if not found.
279
+ """
280
+ # Use a slightly smaller threshold to account for rounding
281
+ # LIN spec requires >= 13 bit times, use 12.5 to be tolerant
282
+ min_break_samples = int(12.5 * bit_period)
283
+
284
+ idx = start_idx
285
+ while idx < len(data) - min_break_samples:
286
+ # Look for recessive-to-dominant transition
287
+ if idx > 0 and data[idx - 1] and not data[idx]:
288
+ # Check if dominant for at least 12.5 bit periods
289
+ dominant_length = 0
290
+ check_idx = idx
291
+ while check_idx < len(data) and not data[check_idx]:
292
+ dominant_length += 1
293
+ check_idx += 1
294
+
295
+ if dominant_length >= min_break_samples:
296
+ return idx
297
+
298
+ idx += 1
299
+
300
+ return None
301
+
302
+ def _decode_byte(
303
+ self,
304
+ data: NDArray[np.bool_],
305
+ start_idx: int,
306
+ bit_period: float,
307
+ half_bit: float,
308
+ ) -> tuple[int, list[str]]:
309
+ """Decode UART-style byte (1 start, 8 data, 1 stop).
310
+
311
+ Args:
312
+ data: Digital data array.
313
+ start_idx: Start index (at start bit).
314
+ bit_period: Bit period in samples.
315
+ half_bit: Half bit period in samples.
316
+
317
+ Returns:
318
+ (byte_value, errors) tuple.
319
+ """
320
+ errors = []
321
+
322
+ # Sample at center of each bit
323
+ sample_points = []
324
+ for bit_num in range(10): # Start + 8 data + stop
325
+ sample_idx = int(start_idx + half_bit + bit_num * bit_period)
326
+ if sample_idx < len(data):
327
+ sample_points.append(sample_idx)
328
+
329
+ if len(sample_points) < 10:
330
+ return 0, ["Incomplete byte"]
331
+
332
+ # Verify start bit (should be 0)
333
+ if data[sample_points[0]]:
334
+ errors.append("Invalid start bit")
335
+
336
+ # Extract data bits (LSB first)
337
+ byte_val = 0
338
+ for i in range(8):
339
+ bit = 1 if data[sample_points[1 + i]] else 0
340
+ byte_val |= bit << i
341
+
342
+ # Verify stop bit (should be 1)
343
+ if not data[sample_points[9]]:
344
+ errors.append("Framing error")
345
+
346
+ return byte_val, errors
347
+
348
+ def _compute_parity(self, frame_id: int) -> int:
349
+ """Compute LIN 2.x protected identifier parity.
350
+
351
+ Args:
352
+ frame_id: 6-bit frame identifier.
353
+
354
+ Returns:
355
+ 2-bit parity value.
356
+ """
357
+ # Extract ID bits
358
+ id0 = (frame_id >> 0) & 1
359
+ id1 = (frame_id >> 1) & 1
360
+ id2 = (frame_id >> 2) & 1
361
+ id3 = (frame_id >> 3) & 1
362
+ id4 = (frame_id >> 4) & 1
363
+ id5 = (frame_id >> 5) & 1
364
+
365
+ # P0 = ID0 ^ ID1 ^ ID2 ^ ID4
366
+ p0 = id0 ^ id1 ^ id2 ^ id4
367
+
368
+ # P1 = !(ID1 ^ ID3 ^ ID4 ^ ID5)
369
+ p1 = (id1 ^ id3 ^ id4 ^ id5) ^ 1
370
+
371
+ return (p1 << 1) | p0
372
+
373
+ def _get_data_length(self, frame_id: int) -> int:
374
+ """Get data length for frame ID.
375
+
376
+ Args:
377
+ frame_id: Frame identifier.
378
+
379
+ Returns:
380
+ Data length in bytes (0-8).
381
+ """
382
+ # Standard frame IDs have predefined lengths
383
+ # For simplicity, assume 8 bytes (can be configured per application)
384
+ return 8
385
+
386
+ def _compute_checksum(self, frame_id: int, data_bytes: list[int]) -> int:
387
+ """Compute LIN checksum.
388
+
389
+ Args:
390
+ frame_id: Frame identifier.
391
+ data_bytes: Data bytes.
392
+
393
+ Returns:
394
+ Checksum byte.
395
+ """
396
+ if self._version == LINVersion.LIN_1X:
397
+ # Classic checksum: sum of data bytes
398
+ checksum = sum(data_bytes)
399
+ else:
400
+ # Enhanced checksum: sum of PID + data bytes
401
+ pid = frame_id | (self._compute_parity(frame_id) << 6)
402
+ checksum = pid + sum(data_bytes)
403
+
404
+ # Handle carries
405
+ while checksum > 0xFF:
406
+ checksum = (checksum & 0xFF) + (checksum >> 8)
407
+
408
+ # Invert
409
+ return (~checksum) & 0xFF
410
+
411
+
412
+ def decode_lin(
413
+ data: NDArray[np.bool_] | WaveformTrace | DigitalTrace,
414
+ sample_rate: float = 1.0,
415
+ baudrate: int = 19200,
416
+ version: Literal["1.x", "2.x"] = "2.x",
417
+ ) -> list[ProtocolPacket]:
418
+ """Convenience function to decode LIN frames.
419
+
420
+ Args:
421
+ data: LIN bus signal (digital array or trace).
422
+ sample_rate: Sample rate in Hz.
423
+ baudrate: Baud rate (9600, 19200, 20000).
424
+ version: LIN version ("1.x" or "2.x").
425
+
426
+ Returns:
427
+ List of decoded LIN frames.
428
+
429
+ Example:
430
+ >>> packets = decode_lin(signal, sample_rate=1e6, baudrate=19200)
431
+ >>> for pkt in packets:
432
+ ... print(f"ID: 0x{pkt.annotations['frame_id']:02X}")
433
+ """
434
+ decoder = LINDecoder(baudrate=baudrate, version=version)
435
+ if isinstance(data, WaveformTrace | DigitalTrace):
436
+ return list(decoder.decode(data))
437
+ else:
438
+ from oscura.core.types import TraceMetadata
439
+
440
+ metadata = TraceMetadata(sample_rate=sample_rate)
441
+ trace = DigitalTrace(data=data, metadata=metadata)
442
+ return list(decoder.decode(trace))
443
+
444
+
445
+ __all__ = ["LINDecoder", "LINVersion", "decode_lin"]