details on how to change Displayed Axis Values

3,689 views
Skip to first unread message

madr...@yahoo.com

unread,
Apr 4, 2014, 2:36:29 PM4/4/14
to pyqt...@googlegroups.com
I am new to OOP and pyQtGraph. 

I am trying to create a Class to display an FFT

I am having a problem, because I do not fully follow the documentation, and pyCharm does not fully traverse the objects in pyQtGraph.

Here's my code, to date.

import PyQt4.QtCore
import PyQt4.QtGui
import pyfftw
from PyQt4.QtGui import QApplication
import numpy as np
import pyqtgraph as pg

class FFT_Plot():
    def __init__(self,
                 win,
                 nSamples,
                 aData,
                 sRate,
                 wFunction,
                 zStart = 0):
        self.nSamples = nSamples    # Number of Sample must be a 2^n power
        self.aData = aData          # Amplitude data array
        self.sRate = sRate          # Sample Rate
        self.wFunction = wFunction  # Windowing Function
        self.zStart = zStart        # Start of Zoom Window if Used
        self.zStop = nSamples/2     # End of Zoom Window if Used
        # Instantiate a plot window within an existing pyQtGraph window.
        self.plot = win.addPlot(title="FFT")
        self.update(aData)
        self.grid_state()

        self.plot.setLabel('left', 'Amplitude', 'Volts')
        self.plot.setLabel('bottom', 'Frequency', 'Hz')

    def update(self, aData):
        x = np.fft.fft(aData,)
        amplitude = np.absolute(x)
        fScale = np.linspace(0 , 50000, self.nSamples)
        self.plot.plot(amplitude)
        # Calculate and set-up X axis
        self.plot.setXRange(SampleSize/2, 0)

    def grid_state(self, x = True, y = True):
        self.plot.showGrid(x, y)


I am not showing how I create a sine wave and test this Class.

How do I scale the axis values so that, ex. 2048 samples, instead of the axis displaying 0 to 1 it displays the frequency values?

Go slowly please my QBasic/VB6 brain already has a headache! :)

Mike Sr.

Luke Campagnola

unread,
Apr 4, 2014, 3:28:13 PM4/4/14
to pyqt...@googlegroups.com
On Fri, Apr 4, 2014 at 2:36 PM, <madr...@yahoo.com> wrote:
I am new to OOP and pyQtGraph. 

I am trying to create a Class to display an FFT

I am having a problem, because I do not fully follow the documentation, and pyCharm does not fully traverse the objects in pyQtGraph.

Here's my code, to date.

[snip]
 
   def update(self, aData):
        x = np.fft.fft(aData,)
        amplitude = np.absolute(x)
        fScale = np.linspace(0 , 50000, self.nSamples)
        self.plot.plot(amplitude)
        # Calculate and set-up X axis
        self.plot.setXRange(SampleSize/2, 0)

How do I scale the axis values so that, ex. 2048 samples, instead of the axis displaying 0 to 1 it displays the frequency values?

There are a couple of important things you are missing:

1) In a pyqtgraph plot, the axis values are determined automatically based on the coordinate system of the displayed data. This means that it is usually not necessary to set the axis values manually. If your data do not match up to the axes, the problem is probably with your data and not with the axes.

2) When you call plot() with only an array of y-values specified, pyqtgraph assumes you want integer x-values like range(len(yValues)). So if you want the x-values of your samples to range from 0 to 50k, you need to provide those values in the call to plot: self.plot.plot(x=fScale, y=amplitude). You should find that the axis values react accordingly.
 



madr...@yahoo.com

unread,
Apr 7, 2014, 6:57:31 AM4/7/14
to pyqt...@googlegroups.com
Thanks Luke,

     I did indeed 'miss' that point.

     Here is the corrected, functional, class prototype. (More work to be done.)

     It appears that the Amplitude Array is going to have to be pre-scaled in order to get the Y-Axis labels to reflect true amplitude, though.

class FFT_Plot():
    def __init__(self,
                 win,
                 nSamples,
                 aData,
                 sRate,
                 wFunction,
                 zStart = 0):
        self.nSamples = nSamples    # Number of Sample must be a 2^n power
        self.aData = aData          # Amplitude data array
        self.sRate = sRate          # Sample Rate as Frequency
        self.wFunction = wFunction  # Windowing Function
        self.zStart = zStart        # Start of Zoom Window if Used
        self.zStop = nSamples/2     # End of Zoom Window if Used
        # Instantiate a plot window within an existing pyQtGraph window.
        self.plot = win.addPlot(title="FFT")
        self.update(aData)
        self.grid_state()
        self.plot.setLabel('left', 'Amplitude', 'Volts')
        self.plot.setLabel('bottom', 'Frequency', 'Hz')

    def update(self, aData):
        x = np.fft.fft(aData,)
        amplitude = np.absolute(x)
        # Create a linear scale based on the Sample Rate and Number of Samples.
        fScale = np.linspace(0 , self.sRate, self.nSamples)
        self.plot.plot(x = fScale, y = amplitude, pen={'color': (0, 0, 0), 'width': 2})
        # Because the X-axis is now tied to the fScale, which os based on sRate, 
        # to set any range limits you must use the sRate.
        self.plot.setXRange(self.sRate/2, 0)

    def grid_state(self, x = True, y = True):
        self.plot.showGrid(x, y)


Any DSP folks have any non-Mathematical comments... Feel free to chime in...


Luke Campagnola

unread,
Apr 7, 2014, 10:24:31 AM4/7/14
to pyqt...@googlegroups.com
On Mon, Apr 7, 2014 at 6:57 AM, <madr...@yahoo.com> wrote:
Thanks Luke,

     I did indeed 'miss' that point.

     Here is the corrected, functional, class prototype. (More work to be done.)

     It appears that the Amplitude Array is going to have to be pre-scaled in order to get the Y-Axis labels to reflect true amplitude, though.

Another option is to set the transformation of the PlotDataItem:

    plotdata = plotitem.plot(x, y)
    tr = QtGui.QTransform()
    tr.scale(1, 0.5)
    plotdata.setTransform(tr)

This method is inherited from QGraphicsItem:


madr...@yahoo.com

unread,
Apr 8, 2014, 3:33:21 PM4/8/14
to pyqt...@googlegroups.com
Another option is to set the transformation of the PlotDataItem:

    plotdata = plotitem.plot(x, y)
    tr = QtGui.QTransform()
    tr.scale(1, 0.5)
    plotdata.setTransform(tr)

This method is inherited from QGraphicsItem:
Luke,
    To be honest I haven't got a clue as to what QTransform is trying to do. When I tried it I was still reading out in kiloVolts. :)
 
    I love DSP... But, as a High School dropout with a GED, Navy electronics schools my Math skills simply do not carry me in this area.
 
    The closest I've been able to come to correcting the Amplitude of the FFT is as follows.
 
        def update(self, aData):
            # Returns a Complex Array
            x = np.fft.fft(aData,)
            # Returns a 'Normal' Array
            amplitude = np.absolute(x)
            # Adjusts the Amplitude of the FFT
            amplitude = amplitude / (self.nSamples/2)
            print 'Max FFT Ampl: ', np.amax(amplitude)

            # Create a linear scale based on the Sample Rate and Number of Samples.
            fScale = np.linspace(-1, self.sRate, self.nSamples)

            self.plot.plot(x = fScale, y = amplitude, pen={'color': (0, 0, 0), 'width': 2})
 
    But, there is a lot of ripple in the amplitude. And, the accuracy is frequency dependent.
 
    You never really appreciate your Spectrum Analyzer until you only have an O'Scope/Digitizer and need to build one yourself. :)
 
    BTW: I really apreciated your extremely prompt reply. And, yes, I know this isn't a oyQtGraph issue. But, maybe there's a DSP guy here that can break this down so a High Schooler can follow it?
 
 

Luke Campagnola

unread,
Apr 8, 2014, 8:07:00 PM4/8/14
to pyqt...@googlegroups.com
On Tue, Apr 8, 2014 at 3:33 PM, <madr...@yahoo.com> wrote:
Another option is to set the transformation of the PlotDataItem:

    plotdata = plotitem.plot(x, y)
    tr = QtGui.QTransform()
    tr.scale(1, 0.5)
    plotdata.setTransform(tr)

This method is inherited from QGraphicsItem:
Luke,
    To be honest I haven't got a clue as to what QTransform is trying to do. When I tried it I was still reading out in kiloVolts. :)

Basically, the transformation for any QGraphicsItem defines how it is scaled, rotated, and translated within its coordinate system. The Qt docs have a pretty extensive explanation.

You should find that the code you wrote:

amplitude = amplitude / (self.nSamples/2)
fScale = np.linspace(-1, self.sRate, self.nSamples)
self.plot.plot(x = fScale, y = amplitude, pen={'color': (0, 0, 0), 'width': 2})

 .. is visually equivalent to the following:

fScale = np.linspace(-1, self.sRate, self.nSamples)
curve = self.plot.plot(x = fScale, y = amplitude, pen={'color': (0, 0, 0), 'width': 2})
tr = QtGui.QTransform()
tr.scale(1, 1.0 / (self.nSamples/2))
curve.setTransform(tr)

(not that I see any advantage to doing it one way or the other)

As far as I know, your amplitude adjustment is correct. Note, though, that the power contained in your signal may be spread over multiple samples in the frequency domain, so the peak may not match the amplitude you expect.

Reply all
Reply to author
Forward
0 new messages