Creating OzRunways traffic

And why you should use it!

In this post I’ll discuss some technical aspects of how we created the OzRunways traffic system. This will be at a nerd level but others may find it interesting.

OzRunways Traffic

Lets start with the requirements for a traffic system. It must be:

  • Low latency,
  • Reliable,
  • Contain a lot of important information, and
  • Able to be used on flakey mobile data networks with high losses.

We settled on UDP as the protocol of choice. UDP has some good characteristics that make it useful for traffic. For a start, it works extremely well over poor network connections. Imagine UDP as “send send send” vs TCP which involves handshakes and a flow of ‘ack’ packets back and forth to establish a reliable connection. Traffic is time sensitive so if an UDP data packet is lost, we don’t care as the next one to arrive in a few seconds completely replaces the data from the last one.

To use UDP with maximum reliability we had to look at how to send a single packet with all of the information we need without the 3G network fragmenting it into multiple packets. A quick google showed that using less than around 500 bytes for each packet (plus the IP header) should guarantee it being sent out as a single packet across 3G networks.

The basic infrastructure involves the OzRunways iOS client sending out its position, callsign, flight plan, unique identifier and a few other details (climb rate, track etc). Our server is a simple echo server that saves this to a database and sends a single packet back with the details of a bunch of nearby aircraft for displaying on the screen.

The OzRunways client firstly needs to check whether we are flying to send packets. We settled on a simple algorithm that uses a combination of timers, height above ground (using the NASA SRTM terrain database) and forward speed to test whether flying. The timers are used for touch & go’s to allow a minute of slow speed and low height so the system keeps sending packets until taxiing clear of the runway. To protect user privacy, once detected not flying, we send an incorrect user position and disable an airborne bit in the packet so the server still sends back nearby aircraft to display, but does not save the client position in the database to display for other users.

For the actual packet implementation, we needed to squeeze as much data as possible into ~ 500 bytes. The biggest saving is coming up with an implementation for a latitude/longitude pair that can be squeezed into a small number of bits. For example a packet sent back with 10 nearby aircraft with flight plans of 5 points each would contain 50 lat/lon pairs. If we have 6 bytes for each lat/lon, that would chew up 300 bytes. Using a standard 4 byte or 8 byte float/double was therefore not ideal.

For our lat/lon pair we looked at the resolution required by dividing earth up into X segments both vertically and horizontally. Since latitudes are +90 to -90, there is 180 degrees of space available. For longitudes, it’s -180 to +180 = 360 degrees at the equator. Longitudes further north or south would be squished so result in better resolution so we’ll use the Equator for worst case.  Here’s the calculations for latitudes:

1 bit of information = 90 degrees resolution available (180 / 2^1)
2 bits = 45 degrees (180 / 2^2)
8 bits (1 byte) = 0.7 degrees (42nm resolution)
16 bits (2 bytes) = 0.0027 degrees = 0.16nm (300m – we’re getting close).
24 bits (3 bytes) = 1.2m resolution.

Excellent – so instead of a 4 byte float, if we represent +90 to -90 of Earth’s latitude as +0 to +180 sliced up into 16,777,216 pieces, we can just send a 3 byte integer. The decoder can just multiply the number by 0.00001072883606 to get the latitude with about 1-2m resolution, which is perfect for our traffic system.

+ (int)readUnsignedMedium: (const void *)bytes {
    int n=0;
    memcpy(&n, bytes, 3);
    n = n << 8;
    return NTOHL(n);

We did the same calculations for all of the data. We could squeeze a lot of data into 1 byte (for example vertical speed with a resolution of 100 ft/sec) resulting in an overall packet size of ~ 80 bytes (includes 20 byte IP header) when sending our information out. This is why the OzRunways traffic system is so reliable — an 80 byte UDP packet is about the most reliable thing you can send out of a little iPad antenna at 24,000 ft. We tried gz compression but did not get any gains for either 80 or 500 byte packets.




For the echo server, we jam as many aircraft as possible into the return packet until we hit 500 bytes. We include all of their information, timestamp (for dead-reckoning their symbol on the map) and flight plans. We average around 18 aircraft per return packet which is why you only see nearby aircraft. You can see all traffic at

In the end, we have written an entire packet specification mapping out individual bit values including some reserved bits for additional features (e.g. an ’emergency’ bit) should we choose to implement them. We think it’s more flexible than the ADS-B specification as we can send & receive flight plans of other users.

There’s obviously a lot more that goes into the traffic system such as database design (storing tens of millions of data packets for fast access), interfaces for SAR agencies and human-interface design of the traffic icons on screen however I can’t go on forever! Let’s finish with a screenshot of somebody landing in remote QLD:


As you can see, the 3G coverage is very good with UDP. If this person were to crash, we could give SAR agencies an exact position. You can also see the timer working to disable the traffic feed after the pilot has landed and taxi’d off the runway.

Fly safe.



Author: Rowan Willson

Founder of OzRunways; nerd; pilot etc.