WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Scaling mismatch between PyWavelets CWT and my implementation #801

@grigorishat

Description

@grigorishat

I would like to understand the scaling in the CWT implementation in Python. I know that there are topics about it in Stackoverflow, although I cannot understand why my implementation does not match with the implementation in PyWavelets.

My approach is just an convolution between my signal and the wavelet. I know the implementation here is based on an old version of a matlab implementation described here.

I’ve attached an example script and resulting plots at the end. The code snippet below shows that when I do the multiplication by 1 / np.sqrt(fs) (fs being the sampling rate) , the amplitude of my implementation overlays compared to PyWavelets. Without that factor, the signals are off.

This extra factor seems unintuitive, and I’m concerned I’m missing a detail in either the wavelet definition or the normalisation.

import numpy as np
import matplotlib.pyplot as plt
from scipy.ndimage import gaussian_filter1d
import pywt

# create a test signal containing 40Hz and 10Hz 

# --- Parameters ---
fs = 3000       # Sampling rate in Hz
duration = 10   # Total duration in seconds
N = int(fs * duration)
t = np.linspace(0, duration, N, endpoint=False)

# --- 10 Hz signal (continuous) ---
f1 = 10
sig10 = np.sin(2 * np.pi * f1 * t)

# --- Create raw on/off mask for 40 Hz signal ---
# intervals: [0–1s], [2–4s], [5–8s]
mask_raw = ((t >= 0) & (t < 1)) | ((t >= 2) & (t < 4)) | ((t >= 5) & (t < 8))
mask_raw = mask_raw.astype(float)  # convert True/False to 1.0/0.0

# --- Smooth the mask with a large Gaussian filter to get ~0.8s transitions ---
sigma = 667  # ~ (0.8s * fs) / 6
mask_smooth = gaussian_filter1d(mask_raw, sigma=sigma)

# --- Create 40 Hz signal and multiply by the smoothed mask ---
f2 = 40
sig40 = np.sin(2 * np.pi * f2 * t) * mask_smooth

# --- Combine signals ---
signal = sig10 + sig40

# --- Plot the final combined signal ---
plt.figure()
plt.plot(t, signal)
plt.title("Test Signal (10 Hz + Intermittent 40 Hz)")
plt.xlabel("Time in s")
plt.ylabel("Amplitude")
plt.xlim(0, 10)

B = 14
C = 2

wavelet = f'cmor{B}-{C}'

# I aim to analyse 40 Hz so I calculate the scale
# freq_analyse is the frequency to analyse diviced by the sampling rate fs
freq_analyse = 40 / fs

scale = pywt.frequency2scale(wavelet, freq_analyse)

# calculate coefficients
coefs, freqs = pywt.cwt(signal, scale, wavelet, sampling_period=1/fs)

# now my implementation follows

a=0 # no shifting, this is done by the convolution below

b = scale/fs # b is the scale in the wavelet function, it can be also computed as: b = C/f with f =40Hz the frequency of interest

# create the wavelet with the same "sampling rate" as my test signal above

# wavelet should go from -2 to 2
wl_start = -2
wl_end = 2
t_wl = np.arange(wl_start, wl_end+1/fs,1/fs)

psi_morlet = 1/np.sqrt(B*np.pi) * np.exp(-((t_wl-a)/b)**2/B) * np.exp(1j*2*np.pi*C*((t_wl-a)/b))

# calculate the convolution 
conv_result =  np.convolve(np.conj(psi_morlet), signal, mode='full')

# scale the result by 1/sqrt(b)

conv_result = 1/np.sqrt(b) * conv_result

# plot the coefficients of the cwt over the time

fig, ax = plt.subplots(1, 1, figsize=(10, 10))

ax.plot(t, abs(coefs[0]), label='pywt implementation')

# create time vector for the convolution
t_conv = np.arange(wl_start,duration+wl_end+1/fs, 1/fs)
ax.plot(t_conv,abs(conv_result), label='my implementation') 

ax.legend()

ax.set_xlabel('Time in s')
ax.set_ylabel('CWT')


# plot the coefficients of the cwt over the time

fig, ax = plt.subplots(1, 1, figsize=(10, 10))

ax.plot(t, abs(coefs[0]), label='pywt implementation')

# create time vector for the convolution
t_conv = np.arange(wl_start,duration+wl_end+1/fs, 1/fs)
# IF IS SCALE MY IMPLEMENTATION BY 1/sqrt(fs) THAN THE RESULTS MATCH
ax.plot(t_conv,abs(conv_result)*1/np.sqrt(fs), label='my implementation') 

ax.legend()
ax.set_xlabel('Time in s')
ax.set_ylabel('CWT')

Image
Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions