2015-02-08 15:27:03 +01:00
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
//
// track.c: aircraft state tracking
//
2016-08-27 15:34:14 +02:00
// Copyright (c) 2014-2016 Oliver Jowett <oliver@mutability.co.uk>
2015-02-08 15:27:03 +01:00
//
2017-06-15 19:16:51 +02:00
// This file is free software: you may copy, redistribute and/or modify it
2015-02-08 15:27:03 +01:00
// under the terms of the GNU General Public License as published by the
2017-06-15 19:16:51 +02:00
// Free Software Foundation, either version 2 of the License, or (at your
// option) any later version.
2015-02-08 15:27:03 +01:00
//
2017-06-15 19:16:51 +02:00
// This file is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2015-02-08 15:27:03 +01:00
// General Public License for more details.
//
2017-06-15 19:16:51 +02:00
// You should have received a copy of the GNU General Public License
2015-02-08 15:27:03 +01:00
// along with this program. If not, see <http://www.gnu.org/licenses/>.
2017-06-15 19:16:51 +02:00
// This file incorporates work covered by the following copyright and
2015-02-08 15:27:03 +01:00
// permission notice:
//
// Copyright (C) 2012 by Salvatore Sanfilippo <antirez@gmail.com>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# include "dump1090.h"
2016-08-27 15:34:14 +02:00
# include <inttypes.h>
2015-02-08 15:27:03 +01:00
2015-02-18 01:12:35 +01:00
/* #define DEBUG_CPR_CHECKS */
2016-10-11 19:00:11 +02:00
uint32_t modeAC_count [ 4096 ] ;
uint32_t modeAC_lastcount [ 4096 ] ;
uint32_t modeAC_match [ 4096 ] ;
2016-10-11 22:29:39 +02:00
uint32_t modeAC_age [ 4096 ] ;
2016-10-11 19:00:11 +02:00
2015-02-08 15:27:03 +01:00
//
// Return a new aircraft structure for the linked list of tracked
// aircraft
//
struct aircraft * trackCreateAircraft ( struct modesMessage * mm ) {
static struct aircraft zeroAircraft ;
struct aircraft * a = ( struct aircraft * ) malloc ( sizeof ( * a ) ) ;
int i ;
// Default everything to zero/NULL
* a = zeroAircraft ;
// Now initialise things that should not be 0/NULL to their defaults
a - > addr = mm - > addr ;
2016-09-14 17:54:00 +02:00
a - > addrtype = mm - > addrtype ;
2015-02-08 15:27:03 +01:00
for ( i = 0 ; i < 8 ; + + i )
2016-02-24 12:51:45 +01:00
a - > signalLevel [ i ] = 1e-5 ;
a - > signalNext = 0 ;
2015-02-08 15:27:03 +01:00
2017-12-07 17:34:08 +01:00
// defaults until we see a message otherwise
a - > adsb_version = - 1 ;
a - > adsb_hrd = HEADING_MAGNETIC ;
a - > adsb_tah = HEADING_GROUND_TRACK ;
// prime FATSV defaults we only emit on change
2016-09-24 17:09:38 +02:00
// start off with the "last emitted" ACAS RA being blank (just the BDS 3,0
// or ES type code)
2016-08-28 02:29:37 +02:00
a - > fatsv_emitted_bds_30 [ 0 ] = 0x30 ;
2016-09-24 17:09:38 +02:00
a - > fatsv_emitted_es_acas_ra [ 0 ] = 0xE2 ;
2017-12-07 17:34:08 +01:00
a - > fatsv_emitted_adsb_version = - 1 ;
a - > fatsv_emitted_addrtype = ADDR_UNKNOWN ;
2016-08-28 02:29:37 +02:00
2017-12-07 17:34:08 +01:00
// don't immediately emit, let some data build up
a - > fatsv_last_emitted = a - > fatsv_last_force_emit = messageNow ( ) ;
2017-06-15 22:07:53 +02:00
If we squelch the first message from an aircraft, emit it when we see a second message.
This is possible now that the SBS output doesn't rely on the global block timestamp;
the output will look like this:
MSG,8,111,11111,4AC954,111111,2015/02/08,17:57:53.917,2015/02/08,17:57:53.936,,,,,,,,,,,,0
MSG,7,111,11111,392AEB,111111,2015/02/08,17:57:53.744,2015/02/08,17:57:53.936,,15375,,,,,,,,,,0
MSG,8,111,11111,392AEB,111111,2015/02/08,17:57:53.917,2015/02/08,17:57:53.936,,,,,,,,,,,,0
MSG,6,111,11111,800387,111111,2015/02/08,17:57:53.919,2015/02/08,17:57:53.936,,,,,,,,4745,0,0,0,0
where the "receive timestamp" (first time column) goes backwards to reflect the original reception
time of the delayed message, but the "forwarded timestamp" (second time column) reflects the actual
forwarding time.
2015-02-08 19:00:18 +01:00
// Copy the first message so we can emit it later when a second message arrives.
a - > first_message = * mm ;
2017-12-02 18:38:33 +01:00
// initialize data validity ages
# define F(f,s,e) do { a->f##_valid.stale_interval = (s) * 1000; a->f##_valid.expire_interval = (e) * 1000; } while (0)
F ( callsign , 60 , 70 ) ; // ADS-B or Comm-B
2017-12-07 17:34:08 +01:00
F ( altitude_baro , 15 , 70 ) ; // ADS-B or Mode S
2017-12-02 18:38:33 +01:00
F ( altitude_geom , 60 , 70 ) ; // ADS-B only
F ( geom_delta , 60 , 70 ) ; // ADS-B only
F ( gs , 60 , 70 ) ; // ADS-B or Comm-B
F ( ias , 60 , 70 ) ; // ADS-B (rare) or Comm-B
F ( tas , 60 , 70 ) ; // ADS-B (rare) or Comm-B
F ( mach , 60 , 70 ) ; // Comm-B only
F ( track , 60 , 70 ) ; // ADS-B or Comm-B
F ( track_rate , 60 , 70 ) ; // Comm-B only
F ( roll , 60 , 70 ) ; // Comm-B only
F ( mag_heading , 60 , 70 ) ; // ADS-B (rare) or Comm-B
F ( true_heading , 60 , 70 ) ; // ADS-B only (rare)
F ( baro_rate , 60 , 70 ) ; // ADS-B or Comm-B
F ( geom_rate , 60 , 70 ) ; // ADS-B or Comm-B
F ( squawk , 15 , 70 ) ; // ADS-B or Mode S
F ( airground , 15 , 70 ) ; // ADS-B or Mode S
2017-12-07 17:34:08 +01:00
F ( nav_qnh , 60 , 70 ) ; // Comm-B only
F ( nav_altitude , 60 , 70 ) ; // ADS-B or Comm-B
F ( nav_heading , 60 , 70 ) ; // ADS-B or Comm-B
F ( nav_modes , 60 , 70 ) ; // ADS-B or Comm-B
2017-12-02 18:38:33 +01:00
F ( cpr_odd , 60 , 70 ) ; // ADS-B only
F ( cpr_even , 60 , 70 ) ; // ADS-B only
F ( position , 60 , 70 ) ; // ADS-B only
2017-12-07 17:34:08 +01:00
F ( nic_a , 60 , 70 ) ; // ADS-B only
F ( nic_c , 60 , 70 ) ; // ADS-B only
F ( nic_baro , 60 , 70 ) ; // ADS-B only
F ( nac_p , 60 , 70 ) ; // ADS-B only
F ( nac_v , 60 , 70 ) ; // ADS-B only
F ( sil , 60 , 70 ) ; // ADS-B only
F ( gva , 60 , 70 ) ; // ADS-B only
F ( sda , 60 , 70 ) ; // ADS-B only
2017-12-02 18:38:33 +01:00
# undef F
2015-02-08 19:47:39 +01:00
Modes . stats_current . unique_aircraft + + ;
2015-02-08 15:27:03 +01:00
return ( a ) ;
}
//
//=========================================================================
//
// Return the aircraft with the specified address, or NULL if no aircraft
// exists with this address.
//
struct aircraft * trackFindAircraft ( uint32_t addr ) {
struct aircraft * a = Modes . aircrafts ;
while ( a ) {
if ( a - > addr = = addr ) return ( a ) ;
a = a - > next ;
}
return ( NULL ) ;
}
2016-08-27 15:34:14 +02:00
// Should we accept some new data from the given source?
// If so, update the validity and return 1
2017-12-02 18:38:33 +01:00
static int accept_data ( data_validity * d , datasource_t source )
2016-08-27 15:34:14 +02:00
{
2017-12-02 18:38:33 +01:00
if ( messageNow ( ) < d - > updated )
return 0 ;
if ( source < d - > source & & messageNow ( ) < d - > stale )
2016-08-27 15:34:14 +02:00
return 0 ;
d - > source = source ;
2017-12-02 18:38:33 +01:00
d - > updated = messageNow ( ) ;
2017-12-07 17:34:08 +01:00
d - > stale = messageNow ( ) + ( d - > stale_interval ? d - > stale_interval : 60000 ) ;
d - > expires = messageNow ( ) + ( d - > expire_interval ? d - > expire_interval : 70000 ) ;
2016-08-27 15:34:14 +02:00
return 1 ;
}
// Given two datasources, produce a third datasource for data combined from them.
static void combine_validity ( data_validity * to , const data_validity * from1 , const data_validity * from2 ) {
if ( from1 - > source = = SOURCE_INVALID ) {
* to = * from2 ;
return ;
}
if ( from2 - > source = = SOURCE_INVALID ) {
* to = * from1 ;
return ;
}
to - > source = ( from1 - > source < from2 - > source ) ? from1 - > source : from2 - > source ; // the worse of the two input sources
to - > updated = ( from1 - > updated > from2 - > updated ) ? from1 - > updated : from2 - > updated ; // the *later* of the two update times
to - > stale = ( from1 - > stale < from2 - > stale ) ? from1 - > stale : from2 - > stale ; // the earlier of the two stale times
to - > expires = ( from1 - > expires < from2 - > expires ) ? from1 - > expires : from2 - > expires ; // the earlier of the two expiry times
}
2017-12-02 18:38:33 +01:00
static int compare_validity ( const data_validity * lhs , const data_validity * rhs ) {
if ( messageNow ( ) < lhs - > stale & & lhs - > source > rhs - > source )
2016-08-27 15:34:14 +02:00
return 1 ;
2017-12-02 18:38:33 +01:00
else if ( messageNow ( ) < rhs - > stale & & lhs - > source < rhs - > source )
2016-08-27 15:34:14 +02:00
return - 1 ;
else if ( lhs - > updated > rhs - > updated )
return 1 ;
else if ( lhs - > updated < rhs - > updated )
return - 1 ;
else
return 0 ;
}
2015-02-08 15:27:03 +01:00
//
// CPR position updating
//
// Distance between points on a spherical earth.
// This has up to 0.5% error because the earth isn't actually spherical
// (but we don't use it in situations where that matters)
static double greatcircle ( double lat0 , double lon0 , double lat1 , double lon1 )
{
2016-08-26 12:36:29 +02:00
double dlat , dlon ;
2015-02-08 15:27:03 +01:00
lat0 = lat0 * M_PI / 180.0 ;
lon0 = lon0 * M_PI / 180.0 ;
lat1 = lat1 * M_PI / 180.0 ;
lon1 = lon1 * M_PI / 180.0 ;
2015-02-18 02:53:47 +01:00
2016-08-26 12:36:29 +02:00
dlat = fabs ( lat1 - lat0 ) ;
dlon = fabs ( lon1 - lon0 ) ;
// use haversine for small distances for better numerical stability
if ( dlat < 0.001 & & dlon < 0.001 ) {
double a = sin ( dlat / 2 ) * sin ( dlat / 2 ) + cos ( lat0 ) * cos ( lat1 ) * sin ( dlon / 2 ) * sin ( dlon / 2 ) ;
return 6371e3 * 2 * atan2 ( sqrt ( a ) , sqrt ( 1.0 - a ) ) ;
}
2015-02-18 02:53:47 +01:00
2016-08-26 12:36:29 +02:00
// spherical law of cosines
return 6371e3 * acos ( sin ( lat0 ) * sin ( lat1 ) + cos ( lat0 ) * cos ( lat1 ) * cos ( dlon ) ) ;
2015-02-08 15:27:03 +01:00
}
2015-06-19 18:29:14 +02:00
static void update_range_histogram ( double lat , double lon )
{
if ( Modes . stats_range_histo & & ( Modes . bUserFlags & MODES_USER_LATLON_VALID ) ) {
double range = greatcircle ( Modes . fUserLat , Modes . fUserLon , lat , lon ) ;
int bucket = round ( range / Modes . maxRange * RANGE_BUCKET_COUNT ) ;
if ( bucket < 0 )
bucket = 0 ;
else if ( bucket > = RANGE_BUCKET_COUNT )
bucket = RANGE_BUCKET_COUNT - 1 ;
+ + Modes . stats_current . range_histogram [ bucket ] ;
}
}
2015-02-18 01:12:35 +01:00
// return true if it's OK for the aircraft to have travelled from its last known position
// to a new position at (lat,lon,surface) at a time of now.
2017-12-02 18:38:33 +01:00
static int speed_check ( struct aircraft * a , double lat , double lon , int surface )
2015-02-18 01:12:35 +01:00
{
uint64_t elapsed ;
double distance ;
double range ;
int speed ;
2015-02-18 02:53:47 +01:00
int inrange ;
2015-02-18 01:12:35 +01:00
2016-08-27 15:34:14 +02:00
if ( ! trackDataValid ( & a - > position_valid ) )
2015-02-18 01:12:35 +01:00
return 1 ; // no reference, assume OK
2017-12-02 18:38:33 +01:00
elapsed = trackDataAge ( & a - > position_valid ) ;
2015-02-18 01:12:35 +01:00
2017-06-15 19:07:40 +02:00
if ( trackDataValid ( & a - > gs_valid ) )
speed = a - > gs ;
else if ( trackDataValid ( & a - > tas_valid ) )
speed = a - > tas * 4 / 3 ;
else if ( trackDataValid ( & a - > ias_valid ) )
speed = a - > ias * 2 ;
2015-02-18 19:26:23 +01:00
else
speed = surface ? 100 : 600 ; // guess
2015-02-18 01:12:35 +01:00
// Work out a reasonable speed to use:
2015-02-18 19:26:23 +01:00
// current speed + 1/3
// surface speed min 20kt, max 150kt
// airborne speed min 200kt, no max
speed = speed * 4 / 3 ;
if ( surface ) {
if ( speed < 20 )
speed = 20 ;
if ( speed > 150 )
speed = 150 ;
2015-02-18 01:12:35 +01:00
} else {
2015-02-18 19:26:23 +01:00
if ( speed < 200 )
speed = 200 ;
2015-02-18 01:12:35 +01:00
}
// 100m (surface) or 500m (airborne) base distance to allow for minor errors,
// plus distance covered at the given speed for the elapsed time + 1 second.
range = ( surface ? 0.1e3 : 0.5e3 ) + ( ( elapsed + 1000.0 ) / 1000.0 ) * ( speed * 1852.0 / 3600.0 ) ;
// find actual distance
distance = greatcircle ( a - > lat , a - > lon , lat , lon ) ;
2015-02-18 02:53:47 +01:00
inrange = ( distance < = range ) ;
2015-02-18 01:12:35 +01:00
# ifdef DEBUG_CPR_CHECKS
2015-02-18 02:53:47 +01:00
if ( ! inrange ) {
2015-02-18 01:12:35 +01:00
fprintf ( stderr , " Speed check failed: %06x: %.3f,%.3f -> %.3f,%.3f in %.1f seconds, max speed %d kt, range %.1fkm, actual %.1fkm \n " ,
a - > addr , a - > lat , a - > lon , lat , lon , elapsed / 1000.0 , speed , range / 1000.0 , distance / 1000.0 ) ;
}
# endif
2015-02-18 02:53:47 +01:00
return inrange ;
2015-02-18 01:12:35 +01:00
}
2017-12-07 17:34:08 +01:00
static int doGlobalCPR ( struct aircraft * a , struct modesMessage * mm , double * lat , double * lon , unsigned * nic , unsigned * rc )
2015-02-08 15:27:03 +01:00
{
int result ;
2016-08-27 15:34:14 +02:00
int fflag = mm - > cpr_odd ;
2016-10-02 01:16:29 +02:00
int surface = ( mm - > cpr_type = = CPR_SURFACE ) ;
2015-02-18 01:12:35 +01:00
2017-12-07 17:34:08 +01:00
// derive NIC, Rc from the worse of the two position
// smaller NIC is worse; larger Rc is worse
* nic = ( a - > cpr_even_nic < a - > cpr_odd_nic ? a - > cpr_even_nic : a - > cpr_odd_nic ) ;
* rc = ( a - > cpr_even_rc > a - > cpr_odd_rc ? a - > cpr_even_rc : a - > cpr_odd_rc ) ;
2015-02-08 15:27:03 +01:00
if ( surface ) {
// surface global CPR
// find reference location
double reflat , reflon ;
2017-12-02 18:38:33 +01:00
if ( trackDataValid ( & a - > position_valid ) ) { // Ok to try aircraft relative first
2015-02-08 15:27:03 +01:00
reflat = a - > lat ;
reflon = a - > lon ;
} else if ( Modes . bUserFlags & MODES_USER_LATLON_VALID ) {
reflat = Modes . fUserLat ;
reflon = Modes . fUserLon ;
} else {
// No local reference, give up
return ( - 1 ) ;
}
result = decodeCPRsurface ( reflat , reflon ,
2016-08-27 15:34:14 +02:00
a - > cpr_even_lat , a - > cpr_even_lon ,
a - > cpr_odd_lat , a - > cpr_odd_lon ,
2015-02-08 15:27:03 +01:00
fflag ,
2015-02-18 01:12:35 +01:00
lat , lon ) ;
2015-02-08 15:27:03 +01:00
} else {
// airborne global CPR
2016-08-27 15:34:14 +02:00
result = decodeCPRairborne ( a - > cpr_even_lat , a - > cpr_even_lon ,
a - > cpr_odd_lat , a - > cpr_odd_lon ,
2015-02-08 15:27:03 +01:00
fflag ,
2015-02-18 01:12:35 +01:00
lat , lon ) ;
2015-02-08 15:27:03 +01:00
}
2015-06-29 13:43:58 +02:00
if ( result < 0 ) {
# ifdef DEBUG_CPR_CHECKS
2016-08-27 15:34:14 +02:00
fprintf ( stderr , " CPR: decode failure for %06X (%d). \n " , a - > addr , result ) ;
fprintf ( stderr , " even: %d %d odd: %d %d fflag: %s \n " ,
a - > cpr_even_lat , a - > cpr_even_lon ,
a - > cpr_odd_lat , a - > cpr_odd_lon ,
fflag ? " odd " : " even " ) ;
2015-06-29 13:43:58 +02:00
# endif
return result ;
}
2015-02-08 15:27:03 +01:00
// check max range
if ( Modes . maxRange > 0 & & ( Modes . bUserFlags & MODES_USER_LATLON_VALID ) ) {
2015-02-18 01:12:35 +01:00
double range = greatcircle ( Modes . fUserLat , Modes . fUserLon , * lat , * lon ) ;
if ( range > Modes . maxRange ) {
# ifdef DEBUG_CPR_CHECKS
fprintf ( stderr , " Global range check failed: %06x: %.3f,%.3f, max range %.1fkm, actual %.1fkm \n " ,
a - > addr , * lat , * lon , Modes . maxRange / 1000.0 , range / 1000.0 ) ;
# endif
Modes . stats_current . cpr_global_range_checks + + ;
2015-02-08 15:27:03 +01:00
return ( - 2 ) ; // we consider an out-of-range value to be bad data
2015-02-18 01:12:35 +01:00
}
2015-02-08 15:27:03 +01:00
}
2016-02-05 16:41:42 +01:00
// for mlat results, skip the speed check
2016-08-27 15:34:14 +02:00
if ( mm - > source = = SOURCE_MLAT )
2016-02-05 16:41:42 +01:00
return result ;
2015-02-18 01:12:35 +01:00
// check speed limit
2017-12-07 17:34:08 +01:00
if ( trackDataValid ( & a - > position_valid ) & & a - > pos_nic > = * nic & & a - > pos_rc < = * rc & & ! speed_check ( a , * lat , * lon , surface ) ) {
2015-02-18 01:12:35 +01:00
Modes . stats_current . cpr_global_speed_checks + + ;
return - 2 ;
}
return result ;
2015-02-08 15:27:03 +01:00
}
2017-12-07 17:34:08 +01:00
static int doLocalCPR ( struct aircraft * a , struct modesMessage * mm , double * lat , double * lon , unsigned * nic , unsigned * rc )
2015-02-08 15:27:03 +01:00
{
// relative CPR
// find reference location
2015-02-18 01:12:35 +01:00
double reflat , reflon ;
2015-02-08 15:27:03 +01:00
double range_limit = 0 ;
int result ;
2016-08-27 15:34:14 +02:00
int fflag = mm - > cpr_odd ;
2016-10-02 01:16:29 +02:00
int surface = ( mm - > cpr_type = = CPR_SURFACE ) ;
2015-02-08 15:27:03 +01:00
2017-12-07 17:34:08 +01:00
if ( fflag ) {
* nic = a - > cpr_odd_nic ;
* rc = a - > cpr_odd_rc ;
} else {
* nic = a - > cpr_even_nic ;
* rc = a - > cpr_even_rc ;
}
2015-02-08 15:27:03 +01:00
2017-12-02 18:38:33 +01:00
if ( trackDataValid ( & a - > position_valid ) ) {
2015-02-08 15:27:03 +01:00
reflat = a - > lat ;
reflon = a - > lon ;
2017-12-07 17:34:08 +01:00
if ( a - > pos_nic < * nic )
* nic = a - > pos_nic ;
if ( a - > pos_rc < * rc )
* rc = a - > pos_rc ;
2016-08-26 12:30:46 +02:00
range_limit = 50e3 ;
2015-02-08 15:27:03 +01:00
} else if ( ! surface & & ( Modes . bUserFlags & MODES_USER_LATLON_VALID ) ) {
reflat = Modes . fUserLat ;
reflon = Modes . fUserLon ;
2017-06-15 19:16:51 +02:00
2015-02-08 15:27:03 +01:00
// The cell size is at least 360NM, giving a nominal
// max range of 180NM (half a cell).
//
// If the receiver range is more than half a cell
// then we must limit this range further to avoid
// ambiguity. (e.g. if we receive a position report
// at 200NM distance, this may resolve to a position
// at (200-360) = 160NM in the wrong direction)
2015-02-11 13:10:40 +01:00
2016-08-26 12:31:32 +02:00
if ( Modes . maxRange = = 0 ) {
return ( - 1 ) ; // Can't do receiver-centered checks at all
} else if ( Modes . maxRange < = 1852 * 180 ) {
2015-02-18 01:12:35 +01:00
range_limit = Modes . maxRange ;
2015-10-01 00:59:01 +02:00
} else if ( Modes . maxRange < 1852 * 360 ) {
2015-02-08 15:27:03 +01:00
range_limit = ( 1852 * 360 ) - Modes . maxRange ;
2015-02-18 01:12:35 +01:00
} else {
return ( - 1 ) ; // Can't do receiver-centered checks at all
2015-02-11 13:10:40 +01:00
}
2015-02-08 15:27:03 +01:00
} else {
// No local reference, give up
return ( - 1 ) ;
}
result = decodeCPRrelative ( reflat , reflon ,
2016-08-27 15:34:14 +02:00
mm - > cpr_lat ,
mm - > cpr_lon ,
2015-02-08 15:27:03 +01:00
fflag , surface ,
2015-02-18 01:12:35 +01:00
lat , lon ) ;
2016-08-27 15:34:14 +02:00
if ( result < 0 ) {
2015-02-08 15:27:03 +01:00
return result ;
2016-08-27 15:34:14 +02:00
}
2015-02-18 01:12:35 +01:00
2015-02-08 15:27:03 +01:00
// check range limit
if ( range_limit > 0 ) {
2015-02-18 01:12:35 +01:00
double range = greatcircle ( reflat , reflon , * lat , * lon ) ;
if ( range > range_limit ) {
Modes . stats_current . cpr_local_range_checks + + ;
2015-02-08 15:27:03 +01:00
return ( - 1 ) ;
2015-02-18 01:12:35 +01:00
}
2015-02-08 15:27:03 +01:00
}
2015-02-18 01:12:35 +01:00
// check speed limit
2017-12-07 17:34:08 +01:00
if ( trackDataValid ( & a - > position_valid ) & & a - > pos_nic > = * nic & & a - > pos_rc < = * rc & & ! speed_check ( a , * lat , * lon , surface ) ) {
2016-08-27 15:34:14 +02:00
# ifdef DEBUG_CPR_CHECKS
fprintf ( stderr , " Speed check for %06X with local decoding failed \n " , a - > addr ) ;
# endif
2015-02-18 01:12:35 +01:00
Modes . stats_current . cpr_local_speed_checks + + ;
return - 1 ;
2015-02-08 15:27:03 +01:00
}
return 0 ;
}
2016-08-27 15:34:14 +02:00
static uint64_t time_between ( uint64_t t1 , uint64_t t2 )
{
if ( t1 > = t2 )
return t1 - t2 ;
else
return t2 - t1 ;
}
2017-12-02 18:38:33 +01:00
static void updatePosition ( struct aircraft * a , struct modesMessage * mm )
2015-02-08 15:27:03 +01:00
{
int location_result = - 1 ;
2016-08-27 15:34:14 +02:00
uint64_t max_elapsed ;
2015-02-18 01:12:35 +01:00
double new_lat = 0 , new_lon = 0 ;
2017-12-07 17:34:08 +01:00
unsigned new_nic = 0 ;
unsigned new_rc = 0 ;
2016-08-27 15:34:14 +02:00
int surface ;
2015-02-09 15:17:31 +01:00
2016-10-02 01:16:29 +02:00
surface = ( mm - > cpr_type = = CPR_SURFACE ) ;
2016-08-27 15:34:14 +02:00
if ( surface ) {
2015-02-19 19:53:11 +01:00
+ + Modes . stats_current . cpr_surface ;
2015-02-09 15:17:31 +01:00
// Surface: 25 seconds if >25kt or speed unknown, 50 seconds otherwise
2017-12-07 17:34:08 +01:00
if ( mm - > gs_valid & & mm - > gs . selected < = 25 )
2015-02-09 15:17:31 +01:00
max_elapsed = 50000 ;
else
max_elapsed = 25000 ;
} else {
2016-08-27 15:34:14 +02:00
+ + Modes . stats_current . cpr_airborne ;
2015-02-09 15:17:31 +01:00
// Airborne: 10 seconds
max_elapsed = 10000 ;
}
2015-02-08 15:27:03 +01:00
// If we have enough recent data, try global CPR
2016-08-27 15:34:14 +02:00
if ( trackDataValid ( & a - > cpr_odd_valid ) & & trackDataValid ( & a - > cpr_even_valid ) & &
a - > cpr_odd_valid . source = = a - > cpr_even_valid . source & &
2016-10-02 01:16:29 +02:00
a - > cpr_odd_type = = a - > cpr_even_type & &
2016-08-27 15:34:14 +02:00
time_between ( a - > cpr_odd_valid . updated , a - > cpr_even_valid . updated ) < = max_elapsed ) {
2017-12-07 17:34:08 +01:00
location_result = doGlobalCPR ( a , mm , & new_lat , & new_lon , & new_nic , & new_rc ) ;
2015-02-18 01:12:35 +01:00
2015-02-08 15:27:03 +01:00
if ( location_result = = - 2 ) {
2015-06-29 13:43:58 +02:00
# ifdef DEBUG_CPR_CHECKS
2016-08-27 15:34:14 +02:00
fprintf ( stderr , " global CPR failure (invalid) for (%06X). \n " , a - > addr ) ;
2015-06-29 13:43:58 +02:00
# endif
2015-02-18 19:26:23 +01:00
// Global CPR failed because the position produced implausible results.
2015-02-08 15:27:03 +01:00
// This is bad data. Discard both odd and even messages and wait for a fresh pair.
// Also disable aircraft-relative positions until we have a new good position (but don't discard the
// recorded position itself)
Modes . stats_current . cpr_global_bad + + ;
2016-08-27 15:34:14 +02:00
a - > cpr_odd_valid . source = a - > cpr_even_valid . source = a - > position_valid . source = SOURCE_INVALID ;
2015-02-08 15:27:03 +01:00
return ;
} else if ( location_result = = - 1 ) {
2015-06-29 13:43:58 +02:00
# ifdef DEBUG_CPR_CHECKS
2016-08-27 15:34:14 +02:00
if ( mm - > source = = SOURCE_MLAT ) {
2015-06-29 13:43:58 +02:00
fprintf ( stderr , " CPR skipped from MLAT (%06X). \n " , a - > addr ) ;
}
# endif
2015-02-08 15:27:03 +01:00
// No local reference for surface position available, or the two messages crossed a zone.
// Nonfatal, try again later.
Modes . stats_current . cpr_global_skipped + + ;
} else {
Modes . stats_current . cpr_global_ok + + ;
2016-08-27 15:34:14 +02:00
combine_validity ( & a - > position_valid , & a - > cpr_even_valid , & a - > cpr_odd_valid ) ;
2015-02-08 15:27:03 +01:00
}
}
// Otherwise try relative CPR.
if ( location_result = = - 1 ) {
2017-12-07 17:34:08 +01:00
location_result = doLocalCPR ( a , mm , & new_lat , & new_lon , & new_nic , & new_rc ) ;
2015-02-18 01:12:35 +01:00
2016-08-27 15:34:14 +02:00
if ( location_result < 0 ) {
2015-02-08 15:27:03 +01:00
Modes . stats_current . cpr_local_skipped + + ;
} else {
Modes . stats_current . cpr_local_ok + + ;
2016-08-27 15:34:14 +02:00
mm - > cpr_relative = 1 ;
if ( mm - > cpr_odd ) {
a - > position_valid = a - > cpr_odd_valid ;
} else {
a - > position_valid = a - > cpr_even_valid ;
}
2015-02-08 15:27:03 +01:00
}
}
if ( location_result = = 0 ) {
// If we sucessfully decoded, back copy the results to mm so that we can print them in list output
2016-08-27 15:34:14 +02:00
mm - > cpr_decoded = 1 ;
mm - > decoded_lat = new_lat ;
mm - > decoded_lon = new_lon ;
2017-12-07 17:34:08 +01:00
mm - > decoded_nic = new_nic ;
mm - > decoded_rc = new_rc ;
2015-02-08 15:27:03 +01:00
// Update aircraft state
2015-02-18 01:12:35 +01:00
a - > lat = new_lat ;
a - > lon = new_lon ;
2017-12-07 17:34:08 +01:00
a - > pos_nic = new_nic ;
a - > pos_rc = new_rc ;
2015-02-18 01:12:35 +01:00
2015-06-19 18:29:14 +02:00
update_range_histogram ( new_lat , new_lon ) ;
2015-02-08 15:27:03 +01:00
}
}
2017-12-07 17:34:08 +01:00
static unsigned compute_nic ( unsigned metype , unsigned version , unsigned nic_a , unsigned nic_b , unsigned nic_c )
{
switch ( metype ) {
case 5 : // surface
case 9 : // airborne
case 20 : // airborne, GNSS altitude
return 11 ;
case 6 : // surface
case 10 : // airborne
case 21 : // airborne, GNSS altitude
return 10 ;
case 7 : // surface
if ( version = = 2 ) {
if ( nic_a & & ! nic_c ) {
return 9 ;
} else {
return 8 ;
}
} else if ( version = = 1 ) {
if ( nic_a ) {
return 9 ;
} else {
return 8 ;
}
} else {
return 8 ;
}
case 8 : // surface
if ( version = = 2 ) {
if ( nic_a & & nic_c ) {
return 7 ;
} else if ( nic_a & & ! nic_c ) {
return 6 ;
} else if ( ! nic_a & & nic_c ) {
return 6 ;
} else {
return 0 ;
}
} else {
return 0 ;
}
case 11 : // airborne
if ( version = = 2 ) {
if ( nic_a & & nic_b ) {
return 9 ;
} else {
return 8 ;
}
} else if ( version = = 1 ) {
if ( nic_a ) {
return 9 ;
} else {
return 8 ;
}
} else {
return 8 ;
}
case 12 : // airborne
return 7 ;
case 13 : // airborne
return 6 ;
case 14 : // airborne
return 5 ;
case 15 : // airborne
return 4 ;
case 16 : // airborne
if ( nic_a & & nic_b ) {
return 3 ;
} else {
return 2 ;
}
case 17 : // airborne
return 1 ;
default :
return 0 ;
}
}
static unsigned compute_rc ( unsigned metype , unsigned version , unsigned nic_a , unsigned nic_b , unsigned nic_c )
{
switch ( metype ) {
case 5 : // surface
case 9 : // airborne
case 20 : // airborne, GNSS altitude
return 8 ; // 7.5m
case 6 : // surface
case 10 : // airborne
case 21 : // airborne, GNSS altitude
return 25 ;
case 7 : // surface
if ( version = = 2 ) {
if ( nic_a & & ! nic_c ) {
return 75 ;
} else {
return 186 ; // 185.2m, 0.1NM
}
} else if ( version = = 1 ) {
if ( nic_a ) {
return 75 ;
} else {
return 186 ; // 185.2m, 0.1NM
}
} else {
return 186 ; // 185.2m, 0.1NM
}
case 8 : // surface
if ( version = = 2 ) {
if ( nic_a & & nic_c ) {
return 371 ; // 370.4m, 0.2NM
} else if ( nic_a & & ! nic_c ) {
return 556 ; // 555.6m, 0.3NM
} else if ( ! nic_a & & nic_c ) {
return 926 ; // 926m, 0.5NM
} else {
return RC_UNKNOWN ;
}
} else {
return RC_UNKNOWN ;
}
case 11 : // airborne
if ( version = = 2 ) {
if ( nic_a & & nic_b ) {
return 75 ;
} else {
return 186 ; // 370.4m, 0.2NM
}
} else if ( version = = 1 ) {
if ( nic_a ) {
return 75 ;
} else {
return 186 ; // 370.4m, 0.2NM
}
} else {
return 186 ; // 370.4m, 0.2NM
}
case 12 : // airborne
return 371 ; // 370.4m, 0.2NM
case 13 : // airborne
if ( version = = 2 ) {
if ( ! nic_a & & nic_b ) {
return 556 ; // 555.6m, 0.3NM
} else if ( ! nic_a & & ! nic_b ) {
return 926 ; // 926m, 0.5NM
} else if ( nic_a & & nic_b ) {
return 1112 ; // 1111.2m, 0.6NM
} else {
return 1112 ; // bad combination, assume worst Rc
}
} else if ( version = = 1 ) {
if ( nic_a ) {
return 1112 ; // 1111.2m, 0.6NM
} else {
return 926 ; // 926m, 0.5NM
}
} else {
return 926 ; // 926m, 0.5NM
}
case 14 : // airborne
return 1852 ; // 1.0NM
case 15 : // airborne
return 3704 ; // 2NM
case 16 : // airborne
if ( version = = 2 ) {
if ( nic_a & & nic_b ) {
return 7408 ; // 4NM
} else {
return 14816 ; // 8NM
}
} else if ( version = = 1 ) {
if ( nic_a ) {
return 7408 ; // 4NM
} else {
return 14816 ; // 8NM
}
} else {
return 18510 ; // 10NM
}
case 17 : // airborne
return 37040 ; // 20NM
default :
return RC_UNKNOWN ;
}
}
static void compute_nic_rc_from_message ( struct modesMessage * mm , struct aircraft * a , unsigned * nic , unsigned * rc )
{
int nic_a = ( trackDataValid ( & a - > nic_a_valid ) & & a - > nic_a ) ;
int nic_b = ( mm - > accuracy . nic_b_valid & & mm - > accuracy . nic_b ) ;
int nic_c = ( trackDataValid ( & a - > nic_c_valid ) & & a - > nic_c ) ;
* nic = compute_nic ( mm - > metype , a - > adsb_version , nic_a , nic_b , nic_c ) ;
* rc = compute_rc ( mm - > metype , a - > adsb_version , nic_a , nic_b , nic_c ) ;
}
static int altitude_to_feet ( int raw , altitude_unit_t unit )
{
switch ( unit ) {
case UNIT_METERS :
return raw / 0.3048 ;
case UNIT_FEET :
return raw ;
default :
return 0 ;
}
}
2015-02-08 15:27:03 +01:00
//
//=========================================================================
//
// Receive new messages and update tracked aircraft state
//
struct aircraft * trackUpdateFromMessage ( struct modesMessage * mm )
{
struct aircraft * a ;
2016-10-11 19:00:11 +02:00
if ( mm - > msgtype = = 32 ) {
// Mode A/C, just count it (we ignore SPI)
modeAC_count [ modeAToIndex ( mm - > squawk ) ] + + ;
return NULL ;
}
2018-02-23 19:10:26 +01:00
if ( mm - > addr = = 0 ) {
// junk address, don't track it
return NULL ;
}
2017-12-02 18:38:33 +01:00
_messageNow = mm - > sysTimestampMsg ;
2018-01-09 18:13:34 +01:00
2015-02-08 15:27:03 +01:00
// Lookup our aircraft or create a new one
a = trackFindAircraft ( mm - > addr ) ;
if ( ! a ) { // If it's a currently unknown aircraft....
a = trackCreateAircraft ( mm ) ; // ., create a new record for it,
a - > next = Modes . aircrafts ; // .. and put it at the head of the list
Modes . aircrafts = a ;
}
2016-02-24 12:51:45 +01:00
if ( mm - > signalLevel > 0 ) {
a - > signalLevel [ a - > signalNext ] = mm - > signalLevel ;
a - > signalNext = ( a - > signalNext + 1 ) & 7 ;
}
2017-12-02 18:38:33 +01:00
a - > seen = messageNow ( ) ;
2015-02-08 15:27:03 +01:00
a - > messages + + ;
2016-09-14 17:54:00 +02:00
// update addrtype, we only ever go towards "more direct" types
if ( mm - > addrtype < a - > addrtype )
a - > addrtype = mm - > addrtype ;
2017-06-15 22:07:53 +02:00
// if we saw some direct ADS-B for the first time, assume version 0
if ( mm - > source = = SOURCE_ADSB & & a - > adsb_version < 0 )
a - > adsb_version = 0 ;
2017-12-07 17:34:08 +01:00
// category shouldn't change over time, don't bother with metadata
if ( mm - > category_valid ) {
a - > category = mm - > category ;
}
// operational status message
// done early to update version / HRD / TAH
if ( mm - > opstatus . valid ) {
a - > adsb_version = mm - > opstatus . version ;
if ( mm - > opstatus . hrd ! = HEADING_INVALID ) {
a - > adsb_hrd = mm - > opstatus . hrd ;
}
if ( mm - > opstatus . tah ! = HEADING_INVALID ) {
a - > adsb_tah = mm - > opstatus . tah ;
}
}
if ( mm - > altitude_baro_valid & & accept_data ( & a - > altitude_baro_valid , mm - > source ) ) {
int alt = altitude_to_feet ( mm - > altitude_baro , mm - > altitude_baro_unit ) ;
2016-10-11 19:00:11 +02:00
if ( a - > modeC_hit ) {
2017-12-07 17:34:08 +01:00
int new_modeC = ( a - > altitude_baro + 49 ) / 100 ;
int old_modeC = ( alt + 49 ) / 100 ;
2016-10-11 19:00:11 +02:00
if ( new_modeC ! = old_modeC ) {
a - > modeC_hit = 0 ;
}
2016-08-27 15:34:14 +02:00
}
2015-02-18 19:26:23 +01:00
2017-12-07 17:34:08 +01:00
a - > altitude_baro = alt ;
2015-02-08 15:27:03 +01:00
}
2017-12-02 18:38:33 +01:00
if ( mm - > squawk_valid & & accept_data ( & a - > squawk_valid , mm - > source ) ) {
2016-08-27 15:34:14 +02:00
if ( mm - > squawk ! = a - > squawk ) {
2016-10-11 19:00:11 +02:00
a - > modeA_hit = 0 ;
2015-06-26 21:43:46 +02:00
}
2016-08-27 15:34:14 +02:00
a - > squawk = mm - > squawk ;
2018-02-23 20:02:33 +01:00
// Handle 7x00 without a corresponding emergency status
if ( ! mm - > emergency_valid ) {
emergency_t squawk_emergency ;
switch ( mm - > squawk ) {
case 0x7500 :
squawk_emergency = EMERGENCY_UNLAWFUL ;
break ;
case 0x7600 :
squawk_emergency = EMERGENCY_NORDO ;
break ;
case 0x7700 :
squawk_emergency = EMERGENCY_GENERAL ;
break ;
default :
squawk_emergency = EMERGENCY_NONE ;
break ;
}
if ( squawk_emergency ! = EMERGENCY_NONE & & accept_data ( & a - > emergency_valid , mm - > source ) ) {
a - > emergency = squawk_emergency ;
}
}
2015-02-08 15:27:03 +01:00
}
2018-01-09 15:43:58 +01:00
if ( mm - > emergency_valid & & accept_data ( & a - > emergency_valid , mm - > source ) ) {
a - > emergency = mm - > emergency ;
}
2017-12-07 17:34:08 +01:00
if ( mm - > altitude_geom_valid & & accept_data ( & a - > altitude_geom_valid , mm - > source ) ) {
a - > altitude_geom = altitude_to_feet ( mm - > altitude_geom , mm - > altitude_geom_unit ) ;
2016-08-27 15:34:14 +02:00
}
2016-01-01 14:42:30 +01:00
2017-12-02 18:38:33 +01:00
if ( mm - > geom_delta_valid & & accept_data ( & a - > geom_delta_valid , mm - > source ) ) {
2017-06-15 19:07:40 +02:00
a - > geom_delta = mm - > geom_delta ;
2016-01-01 14:42:30 +01:00
}
2017-06-15 22:07:53 +02:00
if ( mm - > heading_valid ) {
heading_type_t htype = mm - > heading_type ;
if ( htype = = HEADING_MAGNETIC_OR_TRUE ) {
htype = a - > adsb_hrd ;
} else if ( htype = = HEADING_TRACK_OR_HEADING ) {
htype = a - > adsb_tah ;
}
2017-12-02 18:38:33 +01:00
if ( htype = = HEADING_GROUND_TRACK & & accept_data ( & a - > track_valid , mm - > source ) ) {
2017-06-15 22:07:53 +02:00
a - > track = mm - > heading ;
2017-12-02 18:38:33 +01:00
} else if ( htype = = HEADING_MAGNETIC & & accept_data ( & a - > mag_heading_valid , mm - > source ) ) {
2017-06-15 22:07:53 +02:00
a - > mag_heading = mm - > heading ;
2017-12-02 18:38:33 +01:00
} else if ( htype = = HEADING_TRUE & & accept_data ( & a - > true_heading_valid , mm - > source ) ) {
2017-06-15 22:07:53 +02:00
a - > true_heading = mm - > heading ;
}
2016-08-27 15:34:14 +02:00
}
2016-01-01 14:42:30 +01:00
2017-12-02 18:38:33 +01:00
if ( mm - > track_rate_valid & & accept_data ( & a - > track_rate_valid , mm - > source ) ) {
2017-06-15 19:07:40 +02:00
a - > track_rate = mm - > track_rate ;
2016-01-01 14:42:30 +01:00
}
2017-12-02 18:38:33 +01:00
if ( mm - > roll_valid & & accept_data ( & a - > roll_valid , mm - > source ) ) {
2017-06-15 19:07:40 +02:00
a - > roll = mm - > roll ;
2015-02-08 15:27:03 +01:00
}
2017-12-07 17:34:08 +01:00
if ( mm - > gs_valid ) {
mm - > gs . selected = ( a - > adsb_version = = 2 ? mm - > gs . v2 : mm - > gs . v0 ) ;
if ( accept_data ( & a - > gs_valid , mm - > source ) ) {
a - > gs = mm - > gs . selected ;
}
2015-02-08 15:27:03 +01:00
}
2017-12-02 18:38:33 +01:00
if ( mm - > ias_valid & & accept_data ( & a - > ias_valid , mm - > source ) ) {
2017-06-15 19:07:40 +02:00
a - > ias = mm - > ias ;
}
2017-12-02 18:38:33 +01:00
if ( mm - > tas_valid & & accept_data ( & a - > tas_valid , mm - > source ) ) {
2017-06-15 19:07:40 +02:00
a - > tas = mm - > tas ;
}
2017-12-02 18:38:33 +01:00
if ( mm - > mach_valid & & accept_data ( & a - > mach_valid , mm - > source ) ) {
2017-06-15 19:07:40 +02:00
a - > mach = mm - > mach ;
}
2017-12-02 18:38:33 +01:00
if ( mm - > baro_rate_valid & & accept_data ( & a - > baro_rate_valid , mm - > source ) ) {
2017-06-15 19:07:40 +02:00
a - > baro_rate = mm - > baro_rate ;
}
2017-12-02 18:38:33 +01:00
if ( mm - > geom_rate_valid & & accept_data ( & a - > geom_rate_valid , mm - > source ) ) {
2017-06-15 19:07:40 +02:00
a - > geom_rate = mm - > geom_rate ;
2015-02-08 15:27:03 +01:00
}
2018-01-09 15:47:08 +01:00
if ( mm - > airground ! = AG_INVALID ) {
// If our current state is UNCERTAIN, accept new data as normal
// If our current state is certain but new data is not, only accept the uncertain state if the certain data has gone stale
if ( mm - > airground ! = AG_UNCERTAIN | |
( mm - > airground = = AG_UNCERTAIN & & ! trackDataFresh ( & a - > airground_valid ) ) ) {
if ( accept_data ( & a - > airground_valid , mm - > source ) ) {
a - > airground = mm - > airground ;
}
}
2016-08-27 15:34:14 +02:00
}
2015-02-08 15:27:03 +01:00
2017-12-02 18:38:33 +01:00
if ( mm - > callsign_valid & & accept_data ( & a - > callsign_valid , mm - > source ) ) {
2016-08-27 15:34:14 +02:00
memcpy ( a - > callsign , mm - > callsign , sizeof ( a - > callsign ) ) ;
2016-01-01 14:42:30 +01:00
}
2017-06-15 19:23:28 +02:00
// prefer MCP over FMS
// unless the source says otherwise
2017-12-07 17:34:08 +01:00
if ( mm - > nav . mcp_altitude_valid & & mm - > nav . altitude_source ! = NAV_ALT_FMS & & accept_data ( & a - > nav_altitude_valid , mm - > source ) ) {
a - > nav_altitude = mm - > nav . mcp_altitude ;
} else if ( mm - > nav . fms_altitude_valid & & accept_data ( & a - > nav_altitude_valid , mm - > source ) ) {
a - > nav_altitude = mm - > nav . fms_altitude ;
2017-06-15 19:23:28 +02:00
}
2017-06-15 19:07:40 +02:00
2017-12-07 17:34:08 +01:00
if ( mm - > nav . heading_valid & & accept_data ( & a - > nav_heading_valid , mm - > source ) ) {
a - > nav_heading = mm - > nav . heading ;
2017-06-15 19:23:28 +02:00
}
2017-06-15 19:07:40 +02:00
2017-12-07 17:34:08 +01:00
if ( mm - > nav . modes_valid & & accept_data ( & a - > nav_modes_valid , mm - > source ) ) {
a - > nav_modes = mm - > nav . modes ;
2017-06-16 11:39:01 +02:00
}
2017-12-07 17:34:08 +01:00
if ( mm - > nav . qnh_valid & & accept_data ( & a - > nav_qnh_valid , mm - > source ) ) {
a - > nav_qnh = mm - > nav . qnh ;
2017-06-15 19:07:40 +02:00
}
2016-08-27 15:34:14 +02:00
// CPR, even
2017-12-02 18:38:33 +01:00
if ( mm - > cpr_valid & & ! mm - > cpr_odd & & accept_data ( & a - > cpr_even_valid , mm - > source ) ) {
2016-10-02 01:16:29 +02:00
a - > cpr_even_type = mm - > cpr_type ;
2016-08-27 15:34:14 +02:00
a - > cpr_even_lat = mm - > cpr_lat ;
a - > cpr_even_lon = mm - > cpr_lon ;
2017-12-07 17:34:08 +01:00
compute_nic_rc_from_message ( mm , a , & a - > cpr_even_nic , & a - > cpr_even_rc ) ;
2016-08-27 15:34:14 +02:00
}
2015-06-28 21:04:09 +02:00
2016-08-27 15:34:14 +02:00
// CPR, odd
2017-12-02 18:38:33 +01:00
if ( mm - > cpr_valid & & mm - > cpr_odd & & accept_data ( & a - > cpr_odd_valid , mm - > source ) ) {
2016-10-02 01:16:29 +02:00
a - > cpr_odd_type = mm - > cpr_type ;
2016-08-27 15:34:14 +02:00
a - > cpr_odd_lat = mm - > cpr_lat ;
a - > cpr_odd_lon = mm - > cpr_lon ;
2017-12-07 17:34:08 +01:00
compute_nic_rc_from_message ( mm , a , & a - > cpr_odd_nic , & a - > cpr_odd_rc ) ;
2016-08-27 15:34:14 +02:00
}
2017-12-07 17:34:08 +01:00
if ( mm - > accuracy . sda_valid & & accept_data ( & a - > sda_valid , mm - > source ) ) {
a - > sda = mm - > accuracy . sda ;
}
if ( mm - > accuracy . nic_a_valid & & accept_data ( & a - > nic_a_valid , mm - > source ) ) {
a - > nic_a = mm - > accuracy . nic_a ;
}
if ( mm - > accuracy . nic_c_valid & & accept_data ( & a - > nic_c_valid , mm - > source ) ) {
a - > nic_c = mm - > accuracy . nic_c ;
}
if ( mm - > accuracy . nac_p_valid & & accept_data ( & a - > nac_p_valid , mm - > source ) ) {
a - > nac_p = mm - > accuracy . nac_p ;
}
if ( mm - > accuracy . nac_v_valid & & accept_data ( & a - > nac_v_valid , mm - > source ) ) {
a - > nac_v = mm - > accuracy . nac_v ;
}
2017-12-07 20:36:07 +01:00
if ( mm - > accuracy . sil_type ! = SIL_INVALID & & accept_data ( & a - > sil_valid , mm - > source ) ) {
2017-12-07 17:34:08 +01:00
a - > sil = mm - > accuracy . sil ;
2017-12-07 20:36:07 +01:00
if ( a - > sil_type = = SIL_INVALID | | mm - > accuracy . sil_type ! = SIL_UNKNOWN ) {
2017-12-07 17:34:08 +01:00
a - > sil_type = mm - > accuracy . sil_type ;
2017-06-15 22:07:53 +02:00
}
}
2017-12-07 17:34:08 +01:00
if ( mm - > accuracy . gva_valid & & accept_data ( & a - > gva_valid , mm - > source ) ) {
a - > gva = mm - > accuracy . gva ;
}
if ( mm - > accuracy . sda_valid & & accept_data ( & a - > sda_valid , mm - > source ) ) {
a - > sda = mm - > accuracy . sda ;
}
2016-08-27 15:34:14 +02:00
// Now handle derived data
2017-06-15 19:07:40 +02:00
// derive geometric altitude if we have baro + delta
2017-12-07 17:34:08 +01:00
if ( compare_validity ( & a - > altitude_baro_valid , & a - > altitude_geom_valid ) > 0 & &
2017-12-02 18:38:33 +01:00
compare_validity ( & a - > geom_delta_valid , & a - > altitude_geom_valid ) > 0 ) {
2017-06-15 19:07:40 +02:00
// Baro and delta are both more recent than geometric, derive geometric from baro + delta
2017-12-07 17:34:08 +01:00
a - > altitude_geom = a - > altitude_baro + a - > geom_delta ;
combine_validity ( & a - > altitude_geom_valid , & a - > altitude_baro_valid , & a - > geom_delta_valid ) ;
2016-08-27 15:34:14 +02:00
}
// If we've got a new cprlat or cprlon
if ( mm - > cpr_valid ) {
2017-12-02 18:38:33 +01:00
updatePosition ( a , mm ) ;
2016-08-27 15:34:14 +02:00
}
2016-01-21 20:42:37 +01:00
2015-02-08 15:27:03 +01:00
return ( a ) ;
}
//
// Periodic updates of tracking state
//
2016-10-11 19:00:11 +02:00
// Periodically match up mode A/C results with mode S results
static void trackMatchAC ( uint64_t now )
2015-02-08 15:27:03 +01:00
{
2016-10-11 19:00:11 +02:00
// clear match flags
for ( unsigned i = 0 ; i < 4096 ; + + i ) {
modeAC_match [ i ] = 0 ;
}
// scan aircraft list, look for matches
for ( struct aircraft * a = Modes . aircrafts ; a ; a = a - > next ) {
if ( ( now - a - > seen ) > 5000 ) {
continue ;
}
// match on Mode A
if ( trackDataValid ( & a - > squawk_valid ) ) {
unsigned i = modeAToIndex ( a - > squawk ) ;
2016-10-11 22:29:39 +02:00
if ( ( modeAC_count [ i ] - modeAC_lastcount [ i ] ) > = TRACK_MODEAC_MIN_MESSAGES ) {
2016-10-11 19:00:11 +02:00
a - > modeA_hit = 1 ;
modeAC_match [ i ] = ( modeAC_match [ i ] ? 0xFFFFFFFF : a - > addr ) ;
2015-02-08 15:27:03 +01:00
}
2016-10-11 19:00:11 +02:00
}
2015-02-08 15:27:03 +01:00
2016-10-11 22:29:39 +02:00
// match on Mode C (+/- 100ft)
2017-12-07 17:34:08 +01:00
if ( trackDataValid ( & a - > altitude_baro_valid ) ) {
int modeC = ( a - > altitude_baro + 49 ) / 100 ;
2016-10-11 22:29:39 +02:00
2016-10-11 19:00:11 +02:00
unsigned modeA = modeCToModeA ( modeC ) ;
2016-10-11 22:29:39 +02:00
unsigned i = modeAToIndex ( modeA ) ;
if ( modeA & & ( modeAC_count [ i ] - modeAC_lastcount [ i ] ) > = TRACK_MODEAC_MIN_MESSAGES ) {
a - > modeC_hit = 1 ;
modeAC_match [ i ] = ( modeAC_match [ i ] ? 0xFFFFFFFF : a - > addr ) ;
}
modeA = modeCToModeA ( modeC + 1 ) ;
i = modeAToIndex ( modeA ) ;
if ( modeA & & ( modeAC_count [ i ] - modeAC_lastcount [ i ] ) > = TRACK_MODEAC_MIN_MESSAGES ) {
a - > modeC_hit = 1 ;
modeAC_match [ i ] = ( modeAC_match [ i ] ? 0xFFFFFFFF : a - > addr ) ;
}
modeA = modeCToModeA ( modeC - 1 ) ;
i = modeAToIndex ( modeA ) ;
if ( modeA & & ( modeAC_count [ i ] - modeAC_lastcount [ i ] ) > = TRACK_MODEAC_MIN_MESSAGES ) {
a - > modeC_hit = 1 ;
modeAC_match [ i ] = ( modeAC_match [ i ] ? 0xFFFFFFFF : a - > addr ) ;
2015-02-08 15:27:03 +01:00
}
}
}
2016-10-11 19:00:11 +02:00
// reset counts for next time
for ( unsigned i = 0 ; i < 4096 ; + + i ) {
2016-10-11 22:29:39 +02:00
if ( ! modeAC_count [ i ] )
continue ;
if ( ( modeAC_count [ i ] - modeAC_lastcount [ i ] ) < TRACK_MODEAC_MIN_MESSAGES ) {
if ( + + modeAC_age [ i ] > 15 ) {
// not heard from for a while, clear it out
modeAC_lastcount [ i ] = modeAC_count [ i ] = modeAC_age [ i ] = 0 ;
}
2016-10-11 19:00:11 +02:00
} else {
2016-10-11 22:29:39 +02:00
// this one is live
// set a high initial age for matches, so they age out rapidly
// and don't show up on the interactive display when the matching
// mode S data goes away or changes
if ( modeAC_match [ i ] ) {
modeAC_age [ i ] = 10 ;
} else {
modeAC_age [ i ] = 0 ;
}
2015-02-08 15:27:03 +01:00
}
2016-10-11 22:29:39 +02:00
modeAC_lastcount [ i ] = modeAC_count [ i ] ;
2015-02-08 15:27:03 +01:00
}
}
//
//=========================================================================
//
// If we don't receive new nessages within TRACK_AIRCRAFT_TTL
// we remove the aircraft from the list.
//
2015-02-10 22:49:37 +01:00
static void trackRemoveStaleAircraft ( uint64_t now )
2015-02-08 15:27:03 +01:00
{
struct aircraft * a = Modes . aircrafts ;
struct aircraft * prev = NULL ;
2017-06-15 19:16:51 +02:00
2015-02-08 15:27:03 +01:00
while ( a ) {
2015-02-08 15:37:35 +01:00
if ( ( now - a - > seen ) > TRACK_AIRCRAFT_TTL | |
( a - > messages = = 1 & & ( now - a - > seen ) > TRACK_AIRCRAFT_ONEHIT_TTL ) ) {
2015-02-08 19:47:39 +01:00
// Count aircraft where we saw only one message before reaping them.
// These are likely to be due to messages with bad addresses.
if ( a - > messages = = 1 )
Modes . stats_current . single_message_aircraft + + ;
2015-02-08 15:27:03 +01:00
// Remove the element from the linked list, with care
// if we are removing the first element
if ( ! prev ) {
Modes . aircrafts = a - > next ; free ( a ) ; a = Modes . aircrafts ;
} else {
prev - > next = a - > next ; free ( a ) ; a = prev - > next ;
}
} else {
2016-08-27 15:34:14 +02:00
# define EXPIRE(_f) do { if (a->_f##_valid.source != SOURCE_INVALID && now >= a->_f##_valid.expires) { a->_f##_valid.source = SOURCE_INVALID; } } while (0)
EXPIRE ( callsign ) ;
2017-12-07 17:34:08 +01:00
EXPIRE ( altitude_baro ) ;
2017-06-15 19:07:40 +02:00
EXPIRE ( altitude_geom ) ;
EXPIRE ( geom_delta ) ;
EXPIRE ( gs ) ;
EXPIRE ( ias ) ;
EXPIRE ( tas ) ;
EXPIRE ( mach ) ;
EXPIRE ( track ) ;
EXPIRE ( track_rate ) ;
EXPIRE ( roll ) ;
EXPIRE ( mag_heading ) ;
2017-06-16 12:42:41 +02:00
EXPIRE ( true_heading ) ;
2017-06-15 19:07:40 +02:00
EXPIRE ( baro_rate ) ;
EXPIRE ( geom_rate ) ;
2016-08-27 15:34:14 +02:00
EXPIRE ( squawk ) ;
EXPIRE ( airground ) ;
2017-12-07 17:34:08 +01:00
EXPIRE ( nav_qnh ) ;
EXPIRE ( nav_altitude ) ;
EXPIRE ( nav_heading ) ;
EXPIRE ( nav_modes ) ;
2016-08-27 15:34:14 +02:00
EXPIRE ( cpr_odd ) ;
EXPIRE ( cpr_even ) ;
EXPIRE ( position ) ;
2017-12-07 17:34:08 +01:00
EXPIRE ( nic_a ) ;
EXPIRE ( nic_c ) ;
EXPIRE ( nic_baro ) ;
EXPIRE ( nac_p ) ;
EXPIRE ( sil ) ;
EXPIRE ( gva ) ;
EXPIRE ( sda ) ;
2017-12-02 18:38:33 +01:00
# undef EXPIRE
2015-02-08 15:27:03 +01:00
prev = a ; a = a - > next ;
}
}
}
//
// Entry point for periodic updates
//
void trackPeriodicUpdate ( )
{
2015-02-10 22:49:37 +01:00
static uint64_t next_update ;
uint64_t now = mstime ( ) ;
2015-02-08 15:27:03 +01:00
// Only do updates once per second
if ( now > = next_update ) {
2015-02-10 22:49:37 +01:00
next_update = now + 1000 ;
2015-02-08 15:27:03 +01:00
trackRemoveStaleAircraft ( now ) ;
2016-10-11 19:00:11 +02:00
trackMatchAC ( now ) ;
2015-02-08 15:27:03 +01:00
}
}