libgs.visualisation

date:Mon Jul 17 16:38:56 2017
author:Kjetil Wormnes

Overview

This module contains wraps Bokeh code with the focus to provide the elements to build web-apps for a libgs dashboards. It also contains the code necessary to create and runa bokeh server programatically.

Note

While based on Bokeh, this module does not require the Bokeh server to run. Rather the Bokeh server is initiated from within this module. For that reason the syntax for “live” elements (Plots and Widgets) is slightly different to the most common examples in the Bokeh documentation.

The plots and elements in this module can be updated dynamically.

Implementation

Plot elements

This module implements the following specialised plot elements:

A user can make use of any of these, or make their own plots by deriving from the LivePlot class.

All plots are derived from LivePlot and shall implement the following method and attribute:

  • create_fig(): Shall return a Bokeh figure.
  • name: A unique idenitifer for the figure

Additionally they may implement the following attributes if required:

Data registered in live_data will be passed onto the figure ColumnDataSource using the same mapping. And properites in live_props will be updated on the figures as well.

Dashboard Apps

Apps are defined as independent web-apps, and are created by deriving from BokehDash. Apps then need to be added to a BokehServer object, in order to make them available.

BokehDash implmenents the Bokeh Server and keeps track of the different plots that are added to it. When the web-page is requested by a client, it will construct a document for each client using the individual plots’get_fig function.

It will also create ColumnDataSources for those figures, and in a regular callback loop, keep everything up to date with the live_data and live_props dictionaries in the different plots.

With this implementation, it is possible to have multiple clients connect at the same time and see the same data while not having to run bokeh as a separate application.

The apps that are currently available in this module are:

  • TextDash: A simple app that just shows text (or html) which is updatable via a live_prop property
  • TrackDash: A full-fledged dashboard showing antenna tracking status as well as spectra/waterfall from the radios,
    and the current schedule information.

You can create others by deriving from BokehDash and updating the __init__ method.

Tutorial

Set up and manipulate a basic Track Dash

Create a server

>>> server = BokehServer(port=5001, host='localhost')

We will now add a couple of apps to the server, one TrackDash which provides most of the functionality you will need for the ground station dashboard, and ont TextDash which is just a simple full-page text (or html) view.

We will also add navigation links to the apps.

First create some radios to use with the example. You can use the libgs-emulate command-line tool, which comes with the libgs library, to feed these with dummy data if you do not yet have an actual GNU radio.

>>> from hardware import GR_XMLRPCRadio
>>> r1 = GR_XMLRPCRadio(stream =  'localhost:5551', rpcaddr = 'http://localhost:8051', name='TX')
>>> r2 = GR_XMLRPCRadio(stream =  'localhost:5552', rpcaddr = 'http://localhost:8051', name='RX')

Take note of how we used the same rpcaddress for both radios. This is because in this example the two radios may refer to two streams (e.g. rx and tx) in the same GNU radio flowgraph. But that is not a visualisation topic so see libgs.hardware.GR_XMLRPCRadio for details. The visualisation only cares about the stream of IQ values which will allow it to show a waterfall display.

Set the title and navigation links

>>> TITLE = "My Ground Station test"
>>> LINKS = [("/", "Track dash"), ("/log", "Log")]

Now create the dashboard app:

>>> trackdash   = TrackDash(radios = [r1, r2], title = TITLE, links = LINKS)
>>> logdash     = TextDash(title = TITLE, mtype='PreText', plot_name='log', links = LINKS)

TextDash is just a single Markup plot that covers the page. Here we keep it simple and make it plain text (PreText). It is also possible to do html (Div). See TextDash and Markup for details.

And then we simply add the apps to the server on the URIs we decided (the track dash on / and the log dash on /log)

>>> server.add_app('/', self.trackdash)
>>> server.add_app('/log', self.logdash)

And start it:

>>> server.start()

The start method will return immediately as the Tornado IOLoop is started in a separate thread.

Note that this just creates the pages and plot elements. I.e. you can now got to http://localhost:5001 and you should see the dashboard, and if you have started the radio (or radio emulator) on the port you set up you will also see the waterfalls and spectra change.

But you will obviously need to add code to update the other plot elements. This is very simple, you just need to set the appropriate LivePlot attributes.

For example:

>>> passplotter = self.trackdash.get_plot('spass')
>>> track_info  = self.trackdash.get_plot('track_info')
>>> sch_info = self.trackdash.get_plot('sch_info')
>>> libgs_log = self.logdash.get_plot('log')

Then we could plot a pass like this:

>>> az = [0, 10, 20, 30, 40]
>>> el = [0, 45, 60, 45, 0]
>>> passplotter.plot_pass(az, el)

Obviously, normally the az/el values would be coming from the libgs_ops.scheduling.CommsPass. If you try the above you should see the plot update in real time in your browser.

You can set other SatellitePass live_data elements

>>> passplotter.update_data('ant_cmd', (20,20))
>>> passplotter.update_data('sat', (20,30))

etc… You will see the page update in realtime. See SatellitePass for a full reference for this LivePlot element.

track_info, sch_info and libgs_log are all text elements (Markup), you can update the text in a similar way by updating its ‘text’ property. Many elements have properties such as colour, size, etc… You will have to consult the bokeh documentation for a full reference.

>>> libgs_log.live_props['text']= 'This is a test'

You should immediately see the string ‘This is a test’ appear in the log app. You can try the same for the others.

Create a new dasboard

For this example we will create a simplified version of the TrackDash from scratch that only includes a SatellitePass, Frequency.

First lets create the plot elements

>>> waterfall = Waterfall('waterfall', 32000, 0, plot_height=100, toolbar_location=None)
>>> spectrum  = FrequencyPlot('spectrum', radio.get_spectrum, wfall=waterfall, title=radio.name, plot_height = 100, tools="hover")
>>> spectrum.connect()

That last command makes the FrequencyPlot start calling the callback function get_spectrum() to automatically and regularly update itself. It has been connected to the waterfall using wfall= so that will update too.

Create the satellite pass visualisation

>>> spass = SatellitePass('spass', plot_height=5*UNITS, plot_width=300)

And a textbox with some tracking info

>>> track_info  = Markup('track_info', mtype='PreText', text='', width=500)

The first argument in all these cases is the liveplot name, that was used in the first example when calling BokehDash.get_plot().

Now lets create the dashboard

>>> dash = BokehDash(title='My awesome dashboard')
>>> dash.add_plot(waterfall)
>>> dash.add_plot(spectrum)
>>> dash.add_plot(spass)
>>> dash.add_plot(track_info)

Note that the plots will be laid out automatically, and that your dashboard may therefore not look as awesome as you like.

You can fix this by creating a function that returns a bokeh layout, and pass it to the BokehDash constructor’s layout_callback parameter. See the TextDash or TrackDash __init__ implementation as well as bokeh.layouts for how to do this.

Module reference

Functions

azel2rect(az, el) Helper-function to conver az,el (polar coordinates) to x,y for display on rectangular axes
linktext(links) Convenience function to create a series of navigation links to use on the top of the different apps

Classes

BokehDash([update_rate, title, layout_callback]) A bare-bone Bokeh dashboard app.
BokehServer([port, host]) A class to set up and run the bokeh server to serve the dashboard apps.
FrequencyPlot(name, get_spectrum[, wfall, title]) A frequency spectrum plot for use within a libgs dashboard web-application.
LivePlot() Base class for all the plots.
Markup(name, text[, mtype]) A LivePlot wrapper for the three Bokeh Markup widgets;
SatellitePass(name, **fig_args) Visualisation of the sky.
TextDash(title, plot_name[, mtype, links]) A simple Dash app that just contains a single Markup text nbox.
TrackDash([radios, title, links]) A full dashboard featuring:
Waterfall(name[, sample_rate, freq, …]) LivePlot visualisation of a “waterfall plot”.