Confusing artifacts in PyWavelet complex-Morlet analysis of 1-kHz signal

729 Views Asked by At

I have some artifacts in a PyWavelets transform that are really confusing me. I'm using version 0.5.2. Can someone explain what is happening here?

I start by creating a 1-kHz signal, and then I attempt to analyze this signal with a complex Morlet continuous wavelet transform. I'm doing so with 3 octaves: 0.5 kHz to 1 kHz, 1 kHz to 2 kHz, and 2 kHz to 4 kHz, each one with 40 log-scaled scales. My intuition says that there should be a single peak at y=40 (being equivalent to 1 kHz), and that any difference in time should be minimal. Instead, I'm getting a peak at around y=35 to 37 (0.92 to 0.95 kHz), and there's some kind of periodic effect. (Strangely, this effect seems to occur only in the real component of the transform--the imaginary component looks closer to how I imagined it should look, though it's still not centered correctly. I believe that the real component and the imaginary component should look like time-shifted versions of each other, when looking at a pure sine wave.)

Am I misusing PyWavelets? Is there possibly a bug here? Any help would be welcome.

import numpy
import pywt
import matplotlib.pyplot as plt

# makes a 1-kHz signal
def make_data(length, quality):
    tau = 2*numpy.pi
    x = numpy.arange(length)
    y = numpy.sin(tau * x/(quality/1000)) # the 1000 is for 1 kHz
    return y

# does the continuous wavelet transform, outputting pic
def do_transform(data, base_freq, num_octaves, voices_per_octave, quality):
    # calculate the scales, based on the desired frequencies
    base_scale = quality / (2*base_freq)
    far_scale = base_scale / 2**num_octaves
    scales = numpy.geomspace(base_scale, far_scale, num=num_octaves*voices_per_octave+1, endpoint=True)

    # actual calculation
    coeffs, freqs = pywt.cwt(data, scales, "cmor", 1/quality)

    print("freqs: " +str(freqs))

    # output
    truncated = coeffs[:, 100:200]
    plt.imshow(abs(truncated), origin='lower', interpolation='none')
    #plt.imshow(truncated.real, origin='lower', interpolation='none')
    #plt.imshow(truncated.imag, origin='lower', interpolation='none')
    plt.subplots_adjust(left=0.01, right=0.99, top=0.99, bottom=0.05)
    plt.show()

data = make_data(1000, 44100)
do_transform(data, 500, 3, 40, 44100)

Magnitudes of transform Magnitudes of transform

Real components of transform Real components of transform

Imaginary components of transform Imaginary components of transform

1

There are 1 best solutions below

0
On

It turns out that this is a known issue. (It's still not clear if this is a bug or just unexpected behavior, but the discussion is here: https://github.com/PyWavelets/pywt/issues/307 .)

Thanks to everyone who looked at it and considered it.