oscura 0.0.1__py3-none-any.whl → 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (465) hide show
  1. oscura/__init__.py +813 -8
  2. oscura/__main__.py +392 -0
  3. oscura/analyzers/__init__.py +37 -0
  4. oscura/analyzers/digital/__init__.py +177 -0
  5. oscura/analyzers/digital/bus.py +691 -0
  6. oscura/analyzers/digital/clock.py +805 -0
  7. oscura/analyzers/digital/correlation.py +720 -0
  8. oscura/analyzers/digital/edges.py +632 -0
  9. oscura/analyzers/digital/extraction.py +413 -0
  10. oscura/analyzers/digital/quality.py +878 -0
  11. oscura/analyzers/digital/signal_quality.py +877 -0
  12. oscura/analyzers/digital/thresholds.py +708 -0
  13. oscura/analyzers/digital/timing.py +1104 -0
  14. oscura/analyzers/eye/__init__.py +46 -0
  15. oscura/analyzers/eye/diagram.py +434 -0
  16. oscura/analyzers/eye/metrics.py +555 -0
  17. oscura/analyzers/jitter/__init__.py +83 -0
  18. oscura/analyzers/jitter/ber.py +333 -0
  19. oscura/analyzers/jitter/decomposition.py +759 -0
  20. oscura/analyzers/jitter/measurements.py +413 -0
  21. oscura/analyzers/jitter/spectrum.py +220 -0
  22. oscura/analyzers/measurements.py +40 -0
  23. oscura/analyzers/packet/__init__.py +171 -0
  24. oscura/analyzers/packet/daq.py +1077 -0
  25. oscura/analyzers/packet/metrics.py +437 -0
  26. oscura/analyzers/packet/parser.py +327 -0
  27. oscura/analyzers/packet/payload.py +2156 -0
  28. oscura/analyzers/packet/payload_analysis.py +1312 -0
  29. oscura/analyzers/packet/payload_extraction.py +236 -0
  30. oscura/analyzers/packet/payload_patterns.py +670 -0
  31. oscura/analyzers/packet/stream.py +359 -0
  32. oscura/analyzers/patterns/__init__.py +266 -0
  33. oscura/analyzers/patterns/clustering.py +1036 -0
  34. oscura/analyzers/patterns/discovery.py +539 -0
  35. oscura/analyzers/patterns/learning.py +797 -0
  36. oscura/analyzers/patterns/matching.py +1091 -0
  37. oscura/analyzers/patterns/periodic.py +650 -0
  38. oscura/analyzers/patterns/sequences.py +767 -0
  39. oscura/analyzers/power/__init__.py +116 -0
  40. oscura/analyzers/power/ac_power.py +391 -0
  41. oscura/analyzers/power/basic.py +383 -0
  42. oscura/analyzers/power/conduction.py +314 -0
  43. oscura/analyzers/power/efficiency.py +297 -0
  44. oscura/analyzers/power/ripple.py +356 -0
  45. oscura/analyzers/power/soa.py +372 -0
  46. oscura/analyzers/power/switching.py +479 -0
  47. oscura/analyzers/protocol/__init__.py +150 -0
  48. oscura/analyzers/protocols/__init__.py +150 -0
  49. oscura/analyzers/protocols/base.py +500 -0
  50. oscura/analyzers/protocols/can.py +620 -0
  51. oscura/analyzers/protocols/can_fd.py +448 -0
  52. oscura/analyzers/protocols/flexray.py +405 -0
  53. oscura/analyzers/protocols/hdlc.py +399 -0
  54. oscura/analyzers/protocols/i2c.py +368 -0
  55. oscura/analyzers/protocols/i2s.py +296 -0
  56. oscura/analyzers/protocols/jtag.py +393 -0
  57. oscura/analyzers/protocols/lin.py +445 -0
  58. oscura/analyzers/protocols/manchester.py +333 -0
  59. oscura/analyzers/protocols/onewire.py +501 -0
  60. oscura/analyzers/protocols/spi.py +334 -0
  61. oscura/analyzers/protocols/swd.py +325 -0
  62. oscura/analyzers/protocols/uart.py +393 -0
  63. oscura/analyzers/protocols/usb.py +495 -0
  64. oscura/analyzers/signal_integrity/__init__.py +63 -0
  65. oscura/analyzers/signal_integrity/embedding.py +294 -0
  66. oscura/analyzers/signal_integrity/equalization.py +370 -0
  67. oscura/analyzers/signal_integrity/sparams.py +484 -0
  68. oscura/analyzers/spectral/__init__.py +53 -0
  69. oscura/analyzers/spectral/chunked.py +273 -0
  70. oscura/analyzers/spectral/chunked_fft.py +571 -0
  71. oscura/analyzers/spectral/chunked_wavelet.py +391 -0
  72. oscura/analyzers/spectral/fft.py +92 -0
  73. oscura/analyzers/statistical/__init__.py +250 -0
  74. oscura/analyzers/statistical/checksum.py +923 -0
  75. oscura/analyzers/statistical/chunked_corr.py +228 -0
  76. oscura/analyzers/statistical/classification.py +778 -0
  77. oscura/analyzers/statistical/entropy.py +1113 -0
  78. oscura/analyzers/statistical/ngrams.py +614 -0
  79. oscura/analyzers/statistics/__init__.py +119 -0
  80. oscura/analyzers/statistics/advanced.py +885 -0
  81. oscura/analyzers/statistics/basic.py +263 -0
  82. oscura/analyzers/statistics/correlation.py +630 -0
  83. oscura/analyzers/statistics/distribution.py +298 -0
  84. oscura/analyzers/statistics/outliers.py +463 -0
  85. oscura/analyzers/statistics/streaming.py +93 -0
  86. oscura/analyzers/statistics/trend.py +520 -0
  87. oscura/analyzers/validation.py +598 -0
  88. oscura/analyzers/waveform/__init__.py +36 -0
  89. oscura/analyzers/waveform/measurements.py +943 -0
  90. oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
  91. oscura/analyzers/waveform/spectral.py +1689 -0
  92. oscura/analyzers/waveform/wavelets.py +298 -0
  93. oscura/api/__init__.py +62 -0
  94. oscura/api/dsl.py +538 -0
  95. oscura/api/fluent.py +571 -0
  96. oscura/api/operators.py +498 -0
  97. oscura/api/optimization.py +392 -0
  98. oscura/api/profiling.py +396 -0
  99. oscura/automotive/__init__.py +73 -0
  100. oscura/automotive/can/__init__.py +52 -0
  101. oscura/automotive/can/analysis.py +356 -0
  102. oscura/automotive/can/checksum.py +250 -0
  103. oscura/automotive/can/correlation.py +212 -0
  104. oscura/automotive/can/discovery.py +355 -0
  105. oscura/automotive/can/message_wrapper.py +375 -0
  106. oscura/automotive/can/models.py +385 -0
  107. oscura/automotive/can/patterns.py +381 -0
  108. oscura/automotive/can/session.py +452 -0
  109. oscura/automotive/can/state_machine.py +300 -0
  110. oscura/automotive/can/stimulus_response.py +461 -0
  111. oscura/automotive/dbc/__init__.py +15 -0
  112. oscura/automotive/dbc/generator.py +156 -0
  113. oscura/automotive/dbc/parser.py +146 -0
  114. oscura/automotive/dtc/__init__.py +30 -0
  115. oscura/automotive/dtc/database.py +3036 -0
  116. oscura/automotive/j1939/__init__.py +14 -0
  117. oscura/automotive/j1939/decoder.py +745 -0
  118. oscura/automotive/loaders/__init__.py +35 -0
  119. oscura/automotive/loaders/asc.py +98 -0
  120. oscura/automotive/loaders/blf.py +77 -0
  121. oscura/automotive/loaders/csv_can.py +136 -0
  122. oscura/automotive/loaders/dispatcher.py +136 -0
  123. oscura/automotive/loaders/mdf.py +331 -0
  124. oscura/automotive/loaders/pcap.py +132 -0
  125. oscura/automotive/obd/__init__.py +14 -0
  126. oscura/automotive/obd/decoder.py +707 -0
  127. oscura/automotive/uds/__init__.py +48 -0
  128. oscura/automotive/uds/decoder.py +265 -0
  129. oscura/automotive/uds/models.py +64 -0
  130. oscura/automotive/visualization.py +369 -0
  131. oscura/batch/__init__.py +55 -0
  132. oscura/batch/advanced.py +627 -0
  133. oscura/batch/aggregate.py +300 -0
  134. oscura/batch/analyze.py +139 -0
  135. oscura/batch/logging.py +487 -0
  136. oscura/batch/metrics.py +556 -0
  137. oscura/builders/__init__.py +41 -0
  138. oscura/builders/signal_builder.py +1131 -0
  139. oscura/cli/__init__.py +14 -0
  140. oscura/cli/batch.py +339 -0
  141. oscura/cli/characterize.py +273 -0
  142. oscura/cli/compare.py +775 -0
  143. oscura/cli/decode.py +551 -0
  144. oscura/cli/main.py +247 -0
  145. oscura/cli/shell.py +350 -0
  146. oscura/comparison/__init__.py +66 -0
  147. oscura/comparison/compare.py +397 -0
  148. oscura/comparison/golden.py +487 -0
  149. oscura/comparison/limits.py +391 -0
  150. oscura/comparison/mask.py +434 -0
  151. oscura/comparison/trace_diff.py +30 -0
  152. oscura/comparison/visualization.py +481 -0
  153. oscura/compliance/__init__.py +70 -0
  154. oscura/compliance/advanced.py +756 -0
  155. oscura/compliance/masks.py +363 -0
  156. oscura/compliance/reporting.py +483 -0
  157. oscura/compliance/testing.py +298 -0
  158. oscura/component/__init__.py +38 -0
  159. oscura/component/impedance.py +365 -0
  160. oscura/component/reactive.py +598 -0
  161. oscura/component/transmission_line.py +312 -0
  162. oscura/config/__init__.py +191 -0
  163. oscura/config/defaults.py +254 -0
  164. oscura/config/loader.py +348 -0
  165. oscura/config/memory.py +271 -0
  166. oscura/config/migration.py +458 -0
  167. oscura/config/pipeline.py +1077 -0
  168. oscura/config/preferences.py +530 -0
  169. oscura/config/protocol.py +875 -0
  170. oscura/config/schema.py +713 -0
  171. oscura/config/settings.py +420 -0
  172. oscura/config/thresholds.py +599 -0
  173. oscura/convenience.py +457 -0
  174. oscura/core/__init__.py +299 -0
  175. oscura/core/audit.py +457 -0
  176. oscura/core/backend_selector.py +405 -0
  177. oscura/core/cache.py +590 -0
  178. oscura/core/cancellation.py +439 -0
  179. oscura/core/confidence.py +225 -0
  180. oscura/core/config.py +506 -0
  181. oscura/core/correlation.py +216 -0
  182. oscura/core/cross_domain.py +422 -0
  183. oscura/core/debug.py +301 -0
  184. oscura/core/edge_cases.py +541 -0
  185. oscura/core/exceptions.py +535 -0
  186. oscura/core/gpu_backend.py +523 -0
  187. oscura/core/lazy.py +832 -0
  188. oscura/core/log_query.py +540 -0
  189. oscura/core/logging.py +931 -0
  190. oscura/core/logging_advanced.py +952 -0
  191. oscura/core/memoize.py +171 -0
  192. oscura/core/memory_check.py +274 -0
  193. oscura/core/memory_guard.py +290 -0
  194. oscura/core/memory_limits.py +336 -0
  195. oscura/core/memory_monitor.py +453 -0
  196. oscura/core/memory_progress.py +465 -0
  197. oscura/core/memory_warnings.py +315 -0
  198. oscura/core/numba_backend.py +362 -0
  199. oscura/core/performance.py +352 -0
  200. oscura/core/progress.py +524 -0
  201. oscura/core/provenance.py +358 -0
  202. oscura/core/results.py +331 -0
  203. oscura/core/types.py +504 -0
  204. oscura/core/uncertainty.py +383 -0
  205. oscura/discovery/__init__.py +52 -0
  206. oscura/discovery/anomaly_detector.py +672 -0
  207. oscura/discovery/auto_decoder.py +415 -0
  208. oscura/discovery/comparison.py +497 -0
  209. oscura/discovery/quality_validator.py +528 -0
  210. oscura/discovery/signal_detector.py +769 -0
  211. oscura/dsl/__init__.py +73 -0
  212. oscura/dsl/commands.py +246 -0
  213. oscura/dsl/interpreter.py +455 -0
  214. oscura/dsl/parser.py +689 -0
  215. oscura/dsl/repl.py +172 -0
  216. oscura/exceptions.py +59 -0
  217. oscura/exploratory/__init__.py +111 -0
  218. oscura/exploratory/error_recovery.py +642 -0
  219. oscura/exploratory/fuzzy.py +513 -0
  220. oscura/exploratory/fuzzy_advanced.py +786 -0
  221. oscura/exploratory/legacy.py +831 -0
  222. oscura/exploratory/parse.py +358 -0
  223. oscura/exploratory/recovery.py +275 -0
  224. oscura/exploratory/sync.py +382 -0
  225. oscura/exploratory/unknown.py +707 -0
  226. oscura/export/__init__.py +25 -0
  227. oscura/export/wireshark/README.md +265 -0
  228. oscura/export/wireshark/__init__.py +47 -0
  229. oscura/export/wireshark/generator.py +312 -0
  230. oscura/export/wireshark/lua_builder.py +159 -0
  231. oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
  232. oscura/export/wireshark/type_mapping.py +165 -0
  233. oscura/export/wireshark/validator.py +105 -0
  234. oscura/exporters/__init__.py +94 -0
  235. oscura/exporters/csv.py +303 -0
  236. oscura/exporters/exporters.py +44 -0
  237. oscura/exporters/hdf5.py +219 -0
  238. oscura/exporters/html_export.py +701 -0
  239. oscura/exporters/json_export.py +291 -0
  240. oscura/exporters/markdown_export.py +367 -0
  241. oscura/exporters/matlab_export.py +354 -0
  242. oscura/exporters/npz_export.py +219 -0
  243. oscura/exporters/spice_export.py +210 -0
  244. oscura/extensibility/__init__.py +131 -0
  245. oscura/extensibility/docs.py +752 -0
  246. oscura/extensibility/extensions.py +1125 -0
  247. oscura/extensibility/logging.py +259 -0
  248. oscura/extensibility/measurements.py +485 -0
  249. oscura/extensibility/plugins.py +414 -0
  250. oscura/extensibility/registry.py +346 -0
  251. oscura/extensibility/templates.py +913 -0
  252. oscura/extensibility/validation.py +651 -0
  253. oscura/filtering/__init__.py +89 -0
  254. oscura/filtering/base.py +563 -0
  255. oscura/filtering/convenience.py +564 -0
  256. oscura/filtering/design.py +725 -0
  257. oscura/filtering/filters.py +32 -0
  258. oscura/filtering/introspection.py +605 -0
  259. oscura/guidance/__init__.py +24 -0
  260. oscura/guidance/recommender.py +429 -0
  261. oscura/guidance/wizard.py +518 -0
  262. oscura/inference/__init__.py +251 -0
  263. oscura/inference/active_learning/README.md +153 -0
  264. oscura/inference/active_learning/__init__.py +38 -0
  265. oscura/inference/active_learning/lstar.py +257 -0
  266. oscura/inference/active_learning/observation_table.py +230 -0
  267. oscura/inference/active_learning/oracle.py +78 -0
  268. oscura/inference/active_learning/teachers/__init__.py +15 -0
  269. oscura/inference/active_learning/teachers/simulator.py +192 -0
  270. oscura/inference/adaptive_tuning.py +453 -0
  271. oscura/inference/alignment.py +653 -0
  272. oscura/inference/bayesian.py +943 -0
  273. oscura/inference/binary.py +1016 -0
  274. oscura/inference/crc_reverse.py +711 -0
  275. oscura/inference/logic.py +288 -0
  276. oscura/inference/message_format.py +1305 -0
  277. oscura/inference/protocol.py +417 -0
  278. oscura/inference/protocol_dsl.py +1084 -0
  279. oscura/inference/protocol_library.py +1230 -0
  280. oscura/inference/sequences.py +809 -0
  281. oscura/inference/signal_intelligence.py +1509 -0
  282. oscura/inference/spectral.py +215 -0
  283. oscura/inference/state_machine.py +634 -0
  284. oscura/inference/stream.py +918 -0
  285. oscura/integrations/__init__.py +59 -0
  286. oscura/integrations/llm.py +1827 -0
  287. oscura/jupyter/__init__.py +32 -0
  288. oscura/jupyter/display.py +268 -0
  289. oscura/jupyter/magic.py +334 -0
  290. oscura/loaders/__init__.py +526 -0
  291. oscura/loaders/binary.py +69 -0
  292. oscura/loaders/configurable.py +1255 -0
  293. oscura/loaders/csv.py +26 -0
  294. oscura/loaders/csv_loader.py +473 -0
  295. oscura/loaders/hdf5.py +9 -0
  296. oscura/loaders/hdf5_loader.py +510 -0
  297. oscura/loaders/lazy.py +370 -0
  298. oscura/loaders/mmap_loader.py +583 -0
  299. oscura/loaders/numpy_loader.py +436 -0
  300. oscura/loaders/pcap.py +432 -0
  301. oscura/loaders/preprocessing.py +368 -0
  302. oscura/loaders/rigol.py +287 -0
  303. oscura/loaders/sigrok.py +321 -0
  304. oscura/loaders/tdms.py +367 -0
  305. oscura/loaders/tektronix.py +711 -0
  306. oscura/loaders/validation.py +584 -0
  307. oscura/loaders/vcd.py +464 -0
  308. oscura/loaders/wav.py +233 -0
  309. oscura/math/__init__.py +45 -0
  310. oscura/math/arithmetic.py +824 -0
  311. oscura/math/interpolation.py +413 -0
  312. oscura/onboarding/__init__.py +39 -0
  313. oscura/onboarding/help.py +498 -0
  314. oscura/onboarding/tutorials.py +405 -0
  315. oscura/onboarding/wizard.py +466 -0
  316. oscura/optimization/__init__.py +19 -0
  317. oscura/optimization/parallel.py +440 -0
  318. oscura/optimization/search.py +532 -0
  319. oscura/pipeline/__init__.py +43 -0
  320. oscura/pipeline/base.py +338 -0
  321. oscura/pipeline/composition.py +242 -0
  322. oscura/pipeline/parallel.py +448 -0
  323. oscura/pipeline/pipeline.py +375 -0
  324. oscura/pipeline/reverse_engineering.py +1119 -0
  325. oscura/plugins/__init__.py +122 -0
  326. oscura/plugins/base.py +272 -0
  327. oscura/plugins/cli.py +497 -0
  328. oscura/plugins/discovery.py +411 -0
  329. oscura/plugins/isolation.py +418 -0
  330. oscura/plugins/lifecycle.py +959 -0
  331. oscura/plugins/manager.py +493 -0
  332. oscura/plugins/registry.py +421 -0
  333. oscura/plugins/versioning.py +372 -0
  334. oscura/py.typed +0 -0
  335. oscura/quality/__init__.py +65 -0
  336. oscura/quality/ensemble.py +740 -0
  337. oscura/quality/explainer.py +338 -0
  338. oscura/quality/scoring.py +616 -0
  339. oscura/quality/warnings.py +456 -0
  340. oscura/reporting/__init__.py +248 -0
  341. oscura/reporting/advanced.py +1234 -0
  342. oscura/reporting/analyze.py +448 -0
  343. oscura/reporting/argument_preparer.py +596 -0
  344. oscura/reporting/auto_report.py +507 -0
  345. oscura/reporting/batch.py +615 -0
  346. oscura/reporting/chart_selection.py +223 -0
  347. oscura/reporting/comparison.py +330 -0
  348. oscura/reporting/config.py +615 -0
  349. oscura/reporting/content/__init__.py +39 -0
  350. oscura/reporting/content/executive.py +127 -0
  351. oscura/reporting/content/filtering.py +191 -0
  352. oscura/reporting/content/minimal.py +257 -0
  353. oscura/reporting/content/verbosity.py +162 -0
  354. oscura/reporting/core.py +508 -0
  355. oscura/reporting/core_formats/__init__.py +17 -0
  356. oscura/reporting/core_formats/multi_format.py +210 -0
  357. oscura/reporting/engine.py +836 -0
  358. oscura/reporting/export.py +366 -0
  359. oscura/reporting/formatting/__init__.py +129 -0
  360. oscura/reporting/formatting/emphasis.py +81 -0
  361. oscura/reporting/formatting/numbers.py +403 -0
  362. oscura/reporting/formatting/standards.py +55 -0
  363. oscura/reporting/formatting.py +466 -0
  364. oscura/reporting/html.py +578 -0
  365. oscura/reporting/index.py +590 -0
  366. oscura/reporting/multichannel.py +296 -0
  367. oscura/reporting/output.py +379 -0
  368. oscura/reporting/pdf.py +373 -0
  369. oscura/reporting/plots.py +731 -0
  370. oscura/reporting/pptx_export.py +360 -0
  371. oscura/reporting/renderers/__init__.py +11 -0
  372. oscura/reporting/renderers/pdf.py +94 -0
  373. oscura/reporting/sections.py +471 -0
  374. oscura/reporting/standards.py +680 -0
  375. oscura/reporting/summary_generator.py +368 -0
  376. oscura/reporting/tables.py +397 -0
  377. oscura/reporting/template_system.py +724 -0
  378. oscura/reporting/templates/__init__.py +15 -0
  379. oscura/reporting/templates/definition.py +205 -0
  380. oscura/reporting/templates/index.html +649 -0
  381. oscura/reporting/templates/index.md +173 -0
  382. oscura/schemas/__init__.py +158 -0
  383. oscura/schemas/bus_configuration.json +322 -0
  384. oscura/schemas/device_mapping.json +182 -0
  385. oscura/schemas/packet_format.json +418 -0
  386. oscura/schemas/protocol_definition.json +363 -0
  387. oscura/search/__init__.py +16 -0
  388. oscura/search/anomaly.py +292 -0
  389. oscura/search/context.py +149 -0
  390. oscura/search/pattern.py +160 -0
  391. oscura/session/__init__.py +34 -0
  392. oscura/session/annotations.py +289 -0
  393. oscura/session/history.py +313 -0
  394. oscura/session/session.py +445 -0
  395. oscura/streaming/__init__.py +43 -0
  396. oscura/streaming/chunked.py +611 -0
  397. oscura/streaming/progressive.py +393 -0
  398. oscura/streaming/realtime.py +622 -0
  399. oscura/testing/__init__.py +54 -0
  400. oscura/testing/synthetic.py +808 -0
  401. oscura/triggering/__init__.py +68 -0
  402. oscura/triggering/base.py +229 -0
  403. oscura/triggering/edge.py +353 -0
  404. oscura/triggering/pattern.py +344 -0
  405. oscura/triggering/pulse.py +581 -0
  406. oscura/triggering/window.py +453 -0
  407. oscura/ui/__init__.py +48 -0
  408. oscura/ui/formatters.py +526 -0
  409. oscura/ui/progressive_display.py +340 -0
  410. oscura/utils/__init__.py +99 -0
  411. oscura/utils/autodetect.py +338 -0
  412. oscura/utils/buffer.py +389 -0
  413. oscura/utils/lazy.py +407 -0
  414. oscura/utils/lazy_imports.py +147 -0
  415. oscura/utils/memory.py +836 -0
  416. oscura/utils/memory_advanced.py +1326 -0
  417. oscura/utils/memory_extensions.py +465 -0
  418. oscura/utils/progressive.py +352 -0
  419. oscura/utils/windowing.py +362 -0
  420. oscura/visualization/__init__.py +321 -0
  421. oscura/visualization/accessibility.py +526 -0
  422. oscura/visualization/annotations.py +374 -0
  423. oscura/visualization/axis_scaling.py +305 -0
  424. oscura/visualization/colors.py +453 -0
  425. oscura/visualization/digital.py +337 -0
  426. oscura/visualization/eye.py +420 -0
  427. oscura/visualization/histogram.py +281 -0
  428. oscura/visualization/interactive.py +858 -0
  429. oscura/visualization/jitter.py +702 -0
  430. oscura/visualization/keyboard.py +394 -0
  431. oscura/visualization/layout.py +365 -0
  432. oscura/visualization/optimization.py +1028 -0
  433. oscura/visualization/palettes.py +446 -0
  434. oscura/visualization/plot.py +92 -0
  435. oscura/visualization/power.py +290 -0
  436. oscura/visualization/power_extended.py +626 -0
  437. oscura/visualization/presets.py +467 -0
  438. oscura/visualization/protocols.py +932 -0
  439. oscura/visualization/render.py +207 -0
  440. oscura/visualization/rendering.py +444 -0
  441. oscura/visualization/reverse_engineering.py +791 -0
  442. oscura/visualization/signal_integrity.py +808 -0
  443. oscura/visualization/specialized.py +553 -0
  444. oscura/visualization/spectral.py +811 -0
  445. oscura/visualization/styles.py +381 -0
  446. oscura/visualization/thumbnails.py +311 -0
  447. oscura/visualization/time_axis.py +351 -0
  448. oscura/visualization/waveform.py +367 -0
  449. oscura/workflow/__init__.py +13 -0
  450. oscura/workflow/dag.py +377 -0
  451. oscura/workflows/__init__.py +58 -0
  452. oscura/workflows/compliance.py +280 -0
  453. oscura/workflows/digital.py +272 -0
  454. oscura/workflows/multi_trace.py +502 -0
  455. oscura/workflows/power.py +178 -0
  456. oscura/workflows/protocol.py +492 -0
  457. oscura/workflows/reverse_engineering.py +639 -0
  458. oscura/workflows/signal_integrity.py +227 -0
  459. oscura-0.1.1.dist-info/METADATA +300 -0
  460. oscura-0.1.1.dist-info/RECORD +463 -0
  461. oscura-0.1.1.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/licenses/LICENSE +1 -1
  463. oscura-0.0.1.dist-info/METADATA +0 -63
  464. oscura-0.0.1.dist-info/RECORD +0 -5
  465. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,397 @@
1
+ """Table generation and formatting for Oscura reports.
2
+
3
+ This module provides utilities for creating and formatting measurement
4
+ summary tables with professional appearance.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.reporting.tables import create_measurement_table
9
+ >>> table = create_measurement_table(measurements, format="markdown")
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from typing import TYPE_CHECKING, Any, Literal
15
+
16
+ import numpy as np
17
+
18
+ from oscura.reporting.formatting import NumberFormatter
19
+
20
+ if TYPE_CHECKING:
21
+ from numpy.typing import NDArray
22
+
23
+
24
+ def create_measurement_table(
25
+ measurements: dict[str, Any],
26
+ *,
27
+ format: Literal["dict", "markdown", "html", "csv"] = "dict",
28
+ show_spec: bool = True,
29
+ show_margin: bool = True,
30
+ show_status: bool = True,
31
+ sort_by: str | None = None,
32
+ ) -> dict[str, Any] | str:
33
+ """Create formatted measurement summary table.
34
+
35
+ Args:
36
+ measurements: Dictionary of measurement name -> measurement data.
37
+ format: Output format (dict, markdown, html, csv).
38
+ show_spec: Include specification column.
39
+ show_margin: Include margin column.
40
+ show_status: Include pass/fail status column.
41
+ sort_by: Column to sort by (None for original order).
42
+
43
+ Returns:
44
+ Formatted table as dictionary or string depending on format.
45
+
46
+ Raises:
47
+ ValueError: If format is unknown.
48
+
49
+ Example:
50
+ >>> measurements = {
51
+ ... "rise_time": {"value": 2.3e-9, "spec": 5e-9, "unit": "s"},
52
+ ... "fall_time": {"value": 1.8e-9, "spec": 5e-9, "unit": "s"},
53
+ ... }
54
+ >>> table = create_measurement_table(measurements, format="markdown")
55
+
56
+ References:
57
+ REPORT-004, REPORT-006
58
+ """
59
+ # Build table headers
60
+ headers = ["Parameter", "Value"]
61
+ if show_spec:
62
+ headers.append("Specification")
63
+ if show_margin:
64
+ headers.append("Margin")
65
+ if show_status:
66
+ headers.append("Status")
67
+
68
+ # Build table rows
69
+ rows = []
70
+ formatter = NumberFormatter(sig_figs=3)
71
+
72
+ for name, meas in measurements.items():
73
+ row = [name]
74
+
75
+ # Value
76
+ value = meas.get("value")
77
+ unit = meas.get("unit", "")
78
+ if value is not None:
79
+ row.append(formatter.format(value, unit))
80
+ else:
81
+ row.append("N/A")
82
+
83
+ # Specification
84
+ if show_spec:
85
+ spec = meas.get("spec")
86
+ spec_type = meas.get("spec_type", "max")
87
+ if spec is not None:
88
+ prefix = "<" if spec_type == "max" else ">" if spec_type == "min" else "="
89
+ row.append(f"{prefix}{formatter.format(spec, unit)}")
90
+ else:
91
+ row.append("-")
92
+
93
+ # Margin
94
+ if show_margin:
95
+ spec = meas.get("spec")
96
+ if value is not None and spec is not None and spec != 0:
97
+ spec_type = meas.get("spec_type", "max")
98
+ if spec_type == "max":
99
+ margin = (spec - value) / spec * 100
100
+ else:
101
+ margin = (value - spec) / spec * 100
102
+ row.append(f"{margin:.1f}%")
103
+ else:
104
+ row.append("-")
105
+
106
+ # Status
107
+ if show_status:
108
+ passed = meas.get("passed", True)
109
+ if value is None:
110
+ row.append("N/A")
111
+ else:
112
+ row.append("✓ PASS" if passed else "✗ FAIL")
113
+
114
+ rows.append(row)
115
+
116
+ # Sort if requested
117
+ if sort_by and sort_by in headers:
118
+ col_idx = headers.index(sort_by)
119
+ rows.sort(key=lambda r: r[col_idx])
120
+
121
+ # Format output
122
+ if format == "dict":
123
+ return {"type": "table", "headers": headers, "data": rows}
124
+ elif format == "markdown":
125
+ return _format_markdown_table(headers, rows)
126
+ elif format == "html":
127
+ return _format_html_table(headers, rows)
128
+ elif format == "csv":
129
+ return _format_csv_table(headers, rows)
130
+ else:
131
+ raise ValueError(f"Unknown format: {format}")
132
+
133
+
134
+ def create_comparison_table(
135
+ baseline: dict[str, Any],
136
+ current: dict[str, Any],
137
+ *,
138
+ format: Literal["dict", "markdown", "html"] = "dict",
139
+ show_delta: bool = True,
140
+ show_percent_change: bool = True,
141
+ ) -> dict[str, Any] | str:
142
+ """Create comparison table between baseline and current measurements.
143
+
144
+ Args:
145
+ baseline: Baseline measurements.
146
+ current: Current measurements.
147
+ format: Output format.
148
+ show_delta: Show absolute difference.
149
+ show_percent_change: Show percentage change.
150
+
151
+ Returns:
152
+ Formatted comparison table.
153
+
154
+ Raises:
155
+ ValueError: If format is unknown.
156
+
157
+ References:
158
+ REPORT-008 (Comparison Reports)
159
+ """
160
+ headers = ["Parameter", "Baseline", "Current"]
161
+ if show_delta:
162
+ headers.append("Delta")
163
+ if show_percent_change:
164
+ headers.append("% Change")
165
+
166
+ rows = []
167
+ formatter = NumberFormatter(sig_figs=3)
168
+
169
+ # Get all parameter names
170
+ all_params = set(baseline.keys()) | set(current.keys())
171
+
172
+ for name in sorted(all_params):
173
+ row = [name]
174
+
175
+ base_meas = baseline.get(name, {})
176
+ curr_meas = current.get(name, {})
177
+
178
+ base_val = base_meas.get("value")
179
+ curr_val = curr_meas.get("value")
180
+ unit = base_meas.get("unit", curr_meas.get("unit", ""))
181
+
182
+ # Baseline value
183
+ if base_val is not None:
184
+ row.append(formatter.format(base_val, unit))
185
+ else:
186
+ row.append("-")
187
+
188
+ # Current value
189
+ if curr_val is not None:
190
+ row.append(formatter.format(curr_val, unit))
191
+ else:
192
+ row.append("-")
193
+
194
+ # Delta
195
+ if show_delta:
196
+ if base_val is not None and curr_val is not None:
197
+ delta = curr_val - base_val
198
+ row.append(formatter.format(delta, unit))
199
+ else:
200
+ row.append("-")
201
+
202
+ # Percent change
203
+ if show_percent_change:
204
+ if base_val is not None and curr_val is not None and base_val != 0:
205
+ pct_change = (curr_val - base_val) / base_val * 100
206
+ row.append(f"{pct_change:+.1f}%")
207
+ else:
208
+ row.append("-")
209
+
210
+ rows.append(row)
211
+
212
+ if format == "dict":
213
+ return {"type": "table", "headers": headers, "data": rows}
214
+ elif format == "markdown":
215
+ return _format_markdown_table(headers, rows)
216
+ elif format == "html":
217
+ return _format_html_table(headers, rows)
218
+ else:
219
+ raise ValueError(f"Unknown format: {format}")
220
+
221
+
222
+ def create_statistics_table(
223
+ data: dict[str, NDArray[np.float64]],
224
+ *,
225
+ format: Literal["dict", "markdown", "html"] = "dict",
226
+ statistics: list[str] | None = None,
227
+ ) -> dict[str, Any] | str:
228
+ """Create statistics summary table.
229
+
230
+ Args:
231
+ data: Dictionary of parameter name -> data array.
232
+ format: Output format.
233
+ statistics: List of statistics to include (mean, std, min, max, median).
234
+
235
+ Returns:
236
+ Formatted statistics table.
237
+
238
+ Raises:
239
+ ValueError: If format is unknown.
240
+
241
+ References:
242
+ REPORT-004
243
+ """
244
+ if statistics is None:
245
+ statistics = ["mean", "std", "min", "max", "median"]
246
+
247
+ headers = ["Parameter"] + [stat.capitalize() for stat in statistics]
248
+ rows = []
249
+
250
+ for name, values in data.items():
251
+ row = [name]
252
+
253
+ for stat in statistics:
254
+ if stat == "mean":
255
+ row.append(f"{np.mean(values):.3g}")
256
+ elif stat == "std":
257
+ row.append(f"{np.std(values):.3g}")
258
+ elif stat == "min":
259
+ row.append(f"{np.min(values):.3g}")
260
+ elif stat == "max":
261
+ row.append(f"{np.max(values):.3g}")
262
+ elif stat == "median":
263
+ row.append(f"{np.median(values):.3g}")
264
+ else:
265
+ row.append("-")
266
+
267
+ rows.append(row)
268
+
269
+ if format == "dict":
270
+ return {"type": "table", "headers": headers, "data": rows}
271
+ elif format == "markdown":
272
+ return _format_markdown_table(headers, rows)
273
+ elif format == "html":
274
+ return _format_html_table(headers, rows)
275
+ else:
276
+ raise ValueError(f"Unknown format: {format}")
277
+
278
+
279
+ def _format_markdown_table(headers: list[str], rows: list[list[Any]]) -> str:
280
+ """Format table as Markdown."""
281
+ lines = []
282
+
283
+ # Header row
284
+ lines.append("| " + " | ".join(str(h) for h in headers) + " |")
285
+ lines.append("| " + " | ".join("---" for _ in headers) + " |")
286
+
287
+ # Data rows
288
+ for row in rows:
289
+ lines.append("| " + " | ".join(str(cell) for cell in row) + " |")
290
+
291
+ return "\n".join(lines)
292
+
293
+
294
+ def _format_html_table(headers: list[str], rows: list[list[Any]]) -> str:
295
+ """Format table as HTML."""
296
+ lines = ['<table class="measurement-table">']
297
+
298
+ # Header
299
+ lines.append("<thead><tr>")
300
+ for h in headers:
301
+ lines.append(f"<th>{h}</th>")
302
+ lines.append("</tr></thead>")
303
+
304
+ # Body
305
+ lines.append("<tbody>")
306
+ for row in rows:
307
+ lines.append("<tr>")
308
+ for cell in row:
309
+ cell_str = str(cell)
310
+ # Apply CSS classes for status
311
+ if "PASS" in cell_str:
312
+ lines.append(f'<td class="pass">{cell}</td>')
313
+ elif "FAIL" in cell_str:
314
+ lines.append(f'<td class="fail">{cell}</td>')
315
+ else:
316
+ lines.append(f"<td>{cell}</td>")
317
+ lines.append("</tr>")
318
+ lines.append("</tbody>")
319
+
320
+ lines.append("</table>")
321
+ return "\n".join(lines)
322
+
323
+
324
+ def _format_csv_table(headers: list[str], rows: list[list[Any]]) -> str:
325
+ """Format table as CSV."""
326
+ import csv
327
+ from io import StringIO
328
+
329
+ output = StringIO()
330
+ writer = csv.writer(output)
331
+
332
+ writer.writerow(headers)
333
+ for row in rows:
334
+ writer.writerow(row)
335
+
336
+ return output.getvalue()
337
+
338
+
339
+ def format_batch_summary_table(
340
+ batch_results: list[dict[str, Any]],
341
+ *,
342
+ format: Literal["dict", "markdown", "html"] = "dict",
343
+ ) -> dict[str, Any] | str:
344
+ """Create batch summary table for multi-DUT testing.
345
+
346
+ Args:
347
+ batch_results: List of result dictionaries, one per DUT.
348
+ format: Output format.
349
+
350
+ Returns:
351
+ Formatted batch summary table.
352
+
353
+ Raises:
354
+ ValueError: If format is unknown.
355
+
356
+ References:
357
+ REPORT-009 (Batch Report Aggregation)
358
+ """
359
+ if not batch_results:
360
+ return {"type": "table", "headers": [], "data": []}
361
+
362
+ headers = ["DUT ID", "Total Tests", "Passed", "Failed", "Yield"]
363
+ rows = []
364
+
365
+ for i, result in enumerate(batch_results):
366
+ dut_id = result.get("dut_id", f"DUT-{i + 1}")
367
+ total = result.get("total_count", 0)
368
+ passed = result.get("pass_count", 0)
369
+ failed = total - passed
370
+ yield_pct = (passed / total * 100) if total > 0 else 0
371
+
372
+ rows.append([dut_id, total, passed, failed, f"{yield_pct:.1f}%"])
373
+
374
+ # Add summary row
375
+ total_tests = sum(r.get("total_count", 0) for r in batch_results)
376
+ total_passed = sum(r.get("pass_count", 0) for r in batch_results)
377
+ total_failed = total_tests - total_passed
378
+ overall_yield = (total_passed / total_tests * 100) if total_tests > 0 else 0
379
+
380
+ rows.append(
381
+ [
382
+ "TOTAL",
383
+ total_tests,
384
+ total_passed,
385
+ total_failed,
386
+ f"{overall_yield:.1f}%",
387
+ ]
388
+ )
389
+
390
+ if format == "dict":
391
+ return {"type": "table", "headers": headers, "data": rows}
392
+ elif format == "markdown":
393
+ return _format_markdown_table(headers, rows)
394
+ elif format == "html":
395
+ return _format_html_table(headers, rows)
396
+ else:
397
+ raise ValueError(f"Unknown format: {format}")