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,383 @@
1
+ """Basic power analysis for Oscura.
2
+
3
+ Provides fundamental power calculations including instantaneous power,
4
+ average power, RMS power, peak power, and energy.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.analyzers.power.basic import instantaneous_power, power_statistics
9
+ >>> power_trace = instantaneous_power(voltage_trace, current_trace)
10
+ >>> stats = power_statistics(power_trace)
11
+ >>> print(f"Average: {stats['average']:.2f} W, Peak: {stats['peak']:.2f} W")
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from typing import Any
17
+
18
+ import numpy as np
19
+ from scipy import interpolate
20
+
21
+ from oscura.core.exceptions import AnalysisError
22
+ from oscura.core.types import TraceMetadata, WaveformTrace
23
+
24
+
25
+ def instantaneous_power(
26
+ voltage: WaveformTrace,
27
+ current: WaveformTrace,
28
+ *,
29
+ interpolate_if_needed: bool = True,
30
+ ) -> WaveformTrace:
31
+ """Calculate instantaneous power from voltage and current traces.
32
+
33
+ P(t) = V(t) * I(t)
34
+
35
+ Args:
36
+ voltage: Voltage waveform trace.
37
+ current: Current waveform trace.
38
+ interpolate_if_needed: If True, interpolate if sample rates differ.
39
+
40
+ Returns:
41
+ Power waveform trace (in Watts if inputs are V and A).
42
+
43
+ Raises:
44
+ AnalysisError: If sample rates mismatch and interpolation disabled.
45
+
46
+ Example:
47
+ >>> power = instantaneous_power(v_trace, i_trace)
48
+ >>> print(f"Peak power: {np.max(power.data):.2f} W")
49
+
50
+ References:
51
+ IEC 61000-4-7 (power measurements)
52
+ """
53
+ v_data = voltage.data
54
+ i_data = current.data
55
+ v_rate = voltage.metadata.sample_rate
56
+ i_rate = current.metadata.sample_rate
57
+
58
+ # Handle different sample rates
59
+ if v_rate != i_rate:
60
+ if not interpolate_if_needed:
61
+ raise AnalysisError(
62
+ f"Sample rate mismatch: voltage={v_rate}, current={i_rate}. "
63
+ "Set interpolate_if_needed=True to interpolate."
64
+ )
65
+
66
+ # Use higher sample rate and interpolate the other
67
+ if v_rate > i_rate:
68
+ # Interpolate current to voltage sample rate
69
+ t_current = np.arange(len(i_data)) / i_rate
70
+ t_voltage = np.arange(len(v_data)) / v_rate
71
+ interp = interpolate.interp1d(
72
+ t_current, i_data, kind="linear", fill_value="extrapolate"
73
+ )
74
+ i_data = interp(t_voltage)
75
+ sample_rate = v_rate
76
+ else:
77
+ # Interpolate voltage to current sample rate
78
+ t_voltage = np.arange(len(v_data)) / v_rate
79
+ t_current = np.arange(len(i_data)) / i_rate
80
+ interp = interpolate.interp1d(
81
+ t_voltage, v_data, kind="linear", fill_value="extrapolate"
82
+ )
83
+ v_data = interp(t_current)
84
+ sample_rate = i_rate
85
+ else:
86
+ sample_rate = v_rate
87
+
88
+ # Handle different lengths
89
+ min_len = min(len(v_data), len(i_data))
90
+ v_data = v_data[:min_len]
91
+ i_data = i_data[:min_len]
92
+
93
+ # Calculate instantaneous power
94
+ power_data = v_data * i_data
95
+
96
+ return WaveformTrace(
97
+ data=power_data.astype(np.float64),
98
+ metadata=TraceMetadata(sample_rate=sample_rate),
99
+ )
100
+
101
+
102
+ def average_power(
103
+ power: WaveformTrace | None = None,
104
+ *,
105
+ voltage: WaveformTrace | None = None,
106
+ current: WaveformTrace | None = None,
107
+ ) -> float:
108
+ """Calculate average (mean) power.
109
+
110
+ P_avg = (1/T) * integral(P(t) dt)
111
+
112
+ Args:
113
+ power: Power trace (if already calculated).
114
+ voltage: Voltage trace (alternative to power).
115
+ current: Current trace (alternative to power).
116
+
117
+ Returns:
118
+ Average power in Watts.
119
+
120
+ Raises:
121
+ AnalysisError: If neither power nor both voltage and current provided.
122
+
123
+ Example:
124
+ >>> p_avg = average_power(power_trace)
125
+ >>> # or
126
+ >>> p_avg = average_power(voltage=v, current=i)
127
+ """
128
+ if power is None:
129
+ if voltage is None or current is None:
130
+ raise AnalysisError("Either power trace or both voltage and current traces required")
131
+ power = instantaneous_power(voltage, current)
132
+
133
+ return float(np.mean(power.data))
134
+
135
+
136
+ def rms_power(
137
+ power: WaveformTrace | None = None,
138
+ *,
139
+ voltage: WaveformTrace | None = None,
140
+ current: WaveformTrace | None = None,
141
+ ) -> float:
142
+ """Calculate RMS power.
143
+
144
+ P_rms = sqrt(mean(P(t)^2))
145
+
146
+ Args:
147
+ power: Power trace.
148
+ voltage: Voltage trace (alternative).
149
+ current: Current trace (alternative).
150
+
151
+ Returns:
152
+ RMS power in Watts.
153
+
154
+ Raises:
155
+ AnalysisError: If neither power nor both voltage and current provided.
156
+
157
+ Example:
158
+ >>> p_rms = rms_power(power_trace)
159
+ """
160
+ if power is None:
161
+ if voltage is None or current is None:
162
+ raise AnalysisError("Either power trace or both voltage and current traces required")
163
+ power = instantaneous_power(voltage, current)
164
+
165
+ return float(np.sqrt(np.mean(power.data**2)))
166
+
167
+
168
+ def peak_power(
169
+ power: WaveformTrace | None = None,
170
+ *,
171
+ voltage: WaveformTrace | None = None,
172
+ current: WaveformTrace | None = None,
173
+ absolute: bool = True,
174
+ ) -> float:
175
+ """Calculate peak power.
176
+
177
+ Args:
178
+ power: Power trace.
179
+ voltage: Voltage trace (alternative).
180
+ current: Current trace (alternative).
181
+ absolute: If True, return absolute maximum. If False, maximum value.
182
+
183
+ Returns:
184
+ Peak power in Watts.
185
+
186
+ Raises:
187
+ AnalysisError: If neither power nor both voltage and current provided.
188
+
189
+ Example:
190
+ >>> p_peak = peak_power(power_trace)
191
+ """
192
+ if power is None:
193
+ if voltage is None or current is None:
194
+ raise AnalysisError("Either power trace or both voltage and current traces required")
195
+ power = instantaneous_power(voltage, current)
196
+
197
+ if absolute:
198
+ return float(np.max(np.abs(power.data)))
199
+ return float(np.max(power.data))
200
+
201
+
202
+ def energy(
203
+ power: WaveformTrace | None = None,
204
+ *,
205
+ voltage: WaveformTrace | None = None,
206
+ current: WaveformTrace | None = None,
207
+ start_time: float | None = None,
208
+ end_time: float | None = None,
209
+ ) -> float:
210
+ """Calculate energy (integral of power over time).
211
+
212
+ E = integral(P(t) dt)
213
+
214
+ Args:
215
+ power: Power trace.
216
+ voltage: Voltage trace (alternative).
217
+ current: Current trace (alternative).
218
+ start_time: Start time for integration (seconds).
219
+ end_time: End time for integration (seconds).
220
+
221
+ Returns:
222
+ Energy in Joules.
223
+
224
+ Raises:
225
+ AnalysisError: If neither power nor both voltage and current provided.
226
+
227
+ Example:
228
+ >>> e = energy(power_trace)
229
+ >>> print(f"Total energy: {e*1e3:.2f} mJ")
230
+ """
231
+ if power is None:
232
+ if voltage is None or current is None:
233
+ raise AnalysisError("Either power trace or both voltage and current traces required")
234
+ power = instantaneous_power(voltage, current)
235
+
236
+ data = power.data
237
+ sample_period = power.metadata.time_base
238
+
239
+ # Apply time limits
240
+ if start_time is not None or end_time is not None:
241
+ time_vector = np.arange(len(data)) * sample_period
242
+ if start_time is not None:
243
+ mask = time_vector >= start_time
244
+ else:
245
+ mask = np.ones(len(data), dtype=bool)
246
+ if end_time is not None:
247
+ mask &= time_vector <= end_time
248
+ data = data[mask]
249
+
250
+ # Integrate using trapezoidal rule (scipy is stable across versions)
251
+ from scipy.integrate import trapezoid
252
+
253
+ return float(trapezoid(data, dx=sample_period))
254
+
255
+
256
+ def power_statistics(
257
+ power: WaveformTrace | None = None,
258
+ *,
259
+ voltage: WaveformTrace | None = None,
260
+ current: WaveformTrace | None = None,
261
+ ) -> dict[str, float]:
262
+ """Calculate comprehensive power statistics.
263
+
264
+ Args:
265
+ power: Power trace.
266
+ voltage: Voltage trace (alternative).
267
+ current: Current trace (alternative).
268
+
269
+ Returns:
270
+ Dictionary with:
271
+ - average: Mean power
272
+ - rms: RMS power
273
+ - peak: Peak power (absolute)
274
+ - peak_positive: Maximum positive power
275
+ - peak_negative: Maximum negative power (regeneration)
276
+ - energy: Total energy
277
+ - min: Minimum power value
278
+ - std: Standard deviation
279
+
280
+ Raises:
281
+ AnalysisError: If neither power nor both voltage and current provided.
282
+
283
+ Example:
284
+ >>> stats = power_statistics(voltage=v, current=i)
285
+ >>> print(f"Average: {stats['average']:.2f} W")
286
+ >>> print(f"Peak: {stats['peak']:.2f} W")
287
+ >>> print(f"Energy: {stats['energy']*1e3:.2f} mJ")
288
+ """
289
+ if power is None:
290
+ if voltage is None or current is None:
291
+ raise AnalysisError("Either power trace or both voltage and current traces required")
292
+ power = instantaneous_power(voltage, current)
293
+
294
+ data = power.data
295
+ sample_period = power.metadata.time_base
296
+
297
+ # Use scipy trapezoid for stable API across NumPy versions
298
+ from scipy.integrate import trapezoid
299
+
300
+ return {
301
+ "average": float(np.mean(data)),
302
+ "rms": float(np.sqrt(np.mean(data**2))),
303
+ "peak": float(np.max(np.abs(data))),
304
+ "peak_positive": float(np.max(data)),
305
+ "peak_negative": float(np.min(data)),
306
+ "energy": float(trapezoid(data, dx=sample_period)),
307
+ "min": float(np.min(data)),
308
+ "std": float(np.std(data)),
309
+ }
310
+
311
+
312
+ def power_profile(
313
+ voltage: WaveformTrace,
314
+ current: WaveformTrace,
315
+ *,
316
+ window_size: int | None = None,
317
+ ) -> dict[str, Any]:
318
+ """Generate power profile with rolling statistics.
319
+
320
+ Args:
321
+ voltage: Voltage trace.
322
+ current: Current trace.
323
+ window_size: Window size for rolling calculations. If None, auto-select.
324
+
325
+ Returns:
326
+ Dictionary with:
327
+ - power_trace: Instantaneous power trace
328
+ - rolling_avg: Rolling average power
329
+ - rolling_peak: Rolling peak power
330
+ - cumulative_energy: Cumulative energy over time
331
+ - statistics: Overall power statistics
332
+ """
333
+ power = instantaneous_power(voltage, current)
334
+ data = power.data
335
+ sample_period = power.metadata.time_base
336
+
337
+ if window_size is None:
338
+ # Auto-select: ~1% of total samples or 100, whichever is larger
339
+ window_size = max(100, len(data) // 100)
340
+
341
+ # Ensure window size is odd for centered window
342
+ if window_size % 2 == 0:
343
+ window_size += 1
344
+
345
+ # Rolling average
346
+ kernel = np.ones(window_size) / window_size
347
+ rolling_avg = np.convolve(data, kernel, mode="same")
348
+
349
+ # Rolling peak (using scipy.ndimage.maximum_filter would be faster but numpy-only)
350
+ from scipy.ndimage import maximum_filter1d
351
+
352
+ rolling_peak = maximum_filter1d(np.abs(data), size=window_size)
353
+
354
+ # Cumulative energy
355
+ cumulative_energy = np.cumsum(data) * sample_period
356
+
357
+ return {
358
+ "power_trace": power,
359
+ "rolling_avg": WaveformTrace(
360
+ data=rolling_avg.astype(np.float64),
361
+ metadata=power.metadata,
362
+ ),
363
+ "rolling_peak": WaveformTrace(
364
+ data=rolling_peak.astype(np.float64),
365
+ metadata=power.metadata,
366
+ ),
367
+ "cumulative_energy": WaveformTrace(
368
+ data=cumulative_energy.astype(np.float64),
369
+ metadata=power.metadata,
370
+ ),
371
+ "statistics": power_statistics(power),
372
+ }
373
+
374
+
375
+ __all__ = [
376
+ "average_power",
377
+ "energy",
378
+ "instantaneous_power",
379
+ "peak_power",
380
+ "power_profile",
381
+ "power_statistics",
382
+ "rms_power",
383
+ ]
@@ -0,0 +1,314 @@
1
+ """Conduction loss analysis for Oscura.
2
+
3
+ Provides conduction loss calculations for power semiconductor devices
4
+ during their on-state.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.analyzers.power.conduction import conduction_loss
9
+ >>> p_cond = conduction_loss(v_on_trace, i_d_trace, duty_cycle=0.5)
10
+ >>> print(f"Conduction loss: {p_cond:.2f} W")
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import TYPE_CHECKING
16
+
17
+ import numpy as np
18
+
19
+ from oscura.core.exceptions import AnalysisError
20
+
21
+ if TYPE_CHECKING:
22
+ from oscura.core.types import WaveformTrace
23
+
24
+
25
+ def conduction_loss(
26
+ voltage: WaveformTrace,
27
+ current: WaveformTrace,
28
+ duty_cycle: float | None = None,
29
+ ) -> float:
30
+ """Calculate conduction loss during on-state.
31
+
32
+ P_cond = V_on * I_on * D (steady state)
33
+ or
34
+ P_cond = mean(V(t) * I(t)) over on-state periods
35
+
36
+ Args:
37
+ voltage: On-state voltage trace (V_ds(on) or V_ce(sat)).
38
+ current: On-state current trace.
39
+ duty_cycle: Duty cycle (0 to 1). If None, calculates from waveforms.
40
+
41
+ Returns:
42
+ Conduction loss in Watts.
43
+
44
+ Example:
45
+ >>> p_cond = conduction_loss(v_on, i_d, duty_cycle=0.5)
46
+ >>> print(f"Conduction loss: {p_cond:.2f} W")
47
+
48
+ References:
49
+ Infineon Application Note AN-9010
50
+ """
51
+ v_data = voltage.data
52
+ i_data = current.data
53
+
54
+ # Ensure same length
55
+ min_len = min(len(v_data), len(i_data))
56
+ v_data = v_data[:min_len]
57
+ i_data = i_data[:min_len]
58
+
59
+ if duty_cycle is not None:
60
+ # Use average values and duty cycle
61
+ v_on = float(np.mean(v_data))
62
+ i_on = float(np.mean(i_data))
63
+ return v_on * i_on * duty_cycle
64
+ else:
65
+ # Calculate instantaneous power and average
66
+ power = v_data * i_data
67
+ return float(np.mean(power))
68
+
69
+
70
+ def on_resistance(
71
+ voltage: WaveformTrace,
72
+ current: WaveformTrace,
73
+ *,
74
+ min_current: float | None = None,
75
+ min_current_fraction: float = 0.1,
76
+ ) -> float:
77
+ """Calculate on-state resistance (R_ds(on) or R_ce(sat)).
78
+
79
+ R_on = V_on / I_on
80
+
81
+ Args:
82
+ voltage: On-state voltage trace.
83
+ current: On-state current trace.
84
+ min_current: Minimum current threshold (to avoid division by small values).
85
+ If None, uses min_current_fraction * peak current.
86
+ min_current_fraction: Fraction of peak current to use as minimum threshold
87
+ when min_current is None (default: 0.1 = 10% of peak).
88
+
89
+ Returns:
90
+ On-state resistance in Ohms.
91
+
92
+ Example:
93
+ >>> r_on = on_resistance(v_ds, i_d)
94
+ >>> print(f"R_ds(on): {r_on*1e3:.2f} mOhm")
95
+ >>> # With tighter threshold (5% of peak):
96
+ >>> r_on = on_resistance(v_ds, i_d, min_current_fraction=0.05)
97
+ """
98
+ v_data = voltage.data
99
+ i_data = current.data
100
+
101
+ min_len = min(len(v_data), len(i_data))
102
+ v_data = v_data[:min_len]
103
+ i_data = i_data[:min_len]
104
+
105
+ # Filter by minimum current
106
+ if min_current is None:
107
+ min_current = min_current_fraction * np.max(np.abs(i_data))
108
+
109
+ mask = np.abs(i_data) >= min_current
110
+ if not np.any(mask):
111
+ return np.nan # type: ignore[no-any-return]
112
+
113
+ v_on = v_data[mask]
114
+ i_on = i_data[mask]
115
+
116
+ # Linear fit to get resistance
117
+ # V = I * R -> slope is R
118
+ coeffs = np.polyfit(i_on, v_on, 1)
119
+ return float(coeffs[0])
120
+
121
+
122
+ def forward_voltage(
123
+ voltage: WaveformTrace,
124
+ current: WaveformTrace,
125
+ *,
126
+ current_threshold: float | None = None,
127
+ current_threshold_fraction: float = 0.1,
128
+ threshold_window: float = 0.1,
129
+ ) -> float:
130
+ """Calculate forward voltage drop (for diodes or IGBT V_ce(sat)).
131
+
132
+ Extracts the voltage at a reference current level.
133
+
134
+ Args:
135
+ voltage: Forward voltage trace.
136
+ current: Forward current trace.
137
+ current_threshold: Current level at which to measure Vf.
138
+ If None, uses current_threshold_fraction * peak current.
139
+ current_threshold_fraction: Fraction of peak current to use as threshold
140
+ when current_threshold is None (default: 0.1 = 10% of peak).
141
+ threshold_window: Tolerance window around the current threshold,
142
+ as a fraction of the threshold (default: 0.1 = +/-10% window).
143
+ Samples within this window are averaged to compute Vf.
144
+
145
+ Returns:
146
+ Forward voltage in Volts.
147
+
148
+ Example:
149
+ >>> vf = forward_voltage(v_f, i_f)
150
+ >>> print(f"Forward voltage: {vf:.2f} V")
151
+ >>> # With tighter window for more precise measurement:
152
+ >>> vf = forward_voltage(v_f, i_f, threshold_window=0.05)
153
+ """
154
+ v_data = voltage.data
155
+ i_data = current.data
156
+
157
+ min_len = min(len(v_data), len(i_data))
158
+ v_data = v_data[:min_len]
159
+ i_data = i_data[:min_len]
160
+
161
+ if current_threshold is None:
162
+ current_threshold = current_threshold_fraction * np.max(np.abs(i_data))
163
+
164
+ # Find samples near the current threshold
165
+ near_threshold = np.abs(i_data - current_threshold) < threshold_window * current_threshold
166
+ if not np.any(near_threshold):
167
+ # Interpolate
168
+ idx = np.argmin(np.abs(i_data - current_threshold))
169
+ return float(v_data[idx])
170
+
171
+ return float(np.mean(v_data[near_threshold]))
172
+
173
+
174
+ def duty_cycle_weighted_loss(
175
+ losses: list[tuple[float, float]],
176
+ ) -> float:
177
+ """Calculate total loss from multiple operating points.
178
+
179
+ Useful for variable duty cycle or multi-mode operation.
180
+
181
+ Args:
182
+ losses: List of (loss_watts, duty_cycle) tuples.
183
+
184
+ Returns:
185
+ Total weighted average loss in Watts.
186
+
187
+ Raises:
188
+ AnalysisError: If total duty cycle exceeds 1.0
189
+
190
+ Example:
191
+ >>> # 10W at 30% duty, 5W at 50% duty
192
+ >>> total = duty_cycle_weighted_loss([(10, 0.3), (5, 0.5)])
193
+ """
194
+ total = 0.0
195
+ total_duty = 0.0
196
+
197
+ for loss, duty in losses:
198
+ total += loss * duty
199
+ total_duty += duty
200
+
201
+ if total_duty > 1.0:
202
+ raise AnalysisError(f"Total duty cycle exceeds 1.0: {total_duty}")
203
+
204
+ return total
205
+
206
+
207
+ def temperature_derating(
208
+ r_on_25c: float,
209
+ temperature: float,
210
+ temp_coefficient: float = 0.004,
211
+ ) -> float:
212
+ """Calculate temperature-derated on-resistance.
213
+
214
+ R_on(T) = R_on(25C) * (1 + alpha * (T - 25))
215
+
216
+ Args:
217
+ r_on_25c: On-resistance at 25C in Ohms.
218
+ temperature: Operating temperature in Celsius.
219
+ temp_coefficient: Temperature coefficient (default 0.4%/C for Si MOSFET).
220
+
221
+ Returns:
222
+ Derated on-resistance in Ohms.
223
+
224
+ Example:
225
+ >>> r_on_100c = temperature_derating(0.010, 100) # 10mOhm at 25C
226
+ >>> print(f"R_on at 100C: {r_on_100c*1e3:.2f} mOhm")
227
+ """
228
+ return r_on_25c * (1 + temp_coefficient * (temperature - 25))
229
+
230
+
231
+ def mosfet_conduction_loss(
232
+ i_rms: float,
233
+ r_ds_on: float,
234
+ temperature: float = 25.0,
235
+ temp_coefficient: float = 0.004,
236
+ ) -> float:
237
+ """Calculate MOSFET conduction loss.
238
+
239
+ P_cond = I_rms^2 * R_ds(on)
240
+
241
+ Args:
242
+ i_rms: RMS drain current in Amps.
243
+ r_ds_on: On-state resistance at 25C in Ohms.
244
+ temperature: Junction temperature in Celsius.
245
+ temp_coefficient: Temperature coefficient.
246
+
247
+ Returns:
248
+ Conduction loss in Watts.
249
+
250
+ Example:
251
+ >>> p = mosfet_conduction_loss(i_rms=10, r_ds_on=0.010, temperature=100)
252
+ """
253
+ r_on = temperature_derating(r_ds_on, temperature, temp_coefficient)
254
+ return i_rms**2 * r_on
255
+
256
+
257
+ def diode_conduction_loss(
258
+ i_avg: float,
259
+ i_rms: float,
260
+ v_f: float,
261
+ r_d: float = 0.0,
262
+ ) -> float:
263
+ """Calculate diode conduction loss.
264
+
265
+ P_cond = V_f * I_avg + r_d * I_rms^2
266
+
267
+ Args:
268
+ i_avg: Average forward current in Amps.
269
+ i_rms: RMS forward current in Amps.
270
+ v_f: Forward voltage drop in Volts.
271
+ r_d: Dynamic resistance in Ohms.
272
+
273
+ Returns:
274
+ Conduction loss in Watts.
275
+
276
+ Example:
277
+ >>> p = diode_conduction_loss(i_avg=5, i_rms=7, v_f=0.7, r_d=0.01)
278
+ """
279
+ return v_f * i_avg + r_d * i_rms**2
280
+
281
+
282
+ def igbt_conduction_loss(
283
+ i_c: float,
284
+ v_ce_sat: float,
285
+ r_c: float = 0.0,
286
+ ) -> float:
287
+ """Calculate IGBT conduction loss.
288
+
289
+ P_cond = V_ce(sat) * I_c + r_c * I_c^2
290
+
291
+ Args:
292
+ i_c: Collector current in Amps.
293
+ v_ce_sat: Collector-emitter saturation voltage in Volts.
294
+ r_c: Collector resistance in Ohms.
295
+
296
+ Returns:
297
+ Conduction loss in Watts.
298
+
299
+ Example:
300
+ >>> p = igbt_conduction_loss(i_c=50, v_ce_sat=2.0, r_c=0.01)
301
+ """
302
+ return v_ce_sat * i_c + r_c * i_c**2
303
+
304
+
305
+ __all__ = [
306
+ "conduction_loss",
307
+ "diode_conduction_loss",
308
+ "duty_cycle_weighted_loss",
309
+ "forward_voltage",
310
+ "igbt_conduction_loss",
311
+ "mosfet_conduction_loss",
312
+ "on_resistance",
313
+ "temperature_derating",
314
+ ]