From d3662bba80f6bab5fcfc501b8ec668ab1ef7f0d8 Mon Sep 17 00:00:00 2001 From: Malcolm Robb Date: Sun, 7 Apr 2013 16:22:02 +0100 Subject: [PATCH] Implemented Beast Binary output --- dump1090.c | 143 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 95 insertions(+), 48 deletions(-) diff --git a/dump1090.c b/dump1090.c index 801bd70..85d4338 100644 --- a/dump1090.c +++ b/dump1090.c @@ -49,16 +49,22 @@ #define MODES_DEFAULT_WIDTH 1000 #define MODES_DEFAULT_HEIGHT 700 #define MODES_ASYNC_BUF_NUMBER 12 -#define MODES_DATA_LEN (16*16384) /* 256k */ +#define MODES_ASYNC_BUF_SIZE (16*16384) /* 256k */ +#define MODES_ASYNC_BUF_SAMPLES (MODES_ASYNC_BUF_SIZE / 2) /* Each sample is 2 bytes */ #define MODES_AUTO_GAIN -100 /* Use automatic gain. */ #define MODES_MAX_GAIN 999999 /* Use max available gain. */ -#define MODES_PREAMBLE_US 8 /* microseconds */ -#define MODES_LONG_MSG_BITS 112 -#define MODES_SHORT_MSG_BITS 56 -#define MODES_FULL_LEN (MODES_PREAMBLE_US+MODES_LONG_MSG_BITS) -#define MODES_LONG_MSG_BYTES (112/8) -#define MODES_SHORT_MSG_BYTES (56/8) +#define MODES_PREAMBLE_US 8 /* microseconds = bits */ +#define MODES_PREAMBLE_SAMPLES (MODES_PREAMBLE_US * 2) +#define MODES_PREAMBLE_SIZE (MODES_PREAMBLE_SAMPLES * sizeof(uint16_t)) +#define MODES_LONG_MSG_BYTES 14 +#define MODES_SHORT_MSG_BYTES 7 +#define MODES_LONG_MSG_BITS (MODES_LONG_MSG_BYTES * 8) +#define MODES_SHORT_MSG_BITS (MODES_SHORT_MSG_BYTES * 8) +#define MODES_LONG_MSG_SAMPLES (MODES_LONG_MSG_BITS * 2) +#define MODES_SHORT_MSG_SAMPLES (MODES_SHORT_MSG_BITS * 2) +#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_ICAO_CACHE_LEN 1024 /* Power of two required. */ #define MODES_ICAO_CACHE_TTL 60 /* Time to live of cached addresses. */ @@ -129,6 +135,8 @@ struct { unsigned char *data; /* Raw IQ samples buffer */ uint16_t *magnitude; /* Magnitude vector */ uint32_t data_len; /* Buffer length. */ + long long timestampBlk; /* Timestamp of the start of the current block */ + long long timestampMsg; /* Timestamp of the current message. */ int fd; /* --ifile option file descriptor. */ int data_ready; /* Data ready to be processed. */ uint32_t *icao_cache; /* Recently seen ICAO addresses cache. */ @@ -156,6 +164,7 @@ struct { int fix_errors; /* Single bit error correction if true. */ int check_crc; /* Only display messages with good CRC. */ int raw; /* Raw output format. */ + int beast; /* Beast binary format output. */ int debug; /* Debugging mode. */ int net; /* Enable networking. */ int net_only; /* Enable just networking. */ @@ -199,6 +208,8 @@ struct modesMessage { int errorbit; /* Bit corrected. -1 if no bit corrected. */ int aa1, aa2, aa3; /* ICAO Address bytes 1 2 and 3 */ int phase_corrected; /* True if phase correction was applied. */ + long long timestampMsg; /* Timestamp of the message. */ + unsigned char signalLevel; /* Signal Amplitude */ /* DF 11 */ int ca; /* Responder capabilities. */ @@ -236,6 +247,7 @@ struct modesMessage { void interactiveShowData(void); struct aircraft* interactiveReceiveData(struct modesMessage *mm); void modesSendRawOutput(struct modesMessage *mm); +void modesSendBeastOutput(struct modesMessage *mm); void modesSendSBSOutput(struct modesMessage *mm, struct aircraft *a); void useModesMessage(struct modesMessage *mm); int fixSingleBitErrors(unsigned char *msg, int bits); @@ -265,6 +277,7 @@ void modesInitConfig(void) { Modes.fix_errors = 1; Modes.check_crc = 1; Modes.raw = 0; + Modes.beast = 0; Modes.net = 0; Modes.net_only = 0; Modes.net_output_sbs_port = MODES_NET_OUTPUT_SBS_PORT; @@ -289,20 +302,22 @@ void modesInit(void) { * in the message detection loop, back at the start of the next data * to process. This way we are able to also detect messages crossing * two reads. */ - Modes.data_len = MODES_DATA_LEN + (MODES_FULL_LEN-1)*4; Modes.data_ready = 0; + Modes.timestampBlk = 0; + Modes.timestampMsg = 0; /* Allocate the ICAO address cache. We use two uint32_t for every * entry because it's a addr / timestamp pair for every entry. */ Modes.icao_cache = malloc(sizeof(uint32_t)*MODES_ICAO_CACHE_LEN*2); memset(Modes.icao_cache,0,sizeof(uint32_t)*MODES_ICAO_CACHE_LEN*2); Modes.aircrafts = NULL; Modes.interactive_last_update = 0; - if ((Modes.data = malloc(Modes.data_len)) == NULL || - (Modes.magnitude = malloc(Modes.data_len*2)) == NULL) { + if ((Modes.data = malloc(MODES_ASYNC_BUF_SIZE)) == NULL || + (Modes.magnitude = malloc(MODES_ASYNC_BUF_SIZE+MODES_PREAMBLE_SIZE+MODES_LONG_MSG_SIZE)) == NULL) { fprintf(stderr, "Out of memory allocating data buffer.\n"); exit(1); } - memset(Modes.data,127,Modes.data_len); + memset(Modes.data,127,MODES_ASYNC_BUF_SIZE); + memset(Modes.magnitude,0, MODES_ASYNC_BUF_SIZE+MODES_PREAMBLE_SIZE+MODES_LONG_MSG_SIZE); /* Populate the I/Q -> Magnitude lookup table. It is used because * sqrt or round may be expensive and may vary a lot depending on @@ -396,12 +411,9 @@ void rtlsdrCallback(unsigned char *buf, uint32_t len, void *ctx) { MODES_NOTUSED(ctx); pthread_mutex_lock(&Modes.data_mutex); - if (len > MODES_DATA_LEN) len = MODES_DATA_LEN; - /* Move the last part of the previous buffer, that was not processed, - * on the start of the new buffer. */ - memcpy(Modes.data, Modes.data+MODES_DATA_LEN, (MODES_FULL_LEN-1)*4); + if (len > MODES_ASYNC_BUF_SIZE) len = MODES_ASYNC_BUF_SIZE; /* Read the new data. */ - memcpy(Modes.data+(MODES_FULL_LEN-1)*4, buf, len); + memcpy(Modes.data, buf, len); Modes.data_ready = 1; /* Signal to the other thread that new data is ready */ pthread_cond_signal(&Modes.data_cond); @@ -429,11 +441,8 @@ void readDataFromFile(void) { pthread_mutex_lock(&Modes.data_mutex); } - /* Move the last part of the previous buffer, that was not processed, - * on the start of the new buffer. */ - memcpy(Modes.data, Modes.data+MODES_DATA_LEN, (MODES_FULL_LEN-1)*4); - toread = MODES_DATA_LEN; - p = Modes.data+(MODES_FULL_LEN-1)*4; + toread = MODES_ASYNC_BUF_SIZE; + p = Modes.data; while(toread) { nread = read(Modes.fd, p, toread); if (nread <= 0) { @@ -462,7 +471,7 @@ void *readerThreadEntryPoint(void *arg) { if (Modes.filename == NULL) { rtlsdr_read_async(Modes.dev, rtlsdrCallback, NULL, MODES_ASYNC_BUF_NUMBER, - MODES_DATA_LEN); + MODES_ASYNC_BUF_SIZE); } else { readDataFromFile(); } @@ -511,7 +520,7 @@ void dumpMagnitudeBar(int index, int magnitude) { void dumpMagnitudeVector(uint16_t *m, uint32_t offset) { uint32_t padding = 5; /* Show a few samples before the actual start. */ uint32_t start = (offset < padding) ? 0 : offset-padding; - uint32_t end = offset + (MODES_PREAMBLE_US*2)+(MODES_SHORT_MSG_BITS*2) - 1; + uint32_t end = offset + (MODES_PREAMBLE_SAMPLES)+(MODES_SHORT_MSG_SAMPLES) - 1; uint32_t j; for (j = start; j <= end; j++) { @@ -526,7 +535,7 @@ void dumpRawMessageJS(char *descr, unsigned char *msg, { int padding = 5; /* Show a few samples before the actual start. */ int start = offset - padding; - int end = offset + (MODES_PREAMBLE_US*2)+(MODES_LONG_MSG_BITS*2) - 1; + int end = offset + (MODES_PREAMBLE_SAMPLES)+(MODES_LONG_MSG_SAMPLES) - 1; FILE *fp; int j, fix1 = -1, fix2 = -1; @@ -665,7 +674,7 @@ int modesMessageLenByType(int type) { * of the error bit. Otherwise if fixing failed -1 is returned. */ int fixSingleBitErrors(unsigned char *msg, int bits) { int j; - unsigned char aux[MODES_LONG_MSG_BITS/8]; + unsigned char aux[MODES_LONG_MSG_BYTES]; for (j = 0; j < bits; j++) { int byte = j/8; @@ -696,7 +705,7 @@ int fixSingleBitErrors(unsigned char *msg, int bits) { * don't pass the checksum, and only in Aggressive Mode. */ int fixTwoBitsErrors(unsigned char *msg, int bits) { int j, i; - unsigned char aux[MODES_LONG_MSG_BITS/8]; + unsigned char aux[MODES_LONG_MSG_BYTES]; for (j = 0; j < bits; j++) { int byte1 = j/8; @@ -933,6 +942,8 @@ void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) { /* Work on our local copy */ memcpy(mm->msg,msg,MODES_LONG_MSG_BYTES); + mm->timestampMsg = Modes.timestampMsg; + mm->signalLevel = 0xA5; msg = mm->msg; /* Get the message type ASAP as other operations depend on this */ @@ -1228,19 +1239,21 @@ void displayModesMessage(struct modesMessage *mm) { /* Turn I/Q samples pointed by Modes.data into the magnitude vector * pointed by Modes.magnitude. */ void computeMagnitudeVector(void) { - uint16_t *m = Modes.magnitude; + uint16_t *m = &Modes.magnitude[MODES_PREAMBLE_SAMPLES+MODES_LONG_MSG_SAMPLES]; unsigned char *p = Modes.data; uint32_t j; + memcpy(Modes.magnitude,&Modes.magnitude[MODES_ASYNC_BUF_SAMPLES], MODES_PREAMBLE_SIZE+MODES_LONG_MSG_SIZE); + /* 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. */ - for (j = 0; j < Modes.data_len; j += 2) { - int i = p[j]-127; - int q = p[j+1]-127; + for (j = 0; j < MODES_ASYNC_BUF_SAMPLES; j ++) { + int i = (*p++)-127; + int q = (*p++)-127; if (i < 0) i = -i; if (q < 0) q = -q; - m[j/2] = Modes.maglut[i*129+q]; + *m++ = Modes.maglut[i*129+q]; } } @@ -1289,8 +1302,8 @@ int detectOutOfPhase(uint16_t *m) { void applyPhaseCorrection(uint16_t *m) { int j; - m += 16; /* Skip preamble. */ - for (j = 0; j < (MODES_LONG_MSG_BITS-1)*2; j += 2) { + m += MODES_PREAMBLE_SAMPLES; /* Skip preamble. */ + for (j = 0; j < MODES_LONG_MSG_SAMPLES; j += 2) { if (m[j] > m[j+1]) { /* One */ m[j+2] = (m[j+2] * 5) / 4; @@ -1306,8 +1319,8 @@ void applyPhaseCorrection(uint16_t *m) { * stream of bits and passed to the function to display it. */ void detectModeS(uint16_t *m, uint32_t mlen) { unsigned char bits[MODES_LONG_MSG_BITS]; - unsigned char msg[MODES_LONG_MSG_BITS/2]; - uint16_t aux[MODES_LONG_MSG_BITS*2]; + unsigned char msg[MODES_LONG_MSG_BYTES]; + uint16_t aux[MODES_LONG_MSG_SAMPLES]; uint32_t j; int use_correction = 0; @@ -1334,7 +1347,7 @@ void detectModeS(uint16_t *m, uint32_t mlen) { * 8 -- * 9 ------------------- */ - for (j = 0; j < mlen - MODES_FULL_LEN*2; j++) { + for (j = 0; j < mlen; j++) { int low, high, delta, i, errors; int good_message = 0; @@ -1393,12 +1406,13 @@ void detectModeS(uint16_t *m, uint32_t mlen) { continue; } Modes.stat_valid_preamble++; + Modes.timestampMsg = Modes.timestampBlk + j; good_preamble: /* If the previous attempt with this message failed, retry using * magnitude correction. */ if (use_correction) { - memcpy(aux,m+j+MODES_PREAMBLE_US*2,sizeof(aux)); + memcpy(aux,m+j+MODES_PREAMBLE_SAMPLES,sizeof(aux)); if (j && detectOutOfPhase(m+j)) { applyPhaseCorrection(m+j); Modes.stat_out_of_phase++; @@ -1409,9 +1423,9 @@ good_preamble: /* Decode all the next 112 bits, regardless of the actual message * size. We'll check the actual message type later. */ errors = 0; - for (i = 0; i < MODES_LONG_MSG_BITS*2; i += 2) { - low = m[j+i+MODES_PREAMBLE_US*2]; - high = m[j+i+MODES_PREAMBLE_US*2+1]; + for (i = 0; i < MODES_LONG_MSG_SAMPLES; i += 2) { + low = m[j+i+MODES_PREAMBLE_SAMPLES]; + high = m[j+i+MODES_PREAMBLE_SAMPLES+1]; delta = low-high; if (delta < 0) delta = -delta; @@ -1422,7 +1436,7 @@ good_preamble: * is an effective way to detect if it's just random noise * that was detected as a valid preamble. */ bits[i/2] = 2; /* error */ - if (i < MODES_SHORT_MSG_BITS*2) errors++; + if (i < MODES_SHORT_MSG_SAMPLES) errors++; } else if (low > high) { bits[i/2] = 1; } else { @@ -1433,7 +1447,7 @@ good_preamble: /* Restore the original message if we used magnitude correction. */ if (use_correction) - memcpy(m+j+MODES_PREAMBLE_US*2,aux,sizeof(aux)); + memcpy(m+j+MODES_PREAMBLE_SAMPLES,aux,sizeof(aux)); /* Pack bits into bytes */ for (i = 0; i < MODES_LONG_MSG_BITS; i += 8) { @@ -1455,8 +1469,8 @@ good_preamble: * to mark this as real message and not just noise? */ delta = 0; for (i = 0; i < msglen*8*2; i += 2) { - delta += abs(m[j+i+MODES_PREAMBLE_US*2]- - m[j+i+MODES_PREAMBLE_US*2+1]); + delta += abs(m[j+i+MODES_PREAMBLE_SAMPLES]- + m[j+i+MODES_PREAMBLE_SAMPLES+1]); } delta /= msglen*4; @@ -1556,8 +1570,11 @@ void useModesMessage(struct modesMessage *mm) { if (!Modes.raw && !Modes.onlyaddr) printf("\n"); } /* Send data to connected clients. */ - if (Modes.net) { - modesSendRawOutput(mm); /* Feed raw output clients. */ + if (Modes.net) { /* Feed raw output clients. */ + if (Modes.beast) + modesSendBeastOutput(mm); + else + modesSendRawOutput(mm); } } } @@ -1867,7 +1884,7 @@ void snipMode(int level) { while ((i = getchar()) != EOF && (q = getchar()) != EOF) { if (abs(i-127) < level && abs(q-127) < level) { c++; - if (c > MODES_PREAMBLE_US*4) continue; + if (c > MODES_PREAMBLE_SIZE) continue; } else { c = 0; } @@ -1997,6 +2014,32 @@ void modesSendAllClients(int service, void *msg, int len) { } } +/* Write raw output in Beast Binary format with MLAT Counter to TCP clients */ +void modesSendBeastOutput(struct modesMessage *mm) { + char msg[64], *p = msg; + int msgLen = mm->msgbits / 8; + char * pTimeStamp; + int j; + + *p++ = 0x1a; + if (msgLen == MODES_SHORT_MSG_BYTES) + {*p++ = '2';} + else if (msgLen == MODES_LONG_MSG_BYTES) + {*p++ = '3';} + else + {return;} + + *p++ = mm->signalLevel; + + pTimeStamp = (char *) &mm->timestampMsg; + for (j = 5; j >= 0; j--) { + *p++ = pTimeStamp[j]; + } + + memcpy(p, mm->msg, msgLen); + modesSendAllClients(Modes.ros, msg, (msgLen + 9)); +} + /* Write raw output to TCP clients. */ void modesSendRawOutput(struct modesMessage *mm) { char msg[128], *p = msg; @@ -2366,6 +2409,7 @@ void showHelp(void) { "--interactive-ttl Remove from list if idle for (default: 60).\n" "--raw Show only messages hex values.\n" "--net Enable networking.\n" +"--net-beast TCP raw output in Beast binary format.\n" "--net-only Enable just networking, no RTL device or file used.\n" "--net-ro-port TCP listening port for raw output (default: 30002).\n" "--net-ri-port TCP listening port for raw input (default: 30001).\n" @@ -2440,6 +2484,8 @@ int main(int argc, char **argv) { Modes.raw = 1; } else if (!strcmp(argv[j],"--net")) { Modes.net = 1; + } else if (!strcmp(argv[j],"--net-beast")) { + Modes.beast = 1; } else if (!strcmp(argv[j],"--net-only")) { Modes.net = 1; Modes.net_only = 1; @@ -2542,7 +2588,8 @@ int main(int argc, char **argv) { * stuff * at the same time. (This should only be useful with very * slow processors). */ pthread_mutex_unlock(&Modes.data_mutex); - detectModeS(Modes.magnitude, Modes.data_len/2); + detectModeS(Modes.magnitude, MODES_ASYNC_BUF_SAMPLES); + Modes.timestampBlk += MODES_ASYNC_BUF_SAMPLES; backgroundTasks(); pthread_mutex_lock(&Modes.data_mutex); if (Modes.exit) break;