Introduction

PaGS Screenshot

PAGS (Python-async Ground Station) is a ground station software suite for MAVLink-based autonomous vehicles, including Ardupilot based vehicles.

It is inspired by the MAVProxy GCS, and features a similar module-based architecture.

The “async” comes from PAGS being entirely based on the asyncio library in Python. This allows for efficient asynchonous processing between the modules and links.

It is designed from the ground up as modular and multi-vehicle.

General Features:

  • Can run in a terminal or GUI
  • Can be used as a GCS, or as the foundation of your own GCS
  • Compatible with Python 3.5+ on Windows or Linux
  • Any number of vehicles can be connected to a single PAGS instance
  • Low number of dependencies
  • Module can be quickly and easily developed for additional functionality
  • Full CI testing with high test coverage

Note

PAGS is still an early work in progress. Many features expected of a GCS are not currently present.

Installation

PaGS is compatible with Python 3.6 - 3.7 on both Linux or Windows.

At the command line:

git clone https://github.com/stephendade/PaGS.git
cd ./PaGS
pip3 install -U -r requirements.txt -r requirements_gui.txt
python3 setup.py build install --user

Under Linux, libSDL may also need to be installed:

sudo apt-get install git libsdl2-2.0-0

If using a headless (no screen) system, omit the -r requirements_gui.txt section in the above.

If installing for development, the test dependencies can be installed by:

pip3 install -U -r ./tests/requirements_test.txt

Usage

PaGS can be used in two ways: either as a standalone GCS or as part of a larger application.

Standalone

To run standalone:

pags.py

The following commandline arguments can be used:

  • --source=tcpclient:127.0.0.1:5760:1:0 Connection in format connectiontype:connectionstr:sys:comp
  • --mav=2 Mavlink Version (1 or 2)
  • --dialect=ardupilotmega MAVLink dialect
  • --source-system=255 MAVLink source system for this GCS
  • --source-component=0 MAVLink source component for this GCS
  • --multi
  • --nogui Disable usage of a GUI
  • --sitl=n Connect to Ardupilot SITL instance, where n is the instance ID (ID is required).

(Default values of each argument are shown above).

For the connection sources (--source), the connection types can be:

  • tcpclient with the connectionstr being remoteip:port
  • tcpserver with the connectionstr being localip:port
  • udpserver with the connectionstr being localip:port
  • udpclient with the connectionstr being remoteip:port
  • serial with the connectionstr being serialport:baud, ie source=serial:COM17:115200:1:0

The sys is the System ID of the remote vehicle and the comp is the component ID of the vehicle. These are typically 1 and 0 respectively for Ardupilot with the default parameters.

Multiple --source can be used. Each system ID is assumed to be a different vehicle. Thus multiple connections to a single vehicle can be used.

In the alternate case, where multiple vehicles (each with a different System ID) are on a single connection, simply repeat the --source with the same connectionstr and the relevent (differerent) source ID’s.

If using the --sitl options, multiple connections to different APM SITL instances can be used. For example, to connect to 3 SITL instances: --sitl=0 --sitl=1 --sitl=2

If neither the --source and --sitl arguments are used, PaGS will first look for any USB-connected flight controllers and attempt to connect at a buad rate of 115200, otherwise it will connect to a UDP server on localhost, port 14550.

As an example:

  • Vehicle 1 (System ID 1) and Vehicle 3 (System ID) are both on serial port COM17, baud 57600
  • Vehicle 2 (System ID 22) is on tcpserver 192.168.0.1:14500 and a secondary link on udpclient 192.168.0.10:14600

Gives:

pags.py --source=serial:COM17:57600:1:0 --source=serial:COM17:57600:3:0 --source=tcpserver:192.168.0.1:14500:22:0 -source=udpclient:192.168.0.10:14600:22:0

As a Library

Modules

Modules are plugins for PaGS that can read and write packets to vehicles.

For example, a module might read the system status onto a GUI, or command the vehicle to change mode.

Modules can be loaded by module load xxx, where xx is the module library path.

The current set of loaded modules can be listed via module list.

Terminal Module

module load terminalModule

Summary

This module provides a terminal-based UI for displaying vehicle status and sending commands to vehicles.

Each vehicle has it’s own tab and command interpreter.

Use ctrl+right and ctrl+left to navigate through the vehicle tabs.

The prompt show the vehicle’s current mode and arming status. “D” for disarmed and “A” for armed.

Example of terminal module running

Commands

N/A

Mode Module

module load modeModule

Summary

The module allows the mode and arming status of the vehicle to be controlled.

Example of mode module running

Commands

mode list. List the valid modes for the vehicle

mode do <mode>. Switch to mode <mode>. The <mode> is not case sensitive.

mode arm. Send an arming command to the vehicle

mode disarm. Send a disarm command to the vehicle.

mode reboot. Reboot the Flight Controller.

Terminal Module

module load paramModule

Summary

This module provides commands an a GUI for reading and writing vehicle parameters.

Note that the parameters are not checked for correctness before being sent to the vehicle.

Example of param module running

Commands

param download. Download (or refresh) the parameters from the vehicle. Required before any other parameters commands can be used.

param show <param>. Show a parameter’s current value. Wildcards can be used, for example param show RC1_*

param set <param>. Set a new value for a parameter.

param load <filename>. Load the parameters from file.

param save <filename>. Save the parameters to file.

GUI

<screenshot>

The GUI will automatically populate after a param download has been performed. Each vehicle will have it’s own tab in the window.

The text field at the top can be used to filter parameters.

When a parameter is edited, it will be highlighted until written to the vehicle.

Status Module

module load statusModule

Summary

The module displays the current sub-system status of the Vehicle, both in the console and in a GUI

Example of status module output

In the GUI, the following colours are used for the subsystem status:

  • Grey: System not present
  • Black: Present, not enabled
  • Red: Present, enabled, not healthy
  • Green: Present, enabled, healthy

Commands

status status. Show the current status of the vehicle.

Development

Installing

See the Installation section

Testing and CI

All tests are in the ./tests folder.

Windows users will need the com0com software.

Linux users will need socat installed via sudo apt install socat`

Tests can be run via:

python3 setup.py build install --user
py.test --log-level DEBUG

The CI uses Appveyor to run a build matrix of Windows/Linux and Python 3.5/3.6/3.7. So 6 runs total.

Coveralls is used to check the test coverage. This can be run manually via the ./scripts/run_pytest_coverage.sh script

All changes should be compliant with the PEP8 standard. This is checked as part of the CI processes.

The PEP8 checks can be run via the ./scripts/flake8check.sh script.

Modules

Modules must be placed in the ./PaGS/PaGS/modules folder

There is a template available at ./PaGS/PaGS/modules/blankModule.py, or see below:

"""
<Module Description>
"""

class Module():
    """
    <Module Description>
    """
    def __init__(self, loop, txClbk, vehListClk, vehObjClk, cmdProcessClk, prntr, isGUI):
        """
        Called by PaGS when a module is loaded 'module load xxx'
        """
        # Call this to send out a MAVLink packet
        self.txCallback = txClbk
        # Call this to get a list of current vehicles
        self.vehListCallback = vehListClk
        # Call this to get a Vehicle object by name
        self.vehObjCallback = vehObjClk
        # Call this to print to the console(s)
        self.printer = prntr
        # true if we're running in a GUI environment
        self.isGUI = isGUI

        # The short name of the module.
        self.shortName = ""
        # A dict of user commands. Key is the string name, value is the function to run
        self.commandDict = {}

    def addVehicle(self, name: str):
        """
        Called by PaGS when a new vehicle is added
        """
        pass

    def incomingPacket(self, vehname: str, pkt):
        """
        Called by PaGS when a decoded valid MAVLink packet is recieved from a vehicle
        """
        pass

    def removeVehicle(self, name: str):
        """
        Called by PaGS when a vehicle is removed
        """
        pass

    def closeModule(self):
        """
        Called by PaGS when the module is shut down
        """
        pass

If modules have a GUI, they should respect the isGUI parameter. They should use the wxPython (with wxAsync) GUI library for consistency. For saving/loading window position and sizes, use the wxPersisent class: <example of both>

Modules are free to set/get attributes in the vehicle classes, but they should not assume they are present.

Any commonly used vehicle attributes should be managed from within the vehicle class - parameters, waypoints, etc.

PAGS has a common cache/user setting directory at <>. It can be accessed from the <> attribute.

Each vehicle has it’s own directory <accessed via the .. attribute>, where per vehicle files go - logs, parameter and waypoint files.

Each module should, where practical, test it’s functionality within the unit test suite.

Reference

ConnectionManager