First cut for sampling at 2.4MHz + phase detection.

This commit is contained in:
Oliver Jowett 2014-09-26 22:42:38 +01:00
parent 4cf07536be
commit 5c8e6198b7
3 changed files with 423 additions and 6 deletions

View file

@ -94,9 +94,10 @@ void modesInit(void) {
pthread_cond_init(&Modes.data_cond,NULL);
// Allocate the various buffers used by Modes
Modes.trailing_space = Modes.oversample ? (MODES_OS_PREAMBLE_SIZE + MODES_OS_LONG_MSG_SIZE) : (MODES_PREAMBLE_SIZE + MODES_LONG_MSG_SIZE);
if ( ((Modes.icao_cache = (uint32_t *) malloc(sizeof(uint32_t) * MODES_ICAO_CACHE_LEN * 2) ) == NULL) ||
((Modes.pFileData = (uint16_t *) malloc(MODES_ASYNC_BUF_SIZE) ) == NULL) ||
((Modes.magnitude = (uint16_t *) malloc(MODES_ASYNC_BUF_SIZE+MODES_PREAMBLE_SIZE+MODES_LONG_MSG_SIZE) ) == NULL) ||
((Modes.magnitude = (uint16_t *) malloc(MODES_ASYNC_BUF_SIZE+Modes.trailing_space) ) == NULL) ||
((Modes.maglut = (uint16_t *) malloc(sizeof(uint16_t) * 256 * 256) ) == NULL) ||
((Modes.log10lut = (uint16_t *) malloc(sizeof(uint16_t) * 256 * 256) ) == NULL) ||
((Modes.beastOut = (char *) malloc(MODES_RAWOUT_BUF_SIZE) ) == NULL) ||
@ -109,7 +110,7 @@ void modesInit(void) {
// Clear the buffers that have just been allocated, just in-case
memset(Modes.icao_cache, 0, sizeof(uint32_t) * MODES_ICAO_CACHE_LEN * 2);
memset(Modes.pFileData,127, MODES_ASYNC_BUF_SIZE);
memset(Modes.magnitude, 0, MODES_ASYNC_BUF_SIZE+MODES_PREAMBLE_SIZE+MODES_LONG_MSG_SIZE);
memset(Modes.magnitude, 0, MODES_ASYNC_BUF_SIZE+Modes.trailing_space);
// Validate the users Lat/Lon home location inputs
if ( (Modes.fUserLat > 90.0) // Latitude must be -90 to +90
@ -244,7 +245,8 @@ void modesInitRTLSDR(void) {
rtlsdr_set_freq_correction(Modes.dev, Modes.ppm_error);
if (Modes.enable_agc) rtlsdr_set_agc_mode(Modes.dev, 1);
rtlsdr_set_center_freq(Modes.dev, Modes.freq);
rtlsdr_set_sample_rate(Modes.dev, MODES_DEFAULT_RATE);
rtlsdr_set_sample_rate(Modes.dev, Modes.oversample ? MODES_OVERSAMPLE_RATE : MODES_DEFAULT_RATE);
rtlsdr_reset_buffer(Modes.dev);
fprintf(stderr, "Gain reported by device: %.2f\n",
rtlsdr_get_tuner_gain(Modes.dev)/10.0);
@ -786,6 +788,9 @@ int main(int argc, char **argv) {
Modes.interactive_rtl1090 = 1;
} else if (!strcmp(argv[j],"--no-decode")) {
Modes.no_decode = 1;
} else if (!strcmp(argv[j],"--oversample")) {
Modes.oversample = 1;
fprintf(stderr, "Oversampling enabled. Be very afraid.\n");
} else {
fprintf(stderr,
"Unknown or not enough arguments for option '%s'.\n\n",
@ -892,7 +897,10 @@ int main(int argc, char **argv) {
// Process data after releasing the lock, so that the capturing
// thread can read data while we perform computationally expensive
// stuff at the same time.
detectModeS(Modes.magnitude, MODES_ASYNC_BUF_SAMPLES);
if (Modes.oversample)
detectModeS_oversample(Modes.magnitude, MODES_ASYNC_BUF_SAMPLES);
else
detectModeS(Modes.magnitude, MODES_ASYNC_BUF_SAMPLES);
// Update the timestamp ready for the next block
Modes.timestampBlk += (MODES_ASYNC_BUF_SAMPLES*6);

View file

@ -80,6 +80,7 @@
#define MODES_DEFAULT_PPM 52
#define MODES_DEFAULT_RATE 2000000
#define MODES_OVERSAMPLE_RATE 2400000
#define MODES_DEFAULT_FREQ 1090000000
#define MODES_DEFAULT_WIDTH 1000
#define MODES_DEFAULT_HEIGHT 700
@ -116,6 +117,13 @@
#define MODES_LONG_MSG_SIZE (MODES_LONG_MSG_SAMPLES * sizeof(uint16_t))
#define MODES_SHORT_MSG_SIZE (MODES_SHORT_MSG_SAMPLES * sizeof(uint16_t))
#define MODES_OS_PREAMBLE_SAMPLES (20)
#define MODES_OS_PREAMBLE_SIZE (MODES_OS_PREAMBLE_SAMPLES * sizeof(uint16_t))
#define MODES_OS_LONG_MSG_SAMPLES (268)
#define MODES_OS_SHORT_MSG_SAMPLES (135)
#define MODES_OS_LONG_MSG_SIZE (MODES_LONG_MSG_SAMPLES * sizeof(uint16_t))
#define MODES_OS_SHORT_MSG_SIZE (MODES_SHORT_MSG_SAMPLES * sizeof(uint16_t))
#define MODES_RAWOUT_BUF_SIZE (1500)
#define MODES_RAWOUT_BUF_FLUSH (MODES_RAWOUT_BUF_SIZE - 200)
#define MODES_RAWOUT_BUF_RATE (1000) // 1000 * 64mS = 1 Min approx
@ -250,6 +258,8 @@ struct { // Internal state
int iDataReady; // Fifo content count
int iDataLost; // Count of missed buffers
int trailing_space; // extra trailing space needed by magnitude buffer
uint16_t *pFileData; // Raw IQ samples buffer (from a File)
uint16_t *magnitude; // Magnitude vector
uint64_t timestampBlk; // Timestamp of the start of the current block
@ -287,6 +297,7 @@ struct { // Internal state
// Configuration
char *filename; // Input form file, --ifile option
int oversample;
int phase_enhance; // Enable phase enhancement if true
int nfix_crc; // Number of crc bit error(s) to correct
int check_crc; // Only display messages with good CRC
@ -434,6 +445,7 @@ int ModeAToModeC (unsigned int ModeA);
// Functions exported from mode_s.c
//
void detectModeS (uint16_t *m, uint32_t mlen);
void detectModeS_oversample (uint16_t *m, uint32_t mlen);
void decodeModesMessage (struct modesMessage *mm, unsigned char *msg);
void displayModesMessage(struct modesMessage *mm);
void useModesMessage (struct modesMessage *mm);

401
mode_s.c
View file

@ -1439,10 +1439,10 @@ void displayModesMessage(struct modesMessage *mm) {
// pointed by Modes.magnitude.
//
void computeMagnitudeVector(uint16_t *p) {
uint16_t *m = &Modes.magnitude[MODES_PREAMBLE_SAMPLES+MODES_LONG_MSG_SAMPLES];
uint16_t *m = &Modes.magnitude[Modes.trailing_space];
uint32_t j;
memcpy(Modes.magnitude,&Modes.magnitude[MODES_ASYNC_BUF_SAMPLES], MODES_PREAMBLE_SIZE+MODES_LONG_MSG_SIZE);
memcpy(Modes.magnitude,&Modes.magnitude[MODES_ASYNC_BUF_SAMPLES], Modes.trailing_space);
// Compute the magnitudo vector. It's just SQRT(I^2 + Q^2), but
// we rescale to the 0-255 range to exploit the full resolution.
@ -1450,6 +1450,7 @@ void computeMagnitudeVector(uint16_t *p) {
*m++ = Modes.maglut[*p++];
}
}
//
//=========================================================================
//
@ -1933,6 +1934,402 @@ void detectModeS(uint16_t *m, uint32_t mlen) {
Modes.net_heartbeat_count = 0;
}
}
// 2.4MHz sampling rate version
//
// When sampling at 2.4MHz we have exactly 6 samples per 5 symbols.
// Each symbol is 500ns wide, each sample is 416.7ns wide
//
// We maintain a phase offset that is expressed in units of 1/5 of a sample i.e. 1/6 of a symbol, 83.333ns
// Each symbol we process advances the phase offset by 6 i.e. 6/5 of a sample, 500ns
//
// The correlation functions below correlate a 1-0 pair of symbols (i.e. manchester encoded 1 bit)
// starting at the given sample, and assuming that the symbol starts at a fixed 0-5 phase offset within
// m[0]. They return a correlation value, generally interpreted as >0 = 1 bit, <0 = 0 bit
static inline int correlate_phase0(uint16_t *m) {
return 5 * m[0] - 3 * m[1] - 2 * m[2];
}
static inline int correlate_phase1(uint16_t *m) {
return 4 * m[0] - 1 * m[1] - 3 * m[2];
}
static inline int correlate_phase2(uint16_t *m) {
return 3 * m[0] + 1 * m[1] - 4 * m[2];
}
static inline int correlate_phase3(uint16_t *m) {
return 2 * m[0] + 3 * m[1] - 5 * m[2];
}
static inline int correlate_phase4(uint16_t *m) {
return 1 * m[0] + 5 * m[1] - 5 * m[2] + 1 * m[3];
}
//
// These functions work out the correlation quality for the 10 symbols (5 bits) starting at m[0] + given phase offset.
// This is used to find the right phase offset to use for decoding.
//
static inline int correlate_check_0(uint16_t *m) {
return
abs(correlate_phase0(&m[0])) +
abs(correlate_phase2(&m[2])) +
abs(correlate_phase4(&m[4])) +
abs(correlate_phase1(&m[7])) +
abs(correlate_phase3(&m[9]));
}
static inline int correlate_check_1(uint16_t *m) {
return
abs(correlate_phase1(&m[0])) +
abs(correlate_phase3(&m[2])) +
abs(correlate_phase0(&m[5])) +
abs(correlate_phase2(&m[7])) +
abs(correlate_phase4(&m[9]));
}
static inline int correlate_check_2(uint16_t *m) {
return
abs(correlate_phase2(&m[0])) +
abs(correlate_phase4(&m[2])) +
abs(correlate_phase1(&m[5])) +
abs(correlate_phase3(&m[7])) +
abs(correlate_phase0(&m[10]));
}
static inline int correlate_check_3(uint16_t *m) {
return
abs(correlate_phase3(&m[0])) +
abs(correlate_phase0(&m[3])) +
abs(correlate_phase2(&m[5])) +
abs(correlate_phase4(&m[7])) +
abs(correlate_phase1(&m[10]));
}
static inline int correlate_check_4(uint16_t *m) {
return
abs(correlate_phase4(&m[0])) +
abs(correlate_phase1(&m[3])) +
abs(correlate_phase3(&m[5])) +
abs(correlate_phase0(&m[8])) +
abs(correlate_phase2(&m[10]));
}
// Work out the best phase offset to use for the given message.
// Note that the message may start at anywhere between
// m[19] offset 1 and m[20] offset 0
static int best_phase(uint16_t *m) {
int test;
int best = -1;
int bestval = 50; // minimum correlation quality we will accept
// look at the first 5 bits with each possible phase
test = correlate_check_1(&m[19]);
if (test > bestval) { bestval = test; best = 1; }
test = correlate_check_2(&m[19]);
if (test > bestval) { bestval = test; best = 2; }
test = correlate_check_3(&m[19]);
if (test > bestval) { bestval = test; best = 3; }
test = correlate_check_4(&m[19]);
if (test > bestval) { bestval = test; best = 4; }
test = correlate_check_0(&m[20]);
if (test > bestval) { best = 5; }
return best;
}
//
//=========================================================================
//
// Detect a Mode S messages inside the magnitude buffer pointed by 'm' and of
// size 'mlen' bytes. Every detected Mode S message is convert it into a
// stream of bits and passed to the function to display it.
//
void detectModeS_oversample(uint16_t *m, uint32_t mlen) {
struct modesMessage mm;
unsigned char msg[MODES_LONG_MSG_BYTES], *pMsg;
uint32_t j;
memset(&mm, 0, sizeof(mm));
for (j = 0; j < mlen; j++) {
uint16_t *preamble = &m[j];
int high, i, phase, errors, errors56, errorsTy;
int msglen, scanlen;
uint16_t *pPtr;
uint8_t theByte, theErrs;
// Rather than clear the whole mm structure, just clear the parts which are required. The clear
// is required for every bit of the input stream, and we don't want to be memset-ing the whole
// modesMessage structure two million times per second if we don't have to..
mm.bFlags =
mm.crcok =
mm.correctedbits = 0;
// Look for a message starting at sample 1 with phase offset 0-4
// Check for peaks at (1,12) or (3,9)
if (preamble[1] > preamble[0] &&
preamble[1] > preamble[2] &&
preamble[12] > preamble[11] &&
preamble[12] > preamble[13]) {
high = (preamble[1] + preamble[13]) / 2;
} else if (preamble[3] > preamble[2] &&
preamble[3] > preamble[4] &&
preamble[9] > preamble[8] &&
preamble[9] > preamble[10]) {
high = (preamble[1] + preamble[9]) / 2;
} else {
// No peaks
continue;
}
// The samples between the two spikes must be < than the average
// of the high spikes level. We don't test bits too near to
// the high levels as signals can be out of phase so part of the
// energy can be in the near samples
if (preamble[5] >= high ||
preamble[6] >= high ||
preamble[7] >= high ||
preamble[14] >= high ||
preamble[15] >= high ||
preamble[16] >= high ||
preamble[17] >= high ||
preamble[18] >= high)
continue;
// Crosscorrelate against the first few bits to find a likely phase offset
phase = best_phase(preamble);
if (phase < 0) {
continue; // nothing satisfactory
}
Modes.stat_valid_preamble++;
// Decode all the next 112 bits, regardless of the actual message
// size. We'll check the actual message type later
pMsg = &msg[0];
pPtr = &m[j+19];
if (phase == 5) {
++pPtr;
phase = 0;
}
theByte = 0;
theErrs = 0; errorsTy = 0;
errors = 0; errors56 = 0;
msglen = scanlen = MODES_LONG_MSG_BITS;
for (i = 0; i < scanlen; i++) {
int test;
switch (phase) {
case 0:
test = correlate_phase0(pPtr);
phase = 2;
pPtr += 2;
break;
case 1:
test = correlate_phase1(pPtr);
phase = 3;
pPtr += 2;
break;
case 2:
test = correlate_phase2(pPtr);
phase = 4;
pPtr += 2;
break;
case 3:
test = correlate_phase3(pPtr);
phase = 0;
pPtr += 3;
break;
case 4:
test = correlate_phase4(pPtr);
phase = 1;
pPtr += 3;
break;
default:
test = 0;
break;
}
if (test > 10)
theByte |= 1;
else if (test >= -10) {
if (test > 0)
theByte |= 1; // best guess
if (i >= MODES_SHORT_MSG_BITS) { // poor correlation, and we're in the long part of a frame
errors++;
} else if (i >= 5) { // poor correlation, and we're in the short part of a frame
scanlen = MODES_LONG_MSG_BITS;
errors56 = ++errors;
} else if (i) { // poor correlation, and we're in the message type part of a frame
errorsTy = errors56 = ++errors;
theErrs |= 1;
} else { // poor correlation, and we're in the first bit of the message type part of a frame
errorsTy = errors56 = ++errors;
theErrs |= 1;
}
}
if ((i & 7) == 7)
*pMsg++ = theByte;
theByte = theByte << 1;
if (i < 7)
{theErrs = theErrs << 1;}
// If we've exceeded the permissible number of encoding errors, abandon ship now
if (errors > MODES_MSG_ENCODER_ERRS) {
if (i < MODES_SHORT_MSG_BITS) {
msglen = 0;
} else if ((errorsTy == 1) && (theErrs == 0x80)) {
// If we only saw one error in the first bit of the byte of the frame, then it's possible
// we guessed wrongly about the value of the bit. We may be able to correct it by guessing
// the other way.
//
// We guessed a '1' at bit 7, which is the DF length bit == 112 Bits.
// Inverting bit 7 will change the message type from a long to a short.
// Invert the bit, cross your fingers and carry on.
msglen = MODES_SHORT_MSG_BITS;
msg[0] ^= theErrs; errorsTy = 0;
errors = errors56; // revert to the number of errors prior to bit 56
Modes.stat_DF_Len_Corrected++;
} else if (i < MODES_LONG_MSG_BITS) {
msglen = MODES_SHORT_MSG_BITS;
errors = errors56;
} else {
msglen = MODES_LONG_MSG_BITS;
}
break;
}
}
// Ensure msglen is consistent with the DF type
i = modesMessageLenByType(msg[0] >> 3);
if (msglen > i) {msglen = i;}
else if (msglen < i) {msglen = 0;}
//
// If we guessed at any of the bits in the DF type field, then look to see if our guess was sensible.
// Do this by looking to see if the original guess results in the DF type being one of the ICAO defined
// message types. If it isn't then toggle the guessed bit and see if this new value is ICAO defined.
// if the new value is ICAO defined, then update it in our message.
if ((msglen) && (errorsTy == 1) && (theErrs & 0x78)) {
// We guessed at one (and only one) of the message type bits. See if our guess is "likely"
// to be correct by comparing the DF against a list of known good DF's
int thisDF = ((theByte = msg[0]) >> 3) & 0x1f;
uint32_t validDFbits = 0x017F0831; // One bit per 32 possible DF's. Set bits 0,4,5,11,16.17.18.19,20,21,22,24
uint32_t thisDFbit = (1 << thisDF);
if (0 == (validDFbits & thisDFbit)) {
// The current DF is not ICAO defined, so is probably an errors.
// Toggle the bit we guessed at and see if the resultant DF is more likely
theByte ^= theErrs;
thisDF = (theByte >> 3) & 0x1f;
thisDFbit = (1 << thisDF);
// if this DF any more likely?
if (validDFbits & thisDFbit) {
// Yep, more likely, so update the main message
msg[0] = theByte;
Modes.stat_DF_Type_Corrected++;
errors--; // decrease the error count so we attempt to use the modified DF.
}
}
}
// When we reach this point, if error is small, and the signal strength is large enough
// we may have a Mode S message on our hands. It may still be broken and the CRC may not
// be correct, but this can be handled by the next layer.
if ( (msglen > 0) && (errors <= MODES_MSG_ENCODER_ERRS) ) {
// Set initial mm structure details
mm.timestampMsg = Modes.timestampBlk + (j*5);
mm.signalLevel = 0;
mm.phase_corrected = 0;
//dumpRawMessage("decoded with oversampling", msg, m, j);
// Decode the received message
decodeModesMessage(&mm, msg);
// Update statistics
if (Modes.stats) {
if (mm.crcok || mm.correctedbits) {
switch (errors) {
case 0: {Modes.stat_demodulated0++; break;}
case 1: {Modes.stat_demodulated1++; break;}
case 2: {Modes.stat_demodulated2++; break;}
default:{Modes.stat_demodulated3++; break;}
}
if (mm.correctedbits == 0) {
if (mm.crcok) {Modes.stat_goodcrc++;}
else {Modes.stat_badcrc++;}
} else {
Modes.stat_badcrc++;
Modes.stat_fixed++;
if ( (mm.correctedbits)
&& (mm.correctedbits <= MODES_MAX_BITERRORS) ) {
Modes.stat_bit_fix[mm.correctedbits-1] += 1;
}
}
}
}
// Skip this message if we are sure it's fine
if (mm.crcok) {
j += (16+msglen)*6/5 - 1;
}
// Pass data to the next layer
useModesMessage(&mm);
}
}
//Send any remaining partial raw buffers now
if (Modes.rawOutUsed || Modes.beastOutUsed)
{
Modes.net_output_raw_rate_count++;
if (Modes.net_output_raw_rate_count > Modes.net_output_raw_rate)
{
if (Modes.rawOutUsed) {
modesSendAllClients(Modes.ros, Modes.rawOut, Modes.rawOutUsed);
Modes.rawOutUsed = 0;
}
if (Modes.beastOutUsed) {
modesSendAllClients(Modes.bos, Modes.beastOut, Modes.beastOutUsed);
Modes.beastOutUsed = 0;
}
Modes.net_output_raw_rate_count = 0;
}
}
else if ( (Modes.net)
&& (Modes.net_heartbeat_rate)
&& ((++Modes.net_heartbeat_count) > Modes.net_heartbeat_rate) ) {
//
// We haven't received any Mode A/C/S messages for some time. To try and keep any TCP
// links alive, send a null frame. This will help stop any routers discarding our TCP
// link which will cause an un-recoverable link error if/when a real frame arrives.
//
// Fudge up a null message
memset(&mm, 0, sizeof(mm));
mm.msgbits = MODES_SHORT_MSG_BITS;
mm.timestampMsg = Modes.timestampBlk;
// Feed output clients
modesQueueOutput(&mm);
// Reset the heartbeat counter
Modes.net_heartbeat_count = 0;
}
}
//
//=========================================================================
//