[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gpsd-dev] [PATCH 2/2] Adds skyview rotation support to xgps and xgpsspe
From: |
Fred Wright |
Subject: |
[gpsd-dev] [PATCH 2/2] Adds skyview rotation support to xgps and xgpsspeed. |
Date: |
Thu, 22 Sep 2016 16:28:36 -0700 |
This adds an optional argument to rotate the skyview display, making
it possible to orient it correctly based on the direction one is
actually facing. The specified heading is positioned at the top.
This edit does not update contrib/webgps.py, which is somewhat
more complicated to fix due to the Javascript involvement (not to
mention the rather crude argument parsing).
TESTED:
Ran both programs with and without the -r or --rotate option.
---
xgps | 37 +++++++++++++++++++------------
xgpsspeed | 75 +++++++++++++++++++++++++++++++++++++--------------------------
2 files changed, 67 insertions(+), 45 deletions(-)
diff --git a/xgps b/xgps
index 066401f..44ea062 100755
--- a/xgps
+++ b/xgps
@@ -3,7 +3,7 @@
'''
xgps -- test client for gpsd
-usage: xgps [-D level] [-hV?] [-l degmfmt] [-u units] [server[:port[:device]]]
+usage: xgps [-D level] [-hV?] [-l degmfmt] [-u units] [-r rotation]
[server[:port[:device]]]
'''
# This code runs compatibly under Python 2 and 3.x for x >= 2.
@@ -95,7 +95,7 @@ class SkyView(Gtk.DrawingArea):
HORIZON_PAD = 40 # How much whitespace to leave around horizon
SAT_RADIUS = 5 # Diameter of satellite circle
- def __init__(self):
+ def __init__(self, rotate=0.0):
GObject.GObject.__init__(self)
self.set_size_request(400, 400)
self.cr = None # New cairo context for each expose event
@@ -104,6 +104,7 @@ class SkyView(Gtk.DrawingArea):
self.connect('draw', self.on_draw)
self.satellites = []
self.center_x = self.center_y = self.radius = None
+ self.rotate = rotate
def on_size_allocate(self, _unused, allocation):
width = allocation.width
@@ -181,6 +182,7 @@ class SkyView(Gtk.DrawingArea):
def pol2cart(self, az, el):
"Polar to Cartesian coordinates within the horizon circle."
+ az = (az - self.rotate) % 360.0
az *= (math.pi / 180) # Degrees to radians
# Exact spherical projection would be like this:
# el = sin((90.0 - el) * DEG_2_RAD);
@@ -223,14 +225,14 @@ class SkyView(Gtk.DrawingArea):
self.draw_line(x1, y1, x2, y2)
# The compass-point letters
- (x, y) = self.pol2cart(0, 0)
- self.draw_string(x, y - 10, "N")
- (x, y) = self.pol2cart(90, 0)
- self.draw_string(x + 10, y, "E")
- (x, y) = self.pol2cart(180, 0)
- self.draw_string(x, y + 10, "S")
- (x, y) = self.pol2cart(270, 0)
- self.draw_string(x - 10, y, "W")
+ (x, y) = self.pol2cart(0, -5)
+ self.draw_string(x, y, "N")
+ (x, y) = self.pol2cart(90, -5)
+ self.draw_string(x, y, "E")
+ (x, y) = self.pol2cart(180, -5)
+ self.draw_string(x, y, "S")
+ (x, y) = self.pol2cart(270, -5)
+ self.draw_string(x, y, "W")
# The satellites
self.cr.set_line_width(2)
@@ -460,8 +462,9 @@ class Base(object):
("EPD", lambda s, r: s.update_err_degrees(r, "epd")),
)
- def __init__(self, deg_type):
+ def __init__(self, deg_type, rotate=0.0):
self.deg_type = deg_type
+ self.rotate = rotate
self.conversions = unit_adjustments()
self.saved_mode = -1
self.ais_latch = False
@@ -579,7 +582,7 @@ class Base(object):
viewframe = Gtk.Frame(label="Skyview")
self.satbox.add(viewframe)
- self.skyview = SkyView()
+ self.skyview = SkyView(self.rotate)
viewframe.add(self.skyview)
self.rawdisplay = Gtk.Entry()
@@ -869,11 +872,12 @@ class Base(object):
if __name__ == "__main__":
try:
import getopt
- (options, arguments) = getopt.getopt(sys.argv[1:], "D:hl:u:V?",
+ (options, arguments) = getopt.getopt(sys.argv[1:], "D:hl:u:r:V?",
['verbose'])
debug = 0
degreefmt = 'd'
unit_system = None
+ rotate = 0.0
for (opt, val) in options:
if opt in '-D':
debug = int(val)
@@ -881,6 +885,11 @@ if __name__ == "__main__":
degreeformat = val
elif opt == '-u':
unit_system = val
+ elif opt == '-r':
+ try:
+ rotate = float(val)
+ except ValueError:
+ rotate = 0.0
elif opt in ('-?', '-h', '--help'):
print(__doc__)
sys.exit(0)
@@ -902,7 +911,7 @@ if __name__ == "__main__":
if len(args) >= 3:
device = args[2]
- base = Base(deg_type=degreefmt)
+ base = Base(deg_type=degreefmt, rotate=rotate)
base.set_units(unit_system)
try:
daemon = gps.gps(host=host,
diff --git a/xgpsspeed b/xgpsspeed
index 277b575..29a6fc7 100755
--- a/xgpsspeed
+++ b/xgpsspeed
@@ -228,7 +228,7 @@ class NauticalSpeedometer(Speedometer):
HEADING_SAT_GAP = 0.8
SAT_SIZE = 10 # radius of the satellite circle in skyview
- def __init__(self, speed_unit=None, maxspeed=100):
+ def __init__(self, speed_unit=None, maxspeed=100, rotate=0.0):
Speedometer.__init__(self, speed_unit)
self.connect('size-allocate', self.on_size_allocate)
self.width = self.height = 0
@@ -240,9 +240,9 @@ class NauticalSpeedometer(Speedometer):
self.satellites = []
self.last_heading = 0
self.maxspeed = int(maxspeed)
+ self.rotate = radians(rotate)
- @staticmethod
- def polar2xy(radius, angle, polex, poley):
+ def polar2xy(self, radius, angle, polex, poley):
'''convert Polar coordinate to Cartesian coordinate system
the y axis in pygtk points downward
Args:
@@ -250,6 +250,7 @@ class NauticalSpeedometer(Speedometer):
angle: azimuth from from Polar coordinate system, in radian
polex and poley are the Cartesian coordinate of the pole
return a tuple contains (x, y)'''
+ angle = (angle + self.rotate) % (pi * 2) # Note reversed sense
return (polex + cos(angle) * radius, poley - sin(angle) * radius)
def on_size_allocate(self, _unused, allocation):
@@ -306,12 +307,12 @@ class NauticalSpeedometer(Speedometer):
for i in range(11):
# draw the large ticks
alpha = (8 - i) * pi / 6
- self.cr.move_to(*NauticalSpeedometer.polar2xy(rspeed, alpha, x, y))
+ self.cr.move_to(*self.polar2xy(rspeed, alpha, x, y))
self.cr.set_line_width(radius / 100)
- self.cr.line_to(*NauticalSpeedometer.polar2xy(rspeed - s_long,
alpha, x, y))
+ self.cr.line_to(*self.polar2xy(rspeed - s_long, alpha, x, y))
self.cr.stroke()
self.cr.set_line_width(radius / 200)
- xf, yf = NauticalSpeedometer.polar2xy(rspeed + 10, alpha, x, y)
+ xf, yf = self.polar2xy(rspeed + 10, alpha, x, y)
stxt = (self.maxspeed // 10) * i
self.draw_text(xf, yf, stxt, fontsize=radius / 15)
@@ -319,14 +320,14 @@ class NauticalSpeedometer(Speedometer):
# middle tick
alpha = (8 - i) * pi / 6
beta = (17 - 2 * i) * pi / 12
- self.cr.move_to(*NauticalSpeedometer.polar2xy(rspeed, beta, x, y))
- self.cr.line_to(*NauticalSpeedometer.polar2xy(rspeed - s_middle,
beta, x, y))
+ self.cr.move_to(*self.polar2xy(rspeed, beta, x, y))
+ self.cr.line_to(*self.polar2xy(rspeed - s_middle, beta, x, y))
# short tick
for n in range(10):
gamma = alpha + n * pi / 60
- self.cr.move_to(*NauticalSpeedometer.polar2xy(rspeed, gamma,
x, y))
- self.cr.line_to(*NauticalSpeedometer.polar2xy(rspeed -
s_short, gamma, x, y))
+ self.cr.move_to(*self.polar2xy(rspeed, gamma, x, y))
+ self.cr.line_to(*self.polar2xy(rspeed - s_short, gamma, x, y))
# draw the heading arc
self.cr.new_sub_path()
@@ -342,7 +343,7 @@ class NauticalSpeedometer(Speedometer):
label = str(n * 90)
# self.cr.set_source_rgba(0, 1, 0)
# radius * (1 + NauticalSpeedometer.HEADING_SAT_GAP),
- tbox_x, tbox_y = NauticalSpeedometer.polar2xy(
+ tbox_x, tbox_y = self.polar2xy(
radius * 0.88,
(1 - n) * pi / 2,
x, y)
@@ -359,13 +360,14 @@ class NauticalSpeedometer(Speedometer):
self.cr.set_source_rgba(0, 0, 0)
self.cr.arc(x, y, skyradius * 2 / 3, 0, 2 * pi)
+ self.cr.move_to(x + skyradius / 3, y) # Avoid line connecting circles
self.cr.arc(x, y, skyradius / 3, 0, 2 * pi)
# draw the cross hair
- self.cr.move_to(x - skyradius, y)
- self.cr.line_to(x + skyradius, y)
- self.cr.move_to(x, y - skyradius)
- self.cr.line_to(x, y + skyradius)
+ self.cr.move_to(*self.polar2xy(skyradius, 1.5 * pi, x, y))
+ self.cr.line_to(*self.polar2xy(skyradius, 0.5 * pi, x, y))
+ self.cr.move_to(*self.polar2xy(skyradius, 0.0, x, y))
+ self.cr.line_to(*self.polar2xy(skyradius, pi, x, y))
self.cr.set_line_width(radius / 200)
self.cr.stroke()
@@ -376,22 +378,22 @@ class NauticalSpeedometer(Speedometer):
# draw the large ticks
for i in range(12):
agllong = i * pi / 6
- self.cr.move_to(*NauticalSpeedometer.polar2xy(radius - long_inset,
agllong, x, y))
- self.cr.line_to(*NauticalSpeedometer.polar2xy(radius, agllong, x,
y))
+ self.cr.move_to(*self.polar2xy(radius - long_inset, agllong, x, y))
+ self.cr.line_to(*self.polar2xy(radius, agllong, x, y))
self.cr.set_line_width(radius / 100)
self.cr.stroke()
self.cr.set_line_width(radius / 200)
# middle tick
aglmid = (i + 0.5) * pi / 6
- self.cr.move_to(*NauticalSpeedometer.polar2xy(radius - mid_inset,
aglmid, x, y))
- self.cr.line_to(*NauticalSpeedometer.polar2xy(radius, aglmid, x,
y))
+ self.cr.move_to(*self.polar2xy(radius - mid_inset, aglmid, x, y))
+ self.cr.line_to(*self.polar2xy(radius, aglmid, x, y))
# short tick
for n in range(1, 10):
aglshrt = agllong + n * pi / 60
- self.cr.move_to(*NauticalSpeedometer.polar2xy(radius -
short_inset, aglshrt, x, y))
- self.cr.line_to(*NauticalSpeedometer.polar2xy(radius, aglshrt,
x, y))
+ self.cr.move_to(*self.polar2xy(radius - short_inset, aglshrt,
x, y))
+ self.cr.line_to(*self.polar2xy(radius, aglshrt, x, y))
self.cr.stroke()
def draw_heading(self, trig_height, heading, radius, x, y):
@@ -419,16 +421,16 @@ class NauticalSpeedometer(Speedometer):
self.cr.stroke()
# heading text
- (tbox_x, tbox_y) = NauticalSpeedometer.polar2xy(radius * 1.1, h, x, y)
+ (tbox_x, tbox_y) = self.polar2xy(radius * 1.1, h, x, y)
self.draw_text(tbox_x, tbox_y, int(heading), fontsize=radius / 15)
# the ship shape, based on test and try
shiplen = radius * NauticalSpeedometer.HEADING_SAT_GAP / 4
- xh, yh = NauticalSpeedometer.polar2xy(shiplen * 2.3, h, x, y)
- xa, ya = NauticalSpeedometer.polar2xy(shiplen * 2.2, h + pi - 0.3, x,
y)
- xb, yb = NauticalSpeedometer.polar2xy(shiplen * 2.2, h + pi + 0.3, x,
y)
- xc, yc = NauticalSpeedometer.polar2xy(shiplen * 1.4, h - pi / 5, x, y)
- xd, yd = NauticalSpeedometer.polar2xy(shiplen * 1.4, h + pi / 5, x, y)
+ xh, yh = self.polar2xy(shiplen * 2.3, h, x, y)
+ xa, ya = self.polar2xy(shiplen * 2.2, h + pi - 0.3, x, y)
+ xb, yb = self.polar2xy(shiplen * 2.2, h + pi + 0.3, x, y)
+ xc, yc = self.polar2xy(shiplen * 1.4, h - pi / 5, x, y)
+ xd, yd = self.polar2xy(shiplen * 1.4, h + pi / 5, x, y)
self.cr.set_source_rgba(0, 0.3, 0.2, 0.5)
self.cr.move_to(xa, ya)
@@ -460,7 +462,7 @@ class NauticalSpeedometer(Speedometer):
self.cr.set_line_width(2)
self.cr.set_source_rgb(0, 0, 0)
- x0, y0 = NauticalSpeedometer.polar2xy(radius * (90 - el) // 90, h, x,
y)
+ x0, y0 = self.polar2xy(radius * (90 - el) // 90, h, x, y)
self.cr.new_sub_path()
if gps.is_sbas(satsoup['PRN']):
@@ -515,7 +517,7 @@ class NauticalSpeedometer(Speedometer):
class Main(object):
def __init__(self, host='localhost', port='2947', device=None, debug=0,
- speed_unit=None, maxspeed=0, nautical=False):
+ speed_unit=None, maxspeed=0, nautical=False, rotate=0.0):
self.host = host
self.port = port
self.device = device
@@ -523,6 +525,7 @@ class Main(object):
self.speed_unit = speed_unit
self.maxspeed = maxspeed
self.nautical = nautical
+ self.rotate = rotate
self.window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
if not self.window.get_display():
raise Exception("Can't open display")
@@ -531,7 +534,8 @@ class Main(object):
self.window.set_size_request(500, 550)
self.widget = NauticalSpeedometer(
speed_unit=self.speed_unit,
- maxspeed=self.maxspeed)
+ maxspeed=self.maxspeed,
+ rotate=self.rotate)
else:
self.widget = LandSpeedometer(speed_unit=self.speed_unit)
self.window.connect('delete_event', self.delete_event)
@@ -735,6 +739,14 @@ if __name__ == '__main__':
type='int',
help='Set level of debug. Must be integer. [Default 0]'
)
+ parser.add_option(
+ '--rotate',
+ dest='rotate',
+ default=0,
+ action='store',
+ type='float',
+ help='Rotation of skyview ("up" direction) in degrees. [Default 0]'
+ )
(options, args) = parser.parse_args()
if args:
arg = args[0].split(':')
@@ -755,5 +767,6 @@ if __name__ == '__main__':
speed_unit=options.speedunits,
maxspeed=options.maxspeed,
nautical=options.nautical,
- debug=options.debug
+ debug=options.debug,
+ rotate=options.rotate,
).run()
--
2.9.3