PiCap

From ir-gis.com (external)
First ILS track at Canberra Airport rendered in 3D. That hill obscures the transission at my location, so I lose the track
First takeoff from Canberra Airport rendered in 3D. I can see a fair part of the track, but again local terrain obscures the reception.

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.