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,485 @@
1
+ """Custom measurement framework for user-defined measurements.
2
+
3
+ This module implements a framework for defining and registering custom
4
+ measurements that integrate seamlessly with batch processing and export.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import inspect
10
+ from dataclasses import dataclass, field
11
+ from typing import TYPE_CHECKING, Any
12
+
13
+ from .registry import AlgorithmRegistry
14
+
15
+ if TYPE_CHECKING:
16
+ from collections.abc import Callable
17
+
18
+ from ..core.types import WaveformTrace
19
+
20
+
21
+ @dataclass
22
+ class MeasurementDefinition:
23
+ """Definition of a custom measurement with metadata.
24
+
25
+ Defines a measurement function along with metadata about units, category,
26
+ and documentation. Measurements can be registered globally and used in
27
+ batch processing.
28
+
29
+ Attributes:
30
+ name: Unique name for the measurement.
31
+ func: Callable that computes the measurement.
32
+ units: Units of measurement (e.g., 'V', 'Hz', 's', 'ratio').
33
+ category: Measurement category (e.g., 'amplitude', 'timing', 'frequency').
34
+ description: Human-readable description.
35
+ tags: Optional tags for categorization and search.
36
+
37
+ Example:
38
+ >>> import oscura as osc
39
+ >>> def calculate_crest_factor(trace, **kwargs):
40
+ ... peak = abs(trace.data).max()
41
+ ... rms = (trace.data ** 2).mean() ** 0.5
42
+ ... return peak / rms
43
+ >>> osc.register_measurement(
44
+ ... name='crest_factor',
45
+ ... func=calculate_crest_factor,
46
+ ... units='ratio',
47
+ ... category='amplitude'
48
+ ... )
49
+ >>> cf = osc.measure(trace, 'crest_factor')
50
+
51
+ Advanced Example:
52
+ >>> # Define measurement with full metadata
53
+ >>> slew_rate_defn = osc.MeasurementDefinition(
54
+ ... name='max_slew_rate',
55
+ ... func=lambda trace: abs(trace.derivative()).max(),
56
+ ... units='V/s',
57
+ ... category='edge',
58
+ ... description='Maximum slew rate in trace',
59
+ ... tags=['edge', 'derivative', 'speed']
60
+ ... )
61
+ >>> osc.register_measurement(slew_rate_defn)
62
+
63
+ References:
64
+ API-008: Custom Measurement Framework
65
+ API-006: Algorithm Override Hooks
66
+ """
67
+
68
+ name: str
69
+ func: Callable[[WaveformTrace], float]
70
+ units: str
71
+ category: str
72
+ description: str = ""
73
+ tags: list[str] = field(default_factory=list)
74
+
75
+ def __post_init__(self) -> None:
76
+ """Validate measurement definition.
77
+
78
+ Raises:
79
+ ValueError: If measurement name is empty.
80
+ TypeError: If func is not callable or has invalid signature.
81
+ """
82
+ if not self.name:
83
+ raise ValueError("Measurement name cannot be empty")
84
+
85
+ if not callable(self.func):
86
+ raise TypeError(f"Measurement func must be callable, got {type(self.func).__name__}")
87
+
88
+ # Validate function signature
89
+ self._validate_signature()
90
+
91
+ def _validate_signature(self) -> None:
92
+ """Validate that function has correct signature.
93
+
94
+ Measurement functions should accept (trace, **kwargs) -> float.
95
+
96
+ Raises:
97
+ TypeError: If signature is invalid.
98
+ """
99
+ sig = inspect.signature(self.func)
100
+ params = list(sig.parameters.values())
101
+
102
+ # Should have at least one parameter (trace)
103
+ if len(params) == 0:
104
+ raise TypeError(
105
+ f"Measurement function must accept at least one parameter "
106
+ f"(trace). Got {self.func.__name__} with no parameters."
107
+ )
108
+
109
+ # Check if first parameter could accept WaveformTrace
110
+ first_param = params[0]
111
+ if first_param.kind in (
112
+ inspect.Parameter.VAR_POSITIONAL,
113
+ inspect.Parameter.VAR_KEYWORD,
114
+ ):
115
+ raise TypeError(
116
+ f"First parameter must be a regular parameter (trace), got {first_param.kind}"
117
+ )
118
+
119
+ def __call__(self, trace: WaveformTrace, **kwargs: Any) -> float:
120
+ """Call measurement function.
121
+
122
+ Args:
123
+ trace: WaveformTrace to measure.
124
+ **kwargs: Additional parameters for measurement.
125
+
126
+ Returns:
127
+ Measured value.
128
+
129
+ Example:
130
+ >>> defn = MeasurementDefinition(
131
+ ... name='peak',
132
+ ... func=lambda trace: abs(trace.data).max(),
133
+ ... units='V',
134
+ ... category='amplitude'
135
+ ... )
136
+ >>> value = defn(trace)
137
+ """
138
+ return self.func(trace, **kwargs)
139
+
140
+ def __repr__(self) -> str:
141
+ """String representation.
142
+
143
+ Returns:
144
+ String representation of the measurement definition.
145
+ """
146
+ return (
147
+ f"MeasurementDefinition(name='{self.name}', "
148
+ f"units='{self.units}', category='{self.category}')"
149
+ )
150
+
151
+
152
+ class MeasurementRegistry:
153
+ """Registry for custom measurements.
154
+
155
+ Manages registration and lookup of custom measurements. Integrates with
156
+ the AlgorithmRegistry for storage.
157
+
158
+ Example:
159
+ >>> registry = MeasurementRegistry()
160
+ >>> registry.register(
161
+ ... name='crest_factor',
162
+ ... func=calculate_crest_factor,
163
+ ... units='ratio',
164
+ ... category='amplitude'
165
+ ... )
166
+ >>> measurement = registry.get('crest_factor')
167
+ >>> value = measurement(trace)
168
+
169
+ References:
170
+ API-008: Custom Measurement Framework
171
+ """
172
+
173
+ MEASUREMENT_CATEGORY = "measurement"
174
+
175
+ def __init__(self) -> None:
176
+ """Initialize measurement registry."""
177
+ self._definitions: dict[str, MeasurementDefinition] = {}
178
+ self._algorithm_registry = AlgorithmRegistry()
179
+
180
+ def register(
181
+ self,
182
+ name: str | None = None,
183
+ func: Callable[[WaveformTrace], float] | None = None,
184
+ units: str | None = None,
185
+ category: str | None = None,
186
+ description: str = "",
187
+ tags: list[str] | None = None,
188
+ definition: MeasurementDefinition | None = None,
189
+ ) -> None:
190
+ """Register a custom measurement.
191
+
192
+ Can be called with individual parameters or with a MeasurementDefinition.
193
+
194
+ Args:
195
+ name: Measurement name (required if definition not provided).
196
+ func: Measurement function (required if definition not provided).
197
+ units: Units of measurement (required if definition not provided).
198
+ category: Measurement category (required if definition not provided).
199
+ description: Optional description.
200
+ tags: Optional tags.
201
+ definition: Pre-built MeasurementDefinition (alternative to individual args).
202
+
203
+ Raises:
204
+ ValueError: If required parameters missing or name already exists.
205
+
206
+ Example:
207
+ >>> registry = MeasurementRegistry()
208
+ >>> # Register with individual parameters
209
+ >>> registry.register(
210
+ ... name='peak',
211
+ ... func=lambda trace: abs(trace.data).max(),
212
+ ... units='V',
213
+ ... category='amplitude'
214
+ ... )
215
+ >>> # Register with definition
216
+ >>> defn = MeasurementDefinition(...)
217
+ >>> registry.register(definition=defn)
218
+ """
219
+ # Handle definition argument
220
+ if definition is not None:
221
+ defn = definition
222
+ else:
223
+ # Validate required parameters
224
+ if name is None or func is None or units is None or category is None:
225
+ raise ValueError(
226
+ "Must provide either 'definition' or all of (name, func, units, category)"
227
+ )
228
+
229
+ defn = MeasurementDefinition(
230
+ name=name,
231
+ func=func,
232
+ units=units,
233
+ category=category,
234
+ description=description,
235
+ tags=tags or [],
236
+ )
237
+
238
+ # Check for duplicates
239
+ if defn.name in self._definitions:
240
+ raise ValueError(f"Measurement '{defn.name}' already registered")
241
+
242
+ # Register in both registries
243
+ self._definitions[defn.name] = defn
244
+ self._algorithm_registry.register(
245
+ name=defn.name,
246
+ func=defn.func,
247
+ category=self.MEASUREMENT_CATEGORY,
248
+ validate=False, # Already validated by MeasurementDefinition
249
+ )
250
+
251
+ def get(self, name: str) -> MeasurementDefinition:
252
+ """Get measurement definition by name.
253
+
254
+ Args:
255
+ name: Measurement name.
256
+
257
+ Returns:
258
+ MeasurementDefinition for the measurement.
259
+
260
+ Raises:
261
+ KeyError: If measurement not found.
262
+
263
+ Example:
264
+ >>> measurement = registry.get('crest_factor')
265
+ >>> value = measurement(trace)
266
+ """
267
+ if name not in self._definitions:
268
+ available = list(self._definitions.keys())
269
+ raise KeyError(f"Measurement '{name}' not found. Available: {available}")
270
+
271
+ return self._definitions[name]
272
+
273
+ def has_measurement(self, name: str) -> bool:
274
+ """Check if measurement is registered.
275
+
276
+ Args:
277
+ name: Measurement name.
278
+
279
+ Returns:
280
+ True if measurement is registered.
281
+
282
+ Example:
283
+ >>> if registry.has_measurement('crest_factor'):
284
+ ... cf = registry.get('crest_factor')(trace)
285
+ """
286
+ return name in self._definitions
287
+
288
+ def list_measurements(
289
+ self,
290
+ category: str | None = None,
291
+ tags: list[str] | None = None,
292
+ ) -> list[str]:
293
+ """List registered measurements.
294
+
295
+ Args:
296
+ category: Filter by category (optional).
297
+ tags: Filter by tags (optional).
298
+
299
+ Returns:
300
+ List of measurement names.
301
+
302
+ Example:
303
+ >>> # List all measurements
304
+ >>> all_measurements = registry.list_measurements()
305
+ >>> # List amplitude measurements
306
+ >>> amplitude = registry.list_measurements(category='amplitude')
307
+ >>> # List measurements with 'edge' tag
308
+ >>> edge_measurements = registry.list_measurements(tags=['edge'])
309
+ """
310
+ measurements = []
311
+
312
+ for name, defn in self._definitions.items():
313
+ # Filter by category
314
+ if category is not None and defn.category != category:
315
+ continue
316
+
317
+ # Filter by tags
318
+ if tags is not None and not any(tag in defn.tags for tag in tags):
319
+ continue
320
+
321
+ measurements.append(name)
322
+
323
+ return measurements
324
+
325
+ def get_metadata(self, name: str) -> dict[str, Any]:
326
+ """Get metadata for a measurement.
327
+
328
+ Args:
329
+ name: Measurement name.
330
+
331
+ Returns:
332
+ Dictionary with measurement metadata.
333
+
334
+ Example:
335
+ >>> metadata = registry.get_metadata('crest_factor')
336
+ >>> print(f"Units: {metadata['units']}")
337
+ >>> print(f"Category: {metadata['category']}")
338
+ """
339
+ defn = self.get(name)
340
+ return {
341
+ "name": defn.name,
342
+ "units": defn.units,
343
+ "category": defn.category,
344
+ "description": defn.description,
345
+ "tags": defn.tags,
346
+ }
347
+
348
+ def unregister(self, name: str) -> None:
349
+ """Unregister a measurement.
350
+
351
+ Args:
352
+ name: Measurement name.
353
+
354
+ Example:
355
+ >>> registry.unregister('crest_factor')
356
+ """
357
+ if name in self._definitions:
358
+ del self._definitions[name]
359
+
360
+ if self._algorithm_registry.has_algorithm(self.MEASUREMENT_CATEGORY, name):
361
+ self._algorithm_registry.unregister(self.MEASUREMENT_CATEGORY, name)
362
+
363
+
364
+ # Global measurement registry
365
+ _registry = MeasurementRegistry()
366
+
367
+
368
+ def register_measurement(
369
+ name: str | None = None,
370
+ func: Callable[[WaveformTrace], float] | None = None,
371
+ units: str | None = None,
372
+ category: str | None = None,
373
+ description: str = "",
374
+ tags: list[str] | None = None,
375
+ definition: MeasurementDefinition | None = None,
376
+ ) -> None:
377
+ """Register a custom measurement in the global registry.
378
+
379
+ Convenience function for registering measurements without accessing
380
+ the registry directly.
381
+
382
+ Args:
383
+ name: Measurement name.
384
+ func: Measurement function.
385
+ units: Units of measurement.
386
+ category: Measurement category.
387
+ description: Optional description.
388
+ tags: Optional tags.
389
+ definition: Pre-built MeasurementDefinition.
390
+
391
+ Example:
392
+ >>> import oscura as osc
393
+ >>> def calculate_crest_factor(trace, **kwargs):
394
+ ... peak = abs(trace.data).max()
395
+ ... rms = (trace.data ** 2).mean() ** 0.5
396
+ ... return peak / rms
397
+ >>> osc.register_measurement(
398
+ ... name='crest_factor',
399
+ ... func=calculate_crest_factor,
400
+ ... units='ratio',
401
+ ... category='amplitude'
402
+ ... )
403
+
404
+ References:
405
+ API-008: Custom Measurement Framework
406
+ """
407
+ _registry.register(
408
+ name=name,
409
+ func=func,
410
+ units=units,
411
+ category=category,
412
+ description=description,
413
+ tags=tags,
414
+ definition=definition,
415
+ )
416
+
417
+
418
+ def measure(trace: WaveformTrace, name: str, **kwargs: Any) -> float:
419
+ """Execute a registered measurement.
420
+
421
+ Args:
422
+ trace: WaveformTrace to measure.
423
+ name: Measurement name.
424
+ **kwargs: Additional parameters for the measurement.
425
+
426
+ Returns:
427
+ Measured value.
428
+
429
+ Example:
430
+ >>> import oscura as osc
431
+ >>> cf = osc.measure(trace, 'crest_factor')
432
+ >>> print(f"Crest factor: {cf:.2f}")
433
+
434
+ References:
435
+ API-008: Custom Measurement Framework
436
+ """
437
+ defn = _registry.get(name)
438
+ return defn(trace, **kwargs)
439
+
440
+
441
+ def list_measurements(
442
+ category: str | None = None,
443
+ tags: list[str] | None = None,
444
+ ) -> list[str]:
445
+ """List registered measurements.
446
+
447
+ Args:
448
+ category: Filter by category (optional).
449
+ tags: Filter by tags (optional).
450
+
451
+ Returns:
452
+ List of measurement names.
453
+
454
+ Example:
455
+ >>> import oscura as osc
456
+ >>> measurements = osc.list_measurements(category='amplitude')
457
+ >>> print(f"Amplitude measurements: {measurements}")
458
+
459
+ References:
460
+ API-008: Custom Measurement Framework
461
+ """
462
+ return _registry.list_measurements(category=category, tags=tags)
463
+
464
+
465
+ def get_measurement_registry() -> MeasurementRegistry:
466
+ """Get the global measurement registry.
467
+
468
+ Returns:
469
+ Global MeasurementRegistry instance.
470
+
471
+ Example:
472
+ >>> registry = osc.get_measurement_registry()
473
+ >>> metadata = registry.get_metadata('crest_factor')
474
+ """
475
+ return _registry
476
+
477
+
478
+ __all__ = [
479
+ "MeasurementDefinition",
480
+ "MeasurementRegistry",
481
+ "get_measurement_registry",
482
+ "list_measurements",
483
+ "measure",
484
+ "register_measurement",
485
+ ]