Code:
import sys
import random
from PyQt4 import QtGui, QtCore
# Number of GPS samples per second
GPSRATE = 5
# Number of IMU samples per second
IMURATE = 25
# Weight of the latest GPS sample
WEIGHT = 1.0 / 4.0
# Standard deviation of the GPS error
GPSERROR = 20.0
# Standard deviation of the IMU absolute error
IMUABSERROR = 1.5
# Standard deviation of the IMU scaling error
IMURELERROR = 0.1
class GPS(QtCore.QTimer):
def __init__(self, parent, interval):
global GPSERROR
QtCore.QTimer.__init__(self)
self.Target = parent
self.Radius = GPSERROR
self.Position = ( 0, 0 )
QtCore.QObject.connect(self, QtCore.SIGNAL("timeout()"), self.Signal)
self.start(int(0.5 + 1000.0 * interval))
def Signal(self):
# Generate Gaussian random noise
noise = ( random.gauss(0.0, self.Radius), random.gauss(0.0, self.Radius) )
# Use parent position plus noise to generate new position.
self.Position = ( int(self.Target.Position[0] + noise[0]), int(self.Target.Position[1] + noise[1]) )
# Tell parent to repaint.
self.Target.GPSSignal()
class IMU(QtCore.QTimer):
def __init__(self, parent, interval):
global IMUABSERROR
global IMURELERROR
QtCore.QTimer.__init__(self)
self.Target = parent
self.TargetLast = parent.Position
self.Position = ( 0, 0 )
self.Radius = IMUABSERROR
self.Scale = IMURELERROR
QtCore.QObject.connect(self, QtCore.SIGNAL("timeout()"), self.Signal)
self.start(int(0.5 + 1000.0 * interval))
def Signal(self):
# Calculate true movement since last signal
currTarget = ( self.Target.Position[0], self.Target.Position[1] )
prevTarget = ( self.TargetLast[0], self.TargetLast[1] )
self.TargetLast = ( currTarget[0], currTarget[1] )
delta = ( currTarget[0] - prevTarget[0], currTarget[1] - prevTarget[1] )
# Generate Gaussian random noise for absolute error,
noise = ( random.gauss(0.0, self.Radius), random.gauss(0.0, self.Radius) )
# and for scaling error.
scale = random.gauss(1.0, self.Scale)
# Calculate simulated delta,
self.Delta = ( noise[0] + scale * delta[0], noise[1] + scale * delta[1] )
# and add to current position.
self.Position = ( self.Position[0] + self.Delta[0], self.Position[1] + self.Delta[1] )
# Update.
self.Target.IMUSignal()
class Dog(QtGui.QMainWindow):
def __init__(self, *args):
global GPSRATE
global IMURATE
apply(QtGui.QMainWindow.__init__, (self,) + args)
# Set the window title and margins.
self.setWindowTitle('Dog')
self.setContentsMargins(0, 0, 0, 0)
# Define a new palette, with black button/text color, and a light gray background.
self.setPalette(QtGui.QPalette(QtGui.QColor(0,0,0,255), QtGui.QColor(204,204,204,255)))
self.setBackgroundRole(QtGui.QPalette.Base)
# We need to track mouse movement.
self.setMouseTracking(True)
# Initial Dog position.
self.Position = ( 0, 0 )
# Initial GPS FIX location.
self.Fix = ( 0, 0 )
# Simulate a GPS,
self.GPS = GPS(self, 1.0 / GPSRATE)
# and an IMU.
self.IMU = IMU(self, 1.0 / IMURATE)
# Update.
self.repaint()
def GPSSignal(self):
# Update GPS fix location.
self.Fix = ( (1.0 - WEIGHT) * self.Fix[0] + WEIGHT * (self.GPS.Position[0] - self.IMU.Position[0]), (1.0 - WEIGHT) * self.Fix[1] + WEIGHT * (self.GPS.Position[1] - self.IMU.Position[1] ) )
self.repaint()
def IMUSignal(self):
self.repaint()
def paintEvent(self, event):
global WEIGHT
# Handle for drawing on our window.
painter = QtGui.QPainter()
# Bottom right corner and center of the window.
limits = ( self.width(), self.height() )
center = ( limits[0] / 2, limits[1] / 2 )
painter.begin(self)
# Clear to background color.
painter.fillRect(self.geometry(), QtGui.QBrush(QtGui.QColor(204, 204, 204, 255)))
# Draw the centerlines.
painter.setBrush(QtCore.Qt.NoBrush)
painter.setPen(QtGui.QColor(153, 153, 153, 255))
painter.drawLine(center[0], 0, center[0], limits[1])
painter.drawLine(0, center[1], limits[0], center[1])
# Draw the GPS Fix, a small gray blob.
painter.setBrush(QtGui.QColor(0, 0, 0, 51))
painter.setPen(QtGui.QColor(153, 153, 153, 255))
painter.drawEllipse(QtCore.QPoint(center[0] + self.Fix[0], center[1] + self.Fix[1]), 4, 4)
# Draw the GPS point, a green blob.
painter.setBrush(QtGui.QColor(0, 255, 0, 51))
painter.setPen(QtGui.QColor(0, 153, 0, 255))
painter.drawEllipse(QtCore.QPoint(center[0] + self.GPS.Position[0], center[1] + self.GPS.Position[1]), 5, 5)
# Draw the IMU point, a blue blob.
painter.setBrush(QtGui.QColor(0, 0, 255, 51))
painter.setPen(QtGui.QColor(0, 0, 204, 255))
painter.drawEllipse(QtCore.QPoint(center[0] + self.IMU.Position[0], center[1] + self.IMU.Position[1]), 5, 5)
# Draw the estimated point, a red blob
point = ( self.IMU.Position[0] + self.Fix[0], self.IMU.Position[1] + self.Fix[1] )
painter.setBrush(QtGui.QColor(255, 0, 0, 102))
painter.setPen(QtGui.QColor(153, 0, 0, 255))
painter.drawEllipse(QtCore.QPoint(center[0] + point[0], center[1] + point[1]), 8, 8)
# and a line to the actual point.
painter.drawLine(center[0] + point[0], center[1] + point[1], center[0] + self.Position[0], center[1] + self.Position[1])
# Draw the Dog point, black and white.
painter.setBrush(QtGui.QColor(255, 255, 255, 51))
painter.setPen(QtGui.QColor(0, 0, 0, 255))
painter.drawEllipse(QtCore.QPoint(center[0] + self.Position[0], center[1] + self.Position[1]), 5, 5)
painter.end()
def mousePressEvent(self, event):
self.Position = ( event.x() - self.width() / 2, event.y() - self.height() / 2 )
self.repaint()
def mouseMoveEvent(self, event):
if event.buttons() == QtCore.Qt.LeftButton:
self.Position = ( event.x() - self.width() / 2, event.y() - self.height() / 2 )
self.repaint()
if __name__ == '__main__':
print ""
print "The black and white circle is the dog. Click or drag to move."
print "The red blob is the estimated position, with line showing the error."
print "The blue blob describes cumulative IMU coordinates."
print "The green blob describes GPS points."
print "The small gray blob describes the GPS fix location."
print ""
application = QtGui.QApplication(sys.argv)
window = Dog()
window.show()
window.resizeEvent(None)
result = application.exec_()
sys.exit(result)
It should run on any OS, as long as you have Python and PyQt4 installed (they are available to just about all OSes, certainly for Linux, Mac OS X, and Windows). Save the above to