sofia / examples /signal_analysis.py
rootcastleengineering's picture
release: publish Sofia Engine 3.0.0a1 artifacts, manifests, and documentation
876458a
Raw
History Blame Contribute Delete
4.06 kB
"""Scientific Signal Analysis Example for Sofia Engine.
Demonstrates deterministic digital signal processing across mechanical vibration
and industrial electrical domains:
1. FFT magnitude spectrum & peak detection
2. Welch power spectral density & Parseval energy conservation
3. Hilbert transform analytic amplitude envelope
4. Fortescue 3-phase symmetrical components & Voltage Unbalance Factor (VUF)
Run:
python examples/signal_analysis.py
"""
from __future__ import annotations
import numpy as np
from sofia_ai.signal.electrical import compute_symmetrical_components
from sofia_ai.signal.envelope import amplitude_envelope
from sofia_ai.signal.spectral import peak_frequency, rfft_magnitude, welch_psd
def run_signal_analysis() -> None:
print("=== Sofia Engine: Scientific Signal Analysis ===")
# -------------------------------------------------------------------------
# 1. Vibration Spectral Analysis (FFT & Welch PSD)
# -------------------------------------------------------------------------
fs = 2000.0 # 2 kHz sampling rate
n = 2000 # 1 second duration
t = np.arange(n) / fs
# Synthesize multi-tone machine vibration:
# 50 Hz shaft rotation (0.8 m/s^2) + 250 Hz gear-mesh tone (0.3 m/s^2)
tone_50 = 0.8 * np.sin(2 * np.pi * 50.0 * t)
tone_250 = 0.3 * np.sin(2 * np.pi * 250.0 * t)
noise = 0.05 * np.random.default_rng(123).normal(size=n)
signal = tone_50 + tone_250 + noise
print(f"\n[1] Vibration Telemetry ({n} samples @ {fs} Hz)")
# Compute FFT magnitude spectrum
spec = rfft_magnitude(signal, fs, window="hann", detrend_mean=True)
f_peak = peak_frequency(spec.frequencies, spec.values)
mag_peak = float(np.max(spec.values))
print(f" FFT Peak Frequency: {f_peak:.1f} Hz (Magnitude: {mag_peak:.4f})")
# Compute Welch PSD
psd = welch_psd(signal, fs, segment_length=512, overlap=0.5, window="hann")
# Parseval check: integral of PSD approx equals variance of signal
signal_variance = float(np.var(signal))
psd_total_power = psd.total_power
parseval_ratio = psd_total_power / max(signal_variance, 1e-12)
print(f" Welch PSD Total Power: {psd_total_power:.4f}")
print(f" Time-Domain Variance: {signal_variance:.4f}")
print(f" Parseval Energy Ratio: {parseval_ratio:.4f} (expected ~1.0)")
# -------------------------------------------------------------------------
# 2. Bearing Fault Envelope Demodulation (Hilbert Transform)
# -------------------------------------------------------------------------
print("\n[2] Amplitude Demodulation (Hilbert Analytic Envelope)")
# Amplitude modulated signal: 200 Hz carrier modulated at 15 Hz fault pass frequency
carrier_freq = 200.0
mod_freq = 15.0
envelope_true = 1.0 + 0.6 * np.cos(2 * np.pi * mod_freq * t)
am_signal = envelope_true * np.cos(2 * np.pi * carrier_freq * t)
env = amplitude_envelope(am_signal)
env_spec = rfft_magnitude(env - np.mean(env), fs, window="hann")
env_peak_f = peak_frequency(env_spec.frequencies, env_spec.values)
print(f" Extracted Modulation Frequency: {env_peak_f:.1f} Hz (expected {mod_freq:.1f} Hz)")
# -------------------------------------------------------------------------
# 3. 3-Phase Symmetrical Components (Fortescue Transformation)
# -------------------------------------------------------------------------
print("\n[3] 3-Phase Symmetrical Components (IEC / IEEE 519 analysis)")
# Unbalanced 3-phase voltages (Va=230V @ 0°, Vb=220V @ -125°, Vc=215V @ 115°)
sym = compute_symmetrical_components(
va_amp=230.0, va_phase_deg=0.0,
vb_amp=220.0, vb_phase_deg=-125.0,
vc_amp=215.0, vc_phase_deg=115.0,
)
print(f" Positive Sequence (V1): {sym.v1_pos_seq_v:.2f} V")
print(f" Negative Sequence (V2): {sym.v2_neg_seq_v:.2f} V")
print(f" Zero Sequence (V0): {sym.v0_zero_seq_v:.2f} V")
print(f" Voltage Unbalance Factor (VUF): {sym.vuf_percent:.2f}%")
if __name__ == "__main__":
run_signal_analysis()