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,598 @@
1
+ """Reactive component extraction for TraceKit.
2
+
3
+ This module provides capacitance and inductance measurement from
4
+ waveform data, including parasitic extraction.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.component import measure_capacitance, measure_inductance
9
+ >>> C = measure_capacitance(voltage_trace, current_trace)
10
+ >>> L = measure_inductance(voltage_trace, current_trace)
11
+
12
+ References:
13
+ IEEE 181-2011: Standard for Transitional Waveform Definitions
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from dataclasses import dataclass, field
19
+ from typing import TYPE_CHECKING, Any, Literal, cast
20
+
21
+ import numpy as np
22
+
23
+ from oscura.core.exceptions import AnalysisError, InsufficientDataError
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 CapacitanceMeasurement:
33
+ """Result of capacitance measurement.
34
+
35
+ Attributes:
36
+ capacitance: Measured capacitance in Farads.
37
+ esr: Equivalent Series Resistance in ohms.
38
+ method: Measurement method used.
39
+ confidence: Confidence in measurement (0-1).
40
+ statistics: Additional measurement statistics.
41
+ """
42
+
43
+ capacitance: float
44
+ esr: float = 0.0
45
+ method: str = ""
46
+ confidence: float = 1.0
47
+ statistics: dict = field(default_factory=dict) # type: ignore[type-arg]
48
+
49
+
50
+ @dataclass
51
+ class InductanceMeasurement:
52
+ """Result of inductance measurement.
53
+
54
+ Attributes:
55
+ inductance: Measured inductance in Henrys.
56
+ dcr: DC Resistance in ohms.
57
+ q_factor: Quality factor at measurement frequency.
58
+ method: Measurement method used.
59
+ confidence: Confidence in measurement (0-1).
60
+ statistics: Additional measurement statistics.
61
+ """
62
+
63
+ inductance: float
64
+ dcr: float = 0.0
65
+ q_factor: float | None = None
66
+ method: str = ""
67
+ confidence: float = 1.0
68
+ statistics: dict = field(default_factory=dict) # type: ignore[type-arg]
69
+
70
+
71
+ @dataclass
72
+ class ParasiticExtraction:
73
+ """Result of parasitic parameter extraction.
74
+
75
+ Attributes:
76
+ capacitance: Parasitic capacitance in Farads.
77
+ inductance: Parasitic inductance in Henrys.
78
+ resistance: Parasitic resistance in ohms.
79
+ model_type: Equivalent circuit model type.
80
+ resonant_freq: Self-resonant frequency (if applicable).
81
+ fit_quality: Quality of model fit (R-squared).
82
+ """
83
+
84
+ capacitance: float
85
+ inductance: float
86
+ resistance: float
87
+ model_type: Literal["series_RLC", "parallel_RLC", "pi", "tee"]
88
+ resonant_freq: float | None = None
89
+ fit_quality: float = 0.0
90
+
91
+
92
+ def measure_capacitance(
93
+ voltage_trace: WaveformTrace,
94
+ current_trace: WaveformTrace | None = None,
95
+ *,
96
+ method: Literal["charge", "slope", "frequency"] = "charge",
97
+ resistance: float | None = None,
98
+ ) -> CapacitanceMeasurement:
99
+ """Measure capacitance from voltage/current waveforms.
100
+
101
+ Calculates capacitance using the relationship C = Q/V or C = I/(dV/dt).
102
+
103
+ Args:
104
+ voltage_trace: Voltage waveform across capacitor.
105
+ current_trace: Current waveform through capacitor (optional for
106
+ some methods).
107
+ method: Measurement method:
108
+ - "charge": C = integral(I*dt) / delta_V
109
+ - "slope": C = I / (dV/dt)
110
+ - "frequency": Extract from RC time constant
111
+ resistance: Known resistance for frequency method.
112
+
113
+ Returns:
114
+ CapacitanceMeasurement with capacitance value.
115
+
116
+ Raises:
117
+ AnalysisError: If measurement conditions are not met.
118
+ InsufficientDataError: If insufficient samples are provided.
119
+
120
+ Example:
121
+ >>> C = measure_capacitance(voltage, current)
122
+ >>> print(f"C = {C.capacitance * 1e12:.1f} pF")
123
+
124
+ References:
125
+ COMP-002
126
+ """
127
+ voltage = voltage_trace.data.astype(np.float64)
128
+ sample_rate = voltage_trace.metadata.sample_rate
129
+ dt = 1.0 / sample_rate
130
+
131
+ if len(voltage) < 10:
132
+ raise InsufficientDataError(
133
+ "Capacitance measurement requires at least 10 samples",
134
+ required=10,
135
+ available=len(voltage),
136
+ analysis_type="capacitance",
137
+ )
138
+
139
+ if method == "charge" and current_trace is not None:
140
+ # C = Q / V = integral(I*dt) / delta_V
141
+ current = current_trace.data.astype(np.float64)
142
+ min_len = min(len(voltage), len(current))
143
+ voltage = voltage[:min_len]
144
+ current = current[:min_len]
145
+
146
+ # Integrate current to get charge
147
+ charge = np.cumsum(current) * dt
148
+ delta_v = np.max(voltage) - np.min(voltage)
149
+
150
+ if delta_v > 1e-10:
151
+ delta_q = np.max(charge) - np.min(charge)
152
+ capacitance = delta_q / delta_v
153
+ else:
154
+ raise AnalysisError("Voltage change too small for capacitance measurement")
155
+
156
+ # Estimate ESR from phase relationship
157
+ esr = _estimate_esr(voltage, current, sample_rate)
158
+
159
+ return CapacitanceMeasurement(
160
+ capacitance=float(abs(capacitance)),
161
+ esr=esr,
162
+ method="charge_integration",
163
+ confidence=0.9,
164
+ statistics={
165
+ "delta_v": delta_v,
166
+ "delta_q": delta_q,
167
+ "num_samples": min_len,
168
+ },
169
+ )
170
+
171
+ elif method == "slope" and current_trace is not None:
172
+ # C = I / (dV/dt)
173
+ current = current_trace.data.astype(np.float64)
174
+ min_len = min(len(voltage), len(current))
175
+ voltage = voltage[:min_len]
176
+ current = current[:min_len]
177
+
178
+ # Calculate dV/dt
179
+ dv_dt = np.diff(voltage) / dt
180
+
181
+ # Find region where dV/dt is significant
182
+ significant_mask = np.abs(dv_dt) > np.max(np.abs(dv_dt)) * 0.1
183
+ if np.sum(significant_mask) < 5:
184
+ raise AnalysisError("Insufficient voltage slope for capacitance measurement")
185
+
186
+ # Use corresponding current values
187
+ current_for_slope = current[:-1][significant_mask]
188
+ dv_dt_significant = dv_dt[significant_mask]
189
+
190
+ # C = I / (dV/dt)
191
+ capacitance_values = current_for_slope / dv_dt_significant
192
+ capacitance = float(np.median(np.abs(capacitance_values)))
193
+
194
+ return CapacitanceMeasurement(
195
+ capacitance=capacitance,
196
+ method="slope",
197
+ confidence=0.85,
198
+ statistics={
199
+ "num_valid_points": int(np.sum(significant_mask)),
200
+ "capacitance_std": float(np.std(np.abs(capacitance_values))),
201
+ },
202
+ )
203
+
204
+ elif method == "frequency":
205
+ # Extract from RC time constant
206
+ if resistance is None:
207
+ raise AnalysisError("Resistance value required for frequency method")
208
+
209
+ # Find time constant from step response
210
+ tau = _extract_time_constant(voltage, sample_rate)
211
+
212
+ # C = tau / R
213
+ capacitance = tau / resistance
214
+
215
+ return CapacitanceMeasurement(
216
+ capacitance=float(capacitance),
217
+ method="time_constant",
218
+ confidence=0.8,
219
+ statistics={
220
+ "time_constant": tau,
221
+ "resistance": resistance,
222
+ },
223
+ )
224
+
225
+ else:
226
+ raise AnalysisError(f"Method '{method}' requires current_trace or resistance parameter")
227
+
228
+
229
+ def measure_inductance(
230
+ voltage_trace: WaveformTrace,
231
+ current_trace: WaveformTrace | None = None,
232
+ *,
233
+ method: Literal["flux", "slope", "frequency"] = "slope",
234
+ resistance: float | None = None,
235
+ ) -> InductanceMeasurement:
236
+ """Measure inductance from voltage/current waveforms.
237
+
238
+ Calculates inductance using the relationship V = L * dI/dt.
239
+
240
+ Args:
241
+ voltage_trace: Voltage waveform across inductor.
242
+ current_trace: Current waveform through inductor (optional for
243
+ some methods).
244
+ method: Measurement method:
245
+ - "flux": L = integral(V*dt) / delta_I
246
+ - "slope": L = V / (dI/dt)
247
+ - "frequency": Extract from RL time constant
248
+ resistance: Known resistance for frequency method.
249
+
250
+ Returns:
251
+ InductanceMeasurement with inductance value.
252
+
253
+ Raises:
254
+ AnalysisError: If measurement conditions are not met.
255
+ InsufficientDataError: If insufficient samples are provided.
256
+
257
+ Example:
258
+ >>> L = measure_inductance(voltage, current)
259
+ >>> print(f"L = {L.inductance * 1e6:.1f} uH")
260
+
261
+ References:
262
+ COMP-003
263
+ """
264
+ voltage = voltage_trace.data.astype(np.float64)
265
+ sample_rate = voltage_trace.metadata.sample_rate
266
+ dt = 1.0 / sample_rate
267
+
268
+ if len(voltage) < 10:
269
+ raise InsufficientDataError(
270
+ "Inductance measurement requires at least 10 samples",
271
+ required=10,
272
+ available=len(voltage),
273
+ analysis_type="inductance",
274
+ )
275
+
276
+ if method == "flux" and current_trace is not None:
277
+ # L = flux / I = integral(V*dt) / delta_I
278
+ current = current_trace.data.astype(np.float64)
279
+ min_len = min(len(voltage), len(current))
280
+ voltage = voltage[:min_len]
281
+ current = current[:min_len]
282
+
283
+ # Integrate voltage to get flux linkage
284
+ flux = np.cumsum(voltage) * dt
285
+ delta_i = np.max(current) - np.min(current)
286
+
287
+ if delta_i > 1e-10:
288
+ delta_flux = np.max(flux) - np.min(flux)
289
+ inductance = delta_flux / delta_i
290
+ else:
291
+ raise AnalysisError("Current change too small for inductance measurement")
292
+
293
+ # Estimate DCR from steady-state
294
+ dcr = _estimate_dcr(voltage, current)
295
+
296
+ return InductanceMeasurement(
297
+ inductance=float(abs(inductance)),
298
+ dcr=dcr,
299
+ method="flux_integration",
300
+ confidence=0.9,
301
+ statistics={
302
+ "delta_i": delta_i,
303
+ "delta_flux": delta_flux,
304
+ "num_samples": min_len,
305
+ },
306
+ )
307
+
308
+ elif method == "slope" and current_trace is not None:
309
+ # L = V / (dI/dt)
310
+ current = current_trace.data.astype(np.float64)
311
+ min_len = min(len(voltage), len(current))
312
+ voltage = voltage[:min_len]
313
+ current = current[:min_len]
314
+
315
+ # Calculate dI/dt
316
+ di_dt = np.diff(current) / dt
317
+
318
+ # Find region where dI/dt is significant
319
+ significant_mask = np.abs(di_dt) > np.max(np.abs(di_dt)) * 0.1
320
+ if np.sum(significant_mask) < 5:
321
+ raise AnalysisError("Insufficient current slope for inductance measurement")
322
+
323
+ # Use corresponding voltage values
324
+ voltage_for_slope = voltage[:-1][significant_mask]
325
+ di_dt_significant = di_dt[significant_mask]
326
+
327
+ # L = V / (dI/dt)
328
+ inductance_values = voltage_for_slope / di_dt_significant
329
+ inductance = float(np.median(np.abs(inductance_values)))
330
+
331
+ return InductanceMeasurement(
332
+ inductance=inductance,
333
+ method="slope",
334
+ confidence=0.85,
335
+ statistics={
336
+ "num_valid_points": int(np.sum(significant_mask)),
337
+ "inductance_std": float(np.std(np.abs(inductance_values))),
338
+ },
339
+ )
340
+
341
+ elif method == "frequency":
342
+ # Extract from RL time constant
343
+ if resistance is None:
344
+ raise AnalysisError("Resistance value required for frequency method")
345
+
346
+ # Find time constant from step response
347
+ tau = _extract_time_constant(voltage, sample_rate)
348
+
349
+ # L = tau * R
350
+ inductance = tau * resistance
351
+
352
+ return InductanceMeasurement(
353
+ inductance=float(inductance),
354
+ method="time_constant",
355
+ confidence=0.8,
356
+ statistics={
357
+ "time_constant": tau,
358
+ "resistance": resistance,
359
+ },
360
+ )
361
+
362
+ else:
363
+ raise AnalysisError(f"Method '{method}' requires current_trace or resistance parameter")
364
+
365
+
366
+ def extract_parasitics(
367
+ voltage_trace: WaveformTrace,
368
+ current_trace: WaveformTrace,
369
+ *,
370
+ model: Literal["series_RLC", "parallel_RLC"] = "series_RLC",
371
+ frequency_range: tuple[float, float] | None = None,
372
+ ) -> ParasiticExtraction:
373
+ """Extract parasitic R, L, C parameters from impedance measurement.
374
+
375
+ Fits an equivalent circuit model to measured voltage/current data
376
+ to extract parasitic component values.
377
+
378
+ Args:
379
+ voltage_trace: Voltage waveform.
380
+ current_trace: Current waveform.
381
+ model: Equivalent circuit model type.
382
+ frequency_range: Frequency range for analysis (Hz).
383
+
384
+ Returns:
385
+ ParasiticExtraction with R, L, C values.
386
+
387
+ Raises:
388
+ AnalysisError: If measurement conditions are not met.
389
+ InsufficientDataError: If insufficient samples are provided.
390
+
391
+ Example:
392
+ >>> params = extract_parasitics(voltage, current)
393
+ >>> print(f"C = {params.capacitance*1e12:.1f}pF, L = {params.inductance*1e9:.1f}nH")
394
+
395
+ References:
396
+ COMP-004
397
+ """
398
+ voltage = voltage_trace.data.astype(np.float64)
399
+ current = current_trace.data.astype(np.float64)
400
+ sample_rate = voltage_trace.metadata.sample_rate
401
+
402
+ min_len = min(len(voltage), len(current))
403
+ voltage = voltage[:min_len]
404
+ current = current[:min_len]
405
+
406
+ if min_len < 100:
407
+ raise InsufficientDataError(
408
+ "Parasitic extraction requires at least 100 samples",
409
+ required=100,
410
+ available=min_len,
411
+ analysis_type="parasitic_extraction",
412
+ )
413
+
414
+ # Compute impedance in frequency domain
415
+ from scipy.fft import fft, fftfreq
416
+
417
+ V_fft = fft(voltage)
418
+ I_fft = fft(current)
419
+
420
+ freqs = fftfreq(min_len, 1 / sample_rate)
421
+
422
+ # Use only positive frequencies
423
+ pos_mask = freqs > 0
424
+ freqs = freqs[pos_mask]
425
+ Z_fft = V_fft[pos_mask] / (I_fft[pos_mask] + 1e-20)
426
+
427
+ # Apply frequency range filter
428
+ if frequency_range is not None:
429
+ freq_mask = (freqs >= frequency_range[0]) & (freqs <= frequency_range[1])
430
+ freqs = freqs[freq_mask]
431
+ Z_fft = Z_fft[freq_mask]
432
+
433
+ if len(freqs) < 10:
434
+ raise AnalysisError("Insufficient frequency points for parasitic extraction")
435
+
436
+ # Fit RLC model to impedance data
437
+ if model == "series_RLC":
438
+ R, L, C = _fit_series_rlc(freqs, Z_fft)
439
+ else: # parallel_RLC
440
+ R, L, C = _fit_parallel_rlc(freqs, Z_fft)
441
+
442
+ # Calculate resonant frequency
443
+ f_res = 1 / (2 * np.pi * np.sqrt(L * C)) if L > 0 and C > 0 else None
444
+
445
+ # Calculate fit quality
446
+ Z_model = _calculate_rlc_impedance(freqs, R, L, C, model)
447
+ ss_res = np.sum(np.abs(Z_fft - Z_model) ** 2)
448
+ ss_tot = np.sum(np.abs(Z_fft - np.mean(Z_fft)) ** 2)
449
+ r_squared = 1 - ss_res / (ss_tot + 1e-20)
450
+
451
+ return ParasiticExtraction(
452
+ capacitance=float(C),
453
+ inductance=float(L),
454
+ resistance=float(R),
455
+ model_type=model,
456
+ resonant_freq=f_res,
457
+ fit_quality=float(max(0, r_squared)),
458
+ )
459
+
460
+
461
+ def _extract_time_constant(data: NDArray[np.float64], sample_rate: float) -> float:
462
+ """Extract time constant from step response."""
463
+ # Normalize data
464
+ data_min = np.min(data)
465
+ data_max = np.max(data)
466
+ if data_max - data_min < 1e-10:
467
+ return 1 / sample_rate # Return minimum time constant
468
+
469
+ normalized = (data - data_min) / (data_max - data_min)
470
+
471
+ # Find 63.2% point (time constant)
472
+ target = 0.632
473
+ idx_raw = np.argmax(normalized >= target)
474
+ idx = int(idx_raw)
475
+ if idx == 0:
476
+ idx = int(len(data) // 2)
477
+
478
+ tau = float(idx) / sample_rate
479
+ min_tau = 1 / sample_rate
480
+ return float(tau if tau > min_tau else min_tau)
481
+
482
+
483
+ def _estimate_esr(
484
+ voltage: NDArray[np.float64],
485
+ current: NDArray[np.float64],
486
+ sample_rate: float,
487
+ ) -> float:
488
+ """Estimate ESR from voltage/current phase relationship."""
489
+ # Use correlation to estimate resistive component
490
+ # ESR causes in-phase voltage drop
491
+ correlation = np.correlate(voltage - np.mean(voltage), current - np.mean(current))
492
+ if np.std(current) > 1e-10:
493
+ esr = np.max(correlation) / (len(current) * np.var(current))
494
+ return float(abs(esr))
495
+ return 0.0
496
+
497
+
498
+ def _estimate_dcr(voltage: NDArray[np.float64], current: NDArray[np.float64]) -> float:
499
+ """Estimate DC resistance from steady-state V/I."""
500
+ # Use last 10% of data as steady-state
501
+ n = len(voltage)
502
+ start = int(0.9 * n)
503
+ v_ss = np.mean(voltage[start:])
504
+ i_ss = np.mean(current[start:])
505
+ if abs(i_ss) > 1e-10:
506
+ return float(v_ss / i_ss)
507
+ return 0.0
508
+
509
+
510
+ def _fit_series_rlc(
511
+ freqs: NDArray[np.float64], Z: NDArray[np.complex128]
512
+ ) -> tuple[float, float, float]:
513
+ """Fit series RLC model to impedance data."""
514
+ omega = 2 * np.pi * freqs
515
+
516
+ # Initial estimates
517
+ R_init = float(np.real(np.mean(Z)))
518
+ # From imaginary part at low/high frequency
519
+ L_init = float(np.abs(np.imag(Z[-1])) / omega[-1]) if len(omega) > 0 else 1e-9
520
+ C_init = 1e-12
521
+
522
+ def model(omega: NDArray[np.float64], R: float, L: float, C: float) -> NDArray[np.complex128]:
523
+ return R + 1j * (omega * L - 1 / (omega * C + 1e-20))
524
+
525
+ try:
526
+ # Fit real and imaginary parts separately
527
+ np.real(Z)
528
+ np.imag(Z)
529
+
530
+ # Simple optimization
531
+ from scipy.optimize import minimize
532
+
533
+ def objective(params: NDArray[np.float64]) -> np.floating[Any]: # type: ignore[name-defined]
534
+ R, L, C = params
535
+ Z_model = model(omega, R, L, C)
536
+ return float(np.sum(np.abs(Z - Z_model) ** 2)) # type: ignore[return-value]
537
+
538
+ result = minimize(
539
+ objective,
540
+ [R_init, L_init, C_init],
541
+ bounds=[(1e-6, 1e6), (1e-15, 1e-3), (1e-15, 1e-3)],
542
+ method="L-BFGS-B",
543
+ )
544
+ return tuple(result.x)
545
+ except Exception:
546
+ return (R_init, L_init, C_init)
547
+
548
+
549
+ def _fit_parallel_rlc(
550
+ freqs: NDArray[np.float64], Z: NDArray[np.complex128]
551
+ ) -> tuple[float, float, float]:
552
+ """Fit parallel RLC model to impedance data."""
553
+ # Convert to admittance
554
+ Y = 1 / (Z + 1e-20)
555
+ omega = 2 * np.pi * freqs
556
+
557
+ # Initial estimates
558
+ G_init = float(np.real(np.mean(Y)))
559
+ R_init = 1 / G_init if G_init > 1e-10 else 1e3
560
+ C_init = float(np.abs(np.imag(Y[-1])) / omega[-1]) if len(omega) > 0 else 1e-12
561
+ L_init = 1e-9
562
+
563
+ try:
564
+ from scipy.optimize import minimize
565
+
566
+ def objective(params: NDArray[np.float64]) -> np.floating[Any]: # type: ignore[name-defined]
567
+ R, L, C = params
568
+ Y_model = 1 / R + 1j * omega * C + 1 / (1j * omega * L + 1e-20)
569
+ Z_model = 1 / (Y_model + 1e-20)
570
+ return float(np.sum(np.abs(Z - Z_model) ** 2)) # type: ignore[return-value]
571
+
572
+ result = minimize(
573
+ objective,
574
+ [R_init, L_init, C_init],
575
+ bounds=[(1e-6, 1e9), (1e-15, 1e-3), (1e-15, 1e-3)],
576
+ method="L-BFGS-B",
577
+ )
578
+ return tuple(result.x)
579
+ except Exception:
580
+ return (R_init, L_init, C_init)
581
+
582
+
583
+ def _calculate_rlc_impedance(
584
+ freqs: NDArray[np.float64],
585
+ R: float,
586
+ L: float,
587
+ C: float,
588
+ model: str,
589
+ ) -> NDArray[np.complex128]:
590
+ """Calculate impedance of RLC circuit."""
591
+ omega = 2 * np.pi * freqs
592
+
593
+ if model == "series_RLC":
594
+ Z: NDArray[np.complex128] = R + 1j * (omega * L - 1 / (omega * C + 1e-20))
595
+ return Z
596
+ else: # parallel_RLC
597
+ Y = 1 / R + 1j * omega * C + 1 / (1j * omega * L + 1e-20)
598
+ return cast("NDArray[np.complex128]", 1 / (Y + 1e-20))