2015-01-21 00:04:05 +01:00
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
2015-01-20 00:41:26 +01:00
//
2015-01-21 00:04:05 +01:00
// crc.h: Mode S CRC calculation and error correction.
2015-01-20 00:41:26 +01:00
//
2015-01-21 00:04:05 +01:00
// Copyright (c) 2014,2015 Oliver Jowett <oliver@mutability.co.uk>
2015-01-20 00:41:26 +01:00
//
2017-06-15 19:16:51 +02:00
// This file is free software: you may copy, redistribute and/or modify it
2015-01-21 00:04:05 +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-01-20 00:41:26 +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-01-21 00:04:05 +01:00
// General Public License for more details.
2015-01-20 00:41:26 +01:00
//
2017-06-15 19:16:51 +02:00
// You should have received a copy of the GNU General Public License
2015-01-21 00:04:05 +01:00
// along with this program. If not, see <http://www.gnu.org/licenses/>.
2015-01-20 00:41:26 +01:00
# include "dump1090.h"
2015-01-21 00:04:05 +01:00
# include <assert.h>
2015-01-21 13:54:27 +01:00
// Errorinfo for "no errors"
static struct errorinfo NO_ERRORS ;
2015-01-21 00:04:05 +01:00
// Generator polynomial for the Mode S CRC:
# define MODES_GENERATOR_POLY 0xfff409U
// CRC values for all single-byte messages;
// used to speed up CRC calculation.
static uint32_t crc_table [ 256 ] ;
// Syndrome values for all single-bit errors;
// used to speed up construction of error-
// correction tables.
static uint32_t single_bit_syndrome [ 112 ] ;
static void initLookupTables ( )
{
int i ;
uint8_t msg [ 112 / 8 ] ;
2017-06-15 19:16:51 +02:00
2015-01-21 00:04:05 +01:00
for ( i = 0 ; i < 256 ; + + i ) {
uint32_t c = i < < 16 ;
int j ;
for ( j = 0 ; j < 8 ; + + j ) {
if ( c & 0x800000 )
c = ( c < < 1 ) ^ MODES_GENERATOR_POLY ;
else
c = ( c < < 1 ) ;
}
crc_table [ i ] = c & 0x00ffffff ;
}
memset ( msg , 0 , sizeof ( msg ) ) ;
for ( i = 0 ; i < 112 ; + + i ) {
msg [ i / 8 ] ^ = 1 < < ( 7 - ( i & 7 ) ) ;
single_bit_syndrome [ i ] = modesChecksum ( msg , 112 ) ;
msg [ i / 8 ] ^ = 1 < < ( 7 - ( i & 7 ) ) ;
}
2015-01-20 00:41:26 +01:00
}
2015-01-21 00:04:05 +01:00
uint32_t modesChecksum ( uint8_t * message , int bits )
{
uint32_t rem = 0 ;
int i ;
int n = bits / 8 ;
assert ( bits % 8 = = 0 ) ;
assert ( n > = 3 ) ;
for ( i = 0 ; i < n - 3 ; + + i ) {
rem = ( rem < < 8 ) ^ crc_table [ message [ i ] ^ ( ( rem & 0xff0000 ) > > 16 ) ] ;
rem = rem & 0xffffff ;
}
rem = rem ^ ( message [ n - 3 ] < < 16 ) ^ ( message [ n - 2 ] < < 8 ) ^ ( message [ n - 1 ] ) ;
return rem ;
}
static struct errorinfo * bitErrorTable_short ;
static int bitErrorTableSize_short ;
static struct errorinfo * bitErrorTable_long ;
static int bitErrorTableSize_long ;
// compare two errorinfo structures
static int syndrome_compare ( const void * x , const void * y ) {
struct errorinfo * ex = ( struct errorinfo * ) x ;
struct errorinfo * ey = ( struct errorinfo * ) y ;
return ( int ) ex - > syndrome - ( int ) ey - > syndrome ;
}
// (n k), the number of ways of selecting k distinct items from a set of n items
static int combinations ( int n , int k )
{
int result = 1 , i ;
if ( k = = 0 | | k = = n )
2015-01-20 00:41:26 +01:00
return 1 ;
2015-01-21 00:04:05 +01:00
if ( k > n )
return 0 ;
for ( i = 1 ; i < = k ; + + i ) {
result = result * n / i ;
n = n - 1 ;
2015-01-20 00:41:26 +01:00
}
2015-01-21 00:04:05 +01:00
return result ;
2015-01-20 00:41:26 +01:00
}
2015-01-21 00:04:05 +01:00
// Recursively populates an errorinfo table with error syndromes
2015-01-20 00:41:26 +01:00
//
2015-01-21 00:04:05 +01:00
// in:
// table: the table to fill
// n: first entry to fill
// maxSize: max size of table
// offset: start bit offset for checksum calculation
// startbit: first bit to introduce errors into
// endbit: (one past) last bit to introduce errors info
// base_entry: template entry to start from
// error_bit: how many error bits have already been set
// max_errors: maximum total error bits to set
// out:
// returns: the next free entry in the table
// table: has been populated between [n, return value)
static int prepareSubtable ( struct errorinfo * table , int n , int maxsize , int offset , int startbit , int endbit , struct errorinfo * base_entry , int error_bit , int max_errors )
{
int i = 0 ;
if ( error_bit > = max_errors )
return n ;
for ( i = startbit ; i < endbit ; + + i ) {
assert ( n < maxsize ) ;
table [ n ] = * base_entry ;
table [ n ] . syndrome ^ = single_bit_syndrome [ i + offset ] ;
table [ n ] . errors = error_bit + 1 ;
table [ n ] . bit [ error_bit ] = i ;
2017-06-15 19:16:51 +02:00
2015-01-21 00:04:05 +01:00
+ + n ;
n = prepareSubtable ( table , n , maxsize , offset , i + 1 , endbit , & table [ n - 1 ] , error_bit + 1 , max_errors ) ;
}
return n ;
}
static int flagCollisions ( struct errorinfo * table , int tablesize , int offset , int startbit , int endbit , uint32_t base_syndrome , int error_bit , int first_error , int last_error )
{
int i = 0 ;
int count = 0 ;
if ( error_bit > last_error )
return 0 ;
for ( i = startbit ; i < endbit ; + + i ) {
struct errorinfo ei ;
ei . syndrome = base_syndrome ^ single_bit_syndrome [ i + offset ] ;
if ( error_bit > = first_error ) {
struct errorinfo * collision = bsearch ( & ei , table , tablesize , sizeof ( struct errorinfo ) , syndrome_compare ) ;
if ( collision ! = NULL & & collision - > errors ! = - 1 ) {
+ + count ;
collision - > errors = - 1 ;
2015-01-20 00:41:26 +01:00
}
}
2015-01-21 00:04:05 +01:00
count + = flagCollisions ( table , tablesize , offset , i + 1 , endbit , ei . syndrome , error_bit + 1 , first_error , last_error ) ;
}
return count ;
}
// Allocate and build an error table for messages of length "bits" (max 112)
// returns a pointer to the new table and sets *size_out to the table length
static struct errorinfo * prepareErrorTable ( int bits , int max_correct , int max_detect , int * size_out )
{
int maxsize , usedsize ;
struct errorinfo * table ;
struct errorinfo base_entry ;
int i , j ;
assert ( bits > = 0 & & bits < = 112 ) ;
assert ( max_correct > = 0 & & max_correct < = MODES_MAX_BITERRORS ) ;
assert ( max_detect > = max_correct ) ;
if ( ! max_correct ) {
* size_out = 0 ;
return NULL ;
}
maxsize = 0 ;
for ( i = 1 ; i < = max_correct ; + + i ) {
maxsize + = combinations ( bits - 5 , i ) ; // space needed for all i-bit errors
}
2017-06-15 19:16:51 +02:00
# ifdef CRCDEBUG
2015-01-21 00:04:05 +01:00
fprintf ( stderr , " Preparing syndrome table to correct up to %d-bit errors (detecting %d-bit errors) in a %d-bit message (max %d entries) \n " , max_correct , max_detect , bits , maxsize ) ;
# endif
table = malloc ( maxsize * sizeof ( struct errorinfo ) ) ;
base_entry . syndrome = 0 ;
base_entry . errors = 0 ;
for ( i = 0 ; i < MODES_MAX_BITERRORS ; + + i )
base_entry . bit [ i ] = - 1 ;
// ignore the first 5 bits (DF type)
usedsize = prepareSubtable ( table , 0 , maxsize , 112 - bits , 5 , bits , & base_entry , 0 , max_correct ) ;
2017-06-15 19:16:51 +02:00
2015-01-21 00:04:05 +01:00
# ifdef CRCDEBUG
fprintf ( stderr , " %d syndromes (expected %d). \n " , usedsize , maxsize ) ;
fprintf ( stderr , " Sorting syndromes.. \n " ) ;
# endif
qsort ( table , usedsize , sizeof ( struct errorinfo ) , syndrome_compare ) ;
2017-06-15 19:16:51 +02:00
# ifdef CRCDEBUG
2015-01-21 00:04:05 +01:00
{
// Show the table stats
fprintf ( stderr , " Undetectable errors: \n " ) ;
for ( i = 1 ; i < = max_correct ; + + i ) {
int j , count ;
2017-06-15 19:16:51 +02:00
2015-01-21 00:04:05 +01:00
count = 0 ;
2017-06-15 19:16:51 +02:00
for ( j = 0 ; j < usedsize ; + + j )
2015-01-21 00:04:05 +01:00
if ( table [ j ] . errors = = i & & table [ j ] . syndrome = = 0 )
+ + count ;
fprintf ( stderr , " %d undetectable %d-bit errors \n " , count , i ) ;
2015-01-20 00:41:26 +01:00
}
}
2015-01-21 00:04:05 +01:00
# endif
2015-01-20 00:41:26 +01:00
2015-01-21 00:04:05 +01:00
// Handle ambiguous cases, where there is more than one possible error pattern
// that produces a given syndrome (this happens with >2 bit errors).
2017-06-15 19:16:51 +02:00
# ifdef CRCDEBUG
2015-01-21 00:04:05 +01:00
fprintf ( stderr , " Finding collisions.. \n " ) ;
# endif
for ( i = 0 , j = 0 ; i < usedsize ; + + i ) {
if ( i < usedsize - 1 & & table [ i + 1 ] . syndrome = = table [ i ] . syndrome ) {
// skip over this entry and all collisions
while ( i < usedsize & & table [ i + 1 ] . syndrome = = table [ i ] . syndrome )
+ + i ;
// now table[i] is the last duplicate
continue ;
}
if ( i ! = j )
table [ j ] = table [ i ] ;
+ + j ;
2015-01-20 00:41:26 +01:00
}
2015-01-21 00:04:05 +01:00
if ( j < usedsize ) {
# ifdef CRCDEBUG
fprintf ( stderr , " Discarded %d collisions. \n " , usedsize - j ) ;
# endif
usedsize = j ;
2015-01-20 00:41:26 +01:00
}
2017-06-15 19:16:51 +02:00
2015-01-21 00:04:05 +01:00
// Flag collisions we want to detect but not correct
if ( max_detect > max_correct ) {
int flagged ;
2015-01-20 00:41:26 +01:00
2015-01-21 00:04:05 +01:00
# ifdef CRCDEBUG
fprintf ( stderr , " Flagging collisions between %d - %d bits.. \n " , max_correct + 1 , max_detect ) ;
# endif
flagged = flagCollisions ( table , usedsize , 112 - bits , 5 , bits , 0 , 1 , max_correct + 1 , max_detect ) ;
# ifdef CRCDEBUG
fprintf ( stderr , " Flagged %d collisions for removal. \n " , flagged ) ;
# else
# endif
if ( flagged > 0 ) {
for ( i = 0 , j = 0 ; i < usedsize ; + + i ) {
if ( table [ i ] . errors ! = - 1 ) {
if ( i ! = j )
table [ j ] = table [ i ] ;
+ + j ;
}
}
# ifdef CRCDEBUG
fprintf ( stderr , " Discarded %d flagged collisions. \n " , usedsize - j ) ;
# endif
usedsize = j ;
}
2015-01-20 00:41:26 +01:00
}
2015-01-21 00:04:05 +01:00
if ( usedsize < maxsize ) {
# ifdef CRCDEBUG
fprintf ( stderr , " Shrinking table from %d to %d.. \n " , maxsize , usedsize ) ;
table = realloc ( table , usedsize * sizeof ( struct errorinfo ) ) ;
# endif
2015-01-20 00:41:26 +01:00
}
2017-06-15 19:16:51 +02:00
2015-01-21 00:04:05 +01:00
* size_out = usedsize ;
2017-06-15 19:16:51 +02:00
2015-01-21 00:04:05 +01:00
# ifdef CRCDEBUG
{
// Check the table.
unsigned char * msg = malloc ( bits / 8 ) ;
2015-01-20 00:41:26 +01:00
2015-01-21 00:04:05 +01:00
for ( i = 0 ; i < usedsize ; + + i ) {
int j ;
struct errorinfo * ei ;
uint32_t result ;
memset ( msg , 0 , bits / 8 ) ;
ei = & table [ i ] ;
for ( j = 0 ; j < ei - > errors ; + + j ) {
msg [ ei - > bit [ j ] > > 3 ] ^ = 1 < < ( 7 - ( ei - > bit [ j ] & 7 ) ) ;
}
result = modesChecksum ( msg , bits ) ;
if ( result ! = ei - > syndrome ) {
fprintf ( stderr , " PROBLEM: entry %6d/%6d syndrome %06x errors %d bits " , i , usedsize , ei - > syndrome , ei - > errors ) ;
for ( j = 0 ; j < ei - > errors ; + + j )
fprintf ( stderr , " %3d " , ei - > bit [ j ] ) ;
fprintf ( stderr , " checksum %06x \n " , result ) ;
}
}
free ( msg ) ;
// Show the table stats
fprintf ( stderr , " Syndrome table summary: \n " ) ;
for ( i = 1 ; i < = max_correct ; + + i ) {
int j , count , possible ;
2017-06-15 19:16:51 +02:00
2015-01-21 00:04:05 +01:00
count = 0 ;
2017-06-15 19:16:51 +02:00
for ( j = 0 ; j < usedsize ; + + j )
2015-01-21 00:04:05 +01:00
if ( table [ j ] . errors = = i )
+ + count ;
possible = combinations ( bits - 5 , i ) ;
fprintf ( stderr , " %d entries for %d-bit errors (%d possible, %d%% coverage) \n " , count , i , possible , 100 * count / possible ) ;
}
fprintf ( stderr , " %d entries total \n " , usedsize ) ;
}
# endif
return table ;
}
// Precompute syndrome tables for 56- and 112-bit messages.
void modesChecksumInit ( int fixBits )
{
initLookupTables ( ) ;
switch ( fixBits ) {
case 0 :
bitErrorTable_short = bitErrorTable_long = NULL ;
bitErrorTableSize_short = bitErrorTableSize_long = 0 ;
break ;
case 1 :
// For 1 bit correction, we have 100% coverage up to 4 bit detection, so don't bother
// with flagging collisions there.
bitErrorTable_short = prepareErrorTable ( MODES_SHORT_MSG_BITS , 1 , 1 , & bitErrorTableSize_short ) ;
bitErrorTable_long = prepareErrorTable ( MODES_LONG_MSG_BITS , 1 , 1 , & bitErrorTableSize_long ) ;
break ;
default :
// Detect out to 4 bit errors; this reduces our 2-bit coverage to about 65%.
// This can take a little while - tell the user.
fprintf ( stderr , " Preparing error correction tables.. " ) ;
bitErrorTable_short = prepareErrorTable ( MODES_SHORT_MSG_BITS , 2 , 4 , & bitErrorTableSize_short ) ;
bitErrorTable_long = prepareErrorTable ( MODES_LONG_MSG_BITS , 2 , 4 , & bitErrorTableSize_long ) ;
fprintf ( stderr , " done. \n " ) ;
break ;
}
}
// Given an error syndrome and message length, return
// an error-correction descriptor, or NULL if the
// syndrome is uncorrectable
struct errorinfo * modesChecksumDiagnose ( uint32_t syndrome , int bitlen )
{
struct errorinfo * table ;
int tablesize ;
struct errorinfo ei ;
if ( syndrome = = 0 )
2015-01-21 13:54:27 +01:00
return & NO_ERRORS ;
2015-01-21 00:04:05 +01:00
assert ( bitlen = = 56 | | bitlen = = 112 ) ;
if ( bitlen = = 56 ) { table = bitErrorTable_short ; tablesize = bitErrorTableSize_short ; }
else { table = bitErrorTable_long ; tablesize = bitErrorTableSize_long ; }
if ( ! table )
return NULL ;
ei . syndrome = syndrome ;
return bsearch ( & ei , table , tablesize , sizeof ( struct errorinfo ) , syndrome_compare ) ;
}
// Given a message and an error-correction descriptor,
// apply the error correction to the given message.
void modesChecksumFix ( uint8_t * msg , struct errorinfo * info )
{
int i ;
if ( ! info )
return ;
for ( i = 0 ; i < info - > errors ; + + i )
msg [ info - > bit [ i ] > > 3 ] ^ = 1 < < ( 7 - ( info - > bit [ i ] & 7 ) ) ;
}
# ifdef CRCDEBUG
int main ( int argc , char * * argv )
{
2015-01-23 22:59:31 +01:00
int shortlen , longlen ;
int i ;
struct errorinfo * shorttable , * longtable ;
2015-01-21 00:04:05 +01:00
if ( argc < 3 ) {
fprintf ( stderr , " syntax: crctests <ncorrect> <ndetect> \n " ) ;
return 1 ;
2015-01-20 00:41:26 +01:00
}
2015-01-21 00:04:05 +01:00
initLookupTables ( ) ;
2015-01-23 22:59:31 +01:00
shorttable = prepareErrorTable ( MODES_SHORT_MSG_BITS , atoi ( argv [ 1 ] ) , atoi ( argv [ 2 ] ) , & shortlen ) ;
longtable = prepareErrorTable ( MODES_LONG_MSG_BITS , atoi ( argv [ 1 ] ) , atoi ( argv [ 2 ] ) , & longlen ) ;
// check for DF11 correction syndromes where there is a syndrome with lower 7 bits all zero
// (which would be used for DF11 error correction), but there's also a syndrome which has
// the same upper 17 bits but nonzero lower 7 bits.
// empirically, with ncorrect=1 ndetect=2 we get no ambiguous syndromes;
// for ncorrect=2 ndetect=4 we get 11 ambiguous syndromes:
/*
syndrome 1 = 000 C00 bits = [ 44 45 ]
syndrome 2 = 000 C1B bits = [ 30 43 ]
syndrome 1 = 001400 bits = [ 43 45 ]
syndrome 2 = 00141 B bits = [ 30 44 ]
syndrome 1 = 001 800 bits = [ 43 44 ]
syndrome 2 = 001 81 B bits = [ 30 45 ]
syndrome 1 = 001 800 bits = [ 43 44 ]
syndrome 2 = 001 836 bits = [ 29 42 ]
syndrome 1 = 002400 bits = [ 42 45 ]
syndrome 2 = 00242 D bits = [ 29 30 ]
syndrome 1 = 002 800 bits = [ 42 44 ]
syndrome 2 = 002 836 bits = [ 29 43 ]
syndrome 1 = 003000 bits = [ 42 43 ]
syndrome 2 = 003036 bits = [ 29 44 ]
syndrome 1 = 003000 bits = [ 42 43 ]
syndrome 2 = 00306 C bits = [ 28 41 ]
syndrome 1 = 004 800 bits = [ 41 44 ]
syndrome 2 = 004 85 A bits = [ 28 29 ]
syndrome 1 = 005000 bits = [ 41 43 ]
syndrome 2 = 00506 C bits = [ 28 42 ]
syndrome 1 = 006000 bits = [ 41 42 ]
syndrome 2 = 00606 C bits = [ 28 43 ]
*/
// So in the DF11 correction logic, we just discard messages that require more than a 1 bit fix.
fprintf ( stderr , " checking %d syndromes for DF11 collisions.. \n " , shortlen ) ;
for ( i = 0 ; i < shortlen ; + + i ) {
if ( ( shorttable [ i ] . syndrome & 0xFF ) = = 0 ) {
int j ;
// all syndromes with the same first 17 bits should sort immediately after entry i,
// so this is fairly easy
for ( j = i + 1 ; j < shortlen ; + + j ) {
if ( ( shorttable [ i ] . syndrome & 0xFFFF80 ) = = ( shorttable [ j ] . syndrome & 0xFFFF80 ) ) {
int k ;
int mismatch = 0 ;
// we don't care if the only differences are in bits that lie in the checksum
for ( k = 0 ; k < shorttable [ i ] . errors ; + + k ) {
int l , matched = 0 ;
if ( shorttable [ i ] . bit [ k ] > = 49 )
continue ; // bit is in the final 7 bits, we don't care
for ( l = 0 ; l < shorttable [ j ] . errors ; + + l ) {
if ( shorttable [ i ] . bit [ k ] = = shorttable [ j ] . bit [ l ] ) {
matched = 1 ;
break ;
}
}
if ( ! matched )
mismatch = 1 ;
}
for ( k = 0 ; k < shorttable [ j ] . errors ; + + k ) {
int l , matched = 0 ;
if ( shorttable [ j ] . bit [ k ] > = 49 )
continue ; // bit is in the final 7 bits, we don't care
for ( l = 0 ; l < shorttable [ i ] . errors ; + + l ) {
if ( shorttable [ j ] . bit [ k ] = = shorttable [ i ] . bit [ l ] ) {
matched = 1 ;
break ;
}
}
if ( ! matched )
mismatch = 1 ;
}
if ( mismatch ) {
fprintf ( stderr ,
" DF11 correction collision: \n "
" syndrome 1 = %06X bits=[ " ,
shorttable [ i ] . syndrome ) ;
for ( k = 0 ; k < shorttable [ i ] . errors ; + + k )
fprintf ( stderr , " %d " , shorttable [ i ] . bit [ k ] ) ;
fprintf ( stderr , " ] \n " ) ;
fprintf ( stderr ,
" syndrome 2 = %06X bits=[ " ,
shorttable [ j ] . syndrome ) ;
for ( k = 0 ; k < shorttable [ j ] . errors ; + + k )
fprintf ( stderr , " %d " , shorttable [ j ] . bit [ k ] ) ;
fprintf ( stderr , " ] \n " ) ;
}
} else {
break ;
}
}
}
}
free ( shorttable ) ;
free ( longtable ) ;
2015-01-21 00:04:05 +01:00
return 0 ;
2015-01-20 00:41:26 +01:00
}
2015-01-21 00:04:05 +01:00
# endif