Using peak search, I'm able to put the cursor on any of the several peaks on the spectrum analyzer display. I am trying to do something similar in software, with the output of the FFT of the radio spectrum. Using normal peak detect functions (such as those included in Scipy) does not seem to work. I think that there must be a particular algorithm used for radio, where particular circumstances exist (relatively flat noise floor, etc.) , what is it?


Here is a screenshot:

enter image description here The orange markers are where the peaks are thought to be.

How I made this was filtering and using scipy.signal.argrelextrema, though there are a few different techniques (some on stack overflow) that seem to be just as tricky.

  • 1
    $\begingroup$ Can I please ask you to post a screenshot of the spectrum analyser display, showing the spectrum you are referring to whose peaks are not picked up by the functions you have used? $\endgroup$
    – A_A
    Feb 10, 2017 at 15:26
  • $\begingroup$ @A_A - I've added a screenshot $\endgroup$
    – HorseHair
    Feb 10, 2017 at 16:28

1 Answer 1


This is most likely related to the comparator function and the waveform.

Long story short:

from scipy.signal import argrelextrema
from matplotlib import pyplot as plt
from scipy import linspace,pi,cos

#Time vector
t = linspace(0,4,4*1000); #4 seconds at Fs=1000Hz
#Phase vector
p = 2.0 * pi * t;
S = cos(4.0*p); #A 4Hz sinusoid
# Have a look at it, focus on where the maxima and minima occur.
#Let's find some peaks
Pmin = argrelextrema(S, lambda x,y:x<y)
Pmax = argrelextrema(S, lambda x,y:x>y)

Pmin identifies all peaks: [500, 1500, 2499, 3499]

Pmax has skipped some : [1000,2999]

Try it again, by going for Pmax2 = argrelextrema(S, lambda x,y:x>=y). Pmax2 now is [0,1000,1999,2000,2999,3999]

Now, peak detection is relatively straightforward. The mathematical way to think about it is to get the first derivative and look at where it drops to zero. Every zero of the first derivative is either a local maximum or local minimum. To decide if you are at a peak or a trough, you look at the value of the second derivative at that point. The second derivative of your signal, is the derivative of the first derivative. If that is positive, your candidate point is a local minimum, if it is negative, your candidate point is a local maximum.

That's fine, but costly and there are alternative ways to achieve it. For example, a peak could be defined as the pattern increase, decrease of signal values and a trough could be defined as the pattern decrease, increase of signal values. This is straightforward to understand but in reality, extrema might be "wide". In other words, the pattern of values now is increase, same, same, same, same, same, same, same, decrease. Where is the peak there? (or, conversely, the trough).

You might say, "That's not a problem, the peak is in the middle of the wide top" and that would be a reasonable assumption. In fact, you could achieve this by first "blurring" your data (taking the average) in such a way as to reduce the "wideness" and now you are back in a case of "increase, decrease".

This is achieved in argrelextrema by modifying the order parameter.

So, having said this, why does argrelextrema fails to identify those peaks in the extremely simple case of a cos?

If you take a close look at what the cos is doing, you will observe that:

  • When the peak is identified:
    • ...,0.99993952, 0.99998889, 0.99999877, 0.99996914...
      • Did you spot the pattern? (increase,decrease)
  • When the peak is NOT identified:
    • ..., 0.99995556, 0.99999506, 0.99999506, 0.99995556,...
      • Did you spot the pattern? (increase,same,decrease)

So, by modifying the comparator function, you enable argrelextrema to widen its search for what an extrema looks like.

Try introducing the <=, >= comparator function.

Hope this helps.

  • $\begingroup$ Thanks for the thorough answer. I've been able to get detection to work decently, with scipy.signal.find_peaks_cwt. But there's one problem it won't solve. When a peak is very wide (a television broadcast, etc.), it won't detect it. $\endgroup$
    – HorseHair
    Feb 11, 2017 at 15:45
  • 1
    $\begingroup$ @horsehair Thank you for letting me know, please see this link for more complex techniques and also note that you might have to incorporate some hysteresis in your peak detection. Of course, just as before, it always helps to post the "failing" case. $\endgroup$
    – A_A
    Feb 11, 2017 at 15:55

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.