summaryrefslogtreecommitdiff
path: root/gr-qtgui/python/qtgui/azelplot.py
blob: e091f7cc0799133342241bc1c451cecb7bd1db4d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2020 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
#

from PyQt5 import QtWidgets
import numpy as np
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import math

from gnuradio import gr
import pmt


class AzElPlot(gr.sync_block, FigureCanvas):
    """
    This block creates a polar plot with azimuth represented as the angle
    clockwise around the circle, and elevation represented as the radius.
    90 degrees elevation is center (directly overhead),
    while the horizon (0 degrees) is the outside circe.  Note that if an
    elevation < 0 is provided, the marker will turn to an open circle
    on the perimeter at the specified azimuth angle.
    """

    def __init__(self, lbl, backgroundColor, dotColor, Parent=None,
                 width=4, height=4, dpi=90):
        gr.sync_block.__init__(self, name="MsgPushButton", in_sig=None,
                               out_sig=None)

        self.lbl = lbl

        self.message_port_register_in(pmt.intern("azel"))
        self.set_msg_handler(pmt.intern("azel"), self.msgHandler)

        self.dotColor = dotColor
        self.backgroundColor = backgroundColor
        self.scaleColor = 'black'
        if (self.backgroundColor == 'black'):
            self.scaleColor = 'white'

        self.fig = Figure(figsize=(width, height), dpi=dpi)
        self.fig.patch.set_facecolor(self.backgroundColor)

        self.axes = self.fig.add_subplot(
            111, polar=True, facecolor=self.backgroundColor)

        # Create an "invisible" line at 90 to set the max for the plot
        self.axes.plot(np.linspace(0, 2 * np.pi, 90), np.ones(90) * 90, color=self.scaleColor,
                       linestyle='')

        # Plot line: Initialize out to 90 and blank
        radius = 90
        self.blackline = self.axes.plot(np.linspace(0, 2 * np.pi, 90), np.ones(90) * radius,
                                        color=self.scaleColor, linestyle='-')
        self.reddot = None

        # Rotate zero up
        self.axes.set_theta_zero_location("N")

        # Set limits:
        self.axes.set_rlim(0, 90)

        self.axes.set_yticklabels([], color=self.scaleColor)
        self.axes.set_xticklabels(['0', '315', '270', '225', '180', '135', '90', '45'],
                                  color=self.scaleColor)

        FigureCanvas.__init__(self, self.fig)
        self.setParent(Parent)

        self.title = self.fig.suptitle(
            self.lbl, fontsize=8, fontweight='bold', color='black')

        FigureCanvas.setSizePolicy(self,
                                   QtWidgets.QSizePolicy.Expanding,
                                   QtWidgets.QSizePolicy.Expanding)

        self.setMinimumSize(240, 230)
        FigureCanvas.updateGeometry(self)

    def msgHandler(self, msg):
        new_val = None

        try:
            new_val = pmt.to_python(pmt.car(msg))
            if new_val is not None:
                if type(new_val) == dict:
                    if 'az' in new_val and 'el' in new_val:
                        self.updateData(
                            float(new_val['az']), float(new_val['el']))
                    else:
                        gr.log.error(
                            "az and el keys were not found in the dictionary.")
                else:
                    gr.log.error("Value received was not a dictionary.  Expecting a dictionary "
                                 "in the car message component with az and el keys.")
            else:
                gr.log.error("The CAR section of the inbound message was None.  "
                             "This part should contain the dictionary with 'az' and 'el' float keys.")
        except Exception as e:
            gr.log.error(
                "[AzElPlot] Error with message conversion: %s" % str(e))
            if new_val is not None:
                gr.log.error(str(new_val))

    def updateData(self, azimuth, elevation):
        if self.reddot is not None:
            self.reddot.pop(0).remove()

        # Plot is angle, radius where angle is in radians

        if (elevation > 0):
            # Need to reverse elevation.  90 degrees is center (directly overhead),
            # and 90 degrees is horizon.
            if (elevation > 90.0):
                elevation = 90.0

            convertedElevation = 90.0 - elevation
            # Note: +azimuth for the plot is measured counter-clockwise, so need to reverse it.
            self.reddot = self.axes.plot(-azimuth * math.pi / 180.0, convertedElevation, self.dotColor,
                                         markersize=8)
        else:
            # It's below the horizon.  Show an open circle at the perimeter
            elevation = 0.0
            self.reddot = self.axes.plot(-azimuth * math.pi / 180.0, 89.0, self.dotColor,
                                         markerfacecolor="None", markersize=16, fillstyle=None)

        self.draw()