For several reasons I am attempting to re-modulate a de-modulated FM (as in WFM Radio) signal using python. Demodulating the initial signal goes well, it produces the PSD chart below which looks just as expected. The audio sounds as it should as well. PSD of Non-remodulated Signal

After re-modulating the signal with the code at the end of this post and then demodulating the re-modulated signal the PSD looks like this, which looks mostly correct but from 0-about 15,000Hz there is significantly lower power than in the first signal. This loss of the lower frequencies is also clear in the audio. enter image description here

I've tweaked around this code quite a bit to include variations on directly modulating the carrier and not including the carrier at all, re-sampling, different filters, etc but nothing has corrected the issues. I'm beginning to think I have some sort of fundamental misunderstanding of Frequency Modulation and Frequency Demodulation using a polar discriminator.

Is there a conceptual reason when performing frequency modulation this way that just the lower frequencies are getting attenuated while the rest of the spectrum looks just fine? Is there another way this should be approached?

Thanks for the help, and apologies if this question overlooks something obvious DSP/Radio is new area for me.

# Variation of https://epxx.co/artigos/fmmodulation.html and `https://witestlab.poly.edu/blog/capture-and-decode-fm-radio/`
# translate input into a phase change that changes frequency  up to 75,000 Hz 
# ~75,000 is the MAX_DEVIATION for WFM in the U.S.
# xRemod is the a real valued array that produced the first PSD spectrum
# Fs_decd is the sample rate for xRemod, the IQ signal the was demodulated into xRemod was sampled at 2.5Msps and decimated by 10 before being demodulated.

xRemod2 = xRemod * numpy.pi * 75000 / Fs_decd
xRemod2 = xRemod2 % (2 * numpy.pi)

bui = numpy.cos(xRemod2)
buq = numpy.sin(xRemod2)

xRemodded = numpy.array([0]*len(xRemod2), numpy.complex64)

#As xRemod2 is a phase change? the next few lines have also been used
#They produce the same result
#carrier = 2 * math.pi *  97.3e6
#in_array = numpy.linspace(0,1/Fs_y,len(xRemod2)) 
#bui =  numpy.cos(in_array*carrier + xRemod2 )
#buq =  numpy.sin(in_array*carrier + (xRemod2))

xRemodded = numpy.array([0]*len(xRemod2), numpy.complex64)
xRemodded.real = bui
xRemodded.imag = buq

# Demodulate Again
yReModDemod = xRemodded[1:] * numpy.conj(xRemodded[:-1])  
xReModDemod = numpy.angle(yReModDemod )

## The above is then followed by the plotting code for the PSD which is identical to that used for the first one
  • 1
    $\begingroup$ xRemod2 = xRemod2 % (2 * numpy.pi) -- what is the purpose of this line? Also: have you tried (de)modulating a simple signals, such as a low-frequency sine wave, to validate your code? $\endgroup$ – MBaz Apr 24 at 22:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.