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,207 @@
1
+ """Visualization rendering functions for DPI-aware output.
2
+
3
+ This module provides DPI-aware rendering configuration for adapting
4
+ plot quality and parameters based on target output device (screen vs print).
5
+
6
+
7
+ Example:
8
+ >>> from oscura.visualization.render import configure_dpi_rendering
9
+ >>> config = configure_dpi_rendering("publication")
10
+ >>> fig = plt.figure(dpi=config['dpi'], figsize=config['figsize'])
11
+
12
+ References:
13
+ - matplotlib DPI scaling best practices
14
+ - Print quality standards (300-600 DPI)
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from typing import Any, Literal
20
+
21
+ RenderPreset = Literal["screen", "print", "publication"]
22
+
23
+
24
+ def configure_dpi_rendering(
25
+ preset: RenderPreset = "screen",
26
+ *,
27
+ custom_dpi: int | None = None,
28
+ dpi: int | None = None,
29
+ figsize: tuple[float, float] = (10, 6),
30
+ baseline_dpi: float = 96.0,
31
+ ) -> dict[str, Any]:
32
+ """Configure DPI-aware rendering parameters.
33
+
34
+ Adapts plot rendering quality and parameters based on target DPI
35
+ for print (300-600 DPI) versus screen (72-96 DPI) with export presets.
36
+
37
+ Args:
38
+ preset: Rendering preset ("screen", "print", "publication").
39
+ custom_dpi: Custom DPI override (ignores preset).
40
+ dpi: Alias for custom_dpi.
41
+ figsize: Figure size in inches (width, height).
42
+ baseline_dpi: Baseline DPI for scaling calculations (default 96).
43
+
44
+ Returns:
45
+ Dictionary with rendering configuration:
46
+ - dpi: Target DPI
47
+ - figsize: Figure size
48
+ - font_scale: Font size scale factor
49
+ - line_scale: Line width scale factor
50
+ - marker_scale: Marker size scale factor
51
+ - antialias: Whether to enable anti-aliasing
52
+ - format: Recommended file format
53
+ - style_params: Additional matplotlib rcParams
54
+
55
+ Raises:
56
+ ValueError: If preset is invalid.
57
+
58
+ Example:
59
+ >>> config = configure_dpi_rendering("print")
60
+ >>> plt.rcParams.update(config['style_params'])
61
+ >>> fig = plt.figure(dpi=config['dpi'], figsize=config['figsize'])
62
+
63
+ References:
64
+ VIS-017: DPI-Aware Rendering
65
+ """
66
+ # Define preset configurations
67
+ presets = {
68
+ "screen": {
69
+ "dpi": 96,
70
+ "font_family": "sans-serif",
71
+ "antialias": True,
72
+ "format": "png",
73
+ "description": "Screen display (96 DPI)",
74
+ },
75
+ "print": {
76
+ "dpi": 300,
77
+ "font_family": "sans-serif",
78
+ "antialias": False,
79
+ "format": "pdf",
80
+ "description": "Print output (300 DPI)",
81
+ },
82
+ "publication": {
83
+ "dpi": 600,
84
+ "font_family": "serif",
85
+ "antialias": False,
86
+ "format": "pdf",
87
+ "description": "Publication quality (600 DPI)",
88
+ },
89
+ }
90
+
91
+ # Handle dpi alias
92
+ if dpi is not None and custom_dpi is None:
93
+ custom_dpi = dpi
94
+
95
+ if preset not in presets and custom_dpi is None:
96
+ raise ValueError(f"Invalid preset: {preset}. Must be one of {list(presets.keys())}")
97
+
98
+ # Get preset configuration
99
+ if custom_dpi is not None:
100
+ target_dpi = custom_dpi
101
+ preset_config = {
102
+ "font_family": "sans-serif",
103
+ "antialias": True,
104
+ "format": "png" if target_dpi <= 150 else "pdf",
105
+ "description": f"Custom ({target_dpi} DPI)",
106
+ }
107
+ else:
108
+ preset_config = presets[preset]
109
+ target_dpi = preset_config["dpi"] # type: ignore[assignment]
110
+
111
+ # Calculate scale factors based on DPI
112
+ # Scale factor = target_dpi / baseline_dpi
113
+ scale = target_dpi / baseline_dpi
114
+
115
+ # Font size scaling: proportional to DPI
116
+ # Baseline font sizes at 96 DPI: default 10pt
117
+ font_scale = scale
118
+
119
+ # Line width scaling: proportional to DPI
120
+ # Baseline line width at 96 DPI: 1.0 pt
121
+ line_scale = scale
122
+
123
+ # Marker size scaling: proportional to DPI
124
+ # Baseline marker size at 96 DPI: 6.0 pt
125
+ marker_scale = scale
126
+
127
+ # Build matplotlib rcParams for this preset
128
+ style_params = {
129
+ "figure.dpi": target_dpi,
130
+ "savefig.dpi": target_dpi,
131
+ "font.family": preset_config["font_family"],
132
+ "font.size": 10 * font_scale,
133
+ "axes.titlesize": 12 * font_scale,
134
+ "axes.labelsize": 10 * font_scale,
135
+ "xtick.labelsize": 9 * font_scale,
136
+ "ytick.labelsize": 9 * font_scale,
137
+ "legend.fontsize": 9 * font_scale,
138
+ "lines.linewidth": 1.0 * line_scale,
139
+ "lines.markersize": 6.0 * marker_scale,
140
+ "patch.linewidth": 1.0 * line_scale,
141
+ "grid.linewidth": 0.5 * line_scale,
142
+ "axes.linewidth": 0.8 * line_scale,
143
+ "xtick.major.width": 0.8 * line_scale,
144
+ "ytick.major.width": 0.8 * line_scale,
145
+ "xtick.minor.width": 0.6 * line_scale,
146
+ "ytick.minor.width": 0.6 * line_scale,
147
+ }
148
+
149
+ # Anti-aliasing settings
150
+ if preset_config["antialias"]:
151
+ style_params["lines.antialiased"] = True
152
+ style_params["patch.antialiased"] = True
153
+ style_params["text.antialiased"] = True
154
+ else:
155
+ # Disable for high-DPI print (cleaner output)
156
+ style_params["lines.antialiased"] = False
157
+ style_params["patch.antialiased"] = False
158
+ style_params["text.antialiased"] = False
159
+
160
+ # Publication-specific settings
161
+ if preset == "publication":
162
+ style_params["font.family"] = "serif"
163
+ style_params["mathtext.fontset"] = "cm" # Computer Modern for LaTeX
164
+ style_params["axes.grid"] = True
165
+ style_params["grid.alpha"] = 0.3
166
+ style_params["axes.axisbelow"] = True
167
+
168
+ return {
169
+ "dpi": target_dpi,
170
+ "figsize": figsize,
171
+ "font_scale": font_scale,
172
+ "line_scale": line_scale,
173
+ "marker_scale": marker_scale,
174
+ "antialias": preset_config["antialias"],
175
+ "format": preset_config["format"],
176
+ "style_params": style_params,
177
+ "description": preset_config["description"],
178
+ "preset": preset if custom_dpi is None else "custom",
179
+ }
180
+
181
+
182
+ def apply_rendering_config(config: dict[str, Any]) -> None:
183
+ """Apply rendering configuration to matplotlib rcParams.
184
+
185
+ Args:
186
+ config: Configuration dictionary from configure_dpi_rendering().
187
+
188
+ Raises:
189
+ ImportError: If matplotlib is not available.
190
+
191
+ Example:
192
+ >>> config = configure_dpi_rendering("print")
193
+ >>> apply_rendering_config(config)
194
+ """
195
+ try:
196
+ import matplotlib.pyplot as plt
197
+
198
+ plt.rcParams.update(config["style_params"])
199
+ except ImportError:
200
+ raise ImportError("matplotlib is required for rendering configuration") # noqa: B904
201
+
202
+
203
+ __all__ = [
204
+ "RenderPreset",
205
+ "apply_rendering_config",
206
+ "configure_dpi_rendering",
207
+ ]
@@ -0,0 +1,444 @@
1
+ """Rendering optimization for large datasets and streaming updates.
2
+
3
+ This module provides level-of-detail rendering, progressive rendering,
4
+ and memory-efficient plot updates for high-performance visualization.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.visualization.rendering import render_with_lod
9
+ >>> time_lod, data_lod = render_with_lod(time, data, screen_width=1920)
10
+
11
+ References:
12
+ - Level-of-detail (LOD) rendering techniques
13
+ - Min-max envelope for waveform rendering
14
+ - Progressive rendering algorithms
15
+ - Streaming data visualization
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from typing import TYPE_CHECKING, Literal
21
+
22
+ import numpy as np
23
+
24
+ if TYPE_CHECKING:
25
+ from numpy.typing import NDArray
26
+
27
+
28
+ def render_with_lod(
29
+ time: NDArray[np.float64],
30
+ data: NDArray[np.float64],
31
+ *,
32
+ screen_width: int = 1920,
33
+ samples_per_pixel: float = 2.0,
34
+ max_points: int = 100_000,
35
+ method: Literal["minmax", "lttb", "uniform"] = "minmax",
36
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
37
+ """Render signal with level-of-detail decimation./VIS-019.
38
+
39
+ Reduces number of points while preserving visual appearance using
40
+ intelligent downsampling. Target: <100k points at any zoom level.
41
+
42
+ Args:
43
+ time: Time array.
44
+ data: Signal data array.
45
+ screen_width: Screen width in pixels.
46
+ samples_per_pixel: Target samples per pixel (2.0 recommended).
47
+ max_points: Maximum points to render (default: 100k).
48
+ method: Decimation method ("minmax", "lttb", "uniform").
49
+
50
+ Returns:
51
+ Tuple of (decimated_time, decimated_data).
52
+
53
+ Raises:
54
+ ValueError: If arrays are invalid or method unknown.
55
+
56
+ Example:
57
+ >>> # 1M sample signal decimated for 1920px display
58
+ >>> time_lod, data_lod = render_with_lod(time, data, screen_width=1920)
59
+ >>> print(len(data_lod)) # ~3840 samples (2 per pixel)
60
+
61
+ References:
62
+ VIS-017: Performance - LOD Rendering
63
+ VIS-019: Memory-Efficient Plot Rendering
64
+ """
65
+ if len(time) == 0 or len(data) == 0:
66
+ raise ValueError("Time or data array is empty")
67
+
68
+ if len(time) != len(data):
69
+ raise ValueError(f"Time and data length mismatch: {len(time)} vs {len(data)}")
70
+
71
+ # Calculate target point count
72
+ target_points = min(
73
+ int(screen_width * samples_per_pixel),
74
+ max_points,
75
+ )
76
+
77
+ # Skip decimation if already below target
78
+ if len(data) <= target_points:
79
+ return (time, data)
80
+
81
+ # Apply decimation
82
+ if method == "uniform":
83
+ return _decimate_uniform(time, data, target_points)
84
+ elif method == "minmax":
85
+ return _decimate_minmax_envelope(time, data, target_points)
86
+ elif method == "lttb":
87
+ return _decimate_lttb(time, data, target_points)
88
+ else:
89
+ raise ValueError(f"Unknown decimation method: {method}")
90
+
91
+
92
+ def _decimate_uniform(
93
+ time: NDArray[np.float64],
94
+ data: NDArray[np.float64],
95
+ target_points: int,
96
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
97
+ """Uniform stride decimation (simple but loses peaks).
98
+
99
+ Args:
100
+ time: Time array.
101
+ data: Signal data array.
102
+ target_points: Target number of points after decimation.
103
+
104
+ Returns:
105
+ Tuple of (decimated_time, decimated_data).
106
+ """
107
+ stride = len(data) // target_points
108
+ stride = max(stride, 1)
109
+
110
+ indices = np.arange(0, len(data), stride)[:target_points]
111
+ return (time[indices], data[indices])
112
+
113
+
114
+ def _decimate_minmax_envelope(
115
+ time: NDArray[np.float64],
116
+ data: NDArray[np.float64],
117
+ target_points: int,
118
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
119
+ """Min-max envelope decimation - preserves peaks and valleys.
120
+
121
+ This method ensures all signal extrema are preserved in the decimated view.
122
+
123
+ Args:
124
+ time: Time array.
125
+ data: Signal data array.
126
+ target_points: Target number of points after decimation.
127
+
128
+ Returns:
129
+ Tuple of (decimated_time, decimated_data).
130
+ """
131
+ # Calculate bucket size (each bucket contributes 2 points: min and max)
132
+ bucket_size = len(data) // (target_points // 2)
133
+
134
+ if bucket_size < 1:
135
+ return (time, data)
136
+
137
+ decimated_time = []
138
+ decimated_data = []
139
+
140
+ for i in range(0, len(data), bucket_size):
141
+ bucket_data = data[i : i + bucket_size]
142
+ bucket_time = time[i : i + bucket_size]
143
+
144
+ if len(bucket_data) == 0:
145
+ continue
146
+
147
+ # Find min and max in bucket
148
+ min_idx = np.argmin(bucket_data)
149
+ max_idx = np.argmax(bucket_data)
150
+
151
+ # Add in chronological order
152
+ if min_idx < max_idx:
153
+ decimated_time.extend([bucket_time[min_idx], bucket_time[max_idx]])
154
+ decimated_data.extend([bucket_data[min_idx], bucket_data[max_idx]])
155
+ else:
156
+ decimated_time.extend([bucket_time[max_idx], bucket_time[min_idx]])
157
+ decimated_data.extend([bucket_data[max_idx], bucket_data[min_idx]])
158
+
159
+ return (np.array(decimated_time), np.array(decimated_data))
160
+
161
+
162
+ def _decimate_lttb(
163
+ time: NDArray[np.float64],
164
+ data: NDArray[np.float64],
165
+ target_points: int,
166
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
167
+ """Largest Triangle Three Buckets decimation.
168
+
169
+ Preserves visual shape by maximizing triangle areas.
170
+
171
+ Args:
172
+ time: Time array.
173
+ data: Signal data array.
174
+ target_points: Target number of points after decimation.
175
+
176
+ Returns:
177
+ Tuple of (decimated_time, decimated_data).
178
+ """
179
+ if len(data) <= target_points:
180
+ return (time, data)
181
+
182
+ # Always include first and last points
183
+ sampled_time = [time[0]]
184
+ sampled_data = [data[0]]
185
+
186
+ bucket_size = (len(data) - 2) / (target_points - 2)
187
+
188
+ prev_idx = 0
189
+
190
+ for i in range(target_points - 2):
191
+ # Average point of next bucket
192
+ avg_range_start = int((i + 1) * bucket_size) + 1
193
+ avg_range_end = int((i + 2) * bucket_size) + 1
194
+ avg_range_end = min(avg_range_end, len(data))
195
+
196
+ if avg_range_start < avg_range_end:
197
+ avg_time = np.mean(time[avg_range_start:avg_range_end])
198
+ avg_data = np.mean(data[avg_range_start:avg_range_end])
199
+ else:
200
+ avg_time = time[-1]
201
+ avg_data = data[-1]
202
+
203
+ # Current bucket range
204
+ range_start = int(i * bucket_size) + 1
205
+ range_end = int((i + 1) * bucket_size) + 1
206
+ range_end = min(range_end, len(data) - 1)
207
+
208
+ # Find point in bucket that forms largest triangle
209
+ max_area = -1.0
210
+ max_idx = range_start
211
+
212
+ for idx in range(range_start, range_end):
213
+ # Calculate triangle area
214
+ area = abs(
215
+ (time[prev_idx] - avg_time) * (data[idx] - data[prev_idx])
216
+ - (time[prev_idx] - time[idx]) * (avg_data - data[prev_idx])
217
+ )
218
+
219
+ if area > max_area:
220
+ max_area = area
221
+ max_idx = idx
222
+
223
+ sampled_time.append(time[max_idx])
224
+ sampled_data.append(data[max_idx])
225
+ prev_idx = max_idx
226
+
227
+ # Always include last point
228
+ sampled_time.append(time[-1])
229
+ sampled_data.append(data[-1])
230
+
231
+ return (np.array(sampled_time), np.array(sampled_data))
232
+
233
+
234
+ def progressive_render(
235
+ time: NDArray[np.float64],
236
+ data: NDArray[np.float64],
237
+ *,
238
+ viewport: tuple[float, float] | None = None,
239
+ priority: Literal["viewport", "full"] = "viewport",
240
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
241
+ """Progressive rendering - render visible viewport first.
242
+
243
+ Args:
244
+ time: Time array.
245
+ data: Signal data array.
246
+ viewport: Visible viewport (t_min, t_max). None = full range.
247
+ priority: Rendering priority ("viewport" = visible first, "full" = all data).
248
+
249
+ Returns:
250
+ Tuple of (time, data) for priority rendering.
251
+
252
+ Example:
253
+ >>> # Render only visible portion for fast initial display
254
+ >>> time_vis, data_vis = progressive_render(
255
+ ... time, data, viewport=(0, 0.001), priority="viewport"
256
+ ... )
257
+
258
+ References:
259
+ VIS-019: Memory-Efficient Plot Rendering (progressive rendering)
260
+ """
261
+ if viewport is None or priority == "full":
262
+ return (time, data)
263
+
264
+ t_min, t_max = viewport
265
+
266
+ # Find indices within viewport
267
+ mask = (time >= t_min) & (time <= t_max)
268
+ indices = np.where(mask)[0]
269
+
270
+ if len(indices) == 0:
271
+ # Viewport is outside data range
272
+ return (time, data)
273
+
274
+ # Return viewport data first
275
+ viewport_time = time[indices]
276
+ viewport_data = data[indices]
277
+
278
+ return (viewport_time, viewport_data)
279
+
280
+
281
+ def estimate_memory_usage(
282
+ n_samples: int,
283
+ n_channels: int = 1,
284
+ dtype: type = np.float64,
285
+ ) -> float:
286
+ """Estimate memory usage for plot rendering.
287
+
288
+ Args:
289
+ n_samples: Number of samples per channel.
290
+ n_channels: Number of channels.
291
+ dtype: Data type for arrays.
292
+
293
+ Returns:
294
+ Estimated memory usage in MB.
295
+
296
+ Example:
297
+ >>> mem_mb = estimate_memory_usage(1_000_000, n_channels=4)
298
+ >>> print(f"Memory: {mem_mb:.1f} MB")
299
+
300
+ References:
301
+ VIS-019: Memory-Efficient Plot Rendering
302
+ """
303
+ # Bytes per sample
304
+ if dtype == np.float64:
305
+ bytes_per_sample = 8
306
+ elif dtype == np.float32 or dtype == np.int32:
307
+ bytes_per_sample = 4
308
+ elif dtype == np.int16:
309
+ bytes_per_sample = 2
310
+ else:
311
+ bytes_per_sample = 8 # Default
312
+
313
+ # Total memory: time + data arrays per channel
314
+ # Time array: n_samples * bytes_per_sample
315
+ # Data arrays: n_channels * n_samples * bytes_per_sample
316
+ total_bytes = (1 + n_channels) * n_samples * bytes_per_sample
317
+
318
+ # Convert to MB
319
+ return total_bytes / (1024 * 1024)
320
+
321
+
322
+ def downsample_for_memory(
323
+ time: NDArray[np.float64],
324
+ data: NDArray[np.float64],
325
+ *,
326
+ target_memory_mb: float = 50.0,
327
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
328
+ """Downsample signal to meet memory target.
329
+
330
+ Args:
331
+ time: Time array.
332
+ data: Signal data array.
333
+ target_memory_mb: Target memory usage in MB.
334
+
335
+ Returns:
336
+ Tuple of (decimated_time, decimated_data).
337
+
338
+ Example:
339
+ >>> # Reduce 100MB dataset to 50MB
340
+ >>> time_ds, data_ds = downsample_for_memory(time, data, target_memory_mb=50.0)
341
+
342
+ References:
343
+ VIS-019: Memory-Efficient Plot Rendering (memory target <50MB per subplot)
344
+ """
345
+ current_memory = estimate_memory_usage(len(data), n_channels=1)
346
+
347
+ if current_memory <= target_memory_mb:
348
+ # Already within target
349
+ return (time, data)
350
+
351
+ # Calculate required decimation factor
352
+ decimation_factor = current_memory / target_memory_mb
353
+ target_samples = int(len(data) / decimation_factor)
354
+
355
+ # Use min-max to preserve features
356
+ return _decimate_minmax_envelope(time, data, target_samples)
357
+
358
+
359
+ class StreamingRenderer:
360
+ """Streaming plot renderer for real-time data updates.
361
+
362
+ Handles incremental data updates without full redraws for performance.
363
+
364
+ Example:
365
+ >>> renderer = StreamingRenderer(max_samples=10000)
366
+ >>> renderer.append(new_time, new_data)
367
+ >>> time, data = renderer.get_render_data()
368
+
369
+ References:
370
+ VIS-018: Streaming Plot Updates
371
+ """
372
+
373
+ def __init__(
374
+ self,
375
+ *,
376
+ max_samples: int = 10_000,
377
+ decimation_method: Literal["minmax", "lttb", "uniform"] = "minmax",
378
+ ):
379
+ """Initialize streaming renderer.
380
+
381
+ Args:
382
+ max_samples: Maximum samples to keep in buffer.
383
+ decimation_method: Decimation method for buffer management.
384
+ """
385
+ self.max_samples = max_samples
386
+ self.decimation_method = decimation_method
387
+
388
+ self._time: list[float] = []
389
+ self._data: list[float] = []
390
+
391
+ def append(
392
+ self,
393
+ time: NDArray[np.float64],
394
+ data: NDArray[np.float64],
395
+ ) -> None:
396
+ """Append new data to streaming buffer.
397
+
398
+ Args:
399
+ time: New time samples.
400
+ data: New data samples.
401
+ """
402
+ self._time.extend(time.tolist())
403
+ self._data.extend(data.tolist())
404
+
405
+ # Decimate if buffer exceeds limit
406
+ if len(self._data) > self.max_samples:
407
+ self._decimate_buffer()
408
+
409
+ def _decimate_buffer(self) -> None:
410
+ """Decimate internal buffer to max_samples."""
411
+ time_arr = np.array(self._time)
412
+ data_arr = np.array(self._data)
413
+
414
+ time_dec, data_dec = render_with_lod(
415
+ time_arr,
416
+ data_arr,
417
+ max_points=self.max_samples,
418
+ method=self.decimation_method,
419
+ )
420
+
421
+ self._time = time_dec.tolist()
422
+ self._data = data_dec.tolist()
423
+
424
+ def get_render_data(self) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
425
+ """Get current data for rendering.
426
+
427
+ Returns:
428
+ Tuple of (time, data) arrays.
429
+ """
430
+ return (np.array(self._time), np.array(self._data))
431
+
432
+ def clear(self) -> None:
433
+ """Clear streaming buffer."""
434
+ self._time.clear()
435
+ self._data.clear()
436
+
437
+
438
+ __all__ = [
439
+ "StreamingRenderer",
440
+ "downsample_for_memory",
441
+ "estimate_memory_usage",
442
+ "progressive_render",
443
+ "render_with_lod",
444
+ ]