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,556 @@
1
+ """Batch job performance metrics collection and export.
2
+
3
+ This module provides comprehensive metrics collection for batch processing jobs
4
+ including throughput, timing, error statistics, and export capabilities.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.batch.metrics import BatchMetrics
9
+ >>> metrics = BatchMetrics(batch_id="job-001")
10
+ >>> metrics.start()
11
+ >>> metrics.record_file("file1.wfm", duration=0.5, samples=100000)
12
+ >>> metrics.record_file("file2.wfm", duration=0.3, samples=50000)
13
+ >>> summary = metrics.summary()
14
+ >>> metrics.export_json("metrics.json")
15
+
16
+ References:
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import csv
22
+ import json
23
+ import statistics
24
+ import threading
25
+ import time
26
+ from dataclasses import dataclass, field
27
+ from datetime import UTC, datetime
28
+ from pathlib import Path
29
+ from typing import Any
30
+
31
+ from oscura.core.logging import format_timestamp, get_logger
32
+
33
+ logger = get_logger(__name__)
34
+
35
+
36
+ @dataclass
37
+ class FileMetrics:
38
+ """Metrics for a single file in a batch job.
39
+
40
+ Attributes:
41
+ filename: Path to the file.
42
+ start_time: Processing start time (epoch seconds).
43
+ end_time: Processing end time (epoch seconds).
44
+ duration: Processing duration in seconds.
45
+ samples: Number of samples processed.
46
+ measurements: Number of measurements computed.
47
+ status: Processing status (success, error, skipped).
48
+ error_type: Error type if status is 'error'.
49
+ error_message: Error message if status is 'error'.
50
+ memory_peak: Peak memory usage in bytes (if tracked).
51
+
52
+ References:
53
+ LOG-012: Batch Job Performance Metrics
54
+ """
55
+
56
+ filename: str
57
+ start_time: float = 0.0
58
+ end_time: float = 0.0
59
+ duration: float = 0.0
60
+ samples: int = 0
61
+ measurements: int = 0
62
+ status: str = "pending"
63
+ error_type: str | None = None
64
+ error_message: str | None = None
65
+ memory_peak: int | None = None
66
+
67
+ def to_dict(self) -> dict[str, Any]:
68
+ """Convert to dictionary representation."""
69
+ return {
70
+ "filename": self.filename,
71
+ "start_time": format_timestamp(datetime.fromtimestamp(self.start_time, tz=UTC))
72
+ if self.start_time
73
+ else None,
74
+ "end_time": format_timestamp(datetime.fromtimestamp(self.end_time, tz=UTC))
75
+ if self.end_time
76
+ else None,
77
+ "duration_seconds": self.duration,
78
+ "samples": self.samples,
79
+ "measurements": self.measurements,
80
+ "status": self.status,
81
+ "error_type": self.error_type,
82
+ "error_message": self.error_message,
83
+ "memory_peak_bytes": self.memory_peak,
84
+ "samples_per_second": self.samples / self.duration if self.duration > 0 else 0,
85
+ }
86
+
87
+
88
+ @dataclass
89
+ class ErrorBreakdown:
90
+ """Breakdown of errors by type.
91
+
92
+ Attributes:
93
+ by_type: Count of errors grouped by error type.
94
+ total: Total number of errors.
95
+ rate: Error rate as percentage.
96
+
97
+ References:
98
+ LOG-012: Error breakdown requirement
99
+ """
100
+
101
+ by_type: dict[str, int] = field(default_factory=dict)
102
+ total: int = 0
103
+ rate: float = 0.0
104
+
105
+ def to_dict(self) -> dict[str, Any]:
106
+ """Convert to dictionary representation."""
107
+ return {
108
+ "by_type": self.by_type,
109
+ "total": self.total,
110
+ "rate_percent": round(self.rate * 100, 2),
111
+ }
112
+
113
+
114
+ @dataclass
115
+ class TimingStats:
116
+ """Timing statistics for batch processing.
117
+
118
+ Attributes:
119
+ total_duration: Total wall-clock duration in seconds.
120
+ average_per_file: Average processing time per file.
121
+ min_per_file: Minimum processing time per file.
122
+ max_per_file: Maximum processing time per file.
123
+ median_per_file: Median processing time per file.
124
+ stddev_per_file: Standard deviation of processing times.
125
+
126
+ References:
127
+ LOG-012: Timing metrics requirement
128
+ """
129
+
130
+ total_duration: float = 0.0
131
+ average_per_file: float = 0.0
132
+ min_per_file: float = 0.0
133
+ max_per_file: float = 0.0
134
+ median_per_file: float = 0.0
135
+ stddev_per_file: float = 0.0
136
+
137
+ def to_dict(self) -> dict[str, Any]:
138
+ """Convert to dictionary representation."""
139
+ return {
140
+ "total_duration_seconds": round(self.total_duration, 3),
141
+ "average_per_file_seconds": round(self.average_per_file, 3),
142
+ "min_per_file_seconds": round(self.min_per_file, 3),
143
+ "max_per_file_seconds": round(self.max_per_file, 3),
144
+ "median_per_file_seconds": round(self.median_per_file, 3),
145
+ "stddev_per_file_seconds": round(self.stddev_per_file, 3),
146
+ }
147
+
148
+
149
+ @dataclass
150
+ class ThroughputStats:
151
+ """Throughput statistics for batch processing.
152
+
153
+ Attributes:
154
+ files_per_second: Processing rate in files per second.
155
+ samples_per_second: Processing rate in samples per second.
156
+ measurements_per_second: Rate of measurements computed per second.
157
+ bytes_per_second: Data processing rate (if tracked).
158
+
159
+ References:
160
+ LOG-012: Throughput metrics requirement
161
+ """
162
+
163
+ files_per_second: float = 0.0
164
+ samples_per_second: float = 0.0
165
+ measurements_per_second: float = 0.0
166
+ bytes_per_second: float = 0.0
167
+
168
+ def to_dict(self) -> dict[str, Any]:
169
+ """Convert to dictionary representation."""
170
+ return {
171
+ "files_per_second": round(self.files_per_second, 3),
172
+ "samples_per_second": round(self.samples_per_second, 0),
173
+ "measurements_per_second": round(self.measurements_per_second, 0),
174
+ "bytes_per_second": round(self.bytes_per_second, 0),
175
+ }
176
+
177
+
178
+ @dataclass
179
+ class BatchMetricsSummary:
180
+ """Complete summary of batch processing metrics.
181
+
182
+ Attributes:
183
+ batch_id: Unique batch job identifier.
184
+ total_files: Total number of files in the batch.
185
+ processed_count: Number of successfully processed files.
186
+ error_count: Number of files with errors.
187
+ skip_count: Number of skipped files.
188
+ timing: Timing statistics.
189
+ throughput: Throughput statistics.
190
+ errors: Error breakdown.
191
+ start_time: Batch start time (ISO 8601).
192
+ end_time: Batch end time (ISO 8601).
193
+
194
+ References:
195
+ LOG-012: Batch Job Performance Metrics
196
+ """
197
+
198
+ batch_id: str
199
+ total_files: int
200
+ processed_count: int
201
+ error_count: int
202
+ skip_count: int
203
+ timing: TimingStats
204
+ throughput: ThroughputStats
205
+ errors: ErrorBreakdown
206
+ start_time: str
207
+ end_time: str
208
+
209
+ def to_dict(self) -> dict[str, Any]:
210
+ """Convert to dictionary representation."""
211
+ return {
212
+ "batch_id": self.batch_id,
213
+ "total_files": self.total_files,
214
+ "processed_count": self.processed_count,
215
+ "error_count": self.error_count,
216
+ "skip_count": self.skip_count,
217
+ "success_rate_percent": round(
218
+ (self.processed_count / self.total_files * 100) if self.total_files > 0 else 0, 2
219
+ ),
220
+ "timing": self.timing.to_dict(),
221
+ "throughput": self.throughput.to_dict(),
222
+ "errors": self.errors.to_dict(),
223
+ "start_time": self.start_time,
224
+ "end_time": self.end_time,
225
+ }
226
+
227
+
228
+ class BatchMetrics:
229
+ """Batch job performance metrics collector.
230
+
231
+ Collects and aggregates performance metrics for batch processing jobs
232
+ including throughput, timing, and error statistics.
233
+
234
+ Example:
235
+ >>> metrics = BatchMetrics(batch_id="analysis-001")
236
+ >>> metrics.start()
237
+ >>> for file in files:
238
+ ... metrics.record_file(file, duration=0.5, samples=100000)
239
+ >>> metrics.finish()
240
+ >>> summary = metrics.summary()
241
+ >>> metrics.export_json("metrics.json")
242
+
243
+ References:
244
+ LOG-012: Batch Job Performance Metrics
245
+ """
246
+
247
+ def __init__(self, batch_id: str | None = None) -> None:
248
+ """Initialize batch metrics collector.
249
+
250
+ Args:
251
+ batch_id: Unique batch job identifier. Auto-generated if None.
252
+ """
253
+ import uuid
254
+
255
+ self.batch_id = batch_id or str(uuid.uuid4())
256
+ self._files: list[FileMetrics] = []
257
+ self._lock = threading.Lock()
258
+ self._start_time: float | None = None
259
+ self._end_time: float | None = None
260
+ self._error_types: dict[str, int] = {}
261
+
262
+ def start(self) -> None:
263
+ """Mark batch job as started.
264
+
265
+ Records the start time for duration calculation.
266
+ """
267
+ self._start_time = time.time()
268
+ logger.info(
269
+ "Batch metrics collection started",
270
+ extra={"batch_id": self.batch_id},
271
+ )
272
+
273
+ def finish(self) -> None:
274
+ """Mark batch job as finished.
275
+
276
+ Records the end time and logs the completion summary.
277
+ """
278
+ self._end_time = time.time()
279
+ summary = self.summary()
280
+ logger.info(
281
+ "Batch metrics collection finished",
282
+ extra={
283
+ "batch_id": self.batch_id,
284
+ "total_files": summary.total_files,
285
+ "processed": summary.processed_count,
286
+ "errors": summary.error_count,
287
+ "duration": summary.timing.total_duration,
288
+ },
289
+ )
290
+
291
+ def record_file(
292
+ self,
293
+ filename: str,
294
+ *,
295
+ duration: float,
296
+ samples: int = 0,
297
+ measurements: int = 0,
298
+ status: str = "success",
299
+ error_type: str | None = None,
300
+ error_message: str | None = None,
301
+ memory_peak: int | None = None,
302
+ ) -> None:
303
+ """Record metrics for a processed file.
304
+
305
+ Args:
306
+ filename: Path to the processed file.
307
+ duration: Processing duration in seconds.
308
+ samples: Number of samples processed.
309
+ measurements: Number of measurements computed.
310
+ status: Processing status (success, error, skipped).
311
+ error_type: Error type if status is 'error'.
312
+ error_message: Error message if status is 'error'.
313
+ memory_peak: Peak memory usage in bytes.
314
+
315
+ References:
316
+ LOG-012: Per-file metrics tracking
317
+ """
318
+ now = time.time()
319
+ file_metrics = FileMetrics(
320
+ filename=filename,
321
+ start_time=now - duration,
322
+ end_time=now,
323
+ duration=duration,
324
+ samples=samples,
325
+ measurements=measurements,
326
+ status=status,
327
+ error_type=error_type,
328
+ error_message=error_message,
329
+ memory_peak=memory_peak,
330
+ )
331
+
332
+ with self._lock:
333
+ self._files.append(file_metrics)
334
+ if status == "error" and error_type:
335
+ self._error_types[error_type] = self._error_types.get(error_type, 0) + 1
336
+
337
+ def record_error(
338
+ self,
339
+ filename: str,
340
+ error_type: str,
341
+ error_message: str,
342
+ duration: float = 0.0,
343
+ ) -> None:
344
+ """Record a file processing error.
345
+
346
+ Args:
347
+ filename: Path to the file that failed.
348
+ error_type: Type of error (e.g., "FileNotFoundError").
349
+ error_message: Detailed error message.
350
+ duration: Processing duration before error.
351
+
352
+ References:
353
+ LOG-012: Error tracking
354
+ """
355
+ self.record_file(
356
+ filename,
357
+ duration=duration,
358
+ status="error",
359
+ error_type=error_type,
360
+ error_message=error_message,
361
+ )
362
+
363
+ def record_skip(self, filename: str, reason: str = "") -> None:
364
+ """Record a skipped file.
365
+
366
+ Args:
367
+ filename: Path to the skipped file.
368
+ reason: Reason for skipping.
369
+
370
+ References:
371
+ LOG-012: Skip count tracking
372
+ """
373
+ self.record_file(
374
+ filename,
375
+ duration=0.0,
376
+ status="skipped",
377
+ error_message=reason,
378
+ )
379
+
380
+ def summary(self) -> BatchMetricsSummary:
381
+ """Generate batch processing metrics summary.
382
+
383
+ Returns:
384
+ BatchMetricsSummary with aggregated statistics.
385
+
386
+ References:
387
+ LOG-012: Batch Job Performance Metrics
388
+ """
389
+ with self._lock:
390
+ files = list(self._files)
391
+ error_types = dict(self._error_types)
392
+
393
+ # Count by status
394
+ processed_count = sum(1 for f in files if f.status == "success")
395
+ error_count = sum(1 for f in files if f.status == "error")
396
+ skip_count = sum(1 for f in files if f.status == "skipped")
397
+ total_files = len(files)
398
+
399
+ # Collect durations for successful files
400
+ durations = [f.duration for f in files if f.status == "success" and f.duration > 0]
401
+
402
+ # Calculate timing stats
403
+ if durations:
404
+ timing = TimingStats(
405
+ total_duration=self._end_time - self._start_time
406
+ if self._start_time and self._end_time
407
+ else sum(durations),
408
+ average_per_file=statistics.mean(durations),
409
+ min_per_file=min(durations),
410
+ max_per_file=max(durations),
411
+ median_per_file=statistics.median(durations),
412
+ stddev_per_file=statistics.stdev(durations) if len(durations) > 1 else 0.0,
413
+ )
414
+ else:
415
+ timing = TimingStats(
416
+ total_duration=self._end_time - self._start_time
417
+ if self._start_time and self._end_time
418
+ else 0.0
419
+ )
420
+
421
+ # Calculate throughput stats
422
+ total_samples = sum(f.samples for f in files if f.status == "success")
423
+ total_measurements = sum(f.measurements for f in files if f.status == "success")
424
+
425
+ if timing.total_duration > 0:
426
+ throughput = ThroughputStats(
427
+ files_per_second=processed_count / timing.total_duration,
428
+ samples_per_second=total_samples / timing.total_duration,
429
+ measurements_per_second=total_measurements / timing.total_duration,
430
+ )
431
+ else:
432
+ throughput = ThroughputStats()
433
+
434
+ # Error breakdown
435
+ errors = ErrorBreakdown(
436
+ by_type=error_types,
437
+ total=error_count,
438
+ rate=error_count / total_files if total_files > 0 else 0.0,
439
+ )
440
+
441
+ # Timestamps
442
+ start_time = (
443
+ format_timestamp(datetime.fromtimestamp(self._start_time, tz=UTC))
444
+ if self._start_time
445
+ else ""
446
+ )
447
+ end_time = (
448
+ format_timestamp(datetime.fromtimestamp(self._end_time, tz=UTC))
449
+ if self._end_time
450
+ else ""
451
+ )
452
+
453
+ return BatchMetricsSummary(
454
+ batch_id=self.batch_id,
455
+ total_files=total_files,
456
+ processed_count=processed_count,
457
+ error_count=error_count,
458
+ skip_count=skip_count,
459
+ timing=timing,
460
+ throughput=throughput,
461
+ errors=errors,
462
+ start_time=start_time,
463
+ end_time=end_time,
464
+ )
465
+
466
+ def get_file_metrics(self) -> list[dict[str, Any]]:
467
+ """Get metrics for all files.
468
+
469
+ Returns:
470
+ List of file metric dictionaries.
471
+ """
472
+ with self._lock:
473
+ return [f.to_dict() for f in self._files]
474
+
475
+ def export_json(self, path: str | Path) -> None:
476
+ """Export metrics to JSON file.
477
+
478
+ Args:
479
+ path: Output file path.
480
+
481
+ References:
482
+ LOG-012: Export batch metrics as JSON
483
+ """
484
+ path = Path(path)
485
+ summary = self.summary()
486
+
487
+ output = {
488
+ "summary": summary.to_dict(),
489
+ "files": self.get_file_metrics(),
490
+ }
491
+
492
+ with open(path, "w") as f:
493
+ json.dump(output, f, indent=2, default=str)
494
+
495
+ logger.info(f"Batch metrics exported to {path}")
496
+
497
+ def export_csv(self, path: str | Path) -> None:
498
+ """Export per-file metrics to CSV file.
499
+
500
+ Args:
501
+ path: Output file path.
502
+
503
+ References:
504
+ LOG-012: Export batch metrics as CSV
505
+ """
506
+ path = Path(path)
507
+ files = self.get_file_metrics()
508
+
509
+ if not files:
510
+ logger.warning("No file metrics to export")
511
+ return
512
+
513
+ # Get all keys from first file
514
+ fieldnames = list(files[0].keys())
515
+
516
+ with open(path, "w", newline="") as f:
517
+ writer = csv.DictWriter(f, fieldnames=fieldnames)
518
+ writer.writeheader()
519
+ writer.writerows(files)
520
+
521
+ logger.info(f"Batch metrics CSV exported to {path}")
522
+
523
+
524
+ def get_batch_stats(batch_id: str, metrics: BatchMetrics) -> dict[str, Any]:
525
+ """Get statistics for a batch job.
526
+
527
+ CLI command implementation: oscura batch stats <batch_id>
528
+
529
+ Args:
530
+ batch_id: Batch job identifier.
531
+ metrics: BatchMetrics instance.
532
+
533
+ Returns:
534
+ Dictionary with batch statistics.
535
+
536
+ Raises:
537
+ ValueError: If batch ID does not match the metrics instance.
538
+
539
+ References:
540
+ LOG-012: CLI command requirement
541
+ """
542
+ if metrics.batch_id != batch_id:
543
+ raise ValueError(f"Batch ID mismatch: expected {batch_id}, got {metrics.batch_id}")
544
+
545
+ return metrics.summary().to_dict()
546
+
547
+
548
+ __all__ = [
549
+ "BatchMetrics",
550
+ "BatchMetricsSummary",
551
+ "ErrorBreakdown",
552
+ "FileMetrics",
553
+ "ThroughputStats",
554
+ "TimingStats",
555
+ "get_batch_stats",
556
+ ]
@@ -0,0 +1,41 @@
1
+ """Signal and protocol builders for TraceKit.
2
+
3
+ This module provides fluent builders for generating test signals, protocol
4
+ transactions, and test scenarios. These builders enable composable signal
5
+ generation without manual numpy operations.
6
+
7
+ Example:
8
+ >>> import oscura as tk
9
+ >>> # Simple sine wave with noise
10
+ >>> signal = (tk.SignalBuilder(sample_rate=1e6, duration=0.01)
11
+ ... .add_sine(frequency=1000, amplitude=1.0)
12
+ ... .add_noise(snr_db=40)
13
+ ... .build())
14
+ >>>
15
+ >>> # UART signal for protocol testing
16
+ >>> uart = (tk.SignalBuilder(sample_rate=10e6)
17
+ ... .add_uart(baud_rate=115200, data=b"Hello TraceKit!")
18
+ ... .add_noise(snr_db=30)
19
+ ... .build())
20
+ >>>
21
+ >>> # Multi-channel SPI transaction
22
+ >>> spi = (tk.SignalBuilder(sample_rate=10e6)
23
+ ... .add_spi(clock_freq=1e6, data_mosi=b"\\x9F\\x00\\x00")
24
+ ... .build())
25
+
26
+ References:
27
+ - TraceKit Signal Generation Guide
28
+ - Protocol Test Signal Specifications
29
+ """
30
+
31
+ from oscura.builders.signal_builder import (
32
+ GeneratedSignal,
33
+ SignalBuilder,
34
+ SignalMetadata,
35
+ )
36
+
37
+ __all__ = [
38
+ "GeneratedSignal",
39
+ "SignalBuilder",
40
+ "SignalMetadata",
41
+ ]