Counting Planes with PHP

Tim Bond
October 23, 2025

Longhorn PHP Lightning Talks

This talk is as much about radios and airplanes as it is about PHP

  • Automatic
  • Dependent
  • Surveillance
  • Broadcast

ADS-B

  • dump1090 exposes a TCP server
  • data is streamed in SBS-1 format
  • Try watching from the command line:
    • netcat 192.168.1.pi 30003
  • Not a traditional HTTP request/response

Streaming data

Typical HTTP Request/Response

Open connection

Connected

<!DOCTYPE ...

Close connection

GET /index.html

Streaming

Open connection

Connected

data

🕐

data

MSG,4,5,211,4CA2D6,10057,2008/11/28,14:53:49.986,2008/11/28,14:58:51.153,,,408.3,146.4,,,64,,,,,
MSG,8,5,211,4CA2D6,10057,2008/11/28,14:53:50.391,2008/11/28,14:58:51.153,,,,,,,,,,,,0
MSG,4,5,211,4CA2D6,10057,2008/11/28,14:53:50.391,2008/11/28,14:58:51.153,,,408.3,146.4,,,64,,,,,
MSG,3,5,211,4CA2D6,10057,2008/11/28,14:53:50.594,2008/11/28,14:58:51.153,,37000,,,51.45735,-1.02826,,,0,0,0,0
MSG,8,5,812,ABBEE3,10095,2008/11/28,14:53:50.594,2008/11/28,14:58:51.153,,,,,,,,,,,,0
MSG,3,5,276,4010E9,10088,2008/11/28,14:53:49.986,2008/11/28,14:58:51.153,,28000,,,53.02551,-2.91389,,,0,0,0,0
MSG,4,5,276,4010E9,10088,2008/11/28,14:53:50.188,2008/11/28,14:58:51.153,,,459.4,20.2,,,64,,,,,
MSG,8,5,276,4010E9,10088,2008/11/28,14:53:50.594,2008/11/28,14:58:51.153,,,,,,,,,,,,0
MSG,3,5,276,4010E9,10088,2008/11/28,14:53:50.594,2008/11/28,14:58:51.153,,28000,,,53.02677,-2.91310,,,0,0,0,0
MSG,4,5,769,4CA2CB,10061,2008/11/28,14:53:50.188,2008/11/28,14:58:51.153,,,367.7,138.6,,,-2432,,,,,
MSG,8,5,769,4CA2CB,10061,2008/11/28,14:53:50.391,2008/11/28,14:58:51.153,,,,,,,,,,,,0
MSG,1,1,256,7404F2,11267,2008/11/28,14:58:18.611,2008/11/28,14:58:19.161,RJA1118,,,,,,,,,,,
MSG,2,4,603,400CB6,13168,2008/10/13,14:58:32.414,2008/11/28,14:58:52.074,,,0,6.4,58.3,54.05735,-4.38826,,,,,,0
MSG,3,4,211,4CA2D6,10057,2008/11/28,14:58:50.594,2008/11/28,14:58:51.153,,37000,,,51.45735,-1.02826,,,0,0,0,0
MSG,4,4,469,4CA767,27854,2008/11/28,17:58:13.039,2008/11/28,17:58:13.368,,,288.6,103.2,,,-832,,,,,
MSG,5,4,329,394A65,27868,2008/11/28,17:58:12.644,2008/11/28,17:58:13.368,,10000,,,,,,,0,,0,0
MSG,6,4,237,4CA215,27864,2008/11/28,17:58:12.846,2008/11/28,17:58:13.368,,33325,,,,,,0271,0,0,0,0
MSG,7,4,742,51106E,27929,2008/11/28,17:58:36.523,2008/11/28,17:58:37.054,,3775,,,,,,,,,,0
MSG,8,4,194,405F4E,27884,2008/11/28,17:58:13.244,2008/11/28,17:58:13.368,,,,,,,,,,,,0
  • Unique Identifier
  • Altitude
  • Speed
    • Vertical
    • Ground
  • Track (heading)
  • Latitude/Longitude
  • Squawk Code

What's in a message?

Between 0.1 - 2 Hz

  • 24 bit hex code AA0EE3
  • NOT the tail number
  • Store the hex code, look up the aircraft later
  • We can get a database that has:
    • Hex code
    • Registration (tail number)
    • Model
    • Type code
    • More

Each Plane's Unique ID

  • Can't use file_get_contents or curl
  • Use built-in PHP stream methods (or PSR-7)

Drink From The Firehose

$handle = stream_socket_client('tcp://192.168.1.pi:30003');

while(($buffer = fgets($stream)) !== false) {
	echo $buffer;
}
MSG,4,5,211,4CA2D6,10057,2008/11/28,14:53:49.986,2008/11/28,14:58:51.153,,,408.3,146.4,,,64,,,,,
  • DIY or PIE?  afk11/sbs1

Parse Each Message

$streamReader = new StreamReader();
$redis = new Redis();

foreach(
	$streamReader->readTcpStream(
		lineReader: $lineReader,
		host: '192.168.1.pi',
		port: 30003
	) as $line) {
	if(
		$line->getMessageType()->getId() === MessageType::MSG
		&& $line->getHexIdent()
	) {
		// do something with the hex code
	}
}
  • Hash - works the same as a PHP array
  • Key is the hex code
  • Value could be nothing, or anything
    • We'll store the number of messages we've seen

Store The Data in Redis

HINCRBY adsb:planes:2025-10-23 AA0EE3 1
$redis->HINCRBY(
    key: 'adsb:planes:' . date('Y-m-d'),
    field: $line->getHexIdent(),
    value: 1,
);

Put It All Together

<?php

$lineReader = new LineReader(
	new MessageTypeRegistryFactory()->create(),
	new TransmissionTypeRegistryFactory()->create(),
);
$streamReader = new StreamReader();
$redis = new Redis();

foreach($streamReader->readTcpStream($lineReader, '192.168.1.pi', 30003) as $line) {
	if($line->getMessageType()->getId() === MessageType::MSG && $line->getHexIdent()) {
		$redis->HINCRBY('adsb:planes:' . date('Y-m-d'), $line->getHexIdent(), 1);
	}
}

Retrieve Data in Redis

A8BD1B 4540
A44CA0 6421
A45F59 5324
Array
(
    [A8BD1B] => 4540
    [A44CA0] => 6421
    [A45F59] => 5324
)
HGETALL adsb:planes:2025-10-23

Correlate the types

A8BD1B E75L
A44CA0 B738
A45F59 B738
  • Look at multiple fields:
    • Speed is < 250 knots
    • Altitude is below 10,000 feet
    • Heading is Northerly
    • Positive vertical rate
    • Lat/Lon is just north of Renton
    • 👆Probably a 737 on its maiden flight

Other Use Cases

github.com/cookieguru/php-plane-counter

  • Aviation technical talks
    • DEFCON Aerospace Village
  • ADSB feeder image
    • https://adsb.im/
  • SBS-1 decoder, description
    • https://packagist.org/packages/lnx85/php-sbs-reader
    • http://woodair.net/sbs/article/barebones42_socket_data.htm
  • Aircraft database
    • https://github.com/wiedehopf/tar1090-db/
      • Sourced from https://www.mictronics.de/aircraft-database/

Further Reading

Counting Planes with PHP

By Tim Bond

Counting Planes with PHP

Longhorn PHP Lightning Talks - October 23, 2025

  • 26