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,466 @@
1
+ """Number and value formatting for TraceKit reports.
2
+
3
+ This module provides smart number formatting with SI prefixes,
4
+ significant figures, and contextual annotations.
5
+
6
+
7
+ Example:
8
+ >>> from oscura.reporting import format_with_units, format_with_context
9
+ >>> format_with_units(0.0000023, "s") # "2.3 us"
10
+ >>> format_with_context(2.3e-9, spec=5e-9) # "2.3 ns (spec <5 ns, PASS)"
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import locale as locale_module
16
+ from dataclasses import dataclass
17
+ from datetime import datetime
18
+ from typing import Literal
19
+
20
+ import numpy as np
21
+
22
+ # SI prefixes
23
+ SI_PREFIXES = {
24
+ 24: "Y",
25
+ 21: "Z",
26
+ 18: "E",
27
+ 15: "P",
28
+ 12: "T",
29
+ 9: "G",
30
+ 6: "M",
31
+ 3: "k",
32
+ 0: "",
33
+ -3: "m",
34
+ -6: "u",
35
+ -9: "n",
36
+ -12: "p",
37
+ -15: "f",
38
+ -18: "a",
39
+ -21: "z",
40
+ -24: "y",
41
+ }
42
+
43
+ # Unicode SI prefixes
44
+ SI_PREFIXES_UNICODE = {
45
+ **SI_PREFIXES,
46
+ -6: "\u03bc", # Greek mu
47
+ }
48
+
49
+
50
+ @dataclass
51
+ class NumberFormatter:
52
+ """Configurable number formatter.
53
+
54
+ Attributes:
55
+ sig_figs: Significant figures (default 3).
56
+ auto_scale: Use SI prefixes for scaling.
57
+ engineering_notation: Use engineering notation (10^3, 10^6, etc.).
58
+ unicode_prefixes: Use Unicode characters (e.g., micro symbol).
59
+ min_exp: Minimum exponent before using scientific notation.
60
+ max_exp: Maximum exponent before using scientific notation.
61
+ """
62
+
63
+ sig_figs: int = 3
64
+ auto_scale: bool = True
65
+ engineering_notation: bool = True
66
+ unicode_prefixes: bool = True
67
+ min_exp: int = -3
68
+ max_exp: int = 3
69
+
70
+ def format(
71
+ self,
72
+ value: float,
73
+ unit: str = "",
74
+ *,
75
+ decimal_places: int | None = None,
76
+ ) -> str:
77
+ """Format a numeric value.
78
+
79
+ Args:
80
+ value: Value to format.
81
+ unit: Unit suffix (e.g., "s", "Hz", "V").
82
+ decimal_places: Override significant figures with fixed decimals.
83
+
84
+ Returns:
85
+ Formatted string.
86
+
87
+ Example:
88
+ >>> fmt = NumberFormatter()
89
+ >>> fmt.format(0.0000023, "s")
90
+ '2.30 us'
91
+ """
92
+ if not np.isfinite(value):
93
+ if np.isnan(value):
94
+ return "NaN"
95
+ elif value > 0:
96
+ return "+Inf"
97
+ else:
98
+ return "-Inf"
99
+
100
+ if value == 0:
101
+ return f"0 {unit}".strip()
102
+
103
+ if self.auto_scale and self.engineering_notation:
104
+ return self._format_engineering(value, unit, decimal_places)
105
+ elif self.auto_scale:
106
+ return self._format_scaled(value, unit, decimal_places)
107
+ else:
108
+ return self._format_plain(value, unit, decimal_places)
109
+
110
+ def _format_engineering(
111
+ self,
112
+ value: float,
113
+ unit: str,
114
+ decimal_places: int | None,
115
+ ) -> str:
116
+ """Format with engineering notation (SI prefixes)."""
117
+ abs_value = abs(value)
118
+ sign = "-" if value < 0 else ""
119
+
120
+ # Find appropriate SI prefix
121
+ if abs_value == 0:
122
+ exp = 0
123
+ else:
124
+ exp = int(np.floor(np.log10(abs_value)))
125
+ # Round to nearest multiple of 3
126
+ exp = (exp // 3) * 3
127
+
128
+ # Clamp to available prefixes
129
+ exp = max(-24, min(24, exp))
130
+
131
+ # Get prefix
132
+ prefixes = SI_PREFIXES_UNICODE if self.unicode_prefixes else SI_PREFIXES
133
+ prefix = prefixes.get(exp, "")
134
+
135
+ # Scale value
136
+ scaled = abs_value / (10**exp)
137
+
138
+ # Format with significant figures
139
+ if decimal_places is not None:
140
+ formatted = f"{sign}{scaled:.{decimal_places}f}"
141
+ else:
142
+ # Calculate decimal places from significant figures
143
+ if scaled >= 100:
144
+ decimals = max(0, self.sig_figs - 3)
145
+ elif scaled >= 10:
146
+ decimals = max(0, self.sig_figs - 2)
147
+ elif scaled >= 1:
148
+ decimals = max(0, self.sig_figs - 1)
149
+ else:
150
+ decimals = self.sig_figs
151
+ formatted = f"{sign}{scaled:.{decimals}f}"
152
+
153
+ return f"{formatted} {prefix}{unit}".strip()
154
+
155
+ def _format_scaled(
156
+ self,
157
+ value: float,
158
+ unit: str,
159
+ decimal_places: int | None,
160
+ ) -> str:
161
+ """Format with auto-scaling but not necessarily engineering notation."""
162
+ return self._format_engineering(value, unit, decimal_places)
163
+
164
+ def _format_plain(
165
+ self,
166
+ value: float,
167
+ unit: str,
168
+ decimal_places: int | None,
169
+ ) -> str:
170
+ """Format without scaling."""
171
+ if decimal_places is not None:
172
+ formatted = f"{value:.{decimal_places}f}"
173
+ else:
174
+ formatted = f"{value:.{self.sig_figs}g}"
175
+
176
+ return f"{formatted} {unit}".strip()
177
+
178
+ def format_percentage(self, value: float, *, decimals: int = 1) -> str:
179
+ """Format as percentage.
180
+
181
+ Args:
182
+ value: Value (0-1 or 0-100).
183
+ decimals: Decimal places.
184
+
185
+ Returns:
186
+ Percentage string.
187
+ """
188
+ # Assume value is already in percent if > 1
189
+ if abs(value) <= 1:
190
+ value = value * 100
191
+ return f"{value:.{decimals}f}%"
192
+
193
+ def format_range(
194
+ self,
195
+ min_val: float,
196
+ typ_val: float,
197
+ max_val: float,
198
+ unit: str = "",
199
+ ) -> str:
200
+ """Format min/typ/max range.
201
+
202
+ Args:
203
+ min_val: Minimum value.
204
+ typ_val: Typical value.
205
+ max_val: Maximum value.
206
+ unit: Unit suffix.
207
+
208
+ Returns:
209
+ Formatted range string.
210
+ """
211
+ # Use same scaling for all values
212
+ abs_max = max(abs(min_val), abs(typ_val), abs(max_val))
213
+ exp = 0 if abs_max == 0 else int(np.floor(np.log10(abs_max))) // 3 * 3
214
+ exp = max(-24, min(24, exp))
215
+
216
+ prefixes = SI_PREFIXES_UNICODE if self.unicode_prefixes else SI_PREFIXES
217
+ prefix = prefixes.get(exp, "")
218
+
219
+ scale = 10**exp
220
+ decimals = max(0, self.sig_figs - 1)
221
+
222
+ min_s = f"{min_val / scale:.{decimals}f}"
223
+ typ_s = f"{typ_val / scale:.{decimals}f}"
224
+ max_s = f"{max_val / scale:.{decimals}f}"
225
+
226
+ return f"min/typ/max: {min_s} / {typ_s} / {max_s} {prefix}{unit}".strip()
227
+
228
+
229
+ # Default formatter
230
+ _default_formatter = NumberFormatter()
231
+
232
+
233
+ def format_value(
234
+ value: float,
235
+ unit: str = "",
236
+ *,
237
+ sig_figs: int = 3,
238
+ ) -> str:
239
+ """Format a numeric value with SI prefix.
240
+
241
+ Args:
242
+ value: Value to format.
243
+ unit: Unit suffix.
244
+ sig_figs: Significant figures.
245
+
246
+ Returns:
247
+ Formatted string.
248
+
249
+ Example:
250
+ >>> format_value(0.0000023, "s")
251
+ '2.30 us'
252
+ """
253
+ formatter = NumberFormatter(sig_figs=sig_figs)
254
+ return formatter.format(value, unit)
255
+
256
+
257
+ def format_with_units(
258
+ value: float,
259
+ unit: str,
260
+ *,
261
+ sig_figs: int = 3,
262
+ ) -> str:
263
+ """Format value with automatic SI prefix scaling.
264
+
265
+ Args:
266
+ value: Value to format.
267
+ unit: Base unit (e.g., "s", "Hz", "V").
268
+ sig_figs: Significant figures.
269
+
270
+ Returns:
271
+ Formatted string with SI prefix.
272
+
273
+ Example:
274
+ >>> format_with_units(2300000, "Hz")
275
+ '2.30 MHz'
276
+ """
277
+ return format_value(value, unit, sig_figs=sig_figs)
278
+
279
+
280
+ def format_with_context(
281
+ value: float,
282
+ *,
283
+ spec: float | None = None,
284
+ spec_type: Literal["max", "min", "exact"] = "max",
285
+ unit: str = "",
286
+ sig_figs: int = 3,
287
+ show_margin: bool = True,
288
+ ) -> str:
289
+ """Format value with specification context.
290
+
291
+ Args:
292
+ value: Measured value.
293
+ spec: Specification limit.
294
+ spec_type: Type of specification (max, min, exact).
295
+ unit: Unit suffix.
296
+ sig_figs: Significant figures.
297
+ show_margin: Show margin percentage.
298
+
299
+ Returns:
300
+ Formatted string with context.
301
+
302
+ Example:
303
+ >>> format_with_context(2.3e-9, spec=5e-9, unit="s")
304
+ '2.30 ns (spec <5.00 ns, PASS 54%)'
305
+ """
306
+ formatter = NumberFormatter(sig_figs=sig_figs)
307
+ value_str = formatter.format(value, unit)
308
+
309
+ if spec is None:
310
+ return value_str
311
+
312
+ spec_str = formatter.format(spec, unit)
313
+
314
+ # Determine pass/fail
315
+ if spec_type == "max":
316
+ passed = value <= spec
317
+ spec_prefix = "<"
318
+ elif spec_type == "min":
319
+ passed = value >= spec
320
+ spec_prefix = ">"
321
+ else: # exact
322
+ passed = abs(value - spec) < spec * 0.01 # 1% tolerance
323
+ spec_prefix = "="
324
+
325
+ status_char = "\u2713" if passed else "\u2717" # Check/X marks
326
+
327
+ # Calculate margin
328
+ margin_str = ""
329
+ if show_margin and spec != 0:
330
+ if spec_type == "max":
331
+ margin = (spec - value) / spec * 100
332
+ elif spec_type == "min":
333
+ margin = (value - spec) / spec * 100
334
+ else:
335
+ margin = (1 - abs(value - spec) / spec) * 100
336
+
337
+ margin_str = f" {margin:.0f}%"
338
+
339
+ return f"{value_str} (spec {spec_prefix}{spec_str}, {status_char}{margin_str})"
340
+
341
+
342
+ def format_pass_fail(passed: bool, *, with_symbol: bool = True) -> str:
343
+ """Format pass/fail status.
344
+
345
+ Args:
346
+ passed: True for pass, False for fail.
347
+ with_symbol: Include Unicode symbol.
348
+
349
+ Returns:
350
+ Formatted status string.
351
+ """
352
+ if with_symbol:
353
+ if passed:
354
+ return "\u2713 PASS" # Check mark
355
+ else:
356
+ return "\u2717 FAIL" # X mark
357
+ else:
358
+ return "PASS" if passed else "FAIL"
359
+
360
+
361
+ def format_margin(
362
+ value: float,
363
+ limit: float,
364
+ *,
365
+ limit_type: Literal["upper", "lower"] = "upper",
366
+ ) -> str:
367
+ """Format margin to limit.
368
+
369
+ Args:
370
+ value: Measured value.
371
+ limit: Limit value.
372
+ limit_type: Whether limit is upper or lower bound.
373
+
374
+ Returns:
375
+ Margin string with status indicator.
376
+ """
377
+ if limit_type == "upper":
378
+ margin = limit - value
379
+ margin_pct = (margin / limit * 100) if limit != 0 else 0
380
+ else:
381
+ margin = value - limit
382
+ margin_pct = (margin / limit * 100) if limit != 0 else 0
383
+
384
+ # Status based on margin
385
+ if margin_pct > 20:
386
+ status = "good"
387
+ elif margin_pct > 10:
388
+ status = "ok"
389
+ elif margin_pct > 0:
390
+ status = "marginal"
391
+ else:
392
+ status = "violation"
393
+
394
+ return f"margin: {margin_pct:.1f}% ({status})"
395
+
396
+
397
+ def format_with_locale(
398
+ value: float | None = None,
399
+ locale: str | None = None,
400
+ *,
401
+ date_value: float | None = None,
402
+ ) -> str:
403
+ """Format numbers/dates with locale-aware formatting.
404
+
405
+ Args:
406
+ value: Numeric value to format (mutually exclusive with date_value).
407
+ locale: Locale string (e.g., 'en_US', 'de_DE', 'fr_FR').
408
+ If None, uses system locale.
409
+ date_value: Timestamp to format as date (mutually exclusive with value).
410
+
411
+ Returns:
412
+ Formatted string with locale-specific separators and formats.
413
+
414
+ Example:
415
+ >>> format_with_locale(1234.56, locale="en_US")
416
+ '1,234.56'
417
+ >>> format_with_locale(1234.56, locale="de_DE")
418
+ '1.234,56'
419
+ >>> format_with_locale(1234.56, locale="fr_FR")
420
+ '1 234,56'
421
+
422
+ References:
423
+ REPORT-026: Locale-aware Formatting
424
+ """
425
+ # Determine locale
426
+ current_locale = locale_module.getlocale()[0] or "en_US" if locale is None else locale
427
+
428
+ # Format date if date_value provided
429
+ if date_value is not None:
430
+ dt = datetime.fromtimestamp(date_value)
431
+ if current_locale.startswith("en_US"):
432
+ return dt.strftime("%m/%d/%Y")
433
+ elif current_locale.startswith("de_DE"):
434
+ return dt.strftime("%d.%m.%Y")
435
+ elif current_locale.startswith("fr_FR"):
436
+ return dt.strftime("%d/%m/%Y")
437
+ else: # ISO format as fallback
438
+ return dt.strftime("%Y-%m-%d")
439
+
440
+ # Format number
441
+ if value is None:
442
+ return ""
443
+
444
+ # Locale-specific decimal and thousands separators
445
+ if current_locale.startswith("en_US"):
446
+ decimal_sep = "."
447
+ thousands_sep = ","
448
+ elif current_locale.startswith("de_DE"):
449
+ decimal_sep = ","
450
+ thousands_sep = "."
451
+ elif current_locale.startswith("fr_FR"):
452
+ decimal_sep = ","
453
+ thousands_sep = " "
454
+ else: # SI standard (space for thousands)
455
+ decimal_sep = "."
456
+ thousands_sep = " "
457
+
458
+ # Format with 2 decimal places
459
+ formatted = f"{value:,.2f}"
460
+
461
+ # Replace separators
462
+ formatted = formatted.replace(",", "TEMP")
463
+ formatted = formatted.replace(".", decimal_sep)
464
+ formatted = formatted.replace("TEMP", thousands_sep)
465
+
466
+ return formatted