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,338 @@
1
+ """Auto-detection utilities for signal analysis.
2
+
3
+ This module provides utilities for automatic detection of signal
4
+ parameters such as baud rate, logic levels, and protocol types.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.utils.autodetect import detect_baud_rate
9
+ >>> baudrate = detect_baud_rate(trace)
10
+ >>> print(f"Detected baud rate: {baudrate}")
11
+
12
+ References:
13
+ Standard baud rates. and UART specifications.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from typing import TYPE_CHECKING, Literal
19
+
20
+ import numpy as np
21
+
22
+ from oscura.core.types import DigitalTrace, WaveformTrace
23
+
24
+ if TYPE_CHECKING:
25
+ from numpy.typing import NDArray
26
+
27
+ # Standard baud rates (RS-232, UART, CAN, etc.)
28
+ STANDARD_BAUD_RATES: tuple[int, ...] = (
29
+ 300,
30
+ 600,
31
+ 1200,
32
+ 2400,
33
+ 4800,
34
+ 9600,
35
+ 14400,
36
+ 19200,
37
+ 28800,
38
+ 38400,
39
+ 57600,
40
+ 76800,
41
+ 115200,
42
+ 230400,
43
+ 250000, # CAN common
44
+ 460800,
45
+ 500000, # CAN common
46
+ 576000,
47
+ 921600,
48
+ 1000000, # 1 Mbps
49
+ 1500000,
50
+ 2000000,
51
+ 3000000,
52
+ 4000000,
53
+ )
54
+
55
+
56
+ def detect_baud_rate(
57
+ trace: WaveformTrace | DigitalTrace,
58
+ *,
59
+ threshold: float | Literal["auto"] = "auto",
60
+ method: Literal["pulse_width", "edge_timing", "autocorr"] = "pulse_width",
61
+ tolerance: float = 0.05,
62
+ return_confidence: bool = False,
63
+ ) -> int | tuple[int, float]:
64
+ """Detect baud rate from signal timing.
65
+
66
+ Analyzes pulse widths or edge timing to determine the symbol rate,
67
+ then maps to the nearest standard baud rate.
68
+
69
+ Args:
70
+ trace: Input trace (analog or digital).
71
+ threshold: Threshold for analog to digital conversion.
72
+ method: Detection method:
73
+ - "pulse_width": Minimum pulse width (default)
74
+ - "edge_timing": Edge-to-edge timing analysis
75
+ - "autocorr": Autocorrelation peak detection
76
+ tolerance: Tolerance for matching to standard rate (default 5%).
77
+ return_confidence: If True, also return confidence score.
78
+
79
+ Returns:
80
+ Detected baud rate (nearest standard), or tuple of (rate, confidence)
81
+ if return_confidence=True.
82
+
83
+ Raises:
84
+ ValueError: If unknown detection method specified.
85
+
86
+ Example:
87
+ >>> baudrate = detect_baud_rate(trace)
88
+ >>> print(f"Detected: {baudrate} bps")
89
+
90
+ >>> baudrate, confidence = detect_baud_rate(trace, return_confidence=True)
91
+ >>> print(f"Detected: {baudrate} bps ({confidence:.0%} confidence)")
92
+
93
+ References:
94
+ RS-232 Standard Baud Rates
95
+ """
96
+ # Get digital representation
97
+ if isinstance(trace, WaveformTrace):
98
+ from oscura.analyzers.digital.extraction import to_digital
99
+
100
+ digital_trace = to_digital(trace, threshold=threshold)
101
+ data = digital_trace.data
102
+ else:
103
+ data = trace.data
104
+
105
+ sample_rate = trace.metadata.sample_rate
106
+
107
+ if method == "pulse_width":
108
+ bit_period = _detect_via_pulse_width(data, sample_rate)
109
+ elif method == "edge_timing":
110
+ bit_period = _detect_via_edge_timing(data, sample_rate)
111
+ elif method == "autocorr":
112
+ bit_period = _detect_via_autocorrelation(data, sample_rate)
113
+ else:
114
+ raise ValueError(f"Unknown method: {method}")
115
+
116
+ if bit_period <= 0 or np.isnan(bit_period):
117
+ if return_confidence:
118
+ return 0, 0.0
119
+ return 0
120
+
121
+ # Convert to baud rate
122
+ measured_rate = 1.0 / bit_period
123
+
124
+ # Find nearest standard rate
125
+ best_rate = 0
126
+ best_error = float("inf")
127
+
128
+ for std_rate in STANDARD_BAUD_RATES:
129
+ error = abs(measured_rate - std_rate) / std_rate
130
+ if error < best_error:
131
+ best_error = error
132
+ best_rate = std_rate
133
+
134
+ # Compute confidence
135
+ confidence = max(0.0, 1.0 - best_error / tolerance) if best_error <= tolerance else 0.0
136
+
137
+ if return_confidence:
138
+ return best_rate, confidence
139
+
140
+ return best_rate
141
+
142
+
143
+ def _detect_via_pulse_width(data: NDArray[np.bool_], sample_rate: float) -> float:
144
+ """Detect bit period from minimum pulse width.
145
+
146
+ Args:
147
+ data: Digital signal data.
148
+ sample_rate: Sample rate in Hz.
149
+
150
+ Returns:
151
+ Estimated bit period in seconds.
152
+ """
153
+ # Find pulse widths (runs of consecutive values)
154
+ pulse_widths = []
155
+
156
+ current_value = data[0]
157
+ run_length = 1
158
+
159
+ for i in range(1, len(data)):
160
+ if data[i] == current_value:
161
+ run_length += 1
162
+ else:
163
+ pulse_widths.append(run_length)
164
+ current_value = data[i]
165
+ run_length = 1
166
+
167
+ # Add final run
168
+ pulse_widths.append(run_length)
169
+
170
+ if len(pulse_widths) == 0:
171
+ return 0.0
172
+
173
+ pulse_widths_arr = np.array(pulse_widths, dtype=np.float64)
174
+
175
+ # Filter out very short pulses (noise)
176
+ min_pulse = max(2, np.min(pulse_widths_arr[pulse_widths_arr > 1]))
177
+
178
+ # The minimum pulse width corresponds to a single bit
179
+ # Use the mode of small pulses for robustness
180
+ small_pulses = pulse_widths_arr[pulse_widths_arr <= min_pulse * 1.5]
181
+
182
+ bit_samples = min_pulse if len(small_pulses) == 0 else np.median(small_pulses)
183
+
184
+ return float(bit_samples / sample_rate)
185
+
186
+
187
+ def _detect_via_edge_timing(data: NDArray[np.bool_], sample_rate: float) -> float:
188
+ """Detect bit period from edge-to-edge timing.
189
+
190
+ Args:
191
+ data: Digital signal data.
192
+ sample_rate: Sample rate in Hz.
193
+
194
+ Returns:
195
+ Estimated bit period in seconds.
196
+ """
197
+ # Find all edges
198
+ transitions = np.diff(data.astype(np.int8))
199
+ edge_indices = np.where(transitions != 0)[0]
200
+
201
+ if len(edge_indices) < 2:
202
+ return 0.0
203
+
204
+ # Compute edge intervals
205
+ intervals = np.diff(edge_indices).astype(np.float64)
206
+
207
+ if len(intervals) == 0:
208
+ return 0.0
209
+
210
+ # Intervals should be multiples of bit period
211
+ # Find GCD-like value using histogram
212
+ min_interval = np.min(intervals)
213
+ max_check = min(min_interval * 2, np.median(intervals))
214
+
215
+ # The bit period is the smallest common interval
216
+ # Use histogram to find the cluster
217
+ bins = np.arange(1, max_check + 1)
218
+ hist, _ = np.histogram(intervals, bins=bins)
219
+
220
+ if len(hist) == 0 or np.max(hist) == 0:
221
+ bit_samples = min_interval
222
+ else:
223
+ # Find first significant peak
224
+ threshold = np.max(hist) * 0.3
225
+ peaks = np.where(hist >= threshold)[0]
226
+
227
+ if len(peaks) > 0:
228
+ bit_samples = peaks[0] + 1 # +1 for bin offset
229
+ else:
230
+ bit_samples = min_interval
231
+
232
+ return float(bit_samples / sample_rate)
233
+
234
+
235
+ def _detect_via_autocorrelation(data: NDArray[np.bool_], sample_rate: float) -> float:
236
+ """Detect bit period via autocorrelation.
237
+
238
+ Args:
239
+ data: Digital signal data.
240
+ sample_rate: Sample rate in Hz.
241
+
242
+ Returns:
243
+ Estimated bit period in seconds.
244
+ """
245
+ # Convert to float for correlation
246
+ signal = data.astype(np.float64) * 2 - 1 # Map to [-1, 1]
247
+
248
+ # Remove DC
249
+ signal = signal - np.mean(signal)
250
+
251
+ # Compute autocorrelation
252
+ n = len(signal)
253
+ max_lag = min(n // 2, int(sample_rate / 300)) # Limit to reasonable range
254
+
255
+ autocorr = np.correlate(signal[: max_lag * 2], signal[: max_lag * 2], mode="full")
256
+ autocorr = autocorr[len(autocorr) // 2 :] # Keep positive lags
257
+
258
+ # Normalize
259
+ autocorr = autocorr / autocorr[0]
260
+
261
+ # Find first significant peak after lag 0
262
+ # Skip initial samples to avoid lag-0 region
263
+ min_lag = max(2, max_lag // 100)
264
+
265
+ # Find local maxima
266
+ peaks = []
267
+ for i in range(min_lag, len(autocorr) - 1):
268
+ if autocorr[i] > autocorr[i - 1] and autocorr[i] > autocorr[i + 1]:
269
+ if autocorr[i] > 0.3: # Significance threshold
270
+ peaks.append((i, autocorr[i]))
271
+
272
+ if len(peaks) == 0:
273
+ return 0.0
274
+
275
+ # First significant peak is likely the bit period
276
+ bit_samples = peaks[0][0]
277
+
278
+ return float(bit_samples / sample_rate)
279
+
280
+
281
+ def detect_logic_family(
282
+ trace: WaveformTrace,
283
+ *,
284
+ return_confidence: bool = False,
285
+ ) -> str | tuple[str, float]:
286
+ """Detect logic family from signal levels.
287
+
288
+ Analyzes voltage levels to identify TTL, CMOS, LVTTL, LVCMOS variants.
289
+
290
+ Args:
291
+ trace: Input analog trace.
292
+ return_confidence: If True, also return confidence score.
293
+
294
+ Returns:
295
+ Logic family name (e.g., "TTL", "LVCMOS_3V3"), or tuple of
296
+ (family, confidence) if return_confidence=True.
297
+ """
298
+ from oscura.analyzers.digital.extraction import LOGIC_FAMILIES
299
+
300
+ data = trace.data
301
+
302
+ # Get voltage levels
303
+ v_low = float(np.percentile(data, 10))
304
+ v_high = float(np.percentile(data, 90))
305
+
306
+ # Estimate VCC from high level
307
+ v_cc_est = v_high * 1.1 # Add margin
308
+
309
+ best_family = "TTL"
310
+ best_score = 0.0
311
+
312
+ for family, levels in LOGIC_FAMILIES.items():
313
+ vcc = levels["VCC"]
314
+ vol = levels["VOL_max"]
315
+ voh = levels["VOH_min"]
316
+
317
+ # Score based on how well levels match
318
+ low_match = 1.0 - min(1.0, abs(v_low - vol) / 0.5)
319
+ high_match = 1.0 - min(1.0, abs(v_high - voh) / 0.5)
320
+ vcc_match = 1.0 - min(1.0, abs(v_cc_est - vcc) / vcc)
321
+
322
+ score = (low_match + high_match + vcc_match) / 3
323
+
324
+ if score > best_score:
325
+ best_score = score
326
+ best_family = family
327
+
328
+ if return_confidence:
329
+ return best_family, best_score
330
+
331
+ return best_family
332
+
333
+
334
+ __all__ = [
335
+ "STANDARD_BAUD_RATES",
336
+ "detect_baud_rate",
337
+ "detect_logic_family",
338
+ ]
oscura/utils/buffer.py ADDED
@@ -0,0 +1,389 @@
1
+ """Buffer utilities for streaming data.
2
+
3
+ This module provides circular buffer implementation for
4
+ streaming data with O(1) operations.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.utils.buffer import CircularBuffer
9
+ >>> buf = CircularBuffer(1000)
10
+ >>> buf.append(value)
11
+ >>> recent = buf.get_last(100)
12
+
13
+ References:
14
+ Ring buffer data structure
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from typing import TYPE_CHECKING, Any, TypeVar, overload
20
+
21
+ import numpy as np
22
+
23
+ if TYPE_CHECKING:
24
+ from numpy.typing import DTypeLike, NDArray
25
+
26
+ T = TypeVar("T")
27
+
28
+
29
+ class CircularBuffer[T]:
30
+ """Fixed-size circular buffer with O(1) operations.
31
+
32
+ Thread-safe for single producer, single consumer pattern.
33
+
34
+ Args:
35
+ capacity: Maximum buffer size.
36
+ dtype: NumPy dtype for numeric buffers.
37
+
38
+ Attributes:
39
+ capacity: Buffer capacity.
40
+ count: Current number of items.
41
+
42
+ Example:
43
+ >>> buf = CircularBuffer(1000, dtype=np.float64)
44
+ >>> for value in stream:
45
+ ... buf.append(value)
46
+ ... if buf.is_full():
47
+ ... process(buf.get_all())
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ capacity: int,
53
+ dtype: DTypeLike | None = None,
54
+ ) -> None:
55
+ """Initialize circular buffer.
56
+
57
+ Args:
58
+ capacity: Maximum buffer size.
59
+ dtype: NumPy dtype. If None, uses object array.
60
+ """
61
+ self._capacity = capacity
62
+ self._dtype = dtype
63
+
64
+ if dtype is not None:
65
+ self._data: NDArray[Any] = np.zeros(capacity, dtype=dtype)
66
+ else:
67
+ self._data = np.empty(capacity, dtype=object)
68
+
69
+ self._head = 0 # Next write position
70
+ self._count = 0 # Number of valid items
71
+
72
+ @property
73
+ def capacity(self) -> int:
74
+ """Get buffer capacity."""
75
+ return self._capacity
76
+
77
+ @property
78
+ def count(self) -> int:
79
+ """Get current item count."""
80
+ return self._count
81
+
82
+ def is_empty(self) -> bool:
83
+ """Check if buffer is empty."""
84
+ return self._count == 0
85
+
86
+ def is_full(self) -> bool:
87
+ """Check if buffer is full."""
88
+ return self._count == self._capacity
89
+
90
+ def append(self, value: T) -> None:
91
+ """Append value to buffer.
92
+
93
+ O(1) operation. Overwrites oldest value if full.
94
+
95
+ Args:
96
+ value: Value to append.
97
+ """
98
+ self._data[self._head] = value
99
+ self._head = (self._head + 1) % self._capacity
100
+
101
+ if self._count < self._capacity:
102
+ self._count += 1
103
+
104
+ def extend(self, values: list[T] | NDArray[Any]) -> None:
105
+ """Extend buffer with multiple values.
106
+
107
+ Args:
108
+ values: Values to append.
109
+ """
110
+ for value in values:
111
+ self.append(value)
112
+
113
+ def get_last(self, n: int = 1) -> NDArray[Any]:
114
+ """Get last n items.
115
+
116
+ Args:
117
+ n: Number of items (default 1).
118
+
119
+ Returns:
120
+ Array of last n items (newest first).
121
+ """
122
+ n = min(n, self._count)
123
+
124
+ if n == 0:
125
+ if self._dtype is not None:
126
+ return np.array([], dtype=self._dtype)
127
+ return np.array([], dtype=object)
128
+
129
+ result = np.empty(n, dtype=self._data.dtype)
130
+
131
+ for i in range(n):
132
+ idx = (self._head - 1 - i) % self._capacity
133
+ result[i] = self._data[idx]
134
+
135
+ return result
136
+
137
+ def get_first(self, n: int = 1) -> NDArray[Any]:
138
+ """Get first n items (oldest).
139
+
140
+ Args:
141
+ n: Number of items.
142
+
143
+ Returns:
144
+ Array of first n items (oldest first).
145
+ """
146
+ n = min(n, self._count)
147
+
148
+ if n == 0:
149
+ if self._dtype is not None:
150
+ return np.array([], dtype=self._dtype)
151
+ return np.array([], dtype=object)
152
+
153
+ # Calculate tail position (oldest item)
154
+ tail = (self._head - self._count) % self._capacity
155
+
156
+ result = np.empty(n, dtype=self._data.dtype)
157
+
158
+ for i in range(n):
159
+ idx = (tail + i) % self._capacity
160
+ result[i] = self._data[idx]
161
+
162
+ return result
163
+
164
+ def get_all(self) -> NDArray[Any]:
165
+ """Get all items in order.
166
+
167
+ Returns:
168
+ Array of all items (oldest first).
169
+ """
170
+ return self.get_first(self._count)
171
+
172
+ @overload
173
+ def __getitem__(self, index: int) -> T: ...
174
+
175
+ @overload
176
+ def __getitem__(self, index: slice) -> NDArray[Any]: ...
177
+
178
+ def __getitem__(self, index: int | slice) -> T | NDArray[Any]:
179
+ """Get item(s) by index.
180
+
181
+ Positive indices count from oldest (0 = oldest).
182
+ Negative indices count from newest (-1 = newest).
183
+
184
+ Args:
185
+ index: Integer index or slice.
186
+
187
+ Returns:
188
+ Item or array of items.
189
+
190
+ Raises:
191
+ IndexError: If index out of range.
192
+ """
193
+ if isinstance(index, slice):
194
+ # Convert slice to indices
195
+ start, stop, step = index.indices(self._count)
196
+ result = []
197
+ for i in range(start, stop, step):
198
+ result.append(self[i])
199
+ return np.array(result, dtype=self._data.dtype)
200
+
201
+ if index < 0:
202
+ index = self._count + index
203
+
204
+ if index < 0 or index >= self._count:
205
+ raise IndexError(f"Index {index} out of range [0, {self._count})")
206
+
207
+ # Calculate actual position
208
+ tail = (self._head - self._count) % self._capacity
209
+ actual_idx = (tail + index) % self._capacity
210
+
211
+ return self._data[actual_idx] # type: ignore[no-any-return]
212
+
213
+ def __len__(self) -> int:
214
+ """Get current item count."""
215
+ return self._count
216
+
217
+ def clear(self) -> None:
218
+ """Clear all items."""
219
+ self._head = 0
220
+ self._count = 0
221
+
222
+ def mean(self) -> float:
223
+ """Compute mean of numeric buffer.
224
+
225
+ Returns:
226
+ Mean value, or NaN if empty.
227
+ """
228
+ if self._count == 0:
229
+ return float("nan")
230
+
231
+ return float(np.mean(self.get_all()))
232
+
233
+ def std(self) -> float:
234
+ """Compute standard deviation.
235
+
236
+ Returns:
237
+ Standard deviation, or NaN if empty.
238
+ """
239
+ if self._count < 2:
240
+ return float("nan")
241
+
242
+ return float(np.std(self.get_all()))
243
+
244
+ def min(self) -> T:
245
+ """Get minimum value.
246
+
247
+ Returns:
248
+ Minimum value.
249
+
250
+ Raises:
251
+ ValueError: If buffer is empty.
252
+ """
253
+ if self._count == 0:
254
+ raise ValueError("Buffer is empty")
255
+
256
+ return np.min(self.get_all()) # type: ignore[no-any-return]
257
+
258
+ def max(self) -> T:
259
+ """Get maximum value.
260
+
261
+ Returns:
262
+ Maximum value.
263
+
264
+ Raises:
265
+ ValueError: If buffer is empty.
266
+ """
267
+ if self._count == 0:
268
+ raise ValueError("Buffer is empty")
269
+
270
+ return np.max(self.get_all()) # type: ignore[no-any-return]
271
+
272
+
273
+ class SlidingWindow:
274
+ """Sliding window for time-series analysis.
275
+
276
+ Maintains a window of samples based on time or count.
277
+
278
+ Args:
279
+ window_size: Window size in samples or seconds.
280
+ time_based: If True, window_size is in seconds.
281
+
282
+ Example:
283
+ >>> window = SlidingWindow(1000) # 1000 samples
284
+ >>> for sample, time in stream:
285
+ ... window.add(sample, time)
286
+ ... if window.is_ready():
287
+ ... result = analyze(window.get_data())
288
+ """
289
+
290
+ def __init__(
291
+ self,
292
+ window_size: int | float,
293
+ time_based: bool = False,
294
+ dtype: DTypeLike = np.float64,
295
+ ) -> None:
296
+ """Initialize sliding window.
297
+
298
+ Args:
299
+ window_size: Size in samples or seconds.
300
+ time_based: True for time-based window.
301
+ dtype: Data type for samples.
302
+ """
303
+ self._window_size = window_size
304
+ self._time_based = time_based
305
+
306
+ if time_based:
307
+ # Use large buffer for time-based
308
+ capacity = 100000
309
+ else:
310
+ capacity = int(window_size)
311
+
312
+ self._data = CircularBuffer(capacity, dtype=dtype) # type: ignore[var-annotated]
313
+ self._times = CircularBuffer(capacity, dtype=np.float64) # type: ignore[var-annotated]
314
+
315
+ def add(self, value: float, timestamp: float | None = None) -> None:
316
+ """Add sample to window.
317
+
318
+ Args:
319
+ value: Sample value.
320
+ timestamp: Sample timestamp (required for time-based).
321
+
322
+ Raises:
323
+ ValueError: If timestamp is None for time-based window.
324
+ """
325
+ self._data.append(value)
326
+
327
+ if self._time_based:
328
+ if timestamp is None:
329
+ raise ValueError("Timestamp required for time-based window")
330
+ self._times.append(timestamp)
331
+
332
+ def is_ready(self) -> bool:
333
+ """Check if window is full."""
334
+ if self._time_based:
335
+ if self._times.count < 2:
336
+ return False
337
+ times = self._times.get_all()
338
+ duration = times[-1] - times[0]
339
+ return duration >= self._window_size # type: ignore[no-any-return]
340
+ else:
341
+ return self._data.count >= self._window_size
342
+
343
+ def get_data(self) -> NDArray[np.float64]:
344
+ """Get window data.
345
+
346
+ Returns:
347
+ Array of samples in window.
348
+ """
349
+ if self._time_based:
350
+ # Get samples within time window
351
+ times = self._times.get_all()
352
+ data = self._data.get_all()
353
+
354
+ if len(times) == 0:
355
+ return np.array([], dtype=np.float64)
356
+
357
+ cutoff = times[-1] - self._window_size
358
+ mask = times >= cutoff
359
+
360
+ result: NDArray[np.float64] = data[mask]
361
+ return result
362
+ else:
363
+ result_all: NDArray[np.float64] = self._data.get_all()
364
+ return result_all
365
+
366
+ def get_times(self) -> NDArray[np.float64]:
367
+ """Get timestamps for time-based window.
368
+
369
+ Returns:
370
+ Array of timestamps.
371
+
372
+ Raises:
373
+ ValueError: If not a time-based window.
374
+ """
375
+ if not self._time_based:
376
+ raise ValueError("Not a time-based window")
377
+
378
+ return self._times.get_all()
379
+
380
+ def clear(self) -> None:
381
+ """Clear window."""
382
+ self._data.clear()
383
+ self._times.clear()
384
+
385
+
386
+ __all__ = [
387
+ "CircularBuffer",
388
+ "SlidingWindow",
389
+ ]