Module 3 Wavelet - igheyas/WaveletTransformation GitHub Wiki
📋 Week 3: Properties of the Continuous Wavelet Transform 📝 Outline Goals
Inversion (Reconstruction) Formula
Energy Conservation (Parseval’s Theorem for CWT)
Scalogram Interpretation
Worked Example
Exercises
Energy Estimation
Energy of Continuous Wavelet
Scalogram Vs Spectrogram
Key takeaway
-
Scalogram ≠ Spectrogram, though both show when and at what “frequency-like” band energy occurs.
-
Scalograms use wavelets to give a zoomed-in view at high frequencies (good time detail) and a zoomed-out view at low frequencies (good frequency detail).
-
Spectrograms slice the signal into equal-length windows, giving constant resolution everywhere.
Here’s a self‐contained script that you can drop into your venv (with PyWavelets ≥ 1.1.0) and run end-to-end. It will:
# continuous_wavelet_analysis.py
import numpy as np
import matplotlib.pyplot as plt
import pywt
from scipy.signal import chirp
from scipy.interpolate import interp1d
# 1. Generate a non-stationary signal: two chirps + noise
fs = 1000 # Hz
T = 1.0 # seconds
t = np.linspace(0, T, int(fs*T), endpoint=False)
signal = np.zeros_like(t)
# up-chirp from 50→200 Hz
signal[:fs//2] = chirp(t[:fs//2], f0=50, f1=200, t1=T/2, method='linear')
# down-chirp from 300→100 Hz
signal[fs//2:] = chirp(t[fs//2:], f0=300, f1=100, t1=T/2, method='linear')
# add white noise
signal += 0.2 * np.random.randn(len(t))
# 2. Time-domain energy
E_time = np.trapz(np.abs(signal)**2, t)
print(f"Time-domain energy: {E_time:.6f}")
# 3. Continuous wavelet transform
wavelet = 'morl'
scales = np.arange(1, 128)
coefs, freqs = pywt.cwt(signal, scales, wavelet,
sampling_period=1/fs)
# 4. Raw time–scale energy (no 1/Cψ normalization)
db = t[1] - t[0]
da = scales[1] - scales[0]
E_raw = np.sum(
np.abs(coefs)**2 * (db * da / (scales[:, None]**2))
)
print(f"Raw time–scale energy (∫∫|W|²/(a²)db da): {E_raw:.6f}")
# 5. Estimate admissibility constant Cψ ≃ E_raw/E_time
Cpsi_est = E_raw / E_time
print(f"Estimated admissibility constant C_ψ ≃ {Cpsi_est:.6f}")
# 6. Verify normalized energy matches time-domain
E_norm = E_raw / Cpsi_est
print(f"Normalized time–scale energy: {E_norm:.6f} <-- should ≃ Time-domain")
# 7. Inverse CWT (built-in if possible, else manual fallback)
def manual_icwt(coefs, scales, wname, Cpsi, t, db, da):
wav = pywt.ContinuousWavelet(wname)
# sample mother wavelet at scale=1
psi_vals, psi_x = wav.wavefun(level=10)
interp_psi = interp1d(psi_x, psi_vals,
bounds_error=False, fill_value=0.0)
x_rec = np.zeros_like(t)
for j, a in enumerate(scales):
norm = a**1.5
for i, b in enumerate(t):
W = coefs[j, i]
arg = (t - b) / a
x_rec += W * (interp_psi(arg)/norm) * db * da
return x_rec / Cpsi
if hasattr(pywt, 'icwt'):
signal_rec = pywt.icwt(coefs, scales, wavelet,
sampling_period=1/fs)
else:
print("⚠️ pywt.icwt not available, using slow manual inverse")
signal_rec = manual_icwt(coefs, scales, wavelet,
Cpsi_est, t, db, da)
# 8. Reconstruction performance
mse = np.mean((signal - signal_rec)**2)
snr = 10 * np.log10(np.sum(signal**2) / np.sum((signal - signal_rec)**2))
print(f"Reconstruction MSE: {mse:.6e}")
print(f"Reconstruction SNR: {snr:.2f} dB")
# 9. Plot results
plt.figure(figsize=(10,3))
plt.plot(t, signal, label='Original', alpha=0.7)
plt.plot(t, signal_rec, label='Reconstructed',alpha=0.7)
plt.xlabel('Time [s]')
plt.ylabel('Amplitude')
plt.title('Original vs. Reconstructed Signal')
plt.legend(loc='upper right')
plt.tight_layout()
plt.show()