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,541 @@
1
+ """Edge case handling utilities for TraceKit.
2
+
3
+ This module provides utilities for gracefully handling edge cases including
4
+ empty inputs, single-sample traces, and NaN/Inf values.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.core.edge_cases import (
9
+ ... validate_signal,
10
+ ... handle_empty_trace,
11
+ ... sanitize_signal
12
+ ... )
13
+ >>> validated = validate_signal(signal, min_samples=10)
14
+ >>> clean_signal = sanitize_signal(noisy_signal)
15
+
16
+ References:
17
+ - IEEE 754 floating-point standard
18
+ - NumPy NaN handling best practices
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import warnings
24
+ from typing import TYPE_CHECKING
25
+
26
+ import numpy as np
27
+
28
+ if TYPE_CHECKING:
29
+ from numpy.typing import NDArray
30
+
31
+
32
+ class EmptyTraceError(Exception):
33
+ """Exception raised when trace has no data.
34
+
35
+ : Empty trace returns informative error, not crash.
36
+
37
+ Example:
38
+ >>> from oscura.core.edge_cases import EmptyTraceError
39
+ >>> raise EmptyTraceError("Cannot analyze empty trace (0 samples)")
40
+
41
+ References:
42
+ EDGE-002: Graceful Empty/Short Signal Handling
43
+ """
44
+
45
+ def __init__(self, message: str = "Trace is empty (0 samples)") -> None:
46
+ """Initialize EmptyTraceError.
47
+
48
+ Args:
49
+ message: Error message (default: "Trace is empty (0 samples)")
50
+ """
51
+ super().__init__(message)
52
+
53
+
54
+ class InsufficientSamplesError(Exception):
55
+ """Exception raised when trace has insufficient samples.
56
+
57
+ : Too-short trace warns and adapts.
58
+
59
+ Attributes:
60
+ required: Minimum samples required
61
+ available: Actual samples available
62
+
63
+ Example:
64
+ >>> from oscura.core.edge_cases import InsufficientSamplesError
65
+ >>> raise InsufficientSamplesError("Need at least 100 samples", 100, 10)
66
+
67
+ References:
68
+ EDGE-002: Graceful Empty/Short Signal Handling
69
+ """
70
+
71
+ def __init__(self, message: str, required: int, available: int) -> None:
72
+ """Initialize InsufficientSamplesError.
73
+
74
+ Args:
75
+ message: Error message
76
+ required: Minimum samples required
77
+ available: Actual samples available
78
+ """
79
+ self.required = required
80
+ self.available = available
81
+ full_message = f"{message} (required: {required}, available: {available})"
82
+ super().__init__(full_message)
83
+
84
+
85
+ def validate_signal(
86
+ signal: NDArray[np.float64],
87
+ *,
88
+ min_samples: int = 1,
89
+ allow_empty: bool = False,
90
+ name: str = "signal",
91
+ ) -> NDArray[np.float64]:
92
+ """Validate signal array for basic requirements.
93
+
94
+ : Empty trace returns informative error, not crash.
95
+ Checks for empty arrays and minimum sample requirements.
96
+
97
+ Args:
98
+ signal: Input signal array
99
+ min_samples: Minimum required samples (default: 1)
100
+ allow_empty: Allow empty arrays (default: False)
101
+ name: Signal name for error messages (default: "signal")
102
+
103
+ Returns:
104
+ Validated signal array
105
+
106
+ Raises:
107
+ EmptyTraceError: If signal is empty and allow_empty=False
108
+ InsufficientSamplesError: If signal has fewer than min_samples
109
+ ValueError: If signal is not 1D or has invalid shape
110
+
111
+ Example:
112
+ >>> import numpy as np
113
+ >>> from oscura.core.edge_cases import validate_signal
114
+ >>> signal = np.array([1.0, 2.0, 3.0])
115
+ >>> validated = validate_signal(signal, min_samples=2)
116
+ >>> # Empty signal raises error
117
+ >>> validate_signal(np.array([])) # Raises EmptyTraceError
118
+
119
+ References:
120
+ EDGE-002: Graceful Empty/Short Signal Handling
121
+ """
122
+ # Check if array
123
+ if not isinstance(signal, np.ndarray):
124
+ raise ValueError(f"{name} must be a numpy array, got {type(signal)}")
125
+
126
+ # Check dimensions
127
+ if signal.ndim != 1:
128
+ raise ValueError(f"{name} must be 1-dimensional, got {signal.ndim}D")
129
+
130
+ # Check for empty
131
+ n_samples = len(signal)
132
+ if n_samples == 0:
133
+ if allow_empty:
134
+ return signal
135
+ else:
136
+ raise EmptyTraceError(f"{name} is empty (0 samples)")
137
+
138
+ # Check minimum samples
139
+ if n_samples < min_samples:
140
+ raise InsufficientSamplesError(
141
+ f"{name} has too few samples",
142
+ required=min_samples,
143
+ available=n_samples,
144
+ )
145
+
146
+ return signal
147
+
148
+
149
+ def handle_empty_trace(default_value: float = np.nan) -> NDArray[np.float64]:
150
+ """Return a safe default for empty trace operations.
151
+
152
+ : Empty trace returns informative error, not crash.
153
+ Provides graceful fallback for operations on empty traces.
154
+
155
+ Args:
156
+ default_value: Default value to return (default: NaN)
157
+
158
+ Returns:
159
+ Single-element array with default value
160
+
161
+ Example:
162
+ >>> from oscura.core.edge_cases import handle_empty_trace
163
+ >>> result = handle_empty_trace(0.0)
164
+ >>> print(result)
165
+ [0.]
166
+
167
+ References:
168
+ EDGE-002: Graceful Empty/Short Signal Handling
169
+ """
170
+ return np.array([default_value])
171
+
172
+
173
+ def check_single_sample(
174
+ signal: NDArray[np.float64],
175
+ operation: str = "operation",
176
+ ) -> bool:
177
+ """Check if signal has only one sample and warn.
178
+
179
+ : Handle traces with 1 sample.
180
+ Warns user that statistical operations may not be meaningful.
181
+
182
+ Args:
183
+ signal: Input signal
184
+ operation: Operation name for warning message
185
+
186
+ Returns:
187
+ True if signal has only 1 sample
188
+
189
+ Example:
190
+ >>> import numpy as np
191
+ >>> from oscura.core.edge_cases import check_single_sample
192
+ >>> signal = np.array([42.0])
193
+ >>> if check_single_sample(signal, "FFT"):
194
+ ... print("Cannot compute FFT on single sample")
195
+
196
+ References:
197
+ EDGE-002: Graceful Empty/Short Signal Handling
198
+ """
199
+ if len(signal) == 1:
200
+ warnings.warn(
201
+ f"Signal has only 1 sample. {operation} may not produce meaningful results.",
202
+ UserWarning,
203
+ stacklevel=2,
204
+ )
205
+ return True
206
+ return False
207
+
208
+
209
+ def sanitize_signal(
210
+ signal: NDArray[np.float64],
211
+ *,
212
+ replace_nan: float | str = "interpolate",
213
+ replace_inf: float | str = "clip",
214
+ warn: bool = True,
215
+ ) -> NDArray[np.float64]:
216
+ """Remove or replace NaN and Inf values in signal.
217
+
218
+ : Handle NaN and Inf values gracefully.
219
+ Cleans signal data for robust analysis.
220
+
221
+ Args:
222
+ signal: Input signal array
223
+ replace_nan: How to handle NaN:
224
+ - "interpolate": Linear interpolation (default)
225
+ - "zero": Replace with 0
226
+ - "remove": Remove samples (changes length)
227
+ - float: Replace with specific value
228
+ replace_inf: How to handle Inf:
229
+ - "clip": Clip to min/max of finite values (default)
230
+ - "zero": Replace with 0
231
+ - "remove": Remove samples (changes length)
232
+ - float: Replace with specific value
233
+ warn: Issue warning if NaN/Inf found (default: True)
234
+
235
+ Returns:
236
+ Sanitized signal array
237
+
238
+ Raises:
239
+ ValueError: If replace_nan or replace_inf option is invalid.
240
+
241
+ Example:
242
+ >>> import numpy as np
243
+ >>> from oscura.core.edge_cases import sanitize_signal
244
+ >>> signal = np.array([1.0, np.nan, 3.0, np.inf, 5.0])
245
+ >>> clean = sanitize_signal(signal)
246
+ >>> print(clean)
247
+ [1. 2. 3. 5. 5.]
248
+
249
+ References:
250
+ EDGE-003: NaN/Inf Handling
251
+ """
252
+ signal = signal.copy() # Don't modify input
253
+ n_nan = np.sum(np.isnan(signal))
254
+ n_inf = np.sum(np.isinf(signal))
255
+
256
+ # Warn if issues found
257
+ if warn and (n_nan > 0 or n_inf > 0):
258
+ warnings.warn(
259
+ f"Signal contains {n_nan} NaN and {n_inf} Inf values. Applying sanitization.",
260
+ UserWarning,
261
+ stacklevel=2,
262
+ )
263
+
264
+ # Handle NaN
265
+ if n_nan > 0:
266
+ if replace_nan == "interpolate":
267
+ signal = _interpolate_nan(signal)
268
+ elif replace_nan == "zero":
269
+ signal[np.isnan(signal)] = 0.0
270
+ elif replace_nan == "remove":
271
+ signal = signal[~np.isnan(signal)]
272
+ elif isinstance(replace_nan, int | float):
273
+ signal[np.isnan(signal)] = float(replace_nan)
274
+ else:
275
+ raise ValueError(f"Invalid replace_nan option: {replace_nan}")
276
+
277
+ # Handle Inf
278
+ if n_inf > 0:
279
+ if replace_inf == "clip":
280
+ finite_mask = np.isfinite(signal)
281
+ if np.any(finite_mask):
282
+ min_val = np.min(signal[finite_mask])
283
+ max_val = np.max(signal[finite_mask])
284
+ signal[signal == np.inf] = max_val
285
+ signal[signal == -np.inf] = min_val
286
+ else:
287
+ signal[np.isinf(signal)] = 0.0
288
+ elif replace_inf == "zero":
289
+ signal[np.isinf(signal)] = 0.0
290
+ elif replace_inf == "remove":
291
+ signal = signal[~np.isinf(signal)]
292
+ elif isinstance(replace_inf, int | float):
293
+ signal[np.isinf(signal)] = float(replace_inf)
294
+ else:
295
+ raise ValueError(f"Invalid replace_inf option: {replace_inf}")
296
+
297
+ return signal
298
+
299
+
300
+ def _interpolate_nan(signal: NDArray[np.float64]) -> NDArray[np.float64]:
301
+ """Interpolate NaN values using linear interpolation.
302
+
303
+ Args:
304
+ signal: Signal with NaN values
305
+
306
+ Returns:
307
+ Signal with NaN values interpolated
308
+
309
+ References:
310
+ EDGE-003: NaN/Inf Handling
311
+ """
312
+ # Find NaN locations
313
+ nan_mask = np.isnan(signal)
314
+
315
+ if not np.any(nan_mask):
316
+ return signal
317
+
318
+ # Get valid indices and values
319
+ valid_mask = ~nan_mask
320
+ if not np.any(valid_mask):
321
+ # All NaN - replace with zeros
322
+ return np.zeros_like(signal)
323
+
324
+ valid_indices = np.where(valid_mask)[0]
325
+ valid_values = signal[valid_mask]
326
+
327
+ # Interpolate
328
+ nan_indices = np.where(nan_mask)[0]
329
+ interpolated = np.interp(nan_indices, valid_indices, valid_values)
330
+
331
+ # Replace NaN with interpolated values
332
+ result = signal.copy()
333
+ result[nan_mask] = interpolated
334
+
335
+ return result
336
+
337
+
338
+ def check_signal_quality(
339
+ signal: NDArray[np.float64],
340
+ *,
341
+ clipping_threshold: float = 0.95,
342
+ noise_floor_db: float = -60.0,
343
+ dc_offset_max: float = 0.1,
344
+ ) -> SignalQualityReport:
345
+ """Check signal quality and detect common issues.
346
+
347
+ : Detect clipping, noise floor, and DC offset problems.
348
+ Analyzes signal for quality issues that may affect results.
349
+
350
+ Args:
351
+ signal: Input signal array
352
+ clipping_threshold: Fraction of range for clipping detection (default: 0.95)
353
+ noise_floor_db: Expected noise floor in dB (default: -60)
354
+ dc_offset_max: Maximum acceptable DC offset (default: 0.1)
355
+
356
+ Returns:
357
+ SignalQualityReport with detected issues
358
+
359
+ Example:
360
+ >>> import numpy as np
361
+ >>> from oscura.core.edge_cases import check_signal_quality
362
+ >>> signal = np.random.randn(1000) + 0.5 # Signal with DC offset
363
+ >>> quality = check_signal_quality(signal, dc_offset_max=0.1)
364
+ >>> if quality.dc_offset_excessive:
365
+ ... print(f"DC offset: {quality.dc_offset:.3f}")
366
+
367
+ References:
368
+ EDGE-001: Signal Quality Warnings
369
+ """
370
+ # Calculate statistics
371
+ min_val = float(np.min(signal))
372
+ max_val = float(np.max(signal))
373
+ mean_val = float(np.mean(signal))
374
+ std_val = float(np.std(signal))
375
+
376
+ # Check for clipping
377
+ signal_range = max_val - min_val
378
+ clipping_detected = False
379
+ clipping_percent = 0.0
380
+
381
+ if signal_range > 0:
382
+ # Count samples near limits
383
+ upper_thresh = min_val + signal_range * clipping_threshold
384
+ lower_thresh = min_val + signal_range * (1 - clipping_threshold)
385
+
386
+ n_clipped = np.sum((signal >= upper_thresh) | (signal <= lower_thresh))
387
+ clipping_percent = float(100.0 * n_clipped / len(signal))
388
+ clipping_detected = clipping_percent > 1.0 # >1% clipping
389
+
390
+ # Check noise floor (estimate SNR)
391
+ if std_val > 0:
392
+ snr_db = 20 * np.log10(abs(mean_val) / std_val) if abs(mean_val) > 0 else -np.inf
393
+ else:
394
+ snr_db = np.inf
395
+
396
+ high_noise = snr_db < noise_floor_db
397
+
398
+ # Check DC offset
399
+ dc_offset = abs(mean_val)
400
+ dc_offset_excessive = dc_offset > dc_offset_max
401
+
402
+ return SignalQualityReport(
403
+ clipping_detected=clipping_detected,
404
+ clipping_percent=clipping_percent,
405
+ adc_min=min_val,
406
+ adc_max=max_val,
407
+ high_noise=high_noise,
408
+ noise_floor_db=float(snr_db),
409
+ snr_db=float(snr_db),
410
+ dc_offset_excessive=dc_offset_excessive,
411
+ dc_offset=dc_offset,
412
+ )
413
+
414
+
415
+ class SignalQualityReport:
416
+ """Report of signal quality issues.
417
+
418
+ : Warnings included in measurement results.
419
+
420
+ Attributes:
421
+ clipping_detected: Whether clipping was detected
422
+ clipping_percent: Percentage of samples clipped
423
+ adc_min: Minimum signal value
424
+ adc_max: Maximum signal value
425
+ high_noise: Whether noise floor is excessive
426
+ noise_floor_db: Estimated noise floor in dB
427
+ snr_db: Signal-to-noise ratio in dB
428
+ dc_offset_excessive: Whether DC offset is excessive
429
+ dc_offset: DC offset value
430
+
431
+ Example:
432
+ >>> from oscura.core.edge_cases import check_signal_quality
433
+ >>> quality = check_signal_quality(signal)
434
+ >>> print(quality.summary())
435
+
436
+ References:
437
+ EDGE-001: Signal Quality Warnings
438
+ """
439
+
440
+ def __init__(
441
+ self,
442
+ *,
443
+ clipping_detected: bool = False,
444
+ clipping_percent: float = 0.0,
445
+ adc_min: float = 0.0,
446
+ adc_max: float = 0.0,
447
+ high_noise: bool = False,
448
+ noise_floor_db: float = 0.0,
449
+ snr_db: float = 0.0,
450
+ dc_offset_excessive: bool = False,
451
+ dc_offset: float = 0.0,
452
+ ) -> None:
453
+ """Initialize SignalQualityReport.
454
+
455
+ Args:
456
+ clipping_detected: Clipping detected flag
457
+ clipping_percent: Percentage of clipped samples
458
+ adc_min: Minimum signal value
459
+ adc_max: Maximum signal value
460
+ high_noise: High noise flag
461
+ noise_floor_db: Noise floor in dB
462
+ snr_db: Signal-to-noise ratio in dB
463
+ dc_offset_excessive: Excessive DC offset flag
464
+ dc_offset: DC offset value
465
+ """
466
+ self.clipping_detected = clipping_detected
467
+ self.clipping_percent = clipping_percent
468
+ self.adc_min = adc_min
469
+ self.adc_max = adc_max
470
+ self.high_noise = high_noise
471
+ self.noise_floor_db = noise_floor_db
472
+ self.snr_db = snr_db
473
+ self.dc_offset_excessive = dc_offset_excessive
474
+ self.dc_offset = dc_offset
475
+
476
+ def has_issues(self) -> bool:
477
+ """Check if any quality issues were detected.
478
+
479
+ Returns:
480
+ True if any issues found
481
+
482
+ Example:
483
+ >>> if quality.has_issues():
484
+ ... print(quality.summary())
485
+
486
+ References:
487
+ EDGE-001: Signal Quality Warnings
488
+ """
489
+ return self.clipping_detected or self.high_noise or self.dc_offset_excessive
490
+
491
+ def summary(self) -> str:
492
+ """Get text summary of quality issues.
493
+
494
+ Returns:
495
+ Summary string
496
+
497
+ Example:
498
+ >>> print(quality.summary())
499
+ Signal Quality Report:
500
+ ✓ No clipping detected
501
+ ⚠ High noise floor: -45.2 dB
502
+ ✓ DC offset within limits
503
+
504
+ References:
505
+ EDGE-001: Signal Quality Warnings
506
+ """
507
+ lines = ["Signal Quality Report:"]
508
+
509
+ # Clipping
510
+ if self.clipping_detected:
511
+ lines.append(f" ⚠ Clipping detected: {self.clipping_percent:.1f}% of samples")
512
+ lines.append(f" ADC range: {self.adc_min:.3f} to {self.adc_max:.3f}")
513
+ else:
514
+ lines.append(" ✓ No clipping detected")
515
+
516
+ # Noise
517
+ if self.high_noise:
518
+ lines.append(f" ⚠ High noise floor: {self.noise_floor_db:.1f} dB")
519
+ lines.append(f" SNR: {self.snr_db:.1f} dB")
520
+ else:
521
+ lines.append(f" ✓ Noise floor acceptable (SNR: {self.snr_db:.1f} dB)")
522
+
523
+ # DC offset
524
+ if self.dc_offset_excessive:
525
+ lines.append(f" ⚠ DC offset: {self.dc_offset:.3f}")
526
+ else:
527
+ lines.append(" ✓ DC offset within limits")
528
+
529
+ return "\n".join(lines)
530
+
531
+
532
+ __all__ = [
533
+ "EmptyTraceError",
534
+ "InsufficientSamplesError",
535
+ "SignalQualityReport",
536
+ "check_signal_quality",
537
+ "check_single_sample",
538
+ "handle_empty_trace",
539
+ "sanitize_signal",
540
+ "validate_signal",
541
+ ]