Ejemplo n.º 1
0
class VelocityPanel(wx.Panel):
    def _init_ctrls(self, prnt):
        # generated method, don't edit
        wx.Panel.__init__(self, id=wxID_RIGHTPANEL, name='VelocityPanel',
              parent=prnt, pos=wx.Point(8, 416), size=wx.Size(1000, 250),
              style=wx.TAB_TRAVERSAL)
        self.SetClientSize(wx.Size(1000, 250))
        self.SetBackgroundColour(wx.Colour(0, 0, 255))
        self.Bind(wx.EVT_PAINT, self.OnPolarPanelPaint)

    def __init__(self, parent, id, pos, size, style, name):
        self._init_ctrls(parent)
        
        self.Times = list()
        self.VelocitiesX = list()
        self.VelocitiesY = list()
        self.Velocities = list()
        
        self._initFigure()
        
        ##now finally create the actual plot
        self.x_axes = self.fig.add_subplot(131)                 ## the x velocity
        self.y_axes = self.fig.add_subplot(132)                 ## the y velocity
        self.m_axes = self.fig.add_subplot(133)                 ## the magnitude of the velocity
        #self.axes = self.fig.add_axes((0.1,0.1,0.85,0.8))        ##left,bottom,width,height
        
        self.x_plot = self.x_axes.plot([0,1],[0,1],'b', animated=True)
        self.y_plot = self.y_axes.plot([0,1],[0,1],'b', animated=True)
        self.m_plot = self.m_axes.plot([0,1],[0,1],'r', animated=True)
        
        self.x_axes.set_title('X', fontsize='10')
        self.y_axes.set_title('Y', fontsize='10')
        self.m_axes.set_title('M', fontsize='10')
        self.x_axes.set_ylabel('Velocity (cm/s)', fontsize='10')
        #self.axes.set_ylabel('x (cm)', fontsize='10')

        ##plot formatting
        self._formatAxes(self.x_axes)
        self._formatAxes(self.y_axes)
        self._formatAxes(self.m_axes)
        #self.axes.set_title('Velocity', fontsize='10')
        
        self.canvas.draw()
        self.canvas.gui_repaint()
        
        # save the clean slate background -- everything but the animated line
        # is drawn and saved in the pixel buffer background
        self.background = self.canvas.copy_from_bbox(self.fig.bbox)
        
    def _initFigure(self):
        ##Create a matplotlib figure/canvas in this panel
        ##the background colour will be the same as the panel
        ##the size will also be the same as the panel
        ##calculate size in inches
        pixels_width,pixels_height = self.GetSizeTuple()
        self.dpi = 96.0
        inches_width = pixels_width/self.dpi
        inches_height = pixels_height/self.dpi
        
        ##calculate colour in RGB 0 to 1
        colour = self.GetBackgroundColour()
        self.fig = Figure(figsize=(inches_width,inches_height), dpi = self.dpi\
            ,facecolor=(colour.Red()/255.0, colour.Green()/255.0, colour.Blue()/255.0)\
            ,edgecolor=(colour.Red()/255.0, colour.Green()/255.0, colour.Blue()/255.0))    

        self.canvas = FigureCanvasWxAgg(self, -1, self.fig)

        self.fig.subplots_adjust(left=0.05,right=0.95,wspace=0.08)

        ##now put everything in a sizer
        sizer = wx.BoxSizer(wx.VERTICAL)
        # This way of adding to sizer allows resizing
        sizer.Add(self.canvas, 1, wx.LEFT|wx.TOP|wx.GROW)
        self.SetSizer(sizer)
        self.Fit()
        
    def _formatAxes(self, axes):
        """ """
        ticks = numpy.arange(-25, 25+5, 5)
        labels = [str(tick) for tick in ticks]              # velocity
        axes.set_yticks(ticks)
        axes.set_yticklabels(labels, fontsize=8)
        ticks = numpy.arange(0, 10+1.0, 1.0)                # time
        labels = [str(tick) for tick in ticks]              
        axes.set_xticks(ticks)
        axes.set_xticklabels(labels,fontsize=8)
        
        #if axes == self.m_axes:
        #    self.axes.set_xlabel('time (s)', fontsize='10')
        #self.axes.set_ylabel(' (mm)', fontsize='10')
        
        
    
    def updateData(self, velx, vely, vel):
        """updateData. Updates the data that this panel is displaying.
        """
        self.VelocitiesX.append(velx)
        self.VelocitiesY.append(vely)
        self.Velocities.append(vel)
        timenow = time.time()
        self.Times.append(timenow)
        if timenow - self.Times[0] > 10:
            del self.Times[0]
            del self.VelocitiesX[0]
            del self.VelocitiesY[0]
            del self.Velocities[0]
        
        self.x_plot[0].set_data(numpy.array(self.Times) - self.Times[0], self.VelocitiesX)
        self.y_plot[0].set_data(numpy.array(self.Times) - self.Times[0], self.VelocitiesY)
        self.m_plot[0].set_data(numpy.array(self.Times) - self.Times[0], self.Velocities)
        
        # restore the clean slate background
        self.canvas.restore_region(self.background)
        # just draw the animated artist
        self.fig.draw_artist(self.x_plot[0])
        self.fig.draw_artist(self.y_plot[0])
        self.fig.draw_artist(self.m_plot[0])
        # just redraw the axes rectangle
        self.canvas.blit(self.fig.bbox)

    def OnPolarPanelPaint(self, event):
        pass
Ejemplo n.º 2
0
class VelocityPanel(wx.Panel):
    def _init_ctrls(self, prnt):
        # generated method, don't edit
        wx.Panel.__init__(self,
                          id=wxID_RIGHTPANEL,
                          name='VelocityPanel',
                          parent=prnt,
                          pos=wx.Point(8, 416),
                          size=wx.Size(1000, 250),
                          style=wx.TAB_TRAVERSAL)
        self.SetClientSize(wx.Size(1000, 250))
        self.SetBackgroundColour(wx.Colour(0, 0, 255))
        self.Bind(wx.EVT_PAINT, self.OnPolarPanelPaint)

    def __init__(self, parent, id, pos, size, style, name):
        self._init_ctrls(parent)

        self.Times = list()
        self.VelocitiesX = list()
        self.VelocitiesY = list()
        self.Velocities = list()

        self._initFigure()

        ##now finally create the actual plot
        self.x_axes = self.fig.add_subplot(131)  ## the x velocity
        self.y_axes = self.fig.add_subplot(132)  ## the y velocity
        self.m_axes = self.fig.add_subplot(
            133)  ## the magnitude of the velocity
        #self.axes = self.fig.add_axes((0.1,0.1,0.85,0.8))        ##left,bottom,width,height

        self.x_plot = self.x_axes.plot([0, 1], [0, 1], 'b', animated=True)
        self.y_plot = self.y_axes.plot([0, 1], [0, 1], 'b', animated=True)
        self.m_plot = self.m_axes.plot([0, 1], [0, 1], 'r', animated=True)

        self.x_axes.set_title('X', fontsize='10')
        self.y_axes.set_title('Y', fontsize='10')
        self.m_axes.set_title('M', fontsize='10')
        self.x_axes.set_ylabel('Velocity (cm/s)', fontsize='10')
        #self.axes.set_ylabel('x (cm)', fontsize='10')

        ##plot formatting
        self._formatAxes(self.x_axes)
        self._formatAxes(self.y_axes)
        self._formatAxes(self.m_axes)
        #self.axes.set_title('Velocity', fontsize='10')

        self.canvas.draw()
        self.canvas.gui_repaint()

        # save the clean slate background -- everything but the animated line
        # is drawn and saved in the pixel buffer background
        self.background = self.canvas.copy_from_bbox(self.fig.bbox)

    def _initFigure(self):
        ##Create a matplotlib figure/canvas in this panel
        ##the background colour will be the same as the panel
        ##the size will also be the same as the panel
        ##calculate size in inches
        pixels_width, pixels_height = self.GetSizeTuple()
        self.dpi = 96.0
        inches_width = pixels_width / self.dpi
        inches_height = pixels_height / self.dpi

        ##calculate colour in RGB 0 to 1
        colour = self.GetBackgroundColour()
        self.fig = Figure(figsize=(inches_width,inches_height), dpi = self.dpi\
            ,facecolor=(colour.Red()/255.0, colour.Green()/255.0, colour.Blue()/255.0)\
            ,edgecolor=(colour.Red()/255.0, colour.Green()/255.0, colour.Blue()/255.0))

        self.canvas = FigureCanvasWxAgg(self, -1, self.fig)

        self.fig.subplots_adjust(left=0.05, right=0.95, wspace=0.08)

        ##now put everything in a sizer
        sizer = wx.BoxSizer(wx.VERTICAL)
        # This way of adding to sizer allows resizing
        sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
        self.SetSizer(sizer)
        self.Fit()

    def _formatAxes(self, axes):
        """ """
        ticks = numpy.arange(-25, 25 + 5, 5)
        labels = [str(tick) for tick in ticks]  # velocity
        axes.set_yticks(ticks)
        axes.set_yticklabels(labels, fontsize=8)
        ticks = numpy.arange(0, 10 + 1.0, 1.0)  # time
        labels = [str(tick) for tick in ticks]
        axes.set_xticks(ticks)
        axes.set_xticklabels(labels, fontsize=8)

        #if axes == self.m_axes:
        #    self.axes.set_xlabel('time (s)', fontsize='10')
        #self.axes.set_ylabel(' (mm)', fontsize='10')

    def updateData(self, velx, vely, vel):
        """updateData. Updates the data that this panel is displaying.
        """
        self.VelocitiesX.append(velx)
        self.VelocitiesY.append(vely)
        self.Velocities.append(vel)
        timenow = time.time()
        self.Times.append(timenow)
        if timenow - self.Times[0] > 10:
            del self.Times[0]
            del self.VelocitiesX[0]
            del self.VelocitiesY[0]
            del self.Velocities[0]

        self.x_plot[0].set_data(
            numpy.array(self.Times) - self.Times[0], self.VelocitiesX)
        self.y_plot[0].set_data(
            numpy.array(self.Times) - self.Times[0], self.VelocitiesY)
        self.m_plot[0].set_data(
            numpy.array(self.Times) - self.Times[0], self.Velocities)

        # restore the clean slate background
        self.canvas.restore_region(self.background)
        # just draw the animated artist
        self.fig.draw_artist(self.x_plot[0])
        self.fig.draw_artist(self.y_plot[0])
        self.fig.draw_artist(self.m_plot[0])
        # just redraw the axes rectangle
        self.canvas.blit(self.fig.bbox)

    def OnPolarPanelPaint(self, event):
        pass
class AppForm(QMainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.setWindowTitle('Serial Port Real Time Plotter')
        self.ser = None
        self.create_main_frame()
        self.create_status_bar()
        self.serialport_connected = False
        self.textbox.setText('0.0')
        self.xdata = list(range(-N, 0, 1))
        self.ydata_ch1 = [0 for i in range(N)]
        self.ydata_ch2 = [0 for i in range(N)]
        self.ydata_ch3 = [0 for i in range(N)]
        self.ydata_ch4 = [0 for i in range(N)]
        self.ydata_ch5 = [0 for i in range(N)]
        self.ydata_ch6 = [0 for i in range(N)]
        self.ydata_ch1_cont = []
        self.ydata_ch2_cont = []
        self.ydata_ch3_cont = []
        self.ydata_ch4_cont = []
        self.ydata_ch5_cont = []
        self.ydata_ch6_cont = []

        os.chdir("./outputs")
        self.setFileName()

        self.ydata_raw = []
        self.axes.clear()
        self.axes.grid(True)
        self.axes.set_xlim([-N, 0])
        self.axes.set_ylim([0, 1.25])
        self.plot_refs = self.axes.plot(self.xdata, self.ydata_ch1, 'r',
                                        self.xdata, self.ydata_ch2, 'g',
                                        self.xdata, self.ydata_ch3, 'b',
                                        self.xdata, self.ydata_ch4, 'y',
                                        self.xdata, self.ydata_ch5, 'k',
                                        self.xdata, self.ydata_ch6, 'r')
        #self.axes.legend(['Channel 1','Channel 2','Channel 3','Channel 4'],loc='upper center', bbox_to_anchor=(0.5, -0.05), ncol=4)
        self.canvas.draw()

        self.background = self.fig.canvas.copy_from_bbox(self.axes.bbox)

        self.timer = QTimer()
        self.timer.setInterval(10)
        self.timer.timeout.connect(self.update_plot)

        self.timer_send = QTimer()
        self.timer_send.setInterval(100)
        self.timer_send.timeout.connect(self.send_data)

    def setFileName(self):
        self.files = []
        for file in glob.glob("*.mat"):
            self.files.append(file)

        i = 0
        while True:
            self.filename = "out_" + time.strftime(
                "%Y-%m-%d_%H-%M-%S") + "_" + str(i) + ".mat"
            if self.filename not in self.files:
                break
            i += 1

    def closeEvent(self, event):
        reply = QMessageBox.question(
            self, 'Window Close', 'Are you sure you want to close the window?',
            QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

        if reply == QMessageBox.Yes:
            if self.ser is not None:
                self.ser.close()
            event.accept()
        else:
            event.ignore()

    def create_main_frame(self):
        self.main_frame = QWidget()

        self.dpi = 100
        self.fig = Figure((10.0, 6.0), dpi=self.dpi, tight_layout=True)
        self.canvas = FigureCanvas(self.fig)
        self.canvas.setParent(self.main_frame)

        self.axes = self.fig.add_subplot(111)
        #self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame)

        self.textbox = QLineEdit()
        self.textbox.setMinimumWidth(100)

        self.set_button = QPushButton("&Set")
        self.set_button.clicked.connect(self.set_setpoint)

        self.show_ch1 = QCheckBox("Show &Channel1")
        self.show_ch1.setChecked(True)
        self.show_ch2 = QCheckBox("Show &Channel2")
        self.show_ch2.setChecked(True)
        self.show_ch3 = QCheckBox("Show &Channel3")
        self.show_ch3.setChecked(True)
        self.show_ch4 = QCheckBox("Show &Channel4")
        self.show_ch4.setChecked(True)
        self.show_ch5 = QCheckBox("Show &Channel4")
        self.show_ch5.setChecked(True)
        self.show_ch6 = QCheckBox("Show &Channel4")
        self.show_ch6.setChecked(True)
        slider_label = QLabel('Setpoint:')
        self.slider = QSlider(Qt.Horizontal)
        self.slider.setRange(0, 65535)
        self.slider.setValue(0)
        self.slider.setTracking(True)
        self.slider.setTickPosition(QSlider.TicksBothSides)

        hbox = QHBoxLayout()

        for w in [self.textbox, self.set_button, slider_label, self.slider]:
            hbox.addWidget(w)
            hbox.setAlignment(w, Qt.AlignVCenter)

        hbox2 = QHBoxLayout()
        for w in [self.show_ch1, self.show_ch2, self.show_ch3, self.show_ch4]:
            hbox2.addWidget(w)
            hbox2.setAlignment(w, Qt.AlignVCenter)
        vbox = QVBoxLayout()
        vbox.addWidget(self.canvas)
        #vbox.addWidget(self.mpl_toolbar)
        vbox.addLayout(hbox2)
        vbox.addLayout(hbox)

        hbox3 = QHBoxLayout()
        self.save_mat_button = QPushButton("&Save .mat")
        self.save_mat_button.clicked.connect(self.save_mat)

        self.start_button = QPushButton("&Start")
        self.start_button.clicked.connect(self.connect)

        hbox3.addWidget(self.start_button)
        hbox3.addWidget(self.save_mat_button)
        hbox3.setAlignment(w, Qt.AlignVCenter)
        vbox.addLayout(hbox3)

        hbox4 = QHBoxLayout()
        self.combo_box_ports = QComboBox()
        self.button_update_ports = QPushButton("&Update Ports")
        self.button_update_ports.clicked.connect(self.update_ports)
        hbox4.addWidget(self.combo_box_ports)
        hbox4.addWidget(self.button_update_ports)
        vbox.addLayout(hbox4)
        self.main_frame.setLayout(vbox)
        self.setCentralWidget(self.main_frame)
        self.info_message('Press Start to start plotting')

    def update_ports(self):
        self.info_message('Updating Ports...')
        ports = serial.tools.list_ports.comports()
        self.combo_box_ports.clear()
        for port, desc, hwid in sorted(ports):
            self.combo_box_ports.addItem(str(port) + ": " + str(desc))
        self.success_message('Updated Ports')

    def set_setpoint(self):
        try:
            self.slider.setValue(int(float(self.textbox.text()) * 65565))
        except:
            self.error_message('Parsing error for set value')

    def create_status_bar(self):
        self.status_text = QLabel("")
        self.statusBar().addWidget(self.status_text, 1)

    def add_actions(self, target, actions):
        for action in actions:
            if action is None:
                target.addSeparator()
            else:
                target.addAction(action)

    def create_action(self,
                      text,
                      slot=None,
                      shortcut=None,
                      icon=None,
                      tip=None,
                      checkable=False):
        action = QAction(text, self)
        if icon is not None:
            action.setIcon(QIcon(":/%s.png" % icon))
        if shortcut is not None:
            action.setShortcut(shortcut)
        if tip is not None:
            action.setToolTip(tip)
            action.setStatusTip(tip)
        if slot is not None:
            action.triggered.connect(slot)
        if checkable:
            action.setCheckable(True)
        return action

    def connect(self):
        if 'COM' in str(self.combo_box_ports.currentText()):
            com_port = str(self.combo_box_ports.currentText()).split(':')[0]
        else:
            self.error_message('Select COM Port')
            return

        if self.serialport_connected is False:
            try:
                self.ser = serial.Serial(com_port, 115200, timeout=0.2)
                if self.ser.isOpen() == False:
                    self.ser.open()
                self.success_message('Connected to serial port')
                self.start_button.setText('&Stop')
                self.serialport_connected = True
                self.ydata_ch1 = [-1 for i in range(N)]
                self.ydata_ch2 = [-1 for i in range(N)]
                self.ydata_ch3 = [-1 for i in range(N)]
                self.ydata_ch4 = [-1 for i in range(N)]
                self.ydata_ch5 = [-1 for i in range(N)]
                self.ydata_ch6 = [-1 for i in range(N)]
                self.timer.start()
                self.timer_send.start()
            except:
                self.error_message('Connecting to serial port')
            self.setFileName()
        else:
            try:
                if self.ser.isOpen():
                    self.ser.close()
                self.info_message('Disconnected from serial port')
                self.start_button.setText('&Start')
                self.timer.stop()
                self.timer_send.stop()
                self.serialport_connected = False
            except:
                self.error_message('Disconnecting from serial port')

    def save_mat(self):
        try:
            mat_ch1 = np.array(self.ydata_ch1_cont)
            mat_ch2 = np.array(self.ydata_ch2_cont)
            mat_ch3 = np.array(self.ydata_ch3_cont)
            mat_ch4 = np.array(self.ydata_ch4_cont)
            mat_ch5 = np.array(self.ydata_ch5_cont)
            mat_ch6 = np.array(self.ydata_ch6_cont)
            mat_ch3_raw = np.array(self.ydata_raw)
            print(os.getcwd() + '\\' + self.filename)
            scipy.io.savemat(
                os.getcwd() + '\\' + self.filename, {
                    'channel1': mat_ch1,
                    'channel2': mat_ch2,
                    'channel3': mat_ch3,
                    'channel4': mat_ch4,
                    'channel5': mat_ch5,
                    'channel6': mat_ch6
                })
            self.success_message('Saved data to out.mat')
        except Exception as e:
            self.error_message('Saving Data ' + str(e))

    def error_message(self, message):
        self.statusBar().showMessage('ERROR: ' + str(message))
        self.statusBar().setStyleSheet(
            'QStatusBar { background-color : red; }')

    def info_message(self, message):
        self.statusBar().showMessage('INFO: ' + str(message))
        self.statusBar().setStyleSheet(
            'QStatusBar { background-color : yellow; }')

    def success_message(self, message):
        self.statusBar().showMessage('SUCCESS: ' + str(message))
        self.statusBar().setStyleSheet(
            'QStatusBar { background-color : green; }')

    def update_plot(self):
        #ser.flushInput()
        #print('new plot')
        if self.ser.isOpen() == False:
            return
        rawData = self.ser.read(15)
        if len(rawData) != 15:
            return
        # print(time.time())
        start = rawData[0]
        channel1 = int.from_bytes([rawData[1], rawData[2]],
                                  byteorder='little',
                                  signed=False) * 1.25 / 65536
        channel2 = int.from_bytes([rawData[3], rawData[4]],
                                  byteorder='little',
                                  signed=False) * 1.25 / 65536
        channel3 = int.from_bytes([rawData[5], rawData[6]],
                                  byteorder='little',
                                  signed=False) * 1.25 / 65536
        channel4 = int.from_bytes([rawData[7], rawData[8]],
                                  byteorder='little',
                                  signed=False) * 150 / 65536
        channel5 = int.from_bytes([rawData[9], rawData[10]],
                                  byteorder='little',
                                  signed=False) * 150 / 65536
        channel6 = int.from_bytes([rawData[11], rawData[12]],
                                  byteorder='little',
                                  signed=False) * 150 / 65536
        channel3_raw = int.from_bytes([rawData[5], rawData[6]],
                                      byteorder='little',
                                      signed=False)
        self.ydata_raw.append(channel3_raw)
        end = rawData[13]
        if start != 2 and end != 3:
            print('ERROR: Wrong frame')
            return
        print(
            int.from_bytes(
                [rawData[1], rawData[2]], byteorder='little', signed=False) *
            1.25 / 65536)
        print(
            int.from_bytes(
                [rawData[3], rawData[4]], byteorder='little', signed=False) *
            1.25 / 65536)
        print(
            int.from_bytes(
                [rawData[5], rawData[6]], byteorder='little', signed=False) *
            1.25 / 65536)
        print(
            int.from_bytes(
                [rawData[7], rawData[8]], byteorder='little', signed=False) *
            150 / 65536)
        print(
            int.from_bytes(
                [rawData[9], rawData[10]], byteorder='little', signed=False) *
            150 / 65536)
        print(
            int.from_bytes(
                [rawData[11], rawData[12]], byteorder='little', signed=False) *
            150 / 65536)
        # print(time.time())
        print('----')
        # Drop off the first y element, append a new one.
        self.ydata_ch1 = self.ydata_ch1[1:] + [channel1]
        self.ydata_ch2 = self.ydata_ch2[1:] + [channel2]
        self.ydata_ch3 = self.ydata_ch3[1:] + [channel3]
        self.ydata_ch4 = self.ydata_ch4[1:] + [channel4]
        self.ydata_ch5 = self.ydata_ch5[1:] + [channel5]
        self.ydata_ch6 = self.ydata_ch6[1:] + [channel6]

        self.ydata_ch1_cont = self.ydata_ch1_cont + [channel1]
        self.ydata_ch2_cont = self.ydata_ch2_cont + [channel2]
        self.ydata_ch3_cont = self.ydata_ch3_cont + [channel3]
        self.ydata_ch4_cont = self.ydata_ch4_cont + [channel4]
        self.ydata_ch5_cont = self.ydata_ch5_cont + [channel5]
        self.ydata_ch6_cont = self.ydata_ch6_cont + [channel6]
        #print(time.time())
        if (self.show_ch1.isChecked()):
            self.plot_refs[0].set_visible(True)
            self.plot_refs[0].set_ydata(self.ydata_ch1)
        else:
            self.plot_refs[0].set_visible(False)

        if (self.show_ch2.isChecked()):
            self.plot_refs[1].set_visible(True)
            self.plot_refs[1].set_ydata(self.ydata_ch2)
        else:
            self.plot_refs[1].set_visible(False)

        if (self.show_ch3.isChecked()):
            self.plot_refs[2].set_visible(True)
            self.plot_refs[2].set_ydata(self.ydata_ch3)
        else:
            self.plot_refs[2].set_visible(False)

        if (self.show_ch4.isChecked()):
            self.plot_refs[3].set_visible(True)
            self.plot_refs[3].set_ydata(self.ydata_ch4)
        else:
            self.plot_refs[3].set_visible(False)

        if (self.show_ch5.isChecked()):
            self.plot_refs[4].set_visible(True)
            self.plot_refs[4].set_ydata(self.ydata_ch5)
        else:
            self.plot_refs[4].set_visible(False)

        if (self.show_ch6.isChecked()):
            self.plot_refs[5].set_visible(True)
            self.plot_refs[5].set_ydata(self.ydata_ch6)
        else:
            self.plot_refs[5].set_visible(False)

        self.fig.canvas.restore_region(self.background)
        self.fig.draw_artist(self.plot_refs[0])
        self.fig.draw_artist(self.plot_refs[1])
        self.fig.draw_artist(self.plot_refs[2])
        self.fig.draw_artist(self.plot_refs[3])
        self.fig.draw_artist(self.plot_refs[4])
        self.fig.draw_artist(self.plot_refs[5])
        self.fig.canvas.update()
        #self.fig.canvas.blit()
        self.fig.canvas.flush_events()
        #print(time.time())
        #print('-------')
        #self.ser.reset_input_buffer()

    def send_data(self):
        if self.ser.isOpen() == False:
            return
        data = bytearray(ctypes.c_uint16(self.slider.value()))
        send_array = bytearray(ctypes.c_uint8(2)) + data + bytearray(
            ctypes.c_uint8(3))
        cs = ctypes.c_uint8(0)
        for byte in data:
            cs = ctypes.c_uint8(cs.value + byte)
        send_array = send_array + cs
        self.ser.write(send_array)
Ejemplo n.º 4
0
class wxpygui_frame(wx.Frame):
    """The main gui frame."""
    def __init__(self, tb):
        wx.Frame.__init__(self, parent=None, id=-1, title="gr-analyzer")
        self.tb = tb

        self.min_power = -120  # dBm
        self.max_power = 0  # dBm

        self.init_mpl_canvas()
        self.x = None  # set by configure_mpl_plot

        # Setup a threshold level at None
        self.threshold = threshold.threshold(self, None)

        # Init markers (visible=False)
        self.mkr1 = marker.marker(self, 1, '#00FF00',
                                  'd')  # thin green diamond
        self.mkr2 = marker.marker(self, 2, '#00FF00',
                                  'd')  # thin green diamond

        # init control boxes
        self.gain_ctrls = gain.ctrls(self)
        self.threshold_ctrls = threshold.ctrls(self)
        self.mkr1_ctrls = marker.mkr1_ctrls(self)
        self.mkr2_ctrls = marker.mkr2_ctrls(self)
        self.res_ctrls = resolution.ctrls(self)
        self.windowfn_ctrls = window.ctrls(self)
        self.lo_offset_ctrls = lotuning.ctrls(self)
        self.nframes_ctrls = nframes.ctrls(self)
        self.tune_delay_ctrls = tune_delay.ctrls(self)
        self.frequency_ctrls = frequency.ctrls(self)
        self.span_ctrls = span.ctrls(self)
        self.trigger_ctrls = trigger.ctrls(self)
        self.power_ctrls = power.ctrls(self)
        self.export_ctrls = export.ctrls(self)
        self.detector_ctrls = detector.ctrls(self)
        self.scale_ctrls = scale.ctrls(self)

        self.set_layout()

        self.logger = logging.getLogger('gr-analyzer.wxpygui_frame')

        # gui event handlers
        self.Bind(wx.EVT_CLOSE, self.close)
        self.Bind(wx.EVT_IDLE, self.idle_notifier)

        self.canvas.mpl_connect('button_press_event', self.on_mousedown)
        self.canvas.mpl_connect('button_release_event', self.on_mouseup)

        self.plot_background = None

        # Used to peak search within range
        self.span = None  # the actual matplotlib patch
        self.span_left = None  # left bound x coordinate
        self.span_right = None  # right bound x coordinate

        self.last_click_evt = None

        self.closed = False

        # Used to increment file numbers
        self.fft_data_export_counter = 0
        self.time_data_export_counter = 0

        ####################
        # GUI Sizers/Layout
        ####################

    def set_layout(self):
        """Setup frame layout and sizers"""
        # front panel to hold plot and control stack side-by-side
        frontpanel = wx.BoxSizer(wx.HORIZONTAL)

        # control stack to hold control clusters vertically
        controlstack = wx.BoxSizer(wx.VERTICAL)

        # first cluster - usrp state

        usrpstate_outline = wx.StaticBox(self, wx.ID_ANY, "USRP State")
        usrpstate_cluster = wx.StaticBoxSizer(usrpstate_outline, wx.HORIZONTAL)

        usrpstate_row1 = wx.BoxSizer(wx.HORIZONTAL)
        usrpstate_row1.Add(self.trigger_ctrls.layout, flag=wx.ALL, border=5)
        usrpstate_row1.Add(self.detector_ctrls.layout, flag=wx.ALL, border=5)
        usrpstate_row1.Add(self.gain_ctrls.layout, flag=wx.ALL, border=5)
        usrpstate_row1.Add(self.lo_offset_ctrls.layout, flag=wx.ALL, border=5)

        usrpstate_row2 = wx.BoxSizer(wx.HORIZONTAL)
        usrpstate_row2.Add(
            self.frequency_ctrls.layout,
            proportion=1,
            flag=wx.ALL,  #|wx.EXPAND,
            border=5)
        usrpstate_row2.Add(
            self.span_ctrls.layout,
            proportion=1,
            flag=wx.ALL,  #|wx.EXPAND,
            border=5)
        usrpstate_row2.Add(
            self.scale_ctrls.layout,
            proportion=1,
            flag=wx.ALL,  #|wx.EXPAND,
            border=5)

        usrpstate_col1 = wx.BoxSizer(wx.VERTICAL)
        usrpstate_col1.Add(usrpstate_row1)
        usrpstate_col1.Add(usrpstate_row2, flag=wx.EXPAND)

        usrpstate_col2 = wx.BoxSizer(wx.VERTICAL)

        # col 1
        usrpstate_cluster.Add(usrpstate_col1)
        # col 2
        usrpstate_cluster.Add(usrpstate_col2)

        # second cluster - display controls

        display_outline = wx.StaticBox(self, wx.ID_ANY, "Display")
        display_cluster = wx.StaticBoxSizer(display_outline, wx.HORIZONTAL)

        nframesbox = wx.BoxSizer(wx.HORIZONTAL)
        nframesbox.Add(self.nframes_ctrls.layout,
                       proportion=1,
                       flag=wx.ALL,
                       border=5)
        nframesbox.Add(self.tune_delay_ctrls.layout,
                       proportion=1,
                       flag=wx.ALL,
                       border=5)

        display_col1 = wx.BoxSizer(wx.VERTICAL)
        display_col1.Add(self.res_ctrls.layout, flag=wx.ALL, border=5)
        display_col1.Add(nframesbox, flag=wx.EXPAND)

        display_col2 = wx.BoxSizer(wx.VERTICAL)
        display_col2.Add(self.windowfn_ctrls.layout, flag=wx.ALL, border=5)
        display_col2.Add(self.power_ctrls.layout,
                         flag=wx.ALL | wx.EXPAND,
                         border=5)

        # col 1
        display_cluster.Add(display_col1)
        # col 2
        display_cluster.Add(display_col2)

        # third cluster - data controls

        data_outline = wx.StaticBox(self, wx.ID_ANY, "Data")
        data_cluster = wx.StaticBoxSizer(data_outline, wx.HORIZONTAL)

        data_col3 = wx.BoxSizer(wx.VERTICAL)
        data_col3.Add(self.threshold_ctrls.layout)
        data_col3.Add(self.export_ctrls.layout)

        # col 1
        data_cluster.Add(self.mkr1_ctrls.layout, flag=wx.ALL, border=5)
        # col 2
        data_cluster.Add(self.mkr2_ctrls.layout, flag=wx.ALL, border=5)
        # col 3
        data_cluster.Add(data_col3, flag=wx.ALL, border=5)

        # put everything together

        # Add control clusters vertically to control stack
        controlstack.Add(usrpstate_cluster,
                         flag=wx.EXPAND | wx.LEFT | wx.RIGHT,
                         border=5)
        controlstack.Add(display_cluster,
                         flag=wx.EXPAND | wx.LEFT | wx.RIGHT,
                         border=5)
        controlstack.Add(data_cluster,
                         flag=wx.EXPAND | wx.LEFT | wx.RIGHT,
                         border=5)

        # Add plot and control stack side-by-side on the front panel
        frontpanel.Add(self.plot, flag=wx.ALIGN_CENTER_VERTICAL)
        frontpanel.Add(controlstack, flag=wx.ALIGN_CENTER_VERTICAL)

        self.SetSizer(frontpanel)
        self.Fit()

    ####################
    # GUI Initialization
    ####################

    def init_mpl_canvas(self):
        """Initialize a matplotlib plot."""
        self.plot = wx.Panel(self, wx.ID_ANY, size=(700, 600))
        self.figure = Figure(figsize=(7, 6), dpi=100)
        self.figure.subplots_adjust(right=.95)
        self.canvas = FigureCanvas(self.plot, -1, self.figure)

    def configure_mpl_plot(self, y, adjust_freq_range=True):
        """Configure or reconfigure the matplotlib plot"""
        maxbin = self.tb.cfg.max_plotted_bin
        self.x = self.tb.cfg.bin_freqs[:maxbin]
        # self.line in a numpy array in the form [[x-vals], [y-vals]], where
        # x-vals are bin center frequencies and y-vals are powers. So once we
        # initialize a power at each freq, just find the index of the
        # frequency that a measurement was taken at, and insert it into the
        # corresponding index in y-vals.
        if adjust_freq_range:
            len_x = len(self.x)
            len_y = len(y)
            if len_x != len_y:
                # There's a race condition when in continuous mode and
                # a frequency range-adjusting parameter (like span) is
                # changed, so we sometimes get updated x-values before
                # updated y-values. Since a) it only affects
                # continuous mode and b) the user has requested a
                # different view, there's no harm in simply dropping
                # the old data and re-calling configure_mpl_plot next frame.
                # Still - this is a workaround.
                # The most "correct" solution would be to have
                # controller_c tag the first sample propagated after
                # flowgraph starts, which plotter_f would look for and
                # use to trigger plot reconfig.
                self.logger.debug("data mismatch - frame dropped")
                return False

            if hasattr(self, 'mkr1'):
                self.mkr1.unplot()
            if hasattr(self, 'mkr2'):
                self.mkr2.unplot()
            if hasattr(self, 'line'):
                self.line.remove()

            # initialize a line
            self.line, = self.subplot.plot(self.x,
                                           y,
                                           animated=True,
                                           antialiased=True,
                                           linestyle='-',
                                           color='b')

        self.canvas.draw()
        self._update_background()

        return True

    def format_axis(self):
        """Set the formatting of the plot axes."""
        if hasattr(self, "subplot"):
            ax = self.subplot
        else:
            ax = self.figure.add_subplot(111)

        xaxis_formatter = FuncFormatter(self.format_mhz)
        ax.xaxis.set_major_formatter(xaxis_formatter)
        ax.set_xlabel("Frequency (MHz)")
        ax.set_ylabel("Power (dBm)")
        cf = self.tb.cfg.center_freq
        lowest_xtick = cf - (self.tb.cfg.span / 2)
        highest_xtick = cf + (self.tb.cfg.span / 2)
        ax.set_xlim(lowest_xtick - 1e6, highest_xtick + 1e6)
        ax.set_ylim(self.min_power + 1, self.max_power - 1)
        xticks = np.linspace(lowest_xtick, highest_xtick, 5, endpoint=True)
        ax.set_xticks(xticks)
        ax.set_yticks(np.arange(self.min_power, self.max_power, 10))
        ax.grid(color='.90', linestyle='-', linewidth=1)
        ax.set_title("Power Spectrum")

        self.subplot = ax
        self.canvas.draw()
        self._update_background()

    @staticmethod
    def format_mhz(x, pos):
        """Format x ticks (in Hz) to MHz with 0 decimal places."""
        return "{:.1f}".format(x / float(1e6))

    ####################
    # Plotting functions
    ####################

    def update_plot(self, y, redraw_plot, keep_alive):
        """Update the plot."""

        if redraw_plot:
            #assert not keep_alive
            self.logger.debug("Reconfiguring matplotlib plot")
            self.format_axis()
            if not self.configure_mpl_plot(y):
                # Got bad data, try again next frame
                self.tb.plot_iface.redraw_plot.set()
                return

        # Required for plot blitting
        self.canvas.restore_region(self.plot_background)

        if keep_alive:
            # Just keep markers and span alive after single run
            y = self.line.get_ydata()
            self.subplot.draw_artist(self.line)
        else:
            self._draw_line(y)
            self._check_threshold(y)

        self._draw_span()
        self._draw_threshold()
        self._draw_markers(y)

        # blit canvas
        self.canvas.blit(self.subplot.bbox)

    def _update_background(self):
        """Force update of the plot background."""
        self.plot_background = self.canvas.copy_from_bbox(self.subplot.bbox)

    def _draw_span(self):
        """Draw a span to bound the peak search functionality."""
        if self.span is not None:
            self.subplot.draw_artist(self.span)

    def _draw_threshold(self):
        """Draw a span to bound the peak search functionality."""
        if self.threshold.line is not None:
            self.subplot.draw_artist(self.threshold.line)

    def _draw_line(self, y):
        """Draw the latest chunk of line data."""
        self.line.set_ydata(y)
        self.subplot.draw_artist(self.line)

    def _draw_markers(self, y):
        """Draw power markers at a specific frequency."""
        # Update mkr1 if it's set
        if self.mkr1.freq is not None:
            m1bin = self.mkr1.bin_idx
            mkr1_power = y[m1bin]
            self.mkr1.point.set_ydata(mkr1_power)
            self.mkr1.point.set_visible(True)  # make visible
            self.mkr1.text_label.set_visible(True)
            self.mkr1.text_power.set_text("{:.1f} dBm".format(mkr1_power))
            self.mkr1.text_power.set_visible(True)

            # redraw
            self.subplot.draw_artist(self.mkr1.point)
            self.figure.draw_artist(self.mkr1.text_label)
            self.figure.draw_artist(self.mkr1.text_power)

        # Update mkr2 if it's set
        if self.mkr2.freq is not None:
            m2bin = self.mkr2.bin_idx
            mkr2_power = y[m2bin]
            self.mkr2.point.set_ydata(mkr2_power)
            self.mkr2.point.set_visible(True)  # make visible
            self.mkr2.text_label.set_visible(True)
            self.mkr2.text_power.set_text("{:.2f} dBm".format(mkr2_power))
            self.mkr2.text_power.set_visible(True)

            # Redraw
            self.subplot.draw_artist(self.mkr2.point)
            self.figure.draw_artist(self.mkr2.text_label)
            self.figure.draw_artist(self.mkr2.text_power)

    def _check_threshold(self, y):
        """Warn to stdout if the threshold level has been crossed."""
        # Update threshold
        # indices of where the y-value is greater than self.threshold.level
        if self.threshold.level is not None:
            overloads, = np.where(y > self.threshold.level)
            if overloads.size:  # is > 0
                self.log_threshold_overloads(overloads, y)

    def log_threshold_overloads(self, overloads, y):
        """Outout threshold violations to the logging system."""
        logheader = "============= Overload at {} ============="
        self.logger.warning(logheader.format(int(time.time())))
        logmsg = "Exceeded threshold {0:.0f}dBm ({1:.2f}dBm) at {2:.2f}MHz"
        for i in overloads:
            self.logger.warning(
                logmsg.format(self.threshold.level, y[i], self.x[i] / 1e6))

    ################
    # Event handlers
    ################

    def on_mousedown(self, event):
        """store event info for single click."""
        self.last_click_evt = event

    def on_mouseup(self, event):
        """Determine if mouse event was single click or click-and-drag."""
        if abs(self.last_click_evt.x - event.x) >= 5:
            # mouse was clicked and dragged more than 5 pxls, set a span
            self.span = self.subplot.axvspan(
                self.last_click_evt.xdata,
                event.xdata,
                color='red',
                alpha=0.2,
                # play nice with blitting:
                animated=True)

            xdata_points = [self.last_click_evt.xdata, event.xdata]
            # always set left bound as lower value
            self.span_left, self.span_right = sorted(xdata_points)
        else:
            # caught single click, clear span
            if self.subplot.patches:
                self.span.remove()
                self.subplot.patches = []
                self.span = self.span_left = self.span_right = None

    def idle_notifier(self, event):
        self.tb.plot_iface.set_gui_idle()

    def set_continuous_run(self, event):
        self.tb.pending_cfg.export_raw_time_data = False
        self.tb.pending_cfg.export_raw_fft_data = False
        self.tb.pending_cfg.continuous_run = True
        self.tb.set_continuous_run()

    def set_single_run(self, event):
        self.tb.pending_cfg.continuous_run = False
        self.tb.set_single_run()

    @staticmethod
    def _verify_data_dir(dir):
        if not os.path.exists(dir):
            os.makedirs(dir)

    def export_time_data(self, event):
        if (self.tb.single_run.is_set() or self.tb.continuous_run.is_set()):
            msg = "Can't export data while the flowgraph is running."
            msg += " Use \"single\" run mode."
            self.logger.error(msg)
            return
        else:
            if not self.tb.timedata_sink.data():
                self.logger.warn("No more time data to export")
                return

            # creates path string 'data/time_data_01_TIMESTAMP.dat'
            dirname = "data"
            self._verify_data_dir(dirname)
            fname = str.join(
                '', ('time_data_', str(self.time_data_export_counter).zfill(2),
                     '_', str(int(time.time())), '.dat'))

            wildcard = "Data and Settings files (*.dat; *.mat)|*.dat;*.mat"
            style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT
            filepath_dialog = wx.FileDialog(self,
                                            message="Save As",
                                            defaultDir=dirname,
                                            defaultFile=fname,
                                            wildcard=wildcard,
                                            style=style)

            if filepath_dialog.ShowModal() == wx.ID_CANCEL:
                return

            self.time_data_export_counter += 1
            filepath_dialog.Destroy()

            self.tb.save_time_data_to_file(filepath_dialog.GetPath())

    def export_fft_data(self, event):
        if self.tb.single_run.is_set() or self.tb.continuous_run.is_set():
            msg = "Can't export data while the flowgraph is running."
            msg += " Use \"single\" run mode."
            self.logger.error(msg)
            return
        else:
            if not self.tb.freqdata_sink.data():
                self.logger.warn("No more FFT data to export")
                return False

            # creates path string 'data/fft_data_01_TIMESTAMP.dat'
            dirname = "data"
            self._verify_data_dir(dirname)
            fname = str.join(
                '', ('fft_data_', str(self.fft_data_export_counter).zfill(2),
                     '_', str(int(time.time())), '.dat'))

            wildcard = "Data and Settings files (*.dat; *.mat)|*.dat;*.mat"
            style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT
            filepath_dialog = wx.FileDialog(self,
                                            message="Save As",
                                            defaultDir=dirname,
                                            defaultFile=fname,
                                            wildcard=wildcard,
                                            style=style)

            if filepath_dialog.ShowModal() == wx.ID_CANCEL:
                return

            self.fft_data_export_counter += 1
            filepath_dialog.Destroy()

            self.tb.save_freq_data_to_file(filepath_dialog.GetPath())

    def close(self, event):
        """Handle a closed gui window."""
        self.closed = True
        self.tb.stop()
        self.tb.wait()
        self.Destroy()
        self.logger.debug("GUI closing.")
Ejemplo n.º 5
0
class Frame(wx.Frame):
	def __init__(self):
		wx.Frame.__init__(self, None, -1)

		self.SetTitle('Blitting Example')

		# Figure layout.
		self.fig = Figure((5,4), 100)
		self.canvas = FigureCanvas(self, -1, self.fig)

		# BEGIN: Widget layout.
		self.wx_widgets = []
		sizer = wx.BoxSizer()
		sizer.Add(self.canvas, proportion=1, flag=wx.EXPAND)
		self.SetSizer(sizer)
		self.Fit()
		# END: Widget layout.

		# BEGIN: Plot initialization.
		self.axes = self.fig.add_subplot(111) 
		self.line1_ydata = [numpy.random.normal()]
		self.line2_ydata = [numpy.random.normal()]
		self.line1, = self.axes.plot(self.line1_ydata, color='b', animated=BLIT)
		self.line2, = self.axes.plot(self.line2_ydata, color='r', animated=BLIT)
		self.fps_label = self.axes.annotate("FPS: 0", (0.01, 0.95), xycoords="axes fraction", animated=BLIT)
		self.xaxis = self.axes.get_xaxis()
		self.yaxis = self.axes.get_yaxis()
		# END: Plot initialization.

		# BEGIN: Timers
		# IMPORTANT: Use wx.EVT_TIMER for redraw to avoid resource conflicts/crashes.
		self.redraw_timer = wx.Timer(self, wx.NewId())   # Plot refresh timer.
		self.redraw_timer.Start(50)
		wx.EVT_TIMER(self, self.redraw_timer.GetId(), self.redraw) 
		self.frames = 0
		self.start = time.time()
		# END: Timers

	###############################
	# BEGIN: Plot related methods #
	###############################
	def redraw(self, evt):
	
		# update the ydata, normally this would be done by SyncDB
		y1 = self.line1_ydata[-1] + numpy.random.normal()
		y2 = self.line2_ydata[-1] + numpy.random.normal()
		self.line1_ydata.append(y1)
		self.line2_ydata.append(y2)
		
		self.line1.set_data(range(len(self.line1_ydata)), self.line1_ydata)
		self.line2.set_data(range(len(self.line2_ydata)), self.line2_ydata)
		self.axes.set_ylim(min(self.line1_ydata + self.line2_ydata), max(self.line1_ydata + self.line2_ydata))
		self.axes.set_xlim(max(0, len(self.line1_ydata)-100), max(100, len(self.line1_ydata)))
		self.fps_label.set_text("FPS: %.2f" % (self.frames / float(time.time() - self.start)))
		
		if BLIT:
			self.fig.draw_artist(self.fig)
			self.axes.draw_artist(self.line1)
			self.axes.draw_artist(self.line2)
			self.axes.draw_artist(self.fps_label)
			self.fig.draw_artist(self.xaxis)
			self.fig.draw_artist(self.yaxis)
			self.canvas.blit(self.fig.bbox)
		else:
			self.canvas.draw()
		
		self.frames += 1