[SciPy-User] Accurate Frequency Measurement
Mon Nov 29 17:45:28 CST 2010
I have a few suggestions.
First of all, taking a giant FFT is a good place to start. But you
need to be aware that the performance of FFTs in general and
numpy/scipy's FFTs in particular is very spotty - the runtime can vary
wildly with the prime factorization of the numbers of points. What
this means in practice is that you should almost always do FFTs of
power-of-two numbers of points.
If your data length is not a power of two, which it probably isn't, I
recommend, rather than trimming it, padding it with the mean value.
This should give a nice clean FFT with one whopping peak corresponding
to your sine wave. The location of this peak should give you an
estimate of the frequency.
What you do next depends on the purity of your sine wave, or
equivalently the precise shape of your peak. If your input sine wave
really is an ideal sine wave, your peak will be beautifully narrow,
one or two FFT points wide at half-maximum. But this will only happen
if the oscillator is stable to within one two hundredth of a Hertz. If
there's any wander, at this high spectral resolution your peak may
look smeared, comb-like, or just generally ratty. If this is the case,
your problem becomes one of how to track its variations. There are
techniques for that too, but let's leave them aside for the moment and
assume your sine wave is really pure.
In an ideal world, you'd interpolate between the FFT points (in a
particular way) and find that your peak looked like a beautiful
sin(x)/x peak. You could use numerical optimization (1D maximum
finders) to find the position of its peak, which would be an estimate
of the true frequency limited only by your signal-to-noise. You could
also compute several other nice statistics (think of them as things
like peak width) that would tell you something about the frequency and
amplitude stability of your signal.
In this practical world, that interpolation is a laborious process.
But you can get a quick approximation to it by padding your input data
before taking the FFT: doubling the length of the input FFT (by adding
the data mean, say) serves as a factor-of-two Fourier interpolation;
it also slightly more than doubles the runtime. So you could do this a
few times to get a reasonably high-resolution image of your peak.
Since all that extra input is just padding, your peaks don't get any
narrower; and in fact, there are guaranteed to be no narrow features.
So if you've already interpolated by a factor of 8 or 16, you can do
the rest of the interpolation in the Fourier domain with parabolas and
lose almost nothing. So you can get a very nice frequency estimate at
a modest cost - if the signal is really that stable.
Of course, if you have the signal-to-noise to spare, these techniques
can let you get hundredth-of-a-Hertz frequency measurements with
considerably less than 200s of signal.
On 29 November 2010 17:19, Seth Nickell <firstname.lastname@example.org> wrote:
> I'm trying to measure the frequency of several sin 'tones' in a signal
> to within a hundredth of a Hz. I've made a 200s recording of the
> signal (44100 Hz wav file), but find that I cannot reasonably take an
> fft of the entire signal (takes 'forever'). The size of fft that seems
> to not take forever only gives me a resolution of a tenth of a Hz. The
> sin tones have the highest amplitudes by a long shot (i.e. the signal
> isn't very noisy). I've considered using a sin tone generator and
> using my ears+destructive interference to find the tones, but I'd like
> to be able to re-run this procedure on hundreds of signals, and I
> wouldn't be confident in the precision I could achieve.
> Can anyone suggest a straightforward technique in scipy to measure
> these frequencies more accurately?
> SciPy-User mailing list
More information about the SciPy-User