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,162 @@
1
+ """Verbosity level control for reports.
2
+
3
+ Configurable report detail level for different audiences with 5 distinct
4
+ verbosity levels from executive to debug.
5
+
6
+
7
+ References:
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from dataclasses import dataclass
13
+ from enum import Enum
14
+ from typing import TYPE_CHECKING
15
+
16
+ if TYPE_CHECKING:
17
+ from oscura.reporting.core import Report
18
+
19
+
20
+ class VerbosityLevel(Enum):
21
+ """Verbosity levels for report detail.
22
+
23
+ - EXECUTIVE: 1 page, pass/fail + key findings only
24
+ - SUMMARY: 2-5 pages, results + brief context, no raw data
25
+ - STANDARD: 5-20 pages, full results + methodology + plots
26
+ - DETAILED: 20-50 pages, all measurements + intermediate results
27
+ - DEBUG: 50+ pages, raw data + traces + full provenance
28
+
29
+ References:
30
+ REPORT-009: Verbosity Level Control
31
+ """
32
+
33
+ EXECUTIVE = "executive"
34
+ SUMMARY = "summary"
35
+ STANDARD = "standard"
36
+ DETAILED = "detailed"
37
+ DEBUG = "debug"
38
+
39
+
40
+ @dataclass
41
+ class VerbosityController:
42
+ """Verbosity level controller.
43
+
44
+ Attributes:
45
+ level: Current verbosity level.
46
+
47
+ References:
48
+ REPORT-009: Verbosity Level Control
49
+ """
50
+
51
+ level: VerbosityLevel = VerbosityLevel.STANDARD
52
+
53
+ def should_include_section(self, section_name: str) -> bool:
54
+ """Determine if section should be included at current verbosity.
55
+
56
+ Args:
57
+ section_name: Name of section to check.
58
+
59
+ Returns:
60
+ True if section should be included.
61
+
62
+ References:
63
+ REPORT-009: Verbosity Level Control
64
+ """
65
+ # Define sections for each level
66
+ sections_by_level = {
67
+ VerbosityLevel.EXECUTIVE: {"executive_summary", "key_findings"},
68
+ VerbosityLevel.SUMMARY: {"summary", "results", "key_plots"},
69
+ VerbosityLevel.STANDARD: {
70
+ "summary",
71
+ "results",
72
+ "methodology",
73
+ "plots",
74
+ "tables",
75
+ },
76
+ VerbosityLevel.DETAILED: {
77
+ "summary",
78
+ "results",
79
+ "methodology",
80
+ "plots",
81
+ "tables",
82
+ "measurements",
83
+ "intermediate_results",
84
+ },
85
+ VerbosityLevel.DEBUG: {
86
+ "summary",
87
+ "results",
88
+ "methodology",
89
+ "plots",
90
+ "tables",
91
+ "measurements",
92
+ "intermediate_results",
93
+ "raw_data",
94
+ "logs",
95
+ "provenance",
96
+ },
97
+ }
98
+
99
+ allowed_sections = sections_by_level.get(self.level, set())
100
+ return section_name in allowed_sections
101
+
102
+ def get_max_pages(self) -> int:
103
+ """Get maximum pages for current verbosity level.
104
+
105
+ Returns:
106
+ Maximum page count.
107
+
108
+ References:
109
+ REPORT-009: Verbosity Level Control
110
+ """
111
+ max_pages = {
112
+ VerbosityLevel.EXECUTIVE: 1,
113
+ VerbosityLevel.SUMMARY: 5,
114
+ VerbosityLevel.STANDARD: 20,
115
+ VerbosityLevel.DETAILED: 50,
116
+ VerbosityLevel.DEBUG: 999,
117
+ }
118
+ return max_pages.get(self.level, 20)
119
+
120
+
121
+ def apply_verbosity_level(
122
+ report: Report,
123
+ level: VerbosityLevel | str,
124
+ ) -> None:
125
+ """Apply verbosity level to report.
126
+
127
+ Args:
128
+ report: Report object to modify.
129
+ level: Verbosity level to apply.
130
+
131
+ Example:
132
+ >>> from oscura.reporting.core import Report, ReportConfig
133
+ >>> report = Report(config=ReportConfig())
134
+ >>> apply_verbosity_level(report, "summary")
135
+
136
+ References:
137
+ REPORT-009: Verbosity Level Control
138
+ """
139
+ if isinstance(level, str):
140
+ level = VerbosityLevel(level.lower())
141
+
142
+ controller = VerbosityController(level=level)
143
+
144
+ # Filter sections based on verbosity
145
+ if hasattr(report, "sections"):
146
+ visible_sections = []
147
+ for section in report.sections:
148
+ if controller.should_include_section(section.title.lower()):
149
+ visible_sections.append(section)
150
+
151
+ report.sections = visible_sections
152
+
153
+ # Update config
154
+ if hasattr(report, "config"):
155
+ report.config.verbosity = level.value
156
+
157
+
158
+ __all__ = [
159
+ "VerbosityController",
160
+ "VerbosityLevel",
161
+ "apply_verbosity_level",
162
+ ]
@@ -0,0 +1,508 @@
1
+ """Core report generation for TraceKit.
2
+
3
+ This module provides the main report generation functionality including
4
+ report structure, configuration, and output generation.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.reporting import generate_report
9
+ >>> report = generate_report(results, "report.pdf", verbosity="summary")
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from dataclasses import dataclass, field
15
+ from datetime import datetime
16
+ from pathlib import Path
17
+ from typing import TYPE_CHECKING, Any, Literal
18
+
19
+ if TYPE_CHECKING:
20
+ from numpy.typing import NDArray
21
+
22
+
23
+ @dataclass
24
+ class Section:
25
+ """A section in a report.
26
+
27
+ Attributes:
28
+ title: Section title.
29
+ content: Section content (text, tables, figures).
30
+ level: Heading level (1-4).
31
+ collapsible: Whether section is collapsible in HTML output.
32
+ visible: Whether section is visible in output.
33
+ """
34
+
35
+ title: str
36
+ content: str | list[Any] = ""
37
+ level: int = 2
38
+ collapsible: bool = False
39
+ visible: bool = True
40
+ subsections: list[Section] = field(default_factory=list)
41
+
42
+
43
+ @dataclass
44
+ class ReportConfig:
45
+ """Report generation configuration.
46
+
47
+ Attributes:
48
+ title: Report title.
49
+ author: Report author.
50
+ verbosity: Detail level (executive, summary, standard, detailed, debug).
51
+ format: Output format (pdf, html, markdown, docx).
52
+ template: Template name or path.
53
+ page_size: Page size (letter, A4).
54
+ margins: Page margins in inches.
55
+ logo_path: Path to logo image.
56
+ watermark: Watermark text.
57
+ show_toc: Include table of contents.
58
+ show_page_numbers: Include page numbers.
59
+ """
60
+
61
+ title: str = "TraceKit Analysis Report"
62
+ author: str = ""
63
+ verbosity: Literal["executive", "summary", "standard", "detailed", "debug"] = "standard"
64
+ format: Literal["pdf", "html", "markdown", "docx"] = "pdf"
65
+ template: str = "default"
66
+ page_size: Literal["letter", "A4"] = "letter"
67
+ margins: float = 1.0
68
+ logo_path: str | None = None
69
+ watermark: str | None = None
70
+ show_toc: bool = True
71
+ show_page_numbers: bool = True
72
+ created: datetime = field(default_factory=datetime.now)
73
+
74
+
75
+ @dataclass
76
+ class Report:
77
+ """A generated report.
78
+
79
+ Attributes:
80
+ config: Report configuration.
81
+ sections: Report sections.
82
+ metadata: Report metadata.
83
+ figures: Embedded figures.
84
+ tables: Embedded tables.
85
+ """
86
+
87
+ config: ReportConfig
88
+ sections: list[Section] = field(default_factory=list)
89
+ metadata: dict[str, Any] = field(default_factory=dict)
90
+ figures: list[Any] = field(default_factory=list)
91
+ tables: list[Any] = field(default_factory=list)
92
+
93
+ def add_section(
94
+ self,
95
+ title: str,
96
+ content: str | list[Any] = "",
97
+ level: int = 2,
98
+ **kwargs: Any,
99
+ ) -> Section:
100
+ """Add a section to the report.
101
+
102
+ Args:
103
+ title: Section title.
104
+ content: Section content.
105
+ level: Heading level.
106
+ **kwargs: Additional section options.
107
+
108
+ Returns:
109
+ The created Section.
110
+ """
111
+ section = Section(title=title, content=content, level=level, **kwargs)
112
+ self.sections.append(section)
113
+ return section
114
+
115
+ def add_table(
116
+ self,
117
+ data: list[list[Any]] | NDArray[Any],
118
+ headers: list[str] | None = None,
119
+ caption: str = "",
120
+ ) -> dict: # type: ignore[type-arg]
121
+ """Add a table to the report.
122
+
123
+ Args:
124
+ data: Table data as 2D list or array.
125
+ headers: Column headers.
126
+ caption: Table caption.
127
+
128
+ Returns:
129
+ Table reference dictionary.
130
+ """
131
+ table = {
132
+ "type": "table",
133
+ "data": data if isinstance(data, list) else data.tolist(),
134
+ "headers": headers,
135
+ "caption": caption,
136
+ "id": len(self.tables),
137
+ }
138
+ self.tables.append(table)
139
+ return table
140
+
141
+ def add_figure(
142
+ self,
143
+ figure: Any,
144
+ caption: str = "",
145
+ width: str = "100%",
146
+ ) -> dict: # type: ignore[type-arg]
147
+ """Add a figure to the report.
148
+
149
+ Args:
150
+ figure: Matplotlib figure or image path.
151
+ caption: Figure caption.
152
+ width: Figure width.
153
+
154
+ Returns:
155
+ Figure reference dictionary.
156
+ """
157
+ fig = {
158
+ "type": "figure",
159
+ "figure": figure,
160
+ "caption": caption,
161
+ "width": width,
162
+ "id": len(self.figures),
163
+ }
164
+ self.figures.append(fig)
165
+ return fig
166
+
167
+ def generate_executive_summary(
168
+ self,
169
+ results: dict[str, Any],
170
+ key_findings: list[str] | None = None,
171
+ ) -> str:
172
+ """Generate executive summary from results.
173
+
174
+ Args:
175
+ results: Analysis results dictionary.
176
+ key_findings: List of key findings to highlight.
177
+
178
+ Returns:
179
+ Executive summary text.
180
+ """
181
+ summary_parts = []
182
+
183
+ # Overall status
184
+ if "pass_count" in results and "total_count" in results:
185
+ pass_count = results["pass_count"]
186
+ total = results["total_count"]
187
+ if pass_count == total:
188
+ summary_parts.append(f"All {total} tests passed.")
189
+ else:
190
+ fail_count = total - pass_count
191
+ summary_parts.append(
192
+ f"{fail_count} of {total} tests failed ({fail_count / total * 100:.0f}%)."
193
+ )
194
+
195
+ # Key findings
196
+ if key_findings:
197
+ summary_parts.append("\nKey Findings:")
198
+ for finding in key_findings[:5]: # Top 5
199
+ summary_parts.append(f"- {finding}")
200
+
201
+ # Margin summary
202
+ if "min_margin" in results:
203
+ margin = results["min_margin"]
204
+ if margin < 10:
205
+ summary_parts.append(f"\nWarning: Minimum margin is {margin:.1f}%")
206
+ elif margin < 20:
207
+ summary_parts.append(f"\nNote: Minimum margin is {margin:.1f}%")
208
+
209
+ return "\n".join(summary_parts)
210
+
211
+ def to_markdown(self) -> str:
212
+ """Convert report to Markdown format.
213
+
214
+ Returns:
215
+ Markdown string.
216
+ """
217
+ lines = []
218
+
219
+ # Title
220
+ lines.append(f"# {self.config.title}")
221
+ lines.append("")
222
+
223
+ if self.config.author:
224
+ lines.append(f"**Author:** {self.config.author}")
225
+ lines.append(f"**Date:** {self.config.created.strftime('%Y-%m-%d %H:%M')}")
226
+ lines.append("")
227
+
228
+ # Sections
229
+ for section in self.sections:
230
+ if not section.visible:
231
+ continue
232
+
233
+ prefix = "#" * (section.level + 1)
234
+ lines.append(f"{prefix} {section.title}")
235
+ lines.append("")
236
+
237
+ if isinstance(section.content, str):
238
+ lines.append(section.content)
239
+ elif isinstance(section.content, list):
240
+ for item in section.content:
241
+ if isinstance(item, dict) and item.get("type") == "table":
242
+ lines.extend(self._table_to_markdown(item))
243
+ else:
244
+ lines.append(str(item))
245
+ lines.append("")
246
+
247
+ # Subsections
248
+ for subsec in section.subsections:
249
+ if not subsec.visible:
250
+ continue
251
+ prefix = "#" * (subsec.level + 1)
252
+ lines.append(f"{prefix} {subsec.title}")
253
+ lines.append("")
254
+ if isinstance(subsec.content, str):
255
+ lines.append(subsec.content)
256
+ lines.append("")
257
+
258
+ return "\n".join(lines)
259
+
260
+ def _table_to_markdown(self, table: dict) -> list[str]: # type: ignore[type-arg]
261
+ """Convert table to Markdown format."""
262
+ lines = []
263
+ headers = table.get("headers", [])
264
+ data = table.get("data", [])
265
+
266
+ if headers:
267
+ lines.append("| " + " | ".join(str(h) for h in headers) + " |")
268
+ lines.append("| " + " | ".join("---" for _ in headers) + " |")
269
+
270
+ for row in data:
271
+ lines.append("| " + " | ".join(str(cell) for cell in row) + " |")
272
+
273
+ if table.get("caption"):
274
+ lines.append("")
275
+ lines.append(f"*{table['caption']}*")
276
+
277
+ return lines
278
+
279
+ def save(self, path: str | Path) -> None:
280
+ """Save report to file.
281
+
282
+ Args:
283
+ path: Output file path.
284
+ """
285
+ path = Path(path)
286
+
287
+ if path.suffix == ".md":
288
+ content = self.to_markdown()
289
+ path.write_text(content)
290
+ elif path.suffix == ".html":
291
+ content = self.to_html()
292
+ path.write_text(content)
293
+ else:
294
+ # For PDF and other formats, use Markdown as intermediate
295
+ content = self.to_markdown()
296
+ path.with_suffix(".md").write_text(content)
297
+
298
+ def to_html(self) -> str:
299
+ """Convert report to HTML format.
300
+
301
+ Returns:
302
+ HTML string.
303
+ """
304
+ lines = [
305
+ "<!DOCTYPE html>",
306
+ "<html>",
307
+ "<head>",
308
+ f"<title>{self.config.title}</title>",
309
+ "<style>",
310
+ "body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }",
311
+ "h1 { color: #333; }",
312
+ "h2 { color: #555; border-bottom: 1px solid #ddd; }",
313
+ "table { border-collapse: collapse; width: 100%; margin: 10px 0; }",
314
+ "th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }",
315
+ "th { background-color: #f2f2f2; }",
316
+ "tr:nth-child(even) { background-color: #f9f9f9; }",
317
+ ".pass { color: green; }",
318
+ ".fail { color: red; }",
319
+ ".warning { color: orange; }",
320
+ "</style>",
321
+ "</head>",
322
+ "<body>",
323
+ f"<h1>{self.config.title}</h1>",
324
+ ]
325
+
326
+ if self.config.author:
327
+ lines.append(f"<p><strong>Author:</strong> {self.config.author}</p>")
328
+ lines.append(
329
+ f"<p><strong>Date:</strong> {self.config.created.strftime('%Y-%m-%d %H:%M')}</p>"
330
+ )
331
+
332
+ for section in self.sections:
333
+ if not section.visible:
334
+ continue
335
+
336
+ tag = f"h{min(section.level + 1, 6)}"
337
+ lines.append(f"<{tag}>{section.title}</{tag}>")
338
+
339
+ if isinstance(section.content, str):
340
+ lines.append(f"<p>{section.content}</p>")
341
+ elif isinstance(section.content, list):
342
+ for item in section.content:
343
+ if isinstance(item, dict) and item.get("type") == "table":
344
+ lines.extend(self._table_to_html(item))
345
+ else:
346
+ lines.append(f"<p>{item}</p>")
347
+
348
+ lines.extend(["</body>", "</html>"])
349
+ return "\n".join(lines)
350
+
351
+ def _table_to_html(self, table: dict) -> list[str]: # type: ignore[type-arg]
352
+ """Convert table to HTML format."""
353
+ lines = ["<table>"]
354
+ headers = table.get("headers", [])
355
+ data = table.get("data", [])
356
+
357
+ if headers:
358
+ lines.append("<thead><tr>")
359
+ for h in headers:
360
+ lines.append(f"<th>{h}</th>")
361
+ lines.append("</tr></thead>")
362
+
363
+ lines.append("<tbody>")
364
+ for row in data:
365
+ lines.append("<tr>")
366
+ for cell in row:
367
+ lines.append(f"<td>{cell}</td>")
368
+ lines.append("</tr>")
369
+ lines.append("</tbody>")
370
+
371
+ lines.append("</table>")
372
+
373
+ if table.get("caption"):
374
+ lines.append(f"<p><em>{table['caption']}</em></p>")
375
+
376
+ return lines
377
+
378
+
379
+ def generate_report(
380
+ results: dict[str, Any],
381
+ output_path: str | Path | None = None,
382
+ *,
383
+ title: str = "TraceKit Analysis Report",
384
+ verbosity: Literal["executive", "summary", "standard", "detailed", "debug"] = ("standard"),
385
+ template: str = "default",
386
+ formats: list[str] | None = None,
387
+ **kwargs: Any,
388
+ ) -> Report:
389
+ """Generate a report from analysis results.
390
+
391
+ Creates a formatted report from analysis results with configurable
392
+ verbosity and output formats.
393
+
394
+ Args:
395
+ results: Analysis results dictionary.
396
+ output_path: Output file path (optional).
397
+ title: Report title.
398
+ verbosity: Detail level.
399
+ template: Template name.
400
+ formats: Output formats (pdf, html, markdown).
401
+ **kwargs: Additional configuration options.
402
+
403
+ Returns:
404
+ Generated Report object.
405
+
406
+ Example:
407
+ >>> report = generate_report(results, "report.pdf", verbosity="summary")
408
+ """
409
+ config = ReportConfig(
410
+ title=title,
411
+ verbosity=verbosity,
412
+ template=template,
413
+ **{k: v for k, v in kwargs.items() if hasattr(ReportConfig, k)},
414
+ )
415
+
416
+ report = Report(config=config, metadata={"source": "TraceKit"})
417
+
418
+ # Add executive summary
419
+ if verbosity in ("executive", "summary", "standard", "detailed", "debug"):
420
+ summary = report.generate_executive_summary(results)
421
+ report.add_section("Executive Summary", summary, level=1)
422
+
423
+ # Add results section
424
+ if verbosity in ("summary", "standard", "detailed", "debug"):
425
+ _add_results_section(report, results, verbosity)
426
+
427
+ # Add methodology section
428
+ if verbosity in ("standard", "detailed", "debug"):
429
+ _add_methodology_section(report, results)
430
+
431
+ # Add raw data section
432
+ if verbosity in ("detailed", "debug"):
433
+ _add_raw_data_section(report, results)
434
+
435
+ # Save if output path provided
436
+ if output_path:
437
+ output_path = Path(output_path)
438
+ if formats:
439
+ for fmt in formats:
440
+ path = output_path.with_suffix(f".{fmt}")
441
+ report.save(path)
442
+ else:
443
+ report.save(output_path)
444
+
445
+ return report
446
+
447
+
448
+ def _add_results_section(
449
+ report: Report,
450
+ results: dict[str, Any],
451
+ verbosity: str,
452
+ ) -> None:
453
+ """Add results section to report."""
454
+ report.add_section("Test Results", level=1)
455
+
456
+ # Create results table
457
+ if "measurements" in results:
458
+ measurements = results["measurements"]
459
+ headers = ["Parameter", "Value", "Specification", "Status"]
460
+ data = []
461
+
462
+ for name, meas in measurements.items():
463
+ value = meas.get("value", "N/A")
464
+ spec = meas.get("specification", "N/A")
465
+ status = "PASS" if meas.get("passed", True) else "FAIL"
466
+ data.append([name, value, spec, status])
467
+
468
+ report.add_table(data, headers, "Measurement Results")
469
+
470
+
471
+ def _add_methodology_section(
472
+ report: Report,
473
+ results: dict[str, Any],
474
+ ) -> None:
475
+ """Add methodology section to report."""
476
+ content = []
477
+
478
+ if "sample_rate" in results:
479
+ content.append(f"Sample rate: {results['sample_rate']} Hz")
480
+ if "num_samples" in results:
481
+ content.append(f"Number of samples: {results['num_samples']}")
482
+ if "analysis_time" in results:
483
+ content.append(f"Analysis time: {results['analysis_time']:.3f} seconds")
484
+
485
+ report.add_section(
486
+ "Methodology",
487
+ "\n".join(content) if content else "Standard analysis methodology applied.",
488
+ level=1,
489
+ )
490
+
491
+
492
+ def _add_raw_data_section(
493
+ report: Report,
494
+ results: dict[str, Any],
495
+ ) -> None:
496
+ """Add raw data section to report."""
497
+ content = []
498
+
499
+ for key, value in results.items():
500
+ if isinstance(value, int | float | str):
501
+ content.append(f"{key}: {value}")
502
+
503
+ report.add_section(
504
+ "Raw Data",
505
+ "\n".join(content) if content else "No raw data available.",
506
+ level=1,
507
+ collapsible=True,
508
+ )
@@ -0,0 +1,17 @@
1
+ """Core report generation functionality - Multi-format renderers.
2
+
3
+ Note: Renamed from 'core' to 'core_formats' to avoid naming conflict
4
+ with core.py module which contains Report, ReportConfig, Section.
5
+ """
6
+
7
+ from oscura.reporting.core_formats.multi_format import (
8
+ MultiFormatRenderer,
9
+ detect_format_from_extension,
10
+ render_all_formats,
11
+ )
12
+
13
+ __all__ = [
14
+ "MultiFormatRenderer",
15
+ "detect_format_from_extension",
16
+ "render_all_formats",
17
+ ]