oscura 0.0.1__py3-none-any.whl → 0.1.1__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.1.dist-info/METADATA +300 -0
  460. oscura-0.1.1.dist-info/RECORD +463 -0
  461. oscura-0.1.1.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.1.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.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,230 @@
1
+ """L* observation table for DFA learning.
2
+
3
+ This module implements the observation table data structure used in
4
+ Angluin's L* algorithm for active learning of deterministic finite automata.
5
+
6
+ References:
7
+ Angluin, D. (1987). Learning regular sets from queries and counterexamples.
8
+ Information and Computation, 75(2), 87-106.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import dataclass, field
14
+
15
+ from oscura.inference.state_machine import FiniteAutomaton, State, Transition
16
+
17
+
18
+ @dataclass
19
+ class ObservationTable:
20
+ """L* observation table for DFA learning.
21
+
22
+ The observation table is the core data structure in the L* algorithm.
23
+ It maintains:
24
+ - S: Set of prefixes (rows of upper part)
25
+ - SA: Set of S + Σ (one-step extensions, lower part)
26
+ - E: Set of suffixes (columns/experiments)
27
+ - T: Table entries mapping (prefix + suffix) -> bool (accept/reject)
28
+
29
+ A table is:
30
+ - Closed: Every row in SA has an equivalent row in S
31
+ - Consistent: Identical rows in S have identical one-step extensions
32
+
33
+ Attributes:
34
+ S: Set of prefixes (represented as tuples of symbols)
35
+ E: Set of suffixes (represented as tuples of symbols)
36
+ T: Mapping from string (prefix+suffix) to acceptance
37
+ alphabet: Alphabet of symbols
38
+ """
39
+
40
+ S: set[tuple[str, ...]] = field(default_factory=lambda: {()})
41
+ E: set[tuple[str, ...]] = field(default_factory=lambda: {()})
42
+ T: dict[tuple[str, ...], bool] = field(default_factory=dict)
43
+ alphabet: set[str] = field(default_factory=set)
44
+
45
+ def row(self, s: tuple[str, ...]) -> tuple[bool, ...]:
46
+ """Get row for prefix s.
47
+
48
+ Returns the row in the observation table for prefix s, which
49
+ consists of the membership query results for s·e for each suffix e in E.
50
+
51
+ Args:
52
+ s: Prefix (tuple of symbols)
53
+
54
+ Returns:
55
+ Tuple of boolean values representing the row
56
+ """
57
+ return tuple(self.T.get(s + e, False) for e in sorted(self.E))
58
+
59
+ def is_closed(self) -> bool:
60
+ """Check if table is closed.
61
+
62
+ A table is closed if for every row in SA (one-step extensions),
63
+ there exists an equivalent row in S.
64
+
65
+ Returns:
66
+ True if table is closed
67
+ """
68
+ return self.find_closing_counterexample() is None
69
+
70
+ def is_consistent(self) -> bool:
71
+ """Check if table is consistent.
72
+
73
+ A table is consistent if for any two prefixes s1, s2 in S with
74
+ identical rows, all one-step extensions s1·a and s2·a also have
75
+ identical rows for each symbol a in the alphabet.
76
+
77
+ Returns:
78
+ True if table is consistent
79
+ """
80
+ return self.find_consistency_counterexample() is None
81
+
82
+ def find_closing_counterexample(self) -> tuple[str, ...] | None:
83
+ """Find row in SA without equivalent in S.
84
+
85
+ Searches for a string s·a where s ∈ S and a ∈ Σ such that
86
+ row(s·a) is not equal to row(s') for any s' ∈ S.
87
+
88
+ Returns:
89
+ String in SA without equivalent in S, or None if closed
90
+ """
91
+ # Get all rows in S
92
+ s_rows = {s: self.row(s) for s in self.S}
93
+
94
+ # Check all one-step extensions
95
+ for s in self.S:
96
+ for a in self.alphabet:
97
+ sa = s + (a,)
98
+ sa_row = self.row(sa)
99
+
100
+ # Check if this row exists in S
101
+ if sa_row not in s_rows.values():
102
+ return sa
103
+
104
+ return None
105
+
106
+ def find_consistency_counterexample(
107
+ self,
108
+ ) -> tuple[tuple[str, ...], tuple[str, ...], str] | None:
109
+ """Find inconsistency in table.
110
+
111
+ Searches for two prefixes s1, s2 in S with identical rows and
112
+ a symbol a such that row(s1·a) ≠ row(s2·a).
113
+
114
+ Returns:
115
+ Tuple (s1, s2, a) representing inconsistency, or None if consistent
116
+ """
117
+ # Build mapping from rows to prefixes
118
+ row_to_prefixes: dict[tuple[bool, ...], list[tuple[str, ...]]] = {}
119
+ for s in self.S:
120
+ r = self.row(s)
121
+ if r not in row_to_prefixes:
122
+ row_to_prefixes[r] = []
123
+ row_to_prefixes[r].append(s)
124
+
125
+ # Check each equivalence class
126
+ for prefixes in row_to_prefixes.values():
127
+ if len(prefixes) < 2:
128
+ continue
129
+
130
+ # Check all pairs in this equivalence class
131
+ for i in range(len(prefixes)):
132
+ for j in range(i + 1, len(prefixes)):
133
+ s1 = prefixes[i]
134
+ s2 = prefixes[j]
135
+
136
+ # Check each symbol
137
+ for a in self.alphabet:
138
+ s1a = s1 + (a,)
139
+ s2a = s2 + (a,)
140
+
141
+ if self.row(s1a) != self.row(s2a):
142
+ return (s1, s2, a)
143
+
144
+ return None
145
+
146
+ def to_dfa(self) -> FiniteAutomaton:
147
+ """Construct hypothesis DFA from closed, consistent table.
148
+
149
+ Creates a DFA where:
150
+ - Each distinct row in S corresponds to a state
151
+ - Transitions are determined by row(s·a)
152
+ - Initial state corresponds to row(ε)
153
+ - Accepting states are those where T[s·ε] = True
154
+
155
+ Returns:
156
+ FiniteAutomaton constructed from table
157
+
158
+ Raises:
159
+ ValueError: If table is not closed or not consistent
160
+ """
161
+ if not self.is_closed():
162
+ raise ValueError("Table must be closed to construct DFA")
163
+ if not self.is_consistent():
164
+ raise ValueError("Table must be consistent to construct DFA")
165
+
166
+ # Map rows to state IDs
167
+ row_to_state: dict[tuple[bool, ...], int] = {}
168
+ state_to_row: dict[int, tuple[bool, ...]] = {}
169
+ state_representatives: dict[int, tuple[str, ...]] = {}
170
+
171
+ state_id = 0
172
+ for s in sorted(self.S):
173
+ r = self.row(s)
174
+ if r not in row_to_state:
175
+ row_to_state[r] = state_id
176
+ state_to_row[state_id] = r
177
+ state_representatives[state_id] = s
178
+ state_id += 1
179
+
180
+ # Create states
181
+ states = []
182
+ accepting_states = set()
183
+
184
+ for sid in range(state_id):
185
+ rep = state_representatives[sid]
186
+ # State is accepting if T[rep] = True (i.e., rep is accepted)
187
+ is_accepting = self.T.get(rep, False)
188
+ is_initial = rep == ()
189
+
190
+ state = State(
191
+ id=sid,
192
+ name=f"q{sid}",
193
+ is_initial=is_initial,
194
+ is_accepting=is_accepting,
195
+ )
196
+ states.append(state)
197
+
198
+ if is_accepting:
199
+ accepting_states.add(sid)
200
+
201
+ # Create transitions
202
+ transitions = []
203
+ seen_transitions = set()
204
+
205
+ for sid in range(state_id):
206
+ rep = state_representatives[sid]
207
+
208
+ for a in sorted(self.alphabet):
209
+ # Find target state
210
+ rep_a = rep + (a,)
211
+ target_row = self.row(rep_a)
212
+ target_state = row_to_state[target_row]
213
+
214
+ # Add transition
215
+ key = (sid, target_state, a)
216
+ if key not in seen_transitions:
217
+ seen_transitions.add(key)
218
+ transitions.append(Transition(source=sid, target=target_state, symbol=a))
219
+
220
+ # Find initial state
221
+ initial_row = self.row(())
222
+ initial_state = row_to_state[initial_row]
223
+
224
+ return FiniteAutomaton(
225
+ states=states,
226
+ transitions=transitions,
227
+ alphabet=self.alphabet.copy(),
228
+ initial_state=initial_state,
229
+ accepting_states=accepting_states,
230
+ )
@@ -0,0 +1,78 @@
1
+ """Oracle interface for L* active learning.
2
+
3
+ This module defines the oracle interface used in the L* algorithm.
4
+ The oracle answers membership and equivalence queries about the target DFA.
5
+
6
+ References:
7
+ Angluin, D. (1987). Learning regular sets from queries and counterexamples.
8
+ Information and Computation, 75(2), 87-106.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from abc import ABC, abstractmethod
14
+
15
+ from oscura.inference.state_machine import FiniteAutomaton # noqa: TC001
16
+
17
+
18
+ class Oracle(ABC):
19
+ """Oracle for answering membership and equivalence queries.
20
+
21
+ An oracle provides access to the target language through two types of queries:
22
+
23
+ 1. Membership Query: "Is word w accepted by the target DFA?"
24
+ - Returns True if w is in the target language, False otherwise
25
+ - Must be consistent (same query always returns same answer)
26
+
27
+ 2. Equivalence Query: "Is hypothesis H equivalent to the target DFA?"
28
+ - Returns None if H is equivalent to target
29
+ - Returns a counterexample word if H differs from target
30
+ - Counterexample is a word accepted by exactly one of H or target
31
+
32
+ The oracle also provides the alphabet of the target language.
33
+ """
34
+
35
+ @abstractmethod
36
+ def membership_query(self, word: tuple[str, ...]) -> bool:
37
+ """Answer membership query: Is word accepted by target DFA?
38
+
39
+ Args:
40
+ word: Sequence of symbols to test
41
+
42
+ Returns:
43
+ True if word is accepted by target language, False otherwise
44
+ """
45
+ ...
46
+
47
+ @abstractmethod
48
+ def equivalence_query(self, hypothesis: FiniteAutomaton) -> tuple[str, ...] | None:
49
+ """Answer equivalence query: Is hypothesis equivalent to target?
50
+
51
+ Args:
52
+ hypothesis: Proposed DFA to test for equivalence
53
+
54
+ Returns:
55
+ None if hypothesis is equivalent to target, otherwise a
56
+ counterexample word that is accepted by exactly one of
57
+ hypothesis or target
58
+ """
59
+ ...
60
+
61
+ @abstractmethod
62
+ def get_alphabet(self) -> set[str]:
63
+ """Get the alphabet of the target language.
64
+
65
+ Returns:
66
+ Set of symbols in the target alphabet
67
+ """
68
+ ...
69
+
70
+ def get_query_counts(self) -> tuple[int, int]:
71
+ """Get counts of membership and equivalence queries.
72
+
73
+ This is useful for analyzing the efficiency of the learning algorithm.
74
+
75
+ Returns:
76
+ Tuple of (membership_queries, equivalence_queries)
77
+ """
78
+ return (0, 0) # Default implementation, can be overridden
@@ -0,0 +1,15 @@
1
+ """Teachers for L* active learning.
2
+
3
+ This module provides different oracle implementations (teachers) for the
4
+ L* algorithm. Teachers can be based on:
5
+
6
+ - Simulator: Replay from captured protocol traces
7
+ - Interactive: Live device interaction
8
+ - Model: From formal protocol specification
9
+ """
10
+
11
+ from oscura.inference.active_learning.teachers.simulator import SimulatorTeacher
12
+
13
+ __all__ = [
14
+ "SimulatorTeacher",
15
+ ]
@@ -0,0 +1,192 @@
1
+ """Simulator teacher for L* active learning.
2
+
3
+ This module implements an oracle that uses captured protocol traces as
4
+ ground truth. It's useful for learning DFAs from historical data without
5
+ requiring a live system.
6
+
7
+ The simulator teacher treats traces as examples of valid protocol sequences.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from oscura.inference.active_learning.oracle import Oracle
13
+ from oscura.inference.state_machine import FiniteAutomaton # noqa: TC001
14
+
15
+
16
+ class SimulatorTeacher(Oracle):
17
+ """Oracle that replays captured protocol traces.
18
+
19
+ This teacher uses captured message traces as ground truth for learning.
20
+ It answers queries based on whether sequences appear in the captured traces.
21
+
22
+ Membership queries check if a word is a valid prefix of any trace.
23
+ Equivalence queries test the hypothesis against all captured traces.
24
+
25
+ This is useful for:
26
+ - Learning from historical protocol captures
27
+ - Offline protocol analysis
28
+ - Testing L* implementation with known data
29
+
30
+ Attributes:
31
+ traces: List of message sequences (each trace is list of symbols)
32
+ alphabet: Set of all symbols appearing in traces
33
+ membership_query_count: Number of membership queries answered
34
+ equivalence_query_count: Number of equivalence queries answered
35
+ """
36
+
37
+ def __init__(self, traces: list[list[str]]):
38
+ """Initialize simulator teacher from captured traces.
39
+
40
+ Args:
41
+ traces: List of message sequences, where each sequence is a
42
+ list of symbols (strings). All traces are treated as
43
+ positive examples (accepted sequences).
44
+
45
+ Raises:
46
+ ValueError: If no traces provided
47
+ """
48
+ if not traces:
49
+ raise ValueError("Need at least one trace")
50
+
51
+ self.traces = traces
52
+ self.alphabet = self._extract_alphabet()
53
+ self.membership_query_count = 0
54
+ self.equivalence_query_count = 0
55
+
56
+ # Build set of all valid prefixes for efficient membership queries
57
+ self._valid_prefixes: set[tuple[str, ...]] = set()
58
+ self._build_prefix_set()
59
+
60
+ def _extract_alphabet(self) -> set[str]:
61
+ """Extract alphabet from all traces.
62
+
63
+ Returns:
64
+ Set of all unique symbols in traces
65
+ """
66
+ alphabet = set()
67
+ for trace in self.traces:
68
+ alphabet.update(trace)
69
+ return alphabet
70
+
71
+ def _build_prefix_set(self) -> None:
72
+ """Build set of all valid prefixes from traces.
73
+
74
+ Pre-computes all prefixes for efficient membership query answering.
75
+ """
76
+ for trace in self.traces:
77
+ # Add empty prefix
78
+ self._valid_prefixes.add(())
79
+
80
+ # Add all prefixes of this trace
81
+ for i in range(1, len(trace) + 1):
82
+ prefix = tuple(trace[:i])
83
+ self._valid_prefixes.add(prefix)
84
+
85
+ def membership_query(self, word: tuple[str, ...]) -> bool:
86
+ """Check if word is a valid prefix of any trace.
87
+
88
+ A word is accepted if it appears as a prefix (including the full
89
+ sequence) of at least one captured trace.
90
+
91
+ Args:
92
+ word: Sequence of symbols to test
93
+
94
+ Returns:
95
+ True if word is a prefix of any trace, False otherwise
96
+ """
97
+ self.membership_query_count += 1
98
+ return word in self._valid_prefixes
99
+
100
+ def equivalence_query(self, hypothesis: FiniteAutomaton) -> tuple[str, ...] | None:
101
+ """Check hypothesis against all traces.
102
+
103
+ Tests whether the hypothesis DFA correctly accepts all captured traces
104
+ and their prefixes. Returns a counterexample if the hypothesis disagrees
105
+ with the trace data.
106
+
107
+ Args:
108
+ hypothesis: Proposed DFA to test
109
+
110
+ Returns:
111
+ None if hypothesis matches all trace data, otherwise a
112
+ counterexample word that hypothesis classifies incorrectly
113
+ """
114
+ self.equivalence_query_count += 1
115
+
116
+ # Check all valid prefixes
117
+ for prefix in self._valid_prefixes:
118
+ hypothesis_accepts = hypothesis.accepts(list(prefix))
119
+ target_accepts = True # All prefixes should be accepted
120
+
121
+ if hypothesis_accepts != target_accepts:
122
+ return prefix
123
+
124
+ # Also check some strings not in traces (they should be rejected)
125
+ # Generate some random invalid sequences
126
+ invalid_sequences = self._generate_invalid_sequences(hypothesis)
127
+
128
+ for seq in invalid_sequences:
129
+ hypothesis_accepts = hypothesis.accepts(list(seq))
130
+ target_accepts = seq in self._valid_prefixes
131
+
132
+ if hypothesis_accepts != target_accepts:
133
+ return seq
134
+
135
+ return None
136
+
137
+ def _generate_invalid_sequences(
138
+ self, hypothesis: FiniteAutomaton, max_checks: int = 100
139
+ ) -> list[tuple[str, ...]]:
140
+ """Generate sequences not in traces to test hypothesis.
141
+
142
+ Creates test sequences by:
143
+ 1. Taking prefixes from traces and extending with different symbols
144
+ 2. Creating short random sequences
145
+ 3. Concatenating partial traces
146
+
147
+ Args:
148
+ hypothesis: Current hypothesis (used to guide generation)
149
+ max_checks: Maximum number of invalid sequences to generate
150
+
151
+ Returns:
152
+ List of sequences not in valid prefixes
153
+ """
154
+ invalid = []
155
+
156
+ # Strategy 1: Extend valid prefixes with unexpected symbols
157
+ for prefix in list(self._valid_prefixes)[:20]: # Limit to avoid too many
158
+ for symbol in self.alphabet:
159
+ candidate = prefix + (symbol,)
160
+ if candidate not in self._valid_prefixes:
161
+ invalid.append(candidate)
162
+ if len(invalid) >= max_checks:
163
+ return invalid
164
+
165
+ # Strategy 2: Create sequences from alphabet permutations
166
+ if len(self.alphabet) > 0:
167
+ alphabet_list = list(self.alphabet)
168
+ for i in range(min(5, len(alphabet_list))):
169
+ for j in range(min(5, len(alphabet_list))):
170
+ candidate = (alphabet_list[i], alphabet_list[j])
171
+ if candidate not in self._valid_prefixes:
172
+ invalid.append(candidate)
173
+ if len(invalid) >= max_checks:
174
+ return invalid
175
+
176
+ return invalid
177
+
178
+ def get_alphabet(self) -> set[str]:
179
+ """Get the alphabet of the target language.
180
+
181
+ Returns:
182
+ Set of symbols extracted from traces
183
+ """
184
+ return self.alphabet.copy()
185
+
186
+ def get_query_counts(self) -> tuple[int, int]:
187
+ """Get counts of membership and equivalence queries.
188
+
189
+ Returns:
190
+ Tuple of (membership_queries, equivalence_queries)
191
+ """
192
+ return (self.membership_query_count, self.equivalence_query_count)