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,365 @@
1
+ """Visualization layout functions for multi-channel plots and annotation placement.
2
+
3
+ This module provides intelligent layout algorithms for stacking multiple
4
+ channels and optimizing annotation placement with collision avoidance.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.visualization.layout import layout_stacked_channels
9
+ >>> layout = layout_stacked_channels(n_channels=4, figsize=(10, 8))
10
+ >>> print(f"Channel heights: {layout['heights']}")
11
+
12
+ References:
13
+ - Force-directed graph layout (Fruchterman-Reingold)
14
+ - Constrained layout solver for equal spacing
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from dataclasses import dataclass
20
+ from typing import TYPE_CHECKING
21
+
22
+ import numpy as np
23
+
24
+ if TYPE_CHECKING:
25
+ from numpy.typing import NDArray
26
+
27
+
28
+ @dataclass
29
+ class ChannelLayout:
30
+ """Layout specification for stacked channels.
31
+
32
+ Attributes:
33
+ n_channels: Number of channels to stack
34
+ heights: Array of subplot heights (normalized 0-1)
35
+ gaps: Array of gap sizes between channels (normalized 0-1)
36
+ y_positions: Array of Y positions for each channel (normalized 0-1)
37
+ shared_x: Whether channels share X-axis
38
+ figsize: Figure size (width, height) in inches
39
+ """
40
+
41
+ n_channels: int
42
+ heights: NDArray[np.float64]
43
+ gaps: NDArray[np.float64]
44
+ y_positions: NDArray[np.float64]
45
+ shared_x: bool
46
+ figsize: tuple[float, float]
47
+
48
+
49
+ @dataclass
50
+ class Annotation:
51
+ """Annotation specification with position and bounding box.
52
+
53
+ Attributes:
54
+ text: Annotation text
55
+ x: X coordinate in data units
56
+ y: Y coordinate in data units
57
+ bbox_width: Bounding box width in display units
58
+ bbox_height: Bounding box height in display units
59
+ priority: Priority for placement (0-1, higher is more important)
60
+ anchor: Preferred anchor position ("top", "bottom", "left", "right", "auto")
61
+ """
62
+
63
+ text: str
64
+ x: float
65
+ y: float
66
+ bbox_width: float = 50.0
67
+ bbox_height: float = 20.0
68
+ priority: float = 0.5
69
+ anchor: str = "auto"
70
+
71
+
72
+ @dataclass
73
+ class PlacedAnnotation:
74
+ """Annotation with optimized placement.
75
+
76
+ Attributes:
77
+ annotation: Original annotation
78
+ display_x: Optimized X position in display units
79
+ display_y: Optimized Y position in display units
80
+ needs_leader: Whether a leader line is needed
81
+ leader_points: Points for leader line (if needed)
82
+ """
83
+
84
+ annotation: Annotation
85
+ display_x: float
86
+ display_y: float
87
+ needs_leader: bool
88
+ leader_points: list[tuple[float, float]] | None = None
89
+
90
+
91
+ def layout_stacked_channels(
92
+ n_channels: int,
93
+ *,
94
+ figsize: tuple[float, float] = (10, 8),
95
+ gap_ratio: float = 0.1,
96
+ shared_x: bool = True,
97
+ ) -> ChannelLayout:
98
+ """Calculate equal vertical spacing for stacked multi-channel plots.
99
+
100
+ Implements constrained layout solver for equal spacing with configurable
101
+ gaps between channels, ensuring proper vertical alignment.
102
+
103
+ Args:
104
+ n_channels: Number of channels to stack.
105
+ figsize: Figure size (width, height) in inches.
106
+ gap_ratio: Ratio of gap to channel height (default 0.1 = 10%).
107
+ shared_x: Whether channels share X-axis (affects bottom margin).
108
+
109
+ Returns:
110
+ ChannelLayout with heights, gaps, and positions.
111
+
112
+ Raises:
113
+ ValueError: If n_channels < 1 or gap_ratio invalid.
114
+
115
+ Example:
116
+ >>> layout = layout_stacked_channels(n_channels=3, gap_ratio=0.1)
117
+ >>> print(f"Channel 0 position: {layout.y_positions[0]:.3f}")
118
+
119
+ References:
120
+ VIS-015: Multi-Channel Stack Optimization
121
+ """
122
+ if n_channels < 1:
123
+ raise ValueError("n_channels must be >= 1")
124
+
125
+ if gap_ratio < 0 or gap_ratio > 1:
126
+ raise ValueError(f"gap_ratio must be in [0, 1], got {gap_ratio}")
127
+
128
+ # Total available height (normalized to 1.0)
129
+ # Reserve space for margins
130
+ top_margin = 0.05
131
+ bottom_margin = 0.1 if shared_x else 0.05
132
+ available_height = 1.0 - top_margin - bottom_margin
133
+
134
+ # Calculate channel height with gaps
135
+ # Total height = n_channels * h + (n_channels - 1) * gap
136
+ # where gap = gap_ratio * h
137
+ # Solving: available_height = n_channels * h + (n_channels - 1) * gap_ratio * h
138
+ # = h * (n_channels + (n_channels - 1) * gap_ratio)
139
+ denominator = n_channels + (n_channels - 1) * gap_ratio
140
+ channel_height = available_height / denominator
141
+ gap_height = channel_height * gap_ratio
142
+
143
+ # Calculate heights and gaps arrays
144
+ heights = np.full(n_channels, channel_height, dtype=np.float64)
145
+ gaps = np.full(n_channels - 1, gap_height, dtype=np.float64) if n_channels > 1 else np.array([])
146
+
147
+ # Calculate Y positions (from bottom)
148
+ y_positions = np.zeros(n_channels, dtype=np.float64)
149
+ current_y = bottom_margin
150
+
151
+ for i in range(n_channels):
152
+ # Channels are indexed from bottom to top
153
+ y_positions[i] = current_y
154
+ current_y += channel_height
155
+ if i < n_channels - 1:
156
+ current_y += gap_height
157
+
158
+ return ChannelLayout(
159
+ n_channels=n_channels,
160
+ heights=heights,
161
+ gaps=gaps,
162
+ y_positions=y_positions,
163
+ shared_x=shared_x,
164
+ figsize=figsize,
165
+ )
166
+
167
+
168
+ def optimize_annotation_placement(
169
+ annotations: list[Annotation],
170
+ *,
171
+ display_width: float = 800.0,
172
+ display_height: float = 600.0,
173
+ max_iterations: int = 100,
174
+ repulsion_strength: float = 10.0,
175
+ min_spacing: float = 5.0,
176
+ ) -> list[PlacedAnnotation]:
177
+ """Optimize annotation placement with collision avoidance.
178
+
179
+ Uses force-directed layout algorithm to separate overlapping labels
180
+ with repulsive forces. Generates leader lines when labels must be
181
+ displaced from anchor points.
182
+
183
+ Args:
184
+ annotations: List of annotations to place.
185
+ display_width: Display area width in pixels.
186
+ display_height: Display area height in pixels.
187
+ max_iterations: Maximum iterations for force-directed layout.
188
+ repulsion_strength: Strength of repulsive force between overlapping labels.
189
+ min_spacing: Minimum spacing between annotations in pixels.
190
+
191
+ Returns:
192
+ List of PlacedAnnotation with optimized positions.
193
+
194
+ Raises:
195
+ ValueError: If annotations list is empty.
196
+
197
+ Example:
198
+ >>> annots = [Annotation("Peak", 0.5, 1.0, priority=0.9)]
199
+ >>> placed = optimize_annotation_placement(annots)
200
+ >>> print(f"Needs leader: {placed[0].needs_leader}")
201
+
202
+ References:
203
+ VIS-016: Annotation Placement Intelligence
204
+ Force-directed graph layout (Fruchterman-Reingold)
205
+ """
206
+ if len(annotations) == 0:
207
+ raise ValueError("annotations list cannot be empty")
208
+
209
+ # Convert annotations to display coordinates
210
+ # For now, assume data coordinates are normalized to display units
211
+ placed = []
212
+
213
+ for annot in annotations:
214
+ # Initial placement at anchor point
215
+ placed.append(
216
+ PlacedAnnotation(
217
+ annotation=annot,
218
+ display_x=annot.x,
219
+ display_y=annot.y,
220
+ needs_leader=False,
221
+ leader_points=None,
222
+ )
223
+ )
224
+
225
+ # Apply force-directed layout to resolve overlaps
226
+ for _iteration in range(max_iterations):
227
+ moved = False
228
+
229
+ # Calculate forces between all pairs
230
+ for i in range(len(placed)):
231
+ fx = 0.0
232
+ fy = 0.0
233
+
234
+ for j in range(len(placed)):
235
+ if i == j:
236
+ continue
237
+
238
+ # Check for bounding box overlap
239
+ dx = placed[j].display_x - placed[i].display_x
240
+ dy = placed[j].display_y - placed[i].display_y
241
+
242
+ # Bounding box sizes
243
+ w1 = placed[i].annotation.bbox_width
244
+ h1 = placed[i].annotation.bbox_height
245
+ w2 = placed[j].annotation.bbox_width
246
+ h2 = placed[j].annotation.bbox_height
247
+
248
+ # Minimum separation (sum of half-widths + spacing)
249
+ min_dx = (w1 + w2) / 2 + min_spacing
250
+ min_dy = (h1 + h2) / 2 + min_spacing
251
+
252
+ # Check if overlapping
253
+ if abs(dx) < min_dx and abs(dy) < min_dy:
254
+ # Calculate repulsive force
255
+ distance = np.sqrt(dx**2 + dy**2)
256
+ if distance < 1e-6:
257
+ # Avoid division by zero
258
+ distance = 1e-6
259
+ dx = np.random.randn() * 0.1
260
+ dy = np.random.randn() * 0.1
261
+
262
+ # Repulsion inversely proportional to distance
263
+ force = repulsion_strength / distance
264
+
265
+ # Apply force in direction away from overlap
266
+ fx -= force * dx / distance
267
+ fy -= force * dy / distance
268
+
269
+ # Apply forces with damping (priority affects inertia)
270
+ damping = 0.5
271
+ priority_factor = 1.0 - placed[i].annotation.priority
272
+
273
+ # Higher priority annotations move less
274
+ step_size = damping * priority_factor
275
+
276
+ new_x = placed[i].display_x + fx * step_size
277
+ new_y = placed[i].display_y + fy * step_size
278
+
279
+ # Clamp to display bounds
280
+ new_x = np.clip(new_x, 0, display_width)
281
+ new_y = np.clip(new_y, 0, display_height)
282
+
283
+ # Update if moved significantly
284
+ if abs(new_x - placed[i].display_x) > 0.1 or abs(new_y - placed[i].display_y) > 0.1:
285
+ placed[i] = PlacedAnnotation(
286
+ annotation=placed[i].annotation,
287
+ display_x=new_x,
288
+ display_y=new_y,
289
+ needs_leader=False,
290
+ leader_points=None,
291
+ )
292
+ moved = True
293
+
294
+ # Converged if nothing moved
295
+ if not moved:
296
+ break
297
+
298
+ # Determine which annotations need leader lines
299
+ # (displaced beyond threshold from original position)
300
+ leader_threshold = 20.0 # pixels
301
+
302
+ for i, p in enumerate(placed):
303
+ anchor_x = p.annotation.x
304
+ anchor_y = p.annotation.y
305
+
306
+ displacement = np.sqrt((p.display_x - anchor_x) ** 2 + (p.display_y - anchor_y) ** 2)
307
+
308
+ if displacement > leader_threshold:
309
+ # Generate simple orthogonal leader line
310
+ leader_points = _generate_leader_line(
311
+ (anchor_x, anchor_y),
312
+ (p.display_x, p.display_y),
313
+ )
314
+
315
+ placed[i] = PlacedAnnotation(
316
+ annotation=p.annotation,
317
+ display_x=p.display_x,
318
+ display_y=p.display_y,
319
+ needs_leader=True,
320
+ leader_points=leader_points,
321
+ )
322
+
323
+ return placed
324
+
325
+
326
+ def _generate_leader_line(
327
+ anchor: tuple[float, float],
328
+ label: tuple[float, float],
329
+ ) -> list[tuple[float, float]]:
330
+ """Generate orthogonal leader line from anchor to label.
331
+
332
+ Args:
333
+ anchor: Anchor point (x, y)
334
+ label: Label position (x, y)
335
+
336
+ Returns:
337
+ List of points for leader line
338
+ """
339
+ ax, ay = anchor
340
+ lx, ly = label
341
+
342
+ # Simple L-shaped leader: anchor -> midpoint -> label
343
+ # Choose horizontal-then-vertical or vertical-then-horizontal
344
+ # based on which dimension has larger displacement
345
+
346
+ dx = abs(lx - ax)
347
+ dy = abs(ly - ay)
348
+
349
+ if dx > dy:
350
+ # Horizontal-first
351
+ mid = (lx, ay)
352
+ else:
353
+ # Vertical-first
354
+ mid = (ax, ly)
355
+
356
+ return [anchor, mid, label]
357
+
358
+
359
+ __all__ = [
360
+ "Annotation",
361
+ "ChannelLayout",
362
+ "PlacedAnnotation",
363
+ "layout_stacked_channels",
364
+ "optimize_annotation_placement",
365
+ ]