Pytrack

Pytrack is a Python implementation of Dave Akerman’s original Pi In The Sky (PITS) software, allowing learners to create their own high-altitude balloon (HAB) tracker in a more novice-friendly language.

It offers several advantages:

  • Learners can create their own tracker in a simpler programming language, rather than simply configuring the existing software
  • The core mechanics of the tracker are exposed for the learner to understand, but complex details are abstracted away
  • Pytrack is modular, allowing learners to experiment with underlying radio components
  • Learners can integrate the technology with standard Python libraries and existing projects

Setup

To install our Pytrack software, simply run the following in a command prompt/terminal window:

sudo apt update
sudo apt install pytrack

Then follow the other installation steps described here.

Getting started

To start a project, you can create a simple tracker application using the Tracker class:

from tracker import *
from time import sleep

mytracker = Tracker()

mytracker.set_lora(payload_id='PIP2', channel=0, frequency=434.150, mode=1)

mytracker.start()

while True:
	sleep(1)

This creates a LoRa tracker, sending GPS and other basic telemetry on 434.150MHz using LoRa mode 1. Note that the Tracker class does all the work here, leaving your application to do other things if it wants; here it just sleeps in a loop.

You can use the same class for slightly more complicated trackers, e.g. with added RTTY telemetry, or added photography with live images sent down to the ground (new code in bold):

from tracker import *
from time import sleep

mytracker = Tracker()

mytracker.set_lora(payload_id='PIP2', channel=0, frequency=434.150, mode=1, image_packet_ratio=6)
mytracker.add_lora_camera_schedule('images/LORA', period=60, width=640, height=480)

mytracker.start()

while True:
	sleep(1)

If you want to extend the functionality, you can use some handy callback functions, allowing the program to override the image and/or data being transmitted. This means that the tracker module “calls back” into your application code so you can add extra telemetry or modify the image being sent (e.g. adding a graphic overlay). Here’s a simple example showing how to extend the telemetry (again, new code in bold):

from tracker import *
from time import sleep

def extra_telemetry():
        # sample code to add one telemetry field
        extra_value = 123.4
        return "{:.1f}".format(extra_value)

mytracker = Tracker()

mytracker.set_lora(payload_id='PIP2', channel=0, frequency=434.150, mode=1)
mytracker.set_sentence_callback(extra_telemetry)

mytracker.start()

while True:
	sleep(1)

So, to summarise, using the tracker module you can:

  • Create a simple tracker using RTTY and/or LoRa radio transmissions
  • Include live images in the transmission for RTTY and/or LoRa streams
  • Take larger images for storage only
  • Extend the telemetry (e.g. add sensor data from the Raspberry Pi Sense HAT)
  • Modify the photography (e.g. use a different camera, change the camera settings, add an overlay)

For more custom applications, it’s better to skip the Tracker class and go down one level to use the individual modules that together make up the class. Let’s start with the GPS module (cgps) to get your current position:

from cgps import *
import time

mygps = GPS()
	
while 1:
	time.sleep(1)
	position = mygps.position()
	print ("Posn: ", position.time, position.lat, position.lon, position.alt)

Assuming the GPS has a position lock, this will list your current position once per second.

You need to send that position over the radio in order to track your balloon, together with code identifying the balloon that the position data refers to. You also need to add something called a CRC (cyclic redundancy check) so that receivers know whether they’ve received the full message correctly. To do this, you use the telemetry module:

from cgps import *
from telemetry import *
import time

mygps = GPS()
SentenceCount = 0
	
while 1:
	time.sleep(1)
	position = mygps.position()
	SentenceCount += 1
	fieldlist = ['MY_SUPER_BALLOON',
                      SentenceCount,
                      position.time,
                      "{:.5f}".format(position.lat),
                      "{:.5f}".format(position.lon),
                      int(position.alt),
                      position.sats]
				 
	sentence = build_sentence(fieldlist)
	print(sentence)

Finally, you just need to send this information to a radio module. You have to change tack slightly here, because the radio transmissions can be slow (taking around 2–20 seconds) and you’ll have to wait till one finishes before you send the next. So replace your time delay code with some new code that waits for the radio module to report that it is ready:

from cgps import *
from telemetry import *
import time
from lora import *

mygps = GPS()
mylora = LoRa(Channel=0, Frequency=434.150, Mode=1)
SentenceCount = 0
	
while 1:
	while mylora.is_sending():
		time.sleep(0.01)
	
	position = mygps.position()
	SentenceCount += 1
	fieldlist = ['MY_SUPER_BALLOON',
				 SentenceCount,
				 position.time,
				 "{:.5f}".format(position.lat),
				 "{:.5f}".format(position.lon),
				 int(position.alt),
				 position.sats]
				 
	sentence = build_sentence(fieldlist)
	print(sentence)
	
	mylora.send_text(sentence)

That’s it — a simple tracker using the basic Pytrack modules, allowing you the flexibility to modify the way it works as you wish. We strongly recommend you read the supplied module documentation (.md files) in the GitHub repo, as the modules provide alternative ways of doing things (e.g. callbacks rather than the polling in the above example) to help you build your application.