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.
- oscura/__init__.py +813 -8
- oscura/__main__.py +392 -0
- oscura/analyzers/__init__.py +37 -0
- oscura/analyzers/digital/__init__.py +177 -0
- oscura/analyzers/digital/bus.py +691 -0
- oscura/analyzers/digital/clock.py +805 -0
- oscura/analyzers/digital/correlation.py +720 -0
- oscura/analyzers/digital/edges.py +632 -0
- oscura/analyzers/digital/extraction.py +413 -0
- oscura/analyzers/digital/quality.py +878 -0
- oscura/analyzers/digital/signal_quality.py +877 -0
- oscura/analyzers/digital/thresholds.py +708 -0
- oscura/analyzers/digital/timing.py +1104 -0
- oscura/analyzers/eye/__init__.py +46 -0
- oscura/analyzers/eye/diagram.py +434 -0
- oscura/analyzers/eye/metrics.py +555 -0
- oscura/analyzers/jitter/__init__.py +83 -0
- oscura/analyzers/jitter/ber.py +333 -0
- oscura/analyzers/jitter/decomposition.py +759 -0
- oscura/analyzers/jitter/measurements.py +413 -0
- oscura/analyzers/jitter/spectrum.py +220 -0
- oscura/analyzers/measurements.py +40 -0
- oscura/analyzers/packet/__init__.py +171 -0
- oscura/analyzers/packet/daq.py +1077 -0
- oscura/analyzers/packet/metrics.py +437 -0
- oscura/analyzers/packet/parser.py +327 -0
- oscura/analyzers/packet/payload.py +2156 -0
- oscura/analyzers/packet/payload_analysis.py +1312 -0
- oscura/analyzers/packet/payload_extraction.py +236 -0
- oscura/analyzers/packet/payload_patterns.py +670 -0
- oscura/analyzers/packet/stream.py +359 -0
- oscura/analyzers/patterns/__init__.py +266 -0
- oscura/analyzers/patterns/clustering.py +1036 -0
- oscura/analyzers/patterns/discovery.py +539 -0
- oscura/analyzers/patterns/learning.py +797 -0
- oscura/analyzers/patterns/matching.py +1091 -0
- oscura/analyzers/patterns/periodic.py +650 -0
- oscura/analyzers/patterns/sequences.py +767 -0
- oscura/analyzers/power/__init__.py +116 -0
- oscura/analyzers/power/ac_power.py +391 -0
- oscura/analyzers/power/basic.py +383 -0
- oscura/analyzers/power/conduction.py +314 -0
- oscura/analyzers/power/efficiency.py +297 -0
- oscura/analyzers/power/ripple.py +356 -0
- oscura/analyzers/power/soa.py +372 -0
- oscura/analyzers/power/switching.py +479 -0
- oscura/analyzers/protocol/__init__.py +150 -0
- oscura/analyzers/protocols/__init__.py +150 -0
- oscura/analyzers/protocols/base.py +500 -0
- oscura/analyzers/protocols/can.py +620 -0
- oscura/analyzers/protocols/can_fd.py +448 -0
- oscura/analyzers/protocols/flexray.py +405 -0
- oscura/analyzers/protocols/hdlc.py +399 -0
- oscura/analyzers/protocols/i2c.py +368 -0
- oscura/analyzers/protocols/i2s.py +296 -0
- oscura/analyzers/protocols/jtag.py +393 -0
- oscura/analyzers/protocols/lin.py +445 -0
- oscura/analyzers/protocols/manchester.py +333 -0
- oscura/analyzers/protocols/onewire.py +501 -0
- oscura/analyzers/protocols/spi.py +334 -0
- oscura/analyzers/protocols/swd.py +325 -0
- oscura/analyzers/protocols/uart.py +393 -0
- oscura/analyzers/protocols/usb.py +495 -0
- oscura/analyzers/signal_integrity/__init__.py +63 -0
- oscura/analyzers/signal_integrity/embedding.py +294 -0
- oscura/analyzers/signal_integrity/equalization.py +370 -0
- oscura/analyzers/signal_integrity/sparams.py +484 -0
- oscura/analyzers/spectral/__init__.py +53 -0
- oscura/analyzers/spectral/chunked.py +273 -0
- oscura/analyzers/spectral/chunked_fft.py +571 -0
- oscura/analyzers/spectral/chunked_wavelet.py +391 -0
- oscura/analyzers/spectral/fft.py +92 -0
- oscura/analyzers/statistical/__init__.py +250 -0
- oscura/analyzers/statistical/checksum.py +923 -0
- oscura/analyzers/statistical/chunked_corr.py +228 -0
- oscura/analyzers/statistical/classification.py +778 -0
- oscura/analyzers/statistical/entropy.py +1113 -0
- oscura/analyzers/statistical/ngrams.py +614 -0
- oscura/analyzers/statistics/__init__.py +119 -0
- oscura/analyzers/statistics/advanced.py +885 -0
- oscura/analyzers/statistics/basic.py +263 -0
- oscura/analyzers/statistics/correlation.py +630 -0
- oscura/analyzers/statistics/distribution.py +298 -0
- oscura/analyzers/statistics/outliers.py +463 -0
- oscura/analyzers/statistics/streaming.py +93 -0
- oscura/analyzers/statistics/trend.py +520 -0
- oscura/analyzers/validation.py +598 -0
- oscura/analyzers/waveform/__init__.py +36 -0
- oscura/analyzers/waveform/measurements.py +943 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
- oscura/analyzers/waveform/spectral.py +1689 -0
- oscura/analyzers/waveform/wavelets.py +298 -0
- oscura/api/__init__.py +62 -0
- oscura/api/dsl.py +538 -0
- oscura/api/fluent.py +571 -0
- oscura/api/operators.py +498 -0
- oscura/api/optimization.py +392 -0
- oscura/api/profiling.py +396 -0
- oscura/automotive/__init__.py +73 -0
- oscura/automotive/can/__init__.py +52 -0
- oscura/automotive/can/analysis.py +356 -0
- oscura/automotive/can/checksum.py +250 -0
- oscura/automotive/can/correlation.py +212 -0
- oscura/automotive/can/discovery.py +355 -0
- oscura/automotive/can/message_wrapper.py +375 -0
- oscura/automotive/can/models.py +385 -0
- oscura/automotive/can/patterns.py +381 -0
- oscura/automotive/can/session.py +452 -0
- oscura/automotive/can/state_machine.py +300 -0
- oscura/automotive/can/stimulus_response.py +461 -0
- oscura/automotive/dbc/__init__.py +15 -0
- oscura/automotive/dbc/generator.py +156 -0
- oscura/automotive/dbc/parser.py +146 -0
- oscura/automotive/dtc/__init__.py +30 -0
- oscura/automotive/dtc/database.py +3036 -0
- oscura/automotive/j1939/__init__.py +14 -0
- oscura/automotive/j1939/decoder.py +745 -0
- oscura/automotive/loaders/__init__.py +35 -0
- oscura/automotive/loaders/asc.py +98 -0
- oscura/automotive/loaders/blf.py +77 -0
- oscura/automotive/loaders/csv_can.py +136 -0
- oscura/automotive/loaders/dispatcher.py +136 -0
- oscura/automotive/loaders/mdf.py +331 -0
- oscura/automotive/loaders/pcap.py +132 -0
- oscura/automotive/obd/__init__.py +14 -0
- oscura/automotive/obd/decoder.py +707 -0
- oscura/automotive/uds/__init__.py +48 -0
- oscura/automotive/uds/decoder.py +265 -0
- oscura/automotive/uds/models.py +64 -0
- oscura/automotive/visualization.py +369 -0
- oscura/batch/__init__.py +55 -0
- oscura/batch/advanced.py +627 -0
- oscura/batch/aggregate.py +300 -0
- oscura/batch/analyze.py +139 -0
- oscura/batch/logging.py +487 -0
- oscura/batch/metrics.py +556 -0
- oscura/builders/__init__.py +41 -0
- oscura/builders/signal_builder.py +1131 -0
- oscura/cli/__init__.py +14 -0
- oscura/cli/batch.py +339 -0
- oscura/cli/characterize.py +273 -0
- oscura/cli/compare.py +775 -0
- oscura/cli/decode.py +551 -0
- oscura/cli/main.py +247 -0
- oscura/cli/shell.py +350 -0
- oscura/comparison/__init__.py +66 -0
- oscura/comparison/compare.py +397 -0
- oscura/comparison/golden.py +487 -0
- oscura/comparison/limits.py +391 -0
- oscura/comparison/mask.py +434 -0
- oscura/comparison/trace_diff.py +30 -0
- oscura/comparison/visualization.py +481 -0
- oscura/compliance/__init__.py +70 -0
- oscura/compliance/advanced.py +756 -0
- oscura/compliance/masks.py +363 -0
- oscura/compliance/reporting.py +483 -0
- oscura/compliance/testing.py +298 -0
- oscura/component/__init__.py +38 -0
- oscura/component/impedance.py +365 -0
- oscura/component/reactive.py +598 -0
- oscura/component/transmission_line.py +312 -0
- oscura/config/__init__.py +191 -0
- oscura/config/defaults.py +254 -0
- oscura/config/loader.py +348 -0
- oscura/config/memory.py +271 -0
- oscura/config/migration.py +458 -0
- oscura/config/pipeline.py +1077 -0
- oscura/config/preferences.py +530 -0
- oscura/config/protocol.py +875 -0
- oscura/config/schema.py +713 -0
- oscura/config/settings.py +420 -0
- oscura/config/thresholds.py +599 -0
- oscura/convenience.py +457 -0
- oscura/core/__init__.py +299 -0
- oscura/core/audit.py +457 -0
- oscura/core/backend_selector.py +405 -0
- oscura/core/cache.py +590 -0
- oscura/core/cancellation.py +439 -0
- oscura/core/confidence.py +225 -0
- oscura/core/config.py +506 -0
- oscura/core/correlation.py +216 -0
- oscura/core/cross_domain.py +422 -0
- oscura/core/debug.py +301 -0
- oscura/core/edge_cases.py +541 -0
- oscura/core/exceptions.py +535 -0
- oscura/core/gpu_backend.py +523 -0
- oscura/core/lazy.py +832 -0
- oscura/core/log_query.py +540 -0
- oscura/core/logging.py +931 -0
- oscura/core/logging_advanced.py +952 -0
- oscura/core/memoize.py +171 -0
- oscura/core/memory_check.py +274 -0
- oscura/core/memory_guard.py +290 -0
- oscura/core/memory_limits.py +336 -0
- oscura/core/memory_monitor.py +453 -0
- oscura/core/memory_progress.py +465 -0
- oscura/core/memory_warnings.py +315 -0
- oscura/core/numba_backend.py +362 -0
- oscura/core/performance.py +352 -0
- oscura/core/progress.py +524 -0
- oscura/core/provenance.py +358 -0
- oscura/core/results.py +331 -0
- oscura/core/types.py +504 -0
- oscura/core/uncertainty.py +383 -0
- oscura/discovery/__init__.py +52 -0
- oscura/discovery/anomaly_detector.py +672 -0
- oscura/discovery/auto_decoder.py +415 -0
- oscura/discovery/comparison.py +497 -0
- oscura/discovery/quality_validator.py +528 -0
- oscura/discovery/signal_detector.py +769 -0
- oscura/dsl/__init__.py +73 -0
- oscura/dsl/commands.py +246 -0
- oscura/dsl/interpreter.py +455 -0
- oscura/dsl/parser.py +689 -0
- oscura/dsl/repl.py +172 -0
- oscura/exceptions.py +59 -0
- oscura/exploratory/__init__.py +111 -0
- oscura/exploratory/error_recovery.py +642 -0
- oscura/exploratory/fuzzy.py +513 -0
- oscura/exploratory/fuzzy_advanced.py +786 -0
- oscura/exploratory/legacy.py +831 -0
- oscura/exploratory/parse.py +358 -0
- oscura/exploratory/recovery.py +275 -0
- oscura/exploratory/sync.py +382 -0
- oscura/exploratory/unknown.py +707 -0
- oscura/export/__init__.py +25 -0
- oscura/export/wireshark/README.md +265 -0
- oscura/export/wireshark/__init__.py +47 -0
- oscura/export/wireshark/generator.py +312 -0
- oscura/export/wireshark/lua_builder.py +159 -0
- oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
- oscura/export/wireshark/type_mapping.py +165 -0
- oscura/export/wireshark/validator.py +105 -0
- oscura/exporters/__init__.py +94 -0
- oscura/exporters/csv.py +303 -0
- oscura/exporters/exporters.py +44 -0
- oscura/exporters/hdf5.py +219 -0
- oscura/exporters/html_export.py +701 -0
- oscura/exporters/json_export.py +291 -0
- oscura/exporters/markdown_export.py +367 -0
- oscura/exporters/matlab_export.py +354 -0
- oscura/exporters/npz_export.py +219 -0
- oscura/exporters/spice_export.py +210 -0
- oscura/extensibility/__init__.py +131 -0
- oscura/extensibility/docs.py +752 -0
- oscura/extensibility/extensions.py +1125 -0
- oscura/extensibility/logging.py +259 -0
- oscura/extensibility/measurements.py +485 -0
- oscura/extensibility/plugins.py +414 -0
- oscura/extensibility/registry.py +346 -0
- oscura/extensibility/templates.py +913 -0
- oscura/extensibility/validation.py +651 -0
- oscura/filtering/__init__.py +89 -0
- oscura/filtering/base.py +563 -0
- oscura/filtering/convenience.py +564 -0
- oscura/filtering/design.py +725 -0
- oscura/filtering/filters.py +32 -0
- oscura/filtering/introspection.py +605 -0
- oscura/guidance/__init__.py +24 -0
- oscura/guidance/recommender.py +429 -0
- oscura/guidance/wizard.py +518 -0
- oscura/inference/__init__.py +251 -0
- oscura/inference/active_learning/README.md +153 -0
- oscura/inference/active_learning/__init__.py +38 -0
- oscura/inference/active_learning/lstar.py +257 -0
- oscura/inference/active_learning/observation_table.py +230 -0
- oscura/inference/active_learning/oracle.py +78 -0
- oscura/inference/active_learning/teachers/__init__.py +15 -0
- oscura/inference/active_learning/teachers/simulator.py +192 -0
- oscura/inference/adaptive_tuning.py +453 -0
- oscura/inference/alignment.py +653 -0
- oscura/inference/bayesian.py +943 -0
- oscura/inference/binary.py +1016 -0
- oscura/inference/crc_reverse.py +711 -0
- oscura/inference/logic.py +288 -0
- oscura/inference/message_format.py +1305 -0
- oscura/inference/protocol.py +417 -0
- oscura/inference/protocol_dsl.py +1084 -0
- oscura/inference/protocol_library.py +1230 -0
- oscura/inference/sequences.py +809 -0
- oscura/inference/signal_intelligence.py +1509 -0
- oscura/inference/spectral.py +215 -0
- oscura/inference/state_machine.py +634 -0
- oscura/inference/stream.py +918 -0
- oscura/integrations/__init__.py +59 -0
- oscura/integrations/llm.py +1827 -0
- oscura/jupyter/__init__.py +32 -0
- oscura/jupyter/display.py +268 -0
- oscura/jupyter/magic.py +334 -0
- oscura/loaders/__init__.py +526 -0
- oscura/loaders/binary.py +69 -0
- oscura/loaders/configurable.py +1255 -0
- oscura/loaders/csv.py +26 -0
- oscura/loaders/csv_loader.py +473 -0
- oscura/loaders/hdf5.py +9 -0
- oscura/loaders/hdf5_loader.py +510 -0
- oscura/loaders/lazy.py +370 -0
- oscura/loaders/mmap_loader.py +583 -0
- oscura/loaders/numpy_loader.py +436 -0
- oscura/loaders/pcap.py +432 -0
- oscura/loaders/preprocessing.py +368 -0
- oscura/loaders/rigol.py +287 -0
- oscura/loaders/sigrok.py +321 -0
- oscura/loaders/tdms.py +367 -0
- oscura/loaders/tektronix.py +711 -0
- oscura/loaders/validation.py +584 -0
- oscura/loaders/vcd.py +464 -0
- oscura/loaders/wav.py +233 -0
- oscura/math/__init__.py +45 -0
- oscura/math/arithmetic.py +824 -0
- oscura/math/interpolation.py +413 -0
- oscura/onboarding/__init__.py +39 -0
- oscura/onboarding/help.py +498 -0
- oscura/onboarding/tutorials.py +405 -0
- oscura/onboarding/wizard.py +466 -0
- oscura/optimization/__init__.py +19 -0
- oscura/optimization/parallel.py +440 -0
- oscura/optimization/search.py +532 -0
- oscura/pipeline/__init__.py +43 -0
- oscura/pipeline/base.py +338 -0
- oscura/pipeline/composition.py +242 -0
- oscura/pipeline/parallel.py +448 -0
- oscura/pipeline/pipeline.py +375 -0
- oscura/pipeline/reverse_engineering.py +1119 -0
- oscura/plugins/__init__.py +122 -0
- oscura/plugins/base.py +272 -0
- oscura/plugins/cli.py +497 -0
- oscura/plugins/discovery.py +411 -0
- oscura/plugins/isolation.py +418 -0
- oscura/plugins/lifecycle.py +959 -0
- oscura/plugins/manager.py +493 -0
- oscura/plugins/registry.py +421 -0
- oscura/plugins/versioning.py +372 -0
- oscura/py.typed +0 -0
- oscura/quality/__init__.py +65 -0
- oscura/quality/ensemble.py +740 -0
- oscura/quality/explainer.py +338 -0
- oscura/quality/scoring.py +616 -0
- oscura/quality/warnings.py +456 -0
- oscura/reporting/__init__.py +248 -0
- oscura/reporting/advanced.py +1234 -0
- oscura/reporting/analyze.py +448 -0
- oscura/reporting/argument_preparer.py +596 -0
- oscura/reporting/auto_report.py +507 -0
- oscura/reporting/batch.py +615 -0
- oscura/reporting/chart_selection.py +223 -0
- oscura/reporting/comparison.py +330 -0
- oscura/reporting/config.py +615 -0
- oscura/reporting/content/__init__.py +39 -0
- oscura/reporting/content/executive.py +127 -0
- oscura/reporting/content/filtering.py +191 -0
- oscura/reporting/content/minimal.py +257 -0
- oscura/reporting/content/verbosity.py +162 -0
- oscura/reporting/core.py +508 -0
- oscura/reporting/core_formats/__init__.py +17 -0
- oscura/reporting/core_formats/multi_format.py +210 -0
- oscura/reporting/engine.py +836 -0
- oscura/reporting/export.py +366 -0
- oscura/reporting/formatting/__init__.py +129 -0
- oscura/reporting/formatting/emphasis.py +81 -0
- oscura/reporting/formatting/numbers.py +403 -0
- oscura/reporting/formatting/standards.py +55 -0
- oscura/reporting/formatting.py +466 -0
- oscura/reporting/html.py +578 -0
- oscura/reporting/index.py +590 -0
- oscura/reporting/multichannel.py +296 -0
- oscura/reporting/output.py +379 -0
- oscura/reporting/pdf.py +373 -0
- oscura/reporting/plots.py +731 -0
- oscura/reporting/pptx_export.py +360 -0
- oscura/reporting/renderers/__init__.py +11 -0
- oscura/reporting/renderers/pdf.py +94 -0
- oscura/reporting/sections.py +471 -0
- oscura/reporting/standards.py +680 -0
- oscura/reporting/summary_generator.py +368 -0
- oscura/reporting/tables.py +397 -0
- oscura/reporting/template_system.py +724 -0
- oscura/reporting/templates/__init__.py +15 -0
- oscura/reporting/templates/definition.py +205 -0
- oscura/reporting/templates/index.html +649 -0
- oscura/reporting/templates/index.md +173 -0
- oscura/schemas/__init__.py +158 -0
- oscura/schemas/bus_configuration.json +322 -0
- oscura/schemas/device_mapping.json +182 -0
- oscura/schemas/packet_format.json +418 -0
- oscura/schemas/protocol_definition.json +363 -0
- oscura/search/__init__.py +16 -0
- oscura/search/anomaly.py +292 -0
- oscura/search/context.py +149 -0
- oscura/search/pattern.py +160 -0
- oscura/session/__init__.py +34 -0
- oscura/session/annotations.py +289 -0
- oscura/session/history.py +313 -0
- oscura/session/session.py +445 -0
- oscura/streaming/__init__.py +43 -0
- oscura/streaming/chunked.py +611 -0
- oscura/streaming/progressive.py +393 -0
- oscura/streaming/realtime.py +622 -0
- oscura/testing/__init__.py +54 -0
- oscura/testing/synthetic.py +808 -0
- oscura/triggering/__init__.py +68 -0
- oscura/triggering/base.py +229 -0
- oscura/triggering/edge.py +353 -0
- oscura/triggering/pattern.py +344 -0
- oscura/triggering/pulse.py +581 -0
- oscura/triggering/window.py +453 -0
- oscura/ui/__init__.py +48 -0
- oscura/ui/formatters.py +526 -0
- oscura/ui/progressive_display.py +340 -0
- oscura/utils/__init__.py +99 -0
- oscura/utils/autodetect.py +338 -0
- oscura/utils/buffer.py +389 -0
- oscura/utils/lazy.py +407 -0
- oscura/utils/lazy_imports.py +147 -0
- oscura/utils/memory.py +836 -0
- oscura/utils/memory_advanced.py +1326 -0
- oscura/utils/memory_extensions.py +465 -0
- oscura/utils/progressive.py +352 -0
- oscura/utils/windowing.py +362 -0
- oscura/visualization/__init__.py +321 -0
- oscura/visualization/accessibility.py +526 -0
- oscura/visualization/annotations.py +374 -0
- oscura/visualization/axis_scaling.py +305 -0
- oscura/visualization/colors.py +453 -0
- oscura/visualization/digital.py +337 -0
- oscura/visualization/eye.py +420 -0
- oscura/visualization/histogram.py +281 -0
- oscura/visualization/interactive.py +858 -0
- oscura/visualization/jitter.py +702 -0
- oscura/visualization/keyboard.py +394 -0
- oscura/visualization/layout.py +365 -0
- oscura/visualization/optimization.py +1028 -0
- oscura/visualization/palettes.py +446 -0
- oscura/visualization/plot.py +92 -0
- oscura/visualization/power.py +290 -0
- oscura/visualization/power_extended.py +626 -0
- oscura/visualization/presets.py +467 -0
- oscura/visualization/protocols.py +932 -0
- oscura/visualization/render.py +207 -0
- oscura/visualization/rendering.py +444 -0
- oscura/visualization/reverse_engineering.py +791 -0
- oscura/visualization/signal_integrity.py +808 -0
- oscura/visualization/specialized.py +553 -0
- oscura/visualization/spectral.py +811 -0
- oscura/visualization/styles.py +381 -0
- oscura/visualization/thumbnails.py +311 -0
- oscura/visualization/time_axis.py +351 -0
- oscura/visualization/waveform.py +367 -0
- oscura/workflow/__init__.py +13 -0
- oscura/workflow/dag.py +377 -0
- oscura/workflows/__init__.py +58 -0
- oscura/workflows/compliance.py +280 -0
- oscura/workflows/digital.py +272 -0
- oscura/workflows/multi_trace.py +502 -0
- oscura/workflows/power.py +178 -0
- oscura/workflows/protocol.py +492 -0
- oscura/workflows/reverse_engineering.py +639 -0
- oscura/workflows/signal_integrity.py +227 -0
- oscura-0.1.1.dist-info/METADATA +300 -0
- oscura-0.1.1.dist-info/RECORD +463 -0
- oscura-0.1.1.dist-info/entry_points.txt +2 -0
- {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/licenses/LICENSE +1 -1
- oscura-0.0.1.dist-info/METADATA +0 -63
- oscura-0.0.1.dist-info/RECORD +0 -5
- {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/WHEEL +0 -0
oscura/api/fluent.py
ADDED
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
"""Fluent interface for signal analysis.
|
|
2
|
+
|
|
3
|
+
This module provides a fluent (method chaining) interface for
|
|
4
|
+
expressing signal analysis operations in a readable, intuitive way.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from collections.abc import Callable
|
|
16
|
+
|
|
17
|
+
from numpy.typing import NDArray
|
|
18
|
+
|
|
19
|
+
T = TypeVar("T")
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"FluentResult",
|
|
23
|
+
"FluentTrace",
|
|
24
|
+
"trace",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class FluentResult[T]:
|
|
30
|
+
"""Result container with fluent interface.
|
|
31
|
+
|
|
32
|
+
Provides method chaining for result processing.
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
value: The wrapped value
|
|
36
|
+
metadata: Associated metadata
|
|
37
|
+
|
|
38
|
+
Example:
|
|
39
|
+
>>> result = FluentResult(42.5)
|
|
40
|
+
>>> result.format("The value is {:.2f}").print()
|
|
41
|
+
The value is 42.50
|
|
42
|
+
|
|
43
|
+
References:
|
|
44
|
+
API-019: Fluent Interface
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
value: T
|
|
48
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
49
|
+
|
|
50
|
+
def get(self) -> T:
|
|
51
|
+
"""Get the raw value.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
The wrapped value
|
|
55
|
+
"""
|
|
56
|
+
return self.value
|
|
57
|
+
|
|
58
|
+
def map(self, func: Callable[[T], Any]) -> FluentResult: # type: ignore[type-arg]
|
|
59
|
+
"""Apply function to value.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
func: Function to apply
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
New FluentResult with mapped value
|
|
66
|
+
"""
|
|
67
|
+
return FluentResult(func(self.value), self.metadata.copy())
|
|
68
|
+
|
|
69
|
+
def filter(self, predicate: Callable[[T], bool]) -> FluentResult | None: # type: ignore[type-arg]
|
|
70
|
+
"""Filter based on predicate.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
predicate: Filter function
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Self if predicate passes, None otherwise
|
|
77
|
+
"""
|
|
78
|
+
if predicate(self.value):
|
|
79
|
+
return self
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
def format(self, fmt: str) -> FluentResult[str]:
|
|
83
|
+
"""Format value as string.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
fmt: Format string
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
New FluentResult with formatted string
|
|
90
|
+
"""
|
|
91
|
+
return FluentResult(fmt.format(self.value), self.metadata.copy())
|
|
92
|
+
|
|
93
|
+
def print(self, prefix: str = "") -> FluentResult[T]:
|
|
94
|
+
"""Print value and return self.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
prefix: Optional prefix
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Self (for chaining)
|
|
101
|
+
"""
|
|
102
|
+
print(f"{prefix}{self.value}")
|
|
103
|
+
return self
|
|
104
|
+
|
|
105
|
+
def with_metadata(self, **kwargs: Any) -> FluentResult[T]:
|
|
106
|
+
"""Add metadata.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
**kwargs: Metadata key-value pairs
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Self (for chaining)
|
|
113
|
+
"""
|
|
114
|
+
self.metadata.update(kwargs)
|
|
115
|
+
return self
|
|
116
|
+
|
|
117
|
+
def __repr__(self) -> str:
|
|
118
|
+
return f"FluentResult({self.value!r})"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class FluentTrace:
|
|
122
|
+
"""Fluent interface wrapper for trace data.
|
|
123
|
+
|
|
124
|
+
Provides method chaining for signal processing operations.
|
|
125
|
+
|
|
126
|
+
Example:
|
|
127
|
+
>>> result = (FluentTrace(data, sample_rate=1e9)
|
|
128
|
+
... .lowpass(cutoff=1e6)
|
|
129
|
+
... .normalize()
|
|
130
|
+
... .fft(nfft=8192)
|
|
131
|
+
... .magnitude()
|
|
132
|
+
... .get())
|
|
133
|
+
|
|
134
|
+
References:
|
|
135
|
+
API-019: Fluent Interface
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
def __init__(self, data: NDArray[np.float64], sample_rate: float = 1.0, **metadata: Any):
|
|
139
|
+
"""Initialize fluent trace.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
data: Trace data array
|
|
143
|
+
sample_rate: Sample rate in Hz
|
|
144
|
+
**metadata: Additional metadata
|
|
145
|
+
"""
|
|
146
|
+
self._data = data
|
|
147
|
+
self._sample_rate = sample_rate
|
|
148
|
+
self._metadata = metadata
|
|
149
|
+
self._history: list[str] = []
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def data(self) -> NDArray[np.float64]:
|
|
153
|
+
"""Get current data."""
|
|
154
|
+
return self._data
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def sample_rate(self) -> float:
|
|
158
|
+
"""Get sample rate."""
|
|
159
|
+
return self._sample_rate
|
|
160
|
+
|
|
161
|
+
def get(self) -> NDArray[np.float64]:
|
|
162
|
+
"""Get raw data array.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Data array
|
|
166
|
+
"""
|
|
167
|
+
return self._data
|
|
168
|
+
|
|
169
|
+
def copy(self) -> FluentTrace:
|
|
170
|
+
"""Create copy of trace.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
New FluentTrace with copied data
|
|
174
|
+
"""
|
|
175
|
+
return FluentTrace(self._data.copy(), self._sample_rate, **self._metadata.copy())
|
|
176
|
+
|
|
177
|
+
# =========================================================================
|
|
178
|
+
# Filtering Methods
|
|
179
|
+
# =========================================================================
|
|
180
|
+
|
|
181
|
+
def lowpass(self, cutoff: float, order: int = 4) -> FluentTrace:
|
|
182
|
+
"""Apply low-pass filter.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
cutoff: Cutoff frequency in Hz
|
|
186
|
+
order: Filter order
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Self (for chaining)
|
|
190
|
+
"""
|
|
191
|
+
from scipy import signal
|
|
192
|
+
|
|
193
|
+
nyq = self._sample_rate / 2
|
|
194
|
+
normalized_cutoff = min(cutoff / nyq, 0.99)
|
|
195
|
+
b, a = signal.butter(order, normalized_cutoff, btype="low")
|
|
196
|
+
self._data = signal.filtfilt(b, a, self._data)
|
|
197
|
+
self._history.append(f"lowpass(cutoff={cutoff})")
|
|
198
|
+
return self
|
|
199
|
+
|
|
200
|
+
def highpass(self, cutoff: float, order: int = 4) -> FluentTrace:
|
|
201
|
+
"""Apply high-pass filter.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
cutoff: Cutoff frequency in Hz
|
|
205
|
+
order: Filter order
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
Self (for chaining)
|
|
209
|
+
"""
|
|
210
|
+
from scipy import signal
|
|
211
|
+
|
|
212
|
+
nyq = self._sample_rate / 2
|
|
213
|
+
normalized_cutoff = max(cutoff / nyq, 0.01)
|
|
214
|
+
b, a = signal.butter(order, normalized_cutoff, btype="high")
|
|
215
|
+
self._data = signal.filtfilt(b, a, self._data)
|
|
216
|
+
self._history.append(f"highpass(cutoff={cutoff})")
|
|
217
|
+
return self
|
|
218
|
+
|
|
219
|
+
def bandpass(self, low: float, high: float, order: int = 4) -> FluentTrace:
|
|
220
|
+
"""Apply band-pass filter.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
low: Low cutoff frequency
|
|
224
|
+
high: High cutoff frequency
|
|
225
|
+
order: Filter order
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Self (for chaining)
|
|
229
|
+
"""
|
|
230
|
+
from scipy import signal
|
|
231
|
+
|
|
232
|
+
nyq = self._sample_rate / 2
|
|
233
|
+
b, a = signal.butter(order, [low / nyq, high / nyq], btype="band")
|
|
234
|
+
self._data = signal.filtfilt(b, a, self._data)
|
|
235
|
+
self._history.append(f"bandpass(low={low}, high={high})")
|
|
236
|
+
return self
|
|
237
|
+
|
|
238
|
+
def notch(self, freq: float, Q: float = 30.0) -> FluentTrace:
|
|
239
|
+
"""Apply notch filter.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
freq: Notch frequency
|
|
243
|
+
Q: Quality factor
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
Self (for chaining)
|
|
247
|
+
"""
|
|
248
|
+
from scipy import signal
|
|
249
|
+
|
|
250
|
+
nyq = self._sample_rate / 2
|
|
251
|
+
b, a = signal.iirnotch(freq / nyq, Q)
|
|
252
|
+
self._data = signal.filtfilt(b, a, self._data)
|
|
253
|
+
self._history.append(f"notch(freq={freq})")
|
|
254
|
+
return self
|
|
255
|
+
|
|
256
|
+
# =========================================================================
|
|
257
|
+
# Transform Methods
|
|
258
|
+
# =========================================================================
|
|
259
|
+
|
|
260
|
+
def normalize(self, method: str = "minmax") -> FluentTrace:
|
|
261
|
+
"""Normalize data.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
method: Normalization method (minmax, zscore, peak)
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
Self (for chaining)
|
|
268
|
+
"""
|
|
269
|
+
if method == "minmax":
|
|
270
|
+
data_min = np.min(self._data)
|
|
271
|
+
data_max = np.max(self._data)
|
|
272
|
+
if data_max - data_min > 0:
|
|
273
|
+
self._data = (self._data - data_min) / (data_max - data_min)
|
|
274
|
+
elif method == "zscore":
|
|
275
|
+
std = np.std(self._data)
|
|
276
|
+
if std > 0:
|
|
277
|
+
self._data = (self._data - np.mean(self._data)) / std
|
|
278
|
+
elif method == "peak":
|
|
279
|
+
peak = np.max(np.abs(self._data))
|
|
280
|
+
if peak > 0:
|
|
281
|
+
self._data = self._data / peak
|
|
282
|
+
|
|
283
|
+
self._history.append(f"normalize(method={method})")
|
|
284
|
+
return self
|
|
285
|
+
|
|
286
|
+
def scale(self, factor: float) -> FluentTrace:
|
|
287
|
+
"""Scale data by factor.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
factor: Scale factor
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
Self (for chaining)
|
|
294
|
+
"""
|
|
295
|
+
self._data = self._data * factor
|
|
296
|
+
self._history.append(f"scale(factor={factor})")
|
|
297
|
+
return self
|
|
298
|
+
|
|
299
|
+
def offset(self, value: float) -> FluentTrace:
|
|
300
|
+
"""Add offset to data.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
value: Offset value
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
Self (for chaining)
|
|
307
|
+
"""
|
|
308
|
+
self._data = self._data + value
|
|
309
|
+
self._history.append(f"offset(value={value})")
|
|
310
|
+
return self
|
|
311
|
+
|
|
312
|
+
def clip(self, low: float, high: float) -> FluentTrace:
|
|
313
|
+
"""Clip data to range.
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
low: Low limit
|
|
317
|
+
high: High limit
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
Self (for chaining)
|
|
321
|
+
"""
|
|
322
|
+
self._data = np.clip(self._data, low, high)
|
|
323
|
+
self._history.append(f"clip(low={low}, high={high})")
|
|
324
|
+
return self
|
|
325
|
+
|
|
326
|
+
def abs(self) -> FluentTrace:
|
|
327
|
+
"""Take absolute value.
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
Self (for chaining)
|
|
331
|
+
"""
|
|
332
|
+
self._data = np.abs(self._data)
|
|
333
|
+
self._history.append("abs()")
|
|
334
|
+
return self
|
|
335
|
+
|
|
336
|
+
def diff(self) -> FluentTrace:
|
|
337
|
+
"""Differentiate data.
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
Self (for chaining)
|
|
341
|
+
"""
|
|
342
|
+
self._data = np.diff(self._data, prepend=self._data[0])
|
|
343
|
+
self._history.append("diff()")
|
|
344
|
+
return self
|
|
345
|
+
|
|
346
|
+
def integrate(self) -> FluentTrace:
|
|
347
|
+
"""Integrate data.
|
|
348
|
+
|
|
349
|
+
Returns:
|
|
350
|
+
Self (for chaining)
|
|
351
|
+
"""
|
|
352
|
+
dt = 1.0 / self._sample_rate
|
|
353
|
+
self._data = np.cumsum(self._data) * dt
|
|
354
|
+
self._history.append("integrate()")
|
|
355
|
+
return self
|
|
356
|
+
|
|
357
|
+
# =========================================================================
|
|
358
|
+
# Resampling Methods
|
|
359
|
+
# =========================================================================
|
|
360
|
+
|
|
361
|
+
def resample(self, new_length: int) -> FluentTrace:
|
|
362
|
+
"""Resample to new length.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
new_length: New number of samples
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
Self (for chaining)
|
|
369
|
+
"""
|
|
370
|
+
from scipy import signal
|
|
371
|
+
|
|
372
|
+
self._data = signal.resample(self._data, new_length)
|
|
373
|
+
self._sample_rate = self._sample_rate * new_length / len(self._data)
|
|
374
|
+
self._history.append(f"resample(new_length={new_length})")
|
|
375
|
+
return self
|
|
376
|
+
|
|
377
|
+
def decimate(self, factor: int) -> FluentTrace:
|
|
378
|
+
"""Decimate by factor.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
factor: Decimation factor
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
Self (for chaining)
|
|
385
|
+
"""
|
|
386
|
+
from scipy import signal
|
|
387
|
+
|
|
388
|
+
self._data = signal.decimate(self._data, factor)
|
|
389
|
+
self._sample_rate = self._sample_rate / factor
|
|
390
|
+
self._history.append(f"decimate(factor={factor})")
|
|
391
|
+
return self
|
|
392
|
+
|
|
393
|
+
def slice(self, start: int = 0, end: int | None = None) -> FluentTrace:
|
|
394
|
+
"""Slice data.
|
|
395
|
+
|
|
396
|
+
Args:
|
|
397
|
+
start: Start index
|
|
398
|
+
end: End index (None for end of data)
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
Self (for chaining)
|
|
402
|
+
"""
|
|
403
|
+
self._data = self._data[start:end]
|
|
404
|
+
self._history.append(f"slice(start={start}, end={end})")
|
|
405
|
+
return self
|
|
406
|
+
|
|
407
|
+
# =========================================================================
|
|
408
|
+
# Spectral Methods
|
|
409
|
+
# =========================================================================
|
|
410
|
+
|
|
411
|
+
def fft(self, nfft: int | None = None) -> FluentTrace:
|
|
412
|
+
"""Compute FFT.
|
|
413
|
+
|
|
414
|
+
Args:
|
|
415
|
+
nfft: FFT size
|
|
416
|
+
|
|
417
|
+
Returns:
|
|
418
|
+
Self (for chaining, data is now complex)
|
|
419
|
+
"""
|
|
420
|
+
self._data = np.fft.fft(self._data, n=nfft) # type: ignore[assignment]
|
|
421
|
+
self._history.append(f"fft(nfft={nfft})")
|
|
422
|
+
return self
|
|
423
|
+
|
|
424
|
+
def magnitude(self) -> FluentTrace:
|
|
425
|
+
"""Compute magnitude of complex data.
|
|
426
|
+
|
|
427
|
+
Returns:
|
|
428
|
+
Self (for chaining)
|
|
429
|
+
"""
|
|
430
|
+
self._data = np.abs(self._data)
|
|
431
|
+
self._history.append("magnitude()")
|
|
432
|
+
return self
|
|
433
|
+
|
|
434
|
+
def phase(self) -> FluentTrace:
|
|
435
|
+
"""Compute phase of complex data.
|
|
436
|
+
|
|
437
|
+
Returns:
|
|
438
|
+
Self (for chaining)
|
|
439
|
+
"""
|
|
440
|
+
self._data = np.angle(self._data)
|
|
441
|
+
self._history.append("phase()")
|
|
442
|
+
return self
|
|
443
|
+
|
|
444
|
+
def psd(self, nperseg: int = 256) -> FluentResult[tuple]: # type: ignore[type-arg]
|
|
445
|
+
"""Compute power spectral density.
|
|
446
|
+
|
|
447
|
+
Args:
|
|
448
|
+
nperseg: Segment size
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
FluentResult with (frequencies, psd) tuple
|
|
452
|
+
"""
|
|
453
|
+
from scipy import signal
|
|
454
|
+
|
|
455
|
+
f, psd = signal.welch(self._data, self._sample_rate, nperseg=nperseg)
|
|
456
|
+
return FluentResult((f, psd))
|
|
457
|
+
|
|
458
|
+
# =========================================================================
|
|
459
|
+
# Measurement Methods
|
|
460
|
+
# =========================================================================
|
|
461
|
+
|
|
462
|
+
def mean(self) -> FluentResult[float]:
|
|
463
|
+
"""Compute mean.
|
|
464
|
+
|
|
465
|
+
Returns:
|
|
466
|
+
FluentResult with mean value
|
|
467
|
+
"""
|
|
468
|
+
return FluentResult(float(np.mean(self._data)))
|
|
469
|
+
|
|
470
|
+
def std(self) -> FluentResult[float]:
|
|
471
|
+
"""Compute standard deviation.
|
|
472
|
+
|
|
473
|
+
Returns:
|
|
474
|
+
FluentResult with std value
|
|
475
|
+
"""
|
|
476
|
+
return FluentResult(float(np.std(self._data)))
|
|
477
|
+
|
|
478
|
+
def rms(self) -> FluentResult[float]:
|
|
479
|
+
"""Compute RMS value.
|
|
480
|
+
|
|
481
|
+
Returns:
|
|
482
|
+
FluentResult with RMS value
|
|
483
|
+
"""
|
|
484
|
+
return FluentResult(float(np.sqrt(np.mean(self._data**2))))
|
|
485
|
+
|
|
486
|
+
def peak_to_peak(self) -> FluentResult[float]:
|
|
487
|
+
"""Compute peak-to-peak value.
|
|
488
|
+
|
|
489
|
+
Returns:
|
|
490
|
+
FluentResult with peak-to-peak value
|
|
491
|
+
"""
|
|
492
|
+
return FluentResult(float(np.ptp(self._data)))
|
|
493
|
+
|
|
494
|
+
def min(self) -> FluentResult[float]:
|
|
495
|
+
"""Get minimum value.
|
|
496
|
+
|
|
497
|
+
Returns:
|
|
498
|
+
FluentResult with min value
|
|
499
|
+
"""
|
|
500
|
+
return FluentResult(float(np.min(self._data)))
|
|
501
|
+
|
|
502
|
+
def max(self) -> FluentResult[float]:
|
|
503
|
+
"""Get maximum value.
|
|
504
|
+
|
|
505
|
+
Returns:
|
|
506
|
+
FluentResult with max value
|
|
507
|
+
"""
|
|
508
|
+
return FluentResult(float(np.max(self._data)))
|
|
509
|
+
|
|
510
|
+
# =========================================================================
|
|
511
|
+
# Utility Methods
|
|
512
|
+
# =========================================================================
|
|
513
|
+
|
|
514
|
+
def print_history(self) -> FluentTrace:
|
|
515
|
+
"""Print operation history.
|
|
516
|
+
|
|
517
|
+
Returns:
|
|
518
|
+
Self (for chaining)
|
|
519
|
+
"""
|
|
520
|
+
print("Operation history:")
|
|
521
|
+
for op in self._history:
|
|
522
|
+
print(f" - {op}")
|
|
523
|
+
return self
|
|
524
|
+
|
|
525
|
+
def with_metadata(self, **kwargs: Any) -> FluentTrace:
|
|
526
|
+
"""Add metadata.
|
|
527
|
+
|
|
528
|
+
Args:
|
|
529
|
+
**kwargs: Metadata key-value pairs
|
|
530
|
+
|
|
531
|
+
Returns:
|
|
532
|
+
Self (for chaining)
|
|
533
|
+
"""
|
|
534
|
+
self._metadata.update(kwargs)
|
|
535
|
+
return self
|
|
536
|
+
|
|
537
|
+
def __len__(self) -> int:
|
|
538
|
+
return len(self._data)
|
|
539
|
+
|
|
540
|
+
def __repr__(self) -> str:
|
|
541
|
+
return (
|
|
542
|
+
f"FluentTrace(samples={len(self._data)}, "
|
|
543
|
+
f"sample_rate={self._sample_rate}, "
|
|
544
|
+
f"operations={len(self._history)})"
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
def trace(data: NDArray[np.float64], sample_rate: float = 1.0, **metadata: Any) -> FluentTrace:
|
|
549
|
+
"""Create fluent trace wrapper.
|
|
550
|
+
|
|
551
|
+
Factory function for creating FluentTrace instances.
|
|
552
|
+
|
|
553
|
+
Args:
|
|
554
|
+
data: Trace data array
|
|
555
|
+
sample_rate: Sample rate in Hz
|
|
556
|
+
**metadata: Additional metadata
|
|
557
|
+
|
|
558
|
+
Returns:
|
|
559
|
+
FluentTrace instance
|
|
560
|
+
|
|
561
|
+
Example:
|
|
562
|
+
>>> result = (trace(data, sample_rate=1e9)
|
|
563
|
+
... .lowpass(cutoff=1e6)
|
|
564
|
+
... .normalize()
|
|
565
|
+
... .mean()
|
|
566
|
+
... .get())
|
|
567
|
+
|
|
568
|
+
References:
|
|
569
|
+
API-019: Fluent Interface
|
|
570
|
+
"""
|
|
571
|
+
return FluentTrace(data, sample_rate, **metadata)
|