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,453 @@
1
+ """Color palette selection and accessibility utilities.
2
+
3
+ This module provides intelligent color palette selection based on data
4
+ characteristics and accessibility requirements with WCAG contrast checking.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.visualization.colors import select_optimal_palette
9
+ >>> colors = select_optimal_palette(n_channels=3, palette_type="qualitative")
10
+
11
+ References:
12
+ WCAG 2.1 contrast guidelines
13
+ Colorblind-safe palette design (Brettel 1997)
14
+ ColorBrewer schemes
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from typing import Literal
20
+
21
+ import numpy as np
22
+
23
+ # Predefined colorblind-safe palettes
24
+ COLORBLIND_SAFE_QUALITATIVE = [
25
+ "#0173B2", # Blue
26
+ "#DE8F05", # Orange
27
+ "#029E73", # Green
28
+ "#CC78BC", # Purple
29
+ "#CA9161", # Brown
30
+ "#949494", # Gray
31
+ "#ECE133", # Yellow
32
+ "#56B4E9", # Light blue
33
+ ]
34
+
35
+ SEQUENTIAL_VIRIDIS = [
36
+ "#440154",
37
+ "#481567",
38
+ "#482677",
39
+ "#453781",
40
+ "#404788",
41
+ "#39568C",
42
+ "#33638D",
43
+ "#2D708E",
44
+ "#287D8E",
45
+ "#238A8D",
46
+ "#1F968B",
47
+ "#20A387",
48
+ "#29AF7F",
49
+ "#3CBB75",
50
+ "#55C667",
51
+ "#73D055",
52
+ "#95D840",
53
+ "#B8DE29",
54
+ "#DCE319",
55
+ "#FDE724",
56
+ ]
57
+
58
+ DIVERGING_COOLWARM = [
59
+ "#3B4CC0",
60
+ "#5977E3",
61
+ "#7D9EF2",
62
+ "#A2C0F9",
63
+ "#C7DDFA",
64
+ "#E8F0FC",
65
+ "#F9EBE5",
66
+ "#F6CFBB",
67
+ "#F0AD8E",
68
+ "#E68462",
69
+ "#D8583E",
70
+ "#C52A1E",
71
+ "#B40426",
72
+ ]
73
+
74
+
75
+ def select_optimal_palette(
76
+ n_colors: int,
77
+ *,
78
+ palette_type: Literal["sequential", "diverging", "qualitative"] | None = None,
79
+ data_range: tuple[float, float] | None = None,
80
+ colorblind_safe: bool = True,
81
+ background_color: str = "#FFFFFF",
82
+ min_contrast_ratio: float = 4.5,
83
+ ) -> list[str]:
84
+ """Select optimal color palette based on data characteristics.
85
+
86
+ : Automatically select optimal color palettes based on
87
+ data characteristics, plot type, and accessibility requirements.
88
+
89
+ Args:
90
+ n_colors: Number of colors needed
91
+ palette_type: Type of palette ("sequential", "diverging", "qualitative")
92
+ If None, auto-select based on n_colors and data_range
93
+ data_range: Data range (min, max) for auto-detecting bipolar signals
94
+ colorblind_safe: Ensure colorblind-safe palette (default: True)
95
+ background_color: Background color for contrast checking (default: white)
96
+ min_contrast_ratio: Minimum WCAG contrast ratio (default: 4.5 for AA)
97
+
98
+ Returns:
99
+ List of color hex codes
100
+
101
+ Raises:
102
+ ValueError: If n_colors is invalid or palette cannot meet requirements
103
+
104
+ Example:
105
+ >>> # Auto-select for 3 channels
106
+ >>> colors = select_optimal_palette(3)
107
+ >>> # Diverging palette for bipolar data
108
+ >>> colors = select_optimal_palette(10, palette_type="diverging")
109
+
110
+ References:
111
+ VIS-023: Data-Driven Color Palette
112
+ WCAG 2.1 contrast ratio guidelines (AA: 4.5:1, AAA: 7:1)
113
+ ColorBrewer sequential/diverging schemes
114
+ """
115
+ if n_colors < 1:
116
+ raise ValueError("n_colors must be >= 1")
117
+ if min_contrast_ratio < 1.0:
118
+ raise ValueError("min_contrast_ratio must be >= 1.0")
119
+
120
+ # Auto-select palette type if not specified
121
+ if palette_type is None:
122
+ palette_type = _auto_select_palette_type(n_colors, data_range)
123
+
124
+ # Select base palette
125
+ if palette_type == "qualitative":
126
+ base_colors = (
127
+ COLORBLIND_SAFE_QUALITATIVE if colorblind_safe else _generate_qualitative(n_colors)
128
+ )
129
+ elif palette_type == "sequential":
130
+ base_colors = SEQUENTIAL_VIRIDIS
131
+ elif palette_type == "diverging":
132
+ base_colors = DIVERGING_COOLWARM
133
+ else:
134
+ raise ValueError(f"Unknown palette_type: {palette_type}")
135
+
136
+ # Sample colors if we need fewer than available
137
+ if n_colors <= len(base_colors):
138
+ # Evenly sample from palette
139
+ indices = np.linspace(0, len(base_colors) - 1, n_colors).astype(int)
140
+ colors = [base_colors[i] for i in indices]
141
+ else:
142
+ # Interpolate if we need more colors
143
+ colors = _interpolate_colors(base_colors, n_colors)
144
+
145
+ # Check contrast ratios
146
+ colors_with_contrast = []
147
+ bg_luminance = _relative_luminance(background_color)
148
+
149
+ for color in colors:
150
+ color_luminance = _relative_luminance(color)
151
+ contrast = _contrast_ratio(color_luminance, bg_luminance)
152
+
153
+ if contrast >= min_contrast_ratio:
154
+ colors_with_contrast.append(color)
155
+ else:
156
+ # Adjust lightness to meet contrast requirement
157
+ adjusted = _adjust_for_contrast(color, background_color, min_contrast_ratio)
158
+ colors_with_contrast.append(adjusted)
159
+
160
+ return colors_with_contrast
161
+
162
+
163
+ def _auto_select_palette_type(
164
+ n_colors: int,
165
+ data_range: tuple[float, float] | None,
166
+ ) -> Literal["sequential", "diverging", "qualitative"]:
167
+ """Auto-select palette type based on data characteristics.
168
+
169
+ Args:
170
+ n_colors: Number of colors needed
171
+ data_range: Data range (min, max)
172
+
173
+ Returns:
174
+ Palette type
175
+ """
176
+ # Check for bipolar data (zero-crossing)
177
+ if data_range is not None:
178
+ min_val, max_val = data_range
179
+ if min_val < 0 and max_val > 0:
180
+ # Bipolar signal - use diverging
181
+ return "diverging"
182
+
183
+ # Multi-channel (distinct categories)
184
+ if n_colors <= 8:
185
+ return "qualitative"
186
+
187
+ # Many colors or continuous data
188
+ return "sequential"
189
+
190
+
191
+ def _relative_luminance(color: str) -> float:
192
+ """Calculate relative luminance per WCAG 2.1.
193
+
194
+ Args:
195
+ color: Hex color code
196
+
197
+ Returns:
198
+ Relative luminance (0-1)
199
+ """
200
+ # Parse hex color
201
+ color = color.removeprefix("#")
202
+
203
+ r = int(color[0:2], 16) / 255.0
204
+ g = int(color[2:4], 16) / 255.0
205
+ b = int(color[4:6], 16) / 255.0
206
+
207
+ # Convert to linear RGB
208
+ def to_linear(c: float) -> float:
209
+ if c <= 0.03928:
210
+ return c / 12.92
211
+ else:
212
+ return ((c + 0.055) / 1.055) ** 2.4 # type: ignore[no-any-return]
213
+
214
+ r_linear = to_linear(r)
215
+ g_linear = to_linear(g)
216
+ b_linear = to_linear(b)
217
+
218
+ # Calculate luminance
219
+ return 0.2126 * r_linear + 0.7152 * g_linear + 0.0722 * b_linear
220
+
221
+
222
+ def _contrast_ratio(lum1: float, lum2: float) -> float:
223
+ """Calculate WCAG contrast ratio between two luminances.
224
+
225
+ Args:
226
+ lum1: First luminance (0-1)
227
+ lum2: Second luminance (0-1)
228
+
229
+ Returns:
230
+ Contrast ratio (1-21)
231
+ """
232
+ lighter = max(lum1, lum2)
233
+ darker = min(lum1, lum2)
234
+
235
+ return (lighter + 0.05) / (darker + 0.05)
236
+
237
+
238
+ def _adjust_for_contrast(
239
+ color: str,
240
+ background: str,
241
+ target_ratio: float,
242
+ ) -> str:
243
+ """Adjust color lightness to meet contrast requirement.
244
+
245
+ Args:
246
+ color: Color to adjust
247
+ background: Background color
248
+ target_ratio: Target contrast ratio
249
+
250
+ Returns:
251
+ Adjusted color hex code
252
+ """
253
+ # Parse color
254
+ color_val = color.removeprefix("#")
255
+
256
+ r = int(color_val[0:2], 16)
257
+ g = int(color_val[2:4], 16)
258
+ b = int(color_val[4:6], 16)
259
+
260
+ # Convert to HSL for easier lightness adjustment
261
+ h, s, l = _rgb_to_hsl(r, g, b) # noqa: E741
262
+
263
+ bg_lum = _relative_luminance(background)
264
+
265
+ # Binary search for appropriate lightness
266
+ l_min, l_max = 0.0, 1.0
267
+ iterations = 0
268
+ max_iterations = 20
269
+
270
+ while iterations < max_iterations:
271
+ # Try current lightness
272
+ test_r, test_g, test_b = _hsl_to_rgb(h, s, l)
273
+ test_color = f"#{test_r:02x}{test_g:02x}{test_b:02x}"
274
+ test_lum = _relative_luminance(test_color)
275
+ ratio = _contrast_ratio(test_lum, bg_lum)
276
+
277
+ if abs(ratio - target_ratio) < 0.1:
278
+ break
279
+
280
+ if ratio < target_ratio:
281
+ # Need more contrast - adjust lightness
282
+ if bg_lum > 0.5:
283
+ # Dark background - make lighter
284
+ l_min = l
285
+ l = (l + l_max) / 2 # noqa: E741
286
+ else:
287
+ # Light background - make darker
288
+ l_max = l
289
+ l = (l_min + l) / 2 # noqa: E741
290
+ # Too much contrast - move back
291
+ elif bg_lum > 0.5:
292
+ l_max = l
293
+ l = (l_min + l) / 2 # noqa: E741
294
+ else:
295
+ l_min = l
296
+ l = (l + l_max) / 2 # noqa: E741
297
+
298
+ iterations += 1
299
+
300
+ final_r, final_g, final_b = _hsl_to_rgb(h, s, l)
301
+ return f"#{final_r:02x}{final_g:02x}{final_b:02x}"
302
+
303
+
304
+ def _rgb_to_hsl(r: int, g: int, b: int) -> tuple[float, float, float]:
305
+ """Convert RGB to HSL color space.
306
+
307
+ Args:
308
+ r: Red value (0-255).
309
+ g: Green value (0-255).
310
+ b: Blue value (0-255).
311
+
312
+ Returns:
313
+ (h, s, l) tuple where h in [0, 360), s and l in [0, 1]
314
+ """
315
+ r_norm = r / 255.0
316
+ g_norm = g / 255.0
317
+ b_norm = b / 255.0
318
+
319
+ max_c = max(r_norm, g_norm, b_norm)
320
+ min_c = min(r_norm, g_norm, b_norm)
321
+ delta = max_c - min_c
322
+
323
+ # Lightness
324
+ l = (max_c + min_c) / 2.0 # noqa: E741
325
+
326
+ if delta == 0:
327
+ # Achromatic
328
+ return (0.0, 0.0, l)
329
+
330
+ # Saturation
331
+ s = delta / (max_c + min_c) if l < 0.5 else delta / (2.0 - max_c - min_c)
332
+
333
+ # Hue
334
+ if max_c == r_norm:
335
+ h = ((g_norm - b_norm) / delta) % 6
336
+ elif max_c == g_norm:
337
+ h = ((b_norm - r_norm) / delta) + 2
338
+ else:
339
+ h = ((r_norm - g_norm) / delta) + 4
340
+
341
+ h = h * 60.0
342
+
343
+ return (h, s, l)
344
+
345
+
346
+ def _hsl_to_rgb(h: float, s: float, l: float) -> tuple[int, int, int]: # noqa: E741
347
+ """Convert HSL to RGB color space.
348
+
349
+ Args:
350
+ h: Hue in [0, 360)
351
+ s: Saturation in [0, 1]
352
+ l: Lightness in [0, 1]
353
+
354
+ Returns:
355
+ (r, g, b) tuple with values in [0, 255]
356
+ """
357
+ if s == 0:
358
+ # Achromatic
359
+ gray = int(l * 255)
360
+ return (gray, gray, gray)
361
+
362
+ def hue_to_rgb(p: float, q: float, t: float) -> float:
363
+ if t < 0:
364
+ t += 1
365
+ if t > 1:
366
+ t -= 1
367
+ if t < 1 / 6:
368
+ return p + (q - p) * 6 * t
369
+ if t < 1 / 2:
370
+ return q
371
+ if t < 2 / 3:
372
+ return p + (q - p) * (2 / 3 - t) * 6
373
+ return p
374
+
375
+ q = l * (1 + s) if l < 0.5 else l + s - l * s
376
+
377
+ p = 2 * l - q
378
+
379
+ h_norm = h / 360.0
380
+
381
+ r = hue_to_rgb(p, q, h_norm + 1 / 3)
382
+ g = hue_to_rgb(p, q, h_norm)
383
+ b = hue_to_rgb(p, q, h_norm - 1 / 3)
384
+
385
+ return (int(r * 255), int(g * 255), int(b * 255))
386
+
387
+
388
+ def _generate_qualitative(n_colors: int) -> list[str]:
389
+ """Generate qualitative color palette.
390
+
391
+ Args:
392
+ n_colors: Number of colors
393
+
394
+ Returns:
395
+ List of hex color codes
396
+ """
397
+ # Generate evenly spaced hues
398
+ colors = []
399
+ for i in range(n_colors):
400
+ hue = (i * 360.0 / n_colors) % 360
401
+ r, g, b = _hsl_to_rgb(hue, 0.7, 0.5)
402
+ colors.append(f"#{r:02x}{g:02x}{b:02x}")
403
+
404
+ return colors
405
+
406
+
407
+ def _interpolate_colors(base_colors: list[str], n_colors: int) -> list[str]:
408
+ """Interpolate between base colors to generate more colors.
409
+
410
+ Args:
411
+ base_colors: Base color palette
412
+ n_colors: Target number of colors
413
+
414
+ Returns:
415
+ List of interpolated hex color codes
416
+ """
417
+ if n_colors <= len(base_colors):
418
+ return base_colors[:n_colors]
419
+
420
+ # Convert to RGB arrays
421
+ rgb_array = np.zeros((len(base_colors), 3))
422
+ for i, color in enumerate(base_colors):
423
+ color = color.removeprefix("#")
424
+ rgb_array[i, 0] = int(color[0:2], 16)
425
+ rgb_array[i, 1] = int(color[2:4], 16)
426
+ rgb_array[i, 2] = int(color[4:6], 16)
427
+
428
+ # Interpolate
429
+ indices = np.linspace(0, len(base_colors) - 1, n_colors)
430
+ interp_rgb = np.zeros((n_colors, 3))
431
+
432
+ for channel in range(3):
433
+ interp_rgb[:, channel] = np.interp(
434
+ indices, np.arange(len(base_colors)), rgb_array[:, channel]
435
+ )
436
+
437
+ # Convert back to hex
438
+ colors = []
439
+ for i in range(n_colors):
440
+ r = int(interp_rgb[i, 0])
441
+ g = int(interp_rgb[i, 1])
442
+ b = int(interp_rgb[i, 2])
443
+ colors.append(f"#{r:02x}{g:02x}{b:02x}")
444
+
445
+ return colors
446
+
447
+
448
+ __all__ = [
449
+ "COLORBLIND_SAFE_QUALITATIVE",
450
+ "DIVERGING_COOLWARM",
451
+ "SEQUENTIAL_VIRIDIS",
452
+ "select_optimal_palette",
453
+ ]