예제 #1
0
def atualizar_setor(setor: Wedge, abertura_min: float) -> [Wedge]:
    """Atualiza os valores de theta e opacidade do setor, movimentando-o"""

    global glob_multip_alpha

    # O valor de theta = valor atual + incremento (Se a soma não ultrapassar 360)
    # Se a soma ultrapassar 360, o valor será o resto da divisão (soma / 360)
    setor.set_theta1((setor.theta1 + glob_increm_ang) % 360)
    setor.set_theta2(setor.theta1 + abertura_min)

    # Se a opacidade do setor for maior que 0.4 ou for menor que o valor mínimo:
    # inverta de incremento para decremento ou vice-versa
    if setor.get_alpha() >= 0.4 or setor.get_alpha() < glob_incremento_alpha:
        glob_multip_alpha = (-1) * glob_multip_alpha

    setor.set_alpha(setor.get_alpha() +
                    glob_incremento_alpha * glob_multip_alpha)

    return [setor]
예제 #2
0
class ArtificialHorizonArtist(Artist):
    def __init__(self, ax: plt.Axes, attitude_data: SO3):
        if not isinstance(ax, plt.Axes):
            raise TypeError("The axes must be of plt.Axes type.")
        if not isinstance(attitude_data, SO3):
            raise TypeError("The attitude_data must be an SO3 element.")

        # Background
        ah_circle = Circle((0, 0), radius=1.0, color='w', ec='w')
        ah_colouring = Circle((0, 0), radius=5.0, color='k')
        ax.add_patch(ah_colouring)
        ax.add_patch(ah_circle)

        # Horizon line
        self._attitude_data = attitude_data
        true_horizon = self._compute_horizon(attitude_data)
        self._horizon_line, = ax.plot(true_horizon[0, :], true_horizon[1, :])

        # Horizon shading
        height = self._compute_horizon_height(attitude_data)
        slope = self._compute_horizon_slope(attitude_data) * 180.0 / np.pi
        self._shade = Wedge((0, height), 2.0, -180 + slope, slope, alpha=0.5)
        ax.add_patch(self._shade)

        # Clip path
        clip_circle = Circle((0, 0), radius=1.0, transform=ax.transData)
        self._horizon_line.set_clip_path(clip_circle)
        self._shade.set_clip_path(clip_circle)

        # Center Overlay
        ax.add_patch(Arc((0, 0), 0.3, 0.3, theta1=180, lw=2.0))
        ax.plot([0.15, 0.6], [0, 0], color='k', lw=2.0)
        ax.plot([-0.15, -0.6], [0, 0], color='k', lw=2.0)

        # Axes settings
        ax.axis('square')
        ax.set_xlim([-1, 1])
        ax.set_ylim([-1, 1])

    def set_attitude_data(self, attitude: SE3):
        if not isinstance(attitude, SO3):
            raise TypeError("The attitude_data must be an SO3 element.")

        self._attitude_data = attitude
        true_horizon = self._compute_horizon(attitude)
        self._horizon_line.set_data(true_horizon[0, :], true_horizon[1, :])

        height = self._compute_horizon_height(attitude)
        slope = self._compute_horizon_slope(attitude) * 180.0 / np.pi
        self._shade.set_center((0, height))
        self._shade.set_theta1(-180 + slope)
        self._shade.set_theta2(slope)

    @staticmethod
    def _compute_horizon(attitude: SO3):
        angles = np.linspace(0, 2 * np.pi, 50)
        base_horizon = np.vstack(
            (np.cos(angles), np.sin(angles), np.zeros(angles.shape)))
        true_horizon = attitude.inv() * base_horizon
        true_horizon = true_horizon[:, true_horizon[0, :] > 0]
        true_horizon = true_horizon / true_horizon[0, :]
        true_horizon = true_horizon[1:, :]
        return true_horizon

    @staticmethod
    def _compute_horizon_slope(attitude: SO3):
        theta = ArtificialHorizonArtist._find_heading_angle(attitude)
        H = attitude.inv() * np.array([[np.cos(theta)], [np.sin(theta)], [0.0]
                                       ])
        DH = attitude.inv() * np.array([[-np.sin(theta)], [np.cos(theta)],
                                        [0.0]])
        DH2 = H[0, 0] * DH - DH[0, 0] * H
        slope = np.arctan2(DH2[2, 0], DH2[1, 0])
        return slope

    @staticmethod
    def _compute_horizon_height(attitude: SO3):
        theta = ArtificialHorizonArtist._find_heading_angle(attitude)
        H = attitude.inv() * np.array([[np.cos(theta)], [np.sin(theta)], [0.0]
                                       ])
        H = H / H[0, 0]
        return H[2, 0]

    @staticmethod
    def _find_heading_angle(attitude):
        RT = attitude.as_matrix().T
        theta = np.arctan2(-RT[1, 0], RT[1, 1])
        return theta
예제 #3
0
class PeakSelector(_SelectorWidget):
    """Draw a Peak as triangle."""

    # pylint: disable=too-many-arguments
    # pylint: disable=too-many-instance-attributes
    # pylint: disable=invalid-name
    # pylint: disable=attribute-defined-outside-init
    def __init__(self,
                 ax,
                 onselect,
                 minfwhm=None,
                 minamp=None,
                 useblit=False,
                 wedgeprops=None,
                 onmove_callback=None,
                 peak_stays=False,
                 button=None,
                 limits=None):
        _SelectorWidget.__init__(self,
                                 ax,
                                 onselect,
                                 useblit=useblit,
                                 button=button)

        if minfwhm is not None or minamp is not None:
            raise NotImplementedError

        if wedgeprops is None:
            wedgeprops = dict(facecolor='red', alpha=0.5, fill=True)

        wedgeprops['animated'] = self.useblit

        self.wedge = None
        self.pressv = None

        if limits is None:
            limits = (-np.inf, np.inf)
        self.limits = limits

        self.wedgeprops = wedgeprops
        self.onmove_callback = onmove_callback
        self.minfwhm = minfwhm
        self.minamp = minamp
        self.peak_stays = peak_stays

        # Needed when dragging out of axes
        self.prev = (0, 0)

        # Reset canvas so that `new_axes` connects events.
        self.canvas = None
        self.new_axes(ax)

    def new_axes(self, ax):
        """Set SpanSelector to operate on a new Axes"""
        self.ax = ax
        if self.canvas is not ax.figure.canvas:
            if self.canvas is not None:
                self.disconnect_events()

            self.canvas = ax.figure.canvas
            self.connect_default_events()

        self.wedge = Wedge((0, 0),
                           1e10,
                           0,
                           0,
                           visible=False,
                           **self.wedgeprops)
        if self.peak_stays:
            self.stay_wedge = Wedge((0, 0),
                                    1e10,
                                    0,
                                    0,
                                    visible=False,
                                    **self.wedgeprops)
            self.stay_wedge.set_animated(False)
            self.ax.add_patch(self.stay_wedge)

        self.ax.add_patch(self.wedge)
        self.artists = [self.wedge]

    def set_wedgeprops(self, wedgeprops):
        """Custom: set new rectprops."""
        self.wedgeprops = wedgeprops
        self.new_axes(self.ax)

    def set_limits(self, limits):
        """Sets new limits. Peak will only be drawn when press event occurs
        inside these x values."""
        self.limits = limits

    def ignore(self, event):
        """return *True* if *event* should be ignored"""
        return _SelectorWidget.ignore(self, event) or not self.visible

    def _press(self, event):
        """on button press event"""
        x0, y0 = self._get_data(event)
        if not self.limits[0] <= x0 <= self.limits[1]:
            return True
        self.wedge.set_visible(self.visible)
        if self.peak_stays:
            self.stay_wedge.set_visible(False)
            # really force a draw so that the stay rect is not in
            # the blit background
            if self.useblit:
                self.canvas.draw()
        self.pressv = (x0, y0)
        self.wedge.set_center((x0, y0))
        return False

    def _release(self, event):
        """on button release event"""
        if self.pressv is None:
            return True
        self.buttonDown = False

        self.wedge.set_visible(False)

        if self.peak_stays:
            self.stay_wedge.set_center(self.wedge.center)
            self.stay_wedge.set_radius(self.wedge.r)
            self.stay_wedge.set_theta1(self.wedge.theta1)
            self.stay_wedge.set_theta2(self.wedge.theta2)
            self.stay_wedge.set_visible(True)

        self.canvas.draw_idle()

        x0, y0, = self.pressv
        center = x0
        amplitude = y0

        x, y = self._get_data(event)
        angle = abs(np.arctan((x - x0) / (y - y0)))

        self.onselect(center, amplitude, angle)
        self.pressv = None
        return False

    def _onmove(self, event):
        """on motion notify event"""
        if self.pressv is None:
            return True
        x, y = self._get_data(event)
        if x is None:
            return True
        x0, y0, = self.pressv

        angle = abs(np.arctan((x - x0) / (y - y0)))
        self.wedge.set_theta1(np.rad2deg(-angle) - 90)
        self.wedge.set_theta2(np.rad2deg(angle) - 90)

        if self.onmove_callback is not None:
            center = x0
            amplitude = y0
            self.onmove_callback(center, amplitude, angle)

        self.update()
        return False
예제 #4
0
class MainWindow(QMainWindow):
    
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        
        self.setWindowTitle('Lidar Boom Geometry')
        self.initializeValues()
        self.createLayout()
        self.initializeDrawing()
    
    def initializeValues(self):
        
        self.crestVal = [0.0, 6.5]
        self.backAngleVal = 2
        self.backHeightVal = 4
        self.frontAngleVal = 3
        self.frontHeightVal = 1
        self.frontDropVal = 1
        self.baseDropVal = 1
        self.baseOffsetVal = 12
        self.baseHeightVal = 4 * 0.3048
        self.boomAngleVal = 20
        self.boomLengthVal = 44 * 0.3048
        self.mountAngleVal = 80
        self.hingeAngleVal = 10
        self.mountLengthVal = 0.5
        self.waterLevelVal = 1
        
        # Cabling variables
        self.cableMountHeight = 5 * 0.3048
        self.pulleyALocation = 34 * 0.3048
        
        # Weights
        self.sensorWeight = 500
        
    def drawBoomEnd(self):
        
        # The boom cut
        length = 15
        height = 10
        boomPts = [[0, 0], [length, 0], [length + height/tan(radians(self.mountAngleVal)), height], [0, height]]
        
        x = [i[0] for i in boomPts]
        y = [i[1] for i in boomPts]
        
        self.barAx.cla()
        self.barAx.plot(x, y, 'k')
        fill = Polygon(boomPts, facecolor='k')
        self.barAx.add_patch(fill)

        # The angled hinge
        hingeLength = sqrt((height/tan(radians(self.mountAngleVal)))**2 + height**2)
        angle = self.hingeAngleVal - 90 - (90 - self.mountAngleVal)
        hingePts = [boomPts[2], [boomPts[2][0] + hingeLength*cos(radians(angle)), 
                                 boomPts[2][1] + hingeLength*sin(radians(angle))]]
        
        xh = [i[0] for i in hingePts]
        yh = [i[1] for i in hingePts]
        self.barAx.plot(xh, yh, 'k', lw=2)
        
        self.barAx.set_ylim([-2,12])
        
    def initializeDrawing(self):
        
        self.waterLine, = self.ax.plot([0.0, 300.0], [self.waterLevelVal, self.waterLevelVal])
        
        crestDrop = [self.crestVal[0], self.crestVal[1]-self.frontDropVal]
        waterPt = [(crestDrop[1]-self.frontHeightVal)/tan(radians(self.frontAngleVal)), self.frontHeightVal]
        landPt = [-(self.crestVal[1]-self.backHeightVal)/tan(radians(self.backAngleVal)), self.backHeightVal]
        
        x = [i[0] for i in [landPt, self.crestVal, crestDrop, waterPt]]
        y = [i[1] for i in [landPt, self.crestVal, crestDrop, waterPt]]
        
        self.duneLine, = self.ax.plot(x, y)
        
        baseGround = [self.crestVal[0] - self.baseOffsetVal, self.crestVal[1] - self.baseDropVal]
        baseTop = [baseGround[0], baseGround[1] + self.baseHeightVal]
        boomEnd = [baseTop[0] + self.boomLengthVal*cos(radians(self.boomAngleVal)), baseTop[1] + self.boomLengthVal*sin(radians(self.boomAngleVal))]
        
        self.oceanRange.setText(QString.number(boomEnd[0] + (boomEnd[1]-self.waterLevelVal)/tan(radians(self.mountAngleVal-60)), 'f', 1))
        
        xb = [i[0] for i in [baseGround, baseTop, boomEnd]]
        yb = [i[1] for i in [baseGround, baseTop, boomEnd]]
        
        self.boomLine, = self.ax.plot(xb, yb)
        straightAngle = self.boomAngleVal - (90-self.mountAngleVal) - 90 + self.hingeAngleVal
        self.wedge = Wedge(boomEnd, 300, straightAngle-40, straightAngle+60, alpha=0.5, color='y')
        self.ax.add_artist(self.wedge)
        
        angle = -30+self.mountAngleVal+self.hingeAngleVal+self.boomAngleVal
        self.oceanRange.setText(QString.number(boomEnd[0] + (boomEnd[1]-self.waterLevelVal)*tan(radians(angle)), 'f', 1))
        
        # Initialize cabling drawing
        cableMountBaseTop = [baseTop[0], baseTop[1] + self.cableMountHeight]
        pulleyA = [baseTop[0] + self.pulleyALocation*cos(radians(self.boomAngleVal)), baseTop[1] + self.pulleyALocation*sin(radians(self.boomAngleVal))]
        
        xc = [i[0] for i in [baseTop, cableMountBaseTop, pulleyA]]
        yc = [i[1] for i in [baseTop, cableMountBaseTop, pulleyA]]
        
        self.calculateCablingForces()
        
        self.cableLine, = self.ax.plot(xc, yc, 'k')
        
        self.fig.subplots_adjust(left=0.1)
        
    def update(self):
        
        # Dune calculations
        crestDrop = [self.crestVal[0], self.crestVal[1]-self.frontDropVal]
        waterPt = [(crestDrop[1]-self.frontHeightVal)/tan(radians(self.frontAngleVal)), self.frontHeightVal]
        landPt = [-(self.crestVal[1]-self.backHeightVal)/tan(radians(self.backAngleVal)), self.backHeightVal]
        
        xDune = [i[0] for i in [landPt, self.crestVal, crestDrop, waterPt]]
        yDune = [i[1] for i in [landPt, self.crestVal, crestDrop, waterPt]]
        
        # Base and boom calculations
        baseGround = [self.crestVal[0] - self.baseOffsetVal, self.crestVal[1] - self.baseDropVal]
        baseTop = [baseGround[0], baseGround[1] + self.baseHeightVal]
        boomEnd = [baseTop[0] + self.boomLengthVal*cos(radians(self.boomAngleVal)), baseTop[1] + self.boomLengthVal*sin(radians(self.boomAngleVal))]
        
        xBoom = [i[0] for i in [baseGround, baseTop, boomEnd]]
        yBoom = [i[1] for i in [baseGround, baseTop, boomEnd]]
                
        # Update drawing
        self.waterLine.set_ydata([self.waterLevelVal, self.waterLevelVal])
        
        self.duneLine.set_xdata(xDune)
        self.duneLine.set_ydata(yDune)
        
        self.boomLine.set_xdata(xBoom)
        self.boomLine.set_ydata(yBoom)
        
        self.wedge.set_center(boomEnd)
        straightAngle = self.boomAngleVal - (90-self.mountAngleVal) - 90 + self.hingeAngleVal
        self.wedge.set_theta1(straightAngle-40)
        
        self.wedge.set_theta2(straightAngle+60)
        
        self.drawBoomEnd()
        
        # Calculate and draw cabling
        cableMountBaseTop = [baseTop[0], baseTop[1] + self.cableMountHeight]
        pulleyA = [baseTop[0] + self.pulleyALocation*cos(radians(self.boomAngleVal)), baseTop[1] + self.pulleyALocation*sin(radians(self.boomAngleVal))]
        
        xc = [i[0] for i in [baseTop, cableMountBaseTop, pulleyA]]
        yc = [i[1] for i in [baseTop, cableMountBaseTop, pulleyA]]
        
        self.cableLine.set_xdata(xc)
        self.cableLine.set_ydata(yc)
        
        self.calculateCablingForces()
        
        # Update the interface
        angle = -30+self.mountAngleVal+self.hingeAngleVal+self.boomAngleVal
        self.oceanRange.setText(QString.number(boomEnd[0] + (boomEnd[1]-self.waterLevelVal)*tan(radians(angle)), 'f', 1))
        
        # Update the canvas
        self.fig.canvas.draw()
        
    def calculateCablingForces(self):
        
        h = self.pulleyALocation*sin(radians(self.boomAngleVal)) - self.cableMountHeight
        l = self.pulleyALocation*cos(radians(self.boomAngleVal))
        innerAngle = abs(atan2(h,l) - radians(self.boomAngleVal))
        f = (self.sensorWeight * sin(radians(90 - self.boomAngleVal)) / sin(innerAngle))
        
        self.pulleyAForce.setText(QString.number(f, 'f', 1))
        
        print degrees(innerAngle)
        
    def updateCrest(self, val):
        
        self.crestVal[1] = val
        self.update()
        
    def updateWaterLevel(self, val):
        
        self.waterLevelVal = val
        self.update()
        
    def updateBaseDrop(self, val):
        
        self.baseDropVal = val
        self.update()
        
    def updateBaseOffset(self, val):
        
        self.baseOffsetVal = val
        self.update()
        
    def updateBaseHeight(self, val):
        
        self.baseHeightVal = val
        self.update()
        
    def updateBoomAngle(self, val):
        
        self.boomAngleVal = val
        self.update()
        
    def updateBoomLength(self, val):
        
        self.boomLengthVal = val
        self.update()
        
    def updateMountAngle(self, val):
        
        self.mountAngleVal = val
        self.update()
        
    def updateHingeAngle(self, val):
        
        self.hingeAngleVal = val
        self.update()
        
    def updateCableTowerHeight(self, val):
        
        self.cableMountHeight = val
        self.update()
        
    def updatePulleyALocation(self, val):
        
        self.pulleyALocation = val
        self.update()
        
    def updateSensorWeight(self, val):
        
        self.sensorWeight = val
        self.update()
    
    def createLayout(self):
        self.mainFrame = QWidget()
        
        # Main horizontal layout
        mainLayout = QHBoxLayout()
        
        # Layouts
        controlLayout = QGridLayout()
        infoLayout = QGridLayout()
        leftLayout = QVBoxLayout()
        viewLayout = QVBoxLayout()
        
        line = QFrame(self.mainFrame)
        line.setFrameStyle(QFrame.HLine | QFrame.Raised)
        
        leftLayout.addLayout(controlLayout)
        leftLayout.addWidget(line)
        leftLayout.addLayout(infoLayout)
        leftLayout.addStretch()
        mainLayout.addLayout(leftLayout)
        mainLayout.addLayout(viewLayout)
        
        # Info List
        infoLayout.addWidget(QLabel('<b>LIDAR</b>'), 0, 0, 1, 2, Qt.AlignCenter)
        infoLayout.addWidget(QLabel('Ocean Sight Range:'), 1, 0)
        infoLayout.addWidget(QLabel('<b>Structural</b>'), 2, 0, 1, 2, Qt.AlignCenter)
        infoLayout.addWidget(QLabel('Force - Pulley A (lb)'), 3, 0)
        
        self.oceanRange = QLabel('-', self.mainFrame)
        self.pulleyAForce = QLabel('-', self.mainFrame)

        infoLayout.addWidget(self.oceanRange, 1, 1)
        infoLayout.addWidget(self.pulleyAForce, 3, 1)
        
        infoLayout.setColumnMinimumWidth(1, 100)
        
        # Control grid
        controlLayout.addWidget(QLabel('<b>Dune/Ocean</b>'), 0, 0, 1, 2, Qt.AlignCenter)
        controlLayout.addWidget(QLabel('Dune Crest'), 1, 0)
        controlLayout.addWidget(QLabel('Water Level'), 2, 0)
        controlLayout.addWidget(QLabel('<b>Boom Geometry</b>'), 3, 0, 1, 2, Qt.AlignCenter)
        controlLayout.addWidget(QLabel('Base Drop'), 4, 0)
        controlLayout.addWidget(QLabel('Base Offset'), 5, 0)
        controlLayout.addWidget(QLabel('Base Height'), 6, 0)
        controlLayout.addWidget(QLabel('Boom Length'), 7, 0)
        controlLayout.addWidget(QLabel('Boom Angle'), 8, 0)
        controlLayout.addWidget(QLabel('<b>Mount Geometry</b>'), 9, 0, 1, 2, Qt.AlignCenter)
        controlLayout.addWidget(QLabel('Mount Cut Angle'), 10, 0)
        controlLayout.addWidget(QLabel('Hinge Angle'), 11, 0)
        controlLayout.addWidget(QLabel('<b>Cabling Geometry</b>'), 12, 0, 1, 2, Qt.AlignCenter)
        controlLayout.addWidget(QLabel('Cable Mount Height'), 13, 0)
        controlLayout.addWidget(QLabel('Pulley A Location'), 14, 0)
        controlLayout.addWidget(QLabel('<b>Loading (lbs)</b>'), 15, 0, 1, 2, Qt.AlignCenter)
        controlLayout.addWidget(QLabel('LIDAR Weight'), 16, 0)
        controlLayout.addWidget(QLabel('Boom Weight'), 17, 0)
        
        self.crestHeight = QDoubleSpinBox(self.mainFrame)
        self.crestHeight.setSingleStep(0.1)
        self.crestHeight.setValue(self.crestVal[1])
        controlLayout.addWidget(self.crestHeight, 1, 1)
        self.connect(self.crestHeight, SIGNAL('valueChanged(double)'), self.updateCrest)
        
        self.waterLevel = QDoubleSpinBox(self.mainFrame)
        self.waterLevel.setSingleStep(0.1)
        self.waterLevel.setValue(self.waterLevelVal)
        controlLayout.addWidget(self.waterLevel, 2, 1)
        self.connect(self.waterLevel, SIGNAL('valueChanged(double)'), self.updateWaterLevel)
        
        self.baseDrop = QDoubleSpinBox(self.mainFrame)
        self.baseDrop.setSingleStep(0.1)
        self.baseDrop.setValue(self.baseDropVal)
        controlLayout.addWidget(self.baseDrop, 4, 1)
        self.connect(self.baseDrop, SIGNAL('valueChanged(double)'), self.updateBaseDrop)
        
        self.baseOffset = QDoubleSpinBox(self.mainFrame)
        self.baseOffset.setSingleStep(0.1)
        self.baseOffset.setValue(self.baseOffsetVal)
        controlLayout.addWidget(self.baseOffset, 5, 1)
        self.connect(self.baseOffset, SIGNAL('valueChanged(double)'), self.updateBaseOffset)
        
        self.baseHeight = QDoubleSpinBox(self.mainFrame)
        self.baseHeight.setSingleStep(0.1)
        self.baseHeight.setValue(self.baseHeightVal)
        controlLayout.addWidget(self.baseHeight, 6, 1)
        self.connect(self.baseHeight, SIGNAL('valueChanged(double)'), self.updateBaseHeight)
        
        self.boomLength = QDoubleSpinBox(self.mainFrame)
        self.boomLength.setSingleStep(0.1)
        self.boomLength.setValue(self.boomLengthVal)
        controlLayout.addWidget(self.boomLength, 7, 1)
        self.connect(self.boomLength, SIGNAL('valueChanged(double)'), self.updateBoomLength)
        
        self.boomAngle = QDoubleSpinBox(self.mainFrame)
        self.boomAngle.setSingleStep(0.5)
        self.boomAngle.setMaximum(360)
        self.boomAngle.setValue(self.boomAngleVal)
        controlLayout.addWidget(self.boomAngle, 8, 1)
        self.connect(self.boomAngle, SIGNAL('valueChanged(double)'), self.updateBoomAngle)
        
        self.mountAngle = QDoubleSpinBox(self.mainFrame)
        self.mountAngle.setSingleStep(0.5)
        self.mountAngle.setMaximum(360)
        self.mountAngle.setValue(self.mountAngleVal)
        controlLayout.addWidget(self.mountAngle, 10, 1)
        self.connect(self.mountAngle, SIGNAL('valueChanged(double)'), self.updateMountAngle)
        
        self.hingeAngle = QDoubleSpinBox(self.mainFrame)
        self.hingeAngle.setSingleStep(0.5)
        self.hingeAngle.setMaximum(360)
        self.hingeAngle.setValue(self.hingeAngleVal)
        controlLayout.addWidget(self.hingeAngle, 11, 1)
        self.connect(self.hingeAngle, SIGNAL('valueChanged(double)'), self.updateHingeAngle)
        
        self.cableTowerHeight = QDoubleSpinBox(self.mainFrame)
        self.cableTowerHeight.setSingleStep(0.1)
        self.cableTowerHeight.setValue(self.cableMountHeight)
        controlLayout.addWidget(self.cableTowerHeight, 13, 1)
        self.connect(self.cableTowerHeight, SIGNAL('valueChanged(double)'), self.updateCableTowerHeight)
        
        self.pulleyADistance = QDoubleSpinBox(self.mainFrame)
        self.pulleyADistance.setSingleStep(0.1)
        self.pulleyADistance.setValue(self.pulleyALocation)
        controlLayout.addWidget(self.pulleyADistance, 14, 1)
        self.connect(self.pulleyADistance, SIGNAL('valueChanged(double)'), self.updatePulleyALocation)
        
        self.lidarWeight = QDoubleSpinBox(self.mainFrame)
        self.lidarWeight.setSingleStep(5)
        self.lidarWeight.setMaximum(9999999)
        self.lidarWeight.setValue(self.sensorWeight)
        controlLayout.addWidget(self.lidarWeight, 16, 1)
        self.connect(self.lidarWeight, SIGNAL('valueChanged(double)'), self.updateSensorWeight)
        
        # Create the axes
        self.fig = Figure((5.0, 4.0))
        self.canvas = FigureCanvas(self.fig)
        self.canvas.setParent(self.mainFrame)
        self.ax = self.fig.add_subplot(211, aspect='equal')
        self.barAx = self.fig.add_subplot(212, aspect='equal')
        self.ax.set_xlim([-15,10])
        self.drawBoomEnd()
        self.toolBar = NavigationToolbar(self.canvas, self.mainFrame)
        viewLayout.addWidget(self.canvas)
        viewLayout.addWidget(self.toolBar)
        
        # Set the main layout
        self.mainFrame.setLayout(mainLayout)
        self.setCentralWidget(self.mainFrame)