PiCap


IR-GIS ® piCap - is used to capture and process live-to-air real-time ADS-B position reports from aircraft, including their sCode, squawk id, speed, altitude, flightId - from which aircraft can be tracked and identified. The data is formatted in ir-xml and fed into the IR-GIS ® system. The sCode can also be used to index a platform database to obtain the aircraft tail-id, various levels of completeness are available in Open Source databases.
The following images are photographs from when I first ingested real-time data into the 3D IR-GIS ® MapWorld (or is that RealWorld as I have currently set in the application title-bar).
The flightaware ADS-B reception fork I used for my raspberry pi applications still provides a legacy SBS port for connection to the "Beast". I used this port 30003 to acquire aircraft by processing the messages described here http://woodair.net/sbs/article/barebones42_socket_data.htm
I initially wrote the capture code in Perl code - because I thought it would be fun to see what was happening. I very quickly wrote Java code, which was easier to integrate with JMS and provided that as part of the IR-GIS ® utility toolset.
I will explore submitting JSON requests to the webserver sometime in the future to obtain "richer" data - I just wanted to get this going to demonstrate IR-GIS ® MapWorld capabilities for my planned marketing trips.
Some example code
I actually wrote this following code in Perl to see how compact I could make the code and it was fun. (Note I am an old C++ gun and I have reluctantly written real-time code in Java, and well Perl works surprisingly well in this case too.)
The Aircraft.pm Object
# History:
# 20190908 Ralph Holland - initial writing
#
# object to contain Aircraft details
package Aircraft;
use strict;
sub new {
my $class = shift;
my %options = @_;
my $self = {
scode => 0,
flightId => undef,
gs => undef,
lat => undef,
lon => undef,
alt => undef,
track => undef,
timestamp => undef,
%options,
};
bless($self,$class);
return ($self);
}
sub setSurfacePosition {
# my ($self,$aref) = @_;
my $self = shift ;
my $aref = shift ;
$self->{alt} = $aref->[11] ;
$self->{gs} = $aref->[12] ;
$self->{track} = $aref->[13] ;
$self->{lat} = $aref->[14] ;
$self->{lon} = $aref->[15] ;
}
sub setAirbornePosition {
my ($self,$aref) = @_;
$self->{alt}=@{$aref}[11];
$self->{lon}=@{$aref}[15];
}
sub setAirborneVelocity {
my ($self,$aref) = @_;
$self->{gs}=@{$aref}[12];
$self->{track}=@{$aref}[13];
$self->{rate}=@$aref[16];
}
sub setSurveillanceAlt {
my ($self,$aref)= @_;
my $val ='';
if (@{$aref}[20] eq '1') {
$val = 'ground';
}
else {
$val = @{$aref}[11];
}
$self->{alt}=$val;
}
sub setSurveillanceId {
my ($self,$aref)= @_;
my $val;
if (@{$aref}[20] eq '1') {
$val = 'ground';
}
else {
$val = @{$aref}[11];
}
$self->{alt}=$val;
$self->{squawk}=@{$aref}[17];
}
# this is a setter & a getter (using the well-known perl pattern)
sub timestamp {
my ($self, $value) = @_;
if (@_ == 2) {
$self->{timestamp}= $value;
}
return $self->{timestamp};
}
sub setFlightId {
my $self = shift;
if (@_) {
my $val = shift;
$val =~ s/^\s+|\s+$//g;
chomp($val);
$self->{flightId} = $val;
}
}
The main code piCap.pl:
#!/usr/bin/perl
# History:
# 20190908 Ralph Holland - initial writing
use lib '.';
use Aircraft;
use IO::Socket;
use Switch;
use Data::Dumper;
use strict;
use Date::Parse;
use Date::Language;
my $gRemoteHost = "10.2.1.136";
my $gPort = 30003;
my $socket = new IO::Socket::INET (
PeerAddr=> $gRemoteHost,
PeerPort=> $gPort,
Proto => 'tcp'
) or die "Error in Socket Creation : $!\n";
print "Waiting for server $gRemoteHost port $gPort\n";
my %aircraft = ( );
sub capture {
print "%aircraft\n";
while(1) {
my $data = <$socket>;
chomp($data);
my @values = split(',', $data);
my $length = @values;
if ($length > 4) {
my $scode = $values[4];
$scode =~ s/^\s+|\s+$//g;
if (defined $scode and length $scode == 6) {
my $type = $values[0];
my $craft;
if (exists( $aircraft{$scode} )) {
$craft = $aircraft{$scode};
}
else {
$craft = new Aircraft( scode=>$scode );
$aircraft{$scode}=$craft;
}
# Change the separators / to -
my $genDate = $values[6];
$genDate=~ s,/,-,g;
$genDate = $genDate . 'T' . $values[7];
# print "date=$genDate\n";
my $dateTime = str2time($genDate);
$craft->timestamp($dateTime);
switch($type) {
case 'MSG' {
my $index = $values[1];
print "$data\n$type$index\n";
switch($values[1]) {
case 1 {
$craft->setFlightId($values[10])
}
case 2 {
$craft->setSurfacePosition(\@values);
}
case 3 {
$craft->setAirbornePosition(\@values);
}
case 4 {
$craft->setAirborneVelocity(\@values);
}
case 5 {
$craft->setSurveillanceAlt(\@values);
}
case 6 {
$craft->setSurveillanceId(\@values);
}
case 7 {
}
case 8 {
}
else {}
}
print Dumper($craft);
}
case 'SEL' {
}
case 'ID' {
}
else { }
}
}
}
# Now remove stale entries
while (my ($key, $value) = each %aircraft) {
my $age = time() - $value->{timestamp};
if ($age > 240) {
print "droping aircraft=$key age=$age\n";
delete $aircraft{$key}; # This is safe
}
}
}
}
capture();
This was very easy to rewrite in Java, and to incoporate into the IR-GIS ® product suite.

