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,434 @@
1
+ """Mask testing for Oscura.
2
+
3
+ This module provides mask-based pass/fail testing for waveforms,
4
+ including eye diagram masks and custom polygon masks.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.comparison import mask_test, eye_mask
9
+ >>> mask = eye_mask(0.5, 0.4, 0.3)
10
+ >>> result = mask_test(trace, mask)
11
+
12
+ References:
13
+ IEEE 802.3: Ethernet eye diagram mask specifications
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from dataclasses import dataclass, field
19
+ from typing import TYPE_CHECKING, Any, Literal
20
+
21
+ import numpy as np
22
+
23
+ from oscura.core.exceptions import AnalysisError
24
+
25
+ if TYPE_CHECKING:
26
+ from numpy.typing import NDArray
27
+
28
+ from oscura.core.types import WaveformTrace
29
+
30
+
31
+ @dataclass
32
+ class MaskRegion:
33
+ """A region in a mask definition.
34
+
35
+ Represents a polygon region that waveform data must avoid
36
+ (violation region) or must stay within (boundary region).
37
+
38
+ Attributes:
39
+ vertices: List of (x, y) vertices defining the polygon.
40
+ region_type: "violation" (must avoid) or "boundary" (must stay within).
41
+ name: Optional name for the region.
42
+ """
43
+
44
+ vertices: list[tuple[float, float]]
45
+ region_type: Literal["violation", "boundary"] = "violation"
46
+ name: str = ""
47
+
48
+ def contains_point(self, x: float, y: float) -> bool:
49
+ """Check if a point is inside the polygon.
50
+
51
+ Uses ray casting algorithm for point-in-polygon test.
52
+
53
+ Args:
54
+ x: X coordinate.
55
+ y: Y coordinate.
56
+
57
+ Returns:
58
+ True if point is inside the polygon.
59
+ """
60
+ n = len(self.vertices)
61
+ inside = False
62
+
63
+ j = n - 1
64
+ for i in range(n):
65
+ xi, yi = self.vertices[i]
66
+ xj, yj = self.vertices[j]
67
+
68
+ if ((yi > y) != (yj > y)) and (x < (xj - xi) * (y - yi) / (yj - yi) + xi):
69
+ inside = not inside
70
+ j = i
71
+
72
+ return inside
73
+
74
+
75
+ @dataclass
76
+ class Mask:
77
+ """Mask definition for waveform testing.
78
+
79
+ A mask consists of one or more regions that define pass/fail criteria
80
+ for waveform data.
81
+
82
+ Attributes:
83
+ regions: List of MaskRegion polygons.
84
+ name: Name of the mask.
85
+ x_unit: Unit for X axis (e.g., "UI", "ns", "samples").
86
+ y_unit: Unit for Y axis (e.g., "V", "mV", "normalized").
87
+ description: Optional description.
88
+ """
89
+
90
+ regions: list[MaskRegion] = field(default_factory=list)
91
+ name: str = "mask"
92
+ x_unit: str = "UI"
93
+ y_unit: str = "V"
94
+ description: str = ""
95
+
96
+ def add_region(
97
+ self,
98
+ vertices: list[tuple[float, float]],
99
+ region_type: Literal["violation", "boundary"] = "violation",
100
+ name: str = "",
101
+ ) -> None:
102
+ """Add a region to the mask.
103
+
104
+ Args:
105
+ vertices: List of (x, y) vertices.
106
+ region_type: "violation" or "boundary".
107
+ name: Optional region name.
108
+ """
109
+ self.regions.append(MaskRegion(vertices, region_type, name))
110
+
111
+
112
+ @dataclass
113
+ class MaskTestResult:
114
+ """Result of a mask test.
115
+
116
+ Attributes:
117
+ passed: True if all samples pass the mask test.
118
+ num_violations: Number of samples violating the mask.
119
+ violation_rate: Fraction of samples violating the mask.
120
+ violation_points: List of (x, y) coordinates that violated.
121
+ violations_by_region: Count of violations per region.
122
+ margin: Estimated margin to mask boundary.
123
+ """
124
+
125
+ passed: bool
126
+ num_violations: int
127
+ violation_rate: float
128
+ violation_points: list[tuple[float, float]] = field(default_factory=list)
129
+ violations_by_region: dict[str, int] = field(default_factory=dict)
130
+ margin: float | None = None
131
+
132
+
133
+ def create_mask(
134
+ regions: list[dict], # type: ignore[type-arg]
135
+ *,
136
+ name: str = "custom_mask",
137
+ x_unit: str = "samples",
138
+ y_unit: str = "V",
139
+ ) -> Mask:
140
+ """Create a mask from region definitions.
141
+
142
+ Args:
143
+ regions: List of region dicts with 'vertices' and optional
144
+ 'type' and 'name' keys.
145
+ name: Mask name.
146
+ x_unit: X axis unit.
147
+ y_unit: Y axis unit.
148
+
149
+ Returns:
150
+ Mask instance.
151
+
152
+ Example:
153
+ >>> mask = create_mask([
154
+ ... {"vertices": [(0, 0.5), (0.5, 0.5), (0.5, -0.5), (0, -0.5)],
155
+ ... "type": "violation", "name": "center"}
156
+ ... ])
157
+ """
158
+ mask = Mask(name=name, x_unit=x_unit, y_unit=y_unit)
159
+
160
+ for region in regions:
161
+ vertices = region["vertices"]
162
+ region_type = region.get("type", "violation")
163
+ region_name = region.get("name", "")
164
+ mask.add_region(vertices, region_type, region_name)
165
+
166
+ return mask
167
+
168
+
169
+ def eye_mask(
170
+ eye_width: float = 0.5,
171
+ eye_height: float = 0.4,
172
+ center_height: float = 0.3,
173
+ *,
174
+ x_margin: float = 0.0,
175
+ y_margin: float = 0.1,
176
+ unit_interval: float = 1.0,
177
+ amplitude: float = 1.0,
178
+ ) -> Mask:
179
+ """Create a standard eye diagram mask.
180
+
181
+ Creates a hexagonal eye mask with center violation region and
182
+ optional boundary regions based on eye opening parameters.
183
+
184
+ Args:
185
+ eye_width: Width of eye opening (fraction of UI).
186
+ eye_height: Height of eye opening (fraction of amplitude).
187
+ center_height: Height of center violation region.
188
+ x_margin: X margin for boundary (fraction of UI). Reserved for future use.
189
+ y_margin: Y margin for boundary (fraction of amplitude).
190
+ unit_interval: Duration of unit interval.
191
+ amplitude: Signal amplitude.
192
+
193
+ Returns:
194
+ Mask for eye diagram testing.
195
+
196
+ Example:
197
+ >>> mask = eye_mask(0.5, 0.4) # Standard 50% width, 40% height
198
+ >>> # Creates violation region in center of eye
199
+ """
200
+ mask = Mask(
201
+ name="eye_mask",
202
+ x_unit="UI",
203
+ y_unit="normalized",
204
+ description=f"Eye mask: {eye_width * 100:.0f}% width, {eye_height * 100:.0f}% height",
205
+ )
206
+
207
+ # Scale parameters
208
+ ui = unit_interval
209
+ amp = amplitude
210
+
211
+ # Center violation region (hexagonal)
212
+ # Points arranged clockwise from left
213
+ center_width = eye_width * ui
214
+ center_top = eye_height * amp / 2
215
+ center_bottom = -eye_height * amp / 2
216
+ mid_width = center_width * 0.7 # Narrower at top/bottom
217
+
218
+ center_vertices = [
219
+ (-center_width / 2, 0), # Left
220
+ (-mid_width / 2, center_top), # Upper left
221
+ (mid_width / 2, center_top), # Upper right
222
+ (center_width / 2, 0), # Right
223
+ (mid_width / 2, center_bottom), # Lower right
224
+ (-mid_width / 2, center_bottom), # Lower left
225
+ ]
226
+ mask.add_region(center_vertices, "violation", "eye_center")
227
+
228
+ # Top violation region (above eye)
229
+ top_y = amp / 2 + y_margin * amp
230
+ top_vertices = [
231
+ (-ui / 2, center_top + center_height * amp),
232
+ (ui / 2, center_top + center_height * amp),
233
+ (ui / 2, top_y),
234
+ (-ui / 2, top_y),
235
+ ]
236
+ mask.add_region(top_vertices, "violation", "top")
237
+
238
+ # Bottom violation region (below eye)
239
+ bottom_y = -amp / 2 - y_margin * amp
240
+ bottom_vertices = [
241
+ (-ui / 2, bottom_y),
242
+ (ui / 2, bottom_y),
243
+ (ui / 2, center_bottom - center_height * amp),
244
+ (-ui / 2, center_bottom - center_height * amp),
245
+ ]
246
+ mask.add_region(bottom_vertices, "violation", "bottom")
247
+
248
+ return mask
249
+
250
+
251
+ def mask_test(
252
+ trace: WaveformTrace,
253
+ mask: Mask,
254
+ *,
255
+ x_data: NDArray[np.floating[Any]] | None = None,
256
+ normalize: bool = True,
257
+ sample_rate: float | None = None,
258
+ ) -> MaskTestResult:
259
+ """Test waveform against a mask.
260
+
261
+ Checks if any samples of the waveform violate the mask regions.
262
+
263
+ Args:
264
+ trace: Input waveform trace.
265
+ mask: Mask to test against.
266
+ x_data: X coordinates for each sample (if different from time).
267
+ normalize: Normalize Y data to [-1, 1] range.
268
+ sample_rate: Sample rate override.
269
+
270
+ Returns:
271
+ MaskTestResult with pass/fail status and violation details.
272
+
273
+ Example:
274
+ >>> result = mask_test(eye_trace, mask)
275
+ >>> print(f"Violations: {result.num_violations}")
276
+ """
277
+ # Get Y data
278
+ y_data = trace.data.astype(np.float64)
279
+
280
+ # Get or create X data
281
+ if x_data is None:
282
+ x_data = np.arange(len(y_data), dtype=np.float64)
283
+
284
+ # Normalize if requested
285
+ if normalize:
286
+ y_min, y_max = np.min(y_data), np.max(y_data)
287
+ if y_max - y_min > 0:
288
+ y_data = 2 * (y_data - y_min) / (y_max - y_min) - 1
289
+
290
+ # Test each point against mask regions
291
+ violations: list[tuple[float, float]] = []
292
+ violations_by_region: dict[str, int] = {}
293
+
294
+ for region in mask.regions:
295
+ region_name = region.name or "unnamed"
296
+ violations_by_region[region_name] = 0
297
+
298
+ if region.region_type == "violation":
299
+ # Check if points are inside violation region
300
+ for i, (x, y) in enumerate(zip(x_data, y_data, strict=False)): # noqa: B007
301
+ if region.contains_point(float(x), float(y)):
302
+ violations.append((float(x), float(y)))
303
+ violations_by_region[region_name] += 1
304
+
305
+ elif region.region_type == "boundary":
306
+ # Check if points are outside boundary region
307
+ for i, (x, y) in enumerate(zip(x_data, y_data, strict=False)): # noqa: B007
308
+ if not region.contains_point(float(x), float(y)):
309
+ violations.append((float(x), float(y)))
310
+ violations_by_region[region_name] += 1
311
+
312
+ # Remove duplicates
313
+ unique_violations = list(set(violations))
314
+ num_violations = len(unique_violations)
315
+ violation_rate = num_violations / len(y_data) if len(y_data) > 0 else 0.0
316
+
317
+ # Estimate margin (simplified - distance to nearest mask edge)
318
+ margin = None
319
+ if num_violations == 0 and mask.regions:
320
+ # Find minimum distance to any violation region
321
+ min_dist = float("inf")
322
+ for region in mask.regions:
323
+ if region.region_type == "violation":
324
+ for x, y in zip(x_data, y_data, strict=False):
325
+ for i in range(len(region.vertices)):
326
+ x1, y1 = region.vertices[i]
327
+ x2, y2 = region.vertices[(i + 1) % len(region.vertices)]
328
+ # Distance to line segment
329
+ dist = _point_to_segment_distance(x, y, x1, y1, x2, y2)
330
+ min_dist = min(min_dist, dist)
331
+ margin = min_dist if min_dist != float("inf") else None
332
+
333
+ return MaskTestResult(
334
+ passed=num_violations == 0,
335
+ num_violations=num_violations,
336
+ violation_rate=violation_rate,
337
+ violation_points=unique_violations,
338
+ violations_by_region=violations_by_region,
339
+ margin=margin,
340
+ )
341
+
342
+
343
+ def _point_to_segment_distance(
344
+ px: float, py: float, x1: float, y1: float, x2: float, y2: float
345
+ ) -> float:
346
+ """Calculate distance from point to line segment."""
347
+ dx = x2 - x1
348
+ dy = y2 - y1
349
+ length_sq = dx * dx + dy * dy
350
+
351
+ if length_sq == 0:
352
+ # Segment is a point
353
+ return np.sqrt((px - x1) ** 2 + (py - y1) ** 2) # type: ignore[no-any-return]
354
+
355
+ # Project point onto line
356
+ t = max(0, min(1, ((px - x1) * dx + (py - y1) * dy) / length_sq))
357
+ proj_x = x1 + t * dx
358
+ proj_y = y1 + t * dy
359
+
360
+ return float(np.sqrt((px - proj_x) ** 2 + (py - proj_y) ** 2))
361
+
362
+
363
+ def eye_diagram_mask_test(
364
+ eye_data: NDArray[np.floating[Any]],
365
+ *,
366
+ eye_width: float = 0.5,
367
+ eye_height: float = 0.4,
368
+ unit_interval: float = 1.0,
369
+ ) -> MaskTestResult:
370
+ """Specialized eye diagram mask test.
371
+
372
+ Tests 2D eye diagram data against a standard eye mask.
373
+
374
+ Args:
375
+ eye_data: 2D array of shape (num_traces, samples_per_ui).
376
+ eye_width: Eye opening width (fraction of UI).
377
+ eye_height: Eye opening height (fraction of amplitude).
378
+ unit_interval: Duration of unit interval in samples.
379
+
380
+ Returns:
381
+ MaskTestResult for the eye diagram.
382
+
383
+ Raises:
384
+ AnalysisError: If eye data is not a 2D array.
385
+ """
386
+ if eye_data.ndim != 2:
387
+ raise AnalysisError("Eye data must be 2D array (num_traces x samples_per_ui)")
388
+
389
+ num_traces, samples_per_ui = eye_data.shape
390
+
391
+ # Create mask
392
+ mask = eye_mask(
393
+ eye_width=eye_width,
394
+ eye_height=eye_height,
395
+ unit_interval=unit_interval,
396
+ amplitude=1.0,
397
+ )
398
+
399
+ # Normalize data
400
+ flat_data = eye_data.flatten()
401
+ y_min, y_max = np.min(flat_data), np.max(flat_data)
402
+ normalized = 2 * (eye_data - y_min) / (y_max - y_min) - 1 if y_max - y_min > 0 else eye_data
403
+
404
+ # Create X coordinates (relative to UI center)
405
+ x_coords = np.linspace(-0.5, 0.5, samples_per_ui) * unit_interval
406
+
407
+ # Test all traces
408
+ violations: list[tuple[float, float]] = []
409
+ violations_by_region: dict[str, int] = {r.name or "unnamed": 0 for r in mask.regions}
410
+
411
+ for trace_idx in range(num_traces):
412
+ for sample_idx in range(samples_per_ui):
413
+ x = float(x_coords[sample_idx])
414
+ y = float(normalized[trace_idx, sample_idx])
415
+
416
+ for region in mask.regions:
417
+ if region.region_type == "violation":
418
+ if region.contains_point(x, y):
419
+ violations.append((x, y))
420
+ region_name = region.name or "unnamed"
421
+ violations_by_region[region_name] += 1
422
+
423
+ unique_violations = list(set(violations))
424
+ num_violations = len(unique_violations)
425
+ total_points = num_traces * samples_per_ui
426
+
427
+ return MaskTestResult(
428
+ passed=num_violations == 0,
429
+ num_violations=num_violations,
430
+ violation_rate=num_violations / total_points if total_points > 0 else 0,
431
+ violation_points=unique_violations,
432
+ violations_by_region=violations_by_region,
433
+ margin=None,
434
+ )
@@ -0,0 +1,30 @@
1
+ """Intelligent trace comparison and difference detection.
2
+
3
+ This module provides automatic trace comparison with alignment, difference
4
+ detection, and plain-language explanations. It is a wrapper around the
5
+ discovery.comparison module to maintain API compatibility.
6
+
7
+
8
+ Example:
9
+ >>> from oscura.comparison import compare_traces
10
+ >>> diff = compare_traces(trace1, trace2)
11
+ >>> for d in diff.differences:
12
+ ... print(f"{d.category}: {d.description}")
13
+
14
+ References:
15
+ Oscura Auto-Discovery Specification
16
+ Phase 34 Task-245
17
+ """
18
+
19
+ # Re-export everything from discovery.comparison
20
+ from oscura.discovery.comparison import (
21
+ Difference,
22
+ TraceDiff,
23
+ compare_traces,
24
+ )
25
+
26
+ __all__ = [
27
+ "Difference",
28
+ "TraceDiff",
29
+ "compare_traces",
30
+ ]