oscura 0.1.0__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.
- oscura/__init__.py +14 -14
- oscura/__main__.py +9 -9
- oscura/analyzers/__init__.py +1 -1
- oscura/analyzers/packet/daq.py +1 -1
- oscura/analyzers/patterns/__init__.py +2 -2
- oscura/analyzers/patterns/clustering.py +1 -1
- oscura/analyzers/patterns/discovery.py +1 -1
- oscura/analyzers/patterns/periodic.py +1 -1
- oscura/analyzers/patterns/sequences.py +1 -1
- oscura/analyzers/power/__init__.py +1 -1
- oscura/analyzers/power/ac_power.py +1 -1
- oscura/analyzers/power/basic.py +1 -1
- oscura/analyzers/power/conduction.py +1 -1
- oscura/analyzers/power/efficiency.py +1 -1
- oscura/analyzers/power/ripple.py +1 -1
- oscura/analyzers/power/soa.py +1 -1
- oscura/analyzers/power/switching.py +1 -1
- oscura/api/__init__.py +1 -1
- oscura/automotive/__init__.py +1 -1
- oscura/automotive/can/checksum.py +2 -2
- oscura/automotive/can/session.py +1 -1
- oscura/automotive/can/state_machine.py +1 -1
- oscura/automotive/dbc/generator.py +1 -1
- oscura/automotive/dbc/parser.py +1 -1
- oscura/automotive/loaders/pcap.py +1 -1
- oscura/batch/__init__.py +1 -1
- oscura/batch/aggregate.py +2 -2
- oscura/batch/analyze.py +3 -3
- oscura/builders/__init__.py +7 -7
- oscura/builders/signal_builder.py +4 -4
- oscura/cli/__init__.py +1 -1
- oscura/cli/batch.py +1 -1
- oscura/cli/characterize.py +2 -2
- oscura/cli/compare.py +4 -4
- oscura/cli/decode.py +1 -1
- oscura/cli/main.py +8 -8
- oscura/cli/shell.py +19 -19
- oscura/comparison/__init__.py +1 -1
- oscura/comparison/compare.py +1 -1
- oscura/comparison/golden.py +1 -1
- oscura/comparison/limits.py +1 -1
- oscura/comparison/mask.py +1 -1
- oscura/comparison/trace_diff.py +1 -1
- oscura/compliance/__init__.py +2 -2
- oscura/compliance/reporting.py +2 -2
- oscura/component/__init__.py +1 -1
- oscura/component/impedance.py +1 -1
- oscura/component/reactive.py +1 -1
- oscura/component/transmission_line.py +1 -1
- oscura/config/__init__.py +1 -1
- oscura/config/loader.py +1 -1
- oscura/config/memory.py +3 -3
- oscura/config/migration.py +1 -1
- oscura/config/preferences.py +1 -1
- oscura/config/schema.py +3 -3
- oscura/config/settings.py +1 -1
- oscura/convenience.py +13 -13
- oscura/core/__init__.py +6 -6
- oscura/core/cache.py +10 -10
- oscura/core/cancellation.py +1 -1
- oscura/core/confidence.py +1 -1
- oscura/core/config.py +1 -1
- oscura/core/correlation.py +1 -1
- oscura/core/debug.py +4 -4
- oscura/core/edge_cases.py +1 -1
- oscura/core/exceptions.py +14 -14
- oscura/core/gpu_backend.py +5 -5
- oscura/core/lazy.py +3 -3
- oscura/core/logging.py +6 -6
- oscura/core/logging_advanced.py +1 -1
- oscura/core/memoize.py +3 -3
- oscura/core/memory_check.py +1 -1
- oscura/core/memory_guard.py +1 -1
- oscura/core/memory_limits.py +1 -1
- oscura/core/memory_monitor.py +1 -1
- oscura/core/memory_progress.py +1 -1
- oscura/core/memory_warnings.py +1 -1
- oscura/core/progress.py +1 -1
- oscura/core/provenance.py +5 -5
- oscura/core/results.py +3 -3
- oscura/core/types.py +1 -1
- oscura/core/uncertainty.py +1 -1
- oscura/discovery/__init__.py +4 -4
- oscura/discovery/comparison.py +1 -1
- oscura/dsl/__init__.py +1 -1
- oscura/dsl/commands.py +22 -22
- oscura/dsl/interpreter.py +4 -4
- oscura/dsl/parser.py +4 -4
- oscura/dsl/repl.py +4 -4
- oscura/exceptions.py +3 -3
- oscura/export/__init__.py +2 -2
- oscura/export/wireshark/__init__.py +1 -1
- oscura/export/wireshark/generator.py +2 -2
- oscura/export/wireshark/templates/dissector.lua.j2 +1 -1
- oscura/export/wireshark/type_mapping.py +8 -8
- oscura/exporters/__init__.py +3 -3
- oscura/exporters/csv.py +1 -1
- oscura/exporters/html_export.py +5 -5
- oscura/exporters/json_export.py +9 -9
- oscura/exporters/markdown_export.py +3 -3
- oscura/exporters/npz_export.py +1 -1
- oscura/exporters/spice_export.py +1 -1
- oscura/extensibility/__init__.py +1 -1
- oscura/extensibility/docs.py +1 -1
- oscura/extensibility/extensions.py +7 -7
- oscura/extensibility/measurements.py +12 -12
- oscura/extensibility/plugins.py +12 -12
- oscura/extensibility/registry.py +12 -12
- oscura/extensibility/templates.py +16 -16
- oscura/extensibility/validation.py +3 -3
- oscura/filtering/__init__.py +1 -1
- oscura/filtering/base.py +1 -1
- oscura/filtering/convenience.py +1 -1
- oscura/filtering/design.py +1 -1
- oscura/filtering/introspection.py +1 -1
- oscura/guidance/__init__.py +1 -1
- oscura/guidance/recommender.py +1 -1
- oscura/guidance/wizard.py +1 -1
- oscura/inference/__init__.py +1 -1
- oscura/inference/adaptive_tuning.py +3 -3
- oscura/inference/logic.py +5 -5
- oscura/inference/protocol.py +5 -5
- oscura/inference/signal_intelligence.py +19 -19
- oscura/inference/spectral.py +6 -6
- oscura/integrations/__init__.py +1 -1
- oscura/integrations/llm.py +6 -6
- oscura/jupyter/__init__.py +3 -3
- oscura/jupyter/display.py +1 -1
- oscura/jupyter/magic.py +31 -31
- oscura/loaders/__init__.py +10 -10
- oscura/loaders/mmap_loader.py +1 -1
- oscura/loaders/pcap.py +1 -1
- oscura/math/__init__.py +1 -1
- oscura/math/arithmetic.py +1 -1
- oscura/math/interpolation.py +1 -1
- oscura/onboarding/__init__.py +1 -1
- oscura/onboarding/help.py +18 -18
- oscura/onboarding/tutorials.py +29 -29
- oscura/onboarding/wizard.py +22 -22
- oscura/pipeline/composition.py +15 -15
- oscura/pipeline/pipeline.py +10 -10
- oscura/plugins/__init__.py +2 -2
- oscura/plugins/base.py +5 -5
- oscura/plugins/discovery.py +6 -6
- oscura/plugins/registry.py +6 -6
- oscura/plugins/versioning.py +2 -2
- oscura/quality/__init__.py +1 -1
- oscura/quality/scoring.py +1 -1
- oscura/quality/warnings.py +1 -1
- oscura/reporting/__init__.py +1 -1
- oscura/reporting/advanced.py +1 -1
- oscura/reporting/analyze.py +1 -1
- oscura/reporting/auto_report.py +2 -2
- oscura/reporting/batch.py +3 -3
- oscura/reporting/chart_selection.py +1 -1
- oscura/reporting/comparison.py +1 -1
- oscura/reporting/core.py +4 -4
- oscura/reporting/export.py +1 -1
- oscura/reporting/formatting.py +1 -1
- oscura/reporting/html.py +3 -3
- oscura/reporting/multichannel.py +1 -1
- oscura/reporting/output.py +1 -1
- oscura/reporting/pdf.py +2 -2
- oscura/reporting/pptx_export.py +1 -1
- oscura/reporting/sections.py +1 -1
- oscura/reporting/standards.py +2 -2
- oscura/reporting/summary_generator.py +1 -1
- oscura/reporting/tables.py +1 -1
- oscura/reporting/template_system.py +1 -1
- oscura/reporting/templates/index.html +2 -2
- oscura/schemas/__init__.py +2 -2
- oscura/schemas/bus_configuration.json +1 -1
- oscura/schemas/device_mapping.json +1 -1
- oscura/schemas/packet_format.json +1 -1
- oscura/schemas/protocol_definition.json +1 -1
- oscura/search/__init__.py +1 -1
- oscura/session/__init__.py +4 -4
- oscura/session/history.py +7 -7
- oscura/session/session.py +2 -2
- oscura/streaming/chunked.py +7 -7
- oscura/testing/__init__.py +1 -1
- oscura/triggering/__init__.py +1 -1
- oscura/triggering/base.py +1 -1
- oscura/triggering/edge.py +1 -1
- oscura/triggering/pattern.py +1 -1
- oscura/triggering/pulse.py +1 -1
- oscura/triggering/window.py +1 -1
- oscura/ui/__init__.py +1 -1
- oscura/ui/formatters.py +1 -1
- oscura/ui/progressive_display.py +1 -1
- oscura/utils/__init__.py +1 -1
- oscura/utils/memory.py +3 -3
- oscura/utils/memory_extensions.py +1 -1
- oscura/visualization/__init__.py +1 -1
- oscura/visualization/accessibility.py +1 -1
- oscura/visualization/keyboard.py +1 -1
- oscura/visualization/palettes.py +1 -1
- oscura/visualization/spectral.py +9 -9
- oscura/visualization/waveform.py +4 -4
- oscura/workflows/__init__.py +5 -5
- oscura/workflows/compliance.py +5 -5
- oscura/workflows/digital.py +5 -5
- oscura/workflows/multi_trace.py +25 -25
- oscura/workflows/power.py +7 -7
- oscura/workflows/protocol.py +5 -5
- oscura/workflows/reverse_engineering.py +5 -5
- oscura/workflows/signal_integrity.py +6 -6
- {oscura-0.1.0.dist-info → oscura-0.1.1.dist-info}/METADATA +11 -11
- {oscura-0.1.0.dist-info → oscura-0.1.1.dist-info}/RECORD +212 -212
- {oscura-0.1.0.dist-info → oscura-0.1.1.dist-info}/WHEEL +0 -0
- {oscura-0.1.0.dist-info → oscura-0.1.1.dist-info}/entry_points.txt +0 -0
- {oscura-0.1.0.dist-info → oscura-0.1.1.dist-info}/licenses/LICENSE +0 -0
oscura/session/history.py
CHANGED
|
@@ -91,7 +91,7 @@ class HistoryEntry:
|
|
|
91
91
|
params.append(f"{k}={v!r}")
|
|
92
92
|
|
|
93
93
|
param_str = ", ".join(params)
|
|
94
|
-
return f"
|
|
94
|
+
return f"osc.{self.operation}({param_str})"
|
|
95
95
|
|
|
96
96
|
|
|
97
97
|
@dataclass
|
|
@@ -217,10 +217,10 @@ class OperationHistory:
|
|
|
217
217
|
Example:
|
|
218
218
|
>>> script = history.to_script()
|
|
219
219
|
>>> print(script)
|
|
220
|
-
# Generated by
|
|
221
|
-
import oscura as
|
|
222
|
-
|
|
223
|
-
result =
|
|
220
|
+
# Generated by Oscura
|
|
221
|
+
import oscura as osc
|
|
222
|
+
osc.load("capture.wfm")
|
|
223
|
+
result = osc.measure_rise_time()
|
|
224
224
|
"""
|
|
225
225
|
lines = []
|
|
226
226
|
|
|
@@ -228,12 +228,12 @@ class OperationHistory:
|
|
|
228
228
|
lines.extend(
|
|
229
229
|
[
|
|
230
230
|
"#!/usr/bin/env python3",
|
|
231
|
-
'"""
|
|
231
|
+
'"""Oscura analysis script.',
|
|
232
232
|
"",
|
|
233
233
|
f"Generated: {datetime.now().isoformat()}",
|
|
234
234
|
'"""',
|
|
235
235
|
"",
|
|
236
|
-
"import oscura as
|
|
236
|
+
"import oscura as osc",
|
|
237
237
|
"",
|
|
238
238
|
]
|
|
239
239
|
)
|
oscura/session/session.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Analysis session management.
|
|
2
2
|
|
|
3
|
-
This module provides session save/restore functionality for
|
|
3
|
+
This module provides session save/restore functionality for Oscura.
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
Example:
|
|
@@ -116,7 +116,7 @@ class Session:
|
|
|
116
116
|
Example:
|
|
117
117
|
>>> session = Session()
|
|
118
118
|
>>> data = np.sin(np.linspace(0, 2*np.pi, 1000))
|
|
119
|
-
>>> trace =
|
|
119
|
+
>>> trace = osc.WaveformTrace(data=data, metadata=osc.TraceMetadata(sample_rate=1e6))
|
|
120
120
|
>>> session.add_trace("my_trace", trace)
|
|
121
121
|
"""
|
|
122
122
|
if not name:
|
oscura/streaming/chunked.py
CHANGED
|
@@ -51,19 +51,19 @@ def load_trace_chunks(
|
|
|
51
51
|
|
|
52
52
|
Example:
|
|
53
53
|
>>> # Stream 10 GB file in 100 MB chunks
|
|
54
|
-
>>> for chunk in
|
|
54
|
+
>>> for chunk in osc.load_trace_chunks('huge_trace.bin', chunk_size=100e6):
|
|
55
55
|
... mean = chunk.data.mean()
|
|
56
56
|
... std = chunk.data.std()
|
|
57
57
|
... print(f"Chunk stats: mean={mean:.3f}, std={std:.3f}")
|
|
58
58
|
|
|
59
59
|
Advanced Example:
|
|
60
60
|
>>> # Process with overlap for FFT continuity
|
|
61
|
-
>>> for chunk in
|
|
61
|
+
>>> for chunk in osc.load_trace_chunks(
|
|
62
62
|
... 'large_trace.bin',
|
|
63
63
|
... chunk_size=50e6,
|
|
64
64
|
... overlap=8192 # Overlap for continuity
|
|
65
65
|
... ):
|
|
66
|
-
... fft_result =
|
|
66
|
+
... fft_result = osc.fft(chunk, nfft=8192)
|
|
67
67
|
... # Process FFT result...
|
|
68
68
|
|
|
69
69
|
References:
|
|
@@ -133,17 +133,17 @@ class StreamingAnalyzer:
|
|
|
133
133
|
|
|
134
134
|
Example:
|
|
135
135
|
>>> # Create streaming analyzer
|
|
136
|
-
>>> analyzer =
|
|
136
|
+
>>> analyzer = osc.StreamingAnalyzer()
|
|
137
137
|
>>> # Process file in chunks
|
|
138
|
-
>>> for chunk in
|
|
138
|
+
>>> for chunk in osc.load_trace_chunks('large_trace.bin', chunk_size=50e6):
|
|
139
139
|
... analyzer.accumulate_psd(chunk, nperseg=4096, window='hann')
|
|
140
140
|
>>> # Get aggregated result
|
|
141
141
|
>>> psd_result = analyzer.get_psd()
|
|
142
142
|
|
|
143
143
|
Advanced Example:
|
|
144
144
|
>>> # Compute multiple statistics in streaming fashion
|
|
145
|
-
>>> analyzer =
|
|
146
|
-
>>> for chunk in
|
|
145
|
+
>>> analyzer = osc.StreamingAnalyzer()
|
|
146
|
+
>>> for chunk in osc.load_trace_chunks('huge_file.bin'):
|
|
147
147
|
... analyzer.accumulate_statistics(chunk)
|
|
148
148
|
... analyzer.accumulate_psd(chunk, nperseg=8192)
|
|
149
149
|
>>> stats = analyzer.get_statistics()
|
oscura/testing/__init__.py
CHANGED
oscura/triggering/__init__.py
CHANGED
oscura/triggering/base.py
CHANGED
oscura/triggering/edge.py
CHANGED
oscura/triggering/pattern.py
CHANGED
oscura/triggering/pulse.py
CHANGED
oscura/triggering/window.py
CHANGED
oscura/ui/__init__.py
CHANGED
oscura/ui/formatters.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""UI-specific formatting utilities for
|
|
1
|
+
"""UI-specific formatting utilities for Oscura displays.
|
|
2
2
|
|
|
3
3
|
This module provides formatting functions tailored for user interface display,
|
|
4
4
|
including text alignment, truncation, color codes, and structured output.
|
oscura/ui/progressive_display.py
CHANGED
oscura/utils/__init__.py
CHANGED
oscura/utils/memory.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Memory management utilities for
|
|
1
|
+
"""Memory management utilities for Oscura.
|
|
2
2
|
|
|
3
3
|
This module provides memory estimation, availability checking, and
|
|
4
4
|
OOM prevention for large signal processing operations.
|
|
@@ -419,7 +419,7 @@ _max_memory: int | None = None
|
|
|
419
419
|
|
|
420
420
|
|
|
421
421
|
def set_max_memory(limit: int | str | None) -> None:
|
|
422
|
-
"""Set global memory limit for
|
|
422
|
+
"""Set global memory limit for Oscura operations.
|
|
423
423
|
|
|
424
424
|
Args:
|
|
425
425
|
limit: Maximum memory in bytes, or string like "4GB", "512MB".
|
|
@@ -500,7 +500,7 @@ def get_memory_info() -> dict[str, int]:
|
|
|
500
500
|
|
|
501
501
|
@dataclass
|
|
502
502
|
class MemoryConfig:
|
|
503
|
-
"""Global memory configuration for
|
|
503
|
+
"""Global memory configuration for Oscura operations.
|
|
504
504
|
|
|
505
505
|
|
|
506
506
|
Attributes:
|
oscura/visualization/__init__.py
CHANGED
oscura/visualization/keyboard.py
CHANGED
oscura/visualization/palettes.py
CHANGED
oscura/visualization/spectral.py
CHANGED
|
@@ -88,13 +88,13 @@ def plot_spectrum(
|
|
|
88
88
|
ValueError: If axes must have an associated figure.
|
|
89
89
|
|
|
90
90
|
Example:
|
|
91
|
-
>>> import oscura as
|
|
92
|
-
>>> trace =
|
|
93
|
-
>>> fig =
|
|
91
|
+
>>> import oscura as osc
|
|
92
|
+
>>> trace = osc.load("signal.wfm")
|
|
93
|
+
>>> fig = osc.plot_spectrum(trace, freq_unit="MHz", log_scale=True)
|
|
94
94
|
|
|
95
95
|
>>> # With pre-computed FFT
|
|
96
|
-
>>> freq, mag =
|
|
97
|
-
>>> fig =
|
|
96
|
+
>>> freq, mag = osc.fft(trace)
|
|
97
|
+
>>> fig = osc.plot_spectrum(trace, fft_result=(freq, mag), show=False)
|
|
98
98
|
>>> fig.savefig("spectrum.png")
|
|
99
99
|
"""
|
|
100
100
|
if not HAS_MATPLOTLIB:
|
|
@@ -453,13 +453,13 @@ def plot_fft(
|
|
|
453
453
|
ValueError: If axes must have an associated figure.
|
|
454
454
|
|
|
455
455
|
Example:
|
|
456
|
-
>>> import oscura as
|
|
457
|
-
>>> trace =
|
|
458
|
-
>>> fig =
|
|
456
|
+
>>> import oscura as osc
|
|
457
|
+
>>> trace = osc.load("signal.wfm")
|
|
458
|
+
>>> fig = osc.plot_fft(trace, freq_unit="MHz", show=False)
|
|
459
459
|
>>> fig.savefig("spectrum.png")
|
|
460
460
|
|
|
461
461
|
>>> # With custom styling
|
|
462
|
-
>>> fig =
|
|
462
|
+
>>> fig = osc.plot_fft(trace,
|
|
463
463
|
... title="Signal FFT",
|
|
464
464
|
... log_scale=True,
|
|
465
465
|
... xlim=(1e3, 1e6),
|
oscura/visualization/waveform.py
CHANGED
|
@@ -77,13 +77,13 @@ def plot_waveform(
|
|
|
77
77
|
ValueError: If axes has no associated figure.
|
|
78
78
|
|
|
79
79
|
Example:
|
|
80
|
-
>>> import oscura as
|
|
81
|
-
>>> trace =
|
|
82
|
-
>>> fig =
|
|
80
|
+
>>> import oscura as osc
|
|
81
|
+
>>> trace = osc.load("signal.wfm")
|
|
82
|
+
>>> fig = osc.plot_waveform(trace, time_unit="us", show=False)
|
|
83
83
|
>>> fig.savefig("waveform.png")
|
|
84
84
|
|
|
85
85
|
>>> # With custom styling
|
|
86
|
-
>>> fig =
|
|
86
|
+
>>> fig = osc.plot_waveform(trace,
|
|
87
87
|
... title="Captured Signal",
|
|
88
88
|
... xlabel="Time",
|
|
89
89
|
... ylabel="Voltage",
|
oscura/workflows/__init__.py
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
"""High-level workflow presets for
|
|
1
|
+
"""High-level workflow presets for Oscura.
|
|
2
2
|
|
|
3
3
|
This module provides one-call analysis workflows for common signal
|
|
4
4
|
characterization tasks.
|
|
5
5
|
|
|
6
6
|
Example:
|
|
7
|
-
>>> import oscura as
|
|
7
|
+
>>> import oscura as osc
|
|
8
8
|
>>> # Reverse engineer unknown signal
|
|
9
|
-
>>> result =
|
|
9
|
+
>>> result = osc.workflows.reverse_engineer_signal(trace)
|
|
10
10
|
>>> print(result.protocol_spec)
|
|
11
11
|
>>>
|
|
12
12
|
>>> # EMC compliance testing
|
|
13
|
-
>>> results =
|
|
13
|
+
>>> results = osc.workflows.emc_compliance_test(trace)
|
|
14
14
|
>>>
|
|
15
15
|
>>> # Multi-trace analysis
|
|
16
|
-
>>> stats =
|
|
16
|
+
>>> stats = osc.workflows.load_all(["trace1.wfm", "trace2.wfm"])
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
19
|
from oscura.workflows.compliance import emc_compliance_test
|
oscura/workflows/compliance.py
CHANGED
|
@@ -4,9 +4,9 @@ This module implements spectral compliance testing against regulatory limits.
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
Example:
|
|
7
|
-
>>> import oscura as
|
|
8
|
-
>>> trace =
|
|
9
|
-
>>> result =
|
|
7
|
+
>>> import oscura as osc
|
|
8
|
+
>>> trace = osc.load('emissions.wfm')
|
|
9
|
+
>>> result = osc.emc_compliance_test(trace, standard='FCC_Part15_ClassB')
|
|
10
10
|
>>> print(f"Status: {result['status']}")
|
|
11
11
|
>>> print(f"Violations: {len(result['violations'])}")
|
|
12
12
|
|
|
@@ -83,8 +83,8 @@ def emc_compliance_test(
|
|
|
83
83
|
- limit_mag: Magnitude array for limit mask
|
|
84
84
|
|
|
85
85
|
Example:
|
|
86
|
-
>>> trace =
|
|
87
|
-
>>> result =
|
|
86
|
+
>>> trace = osc.load('radiated_emissions.wfm')
|
|
87
|
+
>>> result = osc.emc_compliance_test(trace, standard='FCC_Part15_ClassB')
|
|
88
88
|
>>> print(f"Compliance: {result['status']}")
|
|
89
89
|
>>> print(f"Margin: {result['margin_to_limit']:.1f} dB")
|
|
90
90
|
>>> if result['violations']:
|
oscura/workflows/digital.py
CHANGED
|
@@ -5,9 +5,9 @@ function call, with automatic logic family detection.
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
Example:
|
|
8
|
-
>>> import oscura as
|
|
9
|
-
>>> trace =
|
|
10
|
-
>>> result =
|
|
8
|
+
>>> import oscura as osc
|
|
9
|
+
>>> trace = osc.load('74hc04_output.wfm')
|
|
10
|
+
>>> result = osc.characterize_buffer(trace)
|
|
11
11
|
>>> print(f"Logic Family: {result['logic_family']}")
|
|
12
12
|
>>> print(f"Rise Time: {result['rise_time']:.2f} ns")
|
|
13
13
|
>>> print(f"Status: {result['status']}")
|
|
@@ -76,8 +76,8 @@ def characterize_buffer(
|
|
|
76
76
|
AnalysisError: If trace has insufficient transitions for analysis.
|
|
77
77
|
|
|
78
78
|
Example:
|
|
79
|
-
>>> trace =
|
|
80
|
-
>>> result =
|
|
79
|
+
>>> trace = osc.load('74hc04_output.wfm')
|
|
80
|
+
>>> result = osc.characterize_buffer(trace, logic_family='CMOS_3V3')
|
|
81
81
|
>>> print(f"Rise Time: {result['rise_time']*1e9:.2f} ns")
|
|
82
82
|
>>> print(f"Status: {result['status']}")
|
|
83
83
|
|
oscura/workflows/multi_trace.py
CHANGED
|
@@ -12,7 +12,7 @@ from typing import Any
|
|
|
12
12
|
|
|
13
13
|
import numpy as np
|
|
14
14
|
|
|
15
|
-
from oscura.core.exceptions import
|
|
15
|
+
from oscura.core.exceptions import OscuraError
|
|
16
16
|
from oscura.core.progress import create_progress_tracker
|
|
17
17
|
|
|
18
18
|
|
|
@@ -84,7 +84,7 @@ class MultiTraceWorkflow:
|
|
|
84
84
|
lazy: If True, load traces on demand
|
|
85
85
|
|
|
86
86
|
Raises:
|
|
87
|
-
|
|
87
|
+
OscuraError: If neither pattern nor traces provided
|
|
88
88
|
"""
|
|
89
89
|
self.pattern = pattern
|
|
90
90
|
self._traces = traces or []
|
|
@@ -98,7 +98,7 @@ class MultiTraceWorkflow:
|
|
|
98
98
|
if pattern:
|
|
99
99
|
self._discover_files()
|
|
100
100
|
elif not traces:
|
|
101
|
-
raise
|
|
101
|
+
raise OscuraError("Must provide either pattern or traces")
|
|
102
102
|
|
|
103
103
|
def _discover_files(self) -> None:
|
|
104
104
|
"""Discover trace files matching pattern."""
|
|
@@ -107,7 +107,7 @@ class MultiTraceWorkflow:
|
|
|
107
107
|
|
|
108
108
|
paths = glob_func(self.pattern) # noqa: PTH207
|
|
109
109
|
if not paths:
|
|
110
|
-
raise
|
|
110
|
+
raise OscuraError(f"No files match pattern: {self.pattern}")
|
|
111
111
|
|
|
112
112
|
self._file_paths = [Path(p) for p in sorted(paths)]
|
|
113
113
|
self.results.trace_ids = [p.name for p in self._file_paths]
|
|
@@ -122,7 +122,7 @@ class MultiTraceWorkflow:
|
|
|
122
122
|
Loaded trace object
|
|
123
123
|
|
|
124
124
|
Raises:
|
|
125
|
-
|
|
125
|
+
OscuraError: If trace cannot be loaded
|
|
126
126
|
"""
|
|
127
127
|
# Determine loader based on extension
|
|
128
128
|
ext = path.suffix.lower()
|
|
@@ -147,10 +147,10 @@ class MultiTraceWorkflow:
|
|
|
147
147
|
|
|
148
148
|
return load_hdf5(str(path))
|
|
149
149
|
else:
|
|
150
|
-
raise
|
|
150
|
+
raise OscuraError(f"Unsupported format: {ext}")
|
|
151
151
|
|
|
152
152
|
except ImportError as e:
|
|
153
|
-
raise
|
|
153
|
+
raise OscuraError(f"Loader not available for {ext}: {e}") # noqa: B904
|
|
154
154
|
|
|
155
155
|
def _iter_traces(self, lazy: bool = False) -> Iterator[tuple[str, Any]]:
|
|
156
156
|
"""Iterate over traces.
|
|
@@ -197,7 +197,7 @@ class MultiTraceWorkflow:
|
|
|
197
197
|
**kwargs: Additional method-specific parameters
|
|
198
198
|
|
|
199
199
|
Raises:
|
|
200
|
-
|
|
200
|
+
OscuraError: If alignment fails
|
|
201
201
|
"""
|
|
202
202
|
if method == AlignmentMethod.TRIGGER:
|
|
203
203
|
self._align_by_trigger(channel, threshold, **kwargs)
|
|
@@ -208,7 +208,7 @@ class MultiTraceWorkflow:
|
|
|
208
208
|
elif method == AlignmentMethod.MANUAL:
|
|
209
209
|
self._align_manual(**kwargs)
|
|
210
210
|
else:
|
|
211
|
-
raise
|
|
211
|
+
raise OscuraError(f"Unknown alignment method: {method}")
|
|
212
212
|
|
|
213
213
|
self._aligned = True
|
|
214
214
|
|
|
@@ -276,11 +276,11 @@ class MultiTraceWorkflow:
|
|
|
276
276
|
**kwargs: Must include 'offsets' dict mapping trace_id -> offset
|
|
277
277
|
|
|
278
278
|
Raises:
|
|
279
|
-
|
|
279
|
+
OscuraError: If offsets parameter not provided.
|
|
280
280
|
"""
|
|
281
281
|
offsets = kwargs.get("offsets", {})
|
|
282
282
|
if not offsets:
|
|
283
|
-
raise
|
|
283
|
+
raise OscuraError("Manual alignment requires 'offsets' parameter")
|
|
284
284
|
|
|
285
285
|
self._alignment_offset.update(offsets)
|
|
286
286
|
|
|
@@ -295,10 +295,10 @@ class MultiTraceWorkflow:
|
|
|
295
295
|
max_workers: Maximum parallel workers (None = CPU count)
|
|
296
296
|
|
|
297
297
|
Raises:
|
|
298
|
-
|
|
298
|
+
OscuraError: If measurement fails
|
|
299
299
|
"""
|
|
300
300
|
if not measurements:
|
|
301
|
-
raise
|
|
301
|
+
raise OscuraError("At least one measurement required")
|
|
302
302
|
|
|
303
303
|
if parallel:
|
|
304
304
|
self._measure_parallel(measurements, max_workers)
|
|
@@ -370,11 +370,11 @@ class MultiTraceWorkflow:
|
|
|
370
370
|
measurement: Measurement name
|
|
371
371
|
|
|
372
372
|
Raises:
|
|
373
|
-
|
|
373
|
+
OscuraError: If measurement not available
|
|
374
374
|
"""
|
|
375
375
|
# Placeholder - would call actual measurement functions
|
|
376
376
|
# from oscura.analyzers.measurements
|
|
377
|
-
raise
|
|
377
|
+
raise OscuraError(
|
|
378
378
|
f"Measurement '{measurement}' not yet implemented in multi-trace workflow"
|
|
379
379
|
)
|
|
380
380
|
|
|
@@ -385,10 +385,10 @@ class MultiTraceWorkflow:
|
|
|
385
385
|
Results with statistics
|
|
386
386
|
|
|
387
387
|
Raises:
|
|
388
|
-
|
|
388
|
+
OscuraError: If no measurements available
|
|
389
389
|
"""
|
|
390
390
|
if not self.results.measurements:
|
|
391
|
-
raise
|
|
391
|
+
raise OscuraError("No measurements available. Call measure() first.")
|
|
392
392
|
|
|
393
393
|
# Compute statistics for each measurement type
|
|
394
394
|
all_measurements = set() # type: ignore[var-annotated]
|
|
@@ -422,7 +422,7 @@ class MultiTraceWorkflow:
|
|
|
422
422
|
format: Report format ('pdf', 'html', 'json')
|
|
423
423
|
|
|
424
424
|
Raises:
|
|
425
|
-
|
|
425
|
+
OscuraError: If export fails
|
|
426
426
|
"""
|
|
427
427
|
if format == "json":
|
|
428
428
|
self._export_json(filename)
|
|
@@ -431,7 +431,7 @@ class MultiTraceWorkflow:
|
|
|
431
431
|
elif format == "html":
|
|
432
432
|
self._export_html(filename)
|
|
433
433
|
else:
|
|
434
|
-
raise
|
|
434
|
+
raise OscuraError(f"Unsupported report format: {format}")
|
|
435
435
|
|
|
436
436
|
def _export_json(self, filename: str) -> None:
|
|
437
437
|
"""Export results to JSON."""
|
|
@@ -464,9 +464,9 @@ class MultiTraceWorkflow:
|
|
|
464
464
|
filename: Output filename
|
|
465
465
|
|
|
466
466
|
Raises:
|
|
467
|
-
|
|
467
|
+
OscuraError: PDF export not yet implemented
|
|
468
468
|
"""
|
|
469
|
-
raise
|
|
469
|
+
raise OscuraError("PDF export not yet implemented")
|
|
470
470
|
|
|
471
471
|
def _export_html(self, filename: str) -> None:
|
|
472
472
|
"""Export results to HTML.
|
|
@@ -475,9 +475,9 @@ class MultiTraceWorkflow:
|
|
|
475
475
|
filename: Output filename
|
|
476
476
|
|
|
477
477
|
Raises:
|
|
478
|
-
|
|
478
|
+
OscuraError: HTML export not yet implemented
|
|
479
479
|
"""
|
|
480
|
-
raise
|
|
480
|
+
raise OscuraError("HTML export not yet implemented")
|
|
481
481
|
|
|
482
482
|
|
|
483
483
|
def load_all(pattern: str, lazy: bool = True) -> list[Any]:
|
|
@@ -491,11 +491,11 @@ def load_all(pattern: str, lazy: bool = True) -> list[Any]:
|
|
|
491
491
|
List of trace objects
|
|
492
492
|
|
|
493
493
|
Raises:
|
|
494
|
-
|
|
494
|
+
OscuraError: If no traces found
|
|
495
495
|
"""
|
|
496
496
|
paths = glob_func(pattern) # noqa: PTH207
|
|
497
497
|
if not paths:
|
|
498
|
-
raise
|
|
498
|
+
raise OscuraError(f"No files match pattern: {pattern}")
|
|
499
499
|
|
|
500
500
|
# For now, just return file paths
|
|
501
501
|
# Would implement lazy loading proxy
|
oscura/workflows/power.py
CHANGED
|
@@ -5,10 +5,10 @@ voltage and current traces.
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
Example:
|
|
8
|
-
>>> import oscura as
|
|
9
|
-
>>> voltage =
|
|
10
|
-
>>> current =
|
|
11
|
-
>>> result =
|
|
8
|
+
>>> import oscura as osc
|
|
9
|
+
>>> voltage = osc.load('vdd.wfm')
|
|
10
|
+
>>> current = osc.load('idd.wfm')
|
|
11
|
+
>>> result = osc.power_analysis(voltage, current)
|
|
12
12
|
>>> print(f"Average Power: {result['average_power']*1e3:.2f} mW")
|
|
13
13
|
|
|
14
14
|
References:
|
|
@@ -70,9 +70,9 @@ def power_analysis(
|
|
|
70
70
|
AnalysisError: If traces have incompatible sample rates or lengths.
|
|
71
71
|
|
|
72
72
|
Example:
|
|
73
|
-
>>> voltage =
|
|
74
|
-
>>> current =
|
|
75
|
-
>>> result =
|
|
73
|
+
>>> voltage = osc.load('vout.wfm')
|
|
74
|
+
>>> current = osc.load('iout.wfm')
|
|
75
|
+
>>> result = osc.power_analysis(voltage, current)
|
|
76
76
|
>>> print(f"Average: {result['average_power']*1e3:.2f} mW")
|
|
77
77
|
>>> print(f"Peak: {result['peak_power']*1e3:.2f} mW")
|
|
78
78
|
>>> print(f"Energy: {result['energy']*1e6:.2f} µJ")
|
oscura/workflows/protocol.py
CHANGED
|
@@ -4,9 +4,9 @@ This module implements auto-detect protocol decoding with error context.
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
Example:
|
|
7
|
-
>>> import oscura as
|
|
8
|
-
>>> trace =
|
|
9
|
-
>>> result =
|
|
7
|
+
>>> import oscura as osc
|
|
8
|
+
>>> trace = osc.load('serial_capture.wfm')
|
|
9
|
+
>>> result = osc.debug_protocol(trace)
|
|
10
10
|
>>> print(f"Protocol: {result['protocol']}")
|
|
11
11
|
>>> print(f"Errors: {len(result['errors'])}")
|
|
12
12
|
|
|
@@ -61,8 +61,8 @@ def debug_protocol(
|
|
|
61
61
|
AnalysisError: If protocol cannot be detected or decoded.
|
|
62
62
|
|
|
63
63
|
Example:
|
|
64
|
-
>>> trace =
|
|
65
|
-
>>> result =
|
|
64
|
+
>>> trace = osc.load('uart_data.wfm')
|
|
65
|
+
>>> result = osc.debug_protocol(trace)
|
|
66
66
|
>>> print(f"Protocol: {result['protocol']}")
|
|
67
67
|
>>> print(f"Baud Rate: {result['baud_rate']}")
|
|
68
68
|
>>> for error in result['errors']:
|
|
@@ -4,9 +4,9 @@ This module provides a complete workflow for reverse engineering unknown
|
|
|
4
4
|
digital signals from initial capture to protocol understanding.
|
|
5
5
|
|
|
6
6
|
Example:
|
|
7
|
-
>>> import oscura as
|
|
8
|
-
>>> trace =
|
|
9
|
-
>>> result =
|
|
7
|
+
>>> import oscura as osc
|
|
8
|
+
>>> trace = osc.load("unknown_capture.wfm")
|
|
9
|
+
>>> result = osc.workflows.reverse_engineer_signal(trace)
|
|
10
10
|
>>> print(result.protocol_spec)
|
|
11
11
|
>>> print(f"Detected baud rate: {result.baud_rate}")
|
|
12
12
|
>>> print(f"Frames decoded: {len(result.frames)}")
|
|
@@ -158,8 +158,8 @@ def reverse_engineer_signal(
|
|
|
158
158
|
ReverseEngineeringResult with protocol specification and decoded frames.
|
|
159
159
|
|
|
160
160
|
Example:
|
|
161
|
-
>>> trace =
|
|
162
|
-
>>> result =
|
|
161
|
+
>>> trace = osc.load("unknown_capture.wfm")
|
|
162
|
+
>>> result = osc.workflows.reverse_engineer_signal(trace)
|
|
163
163
|
>>> print(f"Baud rate: {result.baud_rate}")
|
|
164
164
|
>>> print(f"Sync pattern: {result.protocol_spec.sync_pattern}")
|
|
165
165
|
>>> print(f"Frame length: {result.protocol_spec.frame_length} bytes")
|