From b9b76da02daa2ae8071b953e6edf9494f3bc53ba Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 20 Jan 2015 17:16:35 +0000 Subject: [PATCH] Stats rearrangement, based on experimental branch changes. --- Makefile | 4 +- demod_2000.c | 12 ++-- demod_2400.c | 16 ++--- dump1090.c | 200 +++++++++++++++++++-------------------------------- dump1090.h | 65 +++++------------ mode_s.c | 2 +- net_io.c | 58 ++++++++++++++- stats.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++ stats.h | 106 +++++++++++++++++++++++++++ 9 files changed, 468 insertions(+), 191 deletions(-) create mode 100644 stats.c create mode 100644 stats.h diff --git a/Makefile b/Makefile index 9265ac5..d10a09e 100644 --- a/Makefile +++ b/Makefile @@ -21,10 +21,10 @@ all: dump1090 view1090 %.o: %.c dump1090.h $(CC) $(CPPFLAGS) $(CFLAGS) $(EXTRACFLAGS) -c $< -dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o demod_2000.o demod_2400.o +dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o demod_2000.o demod_2400.o stats.o $(CC) -g -o dump1090 $^ $(LIBS) $(LIBS_RTL) $(LDFLAGS) -view1090: view1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o +view1090: view1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o crc.o stats.o $(CC) -g -o view1090 $^ $(LIBS) $(LDFLAGS) clean: diff --git a/demod_2000.c b/demod_2000.c index a526cdf..f255f5a 100644 --- a/demod_2000.c +++ b/demod_2000.c @@ -347,7 +347,7 @@ void demodulate2000(uint16_t *m, uint32_t mlen) { useModesMessage(&mm); j += MODEAC_MSG_SAMPLES; - Modes.stat_ModeAC++; + Modes.stats_current.ModeAC++; continue; } } @@ -399,7 +399,7 @@ void demodulate2000(uint16_t *m, uint32_t mlen) { dumpRawMessage("Too high level in samples between 10 and 15", msg, m, j); continue; } - Modes.stat_valid_preamble++; + Modes.stats_current.valid_preamble++; } else { @@ -408,7 +408,7 @@ void demodulate2000(uint16_t *m, uint32_t mlen) { // Make a copy of the Payload, and phase correct the copy memcpy(aux, &pPreamble[-1], sizeof(aux)); applyPhaseCorrection(&aux[1]); - Modes.stat_out_of_phase++; + Modes.stats_current.out_of_phase++; pPayload = &aux[1 + MODES_PREAMBLE_SAMPLES]; // TODO ... apply other kind of corrections } @@ -476,7 +476,7 @@ void demodulate2000(uint16_t *m, uint32_t mlen) { 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++; + Modes.stats_current.DF_Len_Corrected++; } else if (i < MODES_LONG_MSG_BITS) { msglen = MODES_SHORT_MSG_BITS; @@ -518,7 +518,7 @@ void demodulate2000(uint16_t *m, uint32_t mlen) { if (validDFbits & thisDFbit) { // Yep, more likely, so update the main message msg[0] = theByte; - Modes.stat_DF_Type_Corrected++; + Modes.stats_current.DF_Type_Corrected++; errors--; // decrease the error count so we attempt to use the modified DF. } } @@ -549,7 +549,7 @@ void demodulate2000(uint16_t *m, uint32_t mlen) { // Update statistics if (Modes.stats) { - struct demod_stats *dstats = (use_correction ? &Modes.stat_demod_phasecorrected : &Modes.stat_demod); + struct demod_stats *dstats = (use_correction ? &Modes.stats_current.demod_phasecorrected : &Modes.stats_current.demod); switch (errors) { case 0: dstats->demodulated0++; break; diff --git a/demod_2400.c b/demod_2400.c index d7464ea..218294e 100644 --- a/demod_2400.c +++ b/demod_2400.c @@ -252,19 +252,19 @@ void demodulate2400(uint16_t *m, uint32_t mlen) preamble[16] >= high || preamble[17] >= high || preamble[18] >= high) { - ++Modes.stat_preamble_not_quiet; + ++Modes.stats_current.preamble_not_quiet; continue; } // Crosscorrelate against the first few bits to find a likely phase offset initial_phase = best_phase(&preamble[19]); if (initial_phase < 0) { - ++Modes.stat_preamble_no_correlation; + ++Modes.stats_current.preamble_no_correlation; continue; // nothing satisfactory } - Modes.stat_valid_preamble++; - Modes.stat_preamble_phase[initial_phase%MODES_MAX_PHASE_STATS]++; + Modes.stats_current.valid_preamble++; + Modes.stats_current.preamble_phase[initial_phase%MODES_MAX_PHASE_STATS]++; try_phase = initial_phase; @@ -395,7 +395,7 @@ void demodulate2400(uint16_t *m, uint32_t mlen) 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++; + Modes.stats_current.DF_Len_Corrected++; } else if (i < MODES_LONG_MSG_BITS) { msglen = MODES_SHORT_MSG_BITS; errors = errors56; @@ -435,7 +435,7 @@ void demodulate2400(uint16_t *m, uint32_t mlen) if (validDFbits & thisDFbit) { // Yep, more likely, so update the main message msg[0] = theByte; - Modes.stat_DF_Type_Corrected++; + Modes.stats_current.DF_Type_Corrected++; errors--; // decrease the error count so we attempt to use the modified DF. } } @@ -466,7 +466,7 @@ void demodulate2400(uint16_t *m, uint32_t mlen) // Update statistics if (Modes.stats) { - struct demod_stats *dstats = (mm.phase_corrected ? &Modes.stat_demod_phasecorrected : &Modes.stat_demod); + struct demod_stats *dstats = (mm.phase_corrected ? &Modes.stats_current.demod_phasecorrected : &Modes.stats_current.demod); switch (errors) { case 0: dstats->demodulated0++; break; @@ -508,7 +508,7 @@ void demodulate2400(uint16_t *m, uint32_t mlen) // peak detection if (Modes.phase_enhance && !mm.crcok && !mm.correctedbits) { if (try_phase == initial_phase) - ++Modes.stat_out_of_phase; + ++Modes.stats_current.out_of_phase; try_phase++; if (try_phase == 9) try_phase = 4; diff --git a/dump1090.c b/dump1090.c index d58a521..43ecc99 100644 --- a/dump1090.c +++ b/dump1090.c @@ -586,111 +586,13 @@ void showCopyright(void) { } #endif - -static void display_demod_stats(const char *prefix, struct demod_stats *dstats) { - int j; - - printf("%d %sdemodulated with 0 errors\n", dstats->demodulated0, prefix); - printf("%d %sdemodulated with 1 error\n", dstats->demodulated1, prefix); - printf("%d %sdemodulated with 2 errors\n", dstats->demodulated2, prefix); - printf("%d %sdemodulated with > 2 errors\n", dstats->demodulated3, prefix); - printf("%d %swith good crc\n", dstats->goodcrc, prefix); - for (j = 0; j < MODES_MAX_PHASE_STATS; ++j) - if (dstats->goodcrc_byphase[j] > 0) - printf(" %d %swith phase offset %d\n", dstats->goodcrc_byphase[j], prefix, j); - printf("%d %swith bad crc\n", dstats->badcrc, prefix); - printf("%d %serrors corrected\n", dstats->fixed, prefix); - - for (j = 0; j < MODES_MAX_BITERRORS; j++) { - printf(" %d %swith %d bit %s\n", dstats->bit_fix[j], prefix, j+1, (j==0)?"error":"errors"); - } +static void display_total_stats(void) +{ + struct stats added; + add_stats(&Modes.stats_alltime, &Modes.stats_current, &added); + display_stats(&added); } -static void reset_demod_stats(struct demod_stats *dstats) { - int j; - - dstats->demodulated0 = - dstats->demodulated1 = - dstats->demodulated2 = - dstats->goodcrc = - dstats->badcrc = - dstats->fixed = 0; - - for (j = 0; j < MODES_MAX_BITERRORS; j++) { - dstats->bit_fix[j] = 0; - } - - for (j = 0; j < MODES_MAX_PHASE_STATS; j++) { - dstats->goodcrc_byphase[j] = 0; - } -} - -static void display_stats(void) { - int j; - time_t now = time(NULL); - - printf("\n\n"); - if (Modes.interactive) - interactiveShowData(); - - printf("Statistics as at %s", ctime(&now)); - - printf("%d sample blocks processed\n", Modes.stat_blocks_processed); - printf("%d sample blocks dropped\n", Modes.stat_blocks_dropped); - - if (Modes.stat_blocks_processed > 0) { - long cpu_millis = (long)Modes.stat_cputime.tv_sec*1000L + Modes.stat_cputime.tv_nsec/1000000L; - long sample_millis = (long) ((uint64_t)Modes.stat_blocks_processed * MODES_ASYNC_BUF_SAMPLES / (Modes.oversample ? 2400 : 2000)); - printf("%ld ms CPU time used to process %ld ms samples, %.1f%% load\n", - cpu_millis, sample_millis, 100.0 * cpu_millis / sample_millis); - } - - printf("%d ModeA/C detected\n", Modes.stat_ModeAC); - printf("%d Mode-S preambles with poor correlation\n", Modes.stat_preamble_no_correlation); - printf("%d Mode-S preambles with noise in the quiet period\n", Modes.stat_preamble_not_quiet); - printf("%d valid Mode-S preambles\n", Modes.stat_valid_preamble); - for (j = 0; j < MODES_MAX_PHASE_STATS; ++j) - if (Modes.stat_preamble_phase[j] > 0) - printf(" %d with phase offset %d\n", Modes.stat_preamble_phase[j], j); - printf("%d DF-?? fields corrected for length\n", Modes.stat_DF_Len_Corrected); - printf("%d DF-?? fields corrected for type\n", Modes.stat_DF_Type_Corrected); - - display_demod_stats("", &Modes.stat_demod); - if (Modes.phase_enhance) { - printf("%d phase enhancement attempts\n", Modes.stat_out_of_phase); - display_demod_stats("phase enhanced ", &Modes.stat_demod_phasecorrected); - } - - printf("%d total usable messages\n", - Modes.stat_messages_total); - - fflush(stdout); - - Modes.stat_cputime.tv_sec = 0; - Modes.stat_cputime.tv_nsec = 0; - - Modes.stat_blocks_processed = - Modes.stat_blocks_dropped = 0; - - Modes.stat_ModeAC = - Modes.stat_preamble_no_correlation = - Modes.stat_preamble_not_quiet = - Modes.stat_valid_preamble = - Modes.stat_DF_Len_Corrected = - Modes.stat_DF_Type_Corrected = - Modes.stat_out_of_phase = 0; - - Modes.stat_messages_total = 0; - - for (j = 0; j < MODES_MAX_PHASE_STATS; j++) { - Modes.stat_preamble_phase[j] = 0; - } - - reset_demod_stats(&Modes.stat_demod); - reset_demod_stats(&Modes.stat_demod_phasecorrected); -} - - // //========================================================================= // @@ -699,7 +601,8 @@ static void display_stats(void) { // from the net, refreshing the screen in interactive mode, and so forth // void backgroundTasks(void) { - static time_t next_stats; + static time_t next_stats_display; + static time_t next_stats_update; static time_t next_json, next_history; time_t now = time(NULL); @@ -718,29 +621,68 @@ void backgroundTasks(void) { interactiveShowData(); } - if (Modes.stats > 0) { - if (now > next_stats) { - if (next_stats != 0) - display_stats(); - next_stats = now + Modes.stats; + // always update end time so it is current when requests arrive + Modes.stats_current.end = now; + + if (now >= next_stats_update) { + int i; + + if (next_stats_update == 0) { + next_stats_update = now + 60; + } else { + Modes.stats_latest_1min = (Modes.stats_latest_1min + 1) % 15; + Modes.stats_1min[Modes.stats_latest_1min] = Modes.stats_current; + + add_stats(&Modes.stats_current, &Modes.stats_alltime, &Modes.stats_alltime); + add_stats(&Modes.stats_current, &Modes.stats_periodic, &Modes.stats_periodic); + + reset_stats(&Modes.stats_5min); + for (i = 0; i < 5; ++i) + add_stats(&Modes.stats_1min[(Modes.stats_latest_1min - i + 15) % 15], &Modes.stats_5min, &Modes.stats_5min); + + reset_stats(&Modes.stats_15min); + for (i = 0; i < 15; ++i) + add_stats(&Modes.stats_1min[i], &Modes.stats_15min, &Modes.stats_15min); + + reset_stats(&Modes.stats_current); + Modes.stats_current.start = Modes.stats_current.end = now; + + if (Modes.json_dir) + writeJsonToFile("stats.json", generateStatsJson); + + next_stats_update += 60; } } - if ((Modes.json_dir || Modes.net_http_port) && now >= next_json) { + if (Modes.stats > 0 && now >= next_stats_display) { + if (next_stats_display == 0) { + next_stats_display = now + Modes.stats; + } else { + add_stats(&Modes.stats_periodic, &Modes.stats_current, &Modes.stats_periodic); + display_stats(&Modes.stats_periodic); + reset_stats(&Modes.stats_periodic); + + next_stats_display += Modes.stats; + } + } + + if (Modes.json_dir && now >= next_json) { writeJsonToFile("aircraft.json", generateAircraftJson); next_json = now + Modes.json_interval; } if ((Modes.json_dir || Modes.net_http_port) && now >= next_history) { - char filebuf[PATH_MAX]; int rewrite_receiver_json = (Modes.json_aircraft_history[HISTORY_SIZE-1].content == NULL); free(Modes.json_aircraft_history[Modes.json_aircraft_history_next].content); // might be NULL, that's OK. Modes.json_aircraft_history[Modes.json_aircraft_history_next].content = generateAircraftJson("/data/aircraft.json", &Modes.json_aircraft_history[Modes.json_aircraft_history_next].clen); - snprintf(filebuf, PATH_MAX, "history_%d.json", Modes.json_aircraft_history_next); - writeJsonToFile(filebuf, generateHistoryJson); + if (Modes.json_dir) { + char filebuf[PATH_MAX]; + snprintf(filebuf, PATH_MAX, "history_%d.json", Modes.json_aircraft_history_next); + writeJsonToFile(filebuf, generateHistoryJson); + } Modes.json_aircraft_history_next = (Modes.json_aircraft_history_next+1) % HISTORY_SIZE; @@ -1002,13 +944,19 @@ int main(int argc, char **argv) { } if (Modes.net) modesInitNet(); - writeJsonToFile("receiver.json", generateReceiverJson); // once on startup + // init stats: + Modes.stats_current.start = Modes.stats_current.end = time(NULL); + + // write initial json files so they're not missing + writeJsonToFile("receiver.json", generateReceiverJson); + writeJsonToFile("stats.json", generateStatsJson); + writeJsonToFile("aircraft.json", generateAircraftJson); // If the user specifies --net-only, just run in order to serve network // clients without reading data from the RTL device while (Modes.net_only) { if (Modes.exit) { - display_stats(); + display_total_stats(); exit(0); // If we exit net_only nothing further in main() } backgroundTasks(); @@ -1044,7 +992,7 @@ int main(int argc, char **argv) { // If we lost some blocks, correct the timestamp if (Modes.iDataLost) { Modes.timestampBlk += (MODES_ASYNC_BUF_SAMPLES * 6 * Modes.iDataLost); - Modes.stat_blocks_dropped += Modes.iDataLost; + Modes.stats_current.blocks_dropped += Modes.iDataLost; Modes.iDataLost = 0; } @@ -1064,14 +1012,14 @@ int main(int argc, char **argv) { demodulate2000(Modes.magnitude, MODES_ASYNC_BUF_SAMPLES); clock_gettime(CLOCK_THREAD_CPUTIME_ID, &cpu_end_time); - Modes.stat_cputime.tv_sec += (cpu_end_time.tv_sec - cpu_start_time.tv_sec); - Modes.stat_cputime.tv_nsec += (cpu_end_time.tv_nsec - cpu_start_time.tv_nsec); - if (Modes.stat_cputime.tv_nsec < 0) { - Modes.stat_cputime.tv_nsec += 1000000000L; - Modes.stat_cputime.tv_sec--; - } else if (Modes.stat_cputime.tv_nsec > 1000000000L) { - Modes.stat_cputime.tv_nsec -= 1000000000L; - Modes.stat_cputime.tv_sec++; + Modes.stats_current.cputime.tv_sec += (cpu_end_time.tv_sec - cpu_start_time.tv_sec); + Modes.stats_current.cputime.tv_nsec += (cpu_end_time.tv_nsec - cpu_start_time.tv_nsec); + if (Modes.stats_current.cputime.tv_nsec < 0) { + Modes.stats_current.cputime.tv_nsec += 1000000000L; + Modes.stats_current.cputime.tv_sec--; + } else if (Modes.stats_current.cputime.tv_nsec > 1000000000L) { + Modes.stats_current.cputime.tv_nsec -= 1000000000L; + Modes.stats_current.cputime.tv_sec++; } // Update the timestamp ready for the next block @@ -1079,7 +1027,7 @@ int main(int argc, char **argv) { Modes.timestampBlk += (MODES_ASYNC_BUF_SAMPLES*5); else Modes.timestampBlk += (MODES_ASYNC_BUF_SAMPLES*6); - Modes.stat_blocks_processed++; + Modes.stats_current.blocks_processed++; } else { pthread_cond_signal (&Modes.data_cond); pthread_mutex_unlock(&Modes.data_mutex); @@ -1093,7 +1041,7 @@ int main(int argc, char **argv) { // If --stats were given, print statistics if (Modes.stats) { - display_stats(); + display_total_stats(); } if (Modes.filename == NULL) { diff --git a/dump1090.h b/dump1090.h index 97296a4..2cf8819 100644 --- a/dump1090.h +++ b/dump1090.h @@ -89,11 +89,7 @@ #include "winstubs.h" //Put everything Windows specific in here #endif -#include "rtl-sdr.h" -#include "anet.h" -#include "crc.h" -#include "demod_2000.h" -#include "demod_2400.h" +#include // ============================= #defines =============================== // @@ -223,8 +219,18 @@ #define HTMLPATH "./public_html" // default path for gmap.html etc #endif +#define HISTORY_SIZE 120 +#define HISTORY_INTERVAL 30 + #define MODES_NOTUSED(V) ((void) V) +#include "anet.h" +#include "crc.h" +#include "demod_2000.h" +#include "demod_2400.h" +#include "stats.h" + + //======================== structure declarations ========================= // Structure used to describe a networking client @@ -236,9 +242,6 @@ struct client { char buf[MODES_CLIENT_BUF_SIZE+1]; // Read buffer }; -#define HISTORY_SIZE 120 -#define HISTORY_INTERVAL 30 - // Structure used to describe an aircraft in iteractive mode struct aircraft { uint32_t addr; // ICAO address @@ -275,22 +278,6 @@ struct aircraft { struct aircraft *next; // Next aircraft in our linked list }; -// Common stats for non-phase-corrected vs phase-corrected cases -struct demod_stats { - unsigned int demodulated0; - unsigned int demodulated1; - unsigned int demodulated2; - unsigned int demodulated3; - unsigned int goodcrc; - unsigned int goodcrc_byphase[MODES_MAX_PHASE_STATS]; - unsigned int badcrc; - unsigned int fixed; - - // Histogram of fixed bit errors: index 0 for single bit erros, - // index 1 for double bit errors etc. - unsigned int bit_fix[MODES_MAX_BITERRORS]; -}; - // Common writer state for all output sockets of one type struct net_writer { int socket; // listening socket FD, used to identify the owning service @@ -405,28 +392,13 @@ struct { // Internal state time_t last_cleanup_time; // Last cleanup time in seconds // Statistics - unsigned int stat_preamble_no_correlation; - unsigned int stat_preamble_not_quiet; - unsigned int stat_valid_preamble; - unsigned int stat_preamble_phase[MODES_MAX_PHASE_STATS]; - - struct demod_stats stat_demod; - struct demod_stats stat_demod_phasecorrected; - - unsigned int stat_http_requests; - unsigned int stat_out_of_phase; - - unsigned int stat_DF_Len_Corrected; - unsigned int stat_DF_Type_Corrected; - unsigned int stat_ModeAC; - - unsigned int stat_blocks_processed; - unsigned int stat_blocks_dropped; - - struct timespec stat_cputime; - - // total messages: - unsigned int stat_messages_total; + struct stats stats_current; + struct stats stats_alltime; + struct stats stats_periodic; + struct stats stats_1min[15]; + int stats_latest_1min; + struct stats stats_5min; + struct stats stats_15min; } Modes; // The struct we use to store information about a decoded message. @@ -518,6 +490,7 @@ void modesNetPeriodicWork (void); void writeJsonToFile(const char *file, char * (*generator) (const char*,int*)); char *generateAircraftJson(const char *url_path, int *len); char *generateReceiverJson(const char *url_path, int *len); +char *generateStatsJson(const char *url_path, int *len); char *generateHistoryJson(const char *url_path, int *len); #ifdef __cplusplus diff --git a/mode_s.c b/mode_s.c index b204fc5..4543bfa 100644 --- a/mode_s.c +++ b/mode_s.c @@ -935,7 +935,7 @@ void computeMagnitudeVector(uint16_t *p) { // void useModesMessage(struct modesMessage *mm) { if ((Modes.check_crc == 0) || (mm->crcok) || (mm->correctedbits)) { // not checking, ok or fixed - ++Modes.stat_messages_total; + ++Modes.stats_current.messages_total; // If we are decoding, track aircraft interactiveReceiveData(mm); diff --git a/net_io.c b/net_io.c index 48479ab..390f7da 100644 --- a/net_io.c +++ b/net_io.c @@ -48,6 +48,9 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "dump1090.h" + +#include + // // ============================= Networking ============================= // @@ -699,7 +702,7 @@ char *generateAircraftJson(const char *url_path, int *len) { "{ \"now\" : %d,\n" " \"messages\" : %u,\n" " \"aircraft\" : [", - (int)now, Modes.stat_messages_total); + (int)now, Modes.stats_current.messages_total); while(a) { if (a->modeACflags & MODEAC_MSG_FLAG) { // skip any fudged ICAO records Mode A/C @@ -750,6 +753,57 @@ char *generateAircraftJson(const char *url_path, int *len) { return buf; } +static char * appendStatsJson(char *p, + char *end, + struct stats *st, + const char *key) +{ + p += snprintf(p, end-p, + " \"%s\" : {\n" + " \"start\" : %d,\n" + " \"end\" : %d,\n" + " \"messages\" : %u,\n", + key, + (int)st->start, + (int)st->end, + st->messages_total); + + p += snprintf(p, end-p, "\n }"); + return p; +} + +char *generateStatsJson(const char *url_path, int *len) { + struct stats add; + char *buf = (char *) malloc(2048), *p = buf, *end = buf + 2048; + + MODES_NOTUSED(url_path); + + p += snprintf(p, end-p, "{\n"); + p = appendStatsJson(p, end, &Modes.stats_current, "latest"); + p += snprintf(p, end-p, ",\n"); + + add_stats(&Modes.stats_1min[Modes.stats_latest_1min], &Modes.stats_current, &add); + p = appendStatsJson(p, end, &add, "last1min"); + p += snprintf(p, end-p, ",\n"); + + add_stats(&Modes.stats_5min, &Modes.stats_current, &add); + p = appendStatsJson(p, end, &add, "last5min"); + p += snprintf(p, end-p, ",\n"); + + add_stats(&Modes.stats_15min, &Modes.stats_current, &add); + p = appendStatsJson(p, end, &add, "last15min"); + p += snprintf(p, end-p, ",\n"); + + add_stats(&Modes.stats_alltime, &Modes.stats_current, &add); + p = appendStatsJson(p, end, &add, "total"); + p += snprintf(p, end-p, "\n}\n"); + + assert(p <= end); + + *len = p-buf; + return buf; +} + // // Return a description of the receiver in json. // @@ -1046,7 +1100,7 @@ int handleHTTPRequest(struct client *c, char *p) { return 1; } free(content); - Modes.stat_http_requests++; + Modes.stats_current.http_requests++; return !keepalive; } // diff --git a/stats.c b/stats.c new file mode 100644 index 0000000..1cf0c6a --- /dev/null +++ b/stats.c @@ -0,0 +1,196 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// stats.c: statistics helpers. +// +// Copyright (c) 2015 Oliver Jowett +// +// This file is free software: you may copy, redistribute and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 2 of the License, or (at your +// option) any later version. +// +// 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// This file incorporates work covered by the following copyright and +// permission notice: +// +// Copyright (C) 2012 by Salvatore Sanfilippo +// +// 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" + +static void display_demod_stats(const char *prefix, struct demod_stats *dstats) { + int j; + + printf("%d %sdemodulated with 0 errors\n", dstats->demodulated0, prefix); + printf("%d %sdemodulated with 1 error\n", dstats->demodulated1, prefix); + printf("%d %sdemodulated with 2 errors\n", dstats->demodulated2, prefix); + printf("%d %sdemodulated with > 2 errors\n", dstats->demodulated3, prefix); + printf("%d %swith good crc\n", dstats->goodcrc, prefix); + for (j = 0; j < MODES_MAX_PHASE_STATS; ++j) + if (dstats->goodcrc_byphase[j] > 0) + printf(" %d %swith phase offset %d\n", dstats->goodcrc_byphase[j], prefix, j); + printf("%d %swith bad crc\n", dstats->badcrc, prefix); + printf("%d %serrors corrected\n", dstats->fixed, prefix); + + for (j = 0; j < Modes.nfix_crc; j++) { + printf(" %d %swith %d bit %s\n", dstats->bit_fix[j], prefix, j+1, (j==0)?"error":"errors"); + } +} + +void display_stats(struct stats *st) { + int j; + struct tm tm_start, tm_end; + char tb_start[30], tb_end[30]; + + printf("\n\n"); + if (Modes.interactive) + interactiveShowData(); + + localtime_r(&st->start, &tm_start); + strftime(tb_start, sizeof(tb_start), "%c", &tm_start); + localtime_r(&st->end, &tm_end); + strftime(tb_end, sizeof(tb_end), "%c", &tm_end); + + printf("Statistics: %s - %s\n", tb_start, tb_end); + + if (!Modes.net_only) { + printf("%d sample blocks processed\n", st->blocks_processed); + printf("%d sample blocks dropped\n", st->blocks_dropped); + + if (st->blocks_processed > 0) { + long cpu_millis = (long)st->cputime.tv_sec*1000L + st->cputime.tv_nsec/1000000L; + long sample_millis = (long) ((uint64_t)st->blocks_processed * MODES_ASYNC_BUF_SAMPLES / (Modes.oversample ? 2400 : 2000)); + printf("%ld ms CPU time used to process %ld ms samples, %.1f%% load\n", + cpu_millis, sample_millis, 100.0 * cpu_millis / sample_millis); + } + + printf("%d ModeA/C detected\n", st->ModeAC); + printf("%d Mode-S preambles with poor correlation\n", st->preamble_no_correlation); + printf("%d Mode-S preambles with noise in the quiet period\n", st->preamble_not_quiet); + printf("%d valid Mode-S preambles\n", st->valid_preamble); + for (j = 0; j < MODES_MAX_PHASE_STATS; ++j) + if (st->preamble_phase[j] > 0) + printf(" %d with phase offset %d\n", st->preamble_phase[j], j); + printf("%d DF-?? fields corrected for length\n", st->DF_Len_Corrected); + printf("%d DF-?? fields corrected for type\n", st->DF_Type_Corrected); + + display_demod_stats("", &st->demod); + if (Modes.phase_enhance) { + printf("%d phase enhancement attempts\n", st->out_of_phase); + display_demod_stats("phase enhanced ", &st->demod_phasecorrected); + } + } + + printf("%d remote messages accepted\n" + "%d remote messages rejected\n", + st->remote_accepted, + st->remote_rejected); + + printf("%d total usable messages\n", + st->messages_total); + + fflush(stdout); +} + +void reset_stats(struct stats *st) { + static struct stats st_zero; + *st = st_zero; +} + +static void add_demod_stats(const struct demod_stats *st1, const struct demod_stats *st2, struct demod_stats *target) +{ + int i; + + target->demodulated0 = st1->demodulated0 + st2->demodulated0; + target->demodulated1 = st1->demodulated1 + st2->demodulated1; + target->demodulated2 = st1->demodulated2 + st2->demodulated2; + target->demodulated3 = st1->demodulated3 + st2->demodulated3; + target->goodcrc = st1->goodcrc + st2->goodcrc; + + for (i = 0; i < MODES_MAX_PHASE_STATS; ++i) + target->goodcrc_byphase[i] = st1->goodcrc_byphase[i] + st2->goodcrc_byphase[i]; + + target->badcrc = st1->badcrc + st2->badcrc; + target->fixed = st1->fixed + st2->fixed; + + for (i = 0; i < MODES_MAX_BITERRORS; ++i) + target->bit_fix[i] = st1->bit_fix[i] + st2->bit_fix[i]; +} + +void add_stats(const struct stats *st1, const struct stats *st2, struct stats *target) { + int i; + + if (st1->start == 0) + target->start = st2->start; + else if (st2->start == 0) + target->start = st1->start; + else if (st1->start < st2->start) + target->start = st1->start; + else + target->start = st2->start; + + target->end = st1->end > st2->end ? st1->end : st2->end; + + target->preamble_no_correlation = st1->preamble_no_correlation + st2->preamble_no_correlation; + target->preamble_not_quiet = st1->preamble_not_quiet + st2->preamble_not_quiet; + target->valid_preamble = st1->valid_preamble + st2->valid_preamble; + for (i = 0; i < MODES_MAX_PHASE_STATS; ++i) + target->preamble_phase[i] = st1->preamble_phase[i] + st2->preamble_phase[i]; + + add_demod_stats(&st1->demod, &st2->demod, &target->demod); + add_demod_stats(&st1->demod_phasecorrected, &st2->demod_phasecorrected, &target->demod_phasecorrected); + + target->http_requests = st1->http_requests + st2->http_requests; + target->out_of_phase = st1->out_of_phase + st2->out_of_phase; + + target->DF_Len_Corrected = st1->DF_Len_Corrected + st2->DF_Len_Corrected; + target->DF_Type_Corrected = st1->DF_Type_Corrected + st2->DF_Type_Corrected; + target->ModeAC = st1->ModeAC + st2->ModeAC; + + target->blocks_processed = st1->blocks_processed + st2->blocks_processed; + target->blocks_dropped = st1->blocks_dropped + st2->blocks_dropped; + + target->cputime.tv_sec = st1->cputime.tv_sec + st2->cputime.tv_sec; + target->cputime.tv_nsec = st1->cputime.tv_nsec + st2->cputime.tv_nsec; + target->cputime.tv_sec += target->cputime.tv_nsec / 1000000000L; + target->cputime.tv_nsec %= 1000000000L; + + // remote messages: + target->remote_accepted = st1->remote_accepted + st2->remote_accepted; + target->remote_rejected = st1->remote_rejected + st2->remote_rejected; + + // total messages: + target->messages_total = st1->messages_total + st2->messages_total; +} + diff --git a/stats.h b/stats.h new file mode 100644 index 0000000..7be07ab --- /dev/null +++ b/stats.h @@ -0,0 +1,106 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// stats.c: statistics structures and prototypes. +// +// Copyright (c) 2015 Oliver Jowett +// +// This file is free software: you may copy, redistribute and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 2 of the License, or (at your +// option) any later version. +// +// 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// This file incorporates work covered by the following copyright and +// permission notice: +// +// Copyright (C) 2012 by Salvatore Sanfilippo +// +// 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. + +#ifndef DUMP1090_STATS_H +#define DUMP1090_STATS_H + +// Common stats for non-phase-corrected vs phase-corrected cases +struct demod_stats { + unsigned int demodulated0; + unsigned int demodulated1; + unsigned int demodulated2; + unsigned int demodulated3; + unsigned int goodcrc; + unsigned int goodcrc_byphase[MODES_MAX_PHASE_STATS]; + unsigned int badcrc; + unsigned int fixed; + + // Histogram of fixed bit errors: index 0 for single bit erros, + // index 1 for double bit errors etc. + unsigned int bit_fix[MODES_MAX_BITERRORS]; +}; + +struct stats { + time_t start; + time_t end; + + // Statistics + unsigned int preamble_no_correlation; + unsigned int preamble_not_quiet; + unsigned int valid_preamble; + unsigned int preamble_phase[MODES_MAX_PHASE_STATS]; + + struct demod_stats demod; + struct demod_stats demod_phasecorrected; + + unsigned int http_requests; + unsigned int out_of_phase; + + unsigned int DF_Len_Corrected; + unsigned int DF_Type_Corrected; + unsigned int ModeAC; + + unsigned int blocks_processed; + unsigned int blocks_dropped; + + struct timespec cputime; + + // remote messages: + unsigned int remote_accepted; + unsigned int remote_rejected; + + // total messages: + unsigned int messages_total; +}; + +void add_stats(const struct stats *st1, const struct stats *st2, struct stats *target); +void display_stats(struct stats *st); +void reset_stats(struct stats *st); + +#endif