2015-02-08 14:27:03 +00:00
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
//
// track.c: aircraft state tracking
//
2016-08-27 13:34:14 +00:00
// Copyright (c) 2014-2016 Oliver Jowett <oliver@mutability.co.uk>
2015-02-08 14:27:03 +00:00
//
2017-06-15 17:16:51 +00:00
// This file is free software: you may copy, redistribute and/or modify it
2015-02-08 14:27:03 +00:00
// under the terms of the GNU General Public License as published by the
2017-06-15 17:16:51 +00:00
// Free Software Foundation, either version 2 of the License, or (at your
// option) any later version.
2015-02-08 14:27:03 +00:00
//
2017-06-15 17:16:51 +00: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 14:27:03 +00:00
// General Public License for more details.
//
2017-06-15 17:16:51 +00:00
// You should have received a copy of the GNU General Public License
2015-02-08 14:27:03 +00:00
// along with this program. If not, see <http://www.gnu.org/licenses/>.
2017-06-15 17:16:51 +00:00
// This file incorporates work covered by the following copyright and
2015-02-08 14:27:03 +00: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 13:34:14 +00:00
# include <inttypes.h>
2015-02-08 14:27:03 +00:00
2015-02-18 00:12:35 +00:00
/* #define DEBUG_CPR_CHECKS */
2016-10-11 17:00:11 +00:00
uint32_t modeAC_count [ 4096 ] ;
uint32_t modeAC_lastcount [ 4096 ] ;
uint32_t modeAC_match [ 4096 ] ;
2016-10-11 20:29:39 +00:00
uint32_t modeAC_age [ 4096 ] ;
2016-10-11 17:00:11 +00:00
2015-02-08 14:27:03 +00:00
//
// Return a new aircraft structure for the linked list of tracked
// aircraft
//
2020-06-29 23:42:21 +00:00
static struct aircraft * trackCreateAircraft ( struct modesMessage * mm ) {
2015-02-08 14:27:03 +00:00
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 15:54:00 +00:00
a - > addrtype = mm - > addrtype ;
2015-02-08 14:27:03 +00:00
for ( i = 0 ; i < 8 ; + + i )
2016-02-24 11:51:45 +00:00
a - > signalLevel [ i ] = 1e-5 ;
a - > signalNext = 0 ;
2015-02-08 14:27:03 +00:00
2017-12-07 16:34:08 +00: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 15:09:38 +00:00
// start off with the "last emitted" ACAS RA being blank (just the BDS 3,0
// or ES type code)
2016-08-28 00:29:37 +00:00
a - > fatsv_emitted_bds_30 [ 0 ] = 0x30 ;
2016-09-24 15:09:38 +00:00
a - > fatsv_emitted_es_acas_ra [ 0 ] = 0xE2 ;
2017-12-07 16:34:08 +00:00
a - > fatsv_emitted_adsb_version = - 1 ;
a - > fatsv_emitted_addrtype = ADDR_UNKNOWN ;
2016-08-28 00:29:37 +00:00
2017-12-07 16:34:08 +00:00
// don't immediately emit, let some data build up
a - > fatsv_last_emitted = a - > fatsv_last_force_emit = messageNow ( ) ;
2017-06-15 20:07:53 +00:00
2017-12-02 17:38:33 +00: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 16:34:08 +00:00
F ( altitude_baro , 15 , 70 ) ; // ADS-B or Mode S
2017-12-02 17:38:33 +00: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
2021-01-15 10:20:50 +00:00
F ( emergency , 60 , 70 ) ; // ADS-B only
2017-12-02 17:38:33 +00:00
F ( airground , 15 , 70 ) ; // ADS-B or Mode S
2017-12-07 16:34:08 +00:00
F ( nav_qnh , 60 , 70 ) ; // Comm-B only
2019-03-19 18:44:09 +00:00
F ( nav_altitude_mcp , 60 , 70 ) ; // ADS-B or Comm-B
F ( nav_altitude_fms , 60 , 70 ) ; // ADS-B or Comm-B
F ( nav_altitude_src , 60 , 70 ) ; // ADS-B or Comm-B
2017-12-07 16:34:08 +00:00
F ( nav_heading , 60 , 70 ) ; // ADS-B or Comm-B
F ( nav_modes , 60 , 70 ) ; // ADS-B or Comm-B
2017-12-02 17:38:33 +00: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 16:34:08 +00: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
2021-07-29 11:04:54 +00:00
F ( mrar_source , 60 , 70 ) ; // Comm-B only
F ( wind , 60 , 70 ) ; // Comm-B only
F ( temperature , 60 , 70 ) ; // Comm-B only
F ( pressure , 60 , 70 ) ; // Comm-B only
F ( turbulence , 60 , 70 ) ; // Comm-B only
F ( humidity , 60 , 70 ) ; // Comm-B only
2017-12-02 17:38:33 +00:00
# undef F
2015-02-08 18:47:39 +00:00
Modes . stats_current . unique_aircraft + + ;
2015-02-08 14:27:03 +00:00
return ( a ) ;
}
//
//=========================================================================
//
// Return the aircraft with the specified address, or NULL if no aircraft
// exists with this address.
//
2020-06-29 23:42:21 +00:00
static struct aircraft * trackFindAircraft ( uint32_t addr ) {
2015-02-08 14:27:03 +00:00
struct aircraft * a = Modes . aircrafts ;
while ( a ) {
if ( a - > addr = = addr ) return ( a ) ;
a = a - > next ;
}
return ( NULL ) ;
}
2016-08-27 13:34:14 +00:00
// Should we accept some new data from the given source?
// If so, update the validity and return 1
2017-12-02 17:38:33 +00:00
static int accept_data ( data_validity * d , datasource_t source )
2016-08-27 13:34:14 +00:00
{
2017-12-02 17:38:33 +00:00
if ( messageNow ( ) < d - > updated )
return 0 ;
if ( source < d - > source & & messageNow ( ) < d - > stale )
2016-08-27 13:34:14 +00:00
return 0 ;
d - > source = source ;
2017-12-02 17:38:33 +00:00
d - > updated = messageNow ( ) ;
2017-12-07 16:34:08 +00:00
d - > stale = messageNow ( ) + ( d - > stale_interval ? d - > stale_interval : 60000 ) ;
d - > expires = messageNow ( ) + ( d - > expire_interval ? d - > expire_interval : 70000 ) ;
2016-08-27 13:34:14 +00: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 17:38:33 +00:00
static int compare_validity ( const data_validity * lhs , const data_validity * rhs ) {
if ( messageNow ( ) < lhs - > stale & & lhs - > source > rhs - > source )
2016-08-27 13:34:14 +00:00
return 1 ;
2017-12-02 17:38:33 +00:00
else if ( messageNow ( ) < rhs - > stale & & lhs - > source < rhs - > source )
2016-08-27 13:34:14 +00: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 14:27:03 +00: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)
2020-09-21 22:00:44 +00:00
double greatcircle ( double lat0 , double lon0 , double lat1 , double lon1 )
2015-02-08 14:27:03 +00:00
{
2016-08-26 10:36:29 +00:00
double dlat , dlon ;
2015-02-08 14:27:03 +00: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 01:53:47 +00:00
2016-08-26 10:36:29 +00: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 01:53:47 +00:00
2016-08-26 10:36:29 +00:00
// spherical law of cosines
return 6371e3 * acos ( sin ( lat0 ) * sin ( lat1 ) + cos ( lat0 ) * cos ( lat1 ) * cos ( dlon ) ) ;
2015-02-08 14:27:03 +00:00
}
2020-09-21 22:00:44 +00:00
double get_bearing ( double lat0 , double lon0 , double lat1 , double lon1 )
{
double dlon ;
lat0 = lat0 * M_PI / 180.0 ;
lon0 = lon0 * M_PI / 180.0 ;
lat1 = lat1 * M_PI / 180.0 ;
lon1 = lon1 * M_PI / 180.0 ;
dlon = ( lon1 - lon0 ) ;
double x = ( cos ( lat0 ) * sin ( lat1 ) ) -
( sin ( lat0 ) * cos ( lat1 ) * cos ( dlon ) ) ;
double y = sin ( dlon ) * cos ( lat1 ) ;
double degree = atan2 ( y , x ) * 180 / M_PI ;
return ( degree > = 0 ) ? degree : ( degree + 360 ) ;
}
2015-06-19 16:29:14 +00: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 00:12:35 +00: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 17:38:33 +00:00
static int speed_check ( struct aircraft * a , double lat , double lon , int surface )
2015-02-18 00:12:35 +00:00
{
uint64_t elapsed ;
double distance ;
double range ;
int speed ;
2015-02-18 01:53:47 +00:00
int inrange ;
2015-02-18 00:12:35 +00:00
2016-08-27 13:34:14 +00:00
if ( ! trackDataValid ( & a - > position_valid ) )
2015-02-18 00:12:35 +00:00
return 1 ; // no reference, assume OK
2017-12-02 17:38:33 +00:00
elapsed = trackDataAge ( & a - > position_valid ) ;
2015-02-18 00:12:35 +00:00
2017-06-15 17:07:40 +00: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 18:26:23 +00:00
else
speed = surface ? 100 : 600 ; // guess
2015-02-18 00:12:35 +00:00
// Work out a reasonable speed to use:
2015-02-18 18:26:23 +00: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 00:12:35 +00:00
} else {
2015-02-18 18:26:23 +00:00
if ( speed < 200 )
speed = 200 ;
2015-02-18 00:12:35 +00: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 01:53:47 +00:00
inrange = ( distance < = range ) ;
2015-02-18 00:12:35 +00:00
# ifdef DEBUG_CPR_CHECKS
2015-02-18 01:53:47 +00:00
if ( ! inrange ) {
2015-02-18 00:12:35 +00: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 01:53:47 +00:00
return inrange ;
2015-02-18 00:12:35 +00:00
}
2018-09-17 21:40:48 +00:00
// return 1 if left_rc is worse (less accurate) than right_rc
static int rcIsWorse ( int left_rc , int right_rc )
{
if ( left_rc = = 0 & & right_rc = = 0 ) // both unknown
return 0 ;
if ( left_rc = = 0 )
return 1 ; // left unknown < right known
if ( right_rc = = 0 )
return 0 ; // left known > right unknown
return ( left_rc > right_rc ) ;
}
2017-12-07 16:34:08 +00:00
static int doGlobalCPR ( struct aircraft * a , struct modesMessage * mm , double * lat , double * lon , unsigned * nic , unsigned * rc )
2015-02-08 14:27:03 +00:00
{
int result ;
2016-08-27 13:34:14 +00:00
int fflag = mm - > cpr_odd ;
2016-10-01 23:16:29 +00:00
int surface = ( mm - > cpr_type = = CPR_SURFACE ) ;
2015-02-18 00:12:35 +00:00
2017-12-07 16:34:08 +00:00
// derive NIC, Rc from the worse of the two position
2018-09-17 21:40:48 +00:00
// smaller NIC is worse
2017-12-07 16:34:08 +00:00
* nic = ( a - > cpr_even_nic < a - > cpr_odd_nic ? a - > cpr_even_nic : a - > cpr_odd_nic ) ;
2018-09-17 21:40:48 +00:00
* rc = ( rcIsWorse ( a - > cpr_even_rc , a - > cpr_odd_rc ) ? a - > cpr_even_rc : a - > cpr_odd_rc ) ;
2015-02-08 14:27:03 +00:00
if ( surface ) {
// surface global CPR
// find reference location
double reflat , reflon ;
2017-12-02 17:38:33 +00:00
if ( trackDataValid ( & a - > position_valid ) ) { // Ok to try aircraft relative first
2015-02-08 14:27:03 +00: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 13:34:14 +00:00
a - > cpr_even_lat , a - > cpr_even_lon ,
a - > cpr_odd_lat , a - > cpr_odd_lon ,
2015-02-08 14:27:03 +00:00
fflag ,
2015-02-18 00:12:35 +00:00
lat , lon ) ;
2015-02-08 14:27:03 +00:00
} else {
// airborne global CPR
2016-08-27 13:34:14 +00:00
result = decodeCPRairborne ( a - > cpr_even_lat , a - > cpr_even_lon ,
a - > cpr_odd_lat , a - > cpr_odd_lon ,
2015-02-08 14:27:03 +00:00
fflag ,
2015-02-18 00:12:35 +00:00
lat , lon ) ;
2015-02-08 14:27:03 +00:00
}
2015-06-29 11:43:58 +00:00
if ( result < 0 ) {
# ifdef DEBUG_CPR_CHECKS
2016-08-27 13:34:14 +00: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 11:43:58 +00:00
# endif
return result ;
}
2015-02-08 14:27:03 +00:00
// check max range
if ( Modes . maxRange > 0 & & ( Modes . bUserFlags & MODES_USER_LATLON_VALID ) ) {
2015-02-18 00:12:35 +00: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 14:27:03 +00:00
return ( - 2 ) ; // we consider an out-of-range value to be bad data
2015-02-18 00:12:35 +00:00
}
2015-02-08 14:27:03 +00:00
}
2016-02-05 15:41:42 +00:00
// for mlat results, skip the speed check
2016-08-27 13:34:14 +00:00
if ( mm - > source = = SOURCE_MLAT )
2016-02-05 15:41:42 +00:00
return result ;
2015-02-18 00:12:35 +00:00
// check speed limit
2018-09-17 21:40:48 +00:00
if ( trackDataValid ( & a - > position_valid ) & & a - > pos_nic > = * nic & & ! rcIsWorse ( a - > pos_rc , * rc ) & & ! speed_check ( a , * lat , * lon , surface ) ) {
2015-02-18 00:12:35 +00:00
Modes . stats_current . cpr_global_speed_checks + + ;
return - 2 ;
}
return result ;
2015-02-08 14:27:03 +00:00
}
2017-12-07 16:34:08 +00:00
static int doLocalCPR ( struct aircraft * a , struct modesMessage * mm , double * lat , double * lon , unsigned * nic , unsigned * rc )
2015-02-08 14:27:03 +00:00
{
// relative CPR
// find reference location
2015-02-18 00:12:35 +00:00
double reflat , reflon ;
2015-02-08 14:27:03 +00:00
double range_limit = 0 ;
int result ;
2016-08-27 13:34:14 +00:00
int fflag = mm - > cpr_odd ;
2016-10-01 23:16:29 +00:00
int surface = ( mm - > cpr_type = = CPR_SURFACE ) ;
2015-02-08 14:27:03 +00:00
2017-12-07 16:34:08 +00: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 14:27:03 +00:00
2017-12-02 17:38:33 +00:00
if ( trackDataValid ( & a - > position_valid ) ) {
2015-02-08 14:27:03 +00:00
reflat = a - > lat ;
reflon = a - > lon ;
2017-12-07 16:34:08 +00:00
if ( a - > pos_nic < * nic )
* nic = a - > pos_nic ;
2018-09-17 21:40:48 +00:00
if ( rcIsWorse ( a - > pos_rc , * rc ) )
2017-12-07 16:34:08 +00:00
* rc = a - > pos_rc ;
2016-08-26 10:30:46 +00:00
range_limit = 50e3 ;
2015-02-08 14:27:03 +00:00
} else if ( ! surface & & ( Modes . bUserFlags & MODES_USER_LATLON_VALID ) ) {
reflat = Modes . fUserLat ;
reflon = Modes . fUserLon ;
2017-06-15 17:16:51 +00:00
2015-02-08 14:27:03 +00: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 12:10:40 +00:00
2016-08-26 10:31:32 +00:00
if ( Modes . maxRange = = 0 ) {
return ( - 1 ) ; // Can't do receiver-centered checks at all
} else if ( Modes . maxRange < = 1852 * 180 ) {
2015-02-18 00:12:35 +00:00
range_limit = Modes . maxRange ;
2015-09-30 22:59:01 +00:00
} else if ( Modes . maxRange < 1852 * 360 ) {
2015-02-08 14:27:03 +00:00
range_limit = ( 1852 * 360 ) - Modes . maxRange ;
2015-02-18 00:12:35 +00:00
} else {
return ( - 1 ) ; // Can't do receiver-centered checks at all
2015-02-11 12:10:40 +00:00
}
2015-02-08 14:27:03 +00:00
} else {
// No local reference, give up
return ( - 1 ) ;
}
result = decodeCPRrelative ( reflat , reflon ,
2016-08-27 13:34:14 +00:00
mm - > cpr_lat ,
mm - > cpr_lon ,
2015-02-08 14:27:03 +00:00
fflag , surface ,
2015-02-18 00:12:35 +00:00
lat , lon ) ;
2016-08-27 13:34:14 +00:00
if ( result < 0 ) {
2015-02-08 14:27:03 +00:00
return result ;
2016-08-27 13:34:14 +00:00
}
2015-02-18 00:12:35 +00:00
2015-02-08 14:27:03 +00:00
// check range limit
if ( range_limit > 0 ) {
2015-02-18 00:12:35 +00:00
double range = greatcircle ( reflat , reflon , * lat , * lon ) ;
if ( range > range_limit ) {
Modes . stats_current . cpr_local_range_checks + + ;
2015-02-08 14:27:03 +00:00
return ( - 1 ) ;
2015-02-18 00:12:35 +00:00
}
2015-02-08 14:27:03 +00:00
}
2015-02-18 00:12:35 +00:00
// check speed limit
2018-09-17 21:40:48 +00:00
if ( trackDataValid ( & a - > position_valid ) & & a - > pos_nic > = * nic & & ! rcIsWorse ( a - > pos_rc , * rc ) & & ! speed_check ( a , * lat , * lon , surface ) ) {
2016-08-27 13:34:14 +00:00
# ifdef DEBUG_CPR_CHECKS
fprintf ( stderr , " Speed check for %06X with local decoding failed \n " , a - > addr ) ;
# endif
2015-02-18 00:12:35 +00:00
Modes . stats_current . cpr_local_speed_checks + + ;
return - 1 ;
2015-02-08 14:27:03 +00:00
}
return 0 ;
}
2016-08-27 13:34:14 +00: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 17:38:33 +00:00
static void updatePosition ( struct aircraft * a , struct modesMessage * mm )
2015-02-08 14:27:03 +00:00
{
int location_result = - 1 ;
2016-08-27 13:34:14 +00:00
uint64_t max_elapsed ;
2015-02-18 00:12:35 +00:00
double new_lat = 0 , new_lon = 0 ;
2017-12-07 16:34:08 +00:00
unsigned new_nic = 0 ;
unsigned new_rc = 0 ;
2016-08-27 13:34:14 +00:00
int surface ;
2015-02-09 14:17:31 +00:00
2016-10-01 23:16:29 +00:00
surface = ( mm - > cpr_type = = CPR_SURFACE ) ;
2016-08-27 13:34:14 +00:00
if ( surface ) {
2015-02-19 18:53:11 +00:00
+ + Modes . stats_current . cpr_surface ;
2015-02-09 14:17:31 +00:00
// Surface: 25 seconds if >25kt or speed unknown, 50 seconds otherwise
2017-12-07 16:34:08 +00:00
if ( mm - > gs_valid & & mm - > gs . selected < = 25 )
2015-02-09 14:17:31 +00:00
max_elapsed = 50000 ;
else
max_elapsed = 25000 ;
} else {
2016-08-27 13:34:14 +00:00
+ + Modes . stats_current . cpr_airborne ;
2015-02-09 14:17:31 +00:00
// Airborne: 10 seconds
max_elapsed = 10000 ;
}
2015-02-08 14:27:03 +00:00
// If we have enough recent data, try global CPR
2016-08-27 13:34:14 +00:00
if ( trackDataValid ( & a - > cpr_odd_valid ) & & trackDataValid ( & a - > cpr_even_valid ) & &
a - > cpr_odd_valid . source = = a - > cpr_even_valid . source & &
2016-10-01 23:16:29 +00:00
a - > cpr_odd_type = = a - > cpr_even_type & &
2016-08-27 13:34:14 +00:00
time_between ( a - > cpr_odd_valid . updated , a - > cpr_even_valid . updated ) < = max_elapsed ) {
2017-12-07 16:34:08 +00:00
location_result = doGlobalCPR ( a , mm , & new_lat , & new_lon , & new_nic , & new_rc ) ;
2015-02-18 00:12:35 +00:00
2015-02-08 14:27:03 +00:00
if ( location_result = = - 2 ) {
2015-06-29 11:43:58 +00:00
# ifdef DEBUG_CPR_CHECKS
2016-08-27 13:34:14 +00:00
fprintf ( stderr , " global CPR failure (invalid) for (%06X). \n " , a - > addr ) ;
2015-06-29 11:43:58 +00:00
# endif
2015-02-18 18:26:23 +00:00
// Global CPR failed because the position produced implausible results.
2015-02-08 14:27:03 +00: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 13:34:14 +00:00
a - > cpr_odd_valid . source = a - > cpr_even_valid . source = a - > position_valid . source = SOURCE_INVALID ;
2015-02-08 14:27:03 +00:00
return ;
} else if ( location_result = = - 1 ) {
2015-06-29 11:43:58 +00:00
# ifdef DEBUG_CPR_CHECKS
2016-08-27 13:34:14 +00:00
if ( mm - > source = = SOURCE_MLAT ) {
2015-06-29 11:43:58 +00:00
fprintf ( stderr , " CPR skipped from MLAT (%06X). \n " , a - > addr ) ;
}
# endif
2015-02-08 14:27:03 +00: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 {
2019-10-19 20:52:57 +00:00
if ( accept_data ( & a - > position_valid , mm - > source ) ) {
Modes . stats_current . cpr_global_ok + + ;
} else {
Modes . stats_current . cpr_global_skipped + + ;
location_result = - 2 ;
}
2015-02-08 14:27:03 +00:00
}
}
// Otherwise try relative CPR.
if ( location_result = = - 1 ) {
2017-12-07 16:34:08 +00:00
location_result = doLocalCPR ( a , mm , & new_lat , & new_lon , & new_nic , & new_rc ) ;
2015-02-18 00:12:35 +00:00
2019-10-19 20:52:57 +00:00
if ( location_result = = 0 & & accept_data ( & a - > position_valid , mm - > source ) ) {
2015-02-08 14:27:03 +00:00
Modes . stats_current . cpr_local_ok + + ;
2016-08-27 13:34:14 +00:00
mm - > cpr_relative = 1 ;
2019-10-19 20:52:57 +00:00
} else {
Modes . stats_current . cpr_local_skipped + + ;
location_result = - 1 ;
2015-02-08 14:27:03 +00: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 13:34:14 +00:00
mm - > cpr_decoded = 1 ;
mm - > decoded_lat = new_lat ;
mm - > decoded_lon = new_lon ;
2017-12-07 16:34:08 +00:00
mm - > decoded_nic = new_nic ;
mm - > decoded_rc = new_rc ;
2015-02-08 14:27:03 +00:00
// Update aircraft state
2015-02-18 00:12:35 +00:00
a - > lat = new_lat ;
a - > lon = new_lon ;
2017-12-07 16:34:08 +00:00
a - > pos_nic = new_nic ;
a - > pos_rc = new_rc ;
2015-02-18 00:12:35 +00:00
2015-06-19 16:29:14 +00:00
update_range_histogram ( new_lat , new_lon ) ;
2015-02-08 14:27:03 +00:00
}
}
2017-12-07 16:34:08 +00: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 )
{
2018-09-19 16:05:39 +00:00
// ED-102 Table 2-14, Table N-4, Table N-11
2017-12-07 16:34:08 +00:00
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 ) {
2018-09-19 16:05:39 +00:00
return 1111 ; // 1111m, 0.6NM
2017-12-07 16:34:08 +00:00
} else {
return RC_UNKNOWN ;
}
} else {
return RC_UNKNOWN ;
}
case 11 : // airborne
if ( version = = 2 ) {
if ( nic_a & & nic_b ) {
return 75 ;
} else {
2018-09-19 16:05:39 +00:00
return 186 ; // 185.2m, 0.1NM
2017-12-07 16:34:08 +00:00
}
} else if ( version = = 1 ) {
if ( nic_a ) {
return 75 ;
} else {
2018-09-19 16:05:39 +00:00
return 186 ; // 185.2m, 0.1NM
2017-12-07 16:34:08 +00:00
}
} else {
2018-09-19 16:05:39 +00:00
return 186 ; // 185.2m, 0.1NM
2017-12-07 16:34:08 +00:00
}
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 {
2018-05-09 13:57:29 +00:00
return RC_UNKNOWN ; // bad combination
2017-12-07 16:34:08 +00:00
}
} 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 {
2018-05-09 13:57:52 +00:00
return 18520 ; // 10NM
2017-12-07 16:34:08 +00:00
}
case 17 : // airborne
return 37040 ; // 20NM
default :
return RC_UNKNOWN ;
}
}
2018-09-19 16:09:11 +00:00
// Map ADS-B v0 position message type to NACp value
// returned computed NACp, or -1 if not a suitable message type
static int compute_v0_nacp ( struct modesMessage * mm )
{
if ( mm - > msgtype ! = 17 & & mm - > msgtype ! = 18 ) {
return - 1 ;
}
// ED-102A Table N-7
switch ( mm - > metype ) {
case 0 : return 0 ;
case 5 : return 11 ;
case 6 : return 10 ;
case 7 : return 8 ;
case 8 : return 0 ;
case 9 : return 11 ;
case 10 : return 10 ;
case 11 : return 8 ;
case 12 : return 7 ;
case 13 : return 6 ;
case 14 : return 5 ;
case 15 : return 4 ;
case 16 : return 1 ;
case 17 : return 1 ;
case 18 : return 0 ;
case 20 : return 11 ;
case 21 : return 10 ;
case 22 : return 0 ;
default : return - 1 ;
}
}
// Map ADS-B v0 position message type to SIL value
// returned computed SIL, or -1 if not a suitable message type
static int compute_v0_sil ( struct modesMessage * mm )
{
if ( mm - > msgtype ! = 17 & & mm - > msgtype ! = 18 ) {
return - 1 ;
}
// ED-102A Table N-8
switch ( mm - > metype ) {
case 0 :
return 0 ;
case 5 :
case 6 :
case 7 :
case 8 :
case 9 :
case 10 :
case 11 :
case 12 :
case 13 :
case 14 :
case 15 :
case 16 :
case 17 :
return 2 ;
case 18 :
return 0 ;
case 20 :
case 21 :
return 2 ;
case 22 :
return 0 ;
default :
return - 1 ;
}
}
2017-12-07 16:34:08 +00:00
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 14:27:03 +00:00
//
//=========================================================================
//
// Receive new messages and update tracked aircraft state
//
struct aircraft * trackUpdateFromMessage ( struct modesMessage * mm )
{
struct aircraft * a ;
2019-10-18 19:02:12 +00:00
unsigned int cpr_new = 0 ;
2016-10-11 17:00:11 +00:00
if ( mm - > msgtype = = 32 ) {
// Mode A/C, just count it (we ignore SPI)
modeAC_count [ modeAToIndex ( mm - > squawk ) ] + + ;
return NULL ;
}
2018-02-23 18:10:26 +00:00
if ( mm - > addr = = 0 ) {
// junk address, don't track it
return NULL ;
}
2017-12-02 17:38:33 +00:00
_messageNow = mm - > sysTimestampMsg ;
2018-01-09 17:13:34 +00:00
2015-02-08 14:27:03 +00: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 11:51:45 +00:00
if ( mm - > signalLevel > 0 ) {
a - > signalLevel [ a - > signalNext ] = mm - > signalLevel ;
a - > signalNext = ( a - > signalNext + 1 ) & 7 ;
}
2017-12-02 17:38:33 +00:00
a - > seen = messageNow ( ) ;
2015-02-08 14:27:03 +00:00
a - > messages + + ;
2019-11-27 12:30:41 +00:00
// count reliable messages we receive; use them as a metric to
// decide when this is a real aircraft, not noise
if ( mm - > msgtype = = 11 & & mm - > reliable ) {
+ + a - > reliableDF11 ;
}
if ( mm - > msgtype = = 17 & & mm - > reliable ) {
+ + a - > reliableDF17 ;
}
if ( a - > reliableDF11 > = TRACK_RELIABLE_DF11_MESSAGES | | a - > reliableDF17 > = TRACK_RELIABLE_DF17_MESSAGES | | a - > messages > = TRACK_RELIABLE_ANY_MESSAGES ) {
a - > reliable = 1 ;
}
if ( ! mm - > reliable & & ! a - > reliable ) {
// no further update from this message as we don't trust it
+ + a - > discarded ;
return a ;
}
2016-09-14 15:54:00 +00:00
// update addrtype, we only ever go towards "more direct" types
if ( mm - > addrtype < a - > addrtype )
a - > addrtype = mm - > addrtype ;
2019-12-12 15:21:26 +00:00
// decide on where to stash the version
int dummy_version = - 1 ; // used for non-adsb/adsr/tisb messages
int * message_version ;
switch ( mm - > source ) {
case SOURCE_ADSB :
message_version = & a - > adsb_version ;
break ;
case SOURCE_TISB :
message_version = & a - > tisb_version ;
break ;
case SOURCE_ADSR :
message_version = & a - > adsr_version ;
break ;
default :
message_version = & dummy_version ;
break ;
}
// assume version 0 until we see something else
if ( * message_version < 0 )
* message_version = 0 ;
2017-06-15 20:07:53 +00:00
2017-12-07 16:34:08 +00: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 ) {
2019-12-12 15:21:26 +00:00
* message_version = mm - > opstatus . version ;
2017-12-07 16:34:08 +00:00
if ( mm - > opstatus . hrd ! = HEADING_INVALID ) {
a - > adsb_hrd = mm - > opstatus . hrd ;
}
if ( mm - > opstatus . tah ! = HEADING_INVALID ) {
a - > adsb_tah = mm - > opstatus . tah ;
}
}
2018-09-19 16:09:11 +00:00
// fill in ADS-B v0 NACp, SIL from position message type
2019-12-12 15:21:26 +00:00
if ( * message_version = = 0 & & ! mm - > accuracy . nac_p_valid ) {
2018-09-19 16:09:11 +00:00
int computed_nacp = compute_v0_nacp ( mm ) ;
if ( computed_nacp ! = - 1 ) {
mm - > accuracy . nac_p_valid = 1 ;
mm - > accuracy . nac_p = computed_nacp ;
}
}
2019-12-12 15:21:26 +00:00
if ( * message_version = = 0 & & mm - > accuracy . sil_type = = SIL_INVALID ) {
2018-09-19 16:09:11 +00:00
int computed_sil = compute_v0_sil ( mm ) ;
if ( computed_sil ! = - 1 ) {
mm - > accuracy . sil_type = SIL_UNKNOWN ;
mm - > accuracy . sil = computed_sil ;
}
}
2017-12-07 16:34:08 +00:00
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 17:00:11 +00:00
if ( a - > modeC_hit ) {
2017-12-07 16:34:08 +00:00
int new_modeC = ( a - > altitude_baro + 49 ) / 100 ;
int old_modeC = ( alt + 49 ) / 100 ;
2016-10-11 17:00:11 +00:00
if ( new_modeC ! = old_modeC ) {
a - > modeC_hit = 0 ;
}
2016-08-27 13:34:14 +00:00
}
2015-02-18 18:26:23 +00:00
2017-12-07 16:34:08 +00:00
a - > altitude_baro = alt ;
2015-02-08 14:27:03 +00:00
}
2017-12-02 17:38:33 +00:00
if ( mm - > squawk_valid & & accept_data ( & a - > squawk_valid , mm - > source ) ) {
2016-08-27 13:34:14 +00:00
if ( mm - > squawk ! = a - > squawk ) {
2016-10-11 17:00:11 +00:00
a - > modeA_hit = 0 ;
2015-06-26 19:43:46 +00:00
}
2016-08-27 13:34:14 +00:00
a - > squawk = mm - > squawk ;
2018-02-23 19:02:33 +00:00
2018-07-06 18:54:58 +00:00
#if 0 // Disabled for now as it obscures the origin of the data
2018-02-23 19:02:33 +00: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 ;
}
}
2018-07-06 18:54:58 +00:00
# endif
2015-02-08 14:27:03 +00:00
}
2018-01-09 14:43:58 +00:00
if ( mm - > emergency_valid & & accept_data ( & a - > emergency_valid , mm - > source ) ) {
a - > emergency = mm - > emergency ;
}
2017-12-07 16:34:08 +00: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 13:34:14 +00:00
}
2016-01-01 13:42:30 +00:00
2017-12-02 17:38:33 +00:00
if ( mm - > geom_delta_valid & & accept_data ( & a - > geom_delta_valid , mm - > source ) ) {
2017-06-15 17:07:40 +00:00
a - > geom_delta = mm - > geom_delta ;
2016-01-01 13:42:30 +00:00
}
2017-06-15 20:07:53 +00: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 17:38:33 +00:00
if ( htype = = HEADING_GROUND_TRACK & & accept_data ( & a - > track_valid , mm - > source ) ) {
2017-06-15 20:07:53 +00:00
a - > track = mm - > heading ;
2017-12-02 17:38:33 +00:00
} else if ( htype = = HEADING_MAGNETIC & & accept_data ( & a - > mag_heading_valid , mm - > source ) ) {
2017-06-15 20:07:53 +00:00
a - > mag_heading = mm - > heading ;
2017-12-02 17:38:33 +00:00
} else if ( htype = = HEADING_TRUE & & accept_data ( & a - > true_heading_valid , mm - > source ) ) {
2017-06-15 20:07:53 +00:00
a - > true_heading = mm - > heading ;
}
2016-08-27 13:34:14 +00:00
}
2016-01-01 13:42:30 +00:00
2017-12-02 17:38:33 +00:00
if ( mm - > track_rate_valid & & accept_data ( & a - > track_rate_valid , mm - > source ) ) {
2017-06-15 17:07:40 +00:00
a - > track_rate = mm - > track_rate ;
2016-01-01 13:42:30 +00:00
}
2017-12-02 17:38:33 +00:00
if ( mm - > roll_valid & & accept_data ( & a - > roll_valid , mm - > source ) ) {
2017-06-15 17:07:40 +00:00
a - > roll = mm - > roll ;
2015-02-08 14:27:03 +00:00
}
2017-12-07 16:34:08 +00:00
if ( mm - > gs_valid ) {
2019-12-12 15:21:26 +00:00
mm - > gs . selected = ( * message_version = = 2 ? mm - > gs . v2 : mm - > gs . v0 ) ;
2017-12-07 16:34:08 +00:00
if ( accept_data ( & a - > gs_valid , mm - > source ) ) {
a - > gs = mm - > gs . selected ;
}
2015-02-08 14:27:03 +00:00
}
2017-12-02 17:38:33 +00:00
if ( mm - > ias_valid & & accept_data ( & a - > ias_valid , mm - > source ) ) {
2017-06-15 17:07:40 +00:00
a - > ias = mm - > ias ;
}
2017-12-02 17:38:33 +00:00
if ( mm - > tas_valid & & accept_data ( & a - > tas_valid , mm - > source ) ) {
2017-06-15 17:07:40 +00:00
a - > tas = mm - > tas ;
}
2017-12-02 17:38:33 +00:00
if ( mm - > mach_valid & & accept_data ( & a - > mach_valid , mm - > source ) ) {
2017-06-15 17:07:40 +00:00
a - > mach = mm - > mach ;
}
2017-12-02 17:38:33 +00:00
if ( mm - > baro_rate_valid & & accept_data ( & a - > baro_rate_valid , mm - > source ) ) {
2017-06-15 17:07:40 +00:00
a - > baro_rate = mm - > baro_rate ;
}
2017-12-02 17:38:33 +00:00
if ( mm - > geom_rate_valid & & accept_data ( & a - > geom_rate_valid , mm - > source ) ) {
2017-06-15 17:07:40 +00:00
a - > geom_rate = mm - > geom_rate ;
2015-02-08 14:27:03 +00:00
}
2018-01-09 14:47:08 +00: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 13:34:14 +00:00
}
2015-02-08 14:27:03 +00:00
2017-12-02 17:38:33 +00:00
if ( mm - > callsign_valid & & accept_data ( & a - > callsign_valid , mm - > source ) ) {
2020-09-21 22:00:44 +00:00
if ( strcmp ( a - > callsign , mm - > callsign ) ! = 0 ) {
// The callsign changed so tell interactive to
// re-evaluate its callsign filter regex if it has one
a - > callsign_matched = 0 ;
}
2016-08-27 13:34:14 +00:00
memcpy ( a - > callsign , mm - > callsign , sizeof ( a - > callsign ) ) ;
2016-01-01 13:42:30 +00:00
}
2019-03-19 18:44:09 +00:00
if ( mm - > nav . mcp_altitude_valid & & accept_data ( & a - > nav_altitude_mcp_valid , mm - > source ) ) {
a - > nav_altitude_mcp = mm - > nav . mcp_altitude ;
}
if ( mm - > nav . fms_altitude_valid & & accept_data ( & a - > nav_altitude_fms_valid , mm - > source ) ) {
a - > nav_altitude_fms = mm - > nav . fms_altitude ;
}
if ( mm - > nav . altitude_source ! = NAV_ALT_INVALID & & accept_data ( & a - > nav_altitude_src_valid , mm - > source ) ) {
a - > nav_altitude_src = mm - > nav . altitude_source ;
2017-06-15 17:23:28 +00:00
}
2017-06-15 17:07:40 +00:00
2017-12-07 16:34:08 +00:00
if ( mm - > nav . heading_valid & & accept_data ( & a - > nav_heading_valid , mm - > source ) ) {
a - > nav_heading = mm - > nav . heading ;
2017-06-15 17:23:28 +00:00
}
2017-06-15 17:07:40 +00:00
2017-12-07 16:34:08 +00:00
if ( mm - > nav . modes_valid & & accept_data ( & a - > nav_modes_valid , mm - > source ) ) {
a - > nav_modes = mm - > nav . modes ;
2017-06-16 09:39:01 +00:00
}
2017-12-07 16:34:08 +00:00
if ( mm - > nav . qnh_valid & & accept_data ( & a - > nav_qnh_valid , mm - > source ) ) {
a - > nav_qnh = mm - > nav . qnh ;
2017-06-15 17:07:40 +00:00
}
2016-08-27 13:34:14 +00:00
// CPR, even
2017-12-02 17:38:33 +00:00
if ( mm - > cpr_valid & & ! mm - > cpr_odd & & accept_data ( & a - > cpr_even_valid , mm - > source ) ) {
2016-10-01 23:16:29 +00:00
a - > cpr_even_type = mm - > cpr_type ;
2016-08-27 13:34:14 +00:00
a - > cpr_even_lat = mm - > cpr_lat ;
a - > cpr_even_lon = mm - > cpr_lon ;
2017-12-07 16:34:08 +00:00
compute_nic_rc_from_message ( mm , a , & a - > cpr_even_nic , & a - > cpr_even_rc ) ;
2019-10-18 19:02:12 +00:00
cpr_new = 1 ;
2016-08-27 13:34:14 +00:00
}
2015-06-28 19:04:09 +00:00
2016-08-27 13:34:14 +00:00
// CPR, odd
2017-12-02 17:38:33 +00:00
if ( mm - > cpr_valid & & mm - > cpr_odd & & accept_data ( & a - > cpr_odd_valid , mm - > source ) ) {
2016-10-01 23:16:29 +00:00
a - > cpr_odd_type = mm - > cpr_type ;
2016-08-27 13:34:14 +00:00
a - > cpr_odd_lat = mm - > cpr_lat ;
a - > cpr_odd_lon = mm - > cpr_lon ;
2017-12-07 16:34:08 +00:00
compute_nic_rc_from_message ( mm , a , & a - > cpr_odd_nic , & a - > cpr_odd_rc ) ;
2019-10-18 19:02:12 +00:00
cpr_new = 1 ;
2016-08-27 13:34:14 +00:00
}
2017-12-07 16:34:08 +00: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 ;
}
2018-06-12 00:13:03 +00:00
if ( mm - > accuracy . nic_baro_valid & & accept_data ( & a - > nic_baro_valid , mm - > source ) ) {
a - > nic_baro = mm - > accuracy . nic_baro ;
}
2017-12-07 16:34:08 +00:00
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 19:36:07 +00:00
if ( mm - > accuracy . sil_type ! = SIL_INVALID & & accept_data ( & a - > sil_valid , mm - > source ) ) {
2017-12-07 16:34:08 +00:00
a - > sil = mm - > accuracy . sil ;
2017-12-07 19:36:07 +00:00
if ( a - > sil_type = = SIL_INVALID | | mm - > accuracy . sil_type ! = SIL_UNKNOWN ) {
2017-12-07 16:34:08 +00:00
a - > sil_type = mm - > accuracy . sil_type ;
2017-06-15 20:07:53 +00:00
}
}
2017-12-07 16:34:08 +00: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 ;
}
2021-07-29 11:04:54 +00:00
if ( mm - > mrar_source_valid & & accept_data ( & a - > mrar_source_valid , mm - > source ) ) {
a - > mrar_source = mm - > mrar_source ;
}
if ( mm - > wind_valid & & accept_data ( & a - > wind_valid , mm - > source ) ) {
a - > wind_speed = mm - > wind_speed ;
a - > wind_dir = mm - > wind_dir ;
}
if ( mm - > temperature_valid & & accept_data ( & a - > temperature_valid , mm - > source ) ) {
a - > temperature = mm - > temperature ;
}
if ( mm - > pressure_valid & & accept_data ( & a - > pressure_valid , mm - > source ) ) {
a - > pressure = mm - > pressure ;
}
if ( mm - > turbulence_valid & & accept_data ( & a - > turbulence_valid , mm - > source ) ) {
a - > turbulence = mm - > turbulence ;
}
if ( mm - > humidity_valid & & accept_data ( & a - > humidity_valid , mm - > source ) ) {
a - > humidity = mm - > humidity ;
}
2016-08-27 13:34:14 +00:00
// Now handle derived data
2017-06-15 17:07:40 +00:00
// derive geometric altitude if we have baro + delta
2017-12-07 16:34:08 +00:00
if ( compare_validity ( & a - > altitude_baro_valid , & a - > altitude_geom_valid ) > 0 & &
2017-12-02 17:38:33 +00:00
compare_validity ( & a - > geom_delta_valid , & a - > altitude_geom_valid ) > 0 ) {
2017-06-15 17:07:40 +00:00
// Baro and delta are both more recent than geometric, derive geometric from baro + delta
2017-12-07 16:34:08 +00: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 13:34:14 +00:00
}
2019-10-18 19:02:12 +00:00
// If we've got a new cpr_odd or cpr_even
if ( cpr_new ) {
2017-12-02 17:38:33 +00:00
updatePosition ( a , mm ) ;
2016-08-27 13:34:14 +00:00
}
2016-01-21 19:42:37 +00:00
2015-02-08 14:27:03 +00:00
return ( a ) ;
}
//
// Periodic updates of tracking state
//
2016-10-11 17:00:11 +00:00
// Periodically match up mode A/C results with mode S results
static void trackMatchAC ( uint64_t now )
2015-02-08 14:27:03 +00:00
{
2016-10-11 17:00:11 +00: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 20:29:39 +00:00
if ( ( modeAC_count [ i ] - modeAC_lastcount [ i ] ) > = TRACK_MODEAC_MIN_MESSAGES ) {
2016-10-11 17:00:11 +00:00
a - > modeA_hit = 1 ;
modeAC_match [ i ] = ( modeAC_match [ i ] ? 0xFFFFFFFF : a - > addr ) ;
2015-02-08 14:27:03 +00:00
}
2016-10-11 17:00:11 +00:00
}
2015-02-08 14:27:03 +00:00
2016-10-11 20:29:39 +00:00
// match on Mode C (+/- 100ft)
2017-12-07 16:34:08 +00:00
if ( trackDataValid ( & a - > altitude_baro_valid ) ) {
int modeC = ( a - > altitude_baro + 49 ) / 100 ;
2016-10-11 20:29:39 +00:00
2016-10-11 17:00:11 +00:00
unsigned modeA = modeCToModeA ( modeC ) ;
2016-10-11 20:29:39 +00: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 14:27:03 +00:00
}
}
}
2016-10-11 17:00:11 +00:00
// reset counts for next time
for ( unsigned i = 0 ; i < 4096 ; + + i ) {
2016-10-11 20:29:39 +00: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 17:00:11 +00:00
} else {
2016-10-11 20:29:39 +00: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 14:27:03 +00:00
}
2016-10-11 20:29:39 +00:00
modeAC_lastcount [ i ] = modeAC_count [ i ] ;
2015-02-08 14:27:03 +00:00
}
}
//
//=========================================================================
//
// If we don't receive new nessages within TRACK_AIRCRAFT_TTL
// we remove the aircraft from the list.
//
2015-02-10 21:49:37 +00:00
static void trackRemoveStaleAircraft ( uint64_t now )
2015-02-08 14:27:03 +00:00
{
struct aircraft * a = Modes . aircrafts ;
struct aircraft * prev = NULL ;
2017-06-15 17:16:51 +00:00
2015-02-08 14:27:03 +00:00
while ( a ) {
2019-11-27 12:30:41 +00:00
if ( ( now - a - > seen ) > TRACK_AIRCRAFT_TTL | | ( ! a - > reliable & & ( now - a - > seen ) > TRACK_AIRCRAFT_UNRELIABLE_TTL ) ) {
2015-02-08 18:47:39 +00: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 + + ;
2019-11-27 13:41:33 +00:00
if ( ! a - > reliable )
Modes . stats_current . unreliable_aircraft + + ;
2015-02-08 18:47:39 +00:00
2015-02-08 14:27:03 +00: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 13:34:14 +00: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 16:34:08 +00:00
EXPIRE ( altitude_baro ) ;
2017-06-15 17:07:40 +00: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 10:42:41 +00:00
EXPIRE ( true_heading ) ;
2017-06-15 17:07:40 +00:00
EXPIRE ( baro_rate ) ;
EXPIRE ( geom_rate ) ;
2016-08-27 13:34:14 +00:00
EXPIRE ( squawk ) ;
2021-01-15 10:20:50 +00:00
EXPIRE ( emergency ) ;
2016-08-27 13:34:14 +00:00
EXPIRE ( airground ) ;
2017-12-07 16:34:08 +00:00
EXPIRE ( nav_qnh ) ;
2019-03-19 18:44:09 +00:00
EXPIRE ( nav_altitude_mcp ) ;
EXPIRE ( nav_altitude_fms ) ;
EXPIRE ( nav_altitude_src ) ;
2017-12-07 16:34:08 +00:00
EXPIRE ( nav_heading ) ;
EXPIRE ( nav_modes ) ;
2016-08-27 13:34:14 +00:00
EXPIRE ( cpr_odd ) ;
EXPIRE ( cpr_even ) ;
EXPIRE ( position ) ;
2017-12-07 16:34:08 +00:00
EXPIRE ( nic_a ) ;
EXPIRE ( nic_c ) ;
EXPIRE ( nic_baro ) ;
EXPIRE ( nac_p ) ;
2021-01-15 10:20:50 +00:00
EXPIRE ( nac_v ) ;
2017-12-07 16:34:08 +00:00
EXPIRE ( sil ) ;
EXPIRE ( gva ) ;
EXPIRE ( sda ) ;
2021-07-29 11:04:54 +00:00
EXPIRE ( mrar_source ) ;
EXPIRE ( wind ) ;
EXPIRE ( temperature ) ;
EXPIRE ( pressure ) ;
EXPIRE ( turbulence ) ;
EXPIRE ( humidity ) ;
2017-12-02 17:38:33 +00:00
# undef EXPIRE
2015-02-08 14:27:03 +00:00
prev = a ; a = a - > next ;
}
}
}
//
// Entry point for periodic updates
//
void trackPeriodicUpdate ( )
{
2015-02-10 21:49:37 +00:00
static uint64_t next_update ;
uint64_t now = mstime ( ) ;
2015-02-08 14:27:03 +00:00
// Only do updates once per second
if ( now > = next_update ) {
2015-02-10 21:49:37 +00:00
next_update = now + 1000 ;
2015-02-08 14:27:03 +00:00
trackRemoveStaleAircraft ( now ) ;
2016-10-11 17:00:11 +00:00
trackMatchAC ( now ) ;
2015-02-08 14:27:03 +00:00
}
}