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,223 @@
1
+ """Automated chart type selection for TraceKit reports.
2
+
3
+ This module provides intelligent chart type selection based on data
4
+ characteristics to optimize data visualization in reports.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.reporting import auto_select_chart
9
+ >>> chart_type = auto_select_chart("time_series", (1000, 2))
10
+ >>> print(chart_type) # "line"
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import TYPE_CHECKING, Literal
16
+
17
+ import numpy as np
18
+
19
+ if TYPE_CHECKING:
20
+ from numpy.typing import NDArray
21
+
22
+ ChartType = Literal["line", "scatter", "bar", "histogram", "heatmap", "pie", "spectrum"]
23
+
24
+
25
+ def auto_select_chart(
26
+ data_type: str,
27
+ data_shape: tuple[int, ...],
28
+ *,
29
+ data: NDArray[np.float64] | None = None,
30
+ ) -> ChartType:
31
+ """Automatically select appropriate chart type based on data characteristics.
32
+
33
+ Args:
34
+ data_type: Type of data - one of:
35
+ - "time_series": Time-domain waveform data
36
+ - "frequency": Frequency-domain spectral data
37
+ - "distribution": Statistical distribution data
38
+ - "comparison": Comparative measurements
39
+ - "correlation": Correlation or scatter data
40
+ - "categorical": Categorical comparison data
41
+ - "matrix": 2D matrix data
42
+ - "parts": Part-to-whole relationships
43
+ data_shape: Shape of the data array (rows, [columns]).
44
+ data: Optional actual data array for additional analysis.
45
+
46
+ Returns:
47
+ Recommended chart type: 'line', 'scatter', 'bar', 'histogram',
48
+ 'heatmap', 'pie', or 'spectrum'.
49
+
50
+ Example:
51
+ >>> # Time series data → line plot
52
+ >>> auto_select_chart("time_series", (1000, 2))
53
+ 'line'
54
+
55
+ >>> # Distribution data → histogram
56
+ >>> auto_select_chart("distribution", (500,))
57
+ 'histogram'
58
+
59
+ >>> # Categorical comparison → bar chart
60
+ >>> auto_select_chart("categorical", (5,))
61
+ 'bar'
62
+
63
+ >>> # 2D matrix → heatmap
64
+ >>> auto_select_chart("matrix", (100, 100))
65
+ 'heatmap'
66
+
67
+ References:
68
+ REPORT-028: Automated Chart Type Selection
69
+ """
70
+ # Time series → line plot
71
+ if data_type == "time_series":
72
+ return "line"
73
+
74
+ # Frequency data → spectrum plot (log scale)
75
+ if data_type == "frequency":
76
+ return "spectrum"
77
+
78
+ # Distribution → histogram or box plot
79
+ if data_type == "distribution":
80
+ return "histogram"
81
+
82
+ # Categorical comparison → bar chart
83
+ if data_type == "categorical":
84
+ # If very few categories, pie chart might be appropriate
85
+ if len(data_shape) > 0 and data_shape[0] <= 6 and data is not None and np.all(data >= 0):
86
+ # Check if data represents parts of a whole
87
+ total = np.sum(data)
88
+ if total > 0 and np.allclose(data / total * 100, data / total * 100):
89
+ return "pie"
90
+ return "bar"
91
+
92
+ # Comparison (continuous) → scatter plot
93
+ if data_type == "comparison":
94
+ # If 2D data with moderate size, scatter plot
95
+ if len(data_shape) >= 2 and data_shape[0] < 10000:
96
+ return "scatter"
97
+ # Large comparison data → bar chart
98
+ return "bar"
99
+
100
+ # Correlation → scatter plot with potential regression
101
+ if data_type == "correlation":
102
+ return "scatter"
103
+
104
+ # 2D matrix → heatmap
105
+ if data_type == "matrix":
106
+ return "heatmap"
107
+
108
+ # Parts-to-whole → pie chart
109
+ if data_type == "parts":
110
+ return "pie"
111
+
112
+ # Default based on shape
113
+ if len(data_shape) == 1:
114
+ # 1D data: histogram for distributions, bar for small sets
115
+ if data_shape[0] < 20:
116
+ return "bar"
117
+ return "histogram"
118
+ elif len(data_shape) == 2:
119
+ # 2D data: heatmap for square-ish matrices, scatter for point clouds
120
+ if data_shape[0] > 50 and data_shape[1] > 50:
121
+ return "heatmap"
122
+ return "scatter"
123
+
124
+ # Fallback to line plot
125
+ return "line"
126
+
127
+
128
+ def recommend_chart_with_reasoning(
129
+ data_type: str,
130
+ data_shape: tuple[int, ...],
131
+ *,
132
+ data: NDArray[np.float64] | None = None,
133
+ ) -> dict[str, str | ChartType]:
134
+ """Recommend chart type with reasoning explanation.
135
+
136
+ Args:
137
+ data_type: Type of data (see auto_select_chart).
138
+ data_shape: Shape of the data array.
139
+ data: Optional actual data array.
140
+
141
+ Returns:
142
+ Dictionary with 'chart_type' and 'reasoning' keys.
143
+
144
+ Example:
145
+ >>> result = recommend_chart_with_reasoning("time_series", (1000, 2))
146
+ >>> print(result['chart_type']) # "line"
147
+ >>> print(result['reasoning']) # "Time series data best shown with line plot"
148
+
149
+ References:
150
+ REPORT-028: Automated Chart Type Selection
151
+ """
152
+ chart_type = auto_select_chart(data_type, data_shape, data=data)
153
+
154
+ # Generate reasoning
155
+ reasoning_map = {
156
+ "line": "Time series or sequential data best visualized with line plot",
157
+ "scatter": "Point data or correlation best shown with scatter plot",
158
+ "bar": "Categorical or discrete comparison best shown with bar chart",
159
+ "histogram": "Distribution data best represented as histogram",
160
+ "heatmap": "2D matrix data best visualized as heatmap",
161
+ "pie": "Part-to-whole relationship best shown with pie chart",
162
+ "spectrum": "Frequency domain data best shown with log-scale spectrum plot",
163
+ }
164
+
165
+ reasoning = reasoning_map.get(
166
+ chart_type, f"Data characteristics suggest {chart_type} visualization"
167
+ )
168
+
169
+ return {
170
+ "chart_type": chart_type,
171
+ "reasoning": reasoning,
172
+ }
173
+
174
+
175
+ def get_axis_scaling(
176
+ data_type: str,
177
+ data: NDArray[np.float64] | None = None,
178
+ ) -> dict[str, str]:
179
+ """Recommend axis scaling (linear vs log) based on data type.
180
+
181
+ Args:
182
+ data_type: Type of data.
183
+ data: Optional actual data array for range analysis.
184
+
185
+ Returns:
186
+ Dictionary with 'x_scale' and 'y_scale' keys ('linear' or 'log').
187
+
188
+ Example:
189
+ >>> scaling = get_axis_scaling("frequency")
190
+ >>> print(scaling) # {'x_scale': 'log', 'y_scale': 'log'}
191
+
192
+ References:
193
+ REPORT-028: Automated Chart Type Selection
194
+ """
195
+ # Default linear scaling
196
+ x_scale = "linear"
197
+ y_scale = "linear"
198
+
199
+ # Frequency data: both axes log
200
+ if data_type == "frequency":
201
+ x_scale = "log"
202
+ y_scale = "log"
203
+
204
+ # Check data range if provided
205
+ if data is not None and len(data) > 0:
206
+ # If data spans > 3 orders of magnitude, use log scale
207
+ data_min = np.min(data[data > 0]) if np.any(data > 0) else 0
208
+ data_max = np.max(data)
209
+ if data_min > 0 and data_max / data_min > 1000:
210
+ y_scale = "log"
211
+
212
+ return {
213
+ "x_scale": x_scale,
214
+ "y_scale": y_scale,
215
+ }
216
+
217
+
218
+ __all__ = [
219
+ "ChartType",
220
+ "auto_select_chart",
221
+ "get_axis_scaling",
222
+ "recommend_chart_with_reasoning",
223
+ ]
@@ -0,0 +1,330 @@
1
+ """Comparison report generation for TraceKit.
2
+
3
+ This module provides utilities for comparing multiple traces or test runs
4
+ and generating comparison reports with diff visualization.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.reporting.comparison import generate_comparison_report
9
+ >>> report = generate_comparison_report(baseline, current, "comparison.pdf")
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from typing import Any, Literal
15
+
16
+ from oscura.reporting.core import Report, ReportConfig, Section
17
+ from oscura.reporting.tables import create_comparison_table
18
+
19
+
20
+ def generate_comparison_report(
21
+ baseline: dict[str, Any],
22
+ current: dict[str, Any],
23
+ *,
24
+ title: str = "Comparison Report",
25
+ mode: Literal["side_by_side", "inline"] = "side_by_side",
26
+ show_only_changes: bool = False,
27
+ highlight_changes: bool = False,
28
+ **kwargs: Any,
29
+ ) -> Report:
30
+ """Generate comparison report between baseline and current results.
31
+
32
+ Args:
33
+ baseline: Baseline results dictionary.
34
+ current: Current results dictionary.
35
+ title: Report title.
36
+ mode: Comparison mode (side_by_side or inline).
37
+ show_only_changes: Only show changed measurements.
38
+ highlight_changes: Highlight changes in output. Reserved for future use.
39
+ **kwargs: Additional report configuration options.
40
+
41
+ Returns:
42
+ Comparison Report object.
43
+
44
+ References:
45
+ REPORT-008
46
+ """
47
+ config = ReportConfig(title=title, **kwargs)
48
+ report = Report(config=config)
49
+
50
+ # Add summary
51
+ summary = _generate_comparison_summary(baseline, current)
52
+ report.add_section("Comparison Summary", summary, level=1)
53
+
54
+ # Add change details
55
+ changes_section = _create_changes_section(
56
+ baseline,
57
+ current,
58
+ show_only_changes=show_only_changes,
59
+ )
60
+ report.sections.append(changes_section)
61
+
62
+ # Add violations comparison
63
+ if "violations" in baseline or "violations" in current:
64
+ violations_section = _create_violations_comparison_section(
65
+ baseline,
66
+ current,
67
+ )
68
+ report.sections.append(violations_section)
69
+
70
+ # Add detailed comparison
71
+ if "measurements" in baseline or "measurements" in current:
72
+ detailed_section = _create_detailed_comparison_section(
73
+ baseline.get("measurements", {}),
74
+ current.get("measurements", {}),
75
+ mode=mode,
76
+ )
77
+ report.sections.append(detailed_section)
78
+
79
+ return report
80
+
81
+
82
+ def _generate_comparison_summary(
83
+ baseline: dict[str, Any],
84
+ current: dict[str, Any],
85
+ ) -> str:
86
+ """Generate comparison summary."""
87
+ summary_parts = []
88
+
89
+ # Count changes
90
+ baseline_meas = baseline.get("measurements", {})
91
+ current_meas = current.get("measurements", {})
92
+
93
+ all_params = set(baseline_meas.keys()) | set(current_meas.keys())
94
+ changed_params = []
95
+ improved_params = []
96
+ degraded_params = []
97
+
98
+ for param in all_params:
99
+ base_val = baseline_meas.get(param, {}).get("value")
100
+ curr_val = current_meas.get(param, {}).get("value")
101
+
102
+ if base_val is not None and curr_val is not None:
103
+ if abs(curr_val - base_val) / abs(base_val) > 0.05: # >5% change
104
+ changed_params.append(param)
105
+
106
+ # Determine if improved or degraded
107
+ base_passed = baseline_meas.get(param, {}).get("passed", True)
108
+ curr_passed = current_meas.get(param, {}).get("passed", True)
109
+
110
+ if not base_passed and curr_passed:
111
+ improved_params.append(param)
112
+ elif base_passed and not curr_passed:
113
+ degraded_params.append(param)
114
+
115
+ summary_parts.append(f"Comparing {len(all_params)} parameter(s) between baseline and current.")
116
+
117
+ if changed_params:
118
+ summary_parts.append(f"\n{len(changed_params)} measurement(s) changed significantly (>5%).")
119
+
120
+ if improved_params:
121
+ summary_parts.append(
122
+ f"\n✓ {len(improved_params)} parameter(s) improved (failures → passes)."
123
+ )
124
+
125
+ if degraded_params:
126
+ summary_parts.append(
127
+ f"\n✗ {len(degraded_params)} parameter(s) degraded (passes → failures)."
128
+ )
129
+
130
+ # Pass/fail comparison
131
+ baseline_pass = baseline.get("pass_count", 0)
132
+ baseline_total = baseline.get("total_count", 0)
133
+ current_pass = current.get("pass_count", 0)
134
+ current_total = current.get("total_count", 0)
135
+
136
+ if baseline_total > 0 and current_total > 0:
137
+ baseline_rate = baseline_pass / baseline_total * 100
138
+ current_rate = current_pass / current_total * 100
139
+ delta = current_rate - baseline_rate
140
+
141
+ summary_parts.append(
142
+ f"\nPass rate: {baseline_rate:.1f}% → {current_rate:.1f}% ({delta:+.1f}% change)"
143
+ )
144
+
145
+ return "\n".join(summary_parts)
146
+
147
+
148
+ def _create_changes_section(
149
+ baseline: dict[str, Any],
150
+ current: dict[str, Any],
151
+ *,
152
+ show_only_changes: bool = False,
153
+ ) -> Section:
154
+ """Create section detailing changes."""
155
+ baseline_meas = baseline.get("measurements", {})
156
+ current_meas = current.get("measurements", {})
157
+
158
+ # Create comparison table
159
+ table = create_comparison_table(
160
+ baseline_meas,
161
+ current_meas,
162
+ format="dict",
163
+ show_delta=True,
164
+ show_percent_change=True,
165
+ )
166
+
167
+ # Filter to only changes if requested
168
+ if show_only_changes:
169
+ filtered_rows = []
170
+ for row in table["data"]: # type: ignore[index]
171
+ # Check if delta is significant
172
+ if len(row) >= 4: # Has delta column
173
+ delta_str = str(row[3])
174
+ if delta_str not in {"-", "0"}:
175
+ filtered_rows.append(row)
176
+
177
+ table["data"] = filtered_rows # type: ignore[index]
178
+
179
+ return Section(
180
+ title="Measurement Changes",
181
+ content=[table],
182
+ level=1,
183
+ visible=True,
184
+ )
185
+
186
+
187
+ def _create_violations_comparison_section(
188
+ baseline: dict[str, Any],
189
+ current: dict[str, Any],
190
+ ) -> Section:
191
+ """Create section comparing violations."""
192
+ baseline_violations = {v.get("parameter") for v in baseline.get("violations", [])}
193
+ current_violations = {v.get("parameter") for v in current.get("violations", [])}
194
+
195
+ content_parts = []
196
+
197
+ # New violations
198
+ new_violations = current_violations - baseline_violations
199
+ if new_violations:
200
+ content_parts.append("**New Violations:**")
201
+ for param in sorted(new_violations):
202
+ content_parts.append(f"- {param}")
203
+
204
+ # Resolved violations
205
+ resolved_violations = baseline_violations - current_violations
206
+ if resolved_violations:
207
+ content_parts.append("\n**Resolved Violations:**")
208
+ for param in sorted(resolved_violations):
209
+ content_parts.append(f"- {param}")
210
+
211
+ # Persistent violations
212
+ persistent_violations = baseline_violations & current_violations
213
+ if persistent_violations:
214
+ content_parts.append("\n**Persistent Violations:**")
215
+ for param in sorted(persistent_violations):
216
+ content_parts.append(f"- {param}")
217
+
218
+ if not content_parts:
219
+ content_parts.append("No violations in either baseline or current.")
220
+
221
+ content = "\n".join(content_parts)
222
+
223
+ return Section(
224
+ title="Violations Comparison",
225
+ content=content,
226
+ level=1,
227
+ visible=True,
228
+ )
229
+
230
+
231
+ def _create_detailed_comparison_section(
232
+ baseline_meas: dict[str, Any],
233
+ current_meas: dict[str, Any],
234
+ *,
235
+ mode: str = "side_by_side",
236
+ ) -> Section:
237
+ """Create detailed measurement comparison section."""
238
+ from oscura.reporting.formatting import NumberFormatter
239
+
240
+ formatter = NumberFormatter()
241
+
242
+ content_parts = []
243
+
244
+ all_params = sorted(set(baseline_meas.keys()) | set(current_meas.keys()))
245
+
246
+ for param in all_params:
247
+ base = baseline_meas.get(param, {})
248
+ curr = current_meas.get(param, {})
249
+
250
+ base_val = base.get("value")
251
+ curr_val = curr.get("value")
252
+ unit = base.get("unit", curr.get("unit", ""))
253
+
254
+ if base_val is not None and curr_val is not None:
255
+ delta = curr_val - base_val
256
+ pct_change = (delta / base_val * 100) if base_val != 0 else 0
257
+
258
+ base_str = formatter.format(base_val, unit)
259
+ curr_str = formatter.format(curr_val, unit)
260
+ delta_str = formatter.format(delta, unit)
261
+
262
+ # Determine if improved/degraded
263
+ base_passed = base.get("passed", True)
264
+ curr_passed = curr.get("passed", True)
265
+
266
+ status = ""
267
+ if not base_passed and curr_passed:
268
+ status = " ✓ IMPROVED"
269
+ elif base_passed and not curr_passed:
270
+ status = " ✗ DEGRADED"
271
+
272
+ content_parts.append(
273
+ f"**{param}:** {base_str} → {curr_str} (Δ {delta_str}, {pct_change:+.1f}%){status}"
274
+ )
275
+
276
+ content = "\n\n".join(content_parts) if content_parts else "No measurements to compare."
277
+
278
+ return Section(
279
+ title="Detailed Comparison",
280
+ content=content,
281
+ level=1,
282
+ visible=True,
283
+ collapsible=True,
284
+ )
285
+
286
+
287
+ def compare_waveforms(
288
+ baseline_signal: dict[str, Any],
289
+ current_signal: dict[str, Any],
290
+ ) -> dict[str, Any]:
291
+ """Compare two waveforms and extract differences.
292
+
293
+ Args:
294
+ baseline_signal: Baseline waveform data.
295
+ current_signal: Current waveform data.
296
+
297
+ Returns:
298
+ Dictionary with comparison metrics.
299
+
300
+ References:
301
+ REPORT-008
302
+ """
303
+ import numpy as np
304
+
305
+ comparison = {
306
+ "correlation": None,
307
+ "rms_difference": None,
308
+ "max_difference": None,
309
+ "mean_difference": None,
310
+ }
311
+
312
+ base_data = baseline_signal.get("data")
313
+ curr_data = current_signal.get("data")
314
+
315
+ if base_data is not None and curr_data is not None:
316
+ # Ensure same length
317
+ min_len = min(len(base_data), len(curr_data))
318
+ base_data = base_data[:min_len]
319
+ curr_data = curr_data[:min_len]
320
+
321
+ # Correlation
322
+ comparison["correlation"] = np.corrcoef(base_data, curr_data)[0, 1]
323
+
324
+ # Differences
325
+ diff = curr_data - base_data
326
+ comparison["rms_difference"] = np.sqrt(np.mean(diff**2))
327
+ comparison["max_difference"] = np.max(np.abs(diff))
328
+ comparison["mean_difference"] = np.mean(diff)
329
+
330
+ return comparison