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,484 @@
1
+ """S-Parameter handling and Touchstone file support.
2
+
3
+ This module provides Touchstone file loading and S-parameter
4
+ calculations including return loss and insertion loss.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.analyzers.signal_integrity.sparams import load_touchstone
9
+ >>> s_params = load_touchstone("cable.s2p")
10
+ >>> rl = return_loss(s_params, frequency=1e9)
11
+
12
+ References:
13
+ Touchstone 2.0 File Format Specification
14
+ IEEE 370-2020: Standard for Electrical Characterization
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import contextlib
20
+ import re
21
+ from dataclasses import dataclass, field
22
+ from pathlib import Path
23
+ from typing import TYPE_CHECKING
24
+
25
+ import numpy as np
26
+
27
+ from oscura.core.exceptions import FormatError, LoaderError
28
+
29
+ if TYPE_CHECKING:
30
+ from numpy.typing import NDArray
31
+
32
+
33
+ @dataclass
34
+ class SParameterData:
35
+ """S-parameter data from Touchstone file.
36
+
37
+ Attributes:
38
+ frequencies: Frequency points in Hz.
39
+ s_matrix: Complex S-parameter matrix (n_freq x n_ports x n_ports).
40
+ n_ports: Number of ports.
41
+ z0: Reference impedance in Ohms.
42
+ format: Original format ("db", "ma", "ri").
43
+ source_file: Path to source file.
44
+ comments: Comments from file header.
45
+ """
46
+
47
+ frequencies: NDArray[np.float64]
48
+ s_matrix: NDArray[np.complex128]
49
+ n_ports: int
50
+ z0: float = 50.0
51
+ format: str = "ri"
52
+ source_file: str | None = None
53
+ comments: list[str] = field(default_factory=list)
54
+
55
+ def __post_init__(self) -> None:
56
+ """Validate S-parameter data."""
57
+ if len(self.frequencies) == 0:
58
+ raise ValueError("frequencies cannot be empty")
59
+
60
+ expected_shape = (len(self.frequencies), self.n_ports, self.n_ports)
61
+ if self.s_matrix.shape != expected_shape:
62
+ raise ValueError(
63
+ f"s_matrix shape {self.s_matrix.shape} does not match expected {expected_shape}"
64
+ )
65
+
66
+ def get_s(
67
+ self,
68
+ i: int,
69
+ j: int,
70
+ frequency: float | None = None,
71
+ ) -> complex | NDArray[np.complex128]:
72
+ """Get S-parameter Sij.
73
+
74
+ Args:
75
+ i: Output port (1-indexed).
76
+ j: Input port (1-indexed).
77
+ frequency: Frequency for interpolation (None = all).
78
+
79
+ Returns:
80
+ Complex S-parameter value(s).
81
+ """
82
+ # Convert to 0-indexed
83
+ i_idx = i - 1
84
+ j_idx = j - 1
85
+
86
+ if frequency is None:
87
+ return self.s_matrix[:, i_idx, j_idx]
88
+
89
+ # Interpolate
90
+ return np.interp(
91
+ frequency,
92
+ self.frequencies,
93
+ self.s_matrix[:, i_idx, j_idx],
94
+ )
95
+
96
+
97
+ def load_touchstone(path: str | Path) -> SParameterData:
98
+ """Load S-parameter data from Touchstone file.
99
+
100
+ Supports .s1p through .s8p formats and both Touchstone 1.0
101
+ and 2.0 file formats.
102
+
103
+ Args:
104
+ path: Path to Touchstone file.
105
+
106
+ Returns:
107
+ SParameterData with loaded S-parameters.
108
+
109
+ Raises:
110
+ LoaderError: If file cannot be read.
111
+ FormatError: If file format is invalid.
112
+
113
+ Example:
114
+ >>> s_params = load_touchstone("cable.s2p")
115
+ >>> print(f"Loaded {s_params.n_ports}-port, {len(s_params.frequencies)} points")
116
+
117
+ References:
118
+ Touchstone 2.0 File Format Specification
119
+ """
120
+ path = Path(path)
121
+
122
+ if not path.exists():
123
+ raise LoaderError(f"File not found: {path}")
124
+
125
+ # Determine number of ports from extension
126
+ suffix = path.suffix.lower()
127
+ match = re.match(r"\.s(\d+)p", suffix)
128
+ if not match:
129
+ raise FormatError(f"Unsupported file extension: {suffix}")
130
+
131
+ n_ports = int(match.group(1))
132
+
133
+ try:
134
+ with open(path) as f:
135
+ lines = f.readlines()
136
+ except Exception as e:
137
+ raise LoaderError(f"Failed to read file: {e}") # noqa: B904
138
+
139
+ return _parse_touchstone(lines, n_ports, str(path))
140
+
141
+
142
+ def _parse_touchstone(
143
+ lines: list[str],
144
+ n_ports: int,
145
+ source_file: str,
146
+ ) -> SParameterData:
147
+ """Parse Touchstone file content.
148
+
149
+ Args:
150
+ lines: File lines.
151
+ n_ports: Number of ports.
152
+ source_file: Source file path.
153
+
154
+ Returns:
155
+ Parsed SParameterData.
156
+
157
+ Raises:
158
+ FormatError: If file format is invalid.
159
+ """
160
+ comments = []
161
+ option_line = None
162
+ data_lines = []
163
+
164
+ for line in lines:
165
+ line = line.strip()
166
+
167
+ if not line:
168
+ continue
169
+
170
+ if line.startswith("!"):
171
+ comments.append(line[1:].strip())
172
+ elif line.startswith("#"):
173
+ option_line = line
174
+ else:
175
+ data_lines.append(line)
176
+
177
+ # Parse option line
178
+ freq_unit = 1e9 # Default GHz
179
+ format_type = "ma" # Default MA (magnitude/angle)
180
+ z0 = 50.0
181
+
182
+ if option_line:
183
+ option_line = option_line.lower()
184
+ parts = option_line.split()
185
+
186
+ for i, part in enumerate(parts):
187
+ if part in ("hz", "khz", "mhz", "ghz"):
188
+ freq_unit = {
189
+ "hz": 1.0,
190
+ "khz": 1e3,
191
+ "mhz": 1e6,
192
+ "ghz": 1e9,
193
+ }[part]
194
+ elif part in ("db", "ma", "ri"):
195
+ format_type = part
196
+ elif part == "r":
197
+ # Reference impedance follows
198
+ if i + 1 < len(parts):
199
+ with contextlib.suppress(ValueError):
200
+ z0 = float(parts[i + 1])
201
+
202
+ # Parse data
203
+ frequencies = []
204
+ s_data = []
205
+
206
+ # Number of S-parameters per frequency
207
+ n_s_params = n_ports * n_ports
208
+
209
+ i = 0
210
+ while i < len(data_lines):
211
+ # First line has frequency and first S-parameters
212
+ parts = data_lines[i].split()
213
+
214
+ if len(parts) < 1:
215
+ i += 1
216
+ continue
217
+
218
+ freq = float(parts[0]) * freq_unit
219
+ frequencies.append(freq)
220
+
221
+ # Collect all S-parameter values for this frequency
222
+ s_values = []
223
+
224
+ # Add values from first line
225
+ for j in range(1, len(parts), 2):
226
+ if j + 1 < len(parts):
227
+ val1 = float(parts[j])
228
+ val2 = float(parts[j + 1])
229
+ s_values.append((val1, val2))
230
+
231
+ i += 1
232
+
233
+ # Continue collecting from subsequent lines if needed
234
+ while len(s_values) < n_s_params and i < len(data_lines):
235
+ parts = data_lines[i].split()
236
+
237
+ # Check if this is a new frequency (has odd number of values)
238
+ try:
239
+ float(parts[0])
240
+ if len(parts) % 2 == 1:
241
+ break # New frequency line
242
+ except (ValueError, IndexError):
243
+ pass
244
+
245
+ for j in range(0, len(parts), 2):
246
+ if j + 1 < len(parts):
247
+ val1 = float(parts[j])
248
+ val2 = float(parts[j + 1])
249
+ s_values.append((val1, val2))
250
+
251
+ i += 1
252
+
253
+ # Convert to complex based on format
254
+ s_complex = []
255
+ for val1, val2 in s_values:
256
+ if format_type == "ri":
257
+ # Real/Imaginary
258
+ s_complex.append(complex(val1, val2))
259
+ elif format_type == "ma":
260
+ # Magnitude/Angle (degrees)
261
+ mag = val1
262
+ angle_rad = np.radians(val2)
263
+ s_complex.append(mag * np.exp(1j * angle_rad))
264
+ elif format_type == "db":
265
+ # dB/Angle (degrees)
266
+ mag = 10 ** (val1 / 20)
267
+ angle_rad = np.radians(val2)
268
+ s_complex.append(mag * np.exp(1j * angle_rad))
269
+
270
+ # Reshape into matrix
271
+ if len(s_complex) == n_s_params:
272
+ s_matrix = np.array(s_complex).reshape(n_ports, n_ports)
273
+ s_data.append(s_matrix)
274
+
275
+ if len(frequencies) == 0:
276
+ raise FormatError("No valid frequency points found")
277
+
278
+ frequencies_arr = np.array(frequencies, dtype=np.float64)
279
+ s_matrix_arr = np.array(s_data, dtype=np.complex128)
280
+
281
+ return SParameterData(
282
+ frequencies=frequencies_arr,
283
+ s_matrix=s_matrix_arr,
284
+ n_ports=n_ports,
285
+ z0=z0,
286
+ format=format_type,
287
+ source_file=source_file,
288
+ comments=comments,
289
+ )
290
+
291
+
292
+ def return_loss(
293
+ s_params: SParameterData,
294
+ frequency: float | None = None,
295
+ *,
296
+ port: int = 1,
297
+ ) -> float | NDArray[np.float64]:
298
+ """Calculate return loss from S-parameters.
299
+
300
+ Return loss = -20 * log10(|S11|)
301
+
302
+ Args:
303
+ s_params: S-parameter data.
304
+ frequency: Frequency in Hz (None = all frequencies).
305
+ port: Port number (1-indexed).
306
+
307
+ Returns:
308
+ Return loss in dB.
309
+
310
+ Example:
311
+ >>> rl = return_loss(s_params, frequency=1e9)
312
+ >>> print(f"Return loss: {rl:.1f} dB")
313
+
314
+ References:
315
+ IEEE 370-2020 Section 5.2
316
+ """
317
+ s11 = s_params.get_s(port, port, frequency)
318
+ magnitude = np.abs(s11)
319
+
320
+ # Avoid log(0)
321
+ magnitude = np.maximum(magnitude, 1e-10)
322
+
323
+ rl = -20 * np.log10(magnitude)
324
+
325
+ if isinstance(rl, np.ndarray):
326
+ return rl
327
+ return float(rl)
328
+
329
+
330
+ def insertion_loss(
331
+ s_params: SParameterData,
332
+ frequency: float | None = None,
333
+ *,
334
+ input_port: int = 1,
335
+ output_port: int = 2,
336
+ ) -> float | NDArray[np.float64]:
337
+ """Calculate insertion loss from S-parameters.
338
+
339
+ Insertion loss = -20 * log10(|S21|)
340
+
341
+ Args:
342
+ s_params: S-parameter data.
343
+ frequency: Frequency in Hz (None = all frequencies).
344
+ input_port: Input port number (1-indexed).
345
+ output_port: Output port number (1-indexed).
346
+
347
+ Returns:
348
+ Insertion loss in dB.
349
+
350
+ Example:
351
+ >>> il = insertion_loss(s_params, frequency=1e9)
352
+ >>> print(f"Insertion loss: {il:.2f} dB")
353
+
354
+ References:
355
+ IEEE 370-2020 Section 5.3
356
+ """
357
+ s21 = s_params.get_s(output_port, input_port, frequency)
358
+ magnitude = np.abs(s21)
359
+
360
+ # Avoid log(0)
361
+ magnitude = np.maximum(magnitude, 1e-10)
362
+
363
+ il = -20 * np.log10(magnitude)
364
+
365
+ if isinstance(il, np.ndarray):
366
+ return il
367
+ return float(il)
368
+
369
+
370
+ def s_to_abcd(
371
+ s_params: SParameterData,
372
+ frequency_idx: int | None = None,
373
+ ) -> NDArray[np.complex128]:
374
+ """Convert S-parameters to ABCD (chain) parameters.
375
+
376
+ Used for cascading networks.
377
+
378
+ Args:
379
+ s_params: S-parameter data (2-port only).
380
+ frequency_idx: Index for specific frequency (None = all).
381
+
382
+ Returns:
383
+ ABCD matrix (2x2) or array of matrices.
384
+
385
+ Raises:
386
+ ValueError: If s_params is not a 2-port network.
387
+
388
+ Example:
389
+ >>> abcd = s_to_abcd(s_params)
390
+
391
+ References:
392
+ Pozar, "Microwave Engineering", Chapter 4
393
+ """
394
+ if s_params.n_ports != 2:
395
+ raise ValueError("ABCD conversion only supported for 2-port networks")
396
+
397
+ z0 = s_params.z0
398
+
399
+ if frequency_idx is not None:
400
+ s = s_params.s_matrix[frequency_idx]
401
+ return _s_to_abcd_single(s, z0)
402
+
403
+ # Convert all frequencies
404
+ n_freq = len(s_params.frequencies)
405
+ abcd = np.zeros((n_freq, 2, 2), dtype=np.complex128)
406
+
407
+ for i in range(n_freq):
408
+ abcd[i] = _s_to_abcd_single(s_params.s_matrix[i], z0)
409
+
410
+ return abcd
411
+
412
+
413
+ def _s_to_abcd_single(s: NDArray[np.complex128], z0: float) -> NDArray[np.complex128]:
414
+ """Convert single frequency S-matrix to ABCD."""
415
+ s11, s12, s21, s22 = s[0, 0], s[0, 1], s[1, 0], s[1, 1]
416
+
417
+ denominator = 2 * s21
418
+
419
+ if abs(denominator) < 1e-12:
420
+ # Singular - return identity-ish
421
+ return np.array([[1, 0], [0, 1]], dtype=np.complex128)
422
+
423
+ A = ((1 + s11) * (1 - s22) + s12 * s21) / denominator
424
+ B = z0 * ((1 + s11) * (1 + s22) - s12 * s21) / denominator
425
+ C = ((1 - s11) * (1 - s22) - s12 * s21) / (z0 * denominator)
426
+ D = ((1 - s11) * (1 + s22) + s12 * s21) / denominator
427
+
428
+ return np.array([[A, B], [C, D]], dtype=np.complex128)
429
+
430
+
431
+ def abcd_to_s(
432
+ abcd: NDArray[np.complex128],
433
+ z0: float = 50.0,
434
+ ) -> NDArray[np.complex128]:
435
+ """Convert ABCD parameters to S-parameters.
436
+
437
+ Args:
438
+ abcd: ABCD matrix (2x2) or array of matrices.
439
+ z0: Reference impedance.
440
+
441
+ Returns:
442
+ S-parameter matrix.
443
+
444
+ References:
445
+ Pozar, "Microwave Engineering", Chapter 4
446
+ """
447
+ if abcd.ndim == 2:
448
+ return _abcd_to_s_single(abcd, z0)
449
+
450
+ # Handle array of matrices
451
+ n_freq = abcd.shape[0]
452
+ s = np.zeros((n_freq, 2, 2), dtype=np.complex128)
453
+
454
+ for i in range(n_freq):
455
+ s[i] = _abcd_to_s_single(abcd[i], z0)
456
+
457
+ return s
458
+
459
+
460
+ def _abcd_to_s_single(abcd: NDArray[np.complex128], z0: float) -> NDArray[np.complex128]:
461
+ """Convert single ABCD matrix to S-parameters."""
462
+ A, B, C, D = abcd[0, 0], abcd[0, 1], abcd[1, 0], abcd[1, 1]
463
+
464
+ denominator = A + B / z0 + C * z0 + D
465
+
466
+ if abs(denominator) < 1e-12:
467
+ return np.zeros((2, 2), dtype=np.complex128)
468
+
469
+ S11 = (A + B / z0 - C * z0 - D) / denominator
470
+ S12 = 2 * (A * D - B * C) / denominator
471
+ S21 = 2 / denominator
472
+ S22 = (-A + B / z0 - C * z0 + D) / denominator
473
+
474
+ return np.array([[S11, S12], [S21, S22]], dtype=np.complex128)
475
+
476
+
477
+ __all__ = [
478
+ "SParameterData",
479
+ "abcd_to_s",
480
+ "insertion_loss",
481
+ "load_touchstone",
482
+ "return_loss",
483
+ "s_to_abcd",
484
+ ]
@@ -0,0 +1,53 @@
1
+ """Spectral analysis module.
2
+
3
+ This module re-exports spectral analysis functions from the waveform package
4
+ for convenient access.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.analyzers.spectral import fft, psd, thd, snr
9
+ >>> freq, mag = fft(trace)
10
+ >>> thd_db = thd(trace)
11
+ """
12
+
13
+ from oscura.analyzers.waveform.spectral import (
14
+ bartlett_psd,
15
+ cwt,
16
+ dwt,
17
+ enob,
18
+ fft,
19
+ fft_chunked,
20
+ hilbert_transform,
21
+ idwt,
22
+ mfcc,
23
+ periodogram,
24
+ psd,
25
+ psd_chunked,
26
+ sfdr,
27
+ sinad,
28
+ snr,
29
+ spectrogram,
30
+ spectrogram_chunked,
31
+ thd,
32
+ )
33
+
34
+ __all__ = [
35
+ "bartlett_psd",
36
+ "cwt",
37
+ "dwt",
38
+ "enob",
39
+ "fft",
40
+ "fft_chunked",
41
+ "hilbert_transform",
42
+ "idwt",
43
+ "mfcc",
44
+ "periodogram",
45
+ "psd",
46
+ "psd_chunked",
47
+ "sfdr",
48
+ "sinad",
49
+ "snr",
50
+ "spectrogram",
51
+ "spectrogram_chunked",
52
+ "thd",
53
+ ]