Пример #1
0
class HistPlot(QtWidgets.QWidget):

    def __init__(self, parent, logger):
        super(HistPlot, self).__init__()

        # store the logger instance
        self.logger = logger

        self.verticalScaleDivision = ScaleDivision(-140, 0, 100)
        self.verticalScaleTransform = CoordinateTransform(-140, 0, 100, 0, 0)

        self.verticalScale = VerticalScaleWidget(self, self.verticalScaleDivision, self.verticalScaleTransform)
        self.verticalScale.setTitle("PSD (dB A)")

        self.horizontalScaleDivision = ScaleDivision(44, 22000, 100)
        self.horizontalScaleTransform = CoordinateTransform(44, 22000, 100, 0, 0)

        self.horizontalScale = HorizontalScaleWidget(self, self.horizontalScaleDivision, self.horizontalScaleTransform)
        self.horizontalScale.setTitle("Frequency (Hz)")

        self.canvasWidget = CanvasWidget(self, self.verticalScaleTransform, self.horizontalScaleTransform)
        self.canvasWidget.setTrackerFormatter(lambda x, y: "%d Hz, %.1f dB" % (x, y))

        plot_layout = QtWidgets.QGridLayout()
        plot_layout.setSpacing(0)
        plot_layout.setContentsMargins(0, 0, 0, 0)
        plot_layout.addWidget(self.verticalScale, 0, 0)
        plot_layout.addWidget(self.canvasWidget, 0, 1)
        plot_layout.addWidget(self.horizontalScale, 1, 1)

        self.setLayout(plot_layout)

        self.needfullreplot = False

        self.horizontalScaleTransform.setLogarithmic()
        self.horizontalScaleDivision.setLogarithmic()

        # insert an additional plot item for the peak bar
        self.bar_peak = HistogramPeakBarItem()
        self.canvasWidget.attach(self.bar_peak)
        self.peak = zeros((1,))
        self.peak_int = 0
        self.peak_decay = PEAK_DECAY_RATE

        self.histogram = HistogramItem()
        self.histogram.set_color(Qt.Qt.darkGreen)
        self.canvasWidget.attach(self.histogram)

        # need to replot here for the size Hints to be computed correctly (depending on axis scales...)
        self.update()

    def setdata(self, fl, fh, fc, y):
        self.histogram.setData(fl, fh, fc, y)

        self.compute_peaks(y)
        self.bar_peak.setData(fl, fh, self.peak, self.peak_int, y)

        # only draw on demand
        # self.draw()

    def draw(self):
        if self.needfullreplot:
            self.needfullreplot = False

            self.verticalScaleDivision.setLength(self.canvasWidget.height())
            self.verticalScaleTransform.setLength(self.canvasWidget.height())

            start_border, end_border = self.verticalScale.spacingBorders()
            self.verticalScaleTransform.setBorders(start_border, end_border)

            self.verticalScale.update()

            self.horizontalScaleDivision.setLength(self.canvasWidget.width())
            self.horizontalScaleTransform.setLength(self.canvasWidget.width())

            start_border, end_border = self.horizontalScale.spacingBorders()
            self.horizontalScaleTransform.setBorders(start_border, end_border)

            self.horizontalScale.update()

            x_major_tick = self.horizontalScaleDivision.majorTicks()
            x_minor_tick = self.horizontalScaleDivision.minorTicks()
            y_major_tick = self.verticalScaleDivision.majorTicks()
            y_minor_tick = self.verticalScaleDivision.minorTicks()
            self.canvasWidget.setGrid(array(x_major_tick),
                                      array(x_minor_tick),
                                      array(y_major_tick),
                                      array(y_minor_tick))

        self.canvasWidget.update()

    # redraw when the widget is resized to update coordinates transformations
    def resizeEvent(self, event):
        self.needfullreplot = True
        self.draw()

    def compute_peaks(self, y):
        if len(self.peak) != len(y):
            y_ones = ones(y.shape)
            self.peak = y_ones * (-500.)
            self.peak_int = zeros(y.shape)
            self.peak_decay = y_ones * 20. * log10(PEAK_DECAY_RATE) * 5000

        mask1 = (self.peak < y)
        mask2 = (~mask1)
        mask2_a = mask2 * (self.peak_int < 0.2)
        mask2_b = mask2 * (self.peak_int >= 0.2)

        self.peak[mask1] = y[mask1]
        self.peak[mask2_a] = self.peak[mask2_a] + self.peak_decay[mask2_a]

        self.peak_decay[mask1] = 20. * log10(PEAK_DECAY_RATE) * 5000
        self.peak_decay[mask2_a] += 20. * log10(PEAK_DECAY_RATE) * 5000

        self.peak_int[mask1] = 1.
        self.peak_int[mask2_b] *= 0.975

    def setspecrange(self, spec_min, spec_max):
        self.verticalScaleTransform.setRange(spec_min, spec_max)
        self.verticalScaleDivision.setRange(spec_min, spec_max)

        # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar)
        self.verticalScale.scaleBar.updateGeometry()

        self.needfullreplot = True
        self.update()

    def setweighting(self, weighting):
        if weighting is 0:
            title = "PSD (dB)"
        elif weighting is 1:
            title = "PSD (dB A)"
        elif weighting is 2:
            title = "PSD (dB B)"
        else:
            title = "PSD (dB C)"

        self.verticalScale.setTitle(title)
Пример #2
0
class TimePlot(QtWidgets.QWidget):

    def __init__(self, parent):
        super(TimePlot, self).__init__()

        self.verticalScaleDivision = ScaleDivision(-1, 1, 100)
        self.verticalScaleTransform = CoordinateTransform(-1, 1, 100, 0, 0)

        self.verticalScale = VerticalScaleWidget(self, self.verticalScaleDivision, self.verticalScaleTransform)
        self.verticalScale.setTitle("Signal")

        self.horizontalScaleDivision = ScaleDivision(-1, 1, 100)
        self.horizontalScaleTransform = CoordinateTransform(-1, 1, 100, 0, 0)

        self.horizontalScale = HorizontalScaleWidget(self, self.horizontalScaleDivision, self.horizontalScaleTransform)
        self.horizontalScale.setTitle("Time (ms)")

        self.canvasWidget = GlCanvasWidget(self, self.verticalScaleTransform, self.horizontalScaleTransform)
        self.canvasWidget.setTrackerFormatter(lambda x, y: "%.3g ms, %.3g" % (x, y))

        self.legendWidget = LegendWidget(self, self.canvasWidget)

        plotLayout = QtWidgets.QGridLayout()
        plotLayout.setSpacing(0)
        plotLayout.setContentsMargins(0, 0, 0, 0)
        plotLayout.addWidget(self.verticalScale, 0, 0)
        plotLayout.addWidget(self.canvasWidget, 0, 1)
        plotLayout.addWidget(self.horizontalScale, 1, 1)
        plotLayout.addWidget(self.legendWidget, 0, 2)

        self.setLayout(plotLayout)
        self.legendWidget.hide()

        self.needfullreplot = False

        self.curve = CurveItem()
        self.curve.setColor(QtGui.QColor(Qt.Qt.red))
        # gives a title to the curve for the legend
        self.curve.setTitle("Ch1")
        self.canvasWidget.attach(self.curve)

        self.curve2 = CurveItem()
        self.curve2.setColor(QtGui.QColor(Qt.Qt.blue))
        # gives a title to the curve for the legend
        self.curve2.setTitle("Ch2")
        # self.curve2 will be attached when needed

        # need to replot here for the size Hints to be computed correctly (depending on axis scales...)
        self.update()

        self.xmin = 0.
        self.xmax = 1.

        self.canvas_width = 0

        self.dual_channel = False

        self.paused = False

        self.canvasWidget.resized.connect(self.canvasResized)

    def setdata(self, x, y):
        if self.canvas_width != self.canvasWidget.width():
            Logger().push("timeplot : changed canvas width")
            self.canvas_width = self.canvasWidget.width()
            self.update_xscale()

        if self.dual_channel:
            self.dual_channel = False
            self.canvasWidget.detach(self.curve2)
            # disable the legend, useless when one channel is active
            self.legendWidget.hide()
            # the canvas reisze event will trigger a full replot

        if self.xmax != x[-1]:
            Logger().push("timeplot : changing x max")
            self.xmax = x[-1]
            self.settimerange(self.xmin, self.xmax)
            self.update_xscale()
            self.needfullreplot = True
        if self.xmin != x[0]:
            Logger().push("timeplot : changing x min")
            self.xmin = x[0]
            self.settimerange(self.xmin, self.xmax)
            self.update_xscale()
            self.needfullreplot = True

        if not self.paused:
            y_interp = np.interp(self.xscaled, x, y)
            self.curve.setData(self.xscaled, y_interp)

        self.draw()

    def draw(self):
        if self.needfullreplot:
            self.needfullreplot = False

            self.verticalScaleDivision.setLength(self.canvasWidget.height())
            self.verticalScaleTransform.setLength(self.canvasWidget.height())
            startBorder, endBorder = self.verticalScale.spacingBorders()
            self.verticalScaleTransform.setBorders(startBorder, endBorder)

            self.verticalScale.update()

            self.horizontalScaleDivision.setLength(self.canvasWidget.width())
            self.horizontalScaleTransform.setLength(self.canvasWidget.width())
            startBorder, endBorder = self.horizontalScale.spacingBorders()
            self.horizontalScaleTransform.setBorders(startBorder, endBorder)

            self.horizontalScale.update()

            self.canvasWidget.setGrid(np.array(self.horizontalScaleDivision.majorTicks()),
                                      np.array(self.horizontalScaleDivision.minorTicks()),
                                      np.array(self.verticalScaleDivision.majorTicks()),
                                      np.array(self.verticalScaleDivision.minorTicks()))

    def pause(self):
        self.paused = True
        self.canvasWidget.pause()

    def restart(self):
        self.paused = False
        self.canvasWidget.restart()

    # redraw when the widget is resized to update coordinates transformations
    # this is done instead of resizeEvent because the canvas can be resized independently of the whole plot (because the legend can disappear)
    def canvasResized(self, canvasWidth, canvasHeight):
        self.needfullreplot = True
        self.draw()

    def setdataTwoChannels(self, x, y, y2):
        if self.canvas_width != self.canvasWidget.width():
            Logger().push("timeplot : changed canvas width")
            self.canvas_width = self.canvasWidget.width()
            self.update_xscale()

        if not self.dual_channel:
            self.dual_channel = True
            self.canvasWidget.attach(self.curve2)
            # enable the legend to discrimate between the two channels
            self.legendWidget.show()
            # the canvas reisze event will trigger a full replot

        if self.xmax != x[-1]:
            Logger().push("timeplot : changing x max")
            self.xmax = x[-1]
            self.settimerange(self.xmin, self.xmax)
            self.update_xscale()
            self.needfullreplot = True
        if self.xmin != x[0]:
            Logger().push("timeplot : changing x min")
            self.xmin = x[0]
            self.settimerange(self.xmin, self.xmax)
            self.update_xscale()
            self.needfullreplot = True

        if not self.paused:
            # y_interp = np.interp(self.xscaled, x, y)
            # y_interp2 = np.interp(self.xscaled, x, y2)
            # ClassPlot.setdata(self, self.xscaled, y_interp)
            # self.curve2.setData(self.xscaled, y_interp2)
            self.curve.setData(x, y)
            self.curve2.setData(x, y2)

        self.draw()

    def update_xscale(self):
        self.xscaled = np.linspace(self.xmin, self.xmax, self.canvas_width)

    def settimerange(self, time_min, time_max):
        self.horizontalScaleTransform.setRange(time_min, time_max)
        self.horizontalScaleDivision.setRange(time_min, time_max)

        # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar)
        self.horizontalScale.scaleBar.updateGeometry()

        self.needfullreplot = True
        self.draw()

    def setverticalrange(self, v_min, v_max):
        self.verticalScaleTransform.setRange(v_min, v_max)
        self.verticalScaleDivision.setRange(v_min, v_max)

        # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar)
        self.verticalScale.scaleBar.updateGeometry()

        self.needfullreplot = True
        self.draw()

    def setverticaltitle(self, title):
        self.verticalScale.setTitle(title)

    def sethorizontaltitle(self, title):
        self.horizontalScale.setTitle(title)

    def setTrackerFormatter(self, formatter):
        self.canvasWidget.setTrackerFormatter(formatter)
Пример #3
0
class SpectrumPlotWidget(QtWidgets.QWidget):
    def __init__(self, parent, logger=None):
        super(SpectrumPlotWidget, self).__init__()

        self.x1 = array([0.1, 0.5, 1.])
        self.x2 = array([0.5, 1., 2.])

        self.needtransform = False

        self.paused = False

        self.verticalScaleDivision = ScaleDivision(0, 1, 100)
        self.verticalScaleTransform = CoordinateTransform(0, 1, 100, 0, 0)

        self.verticalScale = VerticalScaleWidget(self, self.verticalScaleDivision, self.verticalScaleTransform)
        self.verticalScale.setTitle("PSD (dB)")

        self.horizontalScaleDivision = ScaleDivision(0, 22000, 100)
        self.horizontalScaleTransform = CoordinateTransform(0, 22000, 100, 0, 0)

        self.horizontalScale = HorizontalScaleWidget(self, self.horizontalScaleDivision, self.horizontalScaleTransform)
        self.horizontalScale.setTitle("Frequency (Hz)")

        self.canvasWidget = GlCanvasWidget(self, self.verticalScaleTransform, self.horizontalScaleTransform)
        self.canvasWidget.setTrackerFormatter(lambda x, y: "%d Hz, %.1f dB" %(x, y))

        self.quadsItem = QuadsItem()
        self.canvasWidget.attach(self.quadsItem)

        plotLayout = QtWidgets.QGridLayout()
        plotLayout.setSpacing(0)
        plotLayout.setContentsMargins(0, 0, 0, 0)
        plotLayout.addWidget(self.verticalScale, 0, 0)
        plotLayout.addWidget(self.canvasWidget, 0, 1)
        plotLayout.addWidget(self.horizontalScale, 1, 1)

        self.setLayout(plotLayout)

    def setlinfreqscale(self):
        self.horizontalScaleTransform.setLinear()
        self.horizontalScaleDivision.setLinear()

        self.needtransform = True
        self.draw()

    def setlogfreqscale(self):
        self.horizontalScaleTransform.setLogarithmic()
        self.horizontalScaleDivision.setLogarithmic()

        self.needtransform = True
        self.draw()

    def setfreqrange(self, minfreq, maxfreq):
        self.xmin = minfreq
        self.xmax = maxfreq

        self.horizontalScaleTransform.setRange(minfreq, maxfreq)
        self.horizontalScaleDivision.setRange(minfreq, maxfreq)

        # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar)
        self.horizontalScale.scaleBar.updateGeometry()

        self.needtransform = True
        self.draw()

    def setspecrange(self, spec_min, spec_max):
        self.verticalScaleTransform.setRange(spec_min, spec_max)
        self.verticalScaleDivision.setRange(spec_min, spec_max)

        # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar)
        self.verticalScale.scaleBar.updateGeometry()

        self.needtransform = True
        self.draw()

    def setweighting(self, weighting):
        if weighting is 0:
            title = "PSD (dB)"
        elif weighting is 1:
            title = "PSD (dB A)"
        elif weighting is 2:
            title = "PSD (dB B)"
        else:
            title = "PSD (dB C)"

        self.verticalScale.setTitle(title)
        self.needtransform = True
        self.draw()

    def setShowFreqLabel(self, showFreqLabel):
        self.canvasWidget.setShowFreqLabel(showFreqLabel)

    def set_peaks_enabled(self, enabled):
        self.quadsItem.set_peaks_enabled(enabled)

    def set_baseline_displayUnits(self, baseline):
        self.quadsItem.set_baseline_displayUnits(baseline)

    def set_baseline_dataUnits(self, baseline):
        self.quadsItem.set_baseline_dataUnits(baseline)

    def setdata(self, x, y, fmax):
        x1 = zeros(x.shape)
        x2 = zeros(x.shape)
        x1[0] = 1e-10
        x1[1:] = (x[1:] + x[:-1])/2.
        x2[:-1] = x1[1:]
        x2[-1] = float(SAMPLING_RATE/2)

        if len(x1) != len(self.x1):
            self.needtransform = True
            self.x1 = x1
            self.x2 = x2

        if not self.paused:
            self.canvasWidget.setfmax(fmax)
            self.quadsItem.setData(x1, x2, y)

        # TODO :
        # - Fix peaks loss when resizing
        # - optimize if further needed

    def draw(self):
        if self.needtransform:
            self.verticalScaleDivision.setLength(self.canvasWidget.height())
            self.verticalScaleTransform.setLength(self.canvasWidget.height())
            startBorder, endBorder = self.verticalScale.spacingBorders()
            self.verticalScaleTransform.setBorders(startBorder, endBorder)

            self.verticalScale.update()

            self.horizontalScaleDivision.setLength(self.canvasWidget.width())
            self.horizontalScaleTransform.setLength(self.canvasWidget.width())
            startBorder, endBorder = self.horizontalScale.spacingBorders()
            self.horizontalScaleTransform.setBorders(startBorder, endBorder)

            self.horizontalScale.update()

            self.canvasWidget.setGrid(array(self.horizontalScaleDivision.majorTicks()),
                                  array(self.horizontalScaleDivision.minorTicks()),
                                  array(self.verticalScaleDivision.majorTicks()),
                                  array(self.verticalScaleDivision.minorTicks())
                                  )

            self.quadsItem.transformUpdate()

            self.needtransform = False

    def pause(self):
        self.paused = True
        self.canvasWidget.pause()

    def restart(self):
        self.paused = False
        self.canvasWidget.restart()

    # redraw when the widget is resized to update coordinates transformations
    def resizeEvent(self, event):
        self.needtransform = True
        self.draw()
Пример #4
0
class SpectrumPlotWidget(QtWidgets.QWidget):
    def __init__(self, parent, logger=None):
        super(SpectrumPlotWidget, self).__init__(parent)

        self.x1 = array([0.1, 0.5, 1.])
        self.x2 = array([0.5, 1., 2.])

        self.needtransform = False

        self.paused = False

        self.peaks_enabled = True
        self.peak = zeros((3, ))
        self.peak_int = zeros((3, ))
        self.peak_decay = ones((3, )) * PEAK_DECAY_RATE

        self.verticalScaleDivision = ScaleDivision(0, 1, 100)
        self.verticalScaleTransform = CoordinateTransform(0, 1, 100, 0, 0)

        self.verticalScale = VerticalScaleWidget(self,
                                                 self.verticalScaleDivision,
                                                 self.verticalScaleTransform)
        self.verticalScale.setTitle("PSD (dB)")

        self.horizontalScaleDivision = ScaleDivision(0, 22000, 100)
        self.horizontalScaleTransform = CoordinateTransform(
            0, 22000, 100, 0, 0)

        self.horizontalScale = HorizontalScaleWidget(
            self, self.horizontalScaleDivision, self.horizontalScaleTransform)
        self.horizontalScale.setTitle("Frequency (Hz)")

        self.canvasWidget = GlCanvasWidget(self, self.verticalScaleTransform,
                                           self.horizontalScaleTransform)
        self.canvasWidget.setTrackerFormatter(lambda x, y: "%d Hz, %.1f dB" %
                                              (x, y))
        self.canvasWidget.resized.connect(self.canvasResized)

        r_peak = lambda p: 1. + 0. * p
        g_peak = lambda p: 1. - p
        b_peak = lambda p: 1. - p

        self.peakQuadsItem = QuadsItem(r_peak, g_peak, b_peak)
        self.canvasWidget.attach(self.peakQuadsItem)

        r_signal = lambda p: 0. * p
        g_signal = lambda p: 0.3 + 0.5 * p
        b_signal = lambda p: 0. * p

        self.quadsItem = QuadsItem(r_signal, g_signal, b_signal)
        self.canvasWidget.attach(self.quadsItem)

        plotLayout = QtWidgets.QGridLayout()
        plotLayout.setSpacing(0)
        plotLayout.setContentsMargins(0, 0, 0, 0)
        plotLayout.addWidget(self.verticalScale, 0, 0)
        plotLayout.addWidget(self.canvasWidget, 0, 1)
        plotLayout.addWidget(self.horizontalScale, 1, 1)

        self.setLayout(plotLayout)

    def setlinfreqscale(self):
        self.horizontalScaleTransform.setLinear()
        self.horizontalScaleDivision.setLinear()

        self.needtransform = True
        self.draw()

    def setlogfreqscale(self):
        self.horizontalScaleTransform.setLogarithmic()
        self.horizontalScaleDivision.setLogarithmic()

        self.needtransform = True
        self.draw()

    def setfreqrange(self, minfreq, maxfreq):
        self.xmin = minfreq
        self.xmax = maxfreq

        self.horizontalScaleTransform.setRange(minfreq, maxfreq)
        self.horizontalScaleDivision.setRange(minfreq, maxfreq)

        # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar)
        self.horizontalScale.scaleBar.updateGeometry()

        self.needtransform = True
        self.draw()

    def setspecrange(self, spec_min, spec_max):
        if spec_min > spec_max:
            spec_min, spec_max = spec_max, spec_min

        self.verticalScaleTransform.setRange(spec_min, spec_max)
        self.verticalScaleDivision.setRange(spec_min, spec_max)

        # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar)
        self.verticalScale.scaleBar.updateGeometry()

        self.needtransform = True
        self.draw()

    def setweighting(self, weighting):
        if weighting is 0:
            title = "PSD (dB)"
        elif weighting is 1:
            title = "PSD (dB A)"
        elif weighting is 2:
            title = "PSD (dB B)"
        else:
            title = "PSD (dB C)"

        self.verticalScale.setTitle(title)
        self.needtransform = True
        self.draw()

    def setShowFreqLabel(self, showFreqLabel):
        self.canvasWidget.setShowFreqLabel(showFreqLabel)

    def set_peaks_enabled(self, enabled):
        self.peaks_enabled = enabled

        self.canvasWidget.detachAll()
        if enabled:
            self.canvasWidget.attach(self.peakQuadsItem)
        self.canvasWidget.attach(self.quadsItem)

    def set_baseline_displayUnits(self, baseline):
        self.quadsItem.set_baseline_displayUnits(baseline)

    def set_baseline_dataUnits(self, baseline):
        self.quadsItem.set_baseline_dataUnits(baseline)

    def setdata(self, x, y, fmax):
        x1 = zeros(x.shape)
        x2 = zeros(x.shape)
        x1[0] = 1e-10
        x1[1:] = (x[1:] + x[:-1]) / 2.
        x2[:-1] = x1[1:]
        x2[-1] = float(SAMPLING_RATE / 2)

        if len(x1) != len(self.x1):
            self.needtransform = True
            self.x1 = x1
            self.x2 = x2

        if not self.paused:
            self.canvasWidget.setfmax(fmax)

            M = max(y)
            m = self.verticalScaleTransform.coord_min
            y_int = (y - m) / (np.abs(M - m) + 1e-3)

            self.quadsItem.setData(x1, x2, y, y_int)

            if self.peaks_enabled:
                self.compute_peaks(y)
                self.peakQuadsItem.setData(x1, x2, self.peak, self.peak_int)

    def draw(self):
        if self.needtransform:
            self.verticalScaleDivision.setLength(self.canvasWidget.height())
            self.verticalScaleTransform.setLength(self.canvasWidget.height())
            startBorder, endBorder = self.verticalScale.spacingBorders()
            self.verticalScaleTransform.setBorders(startBorder, endBorder)

            self.verticalScale.update()

            self.horizontalScaleDivision.setLength(self.canvasWidget.width())
            self.horizontalScaleTransform.setLength(self.canvasWidget.width())
            startBorder, endBorder = self.horizontalScale.spacingBorders()
            self.horizontalScaleTransform.setBorders(startBorder, endBorder)

            self.horizontalScale.update()

            self.canvasWidget.setGrid(
                array(self.horizontalScaleDivision.majorTicks()),
                array(self.horizontalScaleDivision.minorTicks()),
                array(self.verticalScaleDivision.majorTicks()),
                array(self.verticalScaleDivision.minorTicks()))

            self.quadsItem.transformUpdate()
            self.peakQuadsItem.transformUpdate()

            self.needtransform = False

    def pause(self):
        self.paused = True
        self.canvasWidget.pause()

    def restart(self):
        self.paused = False
        self.canvasWidget.restart()

    # redraw when the widget is resized to update coordinates transformations
    # QOpenGlWidget does not like that we override resizeEvent
    def canvasResized(self, canvasWidth, canvasHeight):
        self.needtransform = True
        self.draw()

    def canvasUpdate(self):
        self.canvasWidget.update()

    def compute_peaks(self, y):
        if len(self.peak) != len(y):
            y_ones = ones(y.shape)
            self.peak = y_ones * (-500.)
            self.peak_int = zeros(y.shape)
            self.peak_decay = y_ones * 20. * log10(PEAK_DECAY_RATE) * 5000

        mask1 = (self.peak < y)
        mask2 = (-mask1)
        mask2_a = mask2 * (self.peak_int < 0.2)
        mask2_b = mask2 * (self.peak_int >= 0.2)

        self.peak[mask1] = y[mask1]
        self.peak[mask2_a] = self.peak[mask2_a] + self.peak_decay[mask2_a]

        self.peak_decay[mask1] = 20. * log10(PEAK_DECAY_RATE) * 5000
        self.peak_decay[mask2_a] += 20. * log10(PEAK_DECAY_RATE) * 5000

        self.peak_int[mask1] = 1.
        self.peak_int[mask2_b] *= 0.975
Пример #5
0
class TimePlot(QtWidgets.QWidget):

    def __init__(self, parent, logger):
        super(TimePlot, self).__init__()

        # store the _logger instance
        self.logger = logger

        self.verticalScaleDivision = ScaleDivision(-1, 1, 100)
        self.verticalScaleTransform = CoordinateTransform(-1, 1, 100, 0, 0)

        self.verticalScale = VerticalScaleWidget(self, self.verticalScaleDivision, self.verticalScaleTransform)
        self.verticalScale.setTitle("Signal")

        self.horizontalScaleDivision = ScaleDivision(-1, 1, 100)
        self.horizontalScaleTransform = CoordinateTransform(-1, 1, 100, 0, 0)

        self.horizontalScale = HorizontalScaleWidget(self, self.horizontalScaleDivision, self.horizontalScaleTransform)
        self.horizontalScale.setTitle("Time (ms)")

        self.canvasWidget = GlCanvasWidget(self, self.verticalScaleTransform, self.horizontalScaleTransform)
        self.canvasWidget.setTrackerFormatter(lambda x, y: "%.3g ms, %.3g" % (x, y))

        self.legendWidget = LegendWidget(self, self.canvasWidget)

        plotLayout = QtWidgets.QGridLayout()
        plotLayout.setSpacing(0)
        plotLayout.setContentsMargins(0, 0, 0, 0)
        plotLayout.addWidget(self.verticalScale, 0, 0)
        plotLayout.addWidget(self.canvasWidget, 0, 1)
        plotLayout.addWidget(self.horizontalScale, 1, 1)
        plotLayout.addWidget(self.legendWidget, 0, 2)

        self.setLayout(plotLayout)
        self.legendWidget.hide()

        self.needfullreplot = False

        self.curve = CurveItem()
        self.curve.setColor(QtGui.QColor(Qt.Qt.red))
        # gives a title to the curve for the legend
        self.curve.setTitle("Ch1")
        self.canvasWidget.attach(self.curve)

        self.curve2 = CurveItem()
        self.curve2.setColor(QtGui.QColor(Qt.Qt.blue))
        # gives a title to the curve for the legend
        self.curve2.setTitle("Ch2")
        # self.curve2 will be attached when needed

        # need to replot here for the size Hints to be computed correctly (depending on axis scales...)
        self.update()

        self.xmin = 0.
        self.xmax = 1.

        self.canvas_width = 0

        self.dual_channel = False

        self.paused = False

        self.canvasWidget.resized.connect(self.canvasResized)

    def setdata(self, x, y):
        if self.canvas_width != self.canvasWidget.width():
            self.logger.push("timeplot : changed canvas width")
            self.canvas_width = self.canvasWidget.width()
            self.update_xscale()

        if self.dual_channel:
            self.dual_channel = False
            self.canvasWidget.detach(self.curve2)
            # disable the legend, useless when one channel is active
            self.legendWidget.hide()
            # the canvas reisze event will trigger a full replot

        if self.xmax != x[-1]:
            self.logger.push("timeplot : changing x max")
            self.xmax = x[-1]
            self.settimerange(self.xmin, self.xmax)
            self.update_xscale()
            self.needfullreplot = True
        if self.xmin != x[0]:
            self.logger.push("timeplot : changing x min")
            self.xmin = x[0]
            self.settimerange(self.xmin, self.xmax)
            self.update_xscale()
            self.needfullreplot = True

        if not self.paused:
            y_interp = interp(self.xscaled, x, y)
            self.curve.setData(self.xscaled, y_interp)

        self.draw()

    def draw(self):
        if self.needfullreplot:
            self.needfullreplot = False

            self.verticalScaleDivision.setLength(self.canvasWidget.height())
            self.verticalScaleTransform.setLength(self.canvasWidget.height())
            startBorder, endBorder = self.verticalScale.spacingBorders()
            self.verticalScaleTransform.setBorders(startBorder, endBorder)

            self.verticalScale.update()

            self.horizontalScaleDivision.setLength(self.canvasWidget.width())
            self.horizontalScaleTransform.setLength(self.canvasWidget.width())
            startBorder, endBorder = self.horizontalScale.spacingBorders()
            self.horizontalScaleTransform.setBorders(startBorder, endBorder)

            self.horizontalScale.update()

            self.canvasWidget.setGrid(array(self.horizontalScaleDivision.majorTicks()),
                                      array(self.horizontalScaleDivision.minorTicks()),
                                      array(self.verticalScaleDivision.majorTicks()),
                                      array(self.verticalScaleDivision.minorTicks()))

    def pause(self):
        self.paused = True
        self.canvasWidget.pause()

    def restart(self):
        self.paused = False
        self.canvasWidget.restart()

    # redraw when the time_plot is resized to update coordinates transformations
    # this is done instead of resizeEvent because the canvas can be resized independently of the whole plot (because the legend can disappear)
    def canvasResized(self, canvasWidth, canvasHeight):
        self.needfullreplot = True
        self.draw()

    def setdataTwoChannels(self, x, y, y2):
        if self.canvas_width != self.canvasWidget.width():
            self.logger.push("timeplot : changed canvas width")
            self.canvas_width = self.canvasWidget.width()
            self.update_xscale()

        if not self.dual_channel:
            self.dual_channel = True
            self.canvasWidget.attach(self.curve2)
            # enable the legend to discrimate between the two channels
            self.legendWidget.show()
            # the canvas reisze event will trigger a full replot

        if self.xmax != x[-1]:
            self.logger.push("timeplot : changing x max")
            self.xmax = x[-1]
            self.settimerange(self.xmin, self.xmax)
            self.update_xscale()
            self.needfullreplot = True
        if self.xmin != x[0]:
            self.logger.push("timeplot : changing x min")
            self.xmin = x[0]
            self.settimerange(self.xmin, self.xmax)
            self.update_xscale()
            self.needfullreplot = True

        if not self.paused:
            # y_interp = interp(self.xscaled, x, y)
            # y_interp2 = interp(self.xscaled, x, y2)
            # ClassPlot.setdata(self, self.xscaled, y_interp)
            # self.curve2.setData(self.xscaled, y_interp2)
            self.curve.setData(x, y)
            self.curve2.setData(x, y2)

        self.draw()

    def update_xscale(self):
        self.xscaled = linspace(self.xmin, self.xmax, self.canvas_width)

    def settimerange(self, time_min, time_max):
        self.horizontalScaleTransform.setRange(time_min, time_max)
        self.horizontalScaleDivision.setRange(time_min, time_max)

        # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar)
        self.horizontalScale.scaleBar.updateGeometry()

        self.needfullreplot = True
        self.draw()

    def setverticalrange(self, v_min, v_max):
        self.verticalScaleTransform.setRange(v_min, v_max)
        self.verticalScaleDivision.setRange(v_min, v_max)

        # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar)
        self.verticalScale.scaleBar.updateGeometry()

        self.needfullreplot = True
        self.draw()

    def setverticaltitle(self, title):
        self.verticalScale.setTitle(title)

    def sethorizontaltitle(self, title):
        self.horizontalScale.setTitle(title)

    def setTrackerFormatter(self, formatter):
        self.canvasWidget.setTrackerFormatter(formatter)
Пример #6
0
class SpectrumPlotWidget(QtWidgets.QWidget):
    def __init__(self, parent, sharedGLWidget, logger=None):
        super(SpectrumPlotWidget, self).__init__()

        self.peaks_enabled = True
        self.peak = zeros((3, ))
        self.peak_int = zeros((3, ))
        self.peak_decay = ones((3, )) * PEAK_DECAY_RATE

        self.x1 = array([0.1, 0.5, 1.])
        self.x2 = array([0.5, 1., 2.])
        self.y = array([0., 0., 0.])

        self.fmax = 1e3

        self.transformed_x1 = self.x1
        self.transformed_x2 = self.x2

        self.baseline_transformed = False
        self.baseline = 0.

        self.needtransform = False

        self.verticalScaleDivision = ScaleDivision(0, 1, 100)
        self.verticalScaleTransform = CoordinateTransform(0, 1, 100, 0, 0)

        self.verticalScale = VerticalScaleWidget(self,
                                                 self.verticalScaleDivision,
                                                 self.verticalScaleTransform)
        self.verticalScale.setTitle("PSD (dB)")

        self.horizontalScaleDivision = ScaleDivision(0, 22000, 100)
        self.horizontalScaleTransform = CoordinateTransform(
            0, 22000, 100, 0, 0)

        self.horizontalScale = HorizontalScaleWidget(
            self, self.horizontalScaleDivision, self.horizontalScaleTransform)
        self.horizontalScale.setTitle("Frequency (Hz)")

        self.canvasWidget = GlCanvasWidget(self, sharedGLWidget,
                                           self.verticalScaleTransform,
                                           self.horizontalScaleTransform)
        self.canvasWidget.setTrackerFormatter(lambda x, y: "%d Hz, %.1f dB" %
                                              (x, y))

        self.quadsItem = QuadsItem()
        self.canvasWidget.attach(self.quadsItem)

        plotLayout = QtWidgets.QGridLayout()
        plotLayout.setSpacing(0)
        plotLayout.setContentsMargins(0, 0, 0, 0)
        plotLayout.addWidget(self.verticalScale, 0, 0)
        plotLayout.addWidget(self.canvasWidget, 0, 1)
        plotLayout.addWidget(self.horizontalScale, 1, 1)

        self.setLayout(plotLayout)

    def setlinfreqscale(self):
        self.logx = False

        self.horizontalScaleTransform.setLinear()
        self.horizontalScaleDivision.setLinear()

        self.needtransform = True
        self.draw()

    def setlogfreqscale(self):
        self.logx = True

        self.horizontalScaleTransform.setLogarithmic()
        self.horizontalScaleDivision.setLogarithmic()

        self.needtransform = True
        self.draw()

    def setfreqrange(self, minfreq, maxfreq):
        self.xmin = minfreq
        self.xmax = maxfreq

        self.horizontalScaleTransform.setRange(minfreq, maxfreq)
        self.horizontalScaleDivision.setRange(minfreq, maxfreq)

        # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar)
        self.horizontalScale.scaleBar.updateGeometry()

        self.needtransform = True
        self.draw()

    def setspecrange(self, spec_min, spec_max):
        self.verticalScaleTransform.setRange(spec_min, spec_max)
        self.verticalScaleDivision.setRange(spec_min, spec_max)

        # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar)
        self.verticalScale.scaleBar.updateGeometry()

        self.needtransform = True
        self.draw()

    def setweighting(self, weighting):
        if weighting is 0:
            title = "PSD (dB)"
        elif weighting is 1:
            title = "PSD (dB A)"
        elif weighting is 2:
            title = "PSD (dB B)"
        else:
            title = "PSD (dB C)"

        self.verticalScale.setTitle(title)
        self.needtransform = True
        self.draw()

    def setShowFreqLabel(self, showFreqLabel):
        self.canvasWidget.setShowFreqLabel(showFreqLabel)

    def set_peaks_enabled(self, enabled):
        self.peaks_enabled = enabled

    def set_baseline_displayUnits(self, baseline):
        self.baseline_transformed = False
        self.baseline = baseline

    def set_baseline_dataUnits(self, baseline):
        self.baseline_transformed = True
        self.baseline = baseline

    def setdata(self, x, y, fmax):
        x1 = zeros(x.shape)
        x2 = zeros(x.shape)
        x1[0] = 1e-10
        x1[1:] = (x[1:] + x[:-1]) / 2.
        x2[:-1] = x1[1:]
        x2[-1] = float(SAMPLING_RATE / 2)

        if len(x1) != len(self.x1):
            self.needtransform = True
            # save data for resizing
            self.x1 = x1
            self.x2 = x2

        # save data for resizing
        self.y = y
        self.fmax = fmax

        self.draw()

        # TODO :
        # - Fix peaks loss when resizing
        # - optimize if further needed

    def pre_tree_rebin(self, x1, x2):
        if len(x2) == 0:
            # enf of recursion !
            return x1, x2, 0

        n0 = max(where(x2 - x1 >= 0.5)[0])

        # leave untouched the frequency bins that span more than half a pixel
        # and first make sure that what will be left can be decimated by two
        rest = len(x2) - n0 - ((len(x2) - n0) // 2) * 2

        n0 += rest

        x1_0 = x1[:n0]
        x2_0 = x2[:n0]

        # decimate the rest
        x1_2 = x1[n0::2]
        x2_2 = x2[n0 + 1::2]

        # recursive !!
        x1_2, x2_2, n2 = self.pre_tree_rebin(x1_2, x2_2)

        if n2 == 0.:
            n = [n0]
        else:
            n = [n0] + [i * 2 + n0 for i in n2]

        x1 = hstack((x1_0, x1_2))
        x2 = hstack((x2_0, x2_2))

        return x1, x2, n

    def tree_rebin(self, y, ns, N):
        y2 = zeros(N)

        n = 0
        for i in range(len(ns) - 1):
            y3 = y[ns[i]:ns[i + 1]]
            d = 2**i
            l = len(y3) / d
            y3.shape = (l, d)

            # Note: the FFT spectrum is mostly used to identify frequency content
            # ans _peaks_ are particularly interesting (e.g. feedback frequencies)
            # so we display the _max_ instead of the mean of each bin
            #y3 = mean(y3, axis=1)
            #y3 = (y3[::2] + y3[1::2])*0.5

            y3 = np.max(y3, axis=1)

            y2[n:n + len(y3)] = y3
            n += l

        return y2

    def draw(self):
        if self.needtransform:
            self.verticalScaleDivision.setLength(self.canvasWidget.height())
            self.verticalScaleTransform.setLength(self.canvasWidget.height())
            startBorder, endBorder = self.verticalScale.spacingBorders()
            self.verticalScaleTransform.setBorders(startBorder, endBorder)

            self.verticalScale.update()

            self.horizontalScaleDivision.setLength(self.canvasWidget.width())
            self.horizontalScaleTransform.setLength(self.canvasWidget.width())
            startBorder, endBorder = self.horizontalScale.spacingBorders()
            self.horizontalScaleTransform.setBorders(startBorder, endBorder)

            self.horizontalScale.update()

            # transform the coordinates only when needed
            x1 = self.horizontalScaleTransform.toScreen(self.x1)
            x2 = self.horizontalScaleTransform.toScreen(self.x2)

            if self.logx:
                self.transformed_x1, self.transformed_x2, n = self.pre_tree_rebin(
                    x1, x2)
                self.n = [0] + n
                self.N = 0
                for i in range(len(self.n) - 1):
                    self.N += (self.n[i + 1] - self.n[i]) / 2**i

            else:
                self.transformed_x1 = x1
                self.transformed_x2 = x2

            self.canvasWidget.setGrid(
                array(self.horizontalScaleDivision.majorTicks()),
                array(self.horizontalScaleDivision.minorTicks()),
                array(self.verticalScaleDivision.majorTicks()),
                array(self.verticalScaleDivision.minorTicks()))

            self.needtransform = False

        # for easier reading
        x1 = self.transformed_x1
        x2 = self.transformed_x2

        if self.logx:
            y = self.tree_rebin(self.y, self.n, self.N)
        else:
            n = floor(1. / (x2[2] - x1[1]))
            if n > 0:
                new_len = len(self.y) // n
                rest = len(self.y) - new_len * n

                new_y = self.y[:-rest]
                new_y.shape = (new_len, n)
                y = mean(new_y, axis=1)

                x1 = x1[:-rest:n]
                x2 = x2[n::n]
            else:
                y = self.y

        if self.peaks_enabled:
            self.compute_peaks(y)

        transformed_y = self.verticalScaleTransform.toScreen(y)

        Ones = ones(x1.shape)
        Ones_shaded = Ones  #.copy()
        # FIXME : the following would give a satisfying result if the
        # bins were one pixel wide at minimum => Need to to a rounding
        # to pixels
        #w = x2 - x1
        #i = where(w<1.)[0]
        #if len(i)>0:
        #    Ones_shaded[:i[0]:2] = 1.2

        if self.peaks_enabled:
            transformed_peak = self.verticalScaleTransform.toScreen(self.peak)

            n = x1.size

            # FIXME should be done conditionally to need_transform
            x1_with_peaks = zeros((2 * n))
            x2_with_peaks = zeros((2 * n))
            y_with_peaks = zeros((2 * n))
            r_with_peaks = zeros((2 * n))
            g_with_peaks = zeros((2 * n))
            b_with_peaks = zeros((2 * n))

            x1_with_peaks[:n] = x1
            x1_with_peaks[n:] = x1

            x2_with_peaks[:n] = x2
            x2_with_peaks[n:] = x2

            y_with_peaks[:n] = transformed_peak
            y_with_peaks[n:] = transformed_y

            r_with_peaks[:n] = 1. * Ones
            r_with_peaks[n:] = 0. * Ones

            g_with_peaks[:n] = 1. - self.peak_int
            g_with_peaks[n:] = 0.5 * Ones_shaded

            b_with_peaks[:n] = 1. - self.peak_int
            b_with_peaks[n:] = 0. * Ones
        else:
            x1_with_peaks = x1
            x2_with_peaks = x2
            y_with_peaks = transformed_y
            r_with_peaks = 0. * Ones
            g_with_peaks = 0.5 * Ones_shaded
            b_with_peaks = 0. * Ones

        if self.baseline_transformed:
            # used for dual channel response measurement
            baseline = self.verticalScaleTransform.toScreen(self.baseline)
        else:
            # used for single channel analysis
            baseline = self.baseline

        xmax = self.horizontalScaleTransform.toScreen(self.fmax)
        self.canvasWidget.setfmax(xmax, self.fmax)

        self.quadsItem.setData(x1_with_peaks, y_with_peaks,
                               x2_with_peaks - x1_with_peaks, baseline,
                               r_with_peaks, g_with_peaks, b_with_peaks)
        self.canvasWidget.update()

    # redraw when the widget is resized to update coordinates transformations
    def resizeEvent(self, event):
        self.needtransform = True
        self.draw()

    def compute_peaks(self, y):
        if len(self.peak) != len(y):
            y_ones = ones(y.shape)
            self.peak = y_ones * (-500.)
            self.peak_int = zeros(y.shape)
            self.peak_decay = y_ones * 20. * log10(PEAK_DECAY_RATE) * 5000

        mask1 = (self.peak < y)
        mask2 = (-mask1)
        mask2_a = mask2 * (self.peak_int < 0.2)
        mask2_b = mask2 * (self.peak_int >= 0.2)

        self.peak[mask1] = y[mask1]
        self.peak[mask2_a] = self.peak[mask2_a] + self.peak_decay[mask2_a]

        self.peak_decay[mask1] = 20. * log10(PEAK_DECAY_RATE) * 5000
        self.peak_decay[mask2_a] += 20. * log10(PEAK_DECAY_RATE) * 5000

        self.peak_int[mask1] = 1.
        self.peak_int[mask2_b] *= 0.975
Пример #7
0
class HistPlot(QtWidgets.QWidget):

    def __init__(self, parent, logger):
        super(HistPlot, self).__init__()

        # store the _logger instance
        self.logger = logger

        self.verticalScaleDivision = ScaleDivision(-140, 0, 100)
        self.verticalScaleTransform = CoordinateTransform(-140, 0, 100, 0, 0)

        self.verticalScale = VerticalScaleWidget(self, self.verticalScaleDivision, self.verticalScaleTransform)
        self.verticalScale.setTitle("PSD (dB A)")

        self.horizontalScaleDivision = ScaleDivision(44, 22000, 100)
        self.horizontalScaleTransform = CoordinateTransform(44, 22000, 100, 0, 0)

        self.horizontalScale = HorizontalScaleWidget(self, self.horizontalScaleDivision, self.horizontalScaleTransform)
        self.horizontalScale.setTitle("Frequency (Hz)")

        self.canvasWidget = CanvasWidget(self, self.verticalScaleTransform, self.horizontalScaleTransform)
        self.canvasWidget.setTrackerFormatter(lambda x, y: "%d Hz, %.1f dB" % (x, y))

        plot_layout = QtWidgets.QGridLayout()
        plot_layout.setSpacing(0)
        plot_layout.setContentsMargins(0, 0, 0, 0)
        plot_layout.addWidget(self.verticalScale, 0, 0)
        plot_layout.addWidget(self.canvasWidget, 0, 1)
        plot_layout.addWidget(self.horizontalScale, 1, 1)

        self.setLayout(plot_layout)

        self.needfullreplot = False

        self.horizontalScaleTransform.setLogarithmic()
        self.horizontalScaleDivision.setLogarithmic()

        # insert an additional plot item for the peak bar
        self.bar_peak = HistogramPeakBarItem()
        self.canvasWidget.attach(self.bar_peak)
        self.peak = zeros((1,))
        self.peak_int = 0
        self.peak_decay = PEAK_DECAY_RATE

        self.histogram = HistogramItem()
        self.histogram.set_color(Qt.Qt.darkGreen)
        self.canvasWidget.attach(self.histogram)

        # need to replot here for the size Hints to be computed correctly (depending on axis scales...)
        self.update()

    def setdata(self, fl, fh, fc, y):
        self.histogram.setData(fl, fh, fc, y)

        self.compute_peaks(y)
        self.bar_peak.setData(fl, fh, self.peak, self.peak_int, y)

        # only draw on demand
        # self.draw()

    def draw(self):
        if self.needfullreplot:
            self.needfullreplot = False

            self.verticalScaleDivision.setLength(self.canvasWidget.height())
            self.verticalScaleTransform.setLength(self.canvasWidget.height())

            start_border, end_border = self.verticalScale.spacingBorders()
            self.verticalScaleTransform.setBorders(start_border, end_border)

            self.verticalScale.update()

            self.horizontalScaleDivision.setLength(self.canvasWidget.width())
            self.horizontalScaleTransform.setLength(self.canvasWidget.width())

            start_border, end_border = self.horizontalScale.spacingBorders()
            self.horizontalScaleTransform.setBorders(start_border, end_border)

            self.horizontalScale.update()

            x_major_tick = self.horizontalScaleDivision.majorTicks()
            x_minor_tick = self.horizontalScaleDivision.minorTicks()
            y_major_tick = self.verticalScaleDivision.majorTicks()
            y_minor_tick = self.verticalScaleDivision.minorTicks()
            self.canvasWidget.setGrid(array(x_major_tick),
                                      array(x_minor_tick),
                                      array(y_major_tick),
                                      array(y_minor_tick))

        self.canvasWidget.update()

    # redraw when the time_plot is resized to update coordinates transformations
    def resizeEvent(self, event):
        self.needfullreplot = True
        self.draw()

    def compute_peaks(self, y):
        if len(self.peak) != len(y):
            y_ones = ones(y.shape)
            self.peak = y_ones * (-500.)
            self.peak_int = zeros(y.shape)
            self.peak_decay = y_ones * 20. * log10(PEAK_DECAY_RATE) * 5000

        mask1 = (self.peak < y)
        mask2 = (-mask1)
        mask2_a = mask2 * (self.peak_int < 0.2)
        mask2_b = mask2 * (self.peak_int >= 0.2)

        self.peak[mask1] = y[mask1]
        self.peak[mask2_a] = self.peak[mask2_a] + self.peak_decay[mask2_a]

        self.peak_decay[mask1] = 20. * log10(PEAK_DECAY_RATE) * 5000
        self.peak_decay[mask2_a] += 20. * log10(PEAK_DECAY_RATE) * 5000

        self.peak_int[mask1] = 1.
        self.peak_int[mask2_b] *= 0.975

    def setspecrange(self, spec_min, spec_max):
        self.verticalScaleTransform.setRange(spec_min, spec_max)
        self.verticalScaleDivision.setRange(spec_min, spec_max)

        # notify that sizeHint has changed (this should be done with a signal emitted from the scale division to the scale bar)
        self.verticalScale.scaleBar.updateGeometry()

        self.needfullreplot = True
        self.update()

    def setweighting(self, weighting):
        if weighting is 0:
            title = "PSD (dB)"
        elif weighting is 1:
            title = "PSD (dB A)"
        elif weighting is 2:
            title = "PSD (dB B)"
        else:
            title = "PSD (dB C)"

        self.verticalScale.setTitle(title)
Пример #8
0
class SpectrumPlotWidget(QtWidgets.QWidget):

    def __init__(self, parent, logger=None):
        super(SpectrumPlotWidget, self).__init__(parent)

        self.x1 = array([0.1, 0.5, 1.])
        self.x2 = array([0.5, 1., 2.])

        self.needtransform = False

        self.paused = False

        self.peaks_enabled = True
        self.peak = zeros((3,))
        self.peak_int = zeros((3,))
        self.peak_decay = ones((3,)) * PEAK_DECAY_RATE

        self.verticalScaleDivision = ScaleDivision(0, 1, 100)
        self.verticalScaleTransform = CoordinateTransform(0, 1, 100, 0, 0)

        self.verticalScale = VerticalScaleWidget(self, self.verticalScaleDivision, self.verticalScaleTransform)
        self.verticalScale.setTitle("PSD (dB)")

        self.horizontalScaleDivision = ScaleDivision(0, 22000, 100)
        self.horizontalScaleTransform = CoordinateTransform(0, 22000, 100, 0, 0)

        self.horizontalScale = HorizontalScaleWidget(self, self.horizontalScaleDivision, self.horizontalScaleTransform)
        self.horizontalScale.setTitle("Frequency (Hz)")

        self.canvasWidget = GlCanvasWidget(self, self.verticalScaleTransform, self.horizontalScaleTransform)
        self.canvasWidget.setTrackerFormatter(lambda x, y: "%d Hz, %.1f dB" % (x, y))
        self.canvasWidget.resized.connect(self.canvasResized)

        r_peak = lambda p: 1. + 0.*p
        g_peak = lambda p: 1. - p
        b_peak = lambda p: 1. - p

        self.peakQuadsItem = QuadsItem(r_peak, g_peak, b_peak)
        self.canvasWidget.attach(self.peakQuadsItem)

        r_signal = lambda p: 0.*p
        g_signal = lambda p: 0.3 + 0.5*p
        b_signal = lambda p: 0.*p

        self.quadsItem = QuadsItem(r_signal, g_signal, b_signal)
        self.canvasWidget.attach(self.quadsItem)

        plotLayout = QtWidgets.QGridLayout()
        plotLayout.setSpacing(0)
        plotLayout.setContentsMargins(0, 0, 0, 0)
        plotLayout.addWidget(self.verticalScale, 0, 0)
        plotLayout.addWidget(self.canvasWidget, 0, 1)
        plotLayout.addWidget(self.horizontalScale, 1, 1)

        self.setLayout(plotLayout)

    def setlinfreqscale(self):
        self.horizontalScaleTransform.setLinear()
        self.horizontalScaleDivision.setLinear()

        self.needtransform = True
        self.draw()

    def setlogfreqscale(self):
        self.horizontalScaleTransform.setLogarithmic()
        self.horizontalScaleDivision.setLogarithmic()

        self.needtransform = True
        self.draw()

    def setfreqrange(self, minfreq, maxfreq):
        self.xmin = minfreq
        self.xmax = maxfreq

        self.horizontalScaleTransform.setRange(minfreq, maxfreq)
        self.horizontalScaleDivision.setRange(minfreq, maxfreq)

        # notify that sizeHint has changed (this should be done with a
        # signal emitted from the scale division to the scale bar)
        self.horizontalScale.scaleBar.updateGeometry()

        self.needtransform = True
        self.draw()

    def setspecrange(self, spec_min, spec_max):
        if spec_min > spec_max:
            spec_min, spec_max = spec_max, spec_min

        self.verticalScaleTransform.setRange(spec_min, spec_max)
        self.verticalScaleDivision.setRange(spec_min, spec_max)

        # notify that sizeHint has changed (this should be done with a
        # signal emitted from the scale division to the scale bar)
        self.verticalScale.scaleBar.updateGeometry()

        self.needtransform = True
        self.draw()

    def setweighting(self, weighting):
        if weighting is 0:
            title = "PSD (dB)"
        elif weighting is 1:
            title = "PSD (dB A)"
        elif weighting is 2:
            title = "PSD (dB B)"
        else:
            title = "PSD (dB C)"

        self.verticalScale.setTitle(title)
        self.needtransform = True
        self.draw()

    def setShowFreqLabel(self, showFreqLabel):
        self.canvasWidget.setShowFreqLabel(showFreqLabel)

    def set_peaks_enabled(self, enabled):
        self.peaks_enabled = enabled

        self.canvasWidget.detachAll()
        if enabled:
            self.canvasWidget.attach(self.peakQuadsItem)
        self.canvasWidget.attach(self.quadsItem)

    def set_baseline_displayUnits(self, baseline):
        self.quadsItem.set_baseline_displayUnits(baseline)

    def set_baseline_dataUnits(self, baseline):
        self.quadsItem.set_baseline_dataUnits(baseline)

    def setdata(self, x, y, fmax):
        x1 = zeros(x.shape)
        x2 = zeros(x.shape)
        x1[0] = 1e-10
        x1[1:] = (x[1:] + x[:-1]) / 2.
        x2[:-1] = x1[1:]
        x2[-1] = float(SAMPLING_RATE / 2)

        if len(x1) != len(self.x1):
            self.needtransform = True
            self.x1 = x1
            self.x2 = x2

        if not self.paused:
            self.canvasWidget.setfmax(fmax)

            M = max(y)
            m = self.verticalScaleTransform.coord_min
            y_int = (y-m)/(np.abs(M-m)+1e-3)

            self.quadsItem.setData(x1, x2, y, y_int)

            if self.peaks_enabled:
                self.compute_peaks(y)
                self.peakQuadsItem.setData(x1, x2, self.peak, self.peak_int)

    def draw(self):
        if self.needtransform:
            self.verticalScaleDivision.setLength(self.canvasWidget.height())
            self.verticalScaleTransform.setLength(self.canvasWidget.height())
            startBorder, endBorder = self.verticalScale.spacingBorders()
            self.verticalScaleTransform.setBorders(startBorder, endBorder)

            self.verticalScale.update()

            self.horizontalScaleDivision.setLength(self.canvasWidget.width())
            self.horizontalScaleTransform.setLength(self.canvasWidget.width())
            startBorder, endBorder = self.horizontalScale.spacingBorders()
            self.horizontalScaleTransform.setBorders(startBorder, endBorder)

            self.horizontalScale.update()

            self.canvasWidget.setGrid(array(self.horizontalScaleDivision.majorTicks()),
                                      array(self.horizontalScaleDivision.minorTicks()),
                                      array(self.verticalScaleDivision.majorTicks()),
                                      array(self.verticalScaleDivision.minorTicks()))

            self.quadsItem.transformUpdate()
            self.peakQuadsItem.transformUpdate()

            self.needtransform = False

    def pause(self):
        self.paused = True
        self.canvasWidget.pause()

    def restart(self):
        self.paused = False
        self.canvasWidget.restart()

    # redraw when the time_plot is resized to update coordinates transformations
    # QOpenGlWidget does not like that we override resizeEvent
    def canvasResized(self, canvasWidth, canvasHeight):
        self.needtransform = True
        self.draw()

    def canvasUpdate(self):
        self.canvasWidget.update()

    def compute_peaks(self, y):
        if len(self.peak) != len(y):
            y_ones = ones(y.shape)
            self.peak = y_ones * (-500.)
            self.peak_int = zeros(y.shape)
            self.peak_decay = y_ones * 20. * log10(PEAK_DECAY_RATE) * 5000

        mask1 = (self.peak < y)
        mask2 = (-mask1)
        mask2_a = mask2 * (abs(self.peak_int) < 0.2)
        mask2_b = mask2 * (abs(self.peak_int) >= 0.2)

        self.peak[mask1] = y[mask1]
        self.peak[mask2_a] = self.peak[mask2_a] + self.peak_decay[mask2_a]

        self.peak_decay[mask1] = 20. * log10(PEAK_DECAY_RATE) * 5000
        self.peak_decay[mask2_a] += 20. * log10(PEAK_DECAY_RATE) * 5000
        self.peak_int[mask1] = 1.
        self.peak_int[mask2_b] *= 0.975