Esempio n. 1
0
class Scope_Widget(QtWidgets.QWidget):

    name = "Oscilloscope"

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

        self.logger = logger

        self.setObjectName(self.name)
        self.gridLayout = QtWidgets.QGridLayout(self)
        self.gridLayout.setObjectName("gridLayout")
        self.PlotZoneUp = TimePlot(self, self.logger)
        self.PlotZoneUp.setObjectName("PlotZoneUp")
        self.gridLayout.addWidget(self.PlotZoneUp, 0, 0, 1, 1)

        self.settings_dialog = Scope_Settings_Dialog(self, self.logger)

        self.time_range_s = DEFAULT_TIMERANGE * 0.001
        self.num_sample_points = int(self.time_range_s * SAMPLING_RATE)

        self.time = numpy.linspace(0, self.time_range_s, self.num_sample_points)
        # Keep a small display buffer so we can use triggering in the data display.
        self.display_buffer = RingBuffer(1, 2*self.num_sample_points)

    def handle_new_data(self, floatdata):
        self.display_buffer.push(floatdata)

        data = self.display_buffer.unwound_data()
        is_dual_channel = data.shape[0] == 2

        # trigger on the first channel only
        trigger_data = data[0, :]
        # trigger on half of the waveform
        half_sample = int(self.num_sample_points/2)
        trig_search_start = half_sample
        trig_search_stop = -half_sample
        trigger_data = trigger_data[trig_search_start:trig_search_stop]

        trigger_level = data.max() * 2. / 3.
        trigger_pos = numpy.where((trigger_data[:-1] < trigger_level) * (trigger_data[1:] >= trigger_level))[0]

        if len(trigger_pos) > 0:
            shift = trigger_pos[0]
        else:
            return

        shift += trig_search_start
        data = data[:, shift - half_sample: shift + half_sample]

        y = data[0, :]
        if is_dual_channel:
            y2 = data[1, :]
        else:
            y2 = None

        dBscope = False
        if dBscope:
            dBmin = -50.
            y = numpy.sign(y) * (20 * numpy.log10(
                abs(y))).clip(dBmin, 0.) / (-dBmin) + numpy.sign(y) * 1.
            if is_dual_channel:
                y2 = numpy.sign(y2) * (20 * numpy.log10(
                    abs(y2))).clip(dBmin, 0.) / (-dBmin) + numpy.sign(y2) * 1.
            else:
                y2 = None

        if y2 is not None:
            self.PlotZoneUp.setdataTwoChannels(self.time, y, y2)
        else:
            self.PlotZoneUp.setdata(self.time, y)

    # method
    def canvasUpdate(self):
        return

    def pause(self):
        self.PlotZoneUp.pause()

    def restart(self):
        self.PlotZoneUp.restart()

    # slot
    def set_timerange(self, timerange):
        self.time_range_s = timerange*0.001
        self.num_sample_points = int(self.time_range_s * SAMPLING_RATE)
        self.time = numpy.linspace(0, self.time_range_s, self.num_sample_points)
        self.display_buffer.reset(self.display_buffer.num_channels, self.num_sample_points)

    # slot
    def settings_called(self, checked):
        self.settings_dialog.show()

    # method
    def saveState(self, settings):
        self.settings_dialog.saveState(settings)

    # method
    def restoreState(self, settings):
        self.settings_dialog.restoreState(settings)
Esempio n. 2
0
class Scope_Widget(QtGui.QWidget):
    def __init__(self, parent = None, logger = None):
        QtGui.QWidget.__init__(self, parent)

        self.audiobuffer = None
        
        # store the logger instance
        if logger is None:
            self.logger = parent.parent.logger
        else:
            self.logger = logger
        
        self.setObjectName("Scope_Widget")
        self.gridLayout = QtGui.QGridLayout(self)
        self.gridLayout.setObjectName("gridLayout")
        self.PlotZoneUp = TimePlot(self, self.logger)
        self.PlotZoneUp.setObjectName("PlotZoneUp")
        self.gridLayout.addWidget(self.PlotZoneUp, 0, 0, 1, 1)
        
        self.setStyleSheet(STYLESHEET)

        self.settings_dialog = Scope_Settings_Dialog(self, self.logger)

    # method
    def set_buffer(self, buffer):
        self.audiobuffer = buffer

    # method
    def update(self):
        if not self.isVisible():
            return

        time = 2*SMOOTH_DISPLAY_TIMER_PERIOD_MS/1000.
        width = time*SAMPLING_RATE
        #basic trigger capability on leading edge
        floatdata = self.audiobuffer.data(2*width)

        #number of data points received at the last audio buffer update
        #newpoints = self.audiobuffer.newpoints
        #print newpoints

        # because of the buffering, sometimes we have not got any data
        #if newpoints==0:
        #    return
        
        #floatdata = self.audiobuffer.data(newpoints + width)

        twoChannels = False
        if floatdata.shape[0] > 1:
            twoChannels = True

        # trigger on the first channel only
        triggerdata = floatdata[0,:]
        # trigger on half of the waveform
        trig_search_start = width/2
        trig_search_stop = -width/2
        triggerdata = triggerdata[trig_search_start : trig_search_stop]

        max = floatdata.max()
        trigger_level = max*2./3.
        #trigger_level = 0.6
        trigger_pos = where((triggerdata[:-1] < trigger_level)*(triggerdata[1:] >= trigger_level))[0]

        if len(trigger_pos)==0:
            return
        
        if len(trigger_pos) > 0:
            shift = trigger_pos[0]
        else:
            #return
            shift = 0
        shift += trig_search_start
        datarange = width
        floatdata = floatdata[:, shift -  datarange/2: shift +  datarange/2]
 
        y = floatdata[0,:] #- floatdata.mean()
        if twoChannels:
            y2 = floatdata[1,:] #- floatdata.mean()
        
        dBscope = False
        if dBscope:
            dBmin = -50.
            y = sign(y)*(20*log10(abs(y))).clip(dBmin, 0.)/(-dBmin) + sign(y)*1.
            if twoChannels:
                y2 = sign(y2)*(20*log10(abs(y2))).clip(dBmin, 0.)/(-dBmin) + sign(y2)*1.
    
        time = (arange(len(y)) - datarange/2)/float(SAMPLING_RATE)
        
        if twoChannels:
            self.PlotZoneUp.setdataTwoChannels(time, y, y2)
        else:
            self.PlotZoneUp.setdata(time, y)

    # slot
    def settings_called(self, checked):
        self.settings_dialog.show()

    # method
    def saveState(self, settings):
        self.settings_dialog.saveState(settings)
    
    # method
    def restoreState(self, settings):
        self.settings_dialog.restoreState(settings)
Esempio n. 3
0
class LongLevelWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setObjectName("LongLevels_Widget")

        self.setObjectName("Scope_Widget")
        self.gridLayout = QtWidgets.QGridLayout(self)
        self.gridLayout.setObjectName("gridLayout")
        self.PlotZoneUp = TimePlot(self)
        self.PlotZoneUp.setObjectName("PlotZoneUp")
        self.PlotZoneUp.setverticaltitle("Level (dB FS RMS)")
        self.PlotZoneUp.sethorizontaltitle("Time (min)")
        self.PlotZoneUp.setTrackerFormatter(lambda x, y: "%.3g min, %.3g" %
                                            (x, y))

        self.level_min = DEFAULT_LEVEL_MIN
        self.level_max = DEFAULT_LEVEL_MAX
        self.PlotZoneUp.setverticalrange(self.level_min, self.level_max)

        self.gridLayout.addWidget(self.PlotZoneUp, 0, 0, 1, 1)

        self.audiobuffer = None

        # initialize the settings dialog
        self.settings_dialog = LongLevels_Settings_Dialog(self)

        # initialize the class instance that will do the fft
        self.proc = audioproc()

        self.level = None  # 1e-30
        self.level_rms = -200.

        self.two_channels = False

        self.i = 0

        self.old_index = 0

        # self.response_time = 60. # 1 minute
        self.response_time = 20.

        # how many times we should decimate to end up with 100 points in the kernel
        self.Ndec = int(
            max(0,
                np.floor(
                    (np.log2(self.response_time * SAMPLING_RATE / 100.)))))

        Ngauss = 4
        self.b = np.array(gauss(10 * Ngauss + 1, 2. * Ngauss))
        self.a = np.zeros(self.b.shape)
        self.a[0] = 1.
        self.zf = np.zeros(max(len(self.b), len(self.a)) - 1)

        self.subsampled_sampling_rate = SAMPLING_RATE / 2**(self.Ndec)
        self.subsampler = Subsampler(self.Ndec)

        self.length_seconds = 60. * 10
        # actually this should be linked to the pixel width of the plot area
        self.length_samples = int(self.length_seconds *
                                  self.subsampled_sampling_rate)

        # ringbuffer for the subsampled data
        self.ringbuffer = RingBuffer()

    # method
    def set_buffer(self, buffer):
        self.audiobuffer = buffer

    def handle_new_data(self, floatdata):
        # we need to maintain an index of where we are in the buffer
        index = self.audiobuffer.ringbuffer.offset
        self.last_data_time = self.audiobuffer.lastDataTime

        available = index - self.old_index

        if available < 0:
            # ringbuffer must have grown or something...
            available = 0
            self.old_index = index

        # if we have enough data to add a frequency column in the time-frequency plane, compute it
        needed = int(2**self.Ndec)
        realizable = int(np.floor(available / needed))

        if realizable > 0:
            for i in range(realizable):
                floatdata = self.audiobuffer.data_indexed(
                    self.old_index, needed)

                # first channel
                y0 = floatdata[0, :]

                y0_squared = y0**2

                # subsample
                y0_squared_dec = self.subsampler.push(y0_squared)

                self.level, self.zf = pyx_lfilter_float64_1D(
                    self.b, self.a, y0_squared_dec, self.zf)

                self.level_rms = 10. * np.log10(max(self.level, 1e-150))

                l = np.array([self.level_rms])
                l.shape = (1, 1)

                self.ringbuffer.push(l)

                self.old_index += needed

            self.time = np.arange(
                self.length_samples) / self.subsampled_sampling_rate

            levels = self.ringbuffer.data(self.length_samples)

            self.PlotZoneUp.setdata(self.time / 60., levels[0, :])

    # method
    def canvasUpdate(self):
        # nothing to do here
        return

    def setmin(self, value):
        self.level_min = value
        self.PlotZoneUp.setverticalrange(self.level_min, self.level_max)

    def setmax(self, value):
        self.level_max = value
        self.PlotZoneUp.setverticalrange(self.level_min, self.level_max)

    # slot
    def settings_called(self, checked):
        self.settings_dialog.show()

    # method
    def saveState(self, settings):
        self.settings_dialog.saveState(settings)

    # method
    def restoreState(self, settings):
        self.settings_dialog.restoreState(settings)
Esempio n. 4
0
class Scope_Widget(QtWidgets.QWidget):
    def __init__(self, parent, sharedGLWidget, logger=PrintLogger()):
        super().__init__(parent)

        self.audiobuffer = None
        self.logger = logger

        self.setObjectName("Scope_Widget")
        self.gridLayout = QtWidgets.QGridLayout(self)
        self.gridLayout.setObjectName("gridLayout")
        self.PlotZoneUp = TimePlot(self, sharedGLWidget, self.logger)
        self.PlotZoneUp.setObjectName("PlotZoneUp")
        self.gridLayout.addWidget(self.PlotZoneUp, 0, 0, 1, 1)

        self.settings_dialog = Scope_Settings_Dialog(self, self.logger)

        self.timerange = DEFAULT_TIMERANGE

    # method
    def set_buffer(self, buffer):
        self.audiobuffer = buffer

    # method
    def update(self):
        if not self.isVisible():
            return

        time = self.timerange * 1e-3
        width = int(time * SAMPLING_RATE)
        #basic trigger capability on leading edge
        floatdata = self.audiobuffer.data(2 * width)

        #number of data points received at the last audio buffer update
        #newpoints = self.audiobuffer.newpoints
        #print newpoints

        # because of the buffering, sometimes we have not got any data
        #if newpoints==0:
        #    return

        #floatdata = self.audiobuffer.data(newpoints + width)

        twoChannels = False
        if floatdata.shape[0] > 1:
            twoChannels = True

        # trigger on the first channel only
        triggerdata = floatdata[0, :]
        # trigger on half of the waveform
        trig_search_start = width / 2
        trig_search_stop = -width / 2
        triggerdata = triggerdata[trig_search_start:trig_search_stop]

        max = floatdata.max()
        trigger_level = max * 2. / 3.
        #trigger_level = 0.6
        trigger_pos = where((triggerdata[:-1] < trigger_level) *
                            (triggerdata[1:] >= trigger_level))[0]

        if len(trigger_pos) == 0:
            return

        if len(trigger_pos) > 0:
            shift = trigger_pos[0]
        else:
            #return
            shift = 0
        shift += trig_search_start
        datarange = width
        floatdata = floatdata[:, shift - datarange / 2:shift + datarange / 2]

        y = floatdata[0, :]  #- floatdata.mean()
        if twoChannels:
            y2 = floatdata[1, :]  #- floatdata.mean()

        dBscope = False
        if dBscope:
            dBmin = -50.
            y = sign(y) * (20 * log10(abs(y))).clip(
                dBmin, 0.) / (-dBmin) + sign(y) * 1.
            if twoChannels:
                y2 = sign(y2) * (20 * log10(abs(y2))).clip(
                    dBmin, 0.) / (-dBmin) + sign(y2) * 1.

        time = (arange(len(y)) - datarange / 2) / float(SAMPLING_RATE)

        if twoChannels:
            self.PlotZoneUp.setdataTwoChannels(time, y, y2)
        else:
            self.PlotZoneUp.setdata(time, y)

    # slot
    def set_timerange(self, timerange):
        self.timerange = timerange

    # slot
    def settings_called(self, checked):
        self.settings_dialog.show()

    # method
    def saveState(self, settings):
        self.settings_dialog.saveState(settings)

    # method
    def restoreState(self, settings):
        self.settings_dialog.restoreState(settings)
Esempio n. 5
0
class Scope_Widget(QtGui.QWidget):
	def __init__(self, parent = None, logger = None):
		QtGui.QWidget.__init__(self, parent)

		self.audiobuffer = None
		
		# store the logger instance
		if logger is None:
		    self.logger = parent.parent.logger
		else:
		    self.logger = logger
		
		self.setObjectName("Scope_Widget")
		self.gridLayout = QtGui.QGridLayout(self)
		self.gridLayout.setObjectName("gridLayout")
		self.PlotZoneUp = TimePlot(self, self.logger)
		self.PlotZoneUp.setObjectName("PlotZoneUp")
		self.gridLayout.addWidget(self.PlotZoneUp, 0, 0, 1, 1)
		
		self.setStyleSheet(STYLESHEET)

		self.settings_dialog = Scope_Settings_Dialog(self, self.logger)

	# method
	def set_buffer(self, buffer):
		self.audiobuffer = buffer

	# method
	def update(self):
		if not self.isVisible():
			return

		time = SMOOTH_DISPLAY_TIMER_PERIOD_MS/1000.
		#basic trigger capability on leading edge
		floatdata = self.audiobuffer.data(time*SAMPLING_RATE)

		twoChannels = False
		if floatdata.shape[0] > 1:
			twoChannels = True

		# trigger on the first channel only
		floatdata = floatdata[0,:]

		max = floatdata.max()
		trigger_level = max*2./3.
		trigger_pos = where((floatdata[:-1] < trigger_level)*(floatdata[1:] >= trigger_level))[0]
		if len(trigger_pos) > 0:
			shift = time*SAMPLING_RATE - trigger_pos[0]
		else:
			shift = 0
		floatdata = self.audiobuffer.data(time*SAMPLING_RATE + shift)
		floatdata = floatdata[:, 0 : time*SAMPLING_RATE]
  
		y = floatdata[0,::-1] #- floatdata.mean()
 		if twoChannels:
			y2 = floatdata[1,::-1] #- floatdata.mean()
		
		dBscope = False
		if dBscope:
		    dBmin = -50.
		    y = sign(y)*(20*log10(abs(y))).clip(dBmin, 0.)/(-dBmin) + sign(y)*1.
 		    if twoChannels:
			 y2 = sign(y2)*(20*log10(abs(y2))).clip(dBmin, 0.)/(-dBmin) + sign(y2)*1.
	
		time = linspace(0., len(y)/float(SAMPLING_RATE), len(y))
		
 		if twoChannels:
			self.PlotZoneUp.setdataTwoChannels(time, y, y2)
 		else:
			self.PlotZoneUp.setdata(time, y)

	# slot
	def settings_called(self, checked):
		self.settings_dialog.show()

	# method
	def saveState(self, settings):
		self.settings_dialog.saveState(settings)
	
	# method
	def restoreState(self, settings):
		self.settings_dialog.restoreState(settings)
Esempio n. 6
0
class LongLevelWidget(QtWidgets.QWidget):

    def __init__(self, parent=None, logger=PrintLogger()):
        super().__init__(parent)
        self.setObjectName("LongLevels_Widget")

        self.logger = logger

        self.setObjectName("Scope_Widget")
        self.gridLayout = QtWidgets.QGridLayout(self)
        self.gridLayout.setObjectName("gridLayout")
        self.PlotZoneUp = TimePlot(self, self.logger)
        self.PlotZoneUp.setObjectName("PlotZoneUp")
        self.PlotZoneUp.setverticaltitle("Level (dB FS RMS)")
        self.PlotZoneUp.sethorizontaltitle("Time (min)")
        self.PlotZoneUp.setTrackerFormatter(lambda x, y: "%.3g min, %.3g" % (x, y))

        self.level_min = DEFAULT_LEVEL_MIN
        self.level_max = DEFAULT_LEVEL_MAX
        self.PlotZoneUp.setverticalrange(self.level_min, self.level_max)

        self.gridLayout.addWidget(self.PlotZoneUp, 0, 0, 1, 1)

        self.logger = logger
        self.audiobuffer = None

        # initialize the settings dialog
        self.settings_dialog = LongLevels_Settings_Dialog(self, self.logger)

        # initialize the class instance that will do the fft
        self.proc = audioproc(self.logger)

        self.level = None # 1e-30
        self.level_rms = -200.

        self.two_channels = False

        self.i = 0

        self.old_index = 0

        #self.response_time = 60. # 1 minute
        self.response_time = 20.

        # how many times we should decimate to end up with 100 points in the kernel
        self.Ndec = int(max(0, np.floor((np.log2(self.response_time * SAMPLING_RATE/100.)))))

        Ngauss = 4
        self.b = np.array(gauss(10*Ngauss+1, 2.*Ngauss))
        self.a = np.zeros(self.b.shape)
        self.a[0] = 1.
        self.zf = np.zeros(max(len(self.b), len(self.a)) - 1)

        self.subsampled_sampling_rate = SAMPLING_RATE / 2 ** (self.Ndec)
        self.subsampler = Subsampler(self.Ndec)

        self.length_seconds = 60.*10
        # actually this should be linked to the pixel width of the plot area
        self.length_samples = self.length_seconds * self.subsampled_sampling_rate

        # ringbuffer for the subsampled data
        self.ringbuffer = RingBuffer(self.logger)

    # method
    def set_buffer(self, buffer):
        self.audiobuffer = buffer

    def handle_new_data(self, floatdata):
        # we need to maintain an index of where we are in the buffer
        index = self.audiobuffer.ringbuffer.offset
        self.last_data_time = self.audiobuffer.lastDataTime

        available = index - self.old_index

        if available < 0:
            # ringbuffer must have grown or something...
            available = 0
            self.old_index = index

        # if we have enough data to add a frequency column in the time-frequency plane, compute it
        needed = 2**self.Ndec
        realizable = int(np.floor(available / needed))

        if realizable > 0:
            for i in range(realizable):
                floatdata = self.audiobuffer.data_indexed(self.old_index, needed)

                # first channel
                y0 = floatdata[0, :]

                y0_squared = y0**2

                # subsample
                y0_squared_dec = self.subsampler.push(y0_squared)

                self.level, self.zf = pyx_lfilter_float64_1D(self.b, self.a, y0_squared_dec, self.zf)

                self.level_rms = 10. * np.log10(max(self.level, 1e-150))

                l = np.array([self.level_rms])
                l.shape = (1, 1)

                self.ringbuffer.push(l)

                self.old_index += int(needed)

            self.time = np.arange(self.length_samples) / self.subsampled_sampling_rate

            levels = self.ringbuffer.data(self.length_samples)

            self.PlotZoneUp.setdata(self.time/60., levels[0,:])

    # method
    def canvasUpdate(self):
        # nothing to do here
        return

    def setmin(self, value):
        self.level_min = value
        self.PlotZoneUp.setverticalrange(self.level_min, self.level_max)

    def setmax(self, value):
        self.level_max = value
        self.PlotZoneUp.setverticalrange(self.level_min, self.level_max)

    # slot
    def settings_called(self, checked):
        self.settings_dialog.show()

    # method
    def saveState(self, settings):
        self.settings_dialog.saveState(settings)

    # method
    def restoreState(self, settings):
        self.settings_dialog.restoreState(settings)
Esempio n. 7
0
class Scope_Widget(QtWidgets.QWidget):
    def __init__(self, parent, logger = PrintLogger()):
        super().__init__(parent)

        self.audiobuffer = None
        self.logger = logger

        self.setObjectName("Scope_Widget")
        self.gridLayout = QtWidgets.QGridLayout(self)
        self.gridLayout.setObjectName("gridLayout")
        self.PlotZoneUp = TimePlot(self, self.logger)
        self.PlotZoneUp.setObjectName("PlotZoneUp")
        self.gridLayout.addWidget(self.PlotZoneUp, 0, 0, 1, 1)

        self.settings_dialog = Scope_Settings_Dialog(self, self.logger)

        self.timerange = DEFAULT_TIMERANGE

        self.time = zeros(10)
        self.y = zeros(10)
        self.y2 = zeros(10)

    # method
    def set_buffer(self, buffer):
        self.audiobuffer = buffer

    def handle_new_data(self, floatdata):
        time = self.timerange*1e-3
        width = int(time*SAMPLING_RATE)
        #basic trigger capability on leading edge
        floatdata = self.audiobuffer.data(2*width)

        #number of data points received at the last audio buffer update
        #newpoints = self.audiobuffer.newpoints
        #print newpoints

        # because of the buffering, sometimes we have not got any data
        #if newpoints==0:
        #    return

        #floatdata = self.audiobuffer.data(newpoints + width)

        twoChannels = False
        if floatdata.shape[0] > 1:
            twoChannels = True

        # trigger on the first channel only
        triggerdata = floatdata[0,:]
        # trigger on half of the waveform
        trig_search_start = width/2
        trig_search_stop = -width/2
        triggerdata = triggerdata[trig_search_start : trig_search_stop]

        max = floatdata.max()
        trigger_level = max*2./3.
        #trigger_level = 0.6
        trigger_pos = where((triggerdata[:-1] < trigger_level)*(triggerdata[1:] >= trigger_level))[0]

        if len(trigger_pos)==0:
            return

        if len(trigger_pos) > 0:
            shift = trigger_pos[0]
        else:
            #return
            shift = 0
        shift += trig_search_start
        datarange = width
        floatdata = floatdata[:, shift -  datarange/2: shift +  datarange/2]

        self.y = floatdata[0,:] #- floatdata.mean()
        if twoChannels:
            self.y2 = floatdata[1,:] #- floatdata.mean()
        else:
            self.y2 = None

        dBscope = False
        if dBscope:
            dBmin = -50.
            self.y = sign(self.y)*(20*log10(abs(self.y))).clip(dBmin, 0.)/(-dBmin) + sign(self.y)*1.
            if twoChannels:
                self.y2 = sign(self.y2)*(20*log10(abs(self.y2))).clip(dBmin, 0.)/(-dBmin) + sign(self.y2)*1.
            else:
                self.y2 = None

        self.time = (arange(len(self.y)) - datarange/2)/float(SAMPLING_RATE)

        if self.y2 is not None:
            self.PlotZoneUp.setdataTwoChannels(self.time, self.y, self.y2)
        else:
            self.PlotZoneUp.setdata(self.time, self.y)

    # method
    def canvasUpdate(self):
        return

    def pause(self):
        self.PlotZoneUp.pause()

    def restart(self):
        self.PlotZoneUp.restart()

    # slot
    def set_timerange(self, timerange):
        self.timerange = timerange

    # slot
    def settings_called(self, checked):
        self.settings_dialog.show()

    # method
    def saveState(self, settings):
        self.settings_dialog.saveState(settings)
    
    # method
    def restoreState(self, settings):
        self.settings_dialog.restoreState(settings)
Esempio n. 8
0
class Scope_Widget(QtWidgets.QWidget):
    def __init__(self, parent, logger=PrintLogger()):
        super().__init__(parent)

        self.audiobuffer = None
        self.logger = logger

        self.setObjectName("Scope_Widget")
        self.gridLayout = QtWidgets.QGridLayout(self)
        self.gridLayout.setObjectName("gridLayout")
        self.PlotZoneUp = TimePlot(self, self.logger)
        self.PlotZoneUp.setObjectName("PlotZoneUp")
        self.gridLayout.addWidget(self.PlotZoneUp, 0, 0, 1, 1)

        self.settings_dialog = Scope_Settings_Dialog(self, self.logger)

        self.timerange = DEFAULT_TIMERANGE

        self.time = zeros(10)
        self.y = zeros(10)
        self.y2 = zeros(10)

    # method
    def set_buffer(self, buffer):
        self.audiobuffer = buffer

    def handle_new_data(self, floatdata):
        time = self.timerange * 1e-3
        width = int(time * SAMPLING_RATE)
        # basic trigger capability on leading edge
        floatdata = self.audiobuffer.data(2 * width)

        twoChannels = False
        if floatdata.shape[0] > 1:
            twoChannels = True

        # trigger on the first channel only
        triggerdata = floatdata[0, :]
        # trigger on half of the waveform
        trig_search_start = width / 2
        trig_search_stop = -width / 2
        triggerdata = triggerdata[trig_search_start:trig_search_stop]

        trigger_level = floatdata.max() * 2. / 3.
        trigger_pos = where((triggerdata[:-1] < trigger_level) *
                            (triggerdata[1:] >= trigger_level))[0]

        if len(trigger_pos) == 0:
            return

        if len(trigger_pos) > 0:
            shift = trigger_pos[0]
        else:
            shift = 0
        shift += trig_search_start
        datarange = width
        floatdata = floatdata[:, shift - datarange / 2:shift + datarange / 2]

        self.y = floatdata[0, :]
        if twoChannels:
            self.y2 = floatdata[1, :]
        else:
            self.y2 = None

        dBscope = False
        if dBscope:
            dBmin = -50.
            self.y = sign(self.y) * (20 * log10(abs(self.y))).clip(
                dBmin, 0.) / (-dBmin) + sign(self.y) * 1.
            if twoChannels:
                self.y2 = sign(self.y2) * (20 * log10(abs(self.y2))).clip(
                    dBmin, 0.) / (-dBmin) + sign(self.y2) * 1.
            else:
                self.y2 = None

        self.time = (arange(len(self.y)) -
                     datarange / 2) / float(SAMPLING_RATE)

        if self.y2 is not None:
            self.PlotZoneUp.setdataTwoChannels(self.time * 1e3, self.y,
                                               self.y2)
        else:
            self.PlotZoneUp.setdata(self.time * 1e3, self.y)

    # method
    def canvasUpdate(self):
        return

    def pause(self):
        self.PlotZoneUp.pause()

    def restart(self):
        self.PlotZoneUp.restart()

    # slot
    def set_timerange(self, timerange):
        self.timerange = timerange

    # slot
    def settings_called(self, checked):
        self.settings_dialog.show()

    # method
    def saveState(self, settings):
        self.settings_dialog.saveState(settings)

    # method
    def restoreState(self, settings):
        self.settings_dialog.restoreState(settings)
Esempio n. 9
0
class LongLevelWidget(QtWidgets.QWidget):
    def __init__(self, parent=None, logger=PrintLogger()):
        super().__init__(parent)
        self.setObjectName("LongLevels_Widget")

        self.logger = logger

        self.setObjectName("Scope_Widget")
        self.gridLayout = QtWidgets.QGridLayout(self)
        self.gridLayout.setObjectName("gridLayout")
        self.PlotZoneUp = TimePlot(self, self.logger)
        self.PlotZoneUp.setObjectName("PlotZoneUp")
        self.PlotZoneUp.setverticaltitle("Level (dB FS RMS)")
        self.PlotZoneUp.sethorizontaltitle("Time (min)")
        self.PlotZoneUp.setTrackerFormatter(lambda x, y: "%.3g min, %.3g" %
                                            (x, y))

        self.level_min = DEFAULT_LEVEL_MIN
        self.level_max = DEFAULT_LEVEL_MAX
        self.PlotZoneUp.setverticalrange(self.level_min, self.level_max)

        self.gridLayout.addWidget(self.PlotZoneUp, 0, 0, 1, 1)

        self.logger = logger
        self.audiobuffer = None

        # initialize the settings dialog
        self.settings_dialog = LongLevels_Settings_Dialog(self, self.logger)

        # initialize the class instance that will do the fft
        self.proc = audioproc(self.logger)

        #self.response_time = 60. # 1 minute
        self.response_time = 1.

        # an exponential smoothing filter is a simple IIR filter
        # s_i = alpha*x_i + (1-alpha)*s_{i-1}
        # we compute alpha so that the n most recent samples represent 100*w percent of the output
        w = 0.65

        # how many times we should decimate to end up with 100 points in the kernel
        self.Ndec = int(
            max(0,
                np.floor(
                    (np.log2(self.response_time * SAMPLING_RATE / 100.)))))

        n = self.response_time * SAMPLING_RATE / 2**self.Ndec
        N = int(5 * n)
        self.alpha = 1. - (1. - w)**(1. / (n + 1))
        self.kernel = (1. - self.alpha)**(np.arange(0, N)[::-1])

        self.level = None  # 1e-30
        self.level_rms = -200.

        self.two_channels = False

        self.i = 0

        self.subsampled_sampling_rate = SAMPLING_RATE / 2**(self.Ndec)
        self.subsampler = Subsampler(self.Ndec)

        self.old_index = 0

        self.length_seconds = 60. * 10
        # actually this should be linked to the pixel width of the plot area
        self.length_samples = self.length_seconds * self.subsampled_sampling_rate

        # ringbuffer for the subsampled data
        self.ringbuffer = RingBuffer(self.logger)

    # method
    def set_buffer(self, buffer):
        self.audiobuffer = buffer

    def handle_new_data(self, floatdata):
        # we need to maintain an index of where we are in the buffer
        index = self.audiobuffer.ringbuffer.offset
        self.last_data_time = self.audiobuffer.lastDataTime

        available = index - self.old_index

        if available < 0:
            # ringbuffer must have grown or something...
            available = 0
            self.old_index = index

        # if we have enough data to add a frequency column in the time-frequency plane, compute it
        needed = 2**self.Ndec
        realizable = int(np.floor(available / needed))

        if realizable > 0:
            for i in range(realizable):
                floatdata = self.audiobuffer.data_indexed(
                    self.old_index, needed)

                # first channel
                y0 = floatdata[0, :]

                y0_squared = y0**2

                # subsample
                y0_squared_dec = self.subsampler.push(y0_squared)

                # exponential smoothing
                if self.level is None:
                    self.level = y0_squared_dec[0]
                else:
                    self.level = self.alpha * y0_squared_dec[0] + (
                        1. - self.alpha) * self.level

                self.level_rms = 10. * np.log10(max(self.level, 1e-150))

                l = np.array([self.level_rms])
                l.shape = (1, 1)

                self.ringbuffer.push(l)

                self.old_index += int(needed)

            self.time = np.arange(
                self.length_samples) / self.subsampled_sampling_rate

            levels = self.ringbuffer.data(self.length_samples)

            self.PlotZoneUp.setdata(self.time / 60., levels[0, :])

    # method
    def canvasUpdate(self):
        # nothing to do here
        return

    def setmin(self, value):
        self.level_min = value
        self.PlotZoneUp.setverticalrange(self.level_min, self.level_max)

    def setmax(self, value):
        self.level_max = value
        self.PlotZoneUp.setverticalrange(self.level_min, self.level_max)

    # slot
    def settings_called(self, checked):
        self.settings_dialog.show()

    # method
    def saveState(self, settings):
        self.settings_dialog.saveState(settings)

    # method
    def restoreState(self, settings):
        self.settings_dialog.restoreState(settings)