discuss-gnuradio
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Discuss-gnuradio] Floating point FFT usage - suppress half of it?


From: Brad Hein
Subject: Re: [Discuss-gnuradio] Floating point FFT usage - suppress half of it?
Date: Sun, 18 Mar 2018 13:29:17 -0400

Thank you Marcus and Max,

Long story short I need to calculate the amplitude of each frequency bin of the input wav. I'm starting with a 1024 bin fft, sample rate 48k. So 0-24khz each bin being 24000/1024 Hz wide. I want to know the amplitude or loudness of sounds each frequency bin.

The goal being to monitor a wav stream and periodically record to a database the average amplitude within each frequency band.

Then I can later query the database for information about what harmonics were spiking at different times. This will be useful I believe in correlating various harmonics with changes taking place at the same time. 

If we listen to a dial tone recording for example, we can clearly tell that two and only two tones are present. Is FFT able to tell us the same thing (exactly two spikes, at expected frequencies) if given floating point samples rather than complex?  (Tried this using wx gui FFT plot and I don't see mirroring, does the WX GUI FFT suppress the mirroring?)


Taking a step back, maybe the mirroring is a product of the method I'm using to interpret the output of the FFT. I'm feeding the stream into a vector sink and reading from the vector sink one "item" at a time with a for loop. Code snippets below:

self.fft_vxx_0 = fft.fft_vfc(myVectorLength, True, (window.blackmanharris(myVectorLength)), 1)
self.blocks_wavfile_source_0 = blocks.wavfile_source('test.wav', False)
self.blocks_vector_sink_x_0 = blocks.vector_sink_f(myVectorLength)
self.blocks_stream_to_vector_0 = blocks.stream_to_vector(gr.sizeof_float*1, myVectorLength)
self.blocks_null_sink_0 = blocks.null_sink(gr.sizeof_float*1)
self.blocks_keep_one_in_n_0 = blocks.keep_one_in_n(gr.sizeof_float*1, self.n_keep)
self.blocks_complex_to_mag_squared_0 = blocks.complex_to_mag_squared(myVectorLength)
self.blocks_throttle = blocks.throttle(gr.sizeof_float * 1, self.samp_rate, True)

# Throw away unneeded channel. input is mono but two channels.
self.connect((self.blocks_wavfile_source_0, 0), (self.blocks_null_sink_0, 0))
# Throttle the input so the flow graph runs long enough to sample it
self.connect((self.blocks_wavfile_source_0, 1), (self.blocks_throttle, 0))
# Down-sample / decimate the input
self.connect((self.blocks_throttle, 0), (self.blocks_keep_one_in_n_0, 0))
# Convert the stream to a vector in preparation for FFT
self.connect((self.blocks_keep_one_in_n_0, 0), (self.blocks_stream_to_vector_0, 0))
# Perform FFT analysis of the stream.
self.connect((self.blocks_stream_to_vector_0, 0), (self.fft_vxx_0, 0))
# Send FFT data (phase and magnitude) into a mag^2 block to get strength of each bin
self.connect((self.fft_vxx_0, 0), (self.blocks_complex_to_mag_squared_0, 0))
# Send the final result (magnitudes in each fft bin) into vector sink.
self.connect((self.blocks_complex_to_mag_squared_0, 0), (self.blocks_vector_sink_x_0, 0))


# Probe thread, processes each item out of the vector probe:
def probeFftDataThread():
    # Sleep while the flow graph starts up. Without waiting we would get attribute errors.
    time.sleep(5)

    bigaverage = 0
    multiplier = 10000

    while True:
        # dataVal = self.probeFft.data()
        # try:

        dataVal = self.blocks_vector_sink_x_0.data()
        if (len(dataVal) == 0):
            time.sleep(0.1)
            continue;


        for thisDataElement in dataVal:
            thisDataElement = thisDataElement * multiplier
            bigaverage = (bigaverage + thisDataElement) / 2

        thresh_min = bigaverage * 8
        thresh_med = bigaverage * 16
        thresh_max = bigaverage * 64

        elemnum = 0
        avgLevel = 0
        # sys.stdout.write("len=" + str(len(dataVal)) + "\n")
        for thisDataElement in dataVal:
            elemnum = elemnum + 1
            thisDataElement = thisDataElement * multiplier
            thisDataElement = int(round(thisDataElement))
            avgLevel = (avgLevel + thisDataElement)/2


            if (thisDataElement < thresh_min):
                sys.stdout.write("  ")
            else:
                if (thisDataElement >= thresh_min and thisDataElement < thresh_med):
                    sys.stdout.write("  ")
                else:
                    if (thisDataElement >= thresh_med and thisDataElement < thresh_max):
                        sys.stdout.write("o ")
                    else:
                        if (thisDataElement >= thresh_max):
                            sys.stdout.write("* ")


            if (elemnum % myVectorLength == 0):
                sys.stdout.write(" avg: " + str(avgLevel) + " bigavg=" + str(bigaverage) + "\n")


            # if (elemnum >= 512):
            #     break
        # print("")
        self.blocks_vector_sink_x_0.reset()


        # Reset the contents of the vector sink?
        # --> reset() caused it to go to 0 length and stay there.
        # self.blocks_vector_sink_x_0.reset()
        # dataVal = dataVal * 1000
        # dataVal = round(dataVal)
        # print ("DATAlength: " + str(len(dataVal)))
        # print ("the datatype for the vector sink data is " + str(type(dataVal)))
        # self.set_variable_function_probe_0(dataVal)

        # except AttributeError:
        #     print ("No vector sink data attribute yet")
        #     pass
        time.sleep(0.1)



Application output looks like this (very wide terminal required. Noote the mirroring):

 
                                            o o                                   o o                                            avg: 1875 bigavg=426.111233193
                                                                                                                                 avg: 153 bigavg=426.111233193
                                        o o o o                                   o o o o                                        avg: 155 bigavg=426.111233193
                o                                                                                               o                avg: 611 bigavg=426.111233193
                                                                                                                                 avg: 1292 bigavg=426.111233193
                                                                                                                                 avg: 449 bigavg=426.111233193
                                                                                                                                 avg: 490 bigavg=426.111233193
                                                                                                                                 avg: 1342 bigavg=426.111233193
                                          o o                                       o o                                          avg: 267 bigavg=426.111233193
                                                                                                                                 avg: 1428 bigavg=426.111233193
      o o o o o                 o o     o                                               o     o o                 o o o o o      avg: 3781 bigavg=426.111233193
                                      o o o                 o       o                 o o o                                      avg: 829 bigavg=426.111233193
            o                                                                                                       o            avg: 469 bigavg=426.111233193
                o                     o o o o                                       o o o o                     o                avg: 634 bigavg=426.111233193
                                                          o o       o o                                                          avg: 3076 bigavg=426.111233193
                                        o                                               o                                        avg: 671 bigavg=426.111233193
                                          o o                                       o o                                          avg: 1135 bigavg=426.111233193
                                        o * o                                       o * o                                        avg: 430 bigavg=426.111233193
              o                         o o o o                                   o o o o                         o              avg: 765 bigavg=426.111233193
                                          * * o           o * o o o * o           o * *                                          avg: 942 bigavg=426.111233193
                                                                                                                                 avg: 586 bigavg=426.111233193
                                      o o o o                                       o o o o                                      avg: 736 bigavg=426.111233193
                                                                                                                                 avg: 425 bigavg=426.111233193
                                                                                                                                 avg: 1448 bigavg=1448.26562964
                                        o                                               o                                        avg: 741 bigavg=740.994978869






On Mar 18, 2018 11:11 AM, "Maximilian Stiefel" <address@hidden> wrote:
Hi Brad,
Hi Marcus,

@ Brad:

The basic properties of the Fourier transform give you this symmetry.
In general the Fourier transform has the following property, usually refered to as conjungation:

F{ x*(t) } = X*(-jw).

For purely real input signals, this boils down to

Xr(jw) = Xr(-jw) (even) and Xi(jw) = -Xi(-jw) (odd).
  ^                     ^
real part           imaginary part of the Fourier transform

For purely imaginary input signals, it boils down to

Xr(jw) = -Xr(-jw) (odd) and Xi(jw) = Xi(-jw) (even).
  ^                     ^
real part           imaginary part of the Fourier transform

Hence, you will always get a symmetric spectrum for a purely real or purely imaginary input, since purely real or purely imaginary signals need two frequencies (a positive and a negative one) to cancel out the imaginary part or the real part respectively (http://whiteboard.ping.se/SDR/IQ).

Think e.g. about a real cosine (even); it's Fourier transform will be real and even (two positive diracs at -w0 and w0). If you have a real sine (odd), the Fourier transform will be imaginary and odd (a positive dirac at w0 and a negative dirac at -w0).

If you think about a complex signal instead e.g. x(t) = A * e^{jw0t}. This will not yield a symmetric spectrum (one dirac at w0).

Thus, you can not really discard the negative frequencies if you want to be able to go back to time domain, but you can predict, what they are going to be. If you do an IFFT with the positive frequencies only, you will get rubbish at the output. You do not really waste CPU time if you calculate the full spectrum as it is just natural to obtain a result from the FFT which spans from -fs to fs (assuming you are sampling complex with fs). The negative frequencies are simply a part of a purely real signal, that you have in your case.

@ Marcus: The FFTW lib probably still needs to compute the negative bins, right?

As I understood it, correct me if I am wrong, you have to change physics to get around the negative frequencies to be calculated. Indeed, it is an interesting question, though.

Regards,

Max


On 2018-03-18 15:17, Müller, Marcus (CEL) wrote:
Hi Brad,

Put another way, is it safe to
discard the left side of the output as it appears to consist only of
aliases of the true data?

Well, it does not contain information that's not in the other side
(it's not really an alias, though, but the conjugate mirror of the
other side).

best way to recover the useful information from the FFT

What's the thing you want to achieve? Because "useful" and "best" are
meaningless without knowing what you want to do! :)

Regarding the numerical inefficiency of computing both sides of
something symmetric: The FFTW, the library used to actually compute
these DFTs, does come with an effort-reduced FFT for real-valued input
data, which will also only output one side of the spectrum.
The savings in CPU cycles are usually relatively modest (because the
non-hermitian FFT is already very optimized, and leaving out the
results stage doesn't simply go and half the effort) – the main
advantage would, in practice, probably be reduced memory bandwidth.

But: audio sample rates on modern CPUs with FFTs of benign length might
not really be what you should worry about computationally, I'd guess.
What is the computational bottleneck you need to widen?

Best regards,
Marcus

On Sun, 2018-03-18 at 09:29 -0400, Brad Hein wrote:

Frequency domain output of the FFT block seems to be mirrored when
using floating point data type. I recall that when using complex
numbers this mirroring doesn't occur. The input I'm working with is
48khz sound from a wav file. I understand this mirroring is a
characteristic of the Fourier transform, but what I'm wondering is
what is the best way to recover the useful information from the FFT

I tried marshaling the data into complex data before feeding it into
the FFT but I couldn't get a reliable process.

If discarding half of the FFT output is the way to go then what about
the wasted CPU in calculating that half of the frequency domain - is
there a better way to recover the useful frequency domain
information?

Thanks!

_______________________________________________
Discuss-gnuradio mailing list
address@hidden
https://lists.gnu.org/mailman/listinfo/discuss-gnuradio


_______________________________________________
Discuss-gnuradio mailing list
address@hidden
https://lists.gnu.org/mailman/listinfo/discuss-gnuradio

_______________________________________________
Discuss-gnuradio mailing list
address@hidden
https://lists.gnu.org/mailman/listinfo/discuss-gnuradio


reply via email to

[Prev in Thread] Current Thread [Next in Thread]