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.