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,315 @@
1
+ """Memory warning threshold system for TraceKit.
2
+
3
+ This module provides configurable memory pressure warnings with
4
+ automatic alerts and optional operation cancellation.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.core.memory_warnings import check_memory_warnings, MemoryWarningLevel
9
+ >>> level = check_memory_warnings()
10
+ >>> if level == MemoryWarningLevel.CRITICAL:
11
+ ... print("Critical memory pressure!")
12
+
13
+ References:
14
+ See oscura.config.memory for threshold configuration.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import warnings
20
+ from enum import Enum
21
+ from typing import TYPE_CHECKING
22
+
23
+ from oscura.config.memory import get_memory_config
24
+ from oscura.utils.memory import get_available_memory, get_memory_pressure
25
+
26
+ if TYPE_CHECKING:
27
+ from collections.abc import Callable
28
+
29
+
30
+ class MemoryWarningLevel(Enum):
31
+ """Memory warning levels based on pressure thresholds.
32
+
33
+ Attributes:
34
+ OK: Memory usage is normal (below warn threshold).
35
+ WARNING: Memory usage is elevated (above warn threshold).
36
+ CRITICAL: Memory usage is critical (above critical threshold).
37
+ """
38
+
39
+ OK = "ok"
40
+ WARNING = "warning"
41
+ CRITICAL = "critical"
42
+
43
+
44
+ def check_memory_warnings() -> MemoryWarningLevel:
45
+ """Check current memory pressure against configured thresholds.
46
+
47
+
48
+ Returns:
49
+ Current memory warning level.
50
+
51
+ Example:
52
+ >>> level = check_memory_warnings()
53
+ >>> if level != MemoryWarningLevel.OK:
54
+ ... print(f"Memory pressure: {level.value}")
55
+ """
56
+ pressure = get_memory_pressure()
57
+ config = get_memory_config()
58
+
59
+ if pressure >= config.critical_threshold:
60
+ return MemoryWarningLevel.CRITICAL
61
+ elif pressure >= config.warn_threshold:
62
+ return MemoryWarningLevel.WARNING
63
+ else:
64
+ return MemoryWarningLevel.OK
65
+
66
+
67
+ def emit_memory_warning(force: bool = False) -> None:
68
+ """Emit warning if memory pressure exceeds thresholds.
69
+
70
+
71
+ Args:
72
+ force: If True, always emit warning regardless of level.
73
+
74
+ Example:
75
+ >>> emit_memory_warning() # Emits warning if pressure high
76
+ """
77
+ level = check_memory_warnings()
78
+ pressure = get_memory_pressure()
79
+ available = get_available_memory()
80
+
81
+ if force or level != MemoryWarningLevel.OK:
82
+ if level == MemoryWarningLevel.CRITICAL:
83
+ warnings.warn(
84
+ f"CRITICAL memory pressure: {pressure * 100:.1f}% utilized. "
85
+ f"Only {available / 1e9:.2f} GB available. "
86
+ "Consider closing applications or canceling operations.",
87
+ ResourceWarning,
88
+ stacklevel=2,
89
+ )
90
+ elif level == MemoryWarningLevel.WARNING:
91
+ warnings.warn(
92
+ f"High memory pressure: {pressure * 100:.1f}% utilized. "
93
+ f"{available / 1e9:.2f} GB available. "
94
+ "Monitor memory usage closely.",
95
+ ResourceWarning,
96
+ stacklevel=2,
97
+ )
98
+
99
+
100
+ def check_and_abort_if_critical(operation: str = "operation") -> None:
101
+ """Check memory and abort if critical threshold exceeded.
102
+
103
+
104
+ Args:
105
+ operation: Name of operation to abort.
106
+
107
+ Raises:
108
+ MemoryError: If memory pressure is critical.
109
+
110
+ Example:
111
+ >>> try:
112
+ ... check_and_abort_if_critical('spectrogram')
113
+ ... # ... perform operation ...
114
+ ... except MemoryError:
115
+ ... print("Operation aborted due to critical memory")
116
+ """
117
+ level = check_memory_warnings()
118
+
119
+ if level == MemoryWarningLevel.CRITICAL:
120
+ pressure = get_memory_pressure()
121
+ available = get_available_memory()
122
+ config = get_memory_config()
123
+
124
+ raise MemoryError(
125
+ f"Critical memory pressure ({pressure * 100:.1f}% > {config.critical_threshold * 100:.0f}%). "
126
+ f"Only {available / 1e9:.2f} GB available. "
127
+ f"Operation '{operation}' aborted to prevent system crash. "
128
+ "Free memory or increase critical threshold to proceed."
129
+ )
130
+
131
+
132
+ class MemoryWarningMonitor:
133
+ """Context manager for monitoring memory warnings during operations.
134
+
135
+
136
+ Monitors memory pressure during an operation and emits warnings
137
+ when thresholds are crossed.
138
+
139
+ Example:
140
+ >>> with MemoryWarningMonitor('spectrogram', check_interval=100):
141
+ ... for i in range(1000):
142
+ ... # ... perform work ...
143
+ ... pass
144
+ """
145
+
146
+ def __init__(
147
+ self,
148
+ operation: str,
149
+ *,
150
+ check_interval: int = 100,
151
+ abort_on_critical: bool = True,
152
+ ):
153
+ """Initialize memory warning monitor.
154
+
155
+ Args:
156
+ operation: Name of operation being monitored.
157
+ check_interval: Check memory every N iterations.
158
+ abort_on_critical: Abort if critical threshold reached.
159
+ """
160
+ self.operation = operation
161
+ self.check_interval = check_interval
162
+ self.abort_on_critical = abort_on_critical
163
+ self._iteration = 0
164
+ self._warned = False
165
+ self._critical_warned = False
166
+
167
+ def __enter__(self) -> MemoryWarningMonitor:
168
+ """Enter context and perform initial check."""
169
+ # Initial check
170
+ emit_memory_warning()
171
+ return self
172
+
173
+ def __exit__(self, exc_type: type, exc_val: Exception, exc_tb: object) -> None:
174
+ """Exit context."""
175
+ # Note: exc_val and exc_tb intentionally unused but required for Python 3.11+ compatibility
176
+
177
+ def check(self, iteration: int | None = None) -> None:
178
+ """Check memory pressure and emit warnings.
179
+
180
+ Args:
181
+ iteration: Current iteration number (for periodic checking).
182
+
183
+ Raises:
184
+ MemoryError: If critical threshold exceeded and abort_on_critical=True.
185
+ """
186
+ self._iteration += 1
187
+
188
+ # Only check periodically
189
+ if iteration is not None and iteration % self.check_interval != 0:
190
+ return
191
+
192
+ level = check_memory_warnings()
193
+ pressure = get_memory_pressure()
194
+ available = get_available_memory()
195
+
196
+ if level == MemoryWarningLevel.CRITICAL:
197
+ if not self._critical_warned:
198
+ warnings.warn(
199
+ f"Critical memory pressure during {self.operation}: "
200
+ f"{pressure * 100:.1f}% utilized, {available / 1e9:.2f} GB available.",
201
+ ResourceWarning,
202
+ stacklevel=2,
203
+ )
204
+ self._critical_warned = True
205
+
206
+ if self.abort_on_critical:
207
+ raise MemoryError(
208
+ f"Critical memory pressure during {self.operation}. "
209
+ f"Operation aborted to prevent system crash. "
210
+ f"Pressure: {pressure * 100:.1f}%, Available: {available / 1e9:.2f} GB"
211
+ )
212
+
213
+ elif level == MemoryWarningLevel.WARNING:
214
+ if not self._warned:
215
+ warnings.warn(
216
+ f"High memory pressure during {self.operation}: "
217
+ f"{pressure * 100:.1f}% utilized, {available / 1e9:.2f} GB available.",
218
+ ResourceWarning,
219
+ stacklevel=2,
220
+ )
221
+ self._warned = True
222
+
223
+
224
+ def register_memory_warning_callback(callback: Callable[[MemoryWarningLevel], None]) -> None:
225
+ """Register a callback for memory warnings.
226
+
227
+ The callback will be invoked whenever memory warnings are checked
228
+ via emit_memory_warning() or MemoryWarningMonitor.
229
+
230
+ Args:
231
+ callback: Function that accepts MemoryWarningLevel.
232
+
233
+ Example:
234
+ >>> def my_callback(level):
235
+ ... if level == MemoryWarningLevel.CRITICAL:
236
+ ... print("CRITICAL MEMORY!")
237
+ >>> register_memory_warning_callback(my_callback)
238
+ """
239
+ _warning_callbacks.append(callback)
240
+
241
+
242
+ def clear_memory_warning_callbacks() -> None:
243
+ """Clear all registered memory warning callbacks.
244
+
245
+ Example:
246
+ >>> clear_memory_warning_callbacks()
247
+ """
248
+ _warning_callbacks.clear()
249
+
250
+
251
+ # Global list of warning callbacks
252
+ _warning_callbacks: list[Callable[[MemoryWarningLevel], None]] = []
253
+
254
+
255
+ def _invoke_callbacks(level: MemoryWarningLevel) -> None:
256
+ """Invoke all registered callbacks with current warning level.
257
+
258
+ Args:
259
+ level: Current memory warning level.
260
+ """
261
+ for callback in _warning_callbacks:
262
+ try:
263
+ callback(level)
264
+ except Exception as e:
265
+ warnings.warn(
266
+ f"Memory warning callback failed: {e}",
267
+ RuntimeWarning,
268
+ stacklevel=2,
269
+ )
270
+
271
+
272
+ def format_memory_warning(level: MemoryWarningLevel) -> str:
273
+ """Format memory warning message.
274
+
275
+ Args:
276
+ level: Memory warning level.
277
+
278
+ Returns:
279
+ Formatted warning message.
280
+
281
+ Example:
282
+ >>> msg = format_memory_warning(MemoryWarningLevel.WARNING)
283
+ >>> print(msg)
284
+ High memory pressure: 75.0% utilized...
285
+ """
286
+ pressure = get_memory_pressure()
287
+ available = get_available_memory()
288
+ config = get_memory_config()
289
+
290
+ if level == MemoryWarningLevel.CRITICAL:
291
+ return (
292
+ f"CRITICAL memory pressure: {pressure * 100:.1f}% utilized "
293
+ f"(threshold: {config.critical_threshold * 100:.0f}%). "
294
+ f"Only {available / 1e9:.2f} GB available."
295
+ )
296
+ elif level == MemoryWarningLevel.WARNING:
297
+ return (
298
+ f"High memory pressure: {pressure * 100:.1f}% utilized "
299
+ f"(threshold: {config.warn_threshold * 100:.0f}%). "
300
+ f"{available / 1e9:.2f} GB available."
301
+ )
302
+ else:
303
+ return f"Memory OK: {pressure * 100:.1f}% utilized, {available / 1e9:.2f} GB available."
304
+
305
+
306
+ __all__ = [
307
+ "MemoryWarningLevel",
308
+ "MemoryWarningMonitor",
309
+ "check_and_abort_if_critical",
310
+ "check_memory_warnings",
311
+ "clear_memory_warning_callbacks",
312
+ "emit_memory_warning",
313
+ "format_memory_warning",
314
+ "register_memory_warning_callback",
315
+ ]
@@ -0,0 +1,362 @@
1
+ """Numba JIT compilation backend for performance-critical code paths.
2
+
3
+ This module provides a unified interface for Numba JIT compilation with graceful
4
+ fallback when Numba is not available. Provides 10-50x speedup for numerical loops
5
+ that cannot be fully vectorized.
6
+
7
+ Usage:
8
+ from oscura.core.numba_backend import njit, prange, HAS_NUMBA
9
+
10
+ @njit(parallel=True, cache=True)
11
+ def fast_function(data):
12
+ result = np.zeros_like(data)
13
+ for i in prange(len(data)):
14
+ result[i] = expensive_computation(data[i])
15
+ return result
16
+
17
+ Performance characteristics:
18
+ - First call: Compilation overhead (~100-500ms)
19
+ - Subsequent calls: 10-50x faster than Python loops
20
+ - Parallel execution: Additional speedup on multi-core systems
21
+ - Cache: Compilation results cached between runs
22
+
23
+ Example:
24
+ >>> from oscura.core.numba_backend import njit, HAS_NUMBA
25
+ >>> import numpy as np
26
+ >>>
27
+ >>> @njit(cache=True)
28
+ >>> def sum_of_squares(arr):
29
+ ... total = 0.0
30
+ ... for i in range(len(arr)):
31
+ ... total += arr[i] ** 2
32
+ ... return total
33
+ >>>
34
+ >>> data = np.random.randn(1_000_000)
35
+ >>> result = sum_of_squares(data) # Fast on second call
36
+ """
37
+
38
+ from __future__ import annotations
39
+
40
+ import functools
41
+ from collections.abc import Callable
42
+ from typing import Any, TypeVar
43
+
44
+ import numpy as np
45
+
46
+ # Try to import Numba
47
+ try:
48
+ from numba import guvectorize as _numba_guvectorize # type: ignore[import-untyped]
49
+ from numba import jit as _numba_jit # type: ignore[import-untyped]
50
+ from numba import njit as _numba_njit # type: ignore[import-untyped]
51
+ from numba import prange as _numba_prange # type: ignore[import-untyped]
52
+ from numba import vectorize as _numba_vectorize # type: ignore[import-untyped]
53
+
54
+ HAS_NUMBA = True
55
+ except ImportError:
56
+ HAS_NUMBA = False
57
+
58
+
59
+ # Type variable for generic functions
60
+ F = TypeVar("F", bound=Callable[..., Any])
61
+
62
+
63
+ if HAS_NUMBA:
64
+ # Numba is available - use real implementations
65
+ njit = _numba_njit
66
+ prange = _numba_prange
67
+ vectorize = _numba_vectorize
68
+ guvectorize = _numba_guvectorize
69
+ jit = _numba_jit
70
+
71
+ else:
72
+ # Numba not available - provide fallback decorators that do nothing
73
+ def njit(*args: Any, **kwargs: Any) -> Callable[[F], F]:
74
+ """No-op decorator when Numba is not available.
75
+
76
+ This decorator does nothing but allows code to remain syntactically valid
77
+ when Numba is not installed.
78
+
79
+ Args:
80
+ *args: Positional arguments (ignored).
81
+ **kwargs: Keyword arguments (ignored).
82
+
83
+ Returns:
84
+ Decorator function or decorated function.
85
+ """
86
+
87
+ def decorator(func: F) -> F:
88
+ @functools.wraps(func)
89
+ def wrapper(*call_args: Any, **call_kwargs: Any) -> Any:
90
+ return func(*call_args, **call_kwargs)
91
+
92
+ return wrapper # type: ignore[return-value]
93
+
94
+ # Handle both @njit and @njit() syntax
95
+ if len(args) == 1 and callable(args[0]) and not kwargs:
96
+ return decorator(args[0]) # type: ignore[no-any-return]
97
+ return decorator # type: ignore[no-any-return]
98
+
99
+ def prange(*args: Any, **kwargs: Any) -> range:
100
+ """Fallback to regular range when Numba is not available.
101
+
102
+ Args:
103
+ *args: Same as range().
104
+ **kwargs: Same as range().
105
+
106
+ Returns:
107
+ Standard Python range object.
108
+ """
109
+ return range(*args, **kwargs)
110
+
111
+ def vectorize(*args: Any, **kwargs: Any) -> Callable[[F], F]:
112
+ """No-op decorator when Numba is not available.
113
+
114
+ Args:
115
+ *args: Positional arguments (ignored).
116
+ **kwargs: Keyword arguments (ignored).
117
+
118
+ Returns:
119
+ Decorator that returns the original function.
120
+ """
121
+
122
+ def decorator(func: F) -> F:
123
+ return func
124
+
125
+ if len(args) == 1 and callable(args[0]):
126
+ return decorator(args[0]) # type: ignore[no-any-return]
127
+ return decorator # type: ignore[no-any-return]
128
+
129
+ def guvectorize(*args: Any, **kwargs: Any) -> Callable[[F], F]:
130
+ """No-op decorator when Numba is not available.
131
+
132
+ Args:
133
+ *args: Positional arguments (ignored).
134
+ **kwargs: Keyword arguments (ignored).
135
+
136
+ Returns:
137
+ Decorator that returns the original function.
138
+ """
139
+
140
+ def decorator(func: F) -> F:
141
+ return func
142
+
143
+ if len(args) == 1 and callable(args[0]):
144
+ return decorator(args[0]) # type: ignore[no-any-return]
145
+ return decorator # type: ignore[no-any-return]
146
+
147
+ def jit(*args: Any, **kwargs: Any) -> Callable[[F], F]:
148
+ """No-op decorator when Numba is not available.
149
+
150
+ Args:
151
+ *args: Positional arguments (ignored).
152
+ **kwargs: Keyword arguments (ignored).
153
+
154
+ Returns:
155
+ Decorator that returns the original function.
156
+ """
157
+
158
+ def decorator(func: F) -> F:
159
+ @functools.wraps(func)
160
+ def wrapper(*call_args: Any, **call_kwargs: Any) -> Any:
161
+ return func(*call_args, **call_kwargs)
162
+
163
+ return wrapper # type: ignore[return-value]
164
+
165
+ if len(args) == 1 and callable(args[0]) and not kwargs:
166
+ return decorator(args[0]) # type: ignore[no-any-return]
167
+ return decorator # type: ignore[no-any-return]
168
+
169
+
170
+ def get_optimal_numba_config(
171
+ parallel: bool = False,
172
+ cache: bool = True,
173
+ fastmath: bool = False,
174
+ nogil: bool = False,
175
+ ) -> dict[str, Any]:
176
+ """Get optimal Numba configuration for given requirements.
177
+
178
+ Args:
179
+ parallel: Enable parallel execution using prange.
180
+ cache: Enable compilation caching for faster subsequent runs.
181
+ fastmath: Enable fast math optimizations (may reduce precision).
182
+ nogil: Release GIL during execution (useful for threading).
183
+
184
+ Returns:
185
+ Dictionary of Numba configuration options.
186
+
187
+ Example:
188
+ >>> config = get_optimal_numba_config(parallel=True, cache=True)
189
+ >>> @njit(**config)
190
+ >>> def my_function(data):
191
+ ... pass
192
+ """
193
+ if not HAS_NUMBA:
194
+ return {}
195
+
196
+ return {
197
+ "parallel": parallel,
198
+ "cache": cache,
199
+ "fastmath": fastmath,
200
+ "nogil": nogil,
201
+ }
202
+
203
+
204
+ # Example Numba-optimized functions for common operations
205
+
206
+
207
+ @njit(cache=True) # type: ignore[misc,untyped-decorator]
208
+ def find_crossings_numba(
209
+ data: np.ndarray, # type: ignore[type-arg]
210
+ threshold: float,
211
+ direction: int = 0,
212
+ ) -> np.ndarray: # type: ignore[type-arg]
213
+ """Find threshold crossings with Numba acceleration.
214
+
215
+ Args:
216
+ data: Input signal data.
217
+ threshold: Threshold value to detect crossings.
218
+ direction: 0=both, 1=rising only, -1=falling only.
219
+
220
+ Returns:
221
+ Array of indices where crossings occur.
222
+ """
223
+ crossings = []
224
+ for i in range(1, len(data)):
225
+ prev_val = data[i - 1]
226
+ curr_val = data[i]
227
+
228
+ if direction >= 0: # Rising or both
229
+ if prev_val < threshold <= curr_val:
230
+ crossings.append(i)
231
+ if direction <= 0 and direction != 1: # Falling or both
232
+ if prev_val > threshold >= curr_val:
233
+ crossings.append(i)
234
+
235
+ return np.array(crossings, dtype=np.int64)
236
+
237
+
238
+ @njit(parallel=True, cache=True) # type: ignore[misc,untyped-decorator]
239
+ def moving_average_numba(
240
+ data: np.ndarray, # type: ignore[type-arg]
241
+ window_size: int,
242
+ ) -> np.ndarray: # type: ignore[type-arg]
243
+ """Compute moving average with Numba parallel acceleration.
244
+
245
+ Args:
246
+ data: Input signal data.
247
+ window_size: Size of the moving window.
248
+
249
+ Returns:
250
+ Array of moving averages.
251
+ """
252
+ n = len(data)
253
+ result = np.zeros(n - window_size + 1, dtype=np.float64)
254
+
255
+ for i in prange(len(result)):
256
+ total = 0.0
257
+ for j in range(window_size):
258
+ total += data[i + j]
259
+ result[i] = total / window_size
260
+
261
+ return result
262
+
263
+
264
+ @njit(cache=True) # type: ignore[misc,untyped-decorator]
265
+ def argrelextrema_numba(
266
+ data: np.ndarray, # type: ignore[type-arg]
267
+ comparator: int,
268
+ order: int = 1,
269
+ ) -> np.ndarray: # type: ignore[type-arg]
270
+ """Find relative extrema (peaks/valleys) with Numba acceleration.
271
+
272
+ Args:
273
+ data: Input signal data.
274
+ comparator: 1 for maxima (peaks), -1 for minima (valleys).
275
+ order: How many points on each side to use for comparison.
276
+
277
+ Returns:
278
+ Array of indices where extrema occur.
279
+ """
280
+ extrema = []
281
+ n = len(data)
282
+
283
+ for i in range(order, n - order):
284
+ is_extremum = True
285
+
286
+ for j in range(1, order + 1):
287
+ if comparator > 0: # Maximum
288
+ if data[i] <= data[i - j] or data[i] <= data[i + j]:
289
+ is_extremum = False
290
+ break
291
+ else: # Minimum
292
+ if data[i] >= data[i - j] or data[i] >= data[i + j]:
293
+ is_extremum = False
294
+ break
295
+
296
+ if is_extremum:
297
+ extrema.append(i)
298
+
299
+ return np.array(extrema, dtype=np.int64)
300
+
301
+
302
+ @njit(cache=True) # type: ignore[misc,untyped-decorator]
303
+ def interpolate_linear_numba(
304
+ x: np.ndarray, # type: ignore[type-arg]
305
+ y: np.ndarray, # type: ignore[type-arg]
306
+ x_new: np.ndarray, # type: ignore[type-arg]
307
+ ) -> np.ndarray: # type: ignore[type-arg]
308
+ """Linear interpolation with Numba acceleration.
309
+
310
+ Args:
311
+ x: Original x coordinates (must be sorted).
312
+ y: Original y values.
313
+ x_new: New x coordinates to interpolate.
314
+
315
+ Returns:
316
+ Interpolated y values at x_new.
317
+ """
318
+ n = len(x)
319
+ m = len(x_new)
320
+ y_new = np.zeros(m, dtype=np.float64)
321
+
322
+ for i in range(m):
323
+ xi = x_new[i]
324
+
325
+ # Binary search for bracketing indices
326
+ left = 0
327
+ right = n - 1
328
+
329
+ while left < right - 1:
330
+ mid = (left + right) // 2
331
+ if x[mid] <= xi:
332
+ left = mid
333
+ else:
334
+ right = mid
335
+
336
+ # Linear interpolation
337
+ if xi <= x[0]:
338
+ y_new[i] = y[0]
339
+ elif xi >= x[n - 1]:
340
+ y_new[i] = y[n - 1]
341
+ else:
342
+ x0, x1 = x[left], x[right]
343
+ y0, y1 = y[left], y[right]
344
+ t = (xi - x0) / (x1 - x0)
345
+ y_new[i] = y0 + t * (y1 - y0)
346
+
347
+ return y_new
348
+
349
+
350
+ __all__ = [
351
+ "HAS_NUMBA",
352
+ "argrelextrema_numba",
353
+ "find_crossings_numba",
354
+ "get_optimal_numba_config",
355
+ "guvectorize",
356
+ "interpolate_linear_numba",
357
+ "jit",
358
+ "moving_average_numba",
359
+ "njit",
360
+ "prange",
361
+ "vectorize",
362
+ ]