From 2e45a59986622fa952a06640ff8123494edd6de0 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Mon, 15 Sep 2014 02:49:11 +0100 Subject: [PATCH 001/610] Make signalLevel represent the SNR in dB, rather than a somewhat arbitrary amplitude value. Include SNR values when dumping message contents. --- dump1090.h | 2 +- interactive.c | 4 ++-- mode_ac.c | 7 ++++--- mode_s.c | 47 ++++++++++++++++++++++++++--------------------- 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/dump1090.h b/dump1090.h index 77584bb60..fe607d817 100644 --- a/dump1090.h +++ b/dump1090.h @@ -88,7 +88,7 @@ #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_MSG_SQUELCH_LEVEL 0x02FF // Average signal strength limit +#define MODES_MSG_SQUELCH_FACTOR 16 // Min SNR expressed as an amplitude ratio, scaled by 10. 20log(16/10) = 4.1dB #define MODES_MSG_ENCODER_ERRS 3 // Maximum number of encoding errors // When changing, change also fixBitErrors() and modesInitErrorTable() !! diff --git a/interactive.c b/interactive.c index 6d08fc8b2..579144c2e 100644 --- a/interactive.c +++ b/interactive.c @@ -513,9 +513,9 @@ void interactiveShowData(void) { snprintf(strFl, 6, "%5d", altitude); } - printf("%06X %-4s %-4s %-8s %5s %3s %3s %7s %8s %3d %5d %2d\n", + printf("%06X %-4s %-4s %-8s %5s %3s %3s %7s %8s %2d.%1d %5d %2d\n", a->addr, strMode, strSquawk, a->flight, strFl, strGs, strTt, - strLat, strLon, signalAverage, msgs, (int)(now - a->seen)); + strLat, strLon, signalAverage/5, 2*(signalAverage%5), msgs, (int)(now - a->seen)); } count++; } diff --git a/mode_ac.c b/mode_ac.c index 881a428c2..d3d8d47b1 100644 --- a/mode_ac.c +++ b/mode_ac.c @@ -144,6 +144,7 @@ int detectModeA(uint16_t *m, struct modesMessage *mm) int F1_sig, F1_noise; int F2_sig, F2_noise; int fSig, fNoise, fLevel, fLoLo; + float snr; // m[0] contains the energy from 0 -> 499 nS // m[1] contains the energy from 500 -> 999 nS @@ -306,8 +307,8 @@ int detectModeA(uint16_t *m, struct modesMessage *mm) if ((ModeABits < 3) || (ModeABits & 0xFFFF8808) || (ModeAErrs) ) {return (ModeABits = 0);} - fSig = (fSig + 0x7F) >> 8; - mm->signalLevel = ((fSig < 255) ? fSig : 255); + snr = 5 * 20.0 * log10f((float)(fSig + fNoise + 365) / (fNoise + 365)); // 365 to adjust for magnitude value offset + mm->signalLevel = ((snr < 255) ? (uint8_t)round(snr) : 255); return ModeABits; } @@ -383,4 +384,4 @@ void decodeModeAMessage(struct modesMessage *mm, int ModeA) } // // ===================== Mode A/C detection and decoding =================== -// \ No newline at end of file +// diff --git a/mode_s.c b/mode_s.c index b9c843900..e73000326 100644 --- a/mode_s.c +++ b/mode_s.c @@ -1176,6 +1176,8 @@ void displayModesMessage(struct modesMessage *mm) { if (mm->correctedbits != 0) printf("No. of bit errors fixed: %d\n", mm->correctedbits); + printf("SNR: %d.%d dB\n", mm->signalLevel/5, 2*(mm->signalLevel%5)); + if (mm->msgtype == 0) { // DF 0 printf("DF 0: Short Air-Air Surveillance.\n"); printf(" VS : %s\n", (mm->msg[0] & 0x04) ? "Ground" : "Airborne"); @@ -1571,7 +1573,8 @@ void detectModeS(uint16_t *m, uint32_t mlen) { int high, i, errors, errors56, errorsTy; uint16_t *pPreamble, *pPayload, *pPtr; uint8_t theByte, theErrs; - int msglen, scanlen, sigStrength; + int msglen, scanlen; + uint32_t sigLevel, noiseLevel; pPreamble = &m[j]; pPayload = &m[j+MODES_PREAMBLE_SAMPLES]; @@ -1677,10 +1680,8 @@ void detectModeS(uint16_t *m, uint32_t mlen) { // We should have 4 'bits' of 0/1 and 1/0 samples in the preamble, // so include these in the signal strength - sigStrength = (pPreamble[0]-pPreamble[1]) - + (pPreamble[2]-pPreamble[3]) - + (pPreamble[7]-pPreamble[6]) - + (pPreamble[9]-pPreamble[8]); + sigLevel = pPreamble[0] + pPreamble[2] + pPreamble[7] + pPreamble[9]; + noiseLevel = pPreamble[1] + pPreamble[3] + pPreamble[4] + pPreamble[6] + pPreamble[8]; msglen = scanlen = MODES_LONG_MSG_BITS; for (i = 0; i < scanlen; i++) { @@ -1688,17 +1689,20 @@ void detectModeS(uint16_t *m, uint32_t mlen) { uint32_t b = *pPtr++; if (a > b) - {theByte |= 1; if (i < 56) {sigStrength += (a-b);}} + {theByte |= 1; if (i < 56) { sigLevel += a; noiseLevel += b; }} else if (a < b) - {/*theByte |= 0;*/ if (i < 56) {sigStrength += (b-a);}} - else if (i >= MODES_SHORT_MSG_BITS) //(a == b), and we're in the long part of a frame - {errors++; /*theByte |= 0;*/} - else if (i >= 5) //(a == b), and we're in the short part of a frame - {scanlen = MODES_LONG_MSG_BITS; errors56 = ++errors;/*theByte |= 0;*/} - else if (i) //(a == b), and we're in the message type part of a frame - {errorsTy = errors56 = ++errors; theErrs |= 1; /*theByte |= 0;*/} - else //(a == b), and we're in the first bit of the message type part of a frame - {errorsTy = errors56 = ++errors; theErrs |= 1; theByte |= 1;} + {/*theByte |= 0;*/ if (i < 56) { sigLevel += b; noiseLevel += a; }} + else { + sigLevel += a; noiseLevel += a; + if (i >= MODES_SHORT_MSG_BITS) //(a == b), and we're in the long part of a frame + {errors++; /*theByte |= 0;*/} + else if (i >= 5) //(a == b), and we're in the short part of a frame + {scanlen = MODES_LONG_MSG_BITS; errors56 = ++errors;/*theByte |= 0;*/} + else if (i) //(a == b), and we're in the message type part of a frame + {errorsTy = errors56 = ++errors; theErrs |= 1; /*theByte |= 0;*/} + else //(a == b), and we're in the first bit of the message type part of a frame + {errorsTy = errors56 = ++errors; theErrs |= 1; theByte |= 1;} + } if ((i & 7) == 7) {*pMsg++ = theByte;} @@ -1775,21 +1779,22 @@ void detectModeS(uint16_t *m, uint32_t mlen) { } } - // We measured signal strength over the first 56 bits. Don't forget to add 4 - // for the preamble samples, so round up and divide by 60. - sigStrength = (sigStrength + 29) / 60; + // adjust for magnitude zero offset + sigLevel += 365*56; + noiseLevel += 365*56; // 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) - && (sigStrength > MODES_MSG_SQUELCH_LEVEL) + && ((sigLevel * 10) > (noiseLevel * MODES_MSG_SQUELCH_FACTOR)) // (sigLevel/noiseLevel) > (MODES_MSG_SQUELCH_FACTOR/10) && (errors <= MODES_MSG_ENCODER_ERRS) ) { + float snr; // Set initial mm structure details mm.timestampMsg = Modes.timestampBlk + (j*6); - sigStrength = (sigStrength + 0x7F) >> 8; - mm.signalLevel = ((sigStrength < 255) ? sigStrength : 255); + snr = 5.0 * 20.0 * log10f( (float)sigLevel / noiseLevel ); // sig/noise levels are amplitudes, so square them when computing SNR + mm.signalLevel = (snr > 255 ? 255 : (uint8_t)round(snr)); mm.phase_corrected = use_correction; // Decode the received message From 76c958b03ef8a6d541a3ab983d292edd95ee6710 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Mon, 22 Sep 2014 14:53:06 +0100 Subject: [PATCH 002/610] Use a lookup table for SNR calculation. --- dump1090.c | 8 ++++++++ dump1090.h | 3 ++- mode_ac.c | 12 +++++++++--- mode_s.c | 19 +++++++++++-------- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/dump1090.c b/dump1090.c index b3bef280f..3db8ee5f2 100644 --- a/dump1090.c +++ b/dump1090.c @@ -98,6 +98,7 @@ void modesInit(void) { ((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.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) || ((Modes.rawOut = (char *) malloc(MODES_RAWOUT_BUF_SIZE) ) == NULL) ) { @@ -185,6 +186,13 @@ void modesInit(void) { } } + // Prepare the log10 lookup table. + // This maps from a magnitude value x (scaled as above) to 100log10(x) + for (i = 0; i <= 65535; i++) { + int l10 = (int) round(100 * log10( (i + 365.4798) * 258.433254) ); + Modes.log10lut[i] = (uint16_t) ((l10 < 65535 ? l10 : 65535)); + } + // Prepare error correction tables modesInitErrorInfo(); } diff --git a/dump1090.h b/dump1090.h index fe607d817..bcb3e804c 100644 --- a/dump1090.h +++ b/dump1090.h @@ -88,7 +88,7 @@ #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_MSG_SQUELCH_FACTOR 16 // Min SNR expressed as an amplitude ratio, scaled by 10. 20log(16/10) = 4.1dB +#define MODES_MSG_SQUELCH_DB 4.0 // Minimum SNR, in dB #define MODES_MSG_ENCODER_ERRS 3 // Maximum number of encoding errors // When changing, change also fixBitErrors() and modesInitErrorTable() !! @@ -257,6 +257,7 @@ struct { // Internal state int fd; // --ifile option file descriptor uint32_t *icao_cache; // Recently seen ICAO addresses cache uint16_t *maglut; // I/Q -> Magnitude lookup table + uint16_t *log10lut; // Magnitude -> log10 lookup table int exit; // Exit from the main loop when true // RTLSDR diff --git a/mode_ac.c b/mode_ac.c index d3d8d47b1..d96b7b3be 100644 --- a/mode_ac.c +++ b/mode_ac.c @@ -144,7 +144,7 @@ int detectModeA(uint16_t *m, struct modesMessage *mm) int F1_sig, F1_noise; int F2_sig, F2_noise; int fSig, fNoise, fLevel, fLoLo; - float snr; + int snr; // m[0] contains the energy from 0 -> 499 nS // m[1] contains the energy from 500 -> 999 nS @@ -307,8 +307,14 @@ int detectModeA(uint16_t *m, struct modesMessage *mm) if ((ModeABits < 3) || (ModeABits & 0xFFFF8808) || (ModeAErrs) ) {return (ModeABits = 0);} - snr = 5 * 20.0 * log10f((float)(fSig + fNoise + 365) / (fNoise + 365)); // 365 to adjust for magnitude value offset - mm->signalLevel = ((snr < 255) ? (uint8_t)round(snr) : 255); + // snr = 5 * 20log10(fSig / (fSig+fNoise)) (in units of 0.2dB) + // = 100log10(fSig) - 100log10(fSig+fNoise) + while (fSig > 65535 || (fSig + fNoise) > 65535) { + fSig >>= 1; + fNoise >>= 1; + } + snr = Modes.log10lut[fSig] - Modes.log10lut[fSig + fNoise]; + mm->signalLevel = ((snr < 255) ? (uint8_t)snr : 255); return ModeABits; } diff --git a/mode_s.c b/mode_s.c index e73000326..da09f5218 100644 --- a/mode_s.c +++ b/mode_s.c @@ -1575,6 +1575,7 @@ void detectModeS(uint16_t *m, uint32_t mlen) { uint8_t theByte, theErrs; int msglen, scanlen; uint32_t sigLevel, noiseLevel; + uint16_t snr; pPreamble = &m[j]; pPayload = &m[j+MODES_PREAMBLE_SAMPLES]; @@ -1779,22 +1780,24 @@ void detectModeS(uint16_t *m, uint32_t mlen) { } } - // adjust for magnitude zero offset - sigLevel += 365*56; - noiseLevel += 365*56; + // snr = 5 * 20log10(sigLevel / noiseLevel) (in units of 0.2dB) + // = 100log10(sigLevel) - 100log10(noiseLevel) + + while (sigLevel > 65535 || noiseLevel > 65535) { + sigLevel >>= 1; + noiseLevel >>= 1; + } + snr = Modes.log10lut[sigLevel] - Modes.log10lut[noiseLevel]; // 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) - && ((sigLevel * 10) > (noiseLevel * MODES_MSG_SQUELCH_FACTOR)) // (sigLevel/noiseLevel) > (MODES_MSG_SQUELCH_FACTOR/10) + && ((2 * snr) > (int) (MODES_MSG_SQUELCH_DB * 10)) && (errors <= MODES_MSG_ENCODER_ERRS) ) { - float snr; - // Set initial mm structure details mm.timestampMsg = Modes.timestampBlk + (j*6); - snr = 5.0 * 20.0 * log10f( (float)sigLevel / noiseLevel ); // sig/noise levels are amplitudes, so square them when computing SNR - mm.signalLevel = (snr > 255 ? 255 : (uint8_t)round(snr)); + mm.signalLevel = (snr > 255 ? 255 : (uint8_t)snr); mm.phase_corrected = use_correction; // Decode the received message From 56830011646f2211b3d2220c974e70cd952f9634 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Mon, 22 Sep 2014 23:56:49 +0100 Subject: [PATCH 003/610] Add --no-decode option. This disables most decoding of the contents of Mode S messages, aircraft tracking, and some output modes that depend on them. It's intended for edge receivers that just forward to a central hub rather than processing data locally. --- dump1090.c | 26 ++++++++++++++++++++++++++ dump1090.h | 1 + mode_s.c | 15 +++++++++++++-- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/dump1090.c b/dump1090.c index b3bef280f..2cca38ec1 100644 --- a/dump1090.c +++ b/dump1090.c @@ -434,6 +434,7 @@ void showHelp(void) { "--debug Debug mode (verbose), see README for details\n" "--quiet Disable output to stdout. Use for daemon applications\n" "--ppm Set receiver error in parts per million (default 0)\n" +"--no-decode Don't decode the message contents beyond the minimum necessary\n" "--help Show this help\n" "\n" "Debug mode flags: d = Log frames decoded with errors\n" @@ -681,6 +682,8 @@ int main(int argc, char **argv) { } else if (!strcmp(argv[j],"--interactive-rtl1090")) { Modes.interactive = 1; Modes.interactive_rtl1090 = 1; + } else if (!strcmp(argv[j],"--no-decode")) { + Modes.no_decode = 1; } else { fprintf(stderr, "Unknown or not enough arguments for option '%s'.\n\n", @@ -690,6 +693,29 @@ int main(int argc, char **argv) { } } + // Handle --no-decode, which turns off various parts of decoding + // that are not useful for an "edge" dump1090 that purely forwards + // raw data to a central hub elsewhere. + if (Modes.no_decode) { + if (Modes.interactive) { + fprintf(stderr, "--no-decode and --interactive cannot be specified together.\n"); + exit(1); + } + + if (Modes.net_output_sbs_port != MODES_NET_OUTPUT_SBS_PORT) { + fprintf(stderr, "--no-decode and --net-sbs-port cannot be specified together.\n"); + exit(1); + } + + if (Modes.net_http_port != MODES_NET_HTTP_PORT) { + fprintf(stderr, "--no-decode and --net-http-port cannot be specified together.\n"); + exit(1); + } + + Modes.net_output_sbs_port = 0; + Modes.net_http_port = 0; + } + #ifdef _WIN32 // Try to comply with the Copyright license conditions for binary distribution if (!Modes.quiet) {showCopyright();} diff --git a/dump1090.h b/dump1090.h index 77584bb60..8b9585d1e 100644 --- a/dump1090.h +++ b/dump1090.h @@ -317,6 +317,7 @@ struct { // Internal state int metric; // Use metric units int mlat; // Use Beast ascii format for raw data output, i.e. @...; iso *...; int interactive_rtl1090; // flight table in interactive mode is formatted like RTL1090 + int no_decode; // Disable decoding and aircraft tracking // User details double fUserLat; // Users receiver/antenna lat/lon needed for initial surface location diff --git a/mode_s.c b/mode_s.c index 34ddec125..6018c4ae8 100644 --- a/mode_s.c +++ b/mode_s.c @@ -911,6 +911,9 @@ void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) { // of the data contents, so save time and give up now. if ((Modes.check_crc) && (!mm->crcok)) { return;} + // If decoding is disabled, this is as far as we go. + if (Modes.no_decode) return; + // Fields for DF0, DF16 if (mm->msgtype == 0 || mm->msgtype == 16) { if (msg[0] & 0x04) { // VS Bit @@ -1176,6 +1179,12 @@ void displayModesMessage(struct modesMessage *mm) { if (mm->correctedbits != 0) printf("No. of bit errors fixed: %d\n", mm->correctedbits); + if (Modes.no_decode) { + // Show DF type and address only; the rest is not decoded. + printf("DF %d; address: %06x\n", mm->msgtype, mm->addr); + return; + } + if (mm->msgtype == 0) { // DF 0 printf("DF 0: Short Air-Air Surveillance.\n"); printf(" VS : %s\n", (mm->msg[0] & 0x04) ? "Ground" : "Airborne"); @@ -1900,8 +1909,10 @@ void detectModeS(uint16_t *m, uint32_t mlen) { void useModesMessage(struct modesMessage *mm) { if ((Modes.check_crc == 0) || (mm->crcok) || (mm->correctedbits)) { // not checking, ok or fixed - // Always track aircraft - interactiveReceiveData(mm); + // If we are decoding, track aircraft + if (!Modes.no_decode) { + interactiveReceiveData(mm); + } // In non-interactive non-quiet mode, display messages on standard output if (!Modes.interactive && !Modes.quiet) { From 83d256e984f2fa916933a204a9929c92a4a6dc6b Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 23 Sep 2014 14:05:25 +0100 Subject: [PATCH 004/610] Fix a thinko in computing the log10 table. (This shouldn't actually affect the resulting SNR since it's just a constant offset, and the errors in signal and noise will cancel out) --- dump1090.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dump1090.c b/dump1090.c index 3db8ee5f2..873faacc2 100644 --- a/dump1090.c +++ b/dump1090.c @@ -189,7 +189,7 @@ void modesInit(void) { // Prepare the log10 lookup table. // This maps from a magnitude value x (scaled as above) to 100log10(x) for (i = 0; i <= 65535; i++) { - int l10 = (int) round(100 * log10( (i + 365.4798) * 258.433254) ); + int l10 = (int) round(100 * log10( (i + 365.4798) / 258.433254) ); Modes.log10lut[i] = (uint16_t) ((l10 < 65535 ? l10 : 65535)); } From fd4c480df7814715663bf529d788bcc59aa7c91a Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 25 Sep 2014 17:18:44 +0100 Subject: [PATCH 005/610] Merge cleanup. --- mode_s.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mode_s.c b/mode_s.c index 25943a53c..569db3e8a 100644 --- a/mode_s.c +++ b/mode_s.c @@ -2100,7 +2100,8 @@ int decodeCPR(struct aircraft *a, int fflag, int surface) { } // Check to see that the latitude is in range: -90 .. +90 - if (rlat0 < -90 || rlat0 > 90 || rlat1 < -90 || rlat1 > 90) return; + if (rlat0 < -90 || rlat0 > 90 || rlat1 < -90 || rlat1 > 90) + return (-1); // Check that both are in the same latitude zone, or abort. if (cprNLFunction(rlat0) != cprNLFunction(rlat1)) From 826d5e92fa1a70d1eb3397466286081faa90b647 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 26 Sep 2014 00:33:39 +0100 Subject: [PATCH 006/610] Don't count ambiguous bits beyond bit 56 towards SNR. --- mode_s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode_s.c b/mode_s.c index da09f5218..6e69adb43 100644 --- a/mode_s.c +++ b/mode_s.c @@ -1694,7 +1694,7 @@ void detectModeS(uint16_t *m, uint32_t mlen) { else if (a < b) {/*theByte |= 0;*/ if (i < 56) { sigLevel += b; noiseLevel += a; }} else { - sigLevel += a; noiseLevel += a; + if (i < 56) { sigLevel += a; noiseLevel += a; } if (i >= MODES_SHORT_MSG_BITS) //(a == b), and we're in the long part of a frame {errors++; /*theByte |= 0;*/} else if (i >= 5) //(a == b), and we're in the short part of a frame From 5c8e6198b7b2076f29c488ccadb843ffbf6608d8 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 26 Sep 2014 22:42:38 +0100 Subject: [PATCH 007/610] First cut for sampling at 2.4MHz + phase detection. --- dump1090.c | 16 ++- dump1090.h | 12 ++ mode_s.c | 401 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 423 insertions(+), 6 deletions(-) diff --git a/dump1090.c b/dump1090.c index 493d66232..c767f983a 100644 --- a/dump1090.c +++ b/dump1090.c @@ -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); diff --git a/dump1090.h b/dump1090.h index babea254f..7e27437ff 100644 --- a/dump1090.h +++ b/dump1090.h @@ -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); diff --git a/mode_s.c b/mode_s.c index 2abb9a0a4..c895cede2 100644 --- a/mode_s.c +++ b/mode_s.c @@ -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; + } +} + // //========================================================================= // From 17f73cc01a76ae313a7e3a38460b5144d2532627 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 26 Sep 2014 22:47:56 +0100 Subject: [PATCH 008/610] Fix preamble quiet-bits check. --- mode_s.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mode_s.c b/mode_s.c index c895cede2..9bf03ed95 100644 --- a/mode_s.c +++ b/mode_s.c @@ -2071,12 +2071,12 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { preamble[1] > preamble[2] && preamble[12] > preamble[11] && preamble[12] > preamble[13]) { - high = (preamble[1] + preamble[13]) / 2; + high = (preamble[1] + preamble[12]) / 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; + high = (preamble[3] + preamble[9]) / 2; } else { // No peaks continue; @@ -2086,6 +2086,7 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { // 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 || From 69a30535d454eb45026c8b0cac4c0c4a21749ff5 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Sep 2014 13:07:23 +0100 Subject: [PATCH 009/610] Oversampling, round two. This now seems to be at the point where it will decode more messages than when using 2MHz with --phase-enhance. --- dump1090.c | 15 +++++ dump1090.h | 5 ++ mode_s.c | 189 +++++++++++++++++++++++++++++++---------------------- 3 files changed, 132 insertions(+), 77 deletions(-) diff --git a/dump1090.c b/dump1090.c index c767f983a..150126ce6 100644 --- a/dump1090.c +++ b/dump1090.c @@ -508,7 +508,12 @@ static void display_stats(void) { printf("%d sample blocks dropped\n", Modes.stat_blocks_dropped); 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); printf("%d demodulated with 0 errors\n", Modes.stat_demodulated0); @@ -516,6 +521,9 @@ static void display_stats(void) { printf("%d demodulated with 2 errors\n", Modes.stat_demodulated2); printf("%d demodulated with > 2 errors\n", Modes.stat_demodulated3); printf("%d with good crc\n", Modes.stat_goodcrc); + for (j = 0; j < MODES_MAX_PHASE_STATS; ++j) + if (Modes.stat_goodcrc_phase[j] > 0) + printf(" %d with phase offset %d\n", Modes.stat_goodcrc_phase[j], j); printf("%d with bad crc\n", Modes.stat_badcrc); printf("%d errors corrected\n", Modes.stat_fixed); @@ -545,6 +553,8 @@ static void display_stats(void) { 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 = @@ -569,6 +579,11 @@ static void display_stats(void) { Modes.stat_ph_bit_fix[j] = 0; Modes.stat_bit_fix[j] = 0; } + + for (j = 0; j < MODES_MAX_PHASE_STATS; j++) { + Modes.stat_preamble_phase[j] = 0; + Modes.stat_goodcrc_phase[j] = 0; + } } diff --git a/dump1090.h b/dump1090.h index 7e27437ff..97d3ef8ef 100644 --- a/dump1090.h +++ b/dump1090.h @@ -347,12 +347,17 @@ struct { // Internal state struct stDF *pDF; // Pointer to DF list // Statistics +#define MODES_MAX_PHASE_STATS 12 + 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]; unsigned int stat_demodulated0; unsigned int stat_demodulated1; unsigned int stat_demodulated2; unsigned int stat_demodulated3; unsigned int stat_goodcrc; + unsigned int stat_goodcrc_phase[MODES_MAX_PHASE_STATS]; unsigned int stat_badcrc; unsigned int stat_fixed; diff --git a/mode_s.c b/mode_s.c index 9bf03ed95..1920954b3 100644 --- a/mode_s.c +++ b/mode_s.c @@ -1947,20 +1947,25 @@ void detectModeS(uint16_t *m, uint32_t mlen) { // 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 +// TODO check if there are better (or more balanced) correlation functions to use here + +// nb: the correlation functions sum to zero, so we do not need to adjust for the DC offset in the input signal +// (adding any constant value to all of m[0..3] does not change the result) + static inline int correlate_phase0(uint16_t *m) { - return 5 * m[0] - 3 * m[1] - 2 * m[2]; + return (5 * m[0] - 3 * m[1] - 2 * m[2]) * 10 / 19; } static inline int correlate_phase1(uint16_t *m) { - return 4 * m[0] - 1 * m[1] - 3 * m[2]; + return (4 * m[0] - 1 * m[1] - 3 * m[2]) * 10 / 13; } static inline int correlate_phase2(uint16_t *m) { - return 3 * m[0] + 1 * m[1] - 4 * m[2]; + return (3 * m[0] + 1 * m[1] - 4 * m[2]) * 10 / 13; } static inline int correlate_phase3(uint16_t *m) { - return 2 * m[0] + 3 * m[1] - 5 * m[2]; + return (2 * m[0] + 3 * m[1] - 5 * m[2]) * 10 / 14; } static inline int correlate_phase4(uint16_t *m) { - return 1 * m[0] + 5 * m[1] - 5 * m[2] + 1 * m[3]; + return (1 * m[0] + 5 * m[1] - 5 * m[2] - 1 * m[3]) * 10 / 28; } // @@ -2014,25 +2019,28 @@ static inline int correlate_check_4(uint16_t *m) { } // 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]); + // empirical testing suggests that 3..8 is the best range to test for here + // (testing a wider range runs the danger of picking the wrong phase for + // a message that would otherwise be successfully decoded - the correlation + // functions can match well with a one symbol / half bit offset) + + test = correlate_check_3(&m[0]); if (test > bestval) { bestval = test; best = 3; } - test = correlate_check_4(&m[19]); + test = correlate_check_4(&m[0]); if (test > bestval) { bestval = test; best = 4; } - test = correlate_check_0(&m[20]); - if (test > bestval) { best = 5; } - + test = correlate_check_0(&m[1]); + if (test > bestval) { bestval = test; best = 5; } + test = correlate_check_1(&m[1]); + if (test > bestval) { bestval = test; best = 6; } + test = correlate_check_2(&m[1]); + if (test > bestval) { bestval = test; best = 7; } + test = correlate_check_3(&m[1]); + if (test > bestval) { bestval = test; best = 8; } return best; } @@ -2052,69 +2060,98 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { for (j = 0; j < mlen; j++) { uint16_t *preamble = &m[j]; - int high, i, phase, errors, errors56, errorsTy; + int high, i, initial_phase, 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 around sample 0 with phase offset 3..7 - // 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[12]) / 2; - } else if (preamble[3] > preamble[2] && - preamble[3] > preamble[4] && - preamble[9] > preamble[8] && - preamble[9] > preamble[10]) { - high = (preamble[3] + preamble[9]) / 2; + // Ideal sample values for preambles with different phase + // Xn is the first data symbol with phase offset N + // + // sample#: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 + // phase 3: 2/4\0/5\1 0 0 0 0/5\1/3 3\0 0 0 0 0 0 X4 + // phase 4: 1/5\0/4\2 0 0 0 0/4\2 2/4\0 0 0 0 0 0 0 X0 + // phase 5: 0/5\1/3 3\0 0 0 0/3 3\1/5\0 0 0 0 0 0 0 X1 + // phase 6: 0/4\2 2/4\0 0 0 0 2/4\0/5\1 0 0 0 0 0 0 X2 + // phase 7: 0/3 3\1/5\0 0 0 0 1/5\0/4\2 0 0 0 0 0 0 X3 + // + + // quick check: we must have a rising edge 0->1 and a falling edge 12->13 + if (! (preamble[0] < preamble[1] && preamble[12] > preamble[13]) ) + continue; + + if (preamble[1] > preamble[2] && // 1 + preamble[2] < preamble[3] && preamble[3] > preamble[4] && // 3 + preamble[8] < preamble[9] && preamble[9] > preamble[10] && // 9 + preamble[10] < preamble[11]) { // 11-12 + // peaks at 1,3,9,11-12: phase 3 + high = (preamble[1] + preamble[3] + preamble[9] + preamble[11] + preamble[12]) / 4; + } else if (preamble[1] > preamble[2] && // 1 + preamble[2] < preamble[3] && preamble[3] > preamble[4] && // 3 + preamble[8] < preamble[9] && preamble[9] > preamble[10] && // 9 + preamble[11] < preamble[12]) { // 12 + // peaks at 1,3,9,12: phase 4 + high = (preamble[1] + preamble[3] + preamble[9] + preamble[12]) / 4; + } else if (preamble[1] > preamble[2] && // 1 + preamble[2] < preamble[3] && preamble[4] > preamble[5] && // 3-4 + preamble[8] < preamble[9] && preamble[10] > preamble[11] && // 9-10 + preamble[11] < preamble[12]) { // 12 + // peaks at 1,3-4,9-10,12: phase 5 + high = (preamble[1] + preamble[3] + preamble[4] + preamble[9] + preamble[10] + preamble[12]) / 4; + } else if (preamble[1] > preamble[2] && // 1 + preamble[3] < preamble[4] && preamble[4] > preamble[5] && // 4 + preamble[9] < preamble[10] && preamble[10] > preamble[11] && // 10 + preamble[11] < preamble[12]) { // 12 + // peaks at 1,4,10,12: phase 6 + high = (preamble[1] + preamble[4] + preamble[10] + preamble[12]) / 4; + } else if (preamble[2] > preamble[3] && // 1-2 + preamble[3] < preamble[4] && preamble[4] > preamble[5] && // 4 + preamble[9] < preamble[10] && preamble[10] > preamble[11] && // 10 + preamble[11] < preamble[12]) { // 12 + // peaks at 1-2,4,10,12: phase 7 + high = (preamble[1] + preamble[2] + preamble[4] + preamble[10] + preamble[12]) / 4; } else { - // No peaks + // no suitable 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 + // Check that the "quiet" bits 6,7,15,16,17 are actually quiet - if (preamble[5] >= high || - preamble[6] >= high || + if (preamble[6] >= high || preamble[7] >= high || preamble[14] >= high || preamble[15] >= high || preamble[16] >= high || - preamble[17] >= high || - preamble[18] >= high) + preamble[17] >= high) { + ++Modes.stat_preamble_not_quiet; continue; + } // Crosscorrelate against the first few bits to find a likely phase offset - phase = best_phase(preamble); - if (phase < 0) { + initial_phase = best_phase(&preamble[19]); + if (initial_phase < 0) { + ++Modes.stat_preamble_no_correlation; continue; // nothing satisfactory } Modes.stat_valid_preamble++; + Modes.stat_preamble_phase[initial_phase%MODES_MAX_PHASE_STATS]++; + + // Rather than clear the whole mm structure, just clear the parts which are required. The clear + // is required for every possible preamble, and we don't want to be memset-ing the whole + // modesMessage structure if we don't have to.. + mm.bFlags = + mm.crcok = + mm.correctedbits = 0; // 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; - } - + pPtr = &m[j+19] + (initial_phase/5); + phase = initial_phase % 5; theByte = 0; theErrs = 0; errorsTy = 0; errors = 0; errors56 = 0; @@ -2158,12 +2195,10 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { break; } - if (test > 10) + if (test > 0) theByte |= 1; - else if (test >= -10) { - if (test > 0) - theByte |= 1; // best guess - + /* else if (test < 0) theByte |= 0; */ + else if (test == 0) { 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 @@ -2261,24 +2296,24 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { // 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; - } + 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++; + Modes.stat_goodcrc_phase[initial_phase%MODES_MAX_PHASE_STATS]++; + } 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; } } } From 309f79c488fa367a408a14308b685b80900e6ae6 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Sep 2014 13:35:41 +0100 Subject: [PATCH 010/610] Tweak correlator weighings to be right, and scale up some more to avoid losing precision. Further tweaking to which phase offsets we look for. --- mode_s.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/mode_s.c b/mode_s.c index 1920954b3..80b776588 100644 --- a/mode_s.c +++ b/mode_s.c @@ -1953,19 +1953,19 @@ void detectModeS(uint16_t *m, uint32_t mlen) { // (adding any constant value to all of m[0..3] does not change the result) static inline int correlate_phase0(uint16_t *m) { - return (5 * m[0] - 3 * m[1] - 2 * m[2]) * 10 / 19; + return (5 * m[0] - 3 * m[1] - 2 * m[2]) * 30 / 19; } static inline int correlate_phase1(uint16_t *m) { - return (4 * m[0] - 1 * m[1] - 3 * m[2]) * 10 / 13; + return (4 * m[0] - 1 * m[1] - 3 * m[2]) * 30 / 13; } static inline int correlate_phase2(uint16_t *m) { - return (3 * m[0] + 1 * m[1] - 4 * m[2]) * 10 / 13; + return (3 * m[0] + 1 * m[1] - 4 * m[2]) * 30 / 13; } static inline int correlate_phase3(uint16_t *m) { - return (2 * m[0] + 3 * m[1] - 5 * m[2]) * 10 / 14; + return (2 * m[0] + 3 * m[1] - 5 * m[2]) * 30 / 19; } static inline int correlate_phase4(uint16_t *m) { - return (1 * m[0] + 5 * m[1] - 5 * m[2] - 1 * m[3]) * 10 / 28; + return (1 * m[0] + 5 * m[1] - 5 * m[2] - 1 * m[3]) * 30 / 26; } // @@ -2024,13 +2024,18 @@ static int best_phase(uint16_t *m) { int best = -1; int bestval = 50; // minimum correlation quality we will accept - // empirical testing suggests that 3..8 is the best range to test for here + // empirical testing suggests that 4..8 is the best range to test for here // (testing a wider range runs the danger of picking the wrong phase for // a message that would otherwise be successfully decoded - the correlation // functions can match well with a one symbol / half bit offset) - test = correlate_check_3(&m[0]); - if (test > bestval) { bestval = test; best = 3; } + // this is consistent with the peak detection which should produce + // the first data symbol with phase offset 4..8 + + //test = correlate_check_2(&m[0]); + //if (test > bestval) { bestval = test; best = 2; } + //test = correlate_check_3(&m[0]); + //if (test > bestval) { bestval = test; best = 3; } test = correlate_check_4(&m[0]); if (test > bestval) { bestval = test; best = 4; } test = correlate_check_0(&m[1]); @@ -2041,6 +2046,8 @@ static int best_phase(uint16_t *m) { if (test > bestval) { bestval = test; best = 7; } test = correlate_check_3(&m[1]); if (test > bestval) { bestval = test; best = 8; } + //test = correlate_check_4(&m[1]); + //if (test > bestval) { bestval = test; best = 9; } return best; } From c3409302ce0209f8d21dfa7d746f02abe9ee14e1 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Sep 2014 16:44:26 +0100 Subject: [PATCH 011/610] Fix mlat inter-block timestamp accounting in oversampling mode. Add calculated phase into the per-message timestamp (it is already exactly a 12MHz offset) --- dump1090.c | 5 ++++- mode_s.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/dump1090.c b/dump1090.c index 150126ce6..64087f1b0 100644 --- a/dump1090.c +++ b/dump1090.c @@ -918,7 +918,10 @@ int main(int argc, char **argv) { detectModeS(Modes.magnitude, MODES_ASYNC_BUF_SAMPLES); // Update the timestamp ready for the next block - Modes.timestampBlk += (MODES_ASYNC_BUF_SAMPLES*6); + if (Modes.oversample) + Modes.timestampBlk += (MODES_ASYNC_BUF_SAMPLES*5); + else + Modes.timestampBlk += (MODES_ASYNC_BUF_SAMPLES*6); Modes.stat_blocks_processed++; } else { pthread_cond_signal (&Modes.data_cond); diff --git a/mode_s.c b/mode_s.c index 80b776588..9c9903f86 100644 --- a/mode_s.c +++ b/mode_s.c @@ -2292,7 +2292,7 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { // 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.timestampMsg = Modes.timestampBlk + (j*5) + initial_phase; mm.signalLevel = 0; mm.phase_corrected = 0; From e118668925f1bd9d4b7c2783036b2fd339635ccc Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Sep 2014 16:45:39 +0100 Subject: [PATCH 012/610] Log message time and symbol phase when dumping message info. --- mode_s.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mode_s.c b/mode_s.c index 9c9903f86..cb4da37f3 100644 --- a/mode_s.c +++ b/mode_s.c @@ -1181,6 +1181,9 @@ void displayModesMessage(struct modesMessage *mm) { printf("SNR: %d.%d dB\n", mm->signalLevel/5, 2*(mm->signalLevel%5)); + if (mm->timestampMsg) + printf("Time: %.2fus (phase: %d)\n", mm->timestampMsg / 12.0, (unsigned int) (360 * (mm->timestampMsg % 6) / 6)); + if (Modes.no_decode) { // Show DF type and address only; the rest is not decoded. printf("DF %d; address: %06x\n", mm->msgtype, mm->addr); From ca372ed105397ef93127b52585b5cbe92946588d Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Sep 2014 16:47:50 +0100 Subject: [PATCH 013/610] Add SNR calculation in oversample mode. --- mode_s.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/mode_s.c b/mode_s.c index cb4da37f3..bef6a7ebb 100644 --- a/mode_s.c +++ b/mode_s.c @@ -2074,6 +2074,8 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { int msglen, scanlen; uint16_t *pPtr; uint8_t theByte, theErrs; + uint32_t sigLevel, noiseLevel; + uint16_t snr; // Look for a message starting at around sample 0 with phase offset 3..7 @@ -2098,30 +2100,40 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { preamble[10] < preamble[11]) { // 11-12 // peaks at 1,3,9,11-12: phase 3 high = (preamble[1] + preamble[3] + preamble[9] + preamble[11] + preamble[12]) / 4; + sigLevel = preamble[1] + preamble[3] + preamble[9]; + noiseLevel = preamble[5] + preamble[6] + preamble[7]; } else if (preamble[1] > preamble[2] && // 1 preamble[2] < preamble[3] && preamble[3] > preamble[4] && // 3 preamble[8] < preamble[9] && preamble[9] > preamble[10] && // 9 preamble[11] < preamble[12]) { // 12 // peaks at 1,3,9,12: phase 4 high = (preamble[1] + preamble[3] + preamble[9] + preamble[12]) / 4; + sigLevel = preamble[1] + preamble[3] + preamble[9] + preamble[12]; + noiseLevel = preamble[5] + preamble[6] + preamble[7] + preamble[8]; } else if (preamble[1] > preamble[2] && // 1 preamble[2] < preamble[3] && preamble[4] > preamble[5] && // 3-4 preamble[8] < preamble[9] && preamble[10] > preamble[11] && // 9-10 preamble[11] < preamble[12]) { // 12 // peaks at 1,3-4,9-10,12: phase 5 high = (preamble[1] + preamble[3] + preamble[4] + preamble[9] + preamble[10] + preamble[12]) / 4; + sigLevel = preamble[1] + preamble[12]; + noiseLevel = preamble[6] + preamble[7]; } else if (preamble[1] > preamble[2] && // 1 preamble[3] < preamble[4] && preamble[4] > preamble[5] && // 4 preamble[9] < preamble[10] && preamble[10] > preamble[11] && // 10 preamble[11] < preamble[12]) { // 12 // peaks at 1,4,10,12: phase 6 high = (preamble[1] + preamble[4] + preamble[10] + preamble[12]) / 4; + sigLevel = preamble[1] + preamble[4] + preamble[10] + preamble[12]; + noiseLevel = preamble[5] + preamble[6] + preamble[7] + preamble[8]; } else if (preamble[2] > preamble[3] && // 1-2 preamble[3] < preamble[4] && preamble[4] > preamble[5] && // 4 preamble[9] < preamble[10] && preamble[10] > preamble[11] && // 10 preamble[11] < preamble[12]) { // 12 // peaks at 1-2,4,10,12: phase 7 high = (preamble[1] + preamble[2] + preamble[4] + preamble[10] + preamble[12]) / 4; + sigLevel = preamble[4] + preamble[10] + preamble[12]; + noiseLevel = preamble[6] + preamble[7] + preamble[8]; } else { // no suitable peaks continue; @@ -2196,6 +2208,35 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { case 4: test = correlate_phase4(pPtr); + + // A phase-4 bit exactly straddles a sample boundary. + // Here's what a 1-0 bit with phase 4 looks like: + // + // |SYM 1| + // xxx| | |xxx + // |SYM 2| + // + // 012340123401234012340 <-- sample phase + // | 0 | 1 | 2 | 3 | <-- sample boundaries + // + // Samples 1 and 2 only have power from symbols 1 and 2. + // So we can use this to extract signal/noise values + // as one of the two symbols is high (signal) and the + // other is low (noise) + // + // This also gives us an equal number of signal and noise + // samples, which is convenient. Using the first half of + // a phase 0 bit, or the second half of a phase 3 bit, would + // also work, but we have no guarantees about how many signal + // or noise bits we'd see in those phases. + + if (test < 0) { // 0 1 + noiseLevel += pPtr[1]; + sigLevel += pPtr[2]; + } else { // 1 0 + sigLevel += pPtr[1]; + noiseLevel += pPtr[2]; + } phase = 1; pPtr += 3; break; @@ -2290,13 +2331,24 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { } } + // snr = 5 * 20log10(sigLevel / noiseLevel) (in units of 0.2dB) + // = 100log10(sigLevel) - 100log10(noiseLevel) + + while (sigLevel > 65535 || noiseLevel > 65535) { + sigLevel >>= 1; + noiseLevel >>= 1; + } + snr = Modes.log10lut[sigLevel] - Modes.log10lut[noiseLevel]; + // 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) ) { + if ( (msglen) + // && ((2 * snr) > (int) (MODES_MSG_SQUELCH_DB * 10)) + && (errors <= MODES_MSG_ENCODER_ERRS) ) { // Set initial mm structure details mm.timestampMsg = Modes.timestampBlk + (j*5) + initial_phase; - mm.signalLevel = 0; + mm.signalLevel = (snr > 255 ? 255 : (uint8_t)snr); mm.phase_corrected = 0; //dumpRawMessage("decoded with oversampling", msg, m, j); From 4732ad3498313bbf7a4c5934435c2854b50012de Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Mon, 29 Sep 2014 23:02:42 +0100 Subject: [PATCH 014/610] Performance tweaking for AGC. Apparently enabling AGC produces samples with quite different characteristics, and ends up eating a lot more CPU as the previous heuristics would generate a lot of false positives. Tweaking the parameters and a bit of optimization seems to bring this back down to usable levels without losing many potential messages. --- mode_s.c | 52 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/mode_s.c b/mode_s.c index bef6a7ebb..20428b681 100644 --- a/mode_s.c +++ b/mode_s.c @@ -1955,20 +1955,36 @@ void detectModeS(uint16_t *m, uint32_t mlen) { // nb: the correlation functions sum to zero, so we do not need to adjust for the DC offset in the input signal // (adding any constant value to all of m[0..3] does not change the result) +static inline int slice_phase0(uint16_t *m) { + return 5 * m[0] - 3 * m[1] - 2 * m[2]; +} +static inline int slice_phase1(uint16_t *m) { + return 4 * m[0] - m[1] - 3 * m[2]; +} +static inline int slice_phase2(uint16_t *m) { + return 3 * m[0] + m[1] - 4 * m[2]; +} +static inline int slice_phase3(uint16_t *m) { + return 2 * m[0] + 3 * m[1] - 5 * m[2]; +} +static inline int slice_phase4(uint16_t *m) { + return m[0] + 5 * m[1] - 5 * m[2] - m[3]; +} + static inline int correlate_phase0(uint16_t *m) { - return (5 * m[0] - 3 * m[1] - 2 * m[2]) * 30 / 19; + return slice_phase0(m) * 3; } static inline int correlate_phase1(uint16_t *m) { - return (4 * m[0] - 1 * m[1] - 3 * m[2]) * 30 / 13; + return slice_phase1(m) * 4; } static inline int correlate_phase2(uint16_t *m) { - return (3 * m[0] + 1 * m[1] - 4 * m[2]) * 30 / 13; + return slice_phase2(m) * 4; } static inline int correlate_phase3(uint16_t *m) { - return (2 * m[0] + 3 * m[1] - 5 * m[2]) * 30 / 19; + return slice_phase3(m) * 3; } static inline int correlate_phase4(uint16_t *m) { - return (1 * m[0] + 5 * m[1] - 5 * m[2] - 1 * m[3]) * 30 / 26; + return slice_phase4(m) * 2; } // @@ -2025,7 +2041,7 @@ static inline int correlate_check_4(uint16_t *m) { static int best_phase(uint16_t *m) { int test; int best = -1; - int bestval = 50; // minimum correlation quality we will accept + int bestval = 10000; // minimum correlation quality we will accept // empirical testing suggests that 4..8 is the best range to test for here // (testing a wider range runs the danger of picking the wrong phase for @@ -2139,18 +2155,24 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { continue; } - // Check that the "quiet" bits 6,7,15,16,17 are actually quiet + // Check for enough signal + if (sigLevel < 2 * noiseLevel) + continue; - if (preamble[6] >= high || + // Check that the "quiet" bits 6,7,15,16,17 are actually quiet + if (preamble[5] >= high || + preamble[6] >= high || preamble[7] >= high || + preamble[8] >= high || preamble[14] >= high || preamble[15] >= high || preamble[16] >= high || - preamble[17] >= high) { + preamble[17] >= high || + preamble[18] >= high) { ++Modes.stat_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) { @@ -2183,31 +2205,31 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { switch (phase) { case 0: - test = correlate_phase0(pPtr); + test = slice_phase0(pPtr); phase = 2; pPtr += 2; break; case 1: - test = correlate_phase1(pPtr); + test = slice_phase1(pPtr); phase = 3; pPtr += 2; break; case 2: - test = correlate_phase2(pPtr); + test = slice_phase2(pPtr); phase = 4; pPtr += 2; break; case 3: - test = correlate_phase3(pPtr); + test = slice_phase3(pPtr); phase = 0; pPtr += 3; break; case 4: - test = correlate_phase4(pPtr); + test = slice_phase4(pPtr); // A phase-4 bit exactly straddles a sample boundary. // Here's what a 1-0 bit with phase 4 looks like: From 7a0ca85a90e432072a5c31f81ddff06d3406aa8f Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Mon, 29 Sep 2014 23:04:58 +0100 Subject: [PATCH 015/610] Measure CPU used by the sample processing thread. --- Makefile | 2 +- dump1090.c | 10 ++++++++++ dump1090.h | 3 +++ mode_s.c | 14 ++++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6bfe9ee14..672ada289 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ EXTRACFLAGS=-DHTMLPATH=\"$(SHAREDIR)\" endif CFLAGS=-O2 -g -Wall -W `pkg-config --cflags librtlsdr` -LIBS=`pkg-config --libs librtlsdr` -lpthread -lm +LIBS=`pkg-config --libs librtlsdr` -lpthread -lm -lrt CC=gcc diff --git a/dump1090.c b/dump1090.c index 64087f1b0..39898e38a 100644 --- a/dump1090.c +++ b/dump1090.c @@ -507,6 +507,13 @@ static void display_stats(void) { 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 = 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); @@ -549,6 +556,9 @@ static void display_stats(void) { printf("%d total usable messages\n", Modes.stat_goodcrc + Modes.stat_ph_goodcrc + Modes.stat_fixed + Modes.stat_ph_fixed); fflush(stdout); + Modes.stat_cputime.tv_sec = 0; + Modes.stat_cputime.tv_nsec = 0; + Modes.stat_blocks_processed = Modes.stat_blocks_dropped = 0; diff --git a/dump1090.h b/dump1090.h index 97d3ef8ef..7b9debad5 100644 --- a/dump1090.h +++ b/dump1090.h @@ -57,6 +57,7 @@ #include #include #include + #include #include "rtl-sdr.h" #include "anet.h" #else @@ -387,6 +388,8 @@ struct { // Internal state unsigned int stat_blocks_processed; unsigned int stat_blocks_dropped; + + struct timespec stat_cputime; } Modes; // The struct we use to store information about a decoded message. diff --git a/mode_s.c b/mode_s.c index 20428b681..9dcd7a21b 100644 --- a/mode_s.c +++ b/mode_s.c @@ -2082,6 +2082,9 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { unsigned char msg[MODES_LONG_MSG_BYTES], *pMsg; uint32_t j; + struct timespec cpu_start_time, cpu_end_time; + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &cpu_start_time); + memset(&mm, 0, sizeof(mm)); for (j = 0; j < mlen; j++) { @@ -2412,6 +2415,17 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { } } + 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++; + } + //Send any remaining partial raw buffers now if (Modes.rawOutUsed || Modes.beastOutUsed) { From dcae71faa813249ef652afcf1d35f2d7a0ca821e Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Mon, 29 Sep 2014 23:11:36 +0100 Subject: [PATCH 016/610] Move CPU instrumentation up into the main loop. --- dump1090.c | 15 +++++++++++++++ mode_s.c | 14 -------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/dump1090.c b/dump1090.c index 39898e38a..0fb1fa6f8 100644 --- a/dump1090.c +++ b/dump1090.c @@ -888,6 +888,7 @@ int main(int argc, char **argv) { pthread_mutex_lock(&Modes.data_mutex); while (Modes.exit == 0) { + struct timespec cpu_start_time, cpu_end_time; if (Modes.iDataReady == 0) { pthread_cond_wait(&Modes.data_cond,&Modes.data_mutex); // This unlocks Modes.data_mutex, and waits for Modes.data_cond @@ -922,11 +923,25 @@ 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. + + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &cpu_start_time); + if (Modes.oversample) detectModeS_oversample(Modes.magnitude, MODES_ASYNC_BUF_SAMPLES); else detectModeS(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++; + } + // Update the timestamp ready for the next block if (Modes.oversample) Modes.timestampBlk += (MODES_ASYNC_BUF_SAMPLES*5); diff --git a/mode_s.c b/mode_s.c index 9dcd7a21b..20428b681 100644 --- a/mode_s.c +++ b/mode_s.c @@ -2082,9 +2082,6 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { unsigned char msg[MODES_LONG_MSG_BYTES], *pMsg; uint32_t j; - struct timespec cpu_start_time, cpu_end_time; - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &cpu_start_time); - memset(&mm, 0, sizeof(mm)); for (j = 0; j < mlen; j++) { @@ -2415,17 +2412,6 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { } } - 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++; - } - //Send any remaining partial raw buffers now if (Modes.rawOutUsed || Modes.beastOutUsed) { From 93e1b9e10d6f906aba29b223c5f61fa0aa84f94a Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 30 Sep 2014 00:19:05 +0100 Subject: [PATCH 017/610] More tweaking to try to get the signal / CPU tradeoff right. --- mode_s.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode_s.c b/mode_s.c index 20428b681..88053c5f1 100644 --- a/mode_s.c +++ b/mode_s.c @@ -2041,7 +2041,7 @@ static inline int correlate_check_4(uint16_t *m) { static int best_phase(uint16_t *m) { int test; int best = -1; - int bestval = 10000; // minimum correlation quality we will accept + int bestval = (m[0] + m[1] + m[2] + m[3] + m[4] + m[5]); // minimum correlation quality we will accept // empirical testing suggests that 4..8 is the best range to test for here // (testing a wider range runs the danger of picking the wrong phase for @@ -2156,7 +2156,7 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { } // Check for enough signal - if (sigLevel < 2 * noiseLevel) + if (sigLevel * 2 < 3 * noiseLevel) // about 3.5dB SNR continue; // Check that the "quiet" bits 6,7,15,16,17 are actually quiet From c11eca44bbaf76fa07b909d810df13e60bd85e0e Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 30 Sep 2014 17:02:22 +0100 Subject: [PATCH 018/610] Try all phases if --oversample --phase-enhance is on. If we demodulate a message in 2.4MHz mode and it has a bad, uncorrectable CRC, and --phase-enhance is on, then retry with the other possible phases until we get a good CRC or run out of phases to try. This is very expensive in AGC mode (lots of candidates that are not real messages) but relatively cheap otherwise. It yields another 10% messages. Also factor out some common stats code to avoid lots more copy/paste. --- dump1090.c | 97 +++++++++++++++++++------------------ dump1090.h | 42 ++++++++-------- mode_s.c | 137 +++++++++++++++++++++++++---------------------------- 3 files changed, 133 insertions(+), 143 deletions(-) diff --git a/dump1090.c b/dump1090.c index 0fb1fa6f8..e3bd9eff6 100644 --- a/dump1090.c +++ b/dump1090.c @@ -494,6 +494,44 @@ 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 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); @@ -523,37 +561,16 @@ static void display_stats(void) { 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); - printf("%d demodulated with 0 errors\n", Modes.stat_demodulated0); - printf("%d demodulated with 1 error\n", Modes.stat_demodulated1); - printf("%d demodulated with 2 errors\n", Modes.stat_demodulated2); - printf("%d demodulated with > 2 errors\n", Modes.stat_demodulated3); - printf("%d with good crc\n", Modes.stat_goodcrc); - for (j = 0; j < MODES_MAX_PHASE_STATS; ++j) - if (Modes.stat_goodcrc_phase[j] > 0) - printf(" %d with phase offset %d\n", Modes.stat_goodcrc_phase[j], j); - printf("%d with bad crc\n", Modes.stat_badcrc); - printf("%d errors corrected\n", Modes.stat_fixed); - - for (j = 0; j < MODES_MAX_BITERRORS; j++) { - printf(" %d with %d bit %s\n", Modes.stat_bit_fix[j], j+1, (j==0)?"error":"errors"); - } + display_demod_stats("", &Modes.stat_demod); if (Modes.phase_enhance) { printf("%d phase enhancement attempts\n", Modes.stat_out_of_phase); - printf("%d phase enhanced demodulated with 0 errors\n", Modes.stat_ph_demodulated0); - printf("%d phase enhanced demodulated with 1 error\n", Modes.stat_ph_demodulated1); - printf("%d phase enhanced demodulated with 2 errors\n", Modes.stat_ph_demodulated2); - printf("%d phase enhanced demodulated with > 2 errors\n", Modes.stat_ph_demodulated3); - printf("%d phase enhanced with good crc\n", Modes.stat_ph_goodcrc); - printf("%d phase enhanced with bad crc\n", Modes.stat_ph_badcrc); - printf("%d phase enhanced errors corrected\n", Modes.stat_ph_fixed); - - for (j = 0; j < MODES_MAX_BITERRORS; j++) { - printf(" %d with %d bit %s\n", Modes.stat_ph_bit_fix[j], j+1, (j==0)?"error":"errors"); - } + display_demod_stats("phase enhanced ", &Modes.stat_demod_phasecorrected); } - printf("%d total usable messages\n", Modes.stat_goodcrc + Modes.stat_ph_goodcrc + Modes.stat_fixed + Modes.stat_ph_fixed); + printf("%d total usable messages\n", + Modes.stat_demod.goodcrc + Modes.stat_demod_phasecorrected.goodcrc + + Modes.stat_demod.fixed + Modes.stat_demod_phasecorrected.fixed); fflush(stdout); Modes.stat_cputime.tv_sec = 0; @@ -567,33 +584,15 @@ static void display_stats(void) { Modes.stat_preamble_not_quiet = Modes.stat_valid_preamble = Modes.stat_DF_Len_Corrected = - Modes.stat_DF_Type_Corrected = - Modes.stat_demodulated0 = - Modes.stat_demodulated1 = - Modes.stat_demodulated2 = - Modes.stat_demodulated3 = - Modes.stat_goodcrc = - Modes.stat_badcrc = - Modes.stat_fixed = 0; - - Modes.stat_out_of_phase = - Modes.stat_ph_demodulated0 = - Modes.stat_ph_demodulated1 = - Modes.stat_ph_demodulated2 = - Modes.stat_ph_demodulated3 = - Modes.stat_ph_goodcrc = - Modes.stat_ph_badcrc = - Modes.stat_ph_fixed = 0; - - for (j = 0; j < MODES_MAX_BITERRORS; j++) { - Modes.stat_ph_bit_fix[j] = 0; - Modes.stat_bit_fix[j] = 0; - } + Modes.stat_DF_Type_Corrected = + Modes.stat_out_of_phase = 0; for (j = 0; j < MODES_MAX_PHASE_STATS; j++) { Modes.stat_preamble_phase[j] = 0; - Modes.stat_goodcrc_phase[j] = 0; } + + reset_demod_stats(&Modes.stat_demod); + reset_demod_stats(&Modes.stat_demod_phasecorrected); } diff --git a/dump1090.h b/dump1090.h index 7b9debad5..b545eca28 100644 --- a/dump1090.h +++ b/dump1090.h @@ -96,6 +96,8 @@ // When changing, change also fixBitErrors() and modesInitErrorTable() !! #define MODES_MAX_BITERRORS 2 // Global max for fixable bit erros +#define MODES_MAX_PHASE_STATS 10 + #define MODEAC_MSG_SAMPLES (25 * 2) // include up to the SPI bit #define MODEAC_MSG_BYTES 2 #define MODEAC_MSG_SQUELCH_LEVEL 0x07FF // Average signal strength limit @@ -246,6 +248,22 @@ struct stDF { unsigned char msg[MODES_LONG_MSG_BYTES]; // the binary } tDF; +// 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]; +}; + // Program global state struct { // Internal state pthread_t reader_thread; @@ -348,39 +366,19 @@ struct { // Internal state struct stDF *pDF; // Pointer to DF list // Statistics -#define MODES_MAX_PHASE_STATS 12 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]; - unsigned int stat_demodulated0; - unsigned int stat_demodulated1; - unsigned int stat_demodulated2; - unsigned int stat_demodulated3; - unsigned int stat_goodcrc; - unsigned int stat_goodcrc_phase[MODES_MAX_PHASE_STATS]; - unsigned int stat_badcrc; - unsigned int stat_fixed; - // Histogram of fixed bit errors: index 0 for single bit erros, - // index 1 for double bit errors etc. - unsigned int stat_bit_fix[MODES_MAX_BITERRORS]; + struct demod_stats stat_demod; + struct demod_stats stat_demod_phasecorrected; unsigned int stat_http_requests; unsigned int stat_sbs_connections; unsigned int stat_raw_connections; unsigned int stat_beast_connections; unsigned int stat_out_of_phase; - unsigned int stat_ph_demodulated0; - unsigned int stat_ph_demodulated1; - unsigned int stat_ph_demodulated2; - unsigned int stat_ph_demodulated3; - unsigned int stat_ph_goodcrc; - unsigned int stat_ph_badcrc; - unsigned int stat_ph_fixed; - // Histogram of fixed bit errors: index 0 for single bit erros, - // index 1 for double bit errors etc. - unsigned int stat_ph_bit_fix[MODES_MAX_BITERRORS]; unsigned int stat_DF_Len_Corrected; unsigned int stat_DF_Type_Corrected; diff --git a/mode_s.c b/mode_s.c index 88053c5f1..ded2b2b2c 100644 --- a/mode_s.c +++ b/mode_s.c @@ -1818,49 +1818,25 @@ void detectModeS(uint16_t *m, uint32_t mlen) { // Update statistics if (Modes.stats) { - if (mm.crcok || use_correction || mm.correctedbits) { - - if (use_correction) { - switch (errors) { - case 0: {Modes.stat_ph_demodulated0++; break;} - case 1: {Modes.stat_ph_demodulated1++; break;} - case 2: {Modes.stat_ph_demodulated2++; break;} - default:{Modes.stat_ph_demodulated3++; break;} - } - } else { - 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 (use_correction) { - if (mm.crcok) {Modes.stat_ph_goodcrc++;} - else {Modes.stat_ph_badcrc++;} - } else { - if (mm.crcok) {Modes.stat_goodcrc++;} - else {Modes.stat_badcrc++;} - } - - } else if (use_correction) { - Modes.stat_ph_badcrc++; - Modes.stat_ph_fixed++; - if ( (mm.correctedbits) - && (mm.correctedbits <= MODES_MAX_BITERRORS) ) { - Modes.stat_ph_bit_fix[mm.correctedbits-1] += 1; - } + struct demod_stats *dstats = (use_correction ? &Modes.stat_demod_phasecorrected : &Modes.stat_demod); - } else { - Modes.stat_badcrc++; - Modes.stat_fixed++; - if ( (mm.correctedbits) - && (mm.correctedbits <= MODES_MAX_BITERRORS) ) { - Modes.stat_bit_fix[mm.correctedbits-1] += 1; - } - } + switch (errors) { + case 0: dstats->demodulated0++; break; + case 1: dstats->demodulated1++; break; + case 2: dstats->demodulated2++; break; + default: dstats->demodulated3++; break; + } + + if (mm.crcok) { + dstats->goodcrc++; + dstats->goodcrc_byphase[0]++; + } else if (mm.correctedbits > 0) { + dstats->badcrc++; + dstats->fixed++; + if (mm.correctedbits <= MODES_MAX_BITERRORS) + dstats->bit_fix[mm.correctedbits-1] += 1; + } else { + dstats->badcrc++; } } @@ -1878,7 +1854,7 @@ void detectModeS(uint16_t *m, uint32_t mlen) { } // Skip this message if we are sure it's fine - if (mm.crcok) { + if (mm.crcok || mm.correctedbits) { j += (MODES_PREAMBLE_US+msglen)*2 - 1; } @@ -1972,19 +1948,19 @@ static inline int slice_phase4(uint16_t *m) { } static inline int correlate_phase0(uint16_t *m) { - return slice_phase0(m) * 3; + return slice_phase0(m) * 26; } static inline int correlate_phase1(uint16_t *m) { - return slice_phase1(m) * 4; + return slice_phase1(m) * 38; } static inline int correlate_phase2(uint16_t *m) { - return slice_phase2(m) * 4; + return slice_phase2(m) * 38; } static inline int correlate_phase3(uint16_t *m) { - return slice_phase3(m) * 3; + return slice_phase3(m) * 26; } static inline int correlate_phase4(uint16_t *m) { - return slice_phase4(m) * 2; + return slice_phase4(m) * 19; } // @@ -2092,6 +2068,7 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { uint8_t theByte, theErrs; uint32_t sigLevel, noiseLevel; uint16_t snr; + int try_phase; // Look for a message starting at around sample 0 with phase offset 3..7 @@ -2183,6 +2160,9 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { Modes.stat_valid_preamble++; Modes.stat_preamble_phase[initial_phase%MODES_MAX_PHASE_STATS]++; + try_phase = initial_phase; + + retry: // Rather than clear the whole mm structure, just clear the parts which are required. The clear // is required for every possible preamble, and we don't want to be memset-ing the whole // modesMessage structure if we don't have to.. @@ -2192,10 +2172,10 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { // 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] + (initial_phase/5); - phase = initial_phase % 5; + pPtr = &m[j+19] + (try_phase/5); + phase = try_phase % 5; theByte = 0; theErrs = 0; errorsTy = 0; errors = 0; errors56 = 0; @@ -2369,46 +2349,59 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { // && ((2 * snr) > (int) (MODES_MSG_SQUELCH_DB * 10)) && (errors <= MODES_MSG_ENCODER_ERRS) ) { // Set initial mm structure details - mm.timestampMsg = Modes.timestampBlk + (j*5) + initial_phase; + mm.timestampMsg = Modes.timestampBlk + (j*5) + try_phase; mm.signalLevel = (snr > 255 ? 255 : (uint8_t)snr); - mm.phase_corrected = 0; - - //dumpRawMessage("decoded with oversampling", msg, m, j); + mm.phase_corrected = (initial_phase != try_phase); // Decode the received message decodeModesMessage(&mm, msg); - + // Update statistics if (Modes.stats) { + struct demod_stats *dstats = (mm.phase_corrected ? &Modes.stat_demod_phasecorrected : &Modes.stat_demod); + 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;} + case 0: dstats->demodulated0++; break; + case 1: dstats->demodulated1++; break; + case 2: dstats->demodulated2++; break; + default: dstats->demodulated3++; break; } - if (mm.correctedbits == 0) { - if (mm.crcok) { - Modes.stat_goodcrc++; - Modes.stat_goodcrc_phase[initial_phase%MODES_MAX_PHASE_STATS]++; - } else {Modes.stat_badcrc++;} + if (mm.crcok) { + dstats->goodcrc++; + dstats->goodcrc_byphase[try_phase%MODES_MAX_PHASE_STATS]++; + } else if (mm.correctedbits > 0) { + dstats->badcrc++; + dstats->fixed++; + if (mm.correctedbits <= MODES_MAX_BITERRORS) + dstats->bit_fix[mm.correctedbits-1] += 1; } else { - Modes.stat_badcrc++; - Modes.stat_fixed++; - if ( (mm.correctedbits) - && (mm.correctedbits <= MODES_MAX_BITERRORS) ) { - Modes.stat_bit_fix[mm.correctedbits-1] += 1; - } + dstats->badcrc++; } } // Skip this message if we are sure it's fine - if (mm.crcok) { + if (mm.crcok || mm.correctedbits) { j += (16+msglen)*6/5 - 1; } // Pass data to the next layer useModesMessage(&mm); + + // Only try with different phases if we mostly demodulated OK, + // but the CRC failed. This seems to catch most of the cases + // where trying different phases actually helps, and is much + // cheaper than trying it on every single candidate that passes + // peak detection + if (Modes.phase_enhance && !mm.crcok && !mm.correctedbits) { + if (try_phase == initial_phase) + ++Modes.stat_out_of_phase; + try_phase++; + if (try_phase == 9) + try_phase = 4; + if (try_phase != initial_phase) + goto retry; + } } } From 864660bf27c020612e63148c8498dbe2679560b7 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 1 Oct 2014 01:43:37 +0100 Subject: [PATCH 019/610] Use uint64_t in load stats to avoid overflow with large intervals. --- dump1090.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dump1090.c b/dump1090.c index e3bd9eff6..94c791d31 100644 --- a/dump1090.c +++ b/dump1090.c @@ -547,7 +547,7 @@ static void display_stats(void) { 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 = Modes.stat_blocks_processed * MODES_ASYNC_BUF_SAMPLES / (Modes.oversample ? 2400 : 2000); + 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); } From a2f49f2bb8d25b6e667aa97356604b7fdd5b6e57 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Thu, 2 Oct 2014 21:06:10 +0100 Subject: [PATCH 020/610] Fix a crash when an outbound client is closed in response to reading a remote message. When we read from some client A, we may end up forwarding a message to other clients. If we forward to some client B and there is a write error, then we close B and remove it from the client list. However, if before this happened A->next == B, then the read loop will still be holding on to a pointer to B, and we crash. As it's unpredictable what clients could be closed at what point, the simplest approach is to retain closed clients in the list until we are at a point where we know there are no stray pointers on stack, and only then modify the list. This also simplifies anything that has to loop over clients, as it doesn't need to worry about the current client being freed under it. --- dump1090.c | 1 + dump1090.h | 1 + net_io.c | 56 +++++++++++++++++++++++++++--------------------------- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/dump1090.c b/dump1090.c index 7a7addda1..3d5ff46f4 100644 --- a/dump1090.c +++ b/dump1090.c @@ -490,6 +490,7 @@ void showCopyright(void) { void backgroundTasks(void) { if (Modes.net) { modesReadFromClients(); + modesNetCleanup(); } // If Modes.aircrafts is not NULL, remove any stale aircraft diff --git a/dump1090.h b/dump1090.h index 77584bb60..5734da5b0 100644 --- a/dump1090.h +++ b/dump1090.h @@ -454,6 +454,7 @@ void modesReadFromClients (void); void modesSendAllClients (int service, void *msg, int len); void modesQueueOutput (struct modesMessage *mm); void modesReadFromClient(struct client *c, char *sep, int(*handler)(struct client *, char *)); +void modesNetCleanup (void); #ifdef __cplusplus } diff --git a/net_io.c b/net_io.c index 2b0ed9a52..ff1b1872a 100644 --- a/net_io.c +++ b/net_io.c @@ -145,23 +145,11 @@ struct client * modesAcceptClients(void) { // On error free the client, collect the structure, adjust maxfd if needed. // void modesFreeClient(struct client *c) { + // Clean up, but defer removing from the list until modesNetCleanup(). + // This is because there may be stackframes still pointing at this + // client (unpredictably: reading from client A may cause client B to + // be freed) - // Unhook this client from the linked list of clients - struct client *p = Modes.clients; - if (p) { - if (p == c) { - Modes.clients = c->next; - } else { - while ((p) && (p->next != c)) { - p = p->next; - } - if (p) { - p->next = c->next; - } - } - } - - // It's now safe to remove this client close(c->fd); if (c->service == Modes.sbsos) { if (Modes.stat_sbs_connections) Modes.stat_sbs_connections--; @@ -174,7 +162,9 @@ void modesFreeClient(struct client *c) { if (Modes.debug & MODES_DEBUG_NET) printf("Closing client %d\n", c->fd); - free(c); + // mark it as inactive and ready to be freed + c->fd = -1; + c->service = -1; } // //========================================================================= @@ -182,12 +172,9 @@ void modesFreeClient(struct client *c) { // Send the specified message to all clients listening for a given service // void modesSendAllClients(int service, void *msg, int len) { - struct client *c = Modes.clients; - - while (c) { - // Read next before servicing client incase the service routine deletes the client! - struct client *next = c->next; + struct client *c; + for (c = Modes.clients; c; c = c->next) { if (c->service == service) { #ifndef _WIN32 int nwritten = write(c->fd, msg, len); @@ -198,7 +185,6 @@ void modesSendAllClients(int service, void *msg, int len) { modesFreeClient(c); } } - c = next; } } // @@ -944,10 +930,7 @@ void modesReadFromClients(void) { struct client *c = modesAcceptClients(); - while (c) { - // Read next before servicing client incase the service routine deletes the client! - struct client *next = c->next; - + for (; c; c = c->next) { if (c->service == Modes.ris) { modesReadFromClient(c,"\n",decodeHexMessage); } else if (c->service == Modes.bis) { @@ -955,9 +938,26 @@ void modesReadFromClients(void) { } else if (c->service == Modes.https) { modesReadFromClient(c,"\r\n\r\n",handleHTTPRequest); } - c = next; } } + +// +// Perform cleanup of any recently-closed clients. +// +void modesNetCleanup(void) { + struct client *c, **prev; + + for (prev = &Modes.clients, c = *prev; c; c = *prev) { + if (c->fd == -1) { + // Recently closed, prune from list + *prev = c->next; + free(c); + } else { + prev = &c->next; + } + } + } + // // =============================== Network IO =========================== // From 1769ac9006df910811d8cf2ce556169035ff6610 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 3 Oct 2014 22:55:21 +0100 Subject: [PATCH 021/610] Restructuring of network output side. Mostly refactoring the common code that was duplicated between the different output types so that there aren't many copies floating around. This introduces "struct net_writer" to store the state of a particular type of output service - buffers, time of last write, connection count etc. prepareWrite() / completeWrite() give access to the buffer and handle the actual writes and flushing when needed. Heartbeat and time-based flushing move into a generic periodic-work function. Update the SBS output code to use the new infrastructure. This makes a big different to CPU use when under load. --- dump1090.c | 30 +++---- dump1090.h | 44 ++++----- mode_s.c | 40 --------- net_io.c | 256 ++++++++++++++++++++++++++++++++++++----------------- 4 files changed, 213 insertions(+), 157 deletions(-) diff --git a/dump1090.c b/dump1090.c index 368635271..cc46f3d1b 100644 --- a/dump1090.c +++ b/dump1090.c @@ -70,7 +70,7 @@ void modesInitConfig(void) { Modes.freq = MODES_DEFAULT_FREQ; Modes.ppm_error = MODES_DEFAULT_PPM; Modes.check_crc = 1; - Modes.net_heartbeat_rate = MODES_NET_HEARTBEAT_RATE; + Modes.net_heartbeat_interval = MODES_NET_HEARTBEAT_INTERVAL; Modes.net_output_sbs_port = MODES_NET_OUTPUT_SBS_PORT; Modes.net_output_raw_port = MODES_NET_OUTPUT_RAW_PORT; Modes.net_input_raw_port = MODES_NET_INPUT_RAW_PORT; @@ -97,9 +97,7 @@ void modesInit(void) { 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.maglut = (uint16_t *) malloc(sizeof(uint16_t) * 256 * 256) ) == NULL) || - ((Modes.beastOut = (char *) malloc(MODES_RAWOUT_BUF_SIZE) ) == NULL) || - ((Modes.rawOut = (char *) malloc(MODES_RAWOUT_BUF_SIZE) ) == NULL) ) + ((Modes.maglut = (uint16_t *) malloc(sizeof(uint16_t) * 256 * 256) ) == NULL) ) { fprintf(stderr, "Out of memory allocating data buffer.\n"); exit(1); @@ -130,10 +128,10 @@ void modesInit(void) { } // Limit the maximum requested raw output size to less than one Ethernet Block - if (Modes.net_output_raw_size > (MODES_RAWOUT_BUF_FLUSH)) - {Modes.net_output_raw_size = MODES_RAWOUT_BUF_FLUSH;} - if (Modes.net_output_raw_rate > (MODES_RAWOUT_BUF_RATE)) - {Modes.net_output_raw_rate = MODES_RAWOUT_BUF_RATE;} + if (Modes.net_output_flush_size > (MODES_OUT_FLUSH_SIZE)) + {Modes.net_output_flush_size = MODES_OUT_FLUSH_SIZE;} + if (Modes.net_output_flush_interval > (MODES_OUT_FLUSH_INTERVAL)) + {Modes.net_output_flush_interval = MODES_OUT_FLUSH_INTERVAL;} if (Modes.net_sndbuf_size > (MODES_NET_SNDBUF_MAX)) {Modes.net_sndbuf_size = MODES_NET_SNDBUF_MAX;} @@ -407,7 +405,6 @@ void showHelp(void) { "--raw Show only messages hex values\n" "--net Enable networking\n" "--modeac Enable decoding of SSR Modes 3/A & 3/C\n" -"--net-beast TCP raw output in Beast binary format\n" "--net-only Enable just networking, no RTL device or file used\n" "--net-http-port HTTP server port (default: 8080)\n" "--net-ri-port TCP raw input listen port (default: 30001)\n" @@ -415,8 +412,8 @@ void showHelp(void) { "--net-sbs-port TCP BaseStation output listen port (default: 30003)\n" "--net-bi-port TCP Beast input listen port (default: 30004)\n" "--net-bo-port TCP Beast output listen port (default: 30005)\n" -"--net-ro-size TCP raw output minimum size (default: 0)\n" -"--net-ro-rate TCP raw output memory flush rate (default: 0)\n" +"--net-ro-size TCP output minimum size (default: 0)\n" +"--net-ro-interval TCP output memory flush rate in seconds (default: 0)\n" "--net-heartbeat TCP heartbeat rate in seconds (default: 60 sec; 0 to disable)\n" "--net-buffer TCP buffer size 64Kb * (2^n) (default: n=0, 64Kb)\n" "--lat Reference/receiver latitude for surface posn (opt)\n" @@ -572,8 +569,7 @@ void backgroundTasks(void) { static time_t next_stats; if (Modes.net) { - modesReadFromClients(); - modesNetCleanup(); + modesNetPeriodicWork(); } // If Modes.aircrafts is not NULL, remove any stale aircraft @@ -701,11 +697,13 @@ int main(int argc, char **argv) { Modes.net = 1; Modes.net_only = 1; } else if (!strcmp(argv[j],"--net-heartbeat") && more) { - Modes.net_heartbeat_rate = atoi(argv[++j]) * 15; + Modes.net_heartbeat_interval = atoi(argv[++j]); } else if (!strcmp(argv[j],"--net-ro-size") && more) { - Modes.net_output_raw_size = atoi(argv[++j]); + Modes.net_output_flush_size = atoi(argv[++j]); } else if (!strcmp(argv[j],"--net-ro-rate") && more) { - Modes.net_output_raw_rate = atoi(argv[++j]); + Modes.net_output_flush_interval = atoi(argv[++j]) / 15; // backwards compatibility + } else if (!strcmp(argv[j],"--net-ro-interval") && more) { + Modes.net_output_flush_interval = atoi(argv[++j]); } else if (!strcmp(argv[j],"--net-ro-port") && more) { if (Modes.beast) // Required for legacy backward compatibility {Modes.net_output_beast_port = atoi(argv[++j]);;} diff --git a/dump1090.h b/dump1090.h index 5f45b08cb..fe1f8bfb3 100644 --- a/dump1090.h +++ b/dump1090.h @@ -116,9 +116,9 @@ #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_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 +#define MODES_OUT_BUF_SIZE (1500) +#define MODES_OUT_FLUSH_SIZE (MODES_OUT_BUF_SIZE - 256) +#define MODES_OUT_FLUSH_INTERVAL (60) #define MODES_ICAO_CACHE_LEN 1024 // Power of two required #define MODES_ICAO_CACHE_TTL 60 // Time to live of cached addresses @@ -165,7 +165,7 @@ #define MODES_INTERACTIVE_DELETE_TTL 300 // Delete from the list after 300 seconds #define MODES_INTERACTIVE_DISPLAY_TTL 60 // Delete from display after 60 seconds -#define MODES_NET_HEARTBEAT_RATE 900 // Each block is approx 65mS - default is > 1 min +#define MODES_NET_HEARTBEAT_INTERVAL 60 // seconds #define MODES_NET_SERVICES_NUM 6 #define MODES_NET_INPUT_RAW_PORT 30001 @@ -237,6 +237,15 @@ struct stDF { unsigned char msg[MODES_LONG_MSG_BYTES]; // the binary } tDF; +// Common writer state for all output sockets of one type +struct net_writer { + int socket; // listening socket FD, used to identify the owning service + int connections; // number of active clients + void *data; // shared write buffer, sized MODES_OUT_BUF_SIZE + int dataUsed; // number of bytes of write buffer currently used + time_t lastWrite; // time of last write to clients +}; + // Program global state struct { // Internal state pthread_t reader_thread; @@ -270,16 +279,14 @@ struct { // Internal state // Networking char aneterr[ANET_ERR_LEN]; struct client *clients; // Our clients - int sbsos; // SBS output listening socket - int ros; // Raw output listening socket int ris; // Raw input listening socket - int bos; // Beast output listening socket int bis; // Beast input listening socket int https; // HTTP listening socket - char *rawOut; // Buffer for building raw output data - int rawOutUsed; // How much of the buffer is currently used - char *beastOut; // Buffer for building beast output data - int beastOutUsed; // How much if the buffer is currently used + + struct net_writer raw_out; // Raw output + struct net_writer beast_out; // Beast-format output + struct net_writer sbs_out; // SBS-format output + #ifdef _WIN32 WSADATA wsaData; // Windows socket initialisation #endif @@ -295,12 +302,10 @@ struct { // Internal state int debug; // Debugging mode int net; // Enable networking int net_only; // Enable just networking - int net_heartbeat_count; // TCP heartbeat counter - int net_heartbeat_rate; // TCP heartbeat rate + int net_heartbeat_interval; // TCP heartbeat interval (seconds) int net_output_sbs_port; // SBS output TCP port - int net_output_raw_size; // Minimum Size of the output raw data - int net_output_raw_rate; // Rate (in 64mS increments) of output raw data - int net_output_raw_rate_count; // Rate (in 64mS increments) of output raw data + int net_output_flush_size; // Minimum Size of output data + int net_output_flush_interval; // Maximum interval (in seconds) between outputwrites int net_output_raw_port; // Raw output TCP port int net_input_raw_port; // Raw input TCP port int net_output_beast_port; // Beast output TCP port @@ -348,9 +353,6 @@ struct { // Internal state unsigned int stat_bit_fix[MODES_MAX_BITERRORS]; unsigned int stat_http_requests; - unsigned int stat_sbs_connections; - unsigned int stat_raw_connections; - unsigned int stat_beast_connections; unsigned int stat_out_of_phase; unsigned int stat_ph_demodulated0; unsigned int stat_ph_demodulated1; @@ -453,11 +455,9 @@ struct stDF *interactiveFindDF (uint32_t addr); // Functions exported from net_io.c // void modesInitNet (void); -void modesReadFromClients (void); -void modesSendAllClients (int service, void *msg, int len); void modesQueueOutput (struct modesMessage *mm); void modesReadFromClient(struct client *c, char *sep, int(*handler)(struct client *, char *)); -void modesNetCleanup (void); +void modesNetPeriodicWork (void); #ifdef __cplusplus } diff --git a/mode_s.c b/mode_s.c index bb0da2c40..a1014c6e5 100644 --- a/mode_s.c +++ b/mode_s.c @@ -1849,43 +1849,6 @@ void detectModeS(uint16_t *m, uint32_t mlen) { use_correction = 0; } } - - //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; - } } // //========================================================================= @@ -1910,9 +1873,6 @@ void useModesMessage(struct modesMessage *mm) { // Feed output clients if (Modes.net) {modesQueueOutput(mm);} - - // Heartbeat not required whilst we're seeing real messages - Modes.net_heartbeat_count = 0; } } // diff --git a/net_io.c b/net_io.c index b092c3306..294a37df7 100644 --- a/net_io.c +++ b/net_io.c @@ -49,6 +49,7 @@ struct service { char *descr; int *socket; + struct net_writer *writer; int port; int enabled; }; @@ -58,14 +59,14 @@ struct service services[MODES_NET_SERVICES_NUM]; void modesInitNet(void) { int j; - struct service svc[MODES_NET_SERVICES_NUM] = { - {"Raw TCP output", &Modes.ros, Modes.net_output_raw_port, 1}, - {"Raw TCP input", &Modes.ris, Modes.net_input_raw_port, 1}, - {"Beast TCP output", &Modes.bos, Modes.net_output_beast_port, 1}, - {"Beast TCP input", &Modes.bis, Modes.net_input_beast_port, 1}, - {"HTTP server", &Modes.https, Modes.net_http_port, 1}, - {"Basestation TCP output", &Modes.sbsos, Modes.net_output_sbs_port, 1} - }; + struct service svc[MODES_NET_SERVICES_NUM] = { + {"Raw TCP output", &Modes.raw_out.socket, &Modes.raw_out, Modes.net_output_raw_port, 1}, + {"Raw TCP input", &Modes.ris, NULL, Modes.net_input_raw_port, 1}, + {"Beast TCP output", &Modes.beast_out.socket, &Modes.beast_out, Modes.net_output_beast_port, 1}, + {"Beast TCP input", &Modes.bis, NULL, Modes.net_input_beast_port, 1}, + {"HTTP server", &Modes.https, NULL, Modes.net_http_port, 1}, + {"Basestation TCP output", &Modes.sbs_out.socket, &Modes.sbs_out, Modes.net_output_sbs_port, 1} + }; memcpy(&services, &svc, sizeof(svc));//services = svc; @@ -93,6 +94,18 @@ void modesInitNet(void) { } anetNonBlock(Modes.aneterr, s); *services[j].socket = s; + + if (services[j].writer) { + if (! (services[j].writer->data = malloc(MODES_OUT_BUF_SIZE)) ) { + fprintf(stderr, "Out of memory allocating output buffer for service %s\n", services[j].descr); + exit(1); + } + + services[j].writer->socket = s; + services[j].writer->connections = 0; + services[j].writer->dataUsed = 0; + services[j].writer->lastWrite = time(NULL); + } } else { if (Modes.debug & MODES_DEBUG_NET) printf("%s port is disabled\n", services[j].descr); } @@ -127,9 +140,11 @@ struct client * modesAcceptClients(void) { Modes.clients = c; anetSetSendBuffer(Modes.aneterr,fd, (MODES_NET_SNDBUF_SIZE << Modes.net_sndbuf_size)); - if (*services[j].socket == Modes.sbsos) Modes.stat_sbs_connections++; - if (*services[j].socket == Modes.ros) Modes.stat_raw_connections++; - if (*services[j].socket == Modes.bos) Modes.stat_beast_connections++; + if (services[j].writer) { + if (++ services[j].writer->connections == 1) { + services[j].writer->lastWrite = time(NULL); // suppress heartbeat initially + } + } j--; // Try again with the same listening port @@ -145,18 +160,21 @@ struct client * modesAcceptClients(void) { // On error free the client, collect the structure, adjust maxfd if needed. // void modesFreeClient(struct client *c) { + int j; + // Clean up, but defer removing from the list until modesNetCleanup(). // This is because there may be stackframes still pointing at this // client (unpredictably: reading from client A may cause client B to // be freed) close(c->fd); - if (c->service == Modes.sbsos) { - if (Modes.stat_sbs_connections) Modes.stat_sbs_connections--; - } else if (c->service == Modes.ros) { - if (Modes.stat_raw_connections) Modes.stat_raw_connections--; - } else if (c->service == Modes.bos) { - if (Modes.stat_beast_connections) Modes.stat_beast_connections--; + + for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { + if (c->service == *services[j].socket) { + if (services[j].writer) + services[j].writer->connections--; + break; + } } if (Modes.debug & MODES_DEBUG_NET) @@ -169,36 +187,72 @@ void modesFreeClient(struct client *c) { // //========================================================================= // -// Send the specified message to all clients listening for a given service +// Send the write buffer for the specified writer to all connected clients // -void modesSendAllClients(int service, void *msg, int len) { +static void flushWrites(struct net_writer *writer) { struct client *c; for (c = Modes.clients; c; c = c->next) { - if (c->service == service) { + if (c->service == writer->socket) { #ifndef _WIN32 - int nwritten = write(c->fd, msg, len); + int nwritten = write(c->fd, writer->data, writer->dataUsed); #else - int nwritten = send(c->fd, msg, len, 0 ); + int nwritten = send(c->fd, writer->data, writer->dataUsed, 0 ); #endif - if (nwritten != len) { + if (nwritten != writer->dataUsed) { modesFreeClient(c); } } } + + writer->dataUsed = 0; + writer->lastWrite = time(NULL); +} + +// Prepare to write up to 'len' bytes to the given net_writer. +// Returns a pointer to write to, or NULL to skip this write. +static void *prepareWrite(struct net_writer *writer, int len) { + if (!writer || + !writer->connections || + !writer->data) + return NULL; + + if (len >= MODES_OUT_BUF_SIZE) + return NULL; + + if (writer->dataUsed + len >= MODES_OUT_BUF_SIZE) { + // Flush now to free some space + flushWrites(writer); + } + + return writer->data + writer->dataUsed; +} + +// Complete a write previously begun by prepareWrite. +// endptr should point one byte past the last byte written +// to the buffer returned from prepareWrite. +static void completeWrite(struct net_writer *writer, void *endptr) { + writer->dataUsed = endptr - writer->data; + + if (writer->dataUsed >= Modes.net_output_flush_size) { + flushWrites(writer); + } } + // //========================================================================= // // Write raw output in Beast Binary format with Timestamp to TCP clients // void modesSendBeastOutput(struct modesMessage *mm) { - char *p = &Modes.beastOut[Modes.beastOutUsed]; int msgLen = mm->msgbits / 8; + char *p = prepareWrite(&Modes.beast_out, 2 + 2 * (7 + msgLen)); char * pTimeStamp; char ch; int j; - int iOutLen = msgLen + 9; // Escape, msgtype, timestamp, sigLevel, msg + + if (!p) + return; *p++ = 0x1a; if (msgLen == MODES_SHORT_MSG_BYTES) @@ -213,36 +267,34 @@ void modesSendBeastOutput(struct modesMessage *mm) { pTimeStamp = (char *) &mm->timestampMsg; for (j = 5; j >= 0; j--) { *p++ = (ch = pTimeStamp[j]); - if (0x1A == ch) {*p++ = ch; iOutLen++;} + if (0x1A == ch) {*p++ = ch; } } *p++ = (ch = mm->signalLevel); - if (0x1A == ch) {*p++ = ch; iOutLen++;} + if (0x1A == ch) {*p++ = ch; } for (j = 0; j < msgLen; j++) { *p++ = (ch = mm->msg[j]); - if (0x1A == ch) {*p++ = ch; iOutLen++;} + if (0x1A == ch) {*p++ = ch; } } - Modes.beastOutUsed += iOutLen; - if (Modes.beastOutUsed >= Modes.net_output_raw_size) - { - modesSendAllClients(Modes.bos, Modes.beastOut, Modes.beastOutUsed); - Modes.beastOutUsed = 0; - Modes.net_output_raw_rate_count = 0; - } + completeWrite(&Modes.beast_out, p); } + // //========================================================================= // // Write raw output to TCP clients // void modesSendRawOutput(struct modesMessage *mm) { - char *p = &Modes.rawOut[Modes.rawOutUsed]; int msgLen = mm->msgbits / 8; + char *p = prepareWrite(&Modes.raw_out, msgLen*2 + 15); int j; unsigned char * pTimeStamp; + if (!p) + return; + if (Modes.mlat && mm->timestampMsg) { *p++ = '@'; pTimeStamp = (unsigned char *) &mm->timestampMsg; @@ -250,7 +302,6 @@ void modesSendRawOutput(struct modesMessage *mm) { sprintf(p, "%02X", pTimeStamp[j]); p += 2; } - Modes.rawOutUsed += 12; // additional 12 characters for timestamp } else *p++ = '*'; @@ -262,13 +313,7 @@ void modesSendRawOutput(struct modesMessage *mm) { *p++ = ';'; *p++ = '\n'; - Modes.rawOutUsed += ((msgLen*2) + 3); - if (Modes.rawOutUsed >= Modes.net_output_raw_size) - { - modesSendAllClients(Modes.ros, Modes.rawOut, Modes.rawOutUsed); - Modes.rawOutUsed = 0; - Modes.net_output_raw_rate_count = 0; - } + completeWrite(&Modes.raw_out, p); } // //========================================================================= @@ -277,17 +322,27 @@ void modesSendRawOutput(struct modesMessage *mm) { // The message structure mm->bFlags tells us what has been updated by this message // void modesSendSBSOutput(struct modesMessage *mm) { - char msg[256], *p = msg; + char *p = prepareWrite(&Modes.sbs_out, 200); uint32_t offset; struct timeb epocTime_receive, epocTime_now; struct tm stTime_receive, stTime_now; int msgType; + if (!p) + return; + // // SBS BS style output checked against the following reference // http://www.homepages.mcb.net/bones/SBS/Article/Barebones42_Socket_Data.htm - seems comprehensive // + if (mm->msgtype == -1) { + // heartbeat + p += sprintf(p, "\r\n"); + completeWrite(&Modes.sbs_out, p); + return; + } + // Decide on the basic SBS Message Type if ((mm->msgtype == 4) || (mm->msgtype == 20)) { msgType = 5; @@ -434,15 +489,16 @@ void modesSendSBSOutput(struct modesMessage *mm) { } p += sprintf(p, "\r\n"); - modesSendAllClients(Modes.sbsos, msg, p-msg); + + completeWrite(&Modes.sbs_out, p); } // //========================================================================= // void modesQueueOutput(struct modesMessage *mm) { - if (Modes.stat_sbs_connections) {modesSendSBSOutput(mm);} - if (Modes.stat_beast_connections) {modesSendBeastOutput(mm);} - if (Modes.stat_raw_connections) {modesSendRawOutput(mm);} + modesSendSBSOutput(mm); + modesSendBeastOutput(mm); + modesSendRawOutput(mm); } // //========================================================================= @@ -928,43 +984,85 @@ void modesReadFromClient(struct client *c, char *sep, } } } + // -//========================================================================= -// -// Read data from clients. This function actually delegates a lower-level -// function that depends on the kind of service (raw, http, ...). +// Perform periodic network work // -void modesReadFromClients(void) { +void modesNetPeriodicWork(void) { + struct client *c, **prev; + time_t now = time(NULL); + int j; + int need_heartbeat = 0, need_flush = 0; - struct client *c = modesAcceptClients(); + // Accept new connetions + modesAcceptClients(); - for (; c; c = c->next) { - if (c->service == Modes.ris) { - modesReadFromClient(c,"\n",decodeHexMessage); - } else if (c->service == Modes.bis) { - modesReadFromClient(c,"",decodeBinMessage); - } else if (c->service == Modes.https) { - modesReadFromClient(c,"\r\n\r\n",handleHTTPRequest); + // Read from clients + for (c = Modes.clients; c; c = c->next) { + if (c->service == Modes.ris) { + modesReadFromClient(c,"\n",decodeHexMessage); + } else if (c->service == Modes.bis) { + modesReadFromClient(c,"",decodeBinMessage); + } else if (c->service == Modes.https) { + modesReadFromClient(c,"\r\n\r\n",handleHTTPRequest); + } + } + + // If we have generated no messages for a while, generate + // a dummy heartbeat message. + if (Modes.net_heartbeat_interval) { + for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { + if (services[j].writer && + services[j].writer->connections && + (services[j].writer->lastWrite + Modes.net_heartbeat_interval) <= now) { + need_flush = 1; + if (services[j].writer->dataUsed == 0) { + need_heartbeat = 1; + break; + } + } + } } - } -} -// -// Perform cleanup of any recently-closed clients. -// -void modesNetCleanup(void) { - struct client *c, **prev; + if (need_heartbeat) { + // + // We haven't sent any traffic 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 + struct modesMessage mm; + + memset(&mm, 0, sizeof(mm)); + mm.msgbits = MODES_SHORT_MSG_BITS; + mm.timestampMsg = 0; + mm.msgtype = -1; + + // Feed output clients + modesQueueOutput(&mm); + } - for (prev = &Modes.clients, c = *prev; c; c = *prev) { - if (c->fd == -1) { - // Recently closed, prune from list - *prev = c->next; - free(c); - } else { - prev = &c->next; - } - } - } + // If we have data that has been waiting to be written for a while, + // write it now. + for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { + if (services[j].writer && + services[j].writer->dataUsed && + (need_flush || (services[j].writer->lastWrite + Modes.net_output_flush_interval) <= now)) { + flushWrites(services[j].writer); + } + } + + // Unlink and free closed clients + for (prev = &Modes.clients, c = *prev; c; c = *prev) { + if (c->fd == -1) { + // Recently closed, prune from list + *prev = c->next; + free(c); + } else { + prev = &c->next; + } + } +} // // =============================== Network IO =========================== From 6910a4bf4dc84ce6c67158ab4c38efb34705a92e Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sun, 23 Nov 2014 16:02:11 +0000 Subject: [PATCH 022/610] Experimental json changes --- dump1090.c | 21 +++++++++++++++++++++ dump1090.h | 5 +++++ net_io.c | 41 +++++++++++++++++++++++++++++++++++++++-- public_html/script.js | 2 +- 4 files changed, 66 insertions(+), 3 deletions(-) diff --git a/dump1090.c b/dump1090.c index 60882fc73..00d907fea 100644 --- a/dump1090.c +++ b/dump1090.c @@ -82,6 +82,7 @@ void modesInitConfig(void) { Modes.interactive_display_ttl = MODES_INTERACTIVE_DISPLAY_TTL; Modes.fUserLat = MODES_USER_LATITUDE_DFLT; Modes.fUserLon = MODES_USER_LONGITUDE_DFLT; + Modes.json_interval = 1; } // //========================================================================= @@ -436,6 +437,8 @@ void showHelp(void) { "--debug Debug mode (verbose), see README for details\n" "--quiet Disable output to stdout. Use for daemon applications\n" "--ppm Set receiver error in parts per million (default 0)\n" +"--write-json Periodically write json output to (for serving by a separate webserver)\n" +"--write-json-every Write json output every t seconds (default 1)\n" "--help Show this help\n" "\n" "Debug mode flags: d = Log frames decoded with errors\n" @@ -571,6 +574,7 @@ static void display_stats(void) { // void backgroundTasks(void) { static time_t next_stats; + static time_t next_json; if (Modes.net) { modesReadFromClients(); @@ -594,6 +598,14 @@ void backgroundTasks(void) { next_stats = now + Modes.stats; } } + + if (Modes.json_path && Modes.json_interval > 0) { + time_t now = time(NULL); + if (now > next_json) { + modesWriteJson(Modes.json_path); + next_json = now + Modes.json_interval; + } + } } // //========================================================================= @@ -778,6 +790,15 @@ int main(int argc, char **argv) { } else if (!strcmp(argv[j],"--interactive-rtl1090")) { Modes.interactive = 1; Modes.interactive_rtl1090 = 1; +#ifndef _WIN32 + } else if (!strcmp(argv[j], "--write-json") && more) { + ++j; + Modes.json_path = malloc(strlen(argv[j]) + 11); + strcpy(Modes.json_path, argv[j]); + strcat(Modes.json_path, "/aircraft.json"); + } else if (!strcmp(argv[j], "--write-json-every") && more) { + Modes.json_interval = atoi(argv[++j]); +#endif } else { fprintf(stderr, "Unknown or not enough arguments for option '%s'.\n\n", diff --git a/dump1090.h b/dump1090.h index 9ad4de037..d38e32cb7 100644 --- a/dump1090.h +++ b/dump1090.h @@ -57,6 +57,7 @@ #include #include #include + #include #include "rtl-sdr.h" #include "anet.h" #else @@ -318,6 +319,9 @@ struct { // Internal state int metric; // Use metric units int mlat; // Use Beast ascii format for raw data output, i.e. @...; iso *...; int interactive_rtl1090; // flight table in interactive mode is formatted like RTL1090 + + char *json_path; // Path to json data file to write, or NULL not to. + int json_interval; // Interval between rewriting the json data file // User details double fUserLat; // Users receiver/antenna lat/lon needed for initial surface location @@ -458,6 +462,7 @@ void modesReadFromClients (void); void modesSendAllClients (int service, void *msg, int len); void modesQueueOutput (struct modesMessage *mm); void modesReadFromClient(struct client *c, char *sep, int(*handler)(struct client *, char *)); +void modesWriteJson (const char *path); #ifdef __cplusplus } diff --git a/net_io.c b/net_io.c index a979883f5..85246fc5f 100644 --- a/net_io.c +++ b/net_io.c @@ -694,6 +694,43 @@ char *aircraftsToJson(int *len) { *len = p-buf; return buf; } + +// Write JSON state to json_path +void modesWriteJson(const char *path) +{ +#ifndef _WIN32 + char tmppath[PATH_MAX]; + int fd; + int len = 0; + char *content; + + snprintf(tmppath, PATH_MAX, "%s.XXXXXX", path); + tmppath[PATH_MAX-1] = 0; + fd = mkstemp(tmppath); + if (fd < 0) + return; + + content = aircraftsToJson(&len); + if (write(fd, content, len) != len) + goto error_1; + + if (close(fd) < 0) + goto error_2; + + free(content); + rename(tmppath, path); + return; + + error_1: + close(fd); + error_2: + free(content); + unlink(tmppath); + return; + +#endif +} + // //========================================================================= // @@ -754,8 +791,8 @@ int handleHTTPRequest(struct client *c, char *p) { // Select the content to send, we have just two so far: // "/" -> Our google map application. - // "/data.json" -> Our ajax request to update planes. - if (strstr(url, "/data.json")) { + // "/aircraft.json" -> Our ajax request to update planes. + if (strstr(url, "/data/aircraft.json")) { statuscode = 200; content = aircraftsToJson(&clen); //snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON); diff --git a/public_html/script.js b/public_html/script.js index 8212e5026..cf12e41a9 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -18,7 +18,7 @@ CenterLon = Number(localStorage['CenterLon']) || CONST_CENTERLON; ZoomLvl = Number(localStorage['ZoomLvl']) || CONST_ZOOMLVL; function fetchData() { - $.getJSON('/dump1090/data.json', function(data) { + $.getJSON('data/aircraft.json', function(data) { PlanesOnMap = 0 SpecialSquawk = false; From d0174994cbf43b0c2f17352f0ce4bb49acf8c45f Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sun, 23 Nov 2014 16:32:22 +0000 Subject: [PATCH 023/610] Fix path buffer size --- dump1090.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dump1090.c b/dump1090.c index 00d907fea..56420707a 100644 --- a/dump1090.c +++ b/dump1090.c @@ -793,7 +793,7 @@ int main(int argc, char **argv) { #ifndef _WIN32 } else if (!strcmp(argv[j], "--write-json") && more) { ++j; - Modes.json_path = malloc(strlen(argv[j]) + 11); + Modes.json_path = malloc(strlen(argv[j]) + 15); strcpy(Modes.json_path, argv[j]); strcat(Modes.json_path, "/aircraft.json"); } else if (!strcmp(argv[j], "--write-json-every") && more) { From ebab2c0e1127aae044f5b77fe6ba8ec7e3bb51ac Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sun, 23 Nov 2014 16:32:45 +0000 Subject: [PATCH 024/610] Create json files with mode 0644 - umask. mkstemp defaults to 0600 which is not so useful for serving the file. --- net_io.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net_io.c b/net_io.c index 85246fc5f..ad970b01b 100644 --- a/net_io.c +++ b/net_io.c @@ -703,6 +703,7 @@ void modesWriteJson(const char *path) int fd; int len = 0; char *content; + mode_t mask; snprintf(tmppath, PATH_MAX, "%s.XXXXXX", path); tmppath[PATH_MAX-1] = 0; @@ -710,6 +711,10 @@ void modesWriteJson(const char *path) if (fd < 0) return; + mask = umask(0); + umask(mask); + fchmod(fd, 0644 & ~mask); + content = aircraftsToJson(&len); if (write(fd, content, len) != len) goto error_1; From 2abc3868950d5388542a68977c5fe41878a47a28 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sun, 23 Nov 2014 17:22:34 +0000 Subject: [PATCH 025/610] Off-by-one error in the json interval calculation. --- dump1090.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dump1090.c b/dump1090.c index 56420707a..a530a6a55 100644 --- a/dump1090.c +++ b/dump1090.c @@ -601,7 +601,7 @@ void backgroundTasks(void) { if (Modes.json_path && Modes.json_interval > 0) { time_t now = time(NULL); - if (now > next_json) { + if (now >= next_json) { modesWriteJson(Modes.json_path); next_json = now + Modes.json_interval; } From 19eed4cf18b2007e5ec6b5d09a4944e940c24864 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 5 Dec 2014 19:33:46 +0000 Subject: [PATCH 026/610] Allow external specification of CFLAGS / CPPFLAGS. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 6bfe9ee14..61ab749f5 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ SHAREDIR=$(PREFIX)/share/$(PROGNAME) EXTRACFLAGS=-DHTMLPATH=\"$(SHAREDIR)\" endif -CFLAGS=-O2 -g -Wall -W `pkg-config --cflags librtlsdr` +CFLAGS+=-O2 -g -Wall -W `pkg-config --cflags librtlsdr` LIBS=`pkg-config --libs librtlsdr` -lpthread -lm CC=gcc @@ -18,7 +18,7 @@ CC=gcc all: dump1090 view1090 %.o: %.c - $(CC) $(CFLAGS) $(EXTRACFLAGS) -c $< + $(CC) $(CPPFLAGS) $(CFLAGS) $(EXTRACFLAGS) -c $< dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o $(CC) -g -o dump1090 dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o $(LIBS) $(LDFLAGS) From 83c80b6d672da952e1da60a6813402b318e62956 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 5 Dec 2014 19:34:40 +0000 Subject: [PATCH 027/610] Don't link view1090 with librtlsdr, it doesn't need it. --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 61ab749f5..dd734876a 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,8 @@ EXTRACFLAGS=-DHTMLPATH=\"$(SHAREDIR)\" endif CFLAGS+=-O2 -g -Wall -W `pkg-config --cflags librtlsdr` -LIBS=`pkg-config --libs librtlsdr` -lpthread -lm +LIBS=-lpthread -lm +LIBS_RTL=`pkg-config --libs librtlsdr` CC=gcc @@ -21,7 +22,7 @@ all: dump1090 view1090 $(CC) $(CPPFLAGS) $(CFLAGS) $(EXTRACFLAGS) -c $< dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o - $(CC) -g -o dump1090 dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o $(LIBS) $(LDFLAGS) + $(CC) -g -o dump1090 dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o $(LIBS) $(LIBS_RTL) $(LDFLAGS) view1090: view1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o $(CC) -g -o view1090 view1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o $(LIBS) $(LDFLAGS) From 0ae1d72e83afcd099494c34c24848e9e56a2bb1f Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 5 Dec 2014 20:18:57 +0000 Subject: [PATCH 028/610] Initial work on raspbian packaging. --- debian/changelog | 5 +++++ debian/compat | 1 + debian/control | 20 ++++++++++++++++++++ debian/copyright | 37 +++++++++++++++++++++++++++++++++++++ debian/dump1090-mr.install | 3 +++ debian/rules | 18 ++++++++++++++++++ debian/source/format | 1 + 7 files changed, 85 insertions(+) create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/dump1090-mr.install create mode 100755 debian/rules create mode 100644 debian/source/format diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 000000000..f18cab6a3 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +dump1090-mr (1.08.2302.14+1-1) UNRELEASED; urgency=medium + + * Initial package release. + + -- Oliver Jowett Fri, 05 Dec 2014 20:12:40 +0000 diff --git a/debian/compat b/debian/compat new file mode 100644 index 000000000..45a4fb75d --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +8 diff --git a/debian/control b/debian/control new file mode 100644 index 000000000..012b733ea --- /dev/null +++ b/debian/control @@ -0,0 +1,20 @@ +Source: dump1090-mr +Section: embedded +Priority: extra +Maintainer: Oliver Jowett +Build-Depends: debhelper(>=8), net-tools, librtlsdr-dev +Standards-Version: 3.9.3 +Homepage: https://github.com/MalcolmRobb/dump1090 +Vcs-Git: https://github.com/mutability/dump1090.git -b mr-pi-package + +Package: dump1090-mr +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, net-tools, adduser +Description: ADS-B Ground Station System for RTL-SDR + Networked Aviation Mode S / ADS-B decoder/translator with RTL-SDR software + defined radio USB device support. + . + Includes a mini-webserver that you can access to see aircraft in the vicinity + of your receiver. + . + This is a packaging of MalcomRobb's fork of the original dump1090 version. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 000000000..ce72a3f86 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,37 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: dump1090 +Upstream-Contact: https://github.com/MalcolmRobb +Source: https://github.com/MalcolmRobb/dump1090 + +Files: * +Copyright: Salvatore Sanfilippo, Malcom Robb and other contributors. +License: BSD-3-Clause + +Files: debian/* +Copyright: 2014 Oliver Jowett +License: BSD-3-Clause + +License: BSD-3-Clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. 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. + 3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + . + 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 HOLDERS 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. diff --git a/debian/dump1090-mr.install b/debian/dump1090-mr.install new file mode 100644 index 000000000..a851311e1 --- /dev/null +++ b/debian/dump1090-mr.install @@ -0,0 +1,3 @@ +dump1090 usr/bin +view1090 usr/bin +public_html/* usr/share/dump1090 diff --git a/debian/rules b/debian/rules new file mode 100755 index 000000000..9430a6327 --- /dev/null +++ b/debian/rules @@ -0,0 +1,18 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +export DH_VERBOSE=1 + +export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +DPKG_EXPORT_BUILDFLAGS = 1 +include /usr/share/dpkg/buildflags.mk + +%: + dh $@ diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 000000000..89ae9db8f --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) From 35a382e3ce50e91781cc9ae75b2481c2c8ff1b9b Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 5 Dec 2014 21:36:02 +0000 Subject: [PATCH 029/610] Set HTMLPATH correctly when compiling. Put static files in /usr/share/dump1090-mr not /usr/share/dump1090 --- debian/dump1090-mr.install | 2 +- debian/rules | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/debian/dump1090-mr.install b/debian/dump1090-mr.install index a851311e1..6ca26a809 100644 --- a/debian/dump1090-mr.install +++ b/debian/dump1090-mr.install @@ -1,3 +1,3 @@ dump1090 usr/bin view1090 usr/bin -public_html/* usr/share/dump1090 +public_html/* usr/share/dump1090-mr diff --git a/debian/rules b/debian/rules index 9430a6327..251ca29d1 100755 --- a/debian/rules +++ b/debian/rules @@ -14,5 +14,7 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all DPKG_EXPORT_BUILDFLAGS = 1 include /usr/share/dpkg/buildflags.mk +override_dh_auto_build: + dh_auto_build -- 'EXTRACFLAGS=-DHTMLPATH=\"/usr/share/dump1090-mr\"' %: dh $@ From 77912fb0879da85a245ebb0589a9497ac5e5b622 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 5 Dec 2014 21:36:51 +0000 Subject: [PATCH 030/610] Install binaries with -mr suffix. --- debian/dump1090-mr.install | 2 -- debian/rules | 7 +++++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/debian/dump1090-mr.install b/debian/dump1090-mr.install index 6ca26a809..8feea8af9 100644 --- a/debian/dump1090-mr.install +++ b/debian/dump1090-mr.install @@ -1,3 +1 @@ -dump1090 usr/bin -view1090 usr/bin public_html/* usr/share/dump1090-mr diff --git a/debian/rules b/debian/rules index 251ca29d1..38f129b9d 100755 --- a/debian/rules +++ b/debian/rules @@ -16,5 +16,12 @@ include /usr/share/dpkg/buildflags.mk override_dh_auto_build: dh_auto_build -- 'EXTRACFLAGS=-DHTMLPATH=\"/usr/share/dump1090-mr\"' + +override_dh_install: + dh_install + install -d debian/dump1090-mr/usr/bin + cp -a dump1090 debian/dump1090-mr/usr/bin/dump1090-mr + cp -a view1090 debian/dump1090-mr/usr/bin/view1090-mr + %: dh $@ From 373ac8ef0cccd51249aec2961ff70cb47b245ad1 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 5 Dec 2014 22:37:33 +0000 Subject: [PATCH 031/610] Add init script, defaults from /etc/default/, postinst to create a user to run as. --- debian/dump1090-mr.default | 89 +++++++++++++++++ debian/dump1090-mr.init | 185 ++++++++++++++++++++++++++++++++++++ debian/dump1090-mr.postinst | 40 ++++++++ 3 files changed, 314 insertions(+) create mode 100644 debian/dump1090-mr.default create mode 100644 debian/dump1090-mr.init create mode 100644 debian/dump1090-mr.postinst diff --git a/debian/dump1090-mr.default b/debian/dump1090-mr.default new file mode 100644 index 000000000..8a96f0a84 --- /dev/null +++ b/debian/dump1090-mr.default @@ -0,0 +1,89 @@ +# Defaults for dump1090-mr +# This is a POSIX shell fragment + +# Set to "yes" to start dump1090 on boot. +START_DUMP1090="no" + +# User to run dump1090 as. +DUMP1090_USER="dump1090" + +# +# The following options are all optional - defaults if not provided are +# shown below. +# + +# +# Receiver options +# + +# RTLSDR device index to use +# If set to "none", dump1090 will be started in --net-only mode +#DEVICE=0 + +# RTLSDR gain in dB. +# If set to "max" (the default) the maximum supported gain is used. +# If set to "agc", the tuner AGC is used to set the gain. +#GAIN=max + +# RTLSDR frequency correction in PPM +#PPM=0 + +# +# Decoding options +# + +# If yes, fixes messages with correctable CRC errors. +# Otherwise, discards messages with errors. +#FIX_CRC=no + +# If yes, enables phase-enhancement of messages that fail to decode +# the first time around. +#PHASE_ENHANCE=no + +# If yes, enables aggressive fixes to damaged messages. +# Use with caution - it can increase the rate of undetected errors. +#AGGRESSIVE=no + +# If set, supplies a reference location for local position decoding. +#LAT=decimal.latitude.value +#LON=decimal.longitude.value + +# +# Networking options +# + +# Port to listen on for HTTP connections. 0 disables. +#HTTP_PORT=8080 + +# Port to listen on for raw (AVR-format) input connections. 0 disables. +#RAW_INPUT_PORT=30001 + +# Port to listen on for raw (AVR-format) output connections. 0 disables. +#RAW_OUTPUT_PORT=30002 + +# Port to listen on for SBS-format output connections. 0 disables. +#SBS_OUTPUT_PORT=30003 + +# Port to listen on for Beast-format input connections. 0 disables. +#BEAST_INPUT_PORT=30004 + +# Port to listen on for Beast-format output connections. 0 disables. +#BEAST_OUTPUT_PORT=30005 + +# TCP heartbeat interval in seconds. 0 disables. +#NET_HEARTBEAT=60 + +# Minimum output buffer size per write, in bytes. +#NET_OUTPUT_SIZE=5 + +# Maximum buffering time before writing. In units of approx 64ms (don't ask..). +#NET_OUTPUT_RATE=5 + +# TCP buffer size order. Power-of-two based - buffer size is 2^(n+16). +#NET_BUFFER=0 + +# Bind ports on a particular address. If unset, binds to all interfaces. +#BIND_ADDRESS= + +# Additional options that are passed to the Daemon. +#EXTRA_ARGS="" diff --git a/debian/dump1090-mr.init b/debian/dump1090-mr.init new file mode 100644 index 000000000..bf526b34f --- /dev/null +++ b/debian/dump1090-mr.init @@ -0,0 +1,185 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: dump1090-mr +# Required-Start: $remote_fs +# Required-Stop: $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: dump1090 daemon (MalcolmRobb variant) +# Description: Receives ADS-B messages from a RTLSDR dongle +# and makes them available to other applications via +# a variety of network protocols. +### END INIT INFO + +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="dump1090-mr daemon" +NAME=dump1090-mr +DAEMON=/usr/bin/$NAME +ARGS="--quiet" +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Set defaults: +START_DUMP1090=no +DUMP1090_USER=dump1090 +DEVICE=0 +GAIN=max +PPM=0 +FIX_CRC=no +PHASE_ENHANCE=no +AGGRESSIVE=no +LAT= +LON= +HTTP_PORT=8080 +RAW_INPUT_PORT=30001 +RAW_OUTPUT_PORT=30002 +SBS_OUTPUT_PORT=30003 +BEAST_INPUT_PORT=30004 +BEAST_OUTPUT_PORT=30005 +NET_HEARTBEAT=60 +NET_OUTPUT_SIZE=5 +NET_OUTPUT_RATE=5 +NET_BUFFER=0 +BIND_ADDRESS= +EXTRA_ARGS= + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# work out daemon args + +# receiver: +case "x$DEVICE" in + x|x0) ARGS="$ARGS --net" ;; + xnone) ARGS="$ARGS --net-only" ;; + *) ARGS="$ARGS --net --device-index $DEVICE" ;; +esac +case "x$GAIN" in + x|xmax) ;; + xagc) ARGS="$ARGS --gain -10" ;; + *) ARGS="$ARGS --gain $GAIN" ;; +esac +if [ "x$PPM" != "x0" ]; then ARGS="$ARGS --ppm $PPM"; fi + +# decoder: +if [ "x$FIX_CRC" = "xyes" ]; then ARGS="$ARGS --fix"; fi +if [ "x$PHASE_ENHANCE" = "xyes" ]; then ARGS="$ARGS --phase-enhance"; fi +if [ "x$AGGRESSIVE" = "xyes" ]; then ARGS="$ARGS --aggressive"; fi +if [ -n "$LAT" ]; then ARGS="$ARGS --lat $LAT"; fi +if [ -n "$LON" ]; then ARGS="$ARGS --lon $LON"; fi + +# net: +if [ "x$HTTP_PORT" != "x8080" ]; then ARGS="$ARGS --net-http-port $HTTP_PORT"; fi +if [ "x$RAW_INPUT_PORT" != "x30001" ]; then ARGS="$ARGS --net-ri-port $RAW_INPUT_PORT"; fi +if [ "x$RAW_OUTPUT_PORT" != "x30002" ]; then ARGS="$ARGS --net-ro-port $RAW_OUTPUT_PORT"; fi +if [ "x$SBS_OUTPUT_PORT" != "x30003" ]; then ARGS="$ARGS --net-sbs-port $SBS_OUTPUT_PORT"; fi +if [ "x$BEAST_INPUT_PORT" != "x30004" ]; then ARGS="$ARGS --net-bi-port $BEAST_INPUT_PORT"; fi +if [ "x$BEAST_OUTPUT_PORT" != "x30005" ]; then ARGS="$ARGS --net-bo-port $BEAST_OUTPUT_PORT"; fi +if [ "x$NET_HEARTBEAT" != "x60" ]; then ARGS="$ARGS --net-heartbeat $NET_HEARTBEAT"; fi +if [ "x$NET_OUTPUT_SIZE" != "x0" ]; then ARGS="$ARGS --net-ro-size $NET_OUTPUT_SIZE"; fi +if [ "x$NET_OUTPUT_RATE" != "x0" ]; then ARGS="$ARGS --net-ro-rate $NET_OUTPUT_RATE"; fi +if [ "x$NET_BUFFER" != "x0" ]; then ARGS="$ARGS --net-buffer $NET_BUFFER"; fi +if [ -n "$BIND_ADDRESS" ]; then ARGS="$ARGS --net-bind-address $BIND_ADDRESS"; fi +if [ -n "$EXTRA_ARGS" ]; then ARGS="$ARGS $EXTRA_ARGS"; fi + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + + if [ "x$START_DUMP1090" != "xyes" ]; then + log_warning_msg "Not starting $NAME daemon, disabled via /etc/default/$NAME" + return 0 + fi + + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet --pidfile $PIDFILE --chuid $DUMP1090_USER --make-pidfile --background --exec $DAEMON -- \ + $ARGS \ + || return 2 + sleep 1 +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --retry=TERM/30/KILL/5 --pidfile $PIDFILE --user $DUMP1090_USER --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + sleep 1 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC:" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC:" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + restart|force-reload) + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + exit 3 + ;; +esac + +: diff --git a/debian/dump1090-mr.postinst b/debian/dump1090-mr.postinst new file mode 100644 index 000000000..e727c02fe --- /dev/null +++ b/debian/dump1090-mr.postinst @@ -0,0 +1,40 @@ +#!/bin/sh +# postinst script for dump1090 +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + adduser --system --home /usr/share/dump1090-mr --no-create-home --quiet dump1090 + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 From b1e1878054e8d5172268750f7731b88513e45b62 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 5 Dec 2014 22:55:04 +0000 Subject: [PATCH 032/610] Fix some stray colons. Report failure if daemon isn't configured to start. --- debian/dump1090-mr.init | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/debian/dump1090-mr.init b/debian/dump1090-mr.init index bf526b34f..514068ff5 100644 --- a/debian/dump1090-mr.init +++ b/debian/dump1090-mr.init @@ -108,7 +108,7 @@ do_start() if [ "x$START_DUMP1090" != "xyes" ]; then log_warning_msg "Not starting $NAME daemon, disabled via /etc/default/$NAME" - return 0 + return 2 fi start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ @@ -140,7 +140,7 @@ do_stop() case "$1" in start) - [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC:" "$NAME" + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; @@ -148,7 +148,7 @@ case "$1" in esac ;; stop) - [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC:" "$NAME" + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; From b7ee4009e4ddeb942423a6c61a34d94788736732 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 5 Dec 2014 22:56:14 +0000 Subject: [PATCH 033/610] Release changelog. --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index f18cab6a3..3f500a0fc 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,5 @@ -dump1090-mr (1.08.2302.14+1-1) UNRELEASED; urgency=medium +dump1090-mr (1.08.2302.14+1-1) unstable; urgency=medium * Initial package release. - -- Oliver Jowett Fri, 05 Dec 2014 20:12:40 +0000 + -- Oliver Jowett Fri, 05 Dec 2014 22:55:41 +0000 From 7a3a9f500f0af8525a733afebeaddb9c2ce07eff Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 6 Dec 2014 16:06:39 +0000 Subject: [PATCH 034/610] Add logging support. --- debian/dump1090-mr.default | 3 +++ debian/dump1090-mr.init | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/debian/dump1090-mr.default b/debian/dump1090-mr.default index 8a96f0a84..f5e40a88f 100644 --- a/debian/dump1090-mr.default +++ b/debian/dump1090-mr.default @@ -7,6 +7,9 @@ START_DUMP1090="no" # User to run dump1090 as. DUMP1090_USER="dump1090" +# Logfile to log to +#LOGFILE="/var/log/dump1090-mr.log" + # # The following options are all optional - defaults if not provided are # shown below. diff --git a/debian/dump1090-mr.init b/debian/dump1090-mr.init index 514068ff5..cf03a31aa 100644 --- a/debian/dump1090-mr.init +++ b/debian/dump1090-mr.init @@ -28,6 +28,7 @@ SCRIPTNAME=/etc/init.d/$NAME # Set defaults: START_DUMP1090=no DUMP1090_USER=dump1090 +LOGFILE=/var/log/$NAME.log DEVICE=0 GAIN=max PPM=0 @@ -113,8 +114,8 @@ do_start() start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ || return 1 - start-stop-daemon --start --quiet --pidfile $PIDFILE --chuid $DUMP1090_USER --make-pidfile --background --exec $DAEMON -- \ - $ARGS \ + start-stop-daemon --start --quiet --pidfile $PIDFILE --chuid $DUMP1090_USER --make-pidfile --background --no-close --exec $DAEMON -- \ + $ARGS >>$LOGFILE 2>&1 \ || return 2 sleep 1 } From 2e189633dabb2333c1437dedf198960080323519 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 6 Dec 2014 16:07:02 +0000 Subject: [PATCH 035/610] Add a logrotate configuration. --- debian/dump1090-mr.logrotate | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 debian/dump1090-mr.logrotate diff --git a/debian/dump1090-mr.logrotate b/debian/dump1090-mr.logrotate new file mode 100644 index 000000000..f42118403 --- /dev/null +++ b/debian/dump1090-mr.logrotate @@ -0,0 +1,5 @@ +/var/log/dump1090-mr.log { + weekly + rotate 4 + copytruncate +} From 977fc07c393a742fa0fc2abe920e2a3c703aee15 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 6 Dec 2014 16:07:44 +0000 Subject: [PATCH 036/610] Pass --name and --user during daemon start so that existing processes can be correctly identified. --- debian/dump1090-mr.init | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/dump1090-mr.init b/debian/dump1090-mr.init index cf03a31aa..96f0ae48e 100644 --- a/debian/dump1090-mr.init +++ b/debian/dump1090-mr.init @@ -112,9 +112,9 @@ do_start() return 2 fi - start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ + start-stop-daemon --start --quiet --pidfile $PIDFILE --name $NAME --user $DUMP1090_USER --exec $DAEMON --test > /dev/null \ || return 1 - start-stop-daemon --start --quiet --pidfile $PIDFILE --chuid $DUMP1090_USER --make-pidfile --background --no-close --exec $DAEMON -- \ + start-stop-daemon --start --quiet --pidfile $PIDFILE --name $NAME --user $DUMP1090_USER --chuid $DUMP1090_USER --make-pidfile --background --no-close --exec $DAEMON -- \ $ARGS >>$LOGFILE 2>&1 \ || return 2 sleep 1 From 3242fcaf29ca591f9c8f135305386ae048c24515 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 6 Dec 2014 16:08:17 +0000 Subject: [PATCH 037/610] Add support for configuring --stats-every (defaults to hourly) --- debian/dump1090-mr.default | 7 +++++++ debian/dump1090-mr.init | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/debian/dump1090-mr.default b/debian/dump1090-mr.default index f5e40a88f..c5a4cce79 100644 --- a/debian/dump1090-mr.default +++ b/debian/dump1090-mr.default @@ -88,5 +88,12 @@ DUMP1090_USER="dump1090" # Bind ports on a particular address. If unset, binds to all interfaces. #BIND_ADDRESS= +# +# Misc options +# + +# Interval (in seconds) between logging stats to the logfile. 0 disables. +#STATS_INTERVAL=3600 + # Additional options that are passed to the Daemon. #EXTRA_ARGS="" diff --git a/debian/dump1090-mr.init b/debian/dump1090-mr.init index 96f0ae48e..77e70fcc3 100644 --- a/debian/dump1090-mr.init +++ b/debian/dump1090-mr.init @@ -48,6 +48,7 @@ NET_OUTPUT_SIZE=5 NET_OUTPUT_RATE=5 NET_BUFFER=0 BIND_ADDRESS= +STATS_INTERVAL=3600 EXTRA_ARGS= # Read configuration variable file if it is present @@ -87,6 +88,9 @@ if [ "x$NET_OUTPUT_SIZE" != "x0" ]; then ARGS="$ARGS --net-ro-size $NET_OUTPUT_S if [ "x$NET_OUTPUT_RATE" != "x0" ]; then ARGS="$ARGS --net-ro-rate $NET_OUTPUT_RATE"; fi if [ "x$NET_BUFFER" != "x0" ]; then ARGS="$ARGS --net-buffer $NET_BUFFER"; fi if [ -n "$BIND_ADDRESS" ]; then ARGS="$ARGS --net-bind-address $BIND_ADDRESS"; fi + +# misc: +if [ "x$STATS_INTERVAL" != "x0" ]; then ARGS="$ARGS --stats-every $STATS_INTERVAL"; fi if [ -n "$EXTRA_ARGS" ]; then ARGS="$ARGS $EXTRA_ARGS"; fi # Load the VERBOSE setting and other rcS variables From 786dd622ee07e3e2cac984afbdfce732c06e181f Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 6 Dec 2014 16:08:42 +0000 Subject: [PATCH 038/610] Update changelog for recent changes. --- debian/changelog | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/debian/changelog b/debian/changelog index 3f500a0fc..75a8e6bfb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +dump1090-mr (1.08.2302.14+1-2) UNRELEASED; urgency=medium + + * Pass --name / --user to start-stop-daemon when starting + * Log to /var/log/dump1090-mr.log (configurable via LOGFILE) + * Log stats once an hour by default (configurable via STATS_INTERVAL) + * Include a logrotate configuration + + -- Oliver Jowett Sat, 06 Dec 2014 15:36:58 +0000 + dump1090-mr (1.08.2302.14+1-1) unstable; urgency=medium * Initial package release. From d88c9d6ab3d6faaa3fdce27a2009135969832193 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 6 Dec 2014 18:37:36 +0000 Subject: [PATCH 039/610] Release changelog. --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 75a8e6bfb..3e071ad72 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,11 +1,11 @@ -dump1090-mr (1.08.2302.14+1-2) UNRELEASED; urgency=medium +dump1090-mr (1.08.2302.14+1-2) unstable; urgency=medium * Pass --name / --user to start-stop-daemon when starting * Log to /var/log/dump1090-mr.log (configurable via LOGFILE) * Log stats once an hour by default (configurable via STATS_INTERVAL) * Include a logrotate configuration - -- Oliver Jowett Sat, 06 Dec 2014 15:36:58 +0000 + -- Oliver Jowett Sat, 06 Dec 2014 18:37:12 +0000 dump1090-mr (1.08.2302.14+1-1) unstable; urgency=medium From b4b1c0b9209299f8fdac372f42c45e7810651fc5 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 6 Dec 2014 21:47:46 +0000 Subject: [PATCH 040/610] Update packaging to build a -mutability package. --- debian/changelog | 15 +++-------- debian/control | 13 +++++----- debian/copyright | 6 ++--- debian/dump1090-mr.install | 1 - ...mr.default => dump1090-mutability.default} | 17 +++++++++--- ...p1090-mr.init => dump1090-mutability.init} | 26 ++++++++++++++----- debian/dump1090-mutability.install | 2 ++ ...ogrotate => dump1090-mutability.logrotate} | 2 +- ....postinst => dump1090-mutability.postinst} | 2 +- debian/lighttpd/90-dump1090.conf | 13 ++++++++++ debian/rules | 8 +++--- 11 files changed, 67 insertions(+), 38 deletions(-) delete mode 100644 debian/dump1090-mr.install rename debian/{dump1090-mr.default => dump1090-mutability.default} (83%) rename debian/{dump1090-mr.init => dump1090-mutability.init} (85%) create mode 100644 debian/dump1090-mutability.install rename debian/{dump1090-mr.logrotate => dump1090-mutability.logrotate} (51%) rename debian/{dump1090-mr.postinst => dump1090-mutability.postinst} (90%) create mode 100644 debian/lighttpd/90-dump1090.conf diff --git a/debian/changelog b/debian/changelog index 3e071ad72..922504c16 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,14 +1,5 @@ -dump1090-mr (1.08.2302.14+1-2) unstable; urgency=medium +dump1090-mutability (1.08.2302.14+1-mu-1) UNRELEASED; urgency=medium - * Pass --name / --user to start-stop-daemon when starting - * Log to /var/log/dump1090-mr.log (configurable via LOGFILE) - * Log stats once an hour by default (configurable via STATS_INTERVAL) - * Include a logrotate configuration + * Initial release. - -- Oliver Jowett Sat, 06 Dec 2014 18:37:12 +0000 - -dump1090-mr (1.08.2302.14+1-1) unstable; urgency=medium - - * Initial package release. - - -- Oliver Jowett Fri, 05 Dec 2014 22:55:41 +0000 + -- Oliver Jowett Sat, 06 Dec 2014 19:44:44 +0000 diff --git a/debian/control b/debian/control index 012b733ea..d27ff82e5 100644 --- a/debian/control +++ b/debian/control @@ -1,13 +1,13 @@ -Source: dump1090-mr +Source: dump1090-mutability Section: embedded Priority: extra Maintainer: Oliver Jowett -Build-Depends: debhelper(>=8), net-tools, librtlsdr-dev +Build-Depends: debhelper(>=8), librtlsdr-dev Standards-Version: 3.9.3 -Homepage: https://github.com/MalcolmRobb/dump1090 -Vcs-Git: https://github.com/mutability/dump1090.git -b mr-pi-package +Homepage: https://github.com/mutability/dump1090 +Vcs-Git: https://github.com/mutability/dump1090.git -Package: dump1090-mr +Package: dump1090-mutability Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, net-tools, adduser Description: ADS-B Ground Station System for RTL-SDR @@ -17,4 +17,5 @@ Description: ADS-B Ground Station System for RTL-SDR Includes a mini-webserver that you can access to see aircraft in the vicinity of your receiver. . - This is a packaging of MalcomRobb's fork of the original dump1090 version. + This is a packaging of the "mutability" fork of dump1090 that includes + sampling at 2.4MHz and other improvements. diff --git a/debian/copyright b/debian/copyright index ce72a3f86..29d539b8a 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,10 +1,10 @@ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: dump1090 -Upstream-Contact: https://github.com/MalcolmRobb -Source: https://github.com/MalcolmRobb/dump1090 +Upstream-Contact: Oliver Jowett +Source: https://github.com/mutability/dump1090 Files: * -Copyright: Salvatore Sanfilippo, Malcom Robb and other contributors. +Copyright: Salvatore Sanfilippo, Malcom Robb, Oliver Jowett and other contributors. License: BSD-3-Clause Files: debian/* diff --git a/debian/dump1090-mr.install b/debian/dump1090-mr.install deleted file mode 100644 index 8feea8af9..000000000 --- a/debian/dump1090-mr.install +++ /dev/null @@ -1 +0,0 @@ -public_html/* usr/share/dump1090-mr diff --git a/debian/dump1090-mr.default b/debian/dump1090-mutability.default similarity index 83% rename from debian/dump1090-mr.default rename to debian/dump1090-mutability.default index c5a4cce79..6f10d61c1 100644 --- a/debian/dump1090-mr.default +++ b/debian/dump1090-mutability.default @@ -1,4 +1,4 @@ -# Defaults for dump1090-mr +# Defaults for dump1090-mutability # This is a POSIX shell fragment # Set to "yes" to start dump1090 on boot. @@ -8,7 +8,7 @@ START_DUMP1090="no" DUMP1090_USER="dump1090" # Logfile to log to -#LOGFILE="/var/log/dump1090-mr.log" +#LOGFILE="/var/log/dump1090-mutability.log" # # The following options are all optional - defaults if not provided are @@ -31,6 +31,9 @@ DUMP1090_USER="dump1090" # RTLSDR frequency correction in PPM #PPM=0 +# If yes, enable sampling at 2.4MHz. Otherwise, 2.0MHz is used. +#OVERSAMPLE=no + # # Decoding options # @@ -79,8 +82,8 @@ DUMP1090_USER="dump1090" # Minimum output buffer size per write, in bytes. #NET_OUTPUT_SIZE=5 -# Maximum buffering time before writing. In units of approx 64ms (don't ask..). -#NET_OUTPUT_RATE=5 +# Maximum buffering time before writing, in seconds. +#NET_OUTPUT_INTERVAL=1 # TCP buffer size order. Power-of-two based - buffer size is 2^(n+16). #NET_BUFFER=0 @@ -95,5 +98,11 @@ DUMP1090_USER="dump1090" # Interval (in seconds) between logging stats to the logfile. 0 disables. #STATS_INTERVAL=3600 +# Path to write json state to (for use with an external webserver). Blank disables. +#JSON_DIR=/run/dump1090-mutability + +# Interval between writing json state (in seconds). 0 disables. +#JSON_INTERVAL=1 + # Additional options that are passed to the Daemon. #EXTRA_ARGS="" diff --git a/debian/dump1090-mr.init b/debian/dump1090-mutability.init similarity index 85% rename from debian/dump1090-mr.init rename to debian/dump1090-mutability.init index 77e70fcc3..5ee95be2f 100644 --- a/debian/dump1090-mr.init +++ b/debian/dump1090-mutability.init @@ -1,11 +1,11 @@ #!/bin/sh ### BEGIN INIT INFO -# Provides: dump1090-mr +# Provides: dump1090-mutability # Required-Start: $remote_fs # Required-Stop: $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 -# Short-Description: dump1090 daemon (MalcolmRobb variant) +# Short-Description: dump1090 daemon (mutability variant) # Description: Receives ADS-B messages from a RTLSDR dongle # and makes them available to other applications via # a variety of network protocols. @@ -15,8 +15,8 @@ # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/sbin:/usr/sbin:/bin:/usr/bin -DESC="dump1090-mr daemon" -NAME=dump1090-mr +DESC="dump1090-mutability daemon" +NAME=dump1090-mutability DAEMON=/usr/bin/$NAME ARGS="--quiet" PIDFILE=/var/run/$NAME.pid @@ -32,6 +32,7 @@ LOGFILE=/var/log/$NAME.log DEVICE=0 GAIN=max PPM=0 +OVERSAMPLE=no FIX_CRC=no PHASE_ENHANCE=no AGGRESSIVE=no @@ -45,7 +46,7 @@ BEAST_INPUT_PORT=30004 BEAST_OUTPUT_PORT=30005 NET_HEARTBEAT=60 NET_OUTPUT_SIZE=5 -NET_OUTPUT_RATE=5 +NET_OUTPUT_INTERVAL=1 NET_BUFFER=0 BIND_ADDRESS= STATS_INTERVAL=3600 @@ -68,6 +69,7 @@ case "x$GAIN" in *) ARGS="$ARGS --gain $GAIN" ;; esac if [ "x$PPM" != "x0" ]; then ARGS="$ARGS --ppm $PPM"; fi +if [ "x$OVERSAMPLE" != "x0" ]; then ARGS="$ARGS --oversample"; fi # decoder: if [ "x$FIX_CRC" = "xyes" ]; then ARGS="$ARGS --fix"; fi @@ -85,12 +87,14 @@ if [ "x$BEAST_INPUT_PORT" != "x30004" ]; then ARGS="$ARGS --net-bi-port $BEAST_I if [ "x$BEAST_OUTPUT_PORT" != "x30005" ]; then ARGS="$ARGS --net-bo-port $BEAST_OUTPUT_PORT"; fi if [ "x$NET_HEARTBEAT" != "x60" ]; then ARGS="$ARGS --net-heartbeat $NET_HEARTBEAT"; fi if [ "x$NET_OUTPUT_SIZE" != "x0" ]; then ARGS="$ARGS --net-ro-size $NET_OUTPUT_SIZE"; fi -if [ "x$NET_OUTPUT_RATE" != "x0" ]; then ARGS="$ARGS --net-ro-rate $NET_OUTPUT_RATE"; fi +if [ "x$NET_OUTPUT_INTERVAL" != "x0" ]; then ARGS="$ARGS --net-ro-interval $NET_OUTPUT_INTERVAL"; fi if [ "x$NET_BUFFER" != "x0" ]; then ARGS="$ARGS --net-buffer $NET_BUFFER"; fi if [ -n "$BIND_ADDRESS" ]; then ARGS="$ARGS --net-bind-address $BIND_ADDRESS"; fi # misc: if [ "x$STATS_INTERVAL" != "x0" ]; then ARGS="$ARGS --stats-every $STATS_INTERVAL"; fi +if [ "x$JSON_DIR" != "x" ]; then ARGS="$ARGS --write-json $JSON_DIR"; fi +if [ "x$JSON_INTERVAL" != "x1" ]; then ARGS="$ARGS --write-json-every $JSON_INTERVAL"; fi if [ -n "$EXTRA_ARGS" ]; then ARGS="$ARGS $EXTRA_ARGS"; fi # Load the VERBOSE setting and other rcS variables @@ -118,6 +122,16 @@ do_start() start-stop-daemon --start --quiet --pidfile $PIDFILE --name $NAME --user $DUMP1090_USER --exec $DAEMON --test > /dev/null \ || return 1 + + + # create JSON_DIR with the appropriate permissions + # (it is on /run by default, so will be lost on reboot) + if [ "x$JSON_DIR" != "x" ]; then + if [ ! -e $JSON_DIR ]; then + (mkdir $JSON_DIR && chmod 0755 $JSON_DIR && chown $DUMP1090_USER $JSON_DIR) || log_warning_msg "Failed to create $JSON_DIR" + fi + fi + start-stop-daemon --start --quiet --pidfile $PIDFILE --name $NAME --user $DUMP1090_USER --chuid $DUMP1090_USER --make-pidfile --background --no-close --exec $DAEMON -- \ $ARGS >>$LOGFILE 2>&1 \ || return 2 diff --git a/debian/dump1090-mutability.install b/debian/dump1090-mutability.install new file mode 100644 index 000000000..d1e444b25 --- /dev/null +++ b/debian/dump1090-mutability.install @@ -0,0 +1,2 @@ +public_html/* usr/share/dump1090-mutability +debian/lighttpd/* etc/lighttpd/conf-available diff --git a/debian/dump1090-mr.logrotate b/debian/dump1090-mutability.logrotate similarity index 51% rename from debian/dump1090-mr.logrotate rename to debian/dump1090-mutability.logrotate index f42118403..0a4f6ad56 100644 --- a/debian/dump1090-mr.logrotate +++ b/debian/dump1090-mutability.logrotate @@ -1,4 +1,4 @@ -/var/log/dump1090-mr.log { +/var/log/dump1090-mutability.log { weekly rotate 4 copytruncate diff --git a/debian/dump1090-mr.postinst b/debian/dump1090-mutability.postinst similarity index 90% rename from debian/dump1090-mr.postinst rename to debian/dump1090-mutability.postinst index e727c02fe..b0fd8f3b8 100644 --- a/debian/dump1090-mr.postinst +++ b/debian/dump1090-mutability.postinst @@ -20,7 +20,7 @@ set -e case "$1" in configure) - adduser --system --home /usr/share/dump1090-mr --no-create-home --quiet dump1090 + adduser --system --home /usr/share/dump1090-mutability --no-create-home --quiet dump1090 ;; abort-upgrade|abort-remove|abort-deconfigure) diff --git a/debian/lighttpd/90-dump1090.conf b/debian/lighttpd/90-dump1090.conf new file mode 100644 index 000000000..a12f0ebaf --- /dev/null +++ b/debian/lighttpd/90-dump1090.conf @@ -0,0 +1,13 @@ +# Allows access to the static files that provide the dump1090 map view, +# and also to the dynamically-generated json parts that contain aircraft +# data and are periodically written by the dump1090 daemon. + +url.redirect += ( + "^/dump1090/$" => "/dump1090/gmap.html", + "^/dump1090$" => "/dump1090/gmap.html" +) + +alias.url += ( + "/dump1090/data/" => "/run/dump1090-mutability/", + "/dump1090/" => "/usr/share/dump1090-mutability/" +) diff --git a/debian/rules b/debian/rules index 38f129b9d..cf81ca191 100755 --- a/debian/rules +++ b/debian/rules @@ -15,13 +15,13 @@ DPKG_EXPORT_BUILDFLAGS = 1 include /usr/share/dpkg/buildflags.mk override_dh_auto_build: - dh_auto_build -- 'EXTRACFLAGS=-DHTMLPATH=\"/usr/share/dump1090-mr\"' + dh_auto_build -- 'EXTRACFLAGS=-DHTMLPATH=\"/usr/share/dump1090-mutability\"' override_dh_install: dh_install - install -d debian/dump1090-mr/usr/bin - cp -a dump1090 debian/dump1090-mr/usr/bin/dump1090-mr - cp -a view1090 debian/dump1090-mr/usr/bin/view1090-mr + install -d debian/dump1090-mutability/usr/bin + cp -a dump1090 debian/dump1090-mutability/usr/bin/dump1090-mutability + cp -a view1090 debian/dump1090-mutability/usr/bin/view1090-mutability %: dh $@ From aa4aadbccc9486050049944af781d78c01d5ed68 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 6 Dec 2014 21:51:08 +0000 Subject: [PATCH 041/610] Can't make up my mind about versions! --- debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 922504c16..22cfc068d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -dump1090-mutability (1.08.2302.14+1-mu-1) UNRELEASED; urgency=medium +dump1090-mutability (1.08.2302.14+1mu-1) UNRELEASED; urgency=medium * Initial release. From 796ca80db8716fd45ff5c0c75bbef35abefaf660 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 6 Dec 2014 22:19:24 +0000 Subject: [PATCH 042/610] Increase the priority of the lighttpd config file to work around an odd ordering problem with the default debian-doc configuration which would mangle the alias list when accessing from localhost. --- debian/lighttpd/{90-dump1090.conf => 89-dump1090.conf} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename debian/lighttpd/{90-dump1090.conf => 89-dump1090.conf} (100%) diff --git a/debian/lighttpd/90-dump1090.conf b/debian/lighttpd/89-dump1090.conf similarity index 100% rename from debian/lighttpd/90-dump1090.conf rename to debian/lighttpd/89-dump1090.conf From 034fcee8faf8d0416744cad1f967fb0c1e19552f Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 6 Dec 2014 22:20:50 +0000 Subject: [PATCH 043/610] Remove a spurious dependency on net-tools. --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index d27ff82e5..0e445365d 100644 --- a/debian/control +++ b/debian/control @@ -9,7 +9,7 @@ Vcs-Git: https://github.com/mutability/dump1090.git Package: dump1090-mutability Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, net-tools, adduser +Depends: ${shlibs:Depends}, ${misc:Depends}, adduser Description: ADS-B Ground Station System for RTL-SDR Networked Aviation Mode S / ADS-B decoder/translator with RTL-SDR software defined radio USB device support. From e5931cbd5024847bf0938062444bc6fe084813f3 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 6 Dec 2014 22:22:02 +0000 Subject: [PATCH 044/610] Various packaging bugfixes found while testing the package. --- debian/dump1090-mutability.init | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/debian/dump1090-mutability.init b/debian/dump1090-mutability.init index 5ee95be2f..1334eaddd 100644 --- a/debian/dump1090-mutability.init +++ b/debian/dump1090-mutability.init @@ -50,6 +50,8 @@ NET_OUTPUT_INTERVAL=1 NET_BUFFER=0 BIND_ADDRESS= STATS_INTERVAL=3600 +JSON_DIR=/run/$NAME +JSON_INTERVAL=1 EXTRA_ARGS= # Read configuration variable file if it is present @@ -69,7 +71,7 @@ case "x$GAIN" in *) ARGS="$ARGS --gain $GAIN" ;; esac if [ "x$PPM" != "x0" ]; then ARGS="$ARGS --ppm $PPM"; fi -if [ "x$OVERSAMPLE" != "x0" ]; then ARGS="$ARGS --oversample"; fi +if [ "x$OVERSAMPLE" = "yes" ]; then ARGS="$ARGS --oversample"; fi # decoder: if [ "x$FIX_CRC" = "xyes" ]; then ARGS="$ARGS --fix"; fi @@ -120,10 +122,9 @@ do_start() return 2 fi - start-stop-daemon --start --quiet --pidfile $PIDFILE --name $NAME --user $DUMP1090_USER --exec $DAEMON --test > /dev/null \ + start-stop-daemon --start --quiet --pidfile $PIDFILE --user $DUMP1090_USER --exec $DAEMON --test > /dev/null \ || return 1 - # create JSON_DIR with the appropriate permissions # (it is on /run by default, so will be lost on reboot) if [ "x$JSON_DIR" != "x" ]; then @@ -132,7 +133,7 @@ do_start() fi fi - start-stop-daemon --start --quiet --pidfile $PIDFILE --name $NAME --user $DUMP1090_USER --chuid $DUMP1090_USER --make-pidfile --background --no-close --exec $DAEMON -- \ + start-stop-daemon --start --quiet --pidfile $PIDFILE --user $DUMP1090_USER --chuid $DUMP1090_USER --make-pidfile --background --no-close --exec $DAEMON -- \ $ARGS >>$LOGFILE 2>&1 \ || return 2 sleep 1 @@ -148,7 +149,7 @@ do_stop() # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred - start-stop-daemon --stop --retry=TERM/30/KILL/5 --pidfile $PIDFILE --user $DUMP1090_USER --name $NAME + start-stop-daemon --stop --retry=TERM/30/KILL/5 --pidfile $PIDFILE --user $DUMP1090_USER --exec $DAEMON RETVAL="$?" [ "$RETVAL" = 2 ] && return 2 sleep 1 From 127bf0cbc2ba0cc6c32a0663bcf833368e44a52c Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 6 Dec 2014 22:23:13 +0000 Subject: [PATCH 045/610] Lock down the default config - no remote connections, no HTTP server. The user can reconfigure if they want a more open setup. Add Suggests: lighttpd and recommend that as the way to run the map interface. --- debian/control | 4 +--- debian/dump1090-mutability.default | 11 +++++++++-- debian/dump1090-mutability.init | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/debian/control b/debian/control index 0e445365d..aebccd784 100644 --- a/debian/control +++ b/debian/control @@ -10,12 +10,10 @@ Vcs-Git: https://github.com/mutability/dump1090.git Package: dump1090-mutability Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, adduser +Suggests: lighttpd Description: ADS-B Ground Station System for RTL-SDR Networked Aviation Mode S / ADS-B decoder/translator with RTL-SDR software defined radio USB device support. . - Includes a mini-webserver that you can access to see aircraft in the vicinity - of your receiver. - . This is a packaging of the "mutability" fork of dump1090 that includes sampling at 2.4MHz and other improvements. diff --git a/debian/dump1090-mutability.default b/debian/dump1090-mutability.default index 6f10d61c1..117292106 100644 --- a/debian/dump1090-mutability.default +++ b/debian/dump1090-mutability.default @@ -59,7 +59,12 @@ DUMP1090_USER="dump1090" # # Port to listen on for HTTP connections. 0 disables. -#HTTP_PORT=8080 +# HTTP defaults to being disabled unless you specify something here. I +# that you do not enable this, and instead serve the contents of +# /usr/share/dump1090-mutability and JSON_DIR (below) using a proper +# webserver. See /etc/lighttpd/conf-available/90-dump1090.conf +# for an example configuration ("sudo lighty-enable-mod dump1090" to enable) +#HTTP_PORT=0 # Port to listen on for raw (AVR-format) input connections. 0 disables. #RAW_INPUT_PORT=30001 @@ -89,7 +94,9 @@ DUMP1090_USER="dump1090" #NET_BUFFER=0 # Bind ports on a particular address. If unset, binds to all interfaces. -#BIND_ADDRESS= +# This defaults to binding to localhost. If you need to allow remote +# connections, change this. +#BIND_ADDRESS=127.0.0.1 # # Misc options diff --git a/debian/dump1090-mutability.init b/debian/dump1090-mutability.init index 1334eaddd..c755b2ac4 100644 --- a/debian/dump1090-mutability.init +++ b/debian/dump1090-mutability.init @@ -38,7 +38,7 @@ PHASE_ENHANCE=no AGGRESSIVE=no LAT= LON= -HTTP_PORT=8080 +HTTP_PORT=0 RAW_INPUT_PORT=30001 RAW_OUTPUT_PORT=30002 SBS_OUTPUT_PORT=30003 @@ -48,7 +48,7 @@ NET_HEARTBEAT=60 NET_OUTPUT_SIZE=5 NET_OUTPUT_INTERVAL=1 NET_BUFFER=0 -BIND_ADDRESS= +BIND_ADDRESS=127.0.0.1 STATS_INTERVAL=3600 JSON_DIR=/run/$NAME JSON_INTERVAL=1 From 129655625c3a1ee7954d6cbbc5c6ac8b368755e6 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 6 Dec 2014 22:50:55 +0000 Subject: [PATCH 046/610] Fix typo in oversampling config check. --- debian/dump1090-mutability.init | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/dump1090-mutability.init b/debian/dump1090-mutability.init index c755b2ac4..54eb3a13d 100644 --- a/debian/dump1090-mutability.init +++ b/debian/dump1090-mutability.init @@ -71,7 +71,7 @@ case "x$GAIN" in *) ARGS="$ARGS --gain $GAIN" ;; esac if [ "x$PPM" != "x0" ]; then ARGS="$ARGS --ppm $PPM"; fi -if [ "x$OVERSAMPLE" = "yes" ]; then ARGS="$ARGS --oversample"; fi +if [ "x$OVERSAMPLE" = "xyes" ]; then ARGS="$ARGS --oversample"; fi # decoder: if [ "x$FIX_CRC" = "xyes" ]; then ARGS="$ARGS --fix"; fi From fcf31a164a530ed67963aea1672d443c61882e7f Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 6 Dec 2014 22:54:40 +0000 Subject: [PATCH 047/610] More sensible NET_OUTPUT_SIZE / NET_BUFFER defaults. --- debian/dump1090-mutability.default | 4 ++-- debian/dump1090-mutability.init | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/debian/dump1090-mutability.default b/debian/dump1090-mutability.default index 117292106..1017adbd9 100644 --- a/debian/dump1090-mutability.default +++ b/debian/dump1090-mutability.default @@ -85,13 +85,13 @@ DUMP1090_USER="dump1090" #NET_HEARTBEAT=60 # Minimum output buffer size per write, in bytes. -#NET_OUTPUT_SIZE=5 +#NET_OUTPUT_SIZE=500 # Maximum buffering time before writing, in seconds. #NET_OUTPUT_INTERVAL=1 # TCP buffer size order. Power-of-two based - buffer size is 2^(n+16). -#NET_BUFFER=0 +#NET_BUFFER=4 # Bind ports on a particular address. If unset, binds to all interfaces. # This defaults to binding to localhost. If you need to allow remote diff --git a/debian/dump1090-mutability.init b/debian/dump1090-mutability.init index 54eb3a13d..e6e8e947d 100644 --- a/debian/dump1090-mutability.init +++ b/debian/dump1090-mutability.init @@ -45,9 +45,9 @@ SBS_OUTPUT_PORT=30003 BEAST_INPUT_PORT=30004 BEAST_OUTPUT_PORT=30005 NET_HEARTBEAT=60 -NET_OUTPUT_SIZE=5 +NET_OUTPUT_SIZE=500 NET_OUTPUT_INTERVAL=1 -NET_BUFFER=0 +NET_BUFFER=4 BIND_ADDRESS=127.0.0.1 STATS_INTERVAL=3600 JSON_DIR=/run/$NAME From 2c374503f0ff46208eb31b0964b9e102c2a18fbe Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 6 Dec 2014 23:07:16 +0000 Subject: [PATCH 048/610] Release changelog. --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 22cfc068d..e9878c626 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,5 @@ -dump1090-mutability (1.08.2302.14+1mu-1) UNRELEASED; urgency=medium +dump1090-mutability (1.08.2302.14+1mu-1) unstable; urgency=medium * Initial release. - -- Oliver Jowett Sat, 06 Dec 2014 19:44:44 +0000 + -- Oliver Jowett Sat, 06 Dec 2014 23:07:02 +0000 From d09eefb827d2209a0669345e34b2fcc8558247e5 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sun, 7 Dec 2014 14:05:24 +0000 Subject: [PATCH 049/610] Fix a memory leak from use of realpath() in HTTP request processing. realpath() returns a heap-allocated buffer if given NULL for the destination buffer. This must be freed by the caller; dump1090 does not do this. Instead of worrying about freeing it, take the simpler approach of just providing a stack-allocated destination buffer. --- dump1090.h | 1 + net_io.c | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/dump1090.h b/dump1090.h index 9ad4de037..eea553b41 100644 --- a/dump1090.h +++ b/dump1090.h @@ -57,6 +57,7 @@ #include #include #include + #include #include "rtl-sdr.h" #include "anet.h" #else diff --git a/net_io.c b/net_io.c index a979883f5..c73232d38 100644 --- a/net_io.c +++ b/net_io.c @@ -762,14 +762,16 @@ int handleHTTPRequest(struct client *c, char *p) { } else { struct stat sbuf; int fd = -1; - char *rp, *hrp; + char rp[PATH_MAX], hrp[PATH_MAX]; + + if (!realpath(getFile, rp)) + rp[0] = 0; + if (!realpath(HTMLPATH, hrp)) + strcpy(hrp, HTMLPATH); - rp = realpath(getFile, NULL); - hrp = realpath(HTMLPATH, NULL); - hrp = (hrp ? hrp : HTMLPATH); clen = -1; content = strdup("Server error occured"); - if (rp && (!strncmp(hrp, rp, strlen(hrp)))) { + if (!strncmp(hrp, rp, strlen(hrp))) { if (stat(getFile, &sbuf) != -1 && (fd = open(getFile, O_RDONLY)) != -1) { content = (char *) realloc(content, sbuf.st_size); if (read(fd, content, sbuf.st_size) != -1) { From 66849e1096b59515fa2623d1179445101a2b6e73 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sun, 7 Dec 2014 14:14:51 +0000 Subject: [PATCH 050/610] Changelog update. --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index e9878c626..0ceed80ad 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +dump1090-mutability (1.08.2302.14+1mu-2) UNRELEASED; urgency=medium + + * Fix a memory leak from use of realpath() in HTTP request processing. + + -- Oliver Jowett Sun, 07 Dec 2014 14:14:25 +0000 + dump1090-mutability (1.08.2302.14+1mu-1) unstable; urgency=medium * Initial release. From 2dcc8e352460759902a9767b0e6633f88b99fd6c Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Mon, 8 Dec 2014 19:56:45 +0000 Subject: [PATCH 051/610] Add direct support for FATSV-format output. This is adapted from the FlightAware fork, with some cleanup and modifications needed to work with the net-cleanup changes. Inclusion of "verbatim" TSV data read from an AVR-format input connection is not supported. --- debian/changelog | 2 + debian/dump1090-mutability.default | 3 + debian/dump1090-mutability.init | 2 + dump1090.c | 4 + dump1090.h | 9 +- net_io.c | 156 ++++++++++++++++++++++++++++- 6 files changed, 173 insertions(+), 3 deletions(-) diff --git a/debian/changelog b/debian/changelog index 0ceed80ad..e8ecf272d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,8 @@ dump1090-mutability (1.08.2302.14+1mu-2) UNRELEASED; urgency=medium * Fix a memory leak from use of realpath() in HTTP request processing. + * Add direct support for FATSV-format output, adapted from the FlightAware + fork. -- Oliver Jowett Sun, 07 Dec 2014 14:14:25 +0000 diff --git a/debian/dump1090-mutability.default b/debian/dump1090-mutability.default index 1017adbd9..d14386b9c 100644 --- a/debian/dump1090-mutability.default +++ b/debian/dump1090-mutability.default @@ -81,6 +81,9 @@ DUMP1090_USER="dump1090" # Port to listen on for Beast-format output connections. 0 disables. #BEAST_OUTPUT_PORT=30005 +# Port to listen on for FATSV-format output connections. 0 disables. +#FATSV_OUTPUT_PORT=10001 + # TCP heartbeat interval in seconds. 0 disables. #NET_HEARTBEAT=60 diff --git a/debian/dump1090-mutability.init b/debian/dump1090-mutability.init index e6e8e947d..2afeb5534 100644 --- a/debian/dump1090-mutability.init +++ b/debian/dump1090-mutability.init @@ -44,6 +44,7 @@ RAW_OUTPUT_PORT=30002 SBS_OUTPUT_PORT=30003 BEAST_INPUT_PORT=30004 BEAST_OUTPUT_PORT=30005 +FATSV_OUTPUT_PORT=10001 NET_HEARTBEAT=60 NET_OUTPUT_SIZE=500 NET_OUTPUT_INTERVAL=1 @@ -87,6 +88,7 @@ if [ "x$RAW_OUTPUT_PORT" != "x30002" ]; then ARGS="$ARGS --net-ro-port $RAW_OUTP if [ "x$SBS_OUTPUT_PORT" != "x30003" ]; then ARGS="$ARGS --net-sbs-port $SBS_OUTPUT_PORT"; fi if [ "x$BEAST_INPUT_PORT" != "x30004" ]; then ARGS="$ARGS --net-bi-port $BEAST_INPUT_PORT"; fi if [ "x$BEAST_OUTPUT_PORT" != "x30005" ]; then ARGS="$ARGS --net-bo-port $BEAST_OUTPUT_PORT"; fi +if [ "x$FATSV_OUTPUT_PORT" != "x10001" ]; then ARGS="$ARGS --net-fatsv-port $FATSV_OUTPUT_PORT"; fi if [ "x$NET_HEARTBEAT" != "x60" ]; then ARGS="$ARGS --net-heartbeat $NET_HEARTBEAT"; fi if [ "x$NET_OUTPUT_SIZE" != "x0" ]; then ARGS="$ARGS --net-ro-size $NET_OUTPUT_SIZE"; fi if [ "x$NET_OUTPUT_INTERVAL" != "x0" ]; then ARGS="$ARGS --net-ro-interval $NET_OUTPUT_INTERVAL"; fi diff --git a/dump1090.c b/dump1090.c index d868f6d82..7c06e0eb2 100644 --- a/dump1090.c +++ b/dump1090.c @@ -77,6 +77,7 @@ void modesInitConfig(void) { Modes.net_output_beast_port = MODES_NET_OUTPUT_BEAST_PORT; Modes.net_input_beast_port = MODES_NET_INPUT_BEAST_PORT; Modes.net_http_port = MODES_NET_HTTP_PORT; + Modes.net_fatsv_port = MODES_NET_OUTPUT_FA_TSV_PORT; Modes.interactive_rows = getTermRows(); Modes.interactive_delete_ttl = MODES_INTERACTIVE_DELETE_TTL; Modes.interactive_display_ttl = MODES_INTERACTIVE_DISPLAY_TTL; @@ -424,6 +425,7 @@ void showHelp(void) { "--net-sbs-port TCP BaseStation output listen port (default: 30003)\n" "--net-bi-port TCP Beast input listen port (default: 30004)\n" "--net-bo-port TCP Beast output listen port (default: 30005)\n" +"--net-fatsv-port FlightAware TSV output port (default: 10001)\n" "--net-ro-size TCP output minimum size (default: 0)\n" "--net-ro-interval TCP output memory flush rate in seconds (default: 0)\n" "--net-heartbeat TCP heartbeat rate in seconds (default: 60 sec; 0 to disable)\n" @@ -767,6 +769,8 @@ int main(int argc, char **argv) { Modes.net_bind_address = strdup(argv[++j]); } else if (!strcmp(argv[j],"--net-http-port") && more) { Modes.net_http_port = atoi(argv[++j]); + } else if (!strcmp(argv[j],"--net-fatsv-port") && more) { + Modes.net_fatsv_port = atoi(argv[++j]); } else if (!strcmp(argv[j],"--net-sbs-port") && more) { Modes.net_output_sbs_port = atoi(argv[++j]); } else if (!strcmp(argv[j],"--net-buffer") && more) { diff --git a/dump1090.h b/dump1090.h index 2dfd49141..f7ee3eccc 100644 --- a/dump1090.h +++ b/dump1090.h @@ -179,13 +179,14 @@ #define MODES_NET_HEARTBEAT_INTERVAL 60 // seconds -#define MODES_NET_SERVICES_NUM 6 +#define MODES_NET_SERVICES_NUM 7 #define MODES_NET_INPUT_RAW_PORT 30001 #define MODES_NET_OUTPUT_RAW_PORT 30002 #define MODES_NET_OUTPUT_SBS_PORT 30003 #define MODES_NET_INPUT_BEAST_PORT 30004 #define MODES_NET_OUTPUT_BEAST_PORT 30005 #define MODES_NET_HTTP_PORT 8080 +#define MODES_NET_OUTPUT_FA_TSV_PORT 10001 #define MODES_CLIENT_BUF_SIZE 1024 #define MODES_NET_SNDBUF_SIZE (1024*64) #define MODES_NET_SNDBUF_MAX (7) @@ -227,6 +228,10 @@ struct aircraft { long modeCcount; // Mode C Altitude hit Count int modeACflags; // Flags for mode A/C recognition + int fatsv_emitted_altitude; // last FA emitted altitude + int fatsv_emitted_track; // last FA emitted angle of flight + time_t fatsv_last_emitted; // time aircraft was last FA emitted + // Encoded latitude and longitude as extracted by odd and even CPR encoded messages int odd_cprlat; int odd_cprlon; @@ -317,6 +322,7 @@ struct { // Internal state struct net_writer raw_out; // Raw output struct net_writer beast_out; // Beast-format output struct net_writer sbs_out; // SBS-format output + struct net_writer fatsv_out; // FATSV-format output #ifdef _WIN32 WSADATA wsaData; // Windows socket initialisation @@ -344,6 +350,7 @@ struct { // Internal state int net_input_beast_port; // Beast input TCP port char *net_bind_address; // Bind address int net_http_port; // HTTP port + int net_fatsv_port; // FlightAware TSV port int net_sndbuf_size; // TCP output buffer size (64Kb * 2^n) int quiet; // Suppress stdout int interactive; // Interactive mode diff --git a/net_io.c b/net_io.c index b27db2830..1d4df73cf 100644 --- a/net_io.c +++ b/net_io.c @@ -65,7 +65,8 @@ void modesInitNet(void) { {"Beast TCP output", &Modes.beast_out.socket, &Modes.beast_out, Modes.net_output_beast_port, 1}, {"Beast TCP input", &Modes.bis, NULL, Modes.net_input_beast_port, 1}, {"HTTP server", &Modes.https, NULL, Modes.net_http_port, 1}, - {"Basestation TCP output", &Modes.sbs_out.socket, &Modes.sbs_out, Modes.net_output_sbs_port, 1} + {"Basestation TCP output", &Modes.sbs_out.socket, &Modes.sbs_out, Modes.net_output_sbs_port, 1}, + {"FlightAware TSV output", &Modes.fatsv_out.socket, &Modes.fatsv_out, Modes.net_fatsv_port, 1} }; memcpy(&services, &svc, sizeof(svc));//services = svc; @@ -217,7 +218,7 @@ static void *prepareWrite(struct net_writer *writer, int len) { !writer->data) return NULL; - if (len >= MODES_OUT_BUF_SIZE) + if (len > MODES_OUT_BUF_SIZE) return NULL; if (writer->dataUsed + len >= MODES_OUT_BUF_SIZE) { @@ -1053,6 +1054,154 @@ void modesReadFromClient(struct client *c, char *sep, } } +#define TSV_MAX_PACKET_SIZE 160 + +static void writeFATSV() { + struct aircraft *a; + time_t now; + static time_t lastTime = 0; + + if (!Modes.fatsv_out.connections) { + return; // no active connections + } + + now = time(NULL); + if (now <= lastTime) { + // scan once a second at most + return; + } + + lastTime = now; + + for (a = Modes.aircrafts; a; a = a->next) { + int altValid = 0; + int alt = 0; + int groundValid = 0; + int ground = 0; + int latlonValid = 0; + int useful = 0; + int emittedSecondsAgo; + char *p; + + // don't emit if it hasn't updated since last time + if (a->seen < a->fatsv_last_emitted) { + continue; + } + + emittedSecondsAgo = (int)(now - a->fatsv_last_emitted); + + // don't emit more than once every five seconds + if (emittedSecondsAgo < 5) { + continue; + } + + if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) { + altValid = 1; + alt = a->altitude; + } + + if (a->bFlags & MODES_ACFLAGS_AOG_VALID) { + groundValid = 1; + + if (a->bFlags & MODES_ACFLAGS_AOG) { + alt = 0; + ground = 1; + } + } + + if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) { + latlonValid = 1; + } + + // if it's over 10,000 feet, don't emit more than once every 10 seconds + if (alt > 10000 && emittedSecondsAgo < 10) { + continue; + } + + // disable if you want only ads-b + // also don't send mode S very often + if (!latlonValid) { + if (emittedSecondsAgo < 30) { + continue; + } + } else { + // if it hasn't changed altitude very much and it hasn't changed + // heading very much, don't update real often + if (abs(a->track - a->fatsv_emitted_track) < 2 && abs(alt - a->fatsv_emitted_altitude) < 50) { + if (alt < 10000) { + // it hasn't changed much but we're below 10,000 feet + // so update more frequently + if (emittedSecondsAgo < 10) { + continue; + } + } else { + // above 10,000 feet, don't update so often when it + // hasn't changed much + if (emittedSecondsAgo < 30) { + continue; + } + } + } + } + + p = prepareWrite(&Modes.fatsv_out, MODES_OUT_BUF_SIZE); + if (!p) + return; + + p += sprintf(p, "clock\t%ld\thexid\t%06X", a->seen, a->addr); + + if (*a->flight != '\0') { + p += sprintf(p, "\tident\t%s", a->flight); + } + + if (a->bFlags & MODES_ACFLAGS_SQUAWK_VALID) { + p += sprintf(p, "\tsquawk\t%04x", a->modeA); + } + + if (altValid) { + p += sprintf(p, "\talt\t%d", alt); + useful = 1; + } + + if (a->bFlags & MODES_ACFLAGS_SPEED_VALID) { + p += sprintf(p, "\tspeed\t%d", a->speed); + useful = 1; + } + + if (groundValid) { + if (ground) { + p += sprintf(p, "\tairGround\tG"); + } else { + p += sprintf(p, "\tairGround\tA"); + } + } + + if (latlonValid) { + p += sprintf(p, "\tlat\t%.5f\tlon\t%.5f", a->lat, a->lon); + useful = 1; + } + + if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) { + p += sprintf(p, "\theading\t%d", a->track); + useful = 1; + } + + // if we didn't get at least an alt or a speed or a latlon or + // a heading, bail out. We don't need to do anything special + // to unwind prepareWrite(). + if (!useful) { + continue; + } + + p += sprintf(p, "\n"); + completeWrite(&Modes.fatsv_out, p); + + a->fatsv_last_emitted = now; + a->fatsv_emitted_altitude = alt; + a->fatsv_emitted_track = a->track; + } +} + // // Perform periodic network work // @@ -1076,6 +1225,9 @@ void modesNetPeriodicWork(void) { } } + // Generate FATSV output + writeFATSV(); + // If we have generated no messages for a while, generate // a dummy heartbeat message. if (Modes.net_heartbeat_interval) { From 39d905324f1af22835adaf6417e1461508bdb28d Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Mon, 8 Dec 2014 20:14:53 +0000 Subject: [PATCH 052/610] Be more paranoid about not overrunning our fatsv output buffer. Request a buffer of a more reasonable size. --- net_io.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/net_io.c b/net_io.c index 1d4df73cf..1da1dcb13 100644 --- a/net_io.c +++ b/net_io.c @@ -1081,7 +1081,7 @@ static void writeFATSV() { int latlonValid = 0; int useful = 0; int emittedSecondsAgo; - char *p; + char *p, *end; // don't emit if it hasn't updated since last time if (a->seen < a->fatsv_last_emitted) { @@ -1144,45 +1144,47 @@ static void writeFATSV() { } } - p = prepareWrite(&Modes.fatsv_out, MODES_OUT_BUF_SIZE); + p = prepareWrite(&Modes.fatsv_out, TSV_MAX_PACKET_SIZE); if (!p) return; - p += sprintf(p, "clock\t%ld\thexid\t%06X", a->seen, a->addr); + end = p + TSV_MAX_PACKET_SIZE; +# define bufsize(_p,_e) ((_p) >= (_e) ? (size_t)0 : (size_t)((_e) - (_p))) + p += snprintf(p, bufsize(p,end), "clock\t%ld\thexid\t%06X", a->seen, a->addr); if (*a->flight != '\0') { - p += sprintf(p, "\tident\t%s", a->flight); + p += snprintf(p, bufsize(p,end), "\tident\t%s", a->flight); } if (a->bFlags & MODES_ACFLAGS_SQUAWK_VALID) { - p += sprintf(p, "\tsquawk\t%04x", a->modeA); + p += snprintf(p, bufsize(p,end), "\tsquawk\t%04x", a->modeA); } if (altValid) { - p += sprintf(p, "\talt\t%d", alt); + p += snprintf(p, bufsize(p,end), "\talt\t%d", alt); useful = 1; } if (a->bFlags & MODES_ACFLAGS_SPEED_VALID) { - p += sprintf(p, "\tspeed\t%d", a->speed); + p += snprintf(p, bufsize(p,end), "\tspeed\t%d", a->speed); useful = 1; } if (groundValid) { if (ground) { - p += sprintf(p, "\tairGround\tG"); + p += snprintf(p, bufsize(p,end), "\tairGround\tG"); } else { - p += sprintf(p, "\tairGround\tA"); + p += snprintf(p, bufsize(p,end), "\tairGround\tA"); } } if (latlonValid) { - p += sprintf(p, "\tlat\t%.5f\tlon\t%.5f", a->lat, a->lon); + p += snprintf(p, bufsize(p,end), "\tlat\t%.5f\tlon\t%.5f", a->lat, a->lon); useful = 1; } if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) { - p += sprintf(p, "\theading\t%d", a->track); + p += snprintf(p, bufsize(p,end), "\theading\t%d", a->track); useful = 1; } @@ -1193,8 +1195,9 @@ static void writeFATSV() { continue; } - p += sprintf(p, "\n"); + p += snprintf(p, bufsize(p,end), "\n"); completeWrite(&Modes.fatsv_out, p); +# undef bufsize a->fatsv_last_emitted = now; a->fatsv_emitted_altitude = alt; From c712543ee57f1c76a925a04eba1b8356061d805b Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Mon, 8 Dec 2014 20:17:48 +0000 Subject: [PATCH 053/610] Only completeWrite() if we didn't run off the end of the buffer. If we did run off the end, complain about it. --- net_io.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net_io.c b/net_io.c index 1da1dcb13..c1b7b5341 100644 --- a/net_io.c +++ b/net_io.c @@ -1196,7 +1196,11 @@ static void writeFATSV() { } p += snprintf(p, bufsize(p,end), "\n"); - completeWrite(&Modes.fatsv_out, p); + + if (p <= end) + completeWrite(&Modes.fatsv_out, p); + else + fprintf(stderr, "fatsv: output too large (max %d, overran by %d)\n", TSV_MAX_PACKET_SIZE, (int) (p - end)); # undef bufsize a->fatsv_last_emitted = now; From 125158b30a6a09ce0a52d0d13305d9dc21fbec84 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 9 Dec 2014 20:36:24 +0000 Subject: [PATCH 054/610] Big rearrangement to add debconf support. /etc/default/dump1090-mutability is now generated in postinst, if it doesn't exist, from a template in /usr/share/dump1090-mutability. Subsequently, the config file is updated in place if debconf-driven changes are made. Static HTML/javascript have moved to a "html" subdir to avoid exposing the template via the webserver. --- ...090-mutability.default => config-template} | 78 +++--- debian/dump1090-mutability.config | 108 ++++++++ debian/dump1090-mutability.init | 37 +-- debian/dump1090-mutability.install | 3 +- debian/dump1090-mutability.postinst | 78 +++++- debian/dump1090-mutability.postrm | 41 ++++ debian/dump1090-mutability.templates | 231 ++++++++++++++++++ debian/lighttpd/89-dump1090.conf | 2 +- debian/rules | 2 +- 9 files changed, 504 insertions(+), 76 deletions(-) rename debian/{dump1090-mutability.default => config-template} (70%) create mode 100644 debian/dump1090-mutability.config create mode 100644 debian/dump1090-mutability.postrm create mode 100644 debian/dump1090-mutability.templates diff --git a/debian/dump1090-mutability.default b/debian/config-template similarity index 70% rename from debian/dump1090-mutability.default rename to debian/config-template index d14386b9c..ad16795e2 100644 --- a/debian/dump1090-mutability.default +++ b/debian/config-template @@ -1,19 +1,19 @@ -# Defaults for dump1090-mutability -# This is a POSIX shell fragment +## TEMPLATE FILE - This is used to create /etc/default/dump1090-mutability ## +## The first three lines will be discarded ## + +# dump1090-mutability configuration file +# This is a POSIX shell fragment. +# You can edit this file directly, or use +# "dpkg-reconfigure dump1090-mutability" # Set to "yes" to start dump1090 on boot. -START_DUMP1090="no" +START_DUMP1090= # User to run dump1090 as. -DUMP1090_USER="dump1090" +DUMP1090_USER= # Logfile to log to -#LOGFILE="/var/log/dump1090-mutability.log" - -# -# The following options are all optional - defaults if not provided are -# shown below. -# +LOGFILE= # # Receiver options @@ -21,38 +21,36 @@ DUMP1090_USER="dump1090" # RTLSDR device index to use # If set to "none", dump1090 will be started in --net-only mode -#DEVICE=0 +DEVICE= # RTLSDR gain in dB. # If set to "max" (the default) the maximum supported gain is used. # If set to "agc", the tuner AGC is used to set the gain. -#GAIN=max +GAIN= # RTLSDR frequency correction in PPM -#PPM=0 +PPM= # If yes, enable sampling at 2.4MHz. Otherwise, 2.0MHz is used. -#OVERSAMPLE=no +OVERSAMPLE= + +# If yes, enables phase-enhancement of messages +PHASE_ENHANCE= # # Decoding options # # If yes, fixes messages with correctable CRC errors. -# Otherwise, discards messages with errors. -#FIX_CRC=no - -# If yes, enables phase-enhancement of messages that fail to decode -# the first time around. -#PHASE_ENHANCE=no +FIX_CRC= # If yes, enables aggressive fixes to damaged messages. # Use with caution - it can increase the rate of undetected errors. -#AGGRESSIVE=no +AGGRESSIVE= # If set, supplies a reference location for local position decoding. -#LAT=decimal.latitude.value -#LON=decimal.longitude.value +LAT= +LON= # # Networking options @@ -64,55 +62,55 @@ DUMP1090_USER="dump1090" # /usr/share/dump1090-mutability and JSON_DIR (below) using a proper # webserver. See /etc/lighttpd/conf-available/90-dump1090.conf # for an example configuration ("sudo lighty-enable-mod dump1090" to enable) -#HTTP_PORT=0 +HTTP_PORT= # Port to listen on for raw (AVR-format) input connections. 0 disables. -#RAW_INPUT_PORT=30001 +RAW_INPUT_PORT= # Port to listen on for raw (AVR-format) output connections. 0 disables. -#RAW_OUTPUT_PORT=30002 +RAW_OUTPUT_PORT= # Port to listen on for SBS-format output connections. 0 disables. -#SBS_OUTPUT_PORT=30003 +SBS_OUTPUT_PORT= # Port to listen on for Beast-format input connections. 0 disables. -#BEAST_INPUT_PORT=30004 +BEAST_INPUT_PORT= # Port to listen on for Beast-format output connections. 0 disables. -#BEAST_OUTPUT_PORT=30005 +BEAST_OUTPUT_PORT= # Port to listen on for FATSV-format output connections. 0 disables. -#FATSV_OUTPUT_PORT=10001 +FATSV_OUTPUT_PORT= # TCP heartbeat interval in seconds. 0 disables. -#NET_HEARTBEAT=60 +NET_HEARTBEAT= # Minimum output buffer size per write, in bytes. -#NET_OUTPUT_SIZE=500 +NET_OUTPUT_SIZE= # Maximum buffering time before writing, in seconds. -#NET_OUTPUT_INTERVAL=1 +NET_OUTPUT_INTERVAL= -# TCP buffer size order. Power-of-two based - buffer size is 2^(n+16). -#NET_BUFFER=4 +# TCP buffer size, in bytes +NET_BUFFER= # Bind ports on a particular address. If unset, binds to all interfaces. # This defaults to binding to localhost. If you need to allow remote # connections, change this. -#BIND_ADDRESS=127.0.0.1 +NET_BIND_ADDRESS= # # Misc options # # Interval (in seconds) between logging stats to the logfile. 0 disables. -#STATS_INTERVAL=3600 +STATS_INTERVAL= # Path to write json state to (for use with an external webserver). Blank disables. -#JSON_DIR=/run/dump1090-mutability +JSON_DIR= # Interval between writing json state (in seconds). 0 disables. -#JSON_INTERVAL=1 +JSON_INTERVAL= # Additional options that are passed to the Daemon. -#EXTRA_ARGS="" +EXTRA_ARGS= diff --git a/debian/dump1090-mutability.config b/debian/dump1090-mutability.config new file mode 100644 index 000000000..1b0e43710 --- /dev/null +++ b/debian/dump1090-mutability.config @@ -0,0 +1,108 @@ +#!/bin/sh + +NAME=dump1090-mutability +CONFIGFILE=/etc/default/$NAME +set -e +. /usr/share/debconf/confmodule + +db_set_yn() { + if [ "x$2" = "xyes" ]; then db_set $1 true; else db_set $1 false; fi +} + +# Load config file, if it exists. +if [ -e $CONFIGFILE ]; then + . $CONFIGFILE || true + + # Store values from config file into + # debconf db. + + db_set_yn $NAME/auto-start "$START_DUMP1090" + db_set $NAME/run-as-user "$DUMP1090_USER" + db_set $NAME/log-file "$LOGFILE" + + db_set $NAME/rtlsdr-device "$DEVICE" + db_set $NAME/rtlsdr-gain "$GAIN" + db_set $NAME/rtlsdr-ppm "$PPM" + db_set_yn $NAME/rtlsdr-oversample "$OVERSAMPLE" + + db_set_yn $NAME/decode-fixcrc "$FIX_CRC" + db_set_yn $NAME/decode-phase-enhance "$PHASE_ENHANCE" + db_set_yn $NAME/decode-aggressive "$AGGRESSIVE" + db_set $NAME/decode-lat "$LAT" + db_set $NAME/decode-lon "$LON" + + db_set $NAME/net-http-port "$HTTP_PORT" + db_set $NAME/net-ri-port "$RAW_INPUT_PORT" + db_set $NAME/net-ro-port "$RAW_OUTPUT_PORT" + db_set $NAME/net-bi-port "$BEAST_INPUT_PORT" + db_set $NAME/net-bo-port "$BEAST_OUTPUT_PORT" + db_set $NAME/net-sbs-port "$SBS_OUTPUT_PORT" + db_set $NAME/net-fatsv-port "$FATSV_OUTPUT_PORT" + db_set $NAME/net-heartbeat "$NET_HEARTBEAT" + db_set $NAME/net-out-size "$NET_OUTPUT_SIZE" + db_set $NAME/net-out-interval "$NET_OUTPUT_INTERVAL" + db_set $NAME/net-buffer "$NET_BUFFER" + db_set $NAME/net-bind-address "$NET_BIND_ADDRESS" + + db_set $NAME/stats-interval "$STATS_INTERVAL" + db_set $NAME/json-dir "$JSON_DIR" + db_set $NAME/json-interval "$JSON_INTERVAL" + db_set $NAME/extra-args "$EXTRA_ARGS" +fi + +# Ask questions. + +db_input high $NAME/auto-start || true + +db_go || true; db_get $NAME/auto-start; if [ "$RET" = "true" ]; then + # all of these are only relevant if the init script is enabled + + db_input low $NAME/run-as-user || true + db_input low $NAME/log-file || true + + db_input medium $NAME/rtlsdr-device || true + + db_go || true; db_get $NAME/rtlsdr-device; if [ "x$RET" != "xnone" ]; then + # only if a real device was chosen: + db_input medium $NAME/rtlsdr-gain || true + db_input medium $NAME/rtlsdr-ppm || true + db_input low $NAME/rtlsdr-oversample || true + fi + + db_input low $NAME/decode-fix-crc || true + db_input low $NAME/decode-aggressive || true + db_input medium $NAME/decode-lat || true + + db_go || true; db_get $NAME/decode-lat; if [ -n "$RET" ]; then + # only if latitude was given: + db_input medium $NAME/decode-lon || true + fi + + db_input medium $NAME/net-http-port || true + db_input low $NAME/net-ri-port || true + db_input low $NAME/net-ro-port || true + db_input low $NAME/net-bi-port || true + db_input low $NAME/net-bo-port || true + db_input low $NAME/net-sbs-port || true + db_input low $NAME/net-fatsv-port || true + db_input low $NAME/net-heartbeat || true + db_input low $NAME/net-out-size || true + db_input low $NAME/net-out-interval || true + db_input low $NAME/net-buffer || true + db_input medium $NAME/net-bind-address || true + + db_input low $NAME/stats-interval || true + db_input low $NAME/json-dir || true + + db_go || true; db_get $NAME/json-dir; if [ -n "$RET" ]; then + # only if json-dir was given: + db_input low $NAME/json-interval || true + fi + + db_input low $NAME/extra-args || true + + db_go || True +fi + +# Done. +db_stop diff --git a/debian/dump1090-mutability.init b/debian/dump1090-mutability.init index 2afeb5534..bf38527f4 100644 --- a/debian/dump1090-mutability.init +++ b/debian/dump1090-mutability.init @@ -25,36 +25,6 @@ SCRIPTNAME=/etc/init.d/$NAME # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 -# Set defaults: -START_DUMP1090=no -DUMP1090_USER=dump1090 -LOGFILE=/var/log/$NAME.log -DEVICE=0 -GAIN=max -PPM=0 -OVERSAMPLE=no -FIX_CRC=no -PHASE_ENHANCE=no -AGGRESSIVE=no -LAT= -LON= -HTTP_PORT=0 -RAW_INPUT_PORT=30001 -RAW_OUTPUT_PORT=30002 -SBS_OUTPUT_PORT=30003 -BEAST_INPUT_PORT=30004 -BEAST_OUTPUT_PORT=30005 -FATSV_OUTPUT_PORT=10001 -NET_HEARTBEAT=60 -NET_OUTPUT_SIZE=500 -NET_OUTPUT_INTERVAL=1 -NET_BUFFER=4 -BIND_ADDRESS=127.0.0.1 -STATS_INTERVAL=3600 -JSON_DIR=/run/$NAME -JSON_INTERVAL=1 -EXTRA_ARGS= - # Read configuration variable file if it is present [ -r /etc/default/$NAME ] && . /etc/default/$NAME @@ -92,8 +62,11 @@ if [ "x$FATSV_OUTPUT_PORT" != "x10001" ]; then ARGS="$ARGS --net-fatsv-port $FAT if [ "x$NET_HEARTBEAT" != "x60" ]; then ARGS="$ARGS --net-heartbeat $NET_HEARTBEAT"; fi if [ "x$NET_OUTPUT_SIZE" != "x0" ]; then ARGS="$ARGS --net-ro-size $NET_OUTPUT_SIZE"; fi if [ "x$NET_OUTPUT_INTERVAL" != "x0" ]; then ARGS="$ARGS --net-ro-interval $NET_OUTPUT_INTERVAL"; fi -if [ "x$NET_BUFFER" != "x0" ]; then ARGS="$ARGS --net-buffer $NET_BUFFER"; fi -if [ -n "$BIND_ADDRESS" ]; then ARGS="$ARGS --net-bind-address $BIND_ADDRESS"; fi +if [ "$NET_BUFFER" -le "65536" ]; then ARGS="$ARGS --net-buffer 0" +elif [ "$NET_BUFFER" -le "131072" ]; then ARGS="$ARGS --net-buffer 1" +elif [ "$NET_BUFFER" -le "262144" ]; then ARGS="$ARGS --net-buffer 2" +else ARGS="$ARGS --net-buffer 3"; fi +if [ -n "$NET_BIND_ADDRESS" ]; then ARGS="$ARGS --net-bind-address $NET_BIND_ADDRESS"; fi # misc: if [ "x$STATS_INTERVAL" != "x0" ]; then ARGS="$ARGS --stats-every $STATS_INTERVAL"; fi diff --git a/debian/dump1090-mutability.install b/debian/dump1090-mutability.install index d1e444b25..bfc44bc81 100644 --- a/debian/dump1090-mutability.install +++ b/debian/dump1090-mutability.install @@ -1,2 +1,3 @@ -public_html/* usr/share/dump1090-mutability +public_html/* usr/share/dump1090-mutability/html debian/lighttpd/* etc/lighttpd/conf-available +debian/config-template usr/share/dump1090-mutability diff --git a/debian/dump1090-mutability.postinst b/debian/dump1090-mutability.postinst index b0fd8f3b8..2be58a905 100644 --- a/debian/dump1090-mutability.postinst +++ b/debian/dump1090-mutability.postinst @@ -17,10 +17,85 @@ set -e # for details, see http://www.debian.org/doc/debian-policy/ or # the debian-policy package +NAME=dump1090-mutability +CONFIGFILE=/etc/default/$NAME +TEMPLATECONFIG=/usr/share/$NAME/config-template +SEDSCRIPT=$CONFIGFILE.sed.tmp + +subvar_raw() { + # $1 = db var value + # $2 = config var name + + # if not present in the config file, add it + test -z "$1" || grep -Eq "^ *$2=" $CONFIGFILE || echo "$2=" >> $CONFIGFILE + # add to the sedscript + echo "s@^ *$2=.*@$2=\"$1\"@" >>$SEDSCRIPT +} + +subvar() { + # $1 = db var name + # $2 = config var name + db_get $NAME/$1 + subvar_raw "$RET" "$2" +} + +subvar_yn() { + # $1 = db var name + # $2 = config var name + db_get $NAME/$1 + if [ "$RET" = "true" ]; then subvar_raw "yes" "$2"; else subvar_raw "no" "$2"; fi +} case "$1" in configure) - adduser --system --home /usr/share/dump1090-mutability --no-create-home --quiet dump1090 + . /usr/share/debconf/confmodule + + # Generate config file, if it doesn't exist. + if [ ! -e $CONFIGFILE ]; then + tail -n +4 $TEMPLATECONFIG >$CONFIGFILE + fi + + rm -f $SEDSCRIPT + + subvar_yn auto-start START_DUMP1090 + subvar run-as-user DUMP1090_USER + subvar log-file LOGFILE + subvar rtlsdr-device DEVICE + subvar rtlsdr-gain GAIN + subvar rtlsdr-ppm PPM + subvar_yn rtlsdr-oversample OVERSAMPLE + subvar_yn decode-fixcrc FIX_CRC + subvar_yn decode-phase-enhance PHASE_ENHANCE + subvar_yn decode-aggressive AGGRESSIVE + subvar decode-lat LAT + subvar decode-lon LON + subvar net-http-port HTTP_PORT + subvar net-ri-port RAW_INPUT_PORT + subvar net-ro-port RAW_OUTPUT_PORT + subvar net-bi-port BEAST_INPUT_PORT + subvar net-bo-port BEAST_OUTPUT_PORT + subvar net-sbs-port SBS_OUTPUT_PORT + subvar net-fatsv-port FATSV_OUTPUT_PORT + subvar net-heartbeat NET_HEARTBEAT + subvar net-out-size NET_OUTPUT_SIZE + subvar net-out-interval NET_OUTPUT_INTERVAL + subvar net-buffer NET_BUFFER + subvar net-bind-address NET_BIND_ADDRESS + subvar stats-interval STATS_INTERVAL + subvar json-dir JSON_DIR + subvar json-interval JSON_INTERVAL + subvar extra-args EXTRA_ARGS + + cp -a -f $CONFIGFILE $CONFIGFILE.tmp + sed -f $SEDSCRIPT < $CONFIGFILE > $CONFIGFILE.tmp + mv -f $CONFIGFILE.tmp $CONFIGFILE + #rm $SEDSCRIPT + + db_get $NAME/auto-start + if [ "$RET" = "true" ]; then + db_get $NAME/run-as-user + adduser --system --home /usr/share/$NAME --no-create-home --quiet "$RET" + fi ;; abort-upgrade|abort-remove|abort-deconfigure) @@ -37,4 +112,5 @@ esac #DEBHELPER# +if [ "$1" = "configure" ]; then db_stop; fi exit 0 diff --git a/debian/dump1090-mutability.postrm b/debian/dump1090-mutability.postrm new file mode 100644 index 000000000..6bdd5ead1 --- /dev/null +++ b/debian/dump1090-mutability.postrm @@ -0,0 +1,41 @@ +#!/bin/sh +# postrm script for #PACKAGE# +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `remove' +# * `purge' +# * `upgrade' +# * `failed-upgrade' +# * `abort-install' +# * `abort-install' +# * `abort-upgrade' +# * `disappear' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + purge) + rm -f /etc/default/dump1090-mutability + ;; + + remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/debian/dump1090-mutability.templates b/debian/dump1090-mutability.templates new file mode 100644 index 000000000..92e490286 --- /dev/null +++ b/debian/dump1090-mutability.templates @@ -0,0 +1,231 @@ +Template: dump1090-mutability/auto-start +Description: Start dump1090 automatically? + dump1090 can be started automatically via an init-script. + Otherwise, the init-script does nothing; you must run dump1090 by hand. +Type: boolean +Default: true + +Template: dump1090-mutability/run-as-user +Description: User to run dump1090 as: + When started automatically, dump1090 runs as an unprivileged system user. + This user will be created if it does not yet exist. +Type: string +Default: dump1090 + +Template: dump1090-mutability/log-file +Description: Path to log to: + When started automatically, dump1090 will log its output somewhere. This + log will contain any startup errors, and periodic statistics reports. +Type: string +Default: /var/log/dump1090-mutability.log + +Template: dump1090-mutability/rtlsdr-device +Description: RTL-SDR dongle to use: + If you have only one dongle connected, select 0. If you have more than one + dongle connected, select the index of the dongle to use (0 is the first + dongle). If you want to run dump1090 in "net only" mode, select "none". +Type: select +Choices: none, 0, 1, 2, 3, 4, 5 +Default: 0 + +Template: dump1090-mutability/rtlsdr-gain +Description: RTL-SDR gain, in dB: + The tuner gain used by dump1090 can be provided as a value in dB, or + "max" to use the maximum gain available, or "agc" to use the tuner's AGC to + control the gain. If unsure, choose "max". +Type: string +Default: max + +Template: dump1090-mutability/rtlsdr-ppm +Description: RTL-SDR frequency correction, in PPM: + The oscillator in each RTL-SDL dongle is not perfectly accurate. You can + choose a correction factor, in parts-per-million, to correct for this. The + correction factor varies from dongle to dongle, and also varies with operating + temperature. You can find a suitable value with "rtl_test -p" or "kalibrate". + If you don't know the value for your dongle, choose 0. +Type: string +Default: 0 + +Template: dump1090-mutability/rtlsdr-oversample +Description: Enable oversampling at 2.4MHz? + Originally, dump1090 would decode incoming signals by sampling at 2MHz. Newer + versions also support sampling at 2.4MHz. This may increase the number of + decodable messages, but takes slightly more CPU and is not as well tested. +Type: boolean +Default: false + +Template: dump1090-mutability/decode-fixcrc +Description: Fix detected CRC errors? + dump1090 can fix unambiguous single-bit CRC errors detected in received + messages. This allows weaker messages to be decoded. It can slightly increase + the rate of undetected errors, but this is not usually significant. +Type: boolean +Default: true + +Template: dump1090-mutability/decode-phase-enhance +Description: Apply phase enhancement? + dump1090 can attempt to correct for messages that are received + out-of-phase from the sampling rate, at the expense of taking more CPU. +Type: boolean +Default: true + +Template: dump1090-mutability/decode-aggressive +Description: Aggressively fix more errors? + dump1090 can apply more aggressive corrections to received messages, + primarily correcting two-bit CRC errors. + . + Use with caution! This can significantly increase the rate of undetected + message errors (i.e. increase the rate of garbled decoded messages) +Type: boolean +Default: false + +Template: dump1090-mutability/decode-lat +Description: Latitude of receiver, in decimal degrees: + If the location of the receiver is provided, dump1090 can do + local position decoding in cases where insufficient position messages are + received for unambiguous global position decoding. +Type: string +Default: + +Template: dump1090-mutability/decode-lon +Description: Longitude of receiver, in decimal degrees: + If the location of the receiver is provided, dump1090 can do + local position decoding in cases where insufficient position messages are + received for unambiguous global position decoding. +Type: string +Default: + +Template: dump1090-mutability/net-http-port +Description: Port for internal webserver (0 disables): + dump1090 can provide an internal webserver that serves a basic "virtual + radar" map. + . + It is generally a better idea to use an external webserver, but if you + really want to use the internal one, you can select a port to listen + on here. +Type: string +Default: 0 + +Template: dump1090-mutability/net-ri-port +Description: Port for AVR-format input connections (0 disables): + dump1090 can accept connections to receive data from other sources in + several formats. This setting controls the port dump1090 will listen + on for AVR ("raw") format input connections. +Type: string +Default: 30001 + +Template: dump1090-mutability/net-ro-port +Description: Port for AVR-format output connections (0 disables): + dump1090 can forward ADS-B messages to other software in several formats. + This setting controls the port dump1090 will listen on for AVR ("raw") + format output connections. +Type: string +Default: 30002 + +Template: dump1090-mutability/net-bi-port +Description: Port for Beast-format input connections (0 disables): + dump1090 can accept connections to receive data from other sources in + several formats. This setting controls the port dump1090 will listen + on for Beast ("binary") format input connections. +Type: string +Default: 30004 + +Template: dump1090-mutability/net-bo-port +Description: Port for Beast-format output connections (0 disables): + dump1090 can forward ADS-B messages to other software in several formats. + This setting controls the port dump1090 will listen on for Beast ("binary") + format output connections. +Type: string +Default: 30005 + +Template: dump1090-mutability/net-sbs-port +Description: Port for SBS-format output connections (0 disables): + dump1090 can forward ADS-B messages to other software in several formats. + This setting controls the port dump1090 will listen on for SBS BaseStation + format output connections. +Type: string +Default: 30003 + +Template: dump1090-mutability/net-fatsv-port +Description: Port for FATSV-format output connections (0 disables): + dump1090 can forward ADS-B messages to other software in several formats. + This setting controls the port dump1090 will listen on for FlightAware TSV + format output connections. +Type: string +Default: 10001 + +Template: dump1090-mutability/net-heartbeat +Description: Seconds between heartbeat messages (0 disables): + If there is no other data sent on a network connection, dump1090 can + periodically send an empty heartbeat message to ensure that the + connection stays established. This setting controls the interval + betweeen heartbeat messages. +Type: string +Default: 60 + +Template: dump1090-mutability/net-out-size +Description: Minimum output message size: + To avoid sending many small network messages, output connections will + accumulate data waiting to be sent until either a minimum size is reached + or a maximum delay is reached. This setting controls the minimum size, + in bytes. +Type: string +Default: 500 + +Template: dump1090-mutability/net-out-interval +Description: Maximum output buffering time: + To avoid sending many small network messages, output connections will + buffer data waiting to be sent until either a minimum size is reached + or a maximum delay is reached. This setting controls the maximum delay, + in seconds. +Type: string +Default: 1 + +Template: dump1090-mutability/net-buffer +Description: SO_SNDBUF size: + Here you can specify the TCP send buffer size to use on network connections. +Type: select +Choices: 65536, 131072, 262144 +Default: 262144 + +Template: dump1090-mutability/net-bind-address +Description: Interface address to bind to (blank for all interfaces): + If you want to limit incoming connections to a particular interface, + specify the interface address here. A blank value will bind to the wildcard + address, allowing connections on all interfaces. + . + The default value of 127.0.0.1 will allow connections only on localhost, + i.e. only connections that originate on the same machine. +Type: string +Default: 127.0.0.1 + +Template: dump1090-mutability/stats-interval +Description: Interval between logging stats, in seconds: + dump1090 will periodically log message reception stats to its logfile. + This setting controls how often that is done. +Type: string +Default: 3600 + +Template: dump1090-mutability/json-dir +Description: Directory to write JSON aircraft state to: + dump1090 periodicallys write a list of aircraft, in JSON format, for use + by the virtual radar view when using an external webserver. This setting + controls the directory to write to. + . + As this is written frequently (by default, once a second), you should + probably select a location that is not on a sdcard. The default path + under /run is on tmpfs and will not write to the sdcard. +Type: string +Default: /run/dump1090-mutability + +Template: dump1090-mutability/json-interval +Description: Interval between writing JSON aircraft state, in seconds: + Here you can control how often the JSON state is updated. +Type: string +Default: 1 + +Template: dump1090-mutability/extra-args +Description: Extra arguments to pass to dump1090: + Here you can add any extra arguments you want to pass to dump1090. +Type: string +Default: diff --git a/debian/lighttpd/89-dump1090.conf b/debian/lighttpd/89-dump1090.conf index a12f0ebaf..fda8a6e8e 100644 --- a/debian/lighttpd/89-dump1090.conf +++ b/debian/lighttpd/89-dump1090.conf @@ -9,5 +9,5 @@ url.redirect += ( alias.url += ( "/dump1090/data/" => "/run/dump1090-mutability/", - "/dump1090/" => "/usr/share/dump1090-mutability/" + "/dump1090/" => "/usr/share/dump1090-mutability/html/" ) diff --git a/debian/rules b/debian/rules index cf81ca191..2f3b1c6f1 100755 --- a/debian/rules +++ b/debian/rules @@ -15,7 +15,7 @@ DPKG_EXPORT_BUILDFLAGS = 1 include /usr/share/dpkg/buildflags.mk override_dh_auto_build: - dh_auto_build -- 'EXTRACFLAGS=-DHTMLPATH=\"/usr/share/dump1090-mutability\"' + dh_auto_build -- 'EXTRACFLAGS=-DHTMLPATH=\"/usr/share/dump1090-mutability/html\"' override_dh_install: dh_install From 47e70a96fec551f9967e9d28b347630f0307a5c9 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 9 Dec 2014 20:37:52 +0000 Subject: [PATCH 055/610] Changelog update. --- debian/changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/changelog b/debian/changelog index e8ecf272d..b2c5d9188 100644 --- a/debian/changelog +++ b/debian/changelog @@ -3,6 +3,7 @@ dump1090-mutability (1.08.2302.14+1mu-2) UNRELEASED; urgency=medium * Fix a memory leak from use of realpath() in HTTP request processing. * Add direct support for FATSV-format output, adapted from the FlightAware fork. + * Big rearrangement of configuration to add debconf support. -- Oliver Jowett Sun, 07 Dec 2014 14:14:25 +0000 From 02c0f618e685383db6064e1feff918d5e737436a Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 9 Dec 2014 20:45:00 +0000 Subject: [PATCH 056/610] Re-enable sedscript cleanup now that I'm done testing it. --- debian/dump1090-mutability.postinst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/dump1090-mutability.postinst b/debian/dump1090-mutability.postinst index 2be58a905..2402a190a 100644 --- a/debian/dump1090-mutability.postinst +++ b/debian/dump1090-mutability.postinst @@ -89,7 +89,7 @@ case "$1" in cp -a -f $CONFIGFILE $CONFIGFILE.tmp sed -f $SEDSCRIPT < $CONFIGFILE > $CONFIGFILE.tmp mv -f $CONFIGFILE.tmp $CONFIGFILE - #rm $SEDSCRIPT + rm $SEDSCRIPT db_get $NAME/auto-start if [ "$RET" = "true" ]; then From 0305eb25e59c6d0cb30c1025f8ee540a450f2e6c Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 9 Dec 2014 21:41:14 +0000 Subject: [PATCH 057/610] Update Build-Depends based on glitches found building under pbuilder. --- debian/changelog | 1 + debian/control | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index b2c5d9188..12e0ceca0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -4,6 +4,7 @@ dump1090-mutability (1.08.2302.14+1mu-2) UNRELEASED; urgency=medium * Add direct support for FATSV-format output, adapted from the FlightAware fork. * Big rearrangement of configuration to add debconf support. + * Update Build-Depends based on glitches found building under pbuilder. -- Oliver Jowett Sun, 07 Dec 2014 14:14:25 +0000 diff --git a/debian/control b/debian/control index aebccd784..82f2d88ab 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: dump1090-mutability Section: embedded Priority: extra Maintainer: Oliver Jowett -Build-Depends: debhelper(>=8), librtlsdr-dev +Build-Depends: debhelper(>=8), librtlsdr-dev, libusb-1.0-0-dev, pkg-config Standards-Version: 3.9.3 Homepage: https://github.com/mutability/dump1090 Vcs-Git: https://github.com/mutability/dump1090.git From 2a2306ff57fdfb8adc92cb5830b128c3289db732 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 10 Dec 2014 01:07:26 +0000 Subject: [PATCH 058/610] Probably time for another release - release changelog. --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 12e0ceca0..9ad06eae7 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -dump1090-mutability (1.08.2302.14+1mu-2) UNRELEASED; urgency=medium +dump1090-mutability (1.08.2302.14+1mu-2) unstable; urgency=medium * Fix a memory leak from use of realpath() in HTTP request processing. * Add direct support for FATSV-format output, adapted from the FlightAware @@ -6,7 +6,7 @@ dump1090-mutability (1.08.2302.14+1mu-2) UNRELEASED; urgency=medium * Big rearrangement of configuration to add debconf support. * Update Build-Depends based on glitches found building under pbuilder. - -- Oliver Jowett Sun, 07 Dec 2014 14:14:25 +0000 + -- Oliver Jowett Wed, 10 Dec 2014 01:06:56 +0000 dump1090-mutability (1.08.2302.14+1mu-1) unstable; urgency=medium From 660eb08c662a18948d123c4d238b728f14e00e70 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 10 Dec 2014 02:02:19 +0000 Subject: [PATCH 059/610] Ask about json interval before json dir. Skip the dir question if the interval is disabled, it makes more sense that way. --- debian/changelog | 7 +++++++ debian/dump1090-mutability.config | 8 ++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/debian/changelog b/debian/changelog index 9ad06eae7..d95b241a5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +dump1090-mutability (1.08.2302.14+1mu-3) UNRELEASED; urgency=medium + + * Ask about json interval before json dir and skip the dir question if + the interval is disabled, it makes more sense that way. + + -- Oliver Jowett Wed, 10 Dec 2014 02:01:27 +0000 + dump1090-mutability (1.08.2302.14+1mu-2) unstable; urgency=medium * Fix a memory leak from use of realpath() in HTTP request processing. diff --git a/debian/dump1090-mutability.config b/debian/dump1090-mutability.config index 1b0e43710..9cd1b50e1 100644 --- a/debian/dump1090-mutability.config +++ b/debian/dump1090-mutability.config @@ -92,11 +92,11 @@ db_go || true; db_get $NAME/auto-start; if [ "$RET" = "true" ]; then db_input medium $NAME/net-bind-address || true db_input low $NAME/stats-interval || true - db_input low $NAME/json-dir || true + db_input low $NAME/json-interval || true - db_go || true; db_get $NAME/json-dir; if [ -n "$RET" ]; then - # only if json-dir was given: - db_input low $NAME/json-interval || true + db_go || true; db_get $NAME/json-interval; if [ -n "$RET" ] && [ "$RET" -gt 0 ]; then + # only if json-interval was given and non-zero + db_input low $NAME/json-dir || true fi db_input low $NAME/extra-args || true From 890d1afbf11d65af4eae59aaa1a59e2867e26488 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 10 Dec 2014 02:03:57 +0000 Subject: [PATCH 060/610] Be much more cautious about missing config settings so we don't explode so badly if something is omitted. --- debian/changelog | 2 ++ debian/dump1090-mutability.init | 39 +++++++++++++++++++++------------ 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/debian/changelog b/debian/changelog index d95b241a5..722937915 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,8 @@ dump1090-mutability (1.08.2302.14+1mu-3) UNRELEASED; urgency=medium * Ask about json interval before json dir and skip the dir question if the interval is disabled, it makes more sense that way. + * Be much more cautious about missing config settings so we don't explode + so badly if something is omitted. -- Oliver Jowett Wed, 10 Dec 2014 02:01:27 +0000 diff --git a/debian/dump1090-mutability.init b/debian/dump1090-mutability.init index bf38527f4..1c9d8d3e4 100644 --- a/debian/dump1090-mutability.init +++ b/debian/dump1090-mutability.init @@ -30,6 +30,19 @@ SCRIPTNAME=/etc/init.d/$NAME # work out daemon args +# sanitize missing settings +[ -z "$START_DUMP1090" ] && START_DUMP1090=no +[ -z "$DUMP1090_USER" ] && DUMP1090_USER="missing-DUMP1090_USER-setting-in-config" +[ -z "$HTTP_PORT" ] && HTTP_PORT=0 +[ -z "$RAW_INPUT_PORT" ] && RAW_INPUT_PORT=0 +[ -z "$RAW_OUTPUT_PORT" ] && RAW_OUTPUT_PORT=0 +[ -z "$SBS_OUTPUT_PORT" ] && SBS_OUTPUT_PORT=0 +[ -z "$BEAST_INPUT_PORT" ] && BEAST_INPUT_PORT=0 +[ -z "$BEAST_OUTPUT_PORT" ] && BEAST_OUTPUT_PORT=0 +[ -z "$FATSV_OUTPUT_PORT" ] && FATSV_OUTPUT_PORT=0 +[ -z "$NET_BUFFER" ] && NET_BUFFER=0 +[ -z "$JSON_INTERVAL" ] && JSON_INTERVAL=0 + # receiver: case "x$DEVICE" in x|x0) ARGS="$ARGS --net" ;; @@ -41,7 +54,7 @@ case "x$GAIN" in xagc) ARGS="$ARGS --gain -10" ;; *) ARGS="$ARGS --gain $GAIN" ;; esac -if [ "x$PPM" != "x0" ]; then ARGS="$ARGS --ppm $PPM"; fi +if [ -n "$PPM" ]; then ARGS="$ARGS --ppm $PPM"; fi if [ "x$OVERSAMPLE" = "xyes" ]; then ARGS="$ARGS --oversample"; fi # decoder: @@ -52,16 +65,14 @@ if [ -n "$LAT" ]; then ARGS="$ARGS --lat $LAT"; fi if [ -n "$LON" ]; then ARGS="$ARGS --lon $LON"; fi # net: -if [ "x$HTTP_PORT" != "x8080" ]; then ARGS="$ARGS --net-http-port $HTTP_PORT"; fi -if [ "x$RAW_INPUT_PORT" != "x30001" ]; then ARGS="$ARGS --net-ri-port $RAW_INPUT_PORT"; fi -if [ "x$RAW_OUTPUT_PORT" != "x30002" ]; then ARGS="$ARGS --net-ro-port $RAW_OUTPUT_PORT"; fi -if [ "x$SBS_OUTPUT_PORT" != "x30003" ]; then ARGS="$ARGS --net-sbs-port $SBS_OUTPUT_PORT"; fi -if [ "x$BEAST_INPUT_PORT" != "x30004" ]; then ARGS="$ARGS --net-bi-port $BEAST_INPUT_PORT"; fi -if [ "x$BEAST_OUTPUT_PORT" != "x30005" ]; then ARGS="$ARGS --net-bo-port $BEAST_OUTPUT_PORT"; fi -if [ "x$FATSV_OUTPUT_PORT" != "x10001" ]; then ARGS="$ARGS --net-fatsv-port $FATSV_OUTPUT_PORT"; fi -if [ "x$NET_HEARTBEAT" != "x60" ]; then ARGS="$ARGS --net-heartbeat $NET_HEARTBEAT"; fi -if [ "x$NET_OUTPUT_SIZE" != "x0" ]; then ARGS="$ARGS --net-ro-size $NET_OUTPUT_SIZE"; fi -if [ "x$NET_OUTPUT_INTERVAL" != "x0" ]; then ARGS="$ARGS --net-ro-interval $NET_OUTPUT_INTERVAL"; fi + +ARGS="$ARGS --net-http-port $HTTP_PORT \ +--net-ri-port $RAW_INPUT_PORT --net-ro-port $RAW_OUTPUT_PORT \ +--net-bi-port $BEAST_INPUT_PORT --net-bo-port $BEAST_OUTPUT_PORT \ +--net-sbs-port $SBS_OUTPUT_PORT --net-fatsv-port $FATSV_OUTPUT_PORT" +if [ -n "$NET_HEARTBEAT" ]; then ARGS="$ARGS --net-heartbeat $NET_HEARTBEAT"; fi +if [ -n "$NET_OUTPUT_SIZE" ]; then ARGS="$ARGS --net-ro-size $NET_OUTPUT_SIZE"; fi +if [ -n "$NET_OUTPUT_INTERVAL" ]; then ARGS="$ARGS --net-ro-interval $NET_OUTPUT_INTERVAL"; fi if [ "$NET_BUFFER" -le "65536" ]; then ARGS="$ARGS --net-buffer 0" elif [ "$NET_BUFFER" -le "131072" ]; then ARGS="$ARGS --net-buffer 1" elif [ "$NET_BUFFER" -le "262144" ]; then ARGS="$ARGS --net-buffer 2" @@ -69,9 +80,9 @@ else ARGS="$ARGS --net-buffer 3"; fi if [ -n "$NET_BIND_ADDRESS" ]; then ARGS="$ARGS --net-bind-address $NET_BIND_ADDRESS"; fi # misc: -if [ "x$STATS_INTERVAL" != "x0" ]; then ARGS="$ARGS --stats-every $STATS_INTERVAL"; fi -if [ "x$JSON_DIR" != "x" ]; then ARGS="$ARGS --write-json $JSON_DIR"; fi -if [ "x$JSON_INTERVAL" != "x1" ]; then ARGS="$ARGS --write-json-every $JSON_INTERVAL"; fi +if [ -n "$STATS_INTERVAL" ]; then ARGS="$ARGS --stats-every $STATS_INTERVAL"; fi +if [ -n "$JSON_DIR" ]; then ARGS="$ARGS --write-json $JSON_DIR"; fi +if [ -n "$JSON_INTERVAL" ]; then ARGS="$ARGS --write-json-every $JSON_INTERVAL"; fi if [ -n "$EXTRA_ARGS" ]; then ARGS="$ARGS $EXTRA_ARGS"; fi # Load the VERBOSE setting and other rcS variables From c6077b1e6ca74917384f59af6c328c4734558e52 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 10 Dec 2014 12:25:43 +0000 Subject: [PATCH 061/610] Use the package version as the version number compiled into the binary. --- debian/changelog | 1 + debian/rules | 4 +++- dump1090.c | 5 +++-- dump1090.h | 4 +++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/debian/changelog b/debian/changelog index 722937915..60ac2865a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -4,6 +4,7 @@ dump1090-mutability (1.08.2302.14+1mu-3) UNRELEASED; urgency=medium the interval is disabled, it makes more sense that way. * Be much more cautious about missing config settings so we don't explode so badly if something is omitted. + * Use the package version as the version number compiled into the binary. -- Oliver Jowett Wed, 10 Dec 2014 02:01:27 +0000 diff --git a/debian/rules b/debian/rules index 2f3b1c6f1..8f04ea0fc 100755 --- a/debian/rules +++ b/debian/rules @@ -14,8 +14,10 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all DPKG_EXPORT_BUILDFLAGS = 1 include /usr/share/dpkg/buildflags.mk +VER=$(shell dpkg-parsechangelog -SVersion) + override_dh_auto_build: - dh_auto_build -- 'EXTRACFLAGS=-DHTMLPATH=\"/usr/share/dump1090-mutability/html\"' + dh_auto_build -- 'EXTRACFLAGS=-DHTMLPATH=\"/usr/share/dump1090-mutability/html\" -DMODES_DUMP1090_VERSION=\"$(VER)\"' override_dh_install: dh_install diff --git a/dump1090.c b/dump1090.c index 7c06e0eb2..7d564165c 100644 --- a/dump1090.c +++ b/dump1090.c @@ -403,7 +403,7 @@ void snipMode(int level) { void showHelp(void) { printf( "-----------------------------------------------------------------------------\n" -"| dump1090 ModeS Receiver Ver : " MODES_DUMP1090_VERSION " |\n" +"| dump1090 ModeS Receiver %30s |\n" "-----------------------------------------------------------------------------\n" "--device-index Select RTL device (default: 0)\n" "--gain Set gain (default: max gain. Use -10 for auto-gain)\n" @@ -457,7 +457,8 @@ void showHelp(void) { " C = Log frames with good CRC\n" " p = Log frames with bad preamble\n" " n = Log network debugging info\n" -" j = Log frames to frames.js, loadable by debug.html\n" +" j = Log frames to frames.js, loadable by debug.html\n", +"version " MODES_DUMP1090_VERSION ); } diff --git a/dump1090.h b/dump1090.h index f7ee3eccc..2fffc3a89 100644 --- a/dump1090.h +++ b/dump1090.h @@ -37,7 +37,9 @@ // MinorVer changes when additional features are added, but not for bug fixes (range 00-99) // DayDate & Year changes for all changes, including for bug fixes. It represent the release date of the update // -#define MODES_DUMP1090_VERSION "1.10.3010.14" +#ifndef MODES_DUMP1090_VERSION +# define MODES_DUMP1090_VERSION "1.10.3010.14" +#endif // ============================= Include files ========================== From f707f2cdce7cd65a8f91133339f8eef4d6767eb0 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 10 Dec 2014 12:44:00 +0000 Subject: [PATCH 062/610] More version reporting tweaks. --- debian/rules | 3 ++- dump1090.c | 4 ++-- dump1090.h | 6 +++++- view1090.c | 5 +++-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/debian/rules b/debian/rules index 8f04ea0fc..047f5320e 100755 --- a/debian/rules +++ b/debian/rules @@ -15,9 +15,10 @@ DPKG_EXPORT_BUILDFLAGS = 1 include /usr/share/dpkg/buildflags.mk VER=$(shell dpkg-parsechangelog -SVersion) +SRCNAME=$(shell dpkg-parsechangelog -SSource) override_dh_auto_build: - dh_auto_build -- 'EXTRACFLAGS=-DHTMLPATH=\"/usr/share/dump1090-mutability/html\" -DMODES_DUMP1090_VERSION=\"$(VER)\"' + dh_auto_build -- 'EXTRACFLAGS=-DHTMLPATH=\"/usr/share/dump1090-mutability/html\" -DMODES_DUMP1090_VARIANT=\"$(SRCNAME)\" -DMODES_DUMP1090_VERSION=\"$(VER)\"' override_dh_install: dh_install diff --git a/dump1090.c b/dump1090.c index 7d564165c..46a7cdbd3 100644 --- a/dump1090.c +++ b/dump1090.c @@ -403,7 +403,7 @@ void snipMode(int level) { void showHelp(void) { printf( "-----------------------------------------------------------------------------\n" -"| dump1090 ModeS Receiver %30s |\n" +"| dump1090 ModeS Receiver %45s |\n" "-----------------------------------------------------------------------------\n" "--device-index Select RTL device (default: 0)\n" "--gain Set gain (default: max gain. Use -10 for auto-gain)\n" @@ -458,7 +458,7 @@ void showHelp(void) { " p = Log frames with bad preamble\n" " n = Log network debugging info\n" " j = Log frames to frames.js, loadable by debug.html\n", -"version " MODES_DUMP1090_VERSION +MODES_DUMP1090_VARIANT " " MODES_DUMP1090_VERSION ); } diff --git a/dump1090.h b/dump1090.h index 2fffc3a89..4dac2bbe2 100644 --- a/dump1090.h +++ b/dump1090.h @@ -38,7 +38,11 @@ // DayDate & Year changes for all changes, including for bug fixes. It represent the release date of the update // #ifndef MODES_DUMP1090_VERSION -# define MODES_DUMP1090_VERSION "1.10.3010.14" +# define MODES_DUMP1090_VERSION "1.10.3010.14+mu" +#endif + +#ifndef MODES_DUMP1090_VARIANT +# define MODES_DUMP1090_VARIANT "dump1090-mutability" #endif // ============================= Include files ========================== diff --git a/view1090.c b/view1090.c index 0b9b8b9d8..d0fc39032 100644 --- a/view1090.c +++ b/view1090.c @@ -164,7 +164,7 @@ int setupConnection(struct client *c) { void showHelp(void) { printf( "-----------------------------------------------------------------------------\n" -"| view1090 dump1090 Viewer Ver : "MODES_DUMP1090_VERSION " |\n" +"| view1090 ModeS Viewer %45s |\n" "-----------------------------------------------------------------------------\n" "--interactive Interactive mode refreshing data on screen\n" "--interactive-rows Max number of rows in interactive mode (default: 15)\n" @@ -180,7 +180,8 @@ void showHelp(void) { "--fix Enable single-bits error correction using CRC\n" "--aggressive More CPU for more messages (two bits fixes, ...)\n" "--metric Use metric units (meters, km/h, ...)\n" - "--help Show this help\n" + "--help Show this help\n", + MODES_DUMP1090_VARIANT " " MODES_DUMP1090_VERSION ); } From 9fa09e0e92b957d63a120fc888417f83e31d919e Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 10 Dec 2014 17:05:22 +0000 Subject: [PATCH 063/610] receiver.json support, internal webserver cleanup. Add data/receiver.json (generated once) and support for it in script.js. Internal webserver rearrangement to support multiple json files. --- debian/changelog | 2 + dump1090.c | 19 +++++-- dump1090.h | 10 +++- net_io.c | 124 +++++++++++++++++++++++++++++------------- public_html/script.js | 43 ++++++++++----- 5 files changed, 139 insertions(+), 59 deletions(-) diff --git a/debian/changelog b/debian/changelog index 60ac2865a..5da9885b0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,6 +5,8 @@ dump1090-mutability (1.08.2302.14+1mu-3) UNRELEASED; urgency=medium * Be much more cautious about missing config settings so we don't explode so badly if something is omitted. * Use the package version as the version number compiled into the binary. + * Add data/receiver.json (generated once) and support for it in script.js. + * Internal webserver rearrangement to support multiple json files. -- Oliver Jowett Wed, 10 Dec 2014 02:01:27 +0000 diff --git a/dump1090.c b/dump1090.c index 46a7cdbd3..619423afe 100644 --- a/dump1090.c +++ b/dump1090.c @@ -634,14 +634,15 @@ void backgroundTasks(void) { } } - if (Modes.json_path && Modes.json_interval > 0) { + if (Modes.json_aircraft_path && Modes.json_interval > 0) { time_t now = time(NULL); if (now >= next_json) { - modesWriteJson(Modes.json_path); + writeJsonToFile(Modes.json_aircraft_path, generateAircraftJson); next_json = now + Modes.json_interval; } } } + // //========================================================================= // @@ -837,9 +838,13 @@ int main(int argc, char **argv) { #ifndef _WIN32 } else if (!strcmp(argv[j], "--write-json") && more) { ++j; - Modes.json_path = malloc(strlen(argv[j]) + 15); - strcpy(Modes.json_path, argv[j]); - strcat(Modes.json_path, "/aircraft.json"); + Modes.json_aircraft_path = malloc(strlen(argv[j]) + 15); + strcpy(Modes.json_aircraft_path, argv[j]); + strcat(Modes.json_aircraft_path, "/aircraft.json"); + + Modes.json_metadata_path = malloc(strlen(argv[j]) + 15); + strcpy(Modes.json_metadata_path, argv[j]); + strcat(Modes.json_metadata_path, "/receiver.json"); } else if (!strcmp(argv[j], "--write-json-every") && more) { Modes.json_interval = atoi(argv[++j]); #endif @@ -908,6 +913,10 @@ int main(int argc, char **argv) { } if (Modes.net) modesInitNet(); + if (Modes.json_metadata_path && Modes.json_interval > 0) { + writeJsonToFile(Modes.json_metadata_path, generateReceiverJson); // once only on startup + } + // 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) { diff --git a/dump1090.h b/dump1090.h index 4dac2bbe2..909cf685b 100644 --- a/dump1090.h +++ b/dump1090.h @@ -369,8 +369,9 @@ struct { // Internal state int mlat; // Use Beast ascii format for raw data output, i.e. @...; iso *...; int interactive_rtl1090; // flight table in interactive mode is formatted like RTL1090 int no_decode; // Disable decoding and aircraft tracking - char *json_path; // Path to json data file to write, or NULL not to. - int json_interval; // Interval between rewriting the json data file + char *json_aircraft_path; // Path to json aircraft file to write, or NULL not to. + char *json_metadata_path; // Path to json metadata file to write, or NULL not to. + int json_interval; // Interval between rewriting the json aircraft file // User details double fUserLat; // Users receiver/antenna lat/lon needed for initial surface location @@ -495,7 +496,10 @@ void modesInitNet (void); void modesQueueOutput (struct modesMessage *mm); void modesReadFromClient(struct client *c, char *sep, int(*handler)(struct client *, char *)); void modesNetPeriodicWork (void); -void modesWriteJson (const char *path); + +void writeJsonToFile(const char *path, char * (*generator) (int*)); +char *generateAircraftJson(int *len); +char *generateReceiverJson(int *len); #ifdef __cplusplus } diff --git a/net_io.c b/net_io.c index c1b7b5341..a55b0e26f 100644 --- a/net_io.c +++ b/net_io.c @@ -667,7 +667,7 @@ int decodeHexMessage(struct client *c, char *hex) { // // Return a description of planes in json. No metric conversion // -char *aircraftsToJson(int *len) { +char *generateAircraftJson(int *len) { time_t now = time(NULL); struct aircraft *a = Modes.aircrafts; int buflen = 1024; // The initial buffer is incremented as needed @@ -727,15 +727,40 @@ char *aircraftsToJson(int *len) { return buf; } -// Write JSON state to json_path -void modesWriteJson(const char *path) +// +// Return a description of the receiver in json. +// +char *generateReceiverJson(int *len) +{ + char *buf = (char *) malloc(1024), *p = buf; + + p += sprintf(p, "{ " \ + "\"version\" : \"%s\", " + "\"refresh\" : %d", + MODES_DUMP1090_VERSION, Modes.json_interval * 1000); + + if (Modes.fUserLat != 0.0 || Modes.fUserLon != 0.0) { + p += sprintf(p, ", " \ + "\"lat\" : %.2f, " + "\"lon\" : %.2f", + Modes.fUserLat, Modes.fUserLon); // round to 2dp - about 0.5-1km accuracy - for privacy reasons + } + + p += sprintf(p, " }\n"); + + *len = (p - buf); + return buf; +} + +// Write JSON to file +void writeJsonToFile(const char *path, char * (*generator) (int*)) { #ifndef _WIN32 char tmppath[PATH_MAX]; int fd; int len = 0; - char *content; mode_t mask; + char *content; snprintf(tmppath, PATH_MAX, "%s.XXXXXX", path); tmppath[PATH_MAX-1] = 0; @@ -747,27 +772,29 @@ void modesWriteJson(const char *path) umask(mask); fchmod(fd, 0644 & ~mask); - content = aircraftsToJson(&len); + content = generator(&len); + if (write(fd, content, len) != len) goto error_1; if (close(fd) < 0) goto error_2; - free(content); rename(tmppath, path); + free(content); return; error_1: close(fd); error_2: - free(content); unlink(tmppath); + free(content); return; - #endif } + + // //========================================================================= // @@ -775,6 +802,17 @@ void modesWriteJson(const char *path) #define MODES_CONTENT_TYPE_CSS "text/css;charset=utf-8" #define MODES_CONTENT_TYPE_JSON "application/json;charset=utf-8" #define MODES_CONTENT_TYPE_JS "application/javascript;charset=utf-8" + +static struct { + char *path; + char * (*handler)(int*); + char *content_type; +} url_handlers[] = { + { "/data/aircraft.json", generateAircraftJson, MODES_CONTENT_TYPE_JSON }, + { "/data/receiver.json", generateReceiverJson, MODES_CONTENT_TYPE_JSON }, + { NULL, NULL, NULL } +}; + // // Get an HTTP request header and write the response to the client. // gain here we assume that the socket buffer is enough without doing @@ -788,10 +826,10 @@ int handleHTTPRequest(struct client *c, char *p) { int clen, hdrlen; int httpver, keepalive; int statuscode = 500; - char *url, *content; - char ctype[48]; - char getFile[1024]; + char *url, *content = NULL; char *ext; + char *content_type; + int i; if (Modes.debug & MODES_DEBUG_NET) printf("\nHTTP request: %s\n", c->buf); @@ -820,23 +858,30 @@ int handleHTTPRequest(struct client *c, char *p) { printf("HTTP requested URL: %s\n\n", url); } - if (strlen(url) < 2) { - snprintf(getFile, sizeof getFile, "%s/gmap.html", HTMLPATH); // Default file - } else { - snprintf(getFile, sizeof getFile, "%s/%s", HTMLPATH, url); + statuscode = 404; + for (i = 0; url_handlers[i].path; ++i) { + if (!strcmp(url, url_handlers[i].path)) { + content_type = url_handlers[i].content_type; + content = url_handlers[i].handler(&clen); + statuscode = 200; + if (Modes.debug & MODES_DEBUG_NET) { + printf("HTTP: 200: %s -> internal (%d bytes, %s)\n", url, clen, content_type); + } + break; + } } - - // Select the content to send, we have just two so far: - // "/" -> Our google map application. - // "/aircraft.json" -> Our ajax request to update planes. - if (strstr(url, "/data/aircraft.json")) { - statuscode = 200; - content = aircraftsToJson(&clen); - //snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON); - } else { + + if (!content) { struct stat sbuf; int fd = -1; char rp[PATH_MAX], hrp[PATH_MAX]; + char getFile[1024]; + + if (strlen(url) < 2) { + snprintf(getFile, sizeof getFile, "%s/gmap.html", HTMLPATH); // Default file + } else { + snprintf(getFile, sizeof getFile, "%s/%s", HTMLPATH, url); + } if (!realpath(getFile, rp)) rp[0] = 0; @@ -866,22 +911,27 @@ int handleHTTPRequest(struct client *c, char *p) { if (fd != -1) { close(fd); } - } - // Get file extension and content type - snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_HTML); // Default content type - ext = strrchr(getFile, '.'); - - if (strlen(ext) > 0) { - if (strstr(ext, ".json")) { - snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON); - } else if (strstr(ext, ".css")) { - snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_CSS); - } else if (strstr(ext, ".js")) { - snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JS); + // Get file extension and content type + content_type = MODES_CONTENT_TYPE_HTML; // Default content type + ext = strrchr(getFile, '.'); + + if (strlen(ext) > 0) { + if (strstr(ext, ".json")) { + content_type = MODES_CONTENT_TYPE_JSON; + } else if (strstr(ext, ".css")) { + content_type = MODES_CONTENT_TYPE_CSS; + } else if (strstr(ext, ".js")) { + content_type = MODES_CONTENT_TYPE_JS; + } + } + + if (Modes.debug & MODES_DEBUG_NET) { + printf("HTTP: %d: %s -> %s (%d bytes, %s)\n", statuscode, url, rp, clen, content_type); } } + // Create the header and send the reply hdrlen = snprintf(hdr, sizeof(hdr), "HTTP/1.1 %d \r\n" @@ -893,7 +943,7 @@ int handleHTTPRequest(struct client *c, char *p) { "Expires: Sat, 26 Jul 1997 05:00:00 GMT\r\n" "\r\n", statuscode, - ctype, + content_type, keepalive ? "keep-alive" : "close", clen); diff --git a/public_html/script.js b/public_html/script.js index cf12e41a9..83aa95e81 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -17,6 +17,9 @@ CenterLat = Number(localStorage['CenterLat']) || CONST_CENTERLAT; CenterLon = Number(localStorage['CenterLon']) || CONST_CENTERLON; ZoomLvl = Number(localStorage['ZoomLvl']) || CONST_ZOOMLVL; +Dump1090Version = "unknown version"; +RefreshInterval = 1000; + function fetchData() { $.getJSON('data/aircraft.json', function(data) { PlanesOnMap = 0 @@ -32,29 +35,41 @@ function fetchData() { var plane = jQuery.extend(true, {}, planeObject); } - /* For special squawk tests - if (data[j].hex == '48413x') { - data[j].squawk = '7700'; - } //*/ - - // Set SpecialSquawk-value - if (data[j].squawk == '7500' || data[j].squawk == '7600' || data[j].squawk == '7700') { - SpecialSquawk = true; - } - + // Set SpecialSquawk-value + if (data[j].squawk == '7500' || data[j].squawk == '7600' || data[j].squawk == '7700') { + SpecialSquawk = true; + } + // Call the function update plane.funcUpdateData(data[j]); // Copy the plane into Planes Planes[plane.icao] = plane; } - + PlanesOnTable = data.length; }); } -// Initalizes the map and starts up our timers to call various functions function initialize() { + // Get receiver metadata, reconfigure using it, then continue + // with initialization + $.getJSON('data/receiver.json') + .done(function(data) { + if (typeof data.receiverlat !== "undefined") { + SiteShow = true; + SiteLat = data.receiverlat; + SiteLon = data.receiverlon; + } + + Dump1090Version = data.version; + RefreshInterval = data.refresh; + }) + .always(initialize_after_config); +} + +// Initalizes the map and starts up our timers to call various functions +function initialize_after_config() { // Make a list of all the available map IDs var mapTypeIds = []; for(var type in google.maps.MapTypeId) { @@ -209,7 +224,7 @@ function initialize() { refreshSelected(); reaper(); extendedPulse(); - }, 1000); + }, RefreshInterval); } // This looks for planes to reap out of the master Planes variable @@ -257,7 +272,7 @@ function refreshSelected() { html += '' + selected.flight + ''; } else { - html += 'DUMP1090'; + html += 'DUMP1090 ' + Dump1090Version + ''; } if (selected && selected.squawk == 7500) { // Lets hope we never see this... Aircraft Hijacking From 55ecde9d5322458c736d1f022e60c9a1ac946fa4 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 10 Dec 2014 20:26:51 +0000 Subject: [PATCH 064/610] Add input validation to most of the config questions. > Some people, when confronted with a problem, think "I know, I'll use regular expressions." > Now they have two problems. Dongle "device index" actually accepts serial numbers too, document that. --- debian/changelog | 2 + debian/config-template | 2 +- debian/dump1090-mutability.config | 143 +++++++++++++++++++++++---- debian/dump1090-mutability.templates | 74 +++++++++++--- 4 files changed, 185 insertions(+), 36 deletions(-) diff --git a/debian/changelog b/debian/changelog index 5da9885b0..e574d9381 100644 --- a/debian/changelog +++ b/debian/changelog @@ -7,6 +7,8 @@ dump1090-mutability (1.08.2302.14+1mu-3) UNRELEASED; urgency=medium * Use the package version as the version number compiled into the binary. * Add data/receiver.json (generated once) and support for it in script.js. * Internal webserver rearrangement to support multiple json files. + * Dongle "device index" actually accepts serial numbers too, document that. + * Add input validation to most of the config questions. -- Oliver Jowett Wed, 10 Dec 2014 02:01:27 +0000 diff --git a/debian/config-template b/debian/config-template index ad16795e2..4c723fcf9 100644 --- a/debian/config-template +++ b/debian/config-template @@ -19,7 +19,7 @@ LOGFILE= # Receiver options # -# RTLSDR device index to use +# RTLSDR device index or serial number to use # If set to "none", dump1090 will be started in --net-only mode DEVICE= diff --git a/debian/dump1090-mutability.config b/debian/dump1090-mutability.config index 9cd1b50e1..74e49fe0d 100644 --- a/debian/dump1090-mutability.config +++ b/debian/dump1090-mutability.config @@ -52,47 +52,148 @@ fi # Ask questions. +db_input_verify() { + # $1 = priority + # $2 = db key + # $3 = verification function, should return 0 if OK + PRI=$1; KEY=$2; VERIFY=$3 + + set +e + db_input $PRI $KEY; RESULT=$? + db_go + set -e + while : + do + db_get $KEY + if $VERIFY $RET; then return 0; fi + if [ $RESULT -ne 0 ]; then + # db_input failed, and the existing value does not validate + if [ $RESULT = 30 ] && [ $PRI != high ] + then + # question wasn't displayed, but existing value is invalid + # bump priority and try again + PRI=high + else + # give up, use the default value + db_reset $KEY + return 0 + fi + fi + + # db_input was OK, but the value did not verify. + # display an error and try again. + set +e + db_input high dump1090-mutability/invalid-$VERIFY + db_fset $KEY seen false + db_input high $KEY; RESULT=$? + db_go + set -e + done +} + +is_unsigned_int() { + if echo "$1" | grep -Eq '^(0|+?[1-9][0-9]*)$'; then return 0; else return 1; fi +} + +is_unsigned_int_or_empty() { + if [ -z "$1" ]; then return 0 + elif is_unsigned_int "$1"; then return 0 + else return 1; fi +} + +is_signed_int() { + if echo "$1" | grep -Eq '^(0|[+-]?[1-9][0-9]*)$'; then return 0; else return 1; fi +} + +is_signed_int_or_empty() { + if [ -z "$1" ]; then return 0 + elif is_signed_int "$1"; then return 0 + else return 1; fi +} + +is_ipaddrish() { + if echo "$1" | grep -Eq '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'; then return 0; else return 1; fi +} + +is_ipaddrish_or_empty() { + if [ -z "$1" ]; then return 0 + elif is_ipaddrish "$1"; then return 0 + else return 1; fi +} + +is_number() { + if echo "$1" | grep -Eq '^(0|[+-]?[1-9][0-9]*)(\.[0-9]+)?$'; then return 0; else return 1; fi +} + +is_number_or_empty() { + if [ -z "$1" ]; then return 0 + elif is_number "$1"; then return 0; + else return 1; fi +} + +is_valid_gain() { + if is_number "$1"; then return 0; + elif [ "$1" = "max" ]; then return 0; + elif [ "$1" = "agc" ]; then return 0; + else return 1; fi +} + +is_not_empty() { + if [ -z "$1" ]; then return 1; else return 0; fi +} + +is_port_number() { + if is_unsigned_int "$1"; then + if [ "$1" -eq 0 ]; then return 0; fi + if [ "$1" -lt 1024 ]; then return 1; fi + if [ "$1" -gt 65535 ]; then return 1; fi + return 0 + else + return 1 + fi +} + db_input high $NAME/auto-start || true db_go || true; db_get $NAME/auto-start; if [ "$RET" = "true" ]; then # all of these are only relevant if the init script is enabled - db_input low $NAME/run-as-user || true - db_input low $NAME/log-file || true + db_input_verify low $NAME/run-as-user is_not_empty || true + db_input_verify low $NAME/log-file is_not_empty || true db_input medium $NAME/rtlsdr-device || true db_go || true; db_get $NAME/rtlsdr-device; if [ "x$RET" != "xnone" ]; then # only if a real device was chosen: - db_input medium $NAME/rtlsdr-gain || true - db_input medium $NAME/rtlsdr-ppm || true + db_input_verify medium $NAME/rtlsdr-gain is_valid_gain || true + db_input_verify medium $NAME/rtlsdr-ppm is_signed_int || true db_input low $NAME/rtlsdr-oversample || true fi db_input low $NAME/decode-fix-crc || true db_input low $NAME/decode-aggressive || true - db_input medium $NAME/decode-lat || true + db_input_verify medium $NAME/decode-lat is_number_or_empty || true db_go || true; db_get $NAME/decode-lat; if [ -n "$RET" ]; then # only if latitude was given: - db_input medium $NAME/decode-lon || true + db_input_verify medium $NAME/decode-lon is_number_or_empty || true fi - db_input medium $NAME/net-http-port || true - db_input low $NAME/net-ri-port || true - db_input low $NAME/net-ro-port || true - db_input low $NAME/net-bi-port || true - db_input low $NAME/net-bo-port || true - db_input low $NAME/net-sbs-port || true - db_input low $NAME/net-fatsv-port || true - db_input low $NAME/net-heartbeat || true - db_input low $NAME/net-out-size || true - db_input low $NAME/net-out-interval || true - db_input low $NAME/net-buffer || true - db_input medium $NAME/net-bind-address || true - - db_input low $NAME/stats-interval || true - db_input low $NAME/json-interval || true + db_input_verify medium $NAME/net-http-port is_port_number || true + db_input_verify low $NAME/net-ri-port is_port_number || true + db_input_verify low $NAME/net-ro-port is_port_number || true + db_input_verify low $NAME/net-bi-port is_port_number || true + db_input_verify low $NAME/net-bo-port is_port_number || true + db_input_verify low $NAME/net-sbs-port is_port_number || true + db_input_verify low $NAME/net-fatsv-port is_port_number || true + db_input_verify low $NAME/net-heartbeat is_unsigned_int || true + db_input_verify low $NAME/net-out-size is_unsigned_int || true + db_input_verify low $NAME/net-out-interval is_unsigned_int || true + db_input_verify low $NAME/net-buffer is_unsigned_int || true + db_input_verify medium $NAME/net-bind-address is_ipaddrish_or_empty || true + + db_input_verify low $NAME/stats-interval is_unsigned_int || true + db_input_verify low $NAME/json-interval is_unsigned_int || true db_go || true; db_get $NAME/json-interval; if [ -n "$RET" ] && [ "$RET" -gt 0 ]; then # only if json-interval was given and non-zero diff --git a/debian/dump1090-mutability.templates b/debian/dump1090-mutability.templates index 92e490286..2e14b57b5 100644 --- a/debian/dump1090-mutability.templates +++ b/debian/dump1090-mutability.templates @@ -2,6 +2,10 @@ Template: dump1090-mutability/auto-start Description: Start dump1090 automatically? dump1090 can be started automatically via an init-script. Otherwise, the init-script does nothing; you must run dump1090 by hand. + . + You can modify the options used when automatically starting + dump1090 by running "dpkg-reconfigure dump1090-mutability" as root, + or by editing /etc/default/dump1090-mutability. Type: boolean Default: true @@ -21,12 +25,15 @@ Default: /var/log/dump1090-mutability.log Template: dump1090-mutability/rtlsdr-device Description: RTL-SDR dongle to use: - If you have only one dongle connected, select 0. If you have more than one - dongle connected, select the index of the dongle to use (0 is the first - dongle). If you want to run dump1090 in "net only" mode, select "none". -Type: select -Choices: none, 0, 1, 2, 3, 4, 5 -Default: 0 + If you have only one dongle connected, you can leave this blank. + . + Otherwise, you can provide the serial number (more reliable) or device + index (first device = 0, but the ordering is unpredictable) to choose + a particular dongle to use. + . + To run dump1090 in "net only" mode, specify the literal word "none". +Type: string +Default: Template: dump1090-mutability/rtlsdr-gain Description: RTL-SDR gain, in dB: @@ -208,19 +215,22 @@ Default: 3600 Template: dump1090-mutability/json-dir Description: Directory to write JSON aircraft state to: - dump1090 periodicallys write a list of aircraft, in JSON format, for use - by the virtual radar view when using an external webserver. This setting - controls the directory to write to. - . - As this is written frequently (by default, once a second), you should - probably select a location that is not on a sdcard. The default path - under /run is on tmpfs and will not write to the sdcard. + As this can be written frequently, you should select a location + that is not on a sdcard. The default path under /run is on tmpfs + and will not write to the sdcard. Type: string Default: /run/dump1090-mutability Template: dump1090-mutability/json-interval Description: Interval between writing JSON aircraft state, in seconds: - Here you can control how often the JSON state is updated. + dump1090 periodically write a list of aircraft, in JSON format, for use + by the virtual radar view when using an external webserver. This setting + controls the directory to write to. + . + Here you can control how often the JSON state is updated, which determines + how frequently the virtual radar view updates. + . + A value of 0 will disable writing JSON state entirely. Type: string Default: 1 @@ -229,3 +239,39 @@ Description: Extra arguments to pass to dump1090: Here you can add any extra arguments you want to pass to dump1090. Type: string Default: + +Template: dump1090-mutability/invalid-is_unsigned_int +Description: Value must be an unsigned integer. +Type: error + +Template: dump1090-mutability/invalid-is_unsigned_int_or_empty +Description: Value must be an unsigned integer, or blank. +Type: error + +Template: dump1090-mutability/invalid-is_signed_int +Description: Value must be an integer. +Type: error + +Template: dump1090-mutability/invalid-is_signed_int_or_empty +Description: Value must be an integer, or blank. +Type: error + +Template: dump1090-mutability/invalid-is_not_empty +Description: Value cannot be empty. +Type: error + +Template: dump1090-mutability/invalid-is_port_number +Description: Value must be a valid port number (1024-65535), or zero to disable. +Type: error + +Template: dump1090-mutability/invalid-is_ipaddrish_or_empty +Description: Value must be an IP address or empty. +Type: error + +Template: dump1090-mutability/invalid-is_number_or_empty +Description: Value must be a decimal number or empty. +Type: error + +Template: dump1090-mutability/invalid-is_valid_gain +Description: Value must be a numeric gain value, or "max", or "agc". +Type: error From 3af0fb51b575c29dfb1048871e93089c73a576f7 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 10 Dec 2014 21:07:45 +0000 Subject: [PATCH 065/610] Raspbian's dpkg-changelog doesn't understand -S, use some sed magic instead. --- debian/rules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/rules b/debian/rules index 047f5320e..dc9e15ba8 100755 --- a/debian/rules +++ b/debian/rules @@ -14,8 +14,8 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all DPKG_EXPORT_BUILDFLAGS = 1 include /usr/share/dpkg/buildflags.mk -VER=$(shell dpkg-parsechangelog -SVersion) -SRCNAME=$(shell dpkg-parsechangelog -SSource) +VER=$(shell dpkg-parsechangelog | sed -n 's/^Version: //p') +SRCNAME=$(shell dpkg-parsechangelog | sed -n 's/^Source: //p') override_dh_auto_build: dh_auto_build -- 'EXTRACFLAGS=-DHTMLPATH=\"/usr/share/dump1090-mutability/html\" -DMODES_DUMP1090_VARIANT=\"$(SRCNAME)\" -DMODES_DUMP1090_VERSION=\"$(VER)\"' From 1cdfe8db2749a050a697acb36df74d955106e9ac Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 10 Dec 2014 21:20:31 +0000 Subject: [PATCH 066/610] Rearrange validation so that the user only ever sees errors *after* providing input. In particular if the existing config does not validate then we should just ensure that the problematic question is displayed without a confusing error message beforehand; the error should only appear if the newly entered response doesn't validate. --- debian/dump1090-mutability.config | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/debian/dump1090-mutability.config b/debian/dump1090-mutability.config index 74e49fe0d..48df016a3 100644 --- a/debian/dump1090-mutability.config +++ b/debian/dump1090-mutability.config @@ -62,28 +62,32 @@ db_input_verify() { db_input $PRI $KEY; RESULT=$? db_go set -e + ASKED=0 while : do db_get $KEY if $VERIFY $RET; then return 0; fi if [ $RESULT -ne 0 ]; then # db_input failed, and the existing value does not validate - if [ $RESULT = 30 ] && [ $PRI != high ] + if [ $RESULT = 30 ] && [ $ASKED = 0 ] then - # question wasn't displayed, but existing value is invalid - # bump priority and try again - PRI=high + # question was skipped, but existing value is invalid + # bump priority and try again (once) + PRI=high + ASKED=1 else # give up, use the default value db_reset $KEY return 0 fi + else + # db_input was OK, but the value did not verify. + # show an error message + db_input high dump1090-mutability/invalid-$VERIFY || true fi - # db_input was OK, but the value did not verify. - # display an error and try again. + # try again set +e - db_input high dump1090-mutability/invalid-$VERIFY db_fset $KEY seen false db_input high $KEY; RESULT=$? db_go From f04c4d5ddee894a285d963d6fa6ebf73ff738955 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 10 Dec 2014 21:34:05 +0000 Subject: [PATCH 067/610] Fix webmap to look for site location under the right names. --- public_html/script.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public_html/script.js b/public_html/script.js index 83aa95e81..8cbbc2ac2 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -56,10 +56,10 @@ function initialize() { // with initialization $.getJSON('data/receiver.json') .done(function(data) { - if (typeof data.receiverlat !== "undefined") { + if (typeof data.lat !== "undefined") { SiteShow = true; - SiteLat = data.receiverlat; - SiteLon = data.receiverlon; + SiteLat = data.lat; + SiteLon = data.lon; } Dump1090Version = data.version; From ff027f009ac7bae9c9b7cf8d3f1342af78e89e94 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 10 Dec 2014 21:58:26 +0000 Subject: [PATCH 068/610] Release changelog. --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index e574d9381..ac2b9dd83 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -dump1090-mutability (1.08.2302.14+1mu-3) UNRELEASED; urgency=medium +dump1090-mutability (1.08.2302.14+1mu-3) unstable; urgency=medium * Ask about json interval before json dir and skip the dir question if the interval is disabled, it makes more sense that way. @@ -10,7 +10,7 @@ dump1090-mutability (1.08.2302.14+1mu-3) UNRELEASED; urgency=medium * Dongle "device index" actually accepts serial numbers too, document that. * Add input validation to most of the config questions. - -- Oliver Jowett Wed, 10 Dec 2014 02:01:27 +0000 + -- Oliver Jowett Wed, 10 Dec 2014 21:58:12 +0000 dump1090-mutability (1.08.2302.14+1mu-2) unstable; urgency=medium From fbed55dee7bf899fd94842d0c97bed473b0ae622 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Dec 2014 19:25:56 +0000 Subject: [PATCH 069/610] Doc updates. --- README-dump1090.md | 284 +++++++++++++++++++++++++++++++++++++++ README.md | 328 +++++++++++---------------------------------- 2 files changed, 359 insertions(+), 253 deletions(-) create mode 100644 README-dump1090.md diff --git a/README-dump1090.md b/README-dump1090.md new file mode 100644 index 000000000..3fdd396ed --- /dev/null +++ b/README-dump1090.md @@ -0,0 +1,284 @@ +Dump1090 README +=== + +Dump 1090 is a Mode S decoder specifically designed for RTLSDR devices. + +The main features are: + +* Robust decoding of weak messages, with mode1090 many users observed + improved range compared to other popular decoders. +* Network support: TCP30003 stream (MSG5...), Raw packets, HTTP. +* Embedded HTTP server that displays the currently detected aircrafts on + Google Map. +* Single bit errors correction using the 24 bit CRC. +* Ability to decode DF11, DF17 messages. +* Ability to decode DF formats like DF0, DF4, DF5, DF16, DF20 and DF21 + where the checksum is xored with the ICAO address by brute forcing the + checksum field using recently seen ICAO addresses. +* Decode raw IQ samples from file (using --ifile command line switch). +* Interactive command-line-interfae mode where aircrafts currently detected + are shown as a list refreshing as more data arrives. +* CPR coordinates decoding and track calculation from velocity. +* TCP server streaming and recceiving raw data to/from connected clients + (using --net). + +Installation +--- + +Type "make". + +Normal usage +--- + +To capture traffic directly from your RTL device and show the captured traffic +on standard output, just run the program without options at all: + + ./dump1090 + +To just output hexadecimal messages: + + ./dump1090 --raw + +To run the program in interactive mode: + + ./dump1090 --interactive + +To run the program in interactive mode, with networking support, and connect +with your browser to http://localhost:8080 to see live traffic: + + ./dump1090 --interactive --net + +In iteractive mode it is possible to have a less information dense but more +"arcade style" output, where the screen is refreshed every second displaying +all the recently seen aircrafts with some additional information such as +altitude and flight number, extracted from the received Mode S packets. + +Using files as source of data +--- + +To decode data from file, use: + + ./dump1090 --ifile /path/to/binfile + +The binary file should be created using `rtl_sdr` like this (or with any other +program that is able to output 8-bit unsigned IQ samples at 2Mhz sample rate). + + rtl_sdr -f 1090000000 -s 2000000 -g 50 output.bin + +In the example `rtl_sdr` a gain of 50 is used, simply you should use the highest +gain availabe for your tuner. This is not needed when calling Dump1090 itself +as it is able to select the highest gain supported automatically. + +It is possible to feed the program with data via standard input using +the --ifile option with "-" as argument. + +Additional options +--- + +Dump1090 can be called with other command line options to set a different +gain, frequency, and so forth. For a list of options use: + + ./dump1090 --help + +Everything is not documented here should be obvious, and for most users calling +it without arguments at all is the best thing to do. + +Reliability +--- + +By default Dump1090 checks for decoding errors using the 24-bit CRC checksum, +where available. Messages with errors are discarded. + +The --fix command line switch enables fixing single bit error correction +based on the CRC checksum. Technically, it uses a table of precomputed +checksum differences resulting from single bit errors to look up the +wrong bit position. + +This is indeed able to fix errors and works reliably in my experience, +however if you are interested in very reliable data I suggest to use +the --no-fix command line switch in order to disable error fixing. + +Performances and sensibility of detection +--- + +In my limited experience Dump1090 was able to decode a big number of messages +even in conditions where I encountered problems using other programs, however +no formal test was performed so I can't really claim that this program is +better or worse compared to other similar programs. + +If you can capture traffic that Dump1090 is not able to decode properly, drop +me an email with a download link. I may try to improve the detection during +my free time (this is just an hobby project). + +Network server features +--- + +By enabling the networking support with --net Dump1090 starts listening +for clients connections on port 30002 and 30001 (you can change both the +ports if you want, see --help output). + +Port 30002 +--- + +Connected clients are served with data ASAP as they arrive from the device +(or from file if --ifile is used) in the raw format similar to the following: + + *8D451E8B99019699C00B0A81F36E; + +Every entry is separated by a simple newline (LF character, hex 0x0A). + +Port 30001 +--- + +Port 30001 is the raw input port, and can be used to feed Dump1090 with +data in the same format as specified above, with hex messages starting with +a `*` and ending with a `;` character. + +So for instance if there is another remote Dump1090 instance collecting data +it is possible to sum the output to a local Dump1090 instance doing something +like this: + + nc remote-dump1090.example.net 30002 | nc localhost 30001 + +It is important to note that what is received via port 30001 is also +broadcasted to clients listening to port 30002. + +In general everything received from port 30001 is handled exactly like the +normal traffic from RTL devices or from file when --ifile is used. + +It is possible to use Dump1090 just as an hub using --ifile with /dev/zero +as argument as in the following example: + + ./dump1090 --net-only + +Or alternatively to see what's happening on the screen: + + ./dump1090 --net-only --interactive + +Then you can feed it from different data sources from the internet. + +Port 30003 +--- + +Connected clients are served with messages in SBS1 (BaseStation) format, +similar to: + + MSG,4,,,738065,,,,,,,,420,179,,,0,,0,0,0,0 + MSG,3,,,738065,,,,,,,35000,,,34.81609,34.07810,,,0,0,0,0 + +This can be used to feed data to various sharing sites without the need to use another decoder. + +Antenna +--- + +Mode S messages are transmitted in the 1090 Mhz frequency. If you have a decent +antenna you'll be able to pick up signals from aircrafts pretty far from your +position, especially if you are outdoor and in a position with a good sky view. + +You can easily build a very cheap antenna following the istructions at: + + http://antirez.com/news/46 + +With this trivial antenna I was able to pick up signals of aircrafts 200+ Km +away from me. + +If you are interested in a more serious antenna check the following +resources: + +* http://gnuradio.org/redmine/attachments/download/246/06-foster-adsb.pdf +* http://www.lll.lu/~edward/edward/adsb/antenna/ADSBantenna.html +* http://modesbeast.com/pix/adsb-ant-drawing.gif + +Aggressive mode +--- + +With --aggressive it is possible to activate the *aggressive mode* that is a +modified version of the Mode S packet detection and decoding. +The aggresive mode uses more CPU usually (especially if there are many planes +sending DF17 packets), but can detect a few more messages. + +The algorithm in aggressive mode is modified in the following ways: + +* Up to two demodulation errors are tolerated (adjacent entires in the + magnitude vector with the same eight). Normally only messages without + errors are checked. +* It tries to fix DF17 messages with CRC errors resulting from any two bit + errors. + +The use of aggressive mdoe is only advised in places where there is +low traffic in order to have a chance to capture some more messages. + +Debug mode +--- + +The Debug mode is a visual help to improve the detection algorithm or to +understand why the program is not working for a given input. + +In this mode messages are displayed in an ASCII-art style graphical +representation, where the individial magnitude bars sampled at 2Mhz are +displayed. + +An index shows the sample number, where 0 is the sample where the first +Mode S peak was found. Some additional background noise is also added +before the first peak to provide some context. + +To enable debug mode and check what combinations of packets you can +log, use `mode1090 --help` to obtain a list of available debug flags. + +Debug mode includes an optional javascript output that is used to visualize +packets using a web browser, you can use the file debug.html under the +'tools' directory to load the generated frames.js file. + +How this program works? +--- + +The code is very documented and written in order to be easy to understand. +For the diligent programmer with a Mode S specification on his hands it +should be trivial to understand how it works. + +The algorithms I used were obtained basically looking at many messages +as displayed using a trow-away SDL program, and trying to model the algorithm +based on how the messages look graphically. + +How to test the program? +--- + +If you have an RTLSDR device and you happen to be in an area where there +are aircrafts flying over your head, just run the program and check for signals. + +However if you don't have an RTLSDR device, or if in your area the presence +of aircrafts is very limited, you may want to try the sample file distributed +with the Dump1090 distribution under the "testfiles" directory. + +Just run it like this: + + ./dump1090 --ifile testfiles/modes1.bin + +What is --strip mode? +--- + +It is just a simple filter that will get raw IQ 8 bit samples in input +and will output a file missing all the parts of the file where I and Q +are lower than the specified for more than 32 samples. + +Use it like this: + + cat big.bin | ./dump1090 --snip 25 > small.bin + +I used it in order to create a small test file to include inside this +program source code distribution. + +Contributing +--- + +Dump1090 was written during some free time during xmas 2012, it is an hobby +project so I'll be able to address issues and improve it only during +free time, however you are incouraged to send pull requests in order to +improve the program. A good starting point can be the TODO list included in +the source distribution. + +Credits +--- + +Dump1090 was written by Salvatore Sanfilippo and is +released under the BSD three clause license. diff --git a/README.md b/README.md index 3fdd396ed..f5d051eb5 100644 --- a/README.md +++ b/README.md @@ -1,284 +1,106 @@ -Dump1090 README -=== +# dump1090-mutability Debian/Raspbian packages -Dump 1090 is a Mode S decoder specifically designed for RTLSDR devices. +This is a fork of MalcolmRobb's version of dump1090 +that adds new functionality and is designed to be built as +a Debian/Raspbian package. -The main features are: +# Features -* Robust decoding of weak messages, with mode1090 many users observed - improved range compared to other popular decoders. -* Network support: TCP30003 stream (MSG5...), Raw packets, HTTP. -* Embedded HTTP server that displays the currently detected aircrafts on - Google Map. -* Single bit errors correction using the 24 bit CRC. -* Ability to decode DF11, DF17 messages. -* Ability to decode DF formats like DF0, DF4, DF5, DF16, DF20 and DF21 - where the checksum is xored with the ICAO address by brute forcing the - checksum field using recently seen ICAO addresses. -* Decode raw IQ samples from file (using --ifile command line switch). -* Interactive command-line-interfae mode where aircrafts currently detected - are shown as a list refreshing as more data arrives. -* CPR coordinates decoding and track calculation from velocity. -* TCP server streaming and recceiving raw data to/from connected clients - (using --net). +* 2.4MHz "oversampling" support +* doesn't run as root +* supports FlightAware-TSV-format connections directly (same as the FlightAware version - no faup1090 needed) +* can start from init.d, with detailed config via debconf or `/etc/default/dump1090-mutability` +* can serve the virtual radar map via an external webserver (lighttpd integration included by default) +* map view uses receiver lat/long given to dump1090 automatically +* somewhat cleaned-up network code +* tries to do things "the debian way" when it comes to config, package structure, etc +* probably a bunch of other things I've forgotten.. -Installation ---- +# Simple install via apt-get -Type "make". +There is a repository that contains the current releases. +To install from it: -Normal usage ---- +```` +$ sudo bash +# echo "deb http://repo.mutability.co.uk/raspbian wheezy rpi" >/etc/apt/sources.list.d/mutability.list +# apt-get update && apt-get install dump1090-mutability +# dpkg-reconfigure dump1090-mutability # for detailed configuration +# apt-get install lighttpd && lighty-enable-mod dump1090 # if you want to use the external webserver integration +```` -To capture traffic directly from your RTL device and show the captured traffic -on standard output, just run the program without options at all: +The repository and packages are (currently) unsigned, you will have to confirm installing from an unsigned source. - ./dump1090 +# Manual installation -To just output hexadecimal messages: +You will need a librtlsdr0 package for Raspbian. +There is no standard build of this. +I have built suitable packages that are available from +[this release page](https://github.com/mutability/librtlsdr/releases) - ./dump1090 --raw +Then you will need the dump1090-mutability package itself from +[this release page](https://github.com/mutability/dump1090/releases) -To run the program in interactive mode: +Install the packages with dpkg. - ./dump1090 --interactive +# Configuraion -To run the program in interactive mode, with networking support, and connect -with your browser to http://localhost:8080 to see live traffic: +By default it'll only ask you whether to start automatically and assume sensible defaults for everything else. +Notable defaults that are perhaps not what you'd first expect: - ./dump1090 --interactive --net +* All network ports are bound to the localhost interface only. + If you need remote access to the ADS-B data ports, you will want to change this to bind to the wildcard address. +* The internal HTTP server is disabled. I recommend using an external webserver (see below). + You can reconfigure to enable the internal one if you don't want to use an external one. -In iteractive mode it is possible to have a less information dense but more -"arcade style" output, where the screen is refreshed every second displaying -all the recently seen aircrafts with some additional information such as -altitude and flight number, extracted from the received Mode S packets. +To reconfigure, either use dpkg-reconfigure dump1090-mutability or edit /etc/default/dump1090-mutability. Both should be self-explanatory. -Using files as source of data ---- +## External webserver configuration -To decode data from file, use: +This is the recommended configuration; a dedicated webserver is almost always going to be better and more secure than the collection of hacks that is the dump1090 webserver. +It works by having dump1090 write json files to a path under /run once a second (this is on tmpfs and will not write to the sdcard). +Then an external webserver is used to serve both the static html/javascript files making up the map view, and the json files that provide the dynamic data. - ./dump1090 --ifile /path/to/binfile +The package includes a config file for lighttpd (which is what I happen to use on my system). +To use this: -The binary file should be created using `rtl_sdr` like this (or with any other -program that is able to output 8-bit unsigned IQ samples at 2Mhz sample rate). +```` +# apt-get install lighttpd # if you don't have it already +# lighty-enable-mod dump1090 +# service lighttpd force-reload +```` - rtl_sdr -f 1090000000 -s 2000000 -g 50 output.bin +This uses a configuration file installed by the package at `/etc/lighttpd/conf-available/89-dump1090.conf`. +It makes the map view available at http:///dump1090/ -In the example `rtl_sdr` a gain of 50 is used, simply you should use the highest -gain availabe for your tuner. This is not needed when calling Dump1090 itself -as it is able to select the highest gain supported automatically. +This should also work fine with other webservers, you will need to write a similar config to the lighttpd one (it's basically just a couple of aliases). +If you do set up a config for something else, please send me a copy so I can integrate it into the package! -It is possible to feed the program with data via standard input using -the --ifile option with "-" as argument. +## Logging -Additional options ---- +The default configuration logs to `/var/log/dump1090-mutability.log` (this can be reconfigured). +The only real logging other than any startup problems is hourly stats. +There is a logrotate configuration installed by the package at `/etc/logrotate.d/dump1090-mutability` that will rotate that logfile weekly. -Dump1090 can be called with other command line options to set a different -gain, frequency, and so forth. For a list of options use: +# Bug reports, feedback etc - ./dump1090 --help +Please use the [github issues page](https://github.com/mutability/dump1090/issues) to report any problems. +Or you can [email me](oliver@mutability.co.uk). -Everything is not documented here should be obvious, and for most users calling -it without arguments at all is the best thing to do. +# Future plans -Reliability ---- +Packages following the same model for MalcolmRobb & FlightAware's forks of dump1090 are in the pipeline. +So is a repackaged version of piaware. -By default Dump1090 checks for decoding errors using the 24-bit CRC checksum, -where available. Messages with errors are discarded. +# Building from source -The --fix command line switch enables fixing single bit error correction -based on the CRC checksum. Technically, it uses a table of precomputed -checksum differences resulting from single bit errors to look up the -wrong bit position. +While there is a Makefile that you can use, the preferred way to build is via the Debian package building system: -This is indeed able to fix errors and works reliably in my experience, -however if you are interested in very reliable data I suggest to use -the --no-fix command line switch in order to disable error fixing. +```` +$ sudo apt-get install librtlsdr-dev libusb-1.0-0-dev pkg-config debhelper +$ dpkg-buildpackage -b +```` -Performances and sensibility of detection ---- - -In my limited experience Dump1090 was able to decode a big number of messages -even in conditions where I encountered problems using other programs, however -no formal test was performed so I can't really claim that this program is -better or worse compared to other similar programs. - -If you can capture traffic that Dump1090 is not able to decode properly, drop -me an email with a download link. I may try to improve the detection during -my free time (this is just an hobby project). - -Network server features ---- - -By enabling the networking support with --net Dump1090 starts listening -for clients connections on port 30002 and 30001 (you can change both the -ports if you want, see --help output). - -Port 30002 ---- - -Connected clients are served with data ASAP as they arrive from the device -(or from file if --ifile is used) in the raw format similar to the following: - - *8D451E8B99019699C00B0A81F36E; - -Every entry is separated by a simple newline (LF character, hex 0x0A). - -Port 30001 ---- - -Port 30001 is the raw input port, and can be used to feed Dump1090 with -data in the same format as specified above, with hex messages starting with -a `*` and ending with a `;` character. - -So for instance if there is another remote Dump1090 instance collecting data -it is possible to sum the output to a local Dump1090 instance doing something -like this: - - nc remote-dump1090.example.net 30002 | nc localhost 30001 - -It is important to note that what is received via port 30001 is also -broadcasted to clients listening to port 30002. - -In general everything received from port 30001 is handled exactly like the -normal traffic from RTL devices or from file when --ifile is used. - -It is possible to use Dump1090 just as an hub using --ifile with /dev/zero -as argument as in the following example: - - ./dump1090 --net-only - -Or alternatively to see what's happening on the screen: - - ./dump1090 --net-only --interactive - -Then you can feed it from different data sources from the internet. - -Port 30003 ---- - -Connected clients are served with messages in SBS1 (BaseStation) format, -similar to: - - MSG,4,,,738065,,,,,,,,420,179,,,0,,0,0,0,0 - MSG,3,,,738065,,,,,,,35000,,,34.81609,34.07810,,,0,0,0,0 - -This can be used to feed data to various sharing sites without the need to use another decoder. - -Antenna ---- - -Mode S messages are transmitted in the 1090 Mhz frequency. If you have a decent -antenna you'll be able to pick up signals from aircrafts pretty far from your -position, especially if you are outdoor and in a position with a good sky view. - -You can easily build a very cheap antenna following the istructions at: - - http://antirez.com/news/46 - -With this trivial antenna I was able to pick up signals of aircrafts 200+ Km -away from me. - -If you are interested in a more serious antenna check the following -resources: - -* http://gnuradio.org/redmine/attachments/download/246/06-foster-adsb.pdf -* http://www.lll.lu/~edward/edward/adsb/antenna/ADSBantenna.html -* http://modesbeast.com/pix/adsb-ant-drawing.gif - -Aggressive mode ---- - -With --aggressive it is possible to activate the *aggressive mode* that is a -modified version of the Mode S packet detection and decoding. -The aggresive mode uses more CPU usually (especially if there are many planes -sending DF17 packets), but can detect a few more messages. - -The algorithm in aggressive mode is modified in the following ways: - -* Up to two demodulation errors are tolerated (adjacent entires in the - magnitude vector with the same eight). Normally only messages without - errors are checked. -* It tries to fix DF17 messages with CRC errors resulting from any two bit - errors. - -The use of aggressive mdoe is only advised in places where there is -low traffic in order to have a chance to capture some more messages. - -Debug mode ---- - -The Debug mode is a visual help to improve the detection algorithm or to -understand why the program is not working for a given input. - -In this mode messages are displayed in an ASCII-art style graphical -representation, where the individial magnitude bars sampled at 2Mhz are -displayed. - -An index shows the sample number, where 0 is the sample where the first -Mode S peak was found. Some additional background noise is also added -before the first peak to provide some context. - -To enable debug mode and check what combinations of packets you can -log, use `mode1090 --help` to obtain a list of available debug flags. - -Debug mode includes an optional javascript output that is used to visualize -packets using a web browser, you can use the file debug.html under the -'tools' directory to load the generated frames.js file. - -How this program works? ---- - -The code is very documented and written in order to be easy to understand. -For the diligent programmer with a Mode S specification on his hands it -should be trivial to understand how it works. - -The algorithms I used were obtained basically looking at many messages -as displayed using a trow-away SDL program, and trying to model the algorithm -based on how the messages look graphically. - -How to test the program? ---- - -If you have an RTLSDR device and you happen to be in an area where there -are aircrafts flying over your head, just run the program and check for signals. - -However if you don't have an RTLSDR device, or if in your area the presence -of aircrafts is very limited, you may want to try the sample file distributed -with the Dump1090 distribution under the "testfiles" directory. - -Just run it like this: - - ./dump1090 --ifile testfiles/modes1.bin - -What is --strip mode? ---- - -It is just a simple filter that will get raw IQ 8 bit samples in input -and will output a file missing all the parts of the file where I and Q -are lower than the specified for more than 32 samples. - -Use it like this: - - cat big.bin | ./dump1090 --snip 25 > small.bin - -I used it in order to create a small test file to include inside this -program source code distribution. - -Contributing ---- - -Dump1090 was written during some free time during xmas 2012, it is an hobby -project so I'll be able to address issues and improve it only during -free time, however you are incouraged to send pull requests in order to -improve the program. A good starting point can be the TODO list included in -the source distribution. - -Credits ---- - -Dump1090 was written by Salvatore Sanfilippo and is -released under the BSD three clause license. +Or you can use debuild, pdebuild, etc. +I find building via qemubuilder quite effective for building images for Raspbian (it's actually faster to build +on an emulated ARM running on my PC than to build directly on real hardware) From 478122e7a3e87814f1e06af5b7205724ceb116cd Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Dec 2014 19:29:54 +0000 Subject: [PATCH 070/610] more docs --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f5d051eb5..c15f28b3a 100644 --- a/README.md +++ b/README.md @@ -53,12 +53,12 @@ Notable defaults that are perhaps not what you'd first expect: * The internal HTTP server is disabled. I recommend using an external webserver (see below). You can reconfigure to enable the internal one if you don't want to use an external one. -To reconfigure, either use dpkg-reconfigure dump1090-mutability or edit /etc/default/dump1090-mutability. Both should be self-explanatory. +To reconfigure, either use `dpkg-reconfigure dump1090-mutability` or edit `/etc/default/dump1090-mutability`. Both should be self-explanatory. ## External webserver configuration This is the recommended configuration; a dedicated webserver is almost always going to be better and more secure than the collection of hacks that is the dump1090 webserver. -It works by having dump1090 write json files to a path under /run once a second (this is on tmpfs and will not write to the sdcard). +It works by having dump1090 write json files to a path under `/run` once a second (this is on tmpfs and will not write to the sdcard). Then an external webserver is used to serve both the static html/javascript files making up the map view, and the json files that provide the dynamic data. The package includes a config file for lighttpd (which is what I happen to use on my system). @@ -85,7 +85,7 @@ There is a logrotate configuration installed by the package at `/etc/logrotate.d # Bug reports, feedback etc Please use the [github issues page](https://github.com/mutability/dump1090/issues) to report any problems. -Or you can [email me](oliver@mutability.co.uk). +Or you can [email me](mailto:oliver@mutability.co.uk). # Future plans From e2153eccdeb24941aa098a51a548d57e10c8ae68 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Dec 2014 19:34:24 +0000 Subject: [PATCH 071/610] Agh. another typo. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c15f28b3a..d0c4a95a0 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Then you will need the dump1090-mutability package itself from Install the packages with dpkg. -# Configuraion +# Configuration By default it'll only ask you whether to start automatically and assume sensible defaults for everything else. Notable defaults that are perhaps not what you'd first expect: From 48986c48cc4410e487d3d0759be5b3c6ea2d9431 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Dec 2014 20:11:10 +0000 Subject: [PATCH 072/610] Remove half-implemented --no-decode option. --- debian/changelog | 6 ++++++ dump1090.c | 25 ------------------------- dump1090.h | 1 - mode_s.c | 14 +------------- 4 files changed, 7 insertions(+), 39 deletions(-) diff --git a/debian/changelog b/debian/changelog index ac2b9dd83..93087f4e8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +dump1090-mutability (1.08.2302.14+1mu-4) UNRELEASED; urgency=medium + + * Remove half-implemented --no-decode option. + + -- Oliver Jowett Sat, 27 Dec 2014 20:08:44 +0000 + dump1090-mutability (1.08.2302.14+1mu-3) unstable; urgency=medium * Ask about json interval before json dir and skip the dir question if diff --git a/dump1090.c b/dump1090.c index 619423afe..d1b03e5da 100644 --- a/dump1090.c +++ b/dump1090.c @@ -830,8 +830,6 @@ int main(int argc, char **argv) { } else if (!strcmp(argv[j],"--interactive-rtl1090")) { Modes.interactive = 1; 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"); @@ -857,29 +855,6 @@ int main(int argc, char **argv) { } } - // Handle --no-decode, which turns off various parts of decoding - // that are not useful for an "edge" dump1090 that purely forwards - // raw data to a central hub elsewhere. - if (Modes.no_decode) { - if (Modes.interactive) { - fprintf(stderr, "--no-decode and --interactive cannot be specified together.\n"); - exit(1); - } - - if (Modes.net_output_sbs_port != MODES_NET_OUTPUT_SBS_PORT) { - fprintf(stderr, "--no-decode and --net-sbs-port cannot be specified together.\n"); - exit(1); - } - - if (Modes.net_http_port != MODES_NET_HTTP_PORT) { - fprintf(stderr, "--no-decode and --net-http-port cannot be specified together.\n"); - exit(1); - } - - Modes.net_output_sbs_port = 0; - Modes.net_http_port = 0; - } - #ifdef _WIN32 // Try to comply with the Copyright license conditions for binary distribution if (!Modes.quiet) {showCopyright();} diff --git a/dump1090.h b/dump1090.h index 909cf685b..d6df11433 100644 --- a/dump1090.h +++ b/dump1090.h @@ -368,7 +368,6 @@ struct { // Internal state int metric; // Use metric units int mlat; // Use Beast ascii format for raw data output, i.e. @...; iso *...; int interactive_rtl1090; // flight table in interactive mode is formatted like RTL1090 - int no_decode; // Disable decoding and aircraft tracking char *json_aircraft_path; // Path to json aircraft file to write, or NULL not to. char *json_metadata_path; // Path to json metadata file to write, or NULL not to. int json_interval; // Interval between rewriting the json aircraft file diff --git a/mode_s.c b/mode_s.c index 5c0b0e6f9..088da82b7 100644 --- a/mode_s.c +++ b/mode_s.c @@ -911,9 +911,6 @@ void decodeModesMessage(struct modesMessage *mm, unsigned char *msg) { // of the data contents, so save time and give up now. if ((Modes.check_crc) && (!mm->crcok) && (!mm->correctedbits)) { return;} - // If decoding is disabled, this is as far as we go. - if (Modes.no_decode) return; - // Fields for DF0, DF16 if (mm->msgtype == 0 || mm->msgtype == 16) { if (msg[0] & 0x04) { // VS Bit @@ -1184,12 +1181,6 @@ void displayModesMessage(struct modesMessage *mm) { if (mm->timestampMsg) printf("Time: %.2fus (phase: %d)\n", mm->timestampMsg / 12.0, (unsigned int) (360 * (mm->timestampMsg % 6) / 6)); - if (Modes.no_decode) { - // Show DF type and address only; the rest is not decoded. - printf("DF %d; address: %06x\n", mm->msgtype, mm->addr); - return; - } - if (mm->msgtype == 0) { // DF 0 printf("DF 0: Short Air-Air Surveillance.\n"); printf(" VS : %s\n", (mm->msg[0] & 0x04) ? "Ground" : "Airborne"); @@ -2381,11 +2372,8 @@ void detectModeS_oversample(uint16_t *m, uint32_t mlen) { // void useModesMessage(struct modesMessage *mm) { if ((Modes.check_crc == 0) || (mm->crcok) || (mm->correctedbits)) { // not checking, ok or fixed - // If we are decoding, track aircraft - if (!Modes.no_decode) { - interactiveReceiveData(mm); - } + interactiveReceiveData(mm); // In non-interactive non-quiet mode, display messages on standard output if (!Modes.interactive && !Modes.quiet) { From 6d61d62a66da44e1a806431f13dcc4ddb4bf928a Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Dec 2014 20:15:08 +0000 Subject: [PATCH 073/610] Pruning windows-related stuff that I can't build/test. --- dump1090-win.1.10.3010.14.zip | Bin 616288 -> 0 bytes dump1090.dsp | 152 ---------------------------------- dump1090.dsw | 41 --------- dump1090.rc | 115 ------------------------- view1090.dsp | 149 --------------------------------- winstubs.h | 111 ------------------------- 6 files changed, 568 deletions(-) delete mode 100644 dump1090-win.1.10.3010.14.zip delete mode 100644 dump1090.dsp delete mode 100644 dump1090.dsw delete mode 100644 dump1090.rc delete mode 100644 view1090.dsp delete mode 100644 winstubs.h diff --git a/dump1090-win.1.10.3010.14.zip b/dump1090-win.1.10.3010.14.zip deleted file mode 100644 index 670d4181c3a6710f0becf3d4c232b386ee904bf1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 616288 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

H%S!1JGqn`5JSR2!Xty^mV8nH-VdWl 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

9QQ~U5;>0 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≺FwasE)$r#s4lPTC8eEVtnbN}`+s`Orz1R$CPGLZ*_9 z3Pb4{ph#j_deG4rm0bus9AT{6ShH?%AMIe6*jUo+BpaJAToSO%_*08SzNZF&6H!tcC6Izr*oasuWCs@C*;1RsAnK#A}Cm z|NT?EH~2Q^`_YzKm13=fp;rDV+AiLp!XuwY@t3t7AwDg05;B#njJ#s1KdOXOAUNkr z_Uv%73{=0<5g>PRBpG9>DFxd-0Pjw^SfajvVXxYFJm{hfd z;u$$sskYM)EU*j&Y?`juk~D#W21lk4xZW_5PyxyC$waQ;a|L2H8~|CU2H0wdiowVv`{a#WFE;mH1Lj=ESl z(v);ids--V_?jYn+)PF7#Gyej>szNUIaAA;lC-Jr7xi3FR<->9GEujvYFOvtUo~u` z@(WRKoFj@_Fn$8_yvjwih(|HWIt?KYa?PEQ2ct$DqO^8T2-*c;hnj;P)2(JRwAUO*;*WWIDI6XK?6iBwae0uL(msI=|6nMRm@<* z6+cO3%NvN*5ymh~17GRHDC@7rM#zw=-UW-@)EdpuB-J1dKVR*lDY1`*1*0Rq${yh* z9_nryehIBDvjS6=b_Pe0*A1MJ?E2n(qCcNeYE7pNa?JC(roB!l-3BE68F#?3Na&0e z>TWkdb{~m(8z&@wfShElhq*6iH4=6eztoUlp_$3rrYKrs6qJH($ku`<%H#bM{6Vq? zBXmF^5UgY`!15C`2NNLiNS!tK#atWYgtexVj)C_J5%718+4_n`r896=X&76b^9w`G zHeayv+=-#s^IvvSV1oZxwJb1;m>2Ma4T`*IN@NGvlK5pxbK0_lpi`F zr4dIc$UZE&@x+|z-1Ua3F039>a!A%+63Dpm zR4A8X>lZ<5X9M%NaNuEl=*d(jbfY-6&#k~&$5Y)=&?>c~_JYbjOFJJWV5;{;hZVz& ztxA?);lI2fWkkLKQdJrP6>sx%J`bET>}n*B)BN^=GSNt~H@%sB2b>YH_dFr9O| z%E8$ozhb4;J)+|8uu|U-bkc<()l9N|0Qe!C1$9fd^c*pctoV_&KV{^f6dC?4K@d z3?ASs^yIm0?5-&#jNIs!6{i>9J$@JBJuA4NDj{8zLf z1my>U;z#N*Raj9tgy(-nQJRr24XI9&rDj+|cZ3VP3ufIAkh4ibaB2~nW(|-DvQYK> zqV21{tJd~fyrqMxYsH8G*g*+r#t@+-9SlZcS26Y}S96{EbhkQR+{$3|RY5Kh1lbQ@ zAPuf=aHSx;K!{}QL9jH0^_`B55U|9iaIb;Pr>zL#2(OW$-0@f#3gwI8wSmxq4yBW(p}?xwv4rGt zLkS#bUsf4}>*8Plcx%y$)7oM9{1H|--{I>bC4((1pU`3{#rY4y9S8@BEf?eiXiPg= z+Z`hMO9vVDls7uukfPJlhbq}~UZk$G0JXQxWdE$9yzD3mhM8rNLEJq-LqKo4URxOUi4V zD{y@!CBHJne3JwuaFvBvUDYzc;_eX`tXQs-2&}TslL)M`j3ff9tVJmmJa;GwYwUz{ zet{^Eu$rN^fo7V15E&T{Xq5(xRvLODU?~zfgRKIK9sN2g^$H2M<#*3OU--C<-T4vibuxF%{;f!6XWdOZgB3Va1z62KeUSUQF$ z#MnELLdWQ|NP;`L)gpF+iv#P$c7PEY(Nji7>ZIWYZGJg%GvpPb-|0?zx&WZ;m8})x zIqsw~ZN-*maG`rMHPF;)J2lS#np^>Dl+UrY3(YeN$ zslgX@7Eh_(z)$3t;_*m34ebJl8v#mo{SL}q#3O(K=1{!PM)u>tNCm{&tCJ{kzU>t zebdh#P!8Y3L;n=dSd$-e;NtYZ`9g2V15Uuxq0^)?I?Z4Wm3d`oOY~<@bzof$ zCAqigux4x6IdoDBYtcRY6Y2~KqLW_Xn|BDC;x9IVh$MRKNW)UANZ&v!bq&K(X_gk*0<(a;YKzq^qbLus zEK`E1bL3kecl1++4XxS7!rpc-~+C*>$jwpCVOO%HA(af41E z7Y3X!h~_~is@<$e?G4p5`ZygOF*?%su4*heg_x5w&xSsJQJfEKEItl!}BbZ zrH|j!X&nHlza)B=`b-e4%(XKNEQG6{)5l4v>aGB4K$X8IWP9B()go&JbXD~^2yEDy zK5iEs5gv)t(qtZQqF`Whz5v43Z!JOLok24w^b*%~tEl*y>dt~znte|OrTLY{ODCab zp<6+$LTAA$^iBLq0Ut~xgE2QcW~_i2>H9%7B6=jEtAewMhNGZ;D^<-kXV3-{Ka|uC zt|Hc)P3+HXKr)S~Zu&!5S9~FwxS|8UHUd*7qQN8ds<=dF6e(K?rjR(JScC2;TP240 z1Sq)&1xyX&j~15WY6w#yO7`dep;+gVwoiwTP}GqUEf&RD3a0 z4P&RoA-f#D!9=?yqDS$tA5SrL$F zhTxN8oIi?iHhl~Fqy*&$f-=Bd7uWwi~ea9d3IJSX|+BQj2?YFQ^R%! z9~JRxkvh!UArrJi)$NP^R*$c)j$C0S&wr^aEWSqq4r(JJu%v^TAo&(vq}Dz&EqBWGU(uHV!d6jNSd{TgU-9RqpT(`~4!m)9e}GIUJ|dI&hNYn(bw;1b z_8o*&3Lweu>1TcnDa8Uy4dBPxQKlCO45h!Oa!rx=^@)}Fjf$5_J(4AT5_~B2E3b4e z9MIV4ewIg(Io1~sd&$&wSz{GqBS0i1A~Jmx z4yuC0IDE4|e&PKHF6r>U9@UHwgK841N6Dy)-$Wva;zut2_gBx4^| z^|-bo6v{TKDjWA7Yp??CWJ~*6u)<#cN2%ag@q9!dm-=c-`pk3Gl#E&^)1-UQ#|uqX z`a*J$qR+gV2(~5QbeuBrel7h{93X|Xb{RxWY3!=1s$OxTz&J19l2!FJE6h#E;0h>w z7nU3yCr{sv_zWpb$yR)pQZQ$pbJqoz_+El8@at>QmM(pXwhWqA!TSWu<~xz{Y=gc; zY4-SDS{e|Y?{#D3r+1nXt9d;Q{p@AE@zueFF6>#nN#Y#LZw<34{EnBYmXekDfrnJF?k*ELx_+v#r!nAw$?jKjC|FS=^Sn(0p|ti(qcDQVO4(fepu-j@TdPk^@^nBO;RxuEN{Lnguuq zX1V{P-NOev(@9OCAn7M}j9@>}5HTHPpUW-P^s`h`H<27$84v)P^>-CQa|=Qz%-L6! zV{gfE#hkTOId5NpCcCro06}#Vpw-9NlfHPcN=B*cv!kktop@m;__{Bux&wGSkJ1p} zqjp|Um+e%AY5-Ia?%#-~mU+Gd={S8Qog9!(4!+I*wRAF2I;tAz3EF_?r^=*{p9k;R zGlN?IZb;MO1ZI$ychnma(Yna)zjCrgdd?Rh`59_o`ZVG>UU!!xR7)r zt#a!jD7KT)+W8PMy(zYn&{{P{pBCG}bUP2Dxp?4C$IM6?& zXX>Z0EDaT2)(@5H2gvl@y6PRAtS?$;a3jhq#A|-F4Nh0`+_6)5DgZe@h|gxZgP8sZ zHbAq3T%ODoBA~zzn}!I9F|NW*zDg*~Uak%oZVdKDs^c*8z`qMELHLG$7n+2HqA+tY zWb))Yi`+;^U&z$k_6{BrkguB4dsqMj)5z;s-$RyZtazvJ&eN9biho2pg-E9>Hi3L! z5qzkJ2=VZ1luyxKLCeHMeN;mqwpl9T4o%vbl&EOoY4ch(2G z_V$1(R8FK)l^v;2?XFqckW$xX&-qrXZ4TCU7$AItr5)LdB61~h3dHqGrF;+eNOwG$ z_h)zXQS_g<3y{%A|6b?l5q30&JG!ir_iI6G7s8P0dcThVrPE-@_N7;QNwAh?@QaE= zqeETNLfFkqSR_%ZC4H(F_%7a^PQs&R8kWMF^)M2sY8Vt`s-r0x6x&Wy61)^F#1MKF zULU0?8LF-;7Ux<37l~JKS;>ursBm?}E(AX3Kk=A_7olhX5EP_x3kp&{fQ)jIA;qJo z0%j=z0GHg9Ww@KyY3VUdNq2hHucW#@>b-zdL~zz%*8t*)O038SutiS5ZR#A`#Lp+b z0iBj^=qI_p8=>ri#7f?g0@9o#;HDAwe4u<9mLI@%geXvj0m(H-A+Z5;k}E`D6_01a zAb3fn;Ye$n!U(d|^%vAeuf)g9e%cTe#V`k#>cqpK`DmVFCcPi?@Q43_F8WBKt?SZ+0)Jl`V$A zmMg-*hI@^HHTQh+e_$8p4B%D5K3Gn8y^tBgeuN2}^L4DwoE0!PI~GjLX}>Oi#)yhv zDmGNY?hp!dv2`kWLvRT@3a?Osc_Ku~{+dkAZgKy(|0h`usADD(A(H3`5bb?c4MQ-? zo&lK24GI1QG0u@tXW8<)7U}~;>hFOSzfmfe|IoahC_>%_InH2ff6Cb_Iib9E%*gAE`d$KbDmXWxHWn?$Q!+dyU_e88fEMnLlm5KeeV~ z$BtC{YJm0#AgAOQKn{ocN|aFtp)|I{4mI*T`@IzUr!7vc!6l}qLJSYhn-L4yhf;#r zABA+DUARK*E2!H;6*X`6!Zws3#kyLMQ*#i8z%*9Idb@(SqC4O!;l^?lmn$a$j3IOq z9tItvmKziaO7KXP&?PI9{W_=}QWPo43SE{WrK3rjrOj;gWV$hDqm_d=O zG#KO%WlO9{D>n+Qj72q^3B-0{Qxd%8>bgR!qGJBp9|MEv0wN5mi+p<%zAMxy;n1jl zPxr+b40Dj=9>6gj=83{aOQ6t44M?bNT|7>v02d1_^(tSqvix`Sstlas_@OFa4L^+5 zwg7N6B`}O967ERx4GNIeMi+ekhhUwpUW>B9E9U~GoUx9GvB1SLc;yrou4)(UEPSRR zfjw@6IIooKlb(2=k6SmqVH3C1Y?WMVAcf_HIt{@^%7n6h>ef-?VbHPntcY}`KoB(s z0HFp0VzCow5Q740j>55Nx)Z$wtLaSPyDdSnEfdPVi}x^Sl|(}KRYV6N|E&acu06(R zdl1IkKvnx8D*GX-z#*#UL)4)LZ4@c6zqYwRnx())8H#))$v0T?w^{PjNPZB>pJ~Zo ziurM&LLnx-qGj`aH@ zhP>Ad#Z4o5wSyc|f^#z*MnrhHDG(K&yqneVwlKo>NQlrnV=$snA6&#uaYp%zXbym8 zx`S^_OOWzy4KXL?u{CMNv|hdDOq(@sj%uDCC8j6Ln>S&RpUCP^y<bfQP>Yk zJEVSI$*O97sR1+18gQOHYAtF&e)nn`082B_fHX!ZZ%`ajl5RHy|IFr7q!H6GP#n?W z?W-q<3K)WOb&5Q8K2BJ8A!`A?zVM5!nZzGv5?k1<0*N++#2-3Ej$Uz4B$1<2++sTu z5;=s#EiyBjjlp&s!EEI!qx}rQ`RpB7MggUVJiZW;t3>OxnlywhCuxrF9l1Aw$~R9bXPB1Zl$p5_$$F7(uKY67aI|w)?!cU=kWvXivwMY2tco>rV@Tq@VI=Jn0@=UT)vY|DTAp!(8-WO(~ znq~rY8YP%xO-W}u={#AVbWzpSq+ivPSkcS-Vllx__zuYG6(u?at5=lk@P%3ISeBgu zngkX@aY9K(OkXEZG|;R~(6uCgkeI)h%n@ueF@KRczrC2h5%SZOtd*D_Zq9eW{I&+g zLCD_$&O#?ga{l@}+tZBk64v+8t%(R&wkbfly%lJIa{=3H9vM{O;e_uz)Mpl$XY+BA zQn2_%7{x0XFT|FBE^Gr@5SoU3CGmT}MTkMM21uTbfub^SFQBV+!G$_S5$Iz>a!H!X zt;%&lzeVTHOMYUlu%+iTrQ!6dpGj(*`dGDL z&?j~;41uLJG^K${-87}&#cvY1lMe@tCXZyQ(kKLVd4+ktWz;RnT=wfM!GyEy>9UHd z$+&^g`z9BIB1g$yoo7b+5s;$(xG$LByXfEqv*Hkc#jT97j3JZ{_kB74MY{hVcsWW5 zowRx(xSzr0F#Pa-FfWiiVDGWx9nWa zbOTwKznzrqn=-u85OIPFq7LTmk74u5ut1&?-^Ycy?=tIJM$G5-&$wTUucLvEiH0~2 zbc+3zI2n2Pp(S=8AaU)`-UjD*r8mDONE~!d7tC_C0S0A#qL*$1ldn%> zwG(l66woW&2bhvV@omefAoPpyZD$E8rfD&8vzpnFnZXGelIfh#`2|lV4WL1)#zCrD zu-0x3G^2igus?=#!`McQ)szXZlj=)Zhd5l-kok1YgZ;!XcKl3Qc@p2oQh=hD=oLly zQsVkn<8x>e%v^G8g9gG;aAWhZ;`t#ShPuGhxB&wbntTiqSR2_*C zs;d!XaYBg(<2?IYF%iVU5^UrNVl+~r198-xP_o=If(-)mjge9($;$lQROrCCB3f$| zg%Uo2o~K$yELX6DU@X&>;NzjmoVA2>;jj|Ce;ugQFF|bs!hNA{(~w-awmb5x_P3O5 zk8ITz*s4d^O4lh)=oROpcD%sO113}1rdFhGLiF!XFrc@*5IR;SX0Y96fMbrN!!5F) zllsg@UCQ93Vj9PqHjCD_oiCC@28dkNek2TR{8Wtr%ptYMRf^I|C(=5Hv!o$7o%KcR zU;v)e1?TJVwnPw-?;X`b>@_>=DqD)erk;nj6DvnuQ=BJME)yyQ+|~>}LBOdZxV9Z0 z0&q7q0eY=1^lX4$JrSeOmUIFZEhI&Y#Ed((B1Q1aF5`TW=f!hNA}e_|b%J0ObJ)=f zU~<_LDJ1jRL72QBDNYAsYv`#crGsHgw*CyPN=XLoMk4vW?0uojB8w}?xI-$Q5El)V zGpeimR2*IT+%pghKCrn*2?}iy&0_2TLN!^Wsuid@V}O$y$m$iU2z1Fg_G7u&J1mME zC+-u(#Q*hC#6BTnlU~a%|GL)-Nv~-E+tg88tQ!19^1yy{v!ny)Iu7lAFUg`Ki$lDQYMRL~2{B5_yS0n_)3 zkWl$a+}*>Cp1)9jH=AEi{EVAdY8TW)G%F3!;DvL0%E&1Z?aC^&IWUvog*9=km>v(s zh6wJ>AzZ>M@=-s4&jMVf5hmA^eJ!^0L~MhL#9>F=lXP}5EMd5=q)K%oB=37&UCYeT z9@M94JZ7qD>NA^XcfegjyiT?y@_-jm%eCpmBIq+!b}iB6Fj+Ov0`wMmxya4LCusIO zZ-2Vi;6`6W*Pd0?h#CadbHuN}cPTSf_GZF1)yaNMv1K)ii7(NTR;OwgSY+Fz2SquA$yDom@u+XIob+6}~z(Hr*^>7*CnW{|I-eOoqP*EmuiTrN%qUN?m4 zVEx%ocN<*LUIfy?@d$d~6=c$~c482Q4+pA<}B%&6< zoEw}EHh^o7U$n{8-cZ>#&-TpvQXDF$>O%*3`|t5!xT`vU3y)5UnJmnI1eaXW8#%d> zw=^N|xzL2j_oNB$-kF;qnkt{kmAu%2|A~!mVxD7*bgvtJauAaKtjQh`%^0;URpmfc z-1aIR`|s%`2=pAVC!MR1XbZ`6K+-hi+w|xpa^o{mh-j)fGkqkzKHX%Gx?#aEU)xCt zo)ERxkXASh&7Wc+b~`J*y+{5RqRO666>R6sdYN6$P9l zU`~kv&u5lLvw&tE)M!-{cuNtz&?lOso;YLLq+Y~7nlo>C(czU(6$Oypgm6`+px#L+=7DNkH({sLDQqk+C&ou`k; z=d33RQ+Q1X6EER12#N4%A>g+pAq!R26E_XbdxIO*U;w$fpK+4j zp^T2el{&7J`+!p=JYrxczZDJ}oM$z2h^splze}j7DdGBHu|ARn?{VsZ35%>4wSx3% zsJPP!bP{02t;DN$NcoPhE2^z;%)Ll!_rVfHSXXg7AS!N3vxmnCyI!m^dOVKD$1h2f z*;k_HQ-YpPYgk5s_OMqz5D9I>?J90JP>GgDk?nVI{G8vAJXjm4$RaP9t^Jyi+Db?j zqV$S@BfXIAim|EIG1JG-~r5gcD*lV4C{X0xc|qtwk1S zgMs0mBEv~-5tiV~c*Wucb#uM=xE!XhKJ=EebXx9rnkSx`vvoZ0=|{ZctjaCY&f#uB z0_FxmCW<_E7ET;6#1yyi^IwWw!8YAu+oMk69zqfEeoMHPgbFM4NF3>ruJ0~ znnF&bSwv`-aPP{<-7=J_&cDq{Aka-u_S^ECL{`g!N z|7JAk)$>A^L4PfKOGfh%(!q-Xg{#2#qivB|1`^wW#JHRga;d)uMRD5z;1lPro~~Cr z`1q?veF~J~`4M#=y~5SUTh&WhCEwC0BH!l!v1S?8Ox7B=SP5X5T7(LlC>haY6UF2z z5VTgBUI^30?@_`Yd~4uf&4StB988-r#e)(y)2zt-UeiYjW)#fdj<WWvoqgO@ zb$lAD?9Gk0M^nt!e1XPNWgm)(%IDZ+C1kl&dmdT9rGY*Qdtr~JO3g%D;fMiIn#*ND zxiEd4vriLMT>z2?8zt;LFydht`4xLJxI`Sh<=}FRO4W0n+BJMkSEAqEz#Dqp-OND z*y%uvwCl-BoGu%31tzodM@z^jJ!<2Md9eT@0Oz@&#is!+)D*5^hp9RQshR}Q+IcWA zaDPsC&N}|x{up)=G>}_oA;=8@6ieeOo(qjzGqzG}Tn4btC5|O)YC(Szql9PlLr4eC zS55BKYnFP(j5(@#<5ZO8Az%43DA#CorMZs`!G)k={qbW-6hM7!T(#hlEdRY8YN^`} z=ig%t%gM#2V#bDg!cp|q5So!^xBmw6nDzqla0q=Acn{Brs#4oWe52IL5kjdX2wC?W z34VvY(N&4~9rl0~mE=3@(GFvMZ2lN=G)Cnd!am59kk=&~I1`QEKX$`Q4B7o8;H1tK zduRvD_7>3UcCinLwJ>#&xdXi)~r} zixQ(d{HDFi-WQ{8uW=+GyyLN4slS%3C?9?tUJI${^ zPV#wSi&&>FWbdx4(H5e|i{n8QYgY@2z3OCA>Kc7|(p z(RdywqA@*YhkpTg5#U@eEaFp*NJM5J$AWY5%)7>mN|b8~gQ#At#UaV6V3o-bq!B(AcO3AC2)aB;&4w z&dxVueiu!;jV7p!*NnHO62>=4(2Vz@BD3W@RRexKYShS*ccS#PCS7iQ)u1f%y*}@T zCR^&$2+7cfSA9Xvw&+QUdjyGu!BRO_3wRKktuNuc594R#-ya1IkiVgjWN$(WrgOHd z3yOh!c?+_R9~PhuEl_sxDJf-o%GRjD!;i?s<5%b-WZds~nQFwXECuh(y*&wZmmI}A z>@-e1n_Js_;JN21a;iH0bOkT$S9BuV5p)ulSHb(9Re7)qOQ~I!QWBw*?lhj#W$`^0 z&gB%w3rV~l#1n%HAu3G_F2W;Gn&4Mbu7(9HtA^stIYOL$c!V@h*O>LKjm^O?B(R+! zs;9W7r}4@~XK+Ke09T1qiomSJ<4vuF;Jxu`7HrTfUR{KDQQhJE07c(JpOsN!qnDD8 zoM@gm$#`yypVmz9s1zk%7|O;$jp4jHpLZLC->oU8wUJPiO|6C1lh0&_LOLOsmvh%o zl8B5u50PlA5WWZ7tWl-#O-$c1bsg3NXeD}UDBMuUz7N)M@l9yXlw1mjbS{2tHT;PA z{no~jnz-Eh>bm;uO~Wupy@%LbykKu76vgkL0rYIcO7R+VZAYNR&ApF31dB^mv)+^( zf(E_u!wagKkixH{^tp+Ptx5^!EkvxiP7uN8CG$=aPt!M>UOp9~ni8u8PMQ+w0#AHo ztQ`&@I!#gOg=@h-DLg%@m7lC2AO5I>wSX0e$8V#p!oq;5F005X;+khwm%|9aW0m;1 zHstg34Saj`p2$bp2}`wwCxmJ@K(z~XBCTfzGpz)H)-3_h{47dh8Ia*lKsm^`_9t`{ z+rnAYKApORb2v$)X}Se$zCCTEqOC{kGd;7FhJL-tmu-g>k$iIDOHUwWUE=#F{()!ob% ztmEPTf-vY^5B9tLc*vf*NA&Y*6_TKzI;j)a9V!36pA}YlcRGW? za)r#}PL?^s4a%B&=+9nfb-~WQ8S|`l3TIyJ%pQbsf_GX(6l(9Q8?#y`v(08*4q?>% zdF8o>(QzYM4O7ZnJv4^a^>b>2$FPPwwdY;{cFF*ogwh&xk_j6{F5`U+F+SKiA+C~2 zuBoRD0TS`MBU$~NYydL@KMzEw4038o3Ldr;+zYW}nVyylrLmZmP^@JQTAN}n)`}Ec zVktJaS}`bMEfi^0gBuAM2{I|y_ZF&iQIPNX`R-&dqlUUBel;@KIm^0+X%Ps;G?!O6 zM^nPRy|}#8+wt9OdAEkbea$rlMZu2{YvNc+6`;2(?q@3vQn@C?@ikN@v#cHUb}7Mp z@*rCqQ(NDNLbWZ)*a5=hg%^hor;rhXU^?j$j&p0r0PZ$K^mb_8iYd8TmLRj@E&(V- zl58EoW!w}Tfm;VF_<<=klP<*A25`&CR4J#AT`0>qJ2`5KR#A%jI zaiNc0q|jG_P8IU}@iw%tNSo;(gJ)Jz< zsbzqs#9D1mwF>aeHP!xg}@n z*@D8@0uJ_GO|eX&7k)tzy0DVyKRE>JEavUNJV)*t1fb(8Cuy-Q_Mnp$x5^7Id8Nd= zoDvYvW~^_Px-Q$Wre#?FzMuG}Y$e_!Fq78iVr0rIihB;CVpr}LEQF~>bqf)9K!E;Z zfwEqQi5oFS#6MlbJoZfm?4MO=enLL+G1?pue2mZob;zKT9>DU=_npeY+rS`F(v{cfd-a2S z{nl>J@ah6A?3J$5wujJ1;Y=@YE(O<5D}RQU=wYEGa5SfacjKiYyIqv===MH)g% zZ)=^ciSedXZYj!c_@%VqkDBq`6lzzI(ln4x%TThK>*U>5RWH2(>-4;29qbIl&-XmN ze2)a1YtZ(4M&53Zxn4DGO$Js2c*2OOL9`@9KvJ^9tKrCBSCH>uMB5mGi-v;LF(3}! z$A--kON+NO7vdH*{kno2lf~95LyIE*JScFYDT6&~qJghXp!6hb|OsFld4 zsx1s$rH=F2h9L z|HoQrEs8-nzDpBq97p>YS~!63<|?(uJbtozNEw1U(B;{%xL-J|&R<+^yH& zVz(lmgfzuBi(XlQyMmdXT4WG!+19)Q0M{4y7PP_$X>V>4Bf&dnFex^yTC+;UX8G!W zAS!aawuI=4^;K+|lhCMdHfJv^Ip(E%6^DjKjR4QvU??ji}XQmcz_N02nu zQ*5}mStNHMi?hNbYl*jTKk(_XaAroN(jIzuwt)XmHl z8Z)`7wjkmJ@A~9xb$TGn_9i46B8f}C3I-P}=p4*CEu`7{Hq{OKYfgYltLmQk2QldH zr4~rSuT{2tKPTAkYcGK2RSS;rre{@=Mo8o(yI)$pvgLTkAQ2oF=8Uh}c>gM{T&F{l zttWCA^F^TtkcH5RRm9Wx5e}7AkcYoiakv!QNRbv z%%4LhAD!5?1HZ?tkCQ?1!&p2MDJt<&&}M6 z&zhn=*opOUyFsVO#~~?$q6UXExD3!q#O()tt;DnFKKq`XUpEUDcN!U=CAX=e884$W z>2fG&dXVwLMi*D0&&EaWHO=ED8INtn#cmcXHzZ+f`gY+y1?{g;EfMNAh_H{WEMl0s z$0E_j)5t5jb*EW6_wOj|+|hf)&c(A^te;ST-E398b91NSRrT!hD#8W7Nzrp~+(W*S zn8VK7Dkbl8E(J_>td)70V9E$%>jRge+#Ea#73Y9odcK8+etKD1e1eTYi+nUpDJ;l1 z*Jb1^Cy6(NxFG_zn4jL=U`LrVxnWPQxllm>hi0AR3(%6OWsVv7gJqpPfPCb4a|| zvI_AdI{te>4O&y-TyQ*cJT?=!PWTg7s^x4y#MPs=G)vB zcMGV!hX85;p}xNq*V=QoRPa9Tv^HuBy44>`vFb4p*DS@0@0|yN-kjmfdHtLjT&ID! zG0r|bF2URH^={)dx>C--e8Pen?gm)fA6R+oFJOTeKA>3S6cOblac zVMq>4yX{4POi|>eDD*1y{iL=_8}eL!+T^VLpmxw_Io9e=Q5pHpH4Am!@$;CUZtJ?2 zgdB|-%f+Vy-`(B&<4y7_-9T}+KPQ@|jmMYWx;sM#%DNMzxEYxL7+N7J7V|6K$-v!p z#mnfO35kZa07cfIlHZ+&kF$*p@QtZOj_mIT1ghm|l}L3+wG3Zq;?U^nfvT`lfBB0P zj~{Ti8^RLc5}h>71K*1Bz?Y;8nxXkP)6o1ux|x7_8J0cfq6Z+&jgpd2xOIbo?M`?l z>*;nVe2Sc15fFZnnm?}8ijc8Kgg6KBYvH+efCNj850&Nk5sCA5coRgX$~97FXI*Oj z4sSxpr;po4f{A+(squ#{-T^4q@3^%PDCy&Youj1A6on7lz$I-KPc)yr$sYmTyTiVm z_l|f&Cp8|6&uHGJ$ndirwZ`IYJTds$0#i+Wa9);__Mnf+`umnHeWyvc@*W9V{u+v# za!A>IST^{rF1R42RY80Q}$31?T%-jBjID8H5jqOxF6~%e*3=y#5-gt3_7a zm-WE~^f7y%;QXbJ@m&mr6?lh36(K;!^ap6v9Kqw!E(d^q`8g0v2mB&|lCAp*M78`f z#En3a^2}DUQUb~mQ^m#QJbx@5U{Pl33J&?5PIDQi~w#F z)>Aba3>#rI9P5vdjy8UgF$y!bVuo^~d8=pQ&(McLGz_9q>udfQ6RL}FVD~pF=Y2ED zjb`|{8iSv0N)vTmO^KY=Bmyj)4(W8k4dc+QlzWvb#T`Md{xPP4rFd z6^!x=hO;Ro!>3i%$NWN}3>KL~a#H%Dt-6CnFp5RONJcEA*mi(Ru2(V@>9tXo+cI?l zL2MVm67C-j7jH$Eh}V$fars3+s#_)N07(4&;j9!w&7?v2kg;f-r8VrOUBJHe1%1D( zL0leQ(d6q(C#`P^BUj0d!wlQ z>pMtFxI0M0T4e2F2$;D_HGU(UdpsOE@qU2shoyG+P9jGt&yoO_p7@P8NqgXn=OhS(Oiv+c<%usQI4{6Y+2|Fn zzS$}nP#2R8g`|g=Y#}7ukR*Giq#R1h)U||psjkqf+U=2Ex%#E>LD~#bExWd)s`+oW z6U2^>D)<232#5I|(PP3Rbu#NL?&w1i0@nt zHeO4v)R9Famw@rn3t*ADZ$L}s%bEk1KD5)b{iXs@HX4~*` zIlQZaz2gkcj_quP?Jjc=+O1_Pgro0`E5#bvyTxYT!ZgdE#a$R5f~8inX*lP3H&X}l zNAr=Qwi?CXzNDx+tiR@Y6*rp*radp_7KrtIsxa3#jns$R@a$NK>N|7$E74VENeypN zXn&fStIm(gLByGWIBYXQ27MgUaWNx^6ppB@nuK0MSNM#8(POg)e1RsKIA?2m86Jy| zwi+7Ehp~RnWUlkN!PN%@^O(Y&C#>F%a{#D5+cV>OiI2j4!3^clXtJ`mu@vjH2A{)3 z>b$vj5cB+Sz0ybBW`QC!1QA=Xc{D-f24m(s%=B2$CX`TV$Qo&p3XZz7bW&TSMZVYA z1Y-bp@I9-l;WNT{fsI#jq^>iLT>@~o02&Ga4-24$0B9oud@ri&u%C*|$`!hWs9(lv zu=pd&)z8>G%o0cOzSUJCSlSjj;1;$;#Q(GPFSOKudo!v3*EZ(*$5g9-Z5#4*Ld(MN z=HfnEM&ZuQWb!-(5Vn1h1)Kulabxw*A~M-JRZtZ`xw!5)XElCUMIV+cooR7;n zE@^&+#i&2RxGjBL3Jbp8D*H&}VR%h2%~_r{=-_9RSaG3{j<#n%{7#lS?4A#n3avK_ z#;E`>cH-K>N9~~UhHAU97o5z?hXfjz543IxAhA<-3rmR2%lN81s3lCs?iio1xSBq&^M$n)`{lf3W)YTZu5 zI@j|R_^0XNIMHkITqm9Zal%_OBwoeH^1^7V(IIHJcEG?6x6V|u*)otz2^i5z)hNio zyd*I%jLmeiSfhNT2CKCT25J9DJWEw#or$kje7fHJ{LnDojTLrKXiabt&rW||E?Uem zcB2V6hZd`xkah~BU^Wa#Eh^&H;cM-KrTukIz`l>I`a>d2-aJEV215`n4daOr*x5>{ z`FkhbA%lp4^MsghWt`QgM~41ZzS;Cj-?5c^Z=rbSv3RG3R4m?ED2{@w3mN(m~xqQj|;=f!sqdB7$-Kdn0&(*xB0_qm7Ys6^rq}#^rEWfw31~;*4Wr5$-hyow*YT zF}?LF4h&)cc!|q|682|;1!KUOpd1aW7sN#@hU!m3z)<>+e3jw?eVOxRo8^Sgd!SzX4 z&encO7B;3c{4u-?inT)cv4a@>#38es70`{^TNi!)4bH+%ZtBMPP@`NKRgI7B7|JHt851dgNYM6Id+ zXAG;oBOEuoIs@HGd?yCoJ$wBpmc1{<*M2^+fq8%uFQdRn08&`AJSy28N)EhZ(Sj^2 z+(3I@RV%@lQaM1GeNdP?fZ$K8EtKbW&prc7i4br0}@~Ccwhhud8U3s@u zXh?B?nUvNp21LzN8O6{PrX0#v1sedj1)s7(9Y{ z)k6q##qcr-`%vc~96YEmb%LY=s3Q=L3JRhQl5haE7s7y`5Nf>$PY}~H#Qc#YJ)8;> z>2wq6w-M>PlW-8_Akr`Cj$vQw6$uAWc_jRf$|2z>>I4Zxs4XNMPOTwfe=4cFKsQDV zqaX|%)|VPd!T_ot2?tT#NEkx3fUuvU59Lh40IIwjhJ&bABn+YQx(W4PhH${}5GozQ z!9#{oe~95m5)P&kAXEFVIOKT2}e=GNjQv>lW+vpnS|j~LlTBk4iJWf z22xTlpXTgwy(D35K(_fl75jg9G~-|^a>ZfP(D zwt0s0O+*QPj=mgU-;n9tO^ft_Qj8b&H*`DU(^y?7Of>T+kVyusWovvVp2<@8H+|<@ zR&8Rz+rY$$Wx9v4oR}r5+_x{XS z451Q%ADtpcwNUV zxuz!C3#!JVjOR(bc;EN)Y&4aU`PN>1e_a>-wV*{0@ui^i{LTEP)_O$_soF8xI@|!P zc%Ju4|FBQ*7zGV9^by-<7%H*I`~L=UM_YN_AIW_mQ>7Qena~7%hQMDnb@;zS8yd|4 zVOcjC1Hb5PaSP1@V(vm*OkCF0;~bIs`(Eijpb2V6lz40rEM00dUyda!#$2=*ucTGv z{s&cY5C+y8Krp@5XIEF37lH)HXv2Z#c}-A1)CC{w&oJEdK0a~z^P2gL$Tu|rHSzrS za0Ff9C8$w(s8K=s5&61WAf83nb-{VuBB(Tg+tU001tXLl^8fJ>`fAAk(?)23-~VGr zXweWHq4`6E5&Dy_d6>2GB_mWnq6lVST`lf7q)(G~bK?qhaV%$BUwD0mo$!MdMP3SW zFuxSU4f6XE*E+U>@1qNnaZPHAk=_t7gupu5_a=1kXYAnOK>ao@!%PZ6#0tZQ0>ZE&G2dWQznJhO@IFTW8DF*p8>Y@vf<+z40i3 ziiQQN_7G&&yuPl&Y*X7iGnnG7P1~na&tm6=!;2C@IdgQrRLwSbH76q|a!crD`Lv z)IeYf*3jxYfM88gCMSU(ldZr^;$wuWoD)kCs>WOZlUx9N%EUMC1>#alH^M=GmUBby zOYtQ+RVxVP5W4tYqP39-zlzkj(LxlPtrVizY=sa7`dsK%e0k!vlv3&xMQqGCTPmFX zGonZzT;z2l>B1sx^rs9gPudMpsSuU^|7g1!;3$qO-3Zw%l8BWYNt7x(UYCgv{|L(l z+p;ZV2?;EW0D)vAumT$G4%)R=JL}z9sf7tBCy0fG@%lITEGL8`J}$@25(lN^9Cy|k z&WMW=Q!dv<=a8JPiaLves$d((x+w7@ZS-UGr#v_QFNfn&+987+`!v_MY&zu)*y&)IVIy6>MI z+76{TNQicVi=v$vo2j=1%R$B8=>Lo!^~}1@9>ZDREZhX=Or7dEk$eB=*Yy0w!&6h| z&K($ktWSH*wuZfdXaZ=QBo4Qq@$qbeqk>n^!1uaqGYq3ibdV1 zEyK`};j^F3XSX9a0&6#m)}sgrWPGx=r{vmFQQ;#+PC)cdT=){VF{^}6E5QwmdOG|2 z&P;zt!Jyj6;*464zQij5k0sdG`&YLf!BMZR*;Bft-O{4Ya_;zoxSHPIvZcK%*;@3u z-Y@ZtL~mh5w3>{%{eQ%TvmPaxdYC?$?dDd4po4!WE6wneqDNbdR=iv*P#chcrY(Az z_U^gD{_7Mqy!<$GEAQ?rD378vc!lNNl_@VKdW!6JKtB#vQrK`CIp$c*S~Tq~x&r?< z39B;W!t>jWh|*Kkb7n72_x}!d?`8X2L20t#u|A#^Lr$|`hloaZyY@IDZ^%+Wcoww8 zjN4pqwR6uG*l9Orxv&^!#i8IHWdJL_EwF^C-;= zG}>EW!E9SS8m5oO5=;MldH+a5k3s6Qc9B+X{*_qz=gZrb@ZJgy(_&&;{FYee>&>DM z^wFbVabie^1=BCCPBD=a7irTAofEXR1-({^Sfd(I3~m;>>sVOW(Ex=&dcQ|mCA$?r ze$>#uQmMgN3z+16m#|lca>-Jw$Zco(wM*9~ZkzC?6YqP};V!cxbouxQu zh>fU-Xt4@;vH7s5;+Pbd+TmH8$|Bun7g4hWVpqG8O!%Y7 zO2njqOuFVXC5Zcwtk%XAYKw+&G^ljesiHX;@@u4oS`kpg9dfM_OU4-K>Sx1EzYIQ@o+#Q-$mMQk@+pMh|(dou~aN6$5o{m z*_d?GjAJrG(WkEFkhEo6mB@pO7LQQc#I!xN;zAOQXZXN#nBp*dZpm-Uo@d5iS9biF zGUxa*^J(mu=Z_)6NNlFVaXE92XbZQr$Wqi|lVZy}hr{75a@H0aAtN)!lIX8sQngl3 zj63R7HIDkz%!TeU^Aea?By_xN(MyCeO@=pzqJtzo zf=u5m-Ji+xyW}zbvMavUjIpr?^&=}UDz2)(bi-`pc-F}Gm+QxGm%n?%!tGoCBd5G) z_+)#@rZXmQXlm-Nw?a4UfB%}3Qx9&>G0ztpJk~uml}c@j%3PH;i>t+^h|<~uCpL9O z*`}^LfD(!s9-~;o1VH|63U$R46}kbbg#s@UG~{|Rm3Ta^iaM!M^iG`MAnJ&5D~@P) zas1xHKabbM7B6{jVq1Ig=o`0O|K{I}w;fDX_BYq1-Uz>Y+lf^>w;lh@hVp^efA;Cg z58f+yd(LZJ!<*JFKXC0=1KRnGgO9#j^p~GJ+Eulu_g6=Q|Mcd+)s||!s7af)w=zF6 zz>hqn@tB9a7V@o-r#$4{ke`SAu!sC$`v6ZZ*Z3&p$J4LdVAb0Sy(Z{Qc-R?!e1Mm) zg6v**c|YVn$V)xsA;?dy)Oe$Zd=cb(A?F_Q$=CpIhJ3Gwd>HcikPmvuQ;=V%K|6TJ zn;{>Be8NLs3VAo=MfbVO3m~tCywpQ}S{dMlkT-hB4?sS;0`>Qhhalev`CbpXAMz07 zgC6pW(E(lp`KX6{5b}wKQGXA4FXTgz7v1j>r;ztTUg{yA3wbT%jUMvzh`%DpV|9U$ z985-Nyzho?#}72V1-SW1jsIbnX77{u0OyFgSfvtGF-_389R(-n8I8{d*1VqPU&ZnB zJ@(lD4)VVBjNR5KY}K!Zt@;pFOF^tFxmbhDFVnczt?>ir5 zbw7dMEgD}B1c3<9*`j5}Yp%mSmi29ZDMCKu$|tqSn5Emq{Vm-YM~wAtP~1h(nsyAn z+;d#xmB4Y}Uyj@T?oSNxxwRUf`+&wvKGFEjk2Jmucm>%1k>)ygOn}o3PB%D(pKJWB zOBz23?3|V6`+<9XX}%7qpA8hGxeWXU^7ny17$BQtIOcsVE!uJqbRNLDv#diZ%37oG zA(ZvlrRj5;8?5|Z@CU(PDe+n?xdL5FBXT=dS}Wu(R@H@dW;lSp$BH?TSSHKC7Aeri zjF7|?86s7|a*m~pkgJV{6|9=On32XUNT97wtOimky5))(f=UTS3_(3D?$p&Cay*f2 z(Qle?@n0sz#gx-o5Q3ttk~4zxuqwoZQvuyHq~#Mdf{nqUDX8Kpr*k60-L ziU2>O`%;FxdW=7y9H6_|TA&eV20}m#pu)PT%>i08>;?7#2Lbvkz(HULpf8G{|BD(0 z#(;5vKKz>i=);jhzz>uF$}A0jGd5Ks+TX2V9^WXa>50eZUYf zMt>?k6LO#&Xa>50eZUYfMvrH~fGY>Ofgu`vS0N2_1495k#i|_W28IB7dUiR`4GaPF ziBC5*;A|Y}|5o#Gq%Dkm90B_C_-?xCoP#5MK=TzG0d@_J05$w*xyFAXrN8vJI=Btv zx+TrkK$>sIk;c^;InCREf0XrcwW!4&3uPUMg*wd574GxhPLzMS`EDQPqx0W9#H+!7 ze{EyC==>`YXHtCO@}Q;{|){SmG*h(ooBQ2&hJube*Lrhyz}incH1UD2L4(j06txZ3#+DIho6CdA?%pf;hLAynd|T%@~41{z+&V- z3EiF->~Ub)UsR>X*}Rvh$NF5v!coLRtV)kFYutWOoSEEIl^#dVILpZCzHF(+Z+zv; z%@Zs7)3)pHBzPy0_W>~LN09$G&10{o`QN@`Z_o3n@74_(FGE>nKSA68;r(fTMoaU1 zaI6KML|!hAZ)$e#ysfzQZZKp0f^i>sp~93`vnzkmzic0dPit{Lwg7YQr;P2t*T)Xs z=VKdw>0|3JkZC3LF7)XXLYb;0SPaPCjb_4gr%u+01;l8yErR<>up> zmCsHAw_SyE;632(tMgeeFb0&&%4b`FQ$Uq3pQV5?V9{*o%+AlWEnOykxW}AJ_o0(N zTDh@BdU>Z1TF$9?z71r>E*cC zvvTQm270{?dwY>S=&*Oj!(Ok$-dx;kjaJ+1I_|JVI&O6xb=azg-n`{@TZ@n`U!Jv9 z?P1HxBYDKaq5TL`X5}9 zzTp;oW{Yp?5VDr~Q@w_737*8;&cx-HUivt{Mc zs|k8FYwSKXS?rM9lv(Z6ILc2UeG2F|c)cFHK}T7`7S16LpSvApO(N}Ei?z&JV~iv{ z)_hDRHxe`J-6{h+@MFj zCfC~AqoB@ivn=UeuSIqCdR5hBIn44J0l&Gmcq z_VR~mf9(-_UN;}$7uVVIsyhex84H){eG%zgaQ(Eu)u5M4dT#Y5?y#3nmhwgY9X2VS zc(iRALjDe5A5aP(uO&Z$GmjYM!}qIP%Ah?q+5<87ORTb^M)_0*aYrs=e@CbLc$-|0 zIos%`TsFO*20Qh-J7rZHV7t@3f9oxMK-}Ou-6!(H9Z`EOo_JiH(6Ku@JH;th~NDnvaZ5jbLg|*8Wg0aS-Jzcjk)bH?V?i287 zebzR`q7Uh7fl^=|t^ulXoua*5w>2K^?~)zUrY)A&JL2E4>s;$>MP4tBKU!}=Mti|- z?GUl}O7%N}^axM_9&sbzIK*qeq1&MSTDQ64z99ld zL)C_C9@VQJ>E;dD*f-}8r!6aw+ITn8{TuApBifI3i?gkcGKZ0V4=8#`-*cnAShq51 zf3|Pxk-qbFec$!1d-~_}x!kylw~dc}_pMykH&wT;q3?kRR*-!kF8qDpGT*Jol5-mR ze!FX1Got$&-IzyCz=+2>Yw@j_dP0l*8%tzJv#f+(S1y3D2 z_w|ocQ(sBW>06dd-?Z8@G;`N7-{BdXHa>c+Z>r(xZN70{NdIU4Y6D|W_t0mBJ$}}= zDtFhnb}h?2K_9WPO=km8nUV^o?jp~6Bp(ys7IRG0q;IKj=TLI?i3%Te{JH#HGZqe& z@2be%RpHaC1ReSUtiBJ&7Y09@I&pP%Dca!ejU*KDNc)A>cA}5#{`jcTPU}F^0`$c( zz8UJQb>o#kot#>4u=V0Ad_Ct2b`Ghlzu#5xa+AiB*Z1%Owym&lc>J}G(7J2?YbM(< zq~;u+v15q7bau{TGsb`Y31k1q+q=NEQDu+g2^nB$(+at?m6ck8=TjDrW>Bj#WUstR{)dj zn*I_yaF3Q>eMTAR4W@S$7+z|jd+JUr^zC2c3DrV!s4^Bwf~j%w#K{vMPreE*;f{ZJ z;j#HgW!I=&m?IbFhTJ}1=z(Inn#wDV(BB&{&CaNFS4`LEF4Rjc79F-ZNEcoOZNAIe z9Q4t{SFxmWKE=mLC7Af-3gV)~yQXuX-9(&qFvdJ78Kpn^5)TN9DP0BC+go}`HSHV{ zE(sNJk#v0et0cV+zrQ=Bl)am@9SPO@q;N4^iqK%vws6sE9C5j5ja*bG7v;)Dc`#)F zC|&y%3|rsrrrE2lN|dHQSs)b2`m)`Pe^ zOGo~UGb6UZsFS_&pRoY_@vR^VuXpK$TeKNyT#gg)F0`&y#L7jk)c`$j63|6coBdIp zI)U^cVp!?dW;ikv>Za=_D%LPMT(kzu!I}scp|6CFUOo*|-s3}i_XMWr6`F+9OAn~GQrWFLICk&#Dff812My52a0>C( zG7Hot$FJ_N(zc1;>2#y|@;u_r?HmwhlR~a1n*OZb)G2(f(~VpoLw?1o!-I*d)Ip~O zIDJ5DSnV0ry+k)MRty9U`BYti_P}qLz!YFivfC@Wm&)$dTivT<_j1{tE4zzHxOSu) z%NH7f0U3Kqp;=)+&G6Icjo(^~Kh3?;{bpSg7fh-I^5`fuUf6wnPz6Ie5-mrzbLI(@?F^>Y+6RK%Z6tjB<8aE`cW!TyYm9P@S+23j zH5qb^E$q(3^()t8%QZQ2O|D#%C)X6qHD1%U9=T?zTvHi#ufpMzYgWrOYvh``;|Hhr z%Qg3v%kA}YjX=Mk*n`sgp1&uRSNLRi3*~xGu~d)MG2%|QE6FcH{T{%{JP#>Nm)m)w z2cnjD(RF}B!kmtUIq=Oj7y~}wpBrH%;vvs{^vRW{m0d;@dA4o6>-Q}?K@P-yblm^{ zCcm-&PU2FvaRx?x#@?DSr8;0(_Wv7);RWa!WPAheDq+fQ8(Jx#83s7;E zdF(r+g^S!93Rp-IV0?ghhJn{PdIUg1(F`lC3r5H^zu(a>I3gLu$&tle&oI~Xcfd}r z=LF~IX`e4e72#s(gd$u3a?BPyXUMaI22$ARC}le+5*mj?NG2ONU|>q8B0fr7V;VQ1 z+irv5Km-B7jQW5_!cY=DY3E@mI~L+l>ZXom61_f!jb&|bFuSgP?e-R0@XHuO5O*am zAnC5Rfq3uR`|IagLM3m~OVg0bz!UMP)Gp}SQ(+F=AeBq)#X50rsC_BMp0_8{dtg-P zjZhOn)93m`*U|QgPW-&j^{GBo>ZM;6t7C@O#9a*S@Gx+4+i)aRx@#EN^>C*mEbD0d zR3|KTv=_ru4=1}0ML5hyB87LzZSRuW9I4kTR}X^tLqCA}Hg$`Ru$ws&_<0NT))M(IvLY!xtw8Hk23<$? zWT;EnDs`KE${eqHx)*0Z7BPhCN9E>>&ST=`!Tac^e~Mwk!m&%m7$UNdHr=Y%!P@Lb zQ37g-B-1~gfL=CRmF5ho&rFx$y7XkoEyZ-$AD420>Iap&bmhgCHBTc3_{z)uPovw5 z{Y_t}*k)ey4Bq|k(BvWPe+|W9F96_fWxy5N_)7(wRR!<;#Ts}^ZB~TO7vbpz03bjI zFd~3|G{gZUOQB^M9T#;~EM@8hhpO33TkX0DW|! zUMHBM<$z(JZB7C| zYQ%+H;SbfnzzT9wxZZ0BkIX=>0dmLfQn1qc)q0AoI4q!07`ye*j1&xw}k$(&)&PeCDdxmKmrHl3W&=L zUrwk6a5bIRYq@&2Z8JS}W#Dz+1+EAE$M1Q3-I=#YVLB6ll95km+d-U5ypenMjhwS@ z&4?qbMxh21a6D}b82)rP| z7}vtxi+ptck6^v+(D#D44Dzi!E5Z?8o~8ip{g4Tq0lSjl&b2%8Z*I@1Kr+pBvFfZo zN`HtFQZFFKQIF&1){j?`6lpD|>lse+1p%#j`pzqCKp8D2WAr%`x8U{;e6OZ=ayl}k zH8xIHL27aWFvQb!@+zA}TF)!3^~7Kw$S=)ozZ(}FtTeNfrxR_sc0kZ&j8|~{lr38&M_)<(gSOhfVrO^0l;E!Hjk$I$Wq_n_Mn%9;_{JAps zkm4=?SpkU2dJd#!7^4gwV4{slHb4rgG72mno(V@8*kmP#183UQ0}L96sCK0$ed|E> zi!*c82Gj#m9v-YDdySfWK4C?JIhLqSoN=j}N11Fkpi(5ljA-+`LaJ9ZFX)b3EUo2r z`1IcfW=Fd+(T=u5|JAntXhe&XG&3|)5eOCQlGxD6u2{n835Q|y28r7UOp3V8RV=a5 z_>Fp|E~b!n4ry0PRurY9_PLnF%5OCE&oAYHOvpyb=?J%eMqIJ1Frs3JdleVAXXhye z?vWKdV5Aq61yTz71V2BlDygz%BXNy;=*xz1ujUty#CaEu@!ThPAk2D{BYN8n{+n5*v&OB?#4;wx~LOxrcy{yHHC> z9KaWDfVxo8hS4H5Tvq>lH=ngHYro+G7!6oT(6XNcVlmJ@kJO}x3XlJEsgB77 zKUn10-#S9tqL@z<`iro3v|Dr_)~8(Mww9sx)5dE{b#p+VjEidxJ(D9$(x5Z;ZvVaAmzrKaYUnGxRUCiif*&2-aQyEf&TKz$T;yxW zE{!e1bYS)}QkG2uneRkzoxj59@D#MBF6bU%rjxQ<;P}mXj=t78Wa%GcWH9T)mYG?7 zPA{l1LUJTZkTwFZkVRuCeH+wdKL!ihj^hUZMNF05xrNaIsfVwGN3+QFJ`iF8%6q1r z-40JNke>G2q;88ZRLfT=bC$!iiuf~7D?B!`6(dKw;O{A>LFFOg2zVz5M<%?r6c2p$ zm;<3^3)x`nDsKRNru%nj7RH(bYPCD`k)#FPHGl=XcvY9~JSJpOMdQAKP*4$UydJ`$1suNzEW-gXXy2~I6j zTxvf@lUq94H$b=sKFkmF(p&MSGkp?d`uFdltWMKF0tW2fQ&X+1PLIn?bh*|7eeK6O z-9RrIuJ?g(ST`0oTt7bhjPmwK+tSlYqUrY4rD(d{dY0Y2;~chIf1{@NU~8W|!df#< zm}IPKlhsq2yd0aP8~??6hkQLlHRgQxUueuRPIlQ=-5c-c(QwLW`1dXJ-6fNj{d+=Z zS@xfP3S=5-)aJ)z!<_(}ec<(vN{o`!K&WlkmF(=%9*m4aQM9tcKjSZ5jR&5_e4&ytP(zpR1juq1omT3$_W!;G&o&FY z#u&Iaop(lkw??C1751TUN`D?&YdGA8SwG4SC$e|vRhxkdnSRtQ*Oq9Q1YSP*2&Ln*+tC{uWP4Cz3C7Eq77d`d?nU}qs?6t^VtL%l}#<0_jdrlg1;f~P> z&`Xd7hd?MTy%Drs_5RVVT3BFLGh*pSf}jU^#=HsSEcs&cApy1NnD}w$ zkgDYykF}hduT8+i=@znX+Y}%AOuQI0p-l6fJBo^$qhQ)rXeu{M34-${GT68stPT)WvFxy z?c>>Rt-1Xh3UfTF}8T;qlwJ*(VHHMvD1_j z&-k$8rT|0I(266nYnbSlF*KddHo`c0rd$t<_XsKoGWV*7h{J%Bdr-?KCHv?-OQ zuYoDaz(FimVlq(-K&&0D&jgAl4k+8o+%s!_4 z)6v^-46D5Xs}*v9w_b~ggOx6Yda_xiKTs?6CRVzX+0Pbq<9j}!RHm1^eTsxIULq!ZZ*U;x-3i>v&cl1QC38v?c z8w6V`{M9R)Y-kB523&FrI@-)ShN^y8N$JZ3H=E}fq==(0Cqqjqaw8|T+N?Ukj4kRT zY4i#8zFn&y@pd)+Vu4P_&gCi_S%+-QtiE)$`qF&BcYq;mfG)w`1Poq|%)-@`?o-ag zw;SG{Qt3~jQqVxo*MT;bZtDCc-e1DokGUOtOq+o3>W~0b@ap=<6G$G^;gsaNEaL3Y z5({#to>_|=?}>GCkug%o=B1h}v2+fI{$K`-v8AGJ1ZaCXopVA_s(hg$G$^f74NBMe z0k9k#&Q++UGhXzp1K4-5Z8`EFl_wS3Dn}kW4INf{MAraQBSn~DJ{iv>xjYMcY(D^> z^~Yfv$XH}H*~I2B{(>DXOzLmPCL2Z&HykE?{N6 zPNA*Hq0=Ka%?u4CE{K;nkCo8DjD_9l_>iO;JVu}`=aIMAHd??e`#6J#TP%(N=XY6P zF*yyrvd2oobOXo{yUaQP`1_R`VDZ6U5B&WB{*b@h;C_F*8rz6*s>#}Ylu{qjqp9EG za9b6(rQu&wOn*l^+B0zl1>~hRqhLV`fQ4BA7D*@drcN25ben+!ffNET76ug`{mn5z zl7*`Q&m!lm=j1N@C!m2aE2t<zGeZ_4CtCzc9NJmT z^vu8i6QJzzNB^Dx6eeN1v=*k=K;A-`x{uzbV>Y&g22rWe z(xpHYLjDXV6isr1{^-~xPDg<_rJU?he7NcSzl;FaOn_QW3NuJK6l|D@GFoUw3>e^o z?t$JN`howG>gP_azLZrTN!p>G(nLBb&p6ol59KFo0U+W4s=I>=x_fa{dPe?%HW~%yb<92lYiC|S6+~`^!JCjXj{{4l zwKb%7rKWDBN~7t16rd`lyT(9XA7}EqF;qh7S0A8|C!~=ZLBtU9A`!Y&f>jOLx)=?W zM9KT4Po=)X`J^AKm?OKQp%qbb94FP$BaTqt-xVb;r~5-L-UpKsK=TDHcbJjL1vD8^m{rg3HmY|-U zvxdEirOvA11@-uc0lm)LAv$&YtCbLk*6^QfJk0347-Ih8j-%F`?91H5_Ko>c63e zor$H+s^Mqs*|u+}K};-lRt;6`*^Y0h!Jb&^tQyW|&-Q&o4M%q*lsc=1zp-ay-%!JI ziKWh};Ro!Q+40|N_;zBcvuY@3&vL$@hOER=XVqY0&wSrd!^m?9rOvA1uk2afH>|AR zB$gWJ4H|3lr5L#$N?MiW}04JdI5+>=AL2R&|f2$Ws~sA3BEE} zb-dzx)QUlg_CQTmxj9{K&R`-8sPQ&Zlqq{M<>nk!sNrM}RF|;fH)>?Q!{jJv^qDv^ zp_1{ii@y)rpugX4Kt4LD=AnUZlLE4`+d`bifKp#?SK`9J1m`T>#NQLEIC7Ub)(S6^& z$FdDVUM4BcVBQTGpiX6wTg`^ON@2S4AZ~GWv|oiM2fFt4xv$pK*FkaXXnS90>Ukm-j^_a~lAy8H=d>ne&5QZr9~kPX!}Tj&?RX4TS*;5%{|l@N!^;?Ra^>{;lB zis|&pX548?*zzw2snfzX{YjM#ByeMZ+@Be8+k7Cjsl!Cvs{kRBLB5V8*#|(ry@I@R z?}2Qb{6aI17Aej%>p^BMT?M5UYo)}G6)~!g9dX4}caSDc9Bn7?bw zLxAJvu2>T2r@OM4eriAFhQ(qGQ009^u@6;L$knlmig4pZ}rWto_^75+9 zVlvxLfeG!iGsix4RA4~)& z-Yc5oRj+}n0aPboTGMf)w0Ge&u89-qf1Eh2;N3^@ZU@p|nn-6)FJN>w|7<#oxdIY# z5b3NXw0(-LrH8${?`!X_KF7O0_AYvQk;Umuz59A!{k~u_l@7DM)+m?X2OUdnZW(JX zK}x`XmUHlfH{x)Dg1Icl&LN-f1D;4RZgwc%}p{!R8`fQjG!Wuee_RQ)eNXq>W=y7d5!Q677lV=xS+$;5fvaDpNU#mIo+X!SZ zl{u*UGE}t0^~W~PSOsoY0j|eu8_wD~Lm`1}fFA5TmMA1Vh2(kjug@VQ)E>vPw9#_F z%P!!8OQD;Vd>1d|A!erjJ1!X<&^B_GSc%@R_|l~amG)hn6*K##QAtQ+~!yE;uB zV7u)&iEdli73JBA6^qdx)hG4qeK;>@Z%1J$at$eUGCVf2AE6b|WoSAp7S3M>Ld2l7dMgcZ`0NtkaF`(4q5p zcAVpzI-f*J?zgH0yCRsn3^3L{B=w~G>X=e)-PA42m)geBk#rBW0T7|Kak?Kz zeGiG!B)AbVkhWM5#HuQQ#;I)ui+PSy;+42W*RP@XuhZ%JI&kQ8a%K9lBGvU)>SeCC z(@8X_4*e^ui1&EmyYuN(NKHr_(vBi^Uu(mL3(pU zg=XlPv=%&{9D2V=T+V>7}dF(kE2pHF;77QOw$-nl3YC|S4WldbeN(nsclRz%!T3F_&1!Q zF?ua3O?&9=a2ZSj zL<&HM2g=8yTgyI&x%zpWXElCtTJg$mi+BNfcoSP5F^UQd^IaOFKbfmn85KIu;f~1; zBomH3;@m)}3xC>=sqVgK6o^xdi)0fo;&~pe;&-h(E|#LWCP`F z@oj8JYSJqZmh7lmW%kjqE2c90F{;}IopWa+v!9r5tJI#Z3kY7a`Av0xE|TsV2O%V? zzBEeT>OmHxJ`_JE?sU5A1kimz62Tm&IH4ld*&ir*?G&-gueu!Chq8LLZBOAMuTbU{ zj9zgu+(9R!h!gryK{r*w&+DP@upWQ`pJP3MW{^Vf+0WJk=%gUI@2UpER~-wLadJtN zj=>|cQ&oA{F8q@?8TM|c6wcm~ier_c?prY@yj1my1ENJ{`#^$N#z}2P3=4y*Uf$0l z3I-;2ZpziJFmyv-+AY|?f}zfC(hcP;6oTLpK+3yNE(hTGlX6KaTg&d{EU&2gYO?~)cO*-~`uR5Q-$`+g(!aiTYCF|&E zoUYOnrV#mo;LKO2v2P2me?9e|r`M@yg~3qz00@>fy~nno+><2CR^Ro?YQ6dzr~{@U zsHZtF4-pM0n}!%(^?fAGpI|8+orbgZ8>TR;?V-sCBfjhdd5_?=n4`X%=e=LJyc{O| zzSC^qatG1v!0=VNC5hB>D5$23CP%Np1wmbGg^kWCI^i0>-MyT;k#BJ9D6^G7f%{b!+Hr z_b`eS*n?wB-dZ`b>n%k|lqOE7cAro!SD^+9%bo(wM)(%ZfY-F1OK*IS4S-A02`k~z zTzJ&+#*grxFoQ6Yl@HcVRpflK6E6VX#Ry3sj3K>2g<}6-u{u@{T;(AoVfs9X{##uz zT&B*y>*C83cY>w6qPmXX;1r(5CHyJ;=y-z-zvzh1m6N2AM?t~Yaap1GoTULt5YZZy=FFM`i?e5XWDd$g0cJYfNUIOjU~WMU@KvI z_eq%c!%)f2w-qK{z4uR4C7;}`TKc*_J*~X0*>1|UeCA!i&l8O=f6HLDo=JOr$Lnm+ zgaxV+LPuxn5!AHEJe9iI)cXwa{Tkx;ZD#K<91S&dbirQ`aQhGpQ5|p=mNf*GEB0mp zI8tWcW*h>&*2Y$~4UA?V*zIZd18|!KU-fnTh{2VzbsPSWcOc3# z)uj*wfiX5P8#{CfsZUt$kwk5H6t&^`KRSmt9Q)^KMP_dgsAi~bCthr!kI%tAhMHq6 zhsbfV2Nm|GcdOB^|;Z&}(Mt{y)XPY>LOf{I}qjFkb5DF9%Q> z8>2I8F^s^h2Ej-p+{m{+g#JB>(E(&5?jR8v`WcG2E!+BO~)U5 zlXMoxAPF}KqQ`fhE(c9bnxw_{MaTHeB1d=l@{n?Wi%__=-Huvzw+4_ET4 zlR4nR-%%TU1R;R|22tK<*ogoc9z%c}Up3Bfh{i^q^k(%L_JL%+1>pzvx4|Wy(KkA! zlln#nifiKaxK+1>^@vpll$)Op(Uu|!ZOR)6hKz(@PaCH>GYMw4XiM<{f#94fcyiZ-Me|Qh{ zojkZWoN@?o@DUs(#ch^Lt%ol@?1~;Li5_zEhiiDL#jF#)?HCj8q5dSK_ohwEvcHp6 z!FmF<-U+ZDdITqj>}0q4WEX>|dt&OUqfqT&4=sKBjN<4M=a!crPPfrz00FdLRYh-r z`;lu74@Rz{|I#zYa^*vhEosN<_9y!t>)b5zHg z3`-*VqI~>8s$-5`C^+c&Kv)YXuuE8NA8_a*(s ziuY+BbPb2JJwwq3ie3pIADWT*YnaQ6R>MerG<@vM!u5vI$V;IbJ^G9d^s4~;p%Z9h;2A0ca3KOhxC{aSC1ZcjUE!mWidMBihdw6 zTX*=w6NfI>{RU^}2(5$iAF#sg5mZ7~sJF_?54py1ww`+ElmhpmCcOIgy&SBes=@tL z9}OQqc7sh2ZUXsIP;$D=q6!j+Ck5ut@k$fLLtvV5F$azWE9 zX?09@*f%O=z~?xN@a)bg69*0tf+*V~4d2o;oNU;Y@3jc$9sC@PvC?X*?yyErv<+b5 zHOnQj&H<4-`1wHZkSlg5Zy>%m449B~@N4a_u?@%?5 z?<7Bv7Iqm?>5XtwiBSQXT;2OLMwOw2qsEt!qkd&h9!kW_W>K#0Rja=W*smTKl(3V- zt*rzjQh*#)g=>^#$)Kj>M~ooVUy7>!oaceP_bVYki1e!EP!azTPGP_#DJh!42O1G( z{jLQAW>`?$WVNK9><4j54dy)`6P93(6F`;-r=)0MspaavxGJt`ua$C+esMMnducU# zonkiNktQUd=X@pXv|^1DQ;$qZ zYkrIrS%K&!D@B%w1u6vvrxEIr8?E6Y3wB-rctD50O@gN^wur`XA**I&h5@1IQ5iXm zv`(i9r{rh=hCs`9!g4^(#F+WdL*;t%A+t}-25s>pjJH>Fg6Ns*b^?7)nid;vU^T6d8LltlU&#?+#8|37j(1 zPp-#gCtq!II+78G8cdx-?a50(w@V7@mG_`E6D)&aCPWoE)CzFaCZ=n!YBne4 zoy()uX~lE+E~mzpf({UYFTmIUzyGs8>No8d^N0_AZrU$=%TMYtNuJclOP`#C8}s0z z=d&azGlS^V&@zntFi3m2v}T?q0_nieqX(yHl0Tpcs3CGHNCKMYelo=jE==&fB&dhU zq}L+Z4^zu*RrXC|d_G=NUSdt4ZhtkkuI6}MrV6_BG?Ssz`H21o?qRkGWFfT)CR=>X&-~NaTLJYg&=x{|^F;9Me0WoK8v~>d zcN+e9hROeDNEmb@tDcz-Sj?>I@378@q1cz|G+UJ6=|YLJ{wJUn_>c{sTU7Orf73Th{J zL^UnebRKH432uT+o*6wyJV?@Z)n8!@GYEQkL`V-OZGh@h(f5oI3&rf2KWdf5H!ka;Z>^4`0{ML!P7ipLkh$Udw zsU#J%jM(fR`i>s6A0%u#;lf95b`VxSYw>4)#1_XU5#|WMQnv&5e>YDc%n?ACBMf1t zy?+{aWkN;9EqR@aXb*B#p$aQWB4g8!0$dBkW6v7Ih2$NI_8-Jiz(8PRrE$_=jAQ`L z0ExN)zItfVvwCqlF|i$7j4F^yqL_*Z6uaFg6{S-V<^=glsn?1Lpb#b1qU!$1Hg;a* zS!a1>+}@aRdq#7^J$U{xGT2bLHDU^_OH8<8B-4)d7=+IjbOT%6p^uz^G9*cg@~xKy zqm>wZAq}6BqSM7oNTGRhoxw6ANzWz;X}BSpsZ%Y7{?M(~AXiDC^F1jw(64FFCW$G* zq{^{0y}eu7W!#d)m=q|?fXP>Yl5U>5zH6b{ieL<97DjP!{y_k8o_K8Kvz%Zd@6n$s ziAx>E4`z8!t-3cu#(?t$EZTB#RnUuJ%t(JIV6`8U%gk=*aw-{|{vi~&gw-fs8ceEz zR-)6-z`dKqW6cK4gZCBU<6qL@#t5H#~Ndt&;0hZW|~!fPvn{OffyQo8U0i0asN@1 z3Rvb=Y1HD}q6f^lJUILbPyQ)MUhebdfv*=;fH1c?roHQ<*WQ(|81 z_(;pmTZL*tF({QvK<#t{*=lt$*%mc&SJ76sj_+)oF?QS@QlEB&d zVjhHYvO7b16Ub72JJ)0+r5Q4}(dlq=4deC!`vJUp!LeRFEH8JuA-qTmTeo_-{gFb4 zpKJKEcIBjZH~O3ef>wV8T^@wC+Oe(dc#06UH4C%`T<=Y@W0x?e%yF{yypWfdXUXN9 z)RSG-!bLDWR8b}aJiB}u$UXqIop}QeAt3p3Ta1Ab9k^ZI0Z7eM^^YsXF{x zlWNgPu2?p_Rs`TWYPg2om^UUNrHaPFE}4EhK6!QYMP!^C4Z7ZKcig@vWjy`KyiWOc?iOhPM0uc7(erO+uSJ_AUCR5nZ@ENtr^XZ?@s8!BTZop!5 z9>?&(+1lH0!rK4|d%r5dc8MEl*IPhb57g;)Zf|*Y2av1^+)XCRQcjdgBM)=~PyfeBC6Q6C-MtJ?G{ z?h@2fu{{mn73&MvuD$0wc;xXm&$;i%C$m5{(gU2X=|a+wF7R7}zL=;y{+GUIE)#`d1>I#)a%awhmmrG7!oyXkoe1bIsMT_PyGK<%AE)cFl zs>rIogglTgg`sMb>1EeRN53#v0YS$#j>{LS|5U4f7jqtu2b-vCcxx((q2@%#S)`&r zU-xx6RPNf|5;S&=MPX8QpN=V$Q?Xb}TKROB%+icl>TuhG-VkI73rG7vj*HmI^{jJa zK%PKSUhHra8u+{^(Fn}OG(Tvm0qhu^JOH-ehf1}vn?I7I#7MjV1wag z1O%Z3o^w1I<~*3u^#g>`22heVUNv&qkA+kB0p5o^HW_#qhN&3Q8w3NUo6~)AbB1aI z0i04_2-Vif%>{C^Q*JJno4s=LQn?w5Eti`s<>pl&?8(im<>oca*TpV3a_Z~YS!K>D ztf+`bgGb!Sz3;4L@-F>$-^4O`*GK>I_GzV#{%Lzl9j;F7zB0$cmggKRdtaEVE=PO6 z{0MVp+lf;ZDGPFSc#d`*I_LZ4ovJJLR_14q1sr|iRhaf=Oci6tSg8HNSWE%wbu!Z4 zb!6mznCm{BKB%Xd>c5H_fH?Z$pXRjh@e+^mYzDaTQA|C#v6BgvmJKj`4+d+k)N0F4dmSxXrXP0ufAJ!;Y*(Pb z@_?@|`6am0(T;lYh zf)UK|J_+ea)x;&{-Wy?CkHxmitgvZQ7Toa)OT6MTwE$|=AnHDY0?IQepFlz9GmOJA zuEiir7D4aczl2r_j>V@ryxxZ!Ha1Tfn4jAA* z_^x~WoFhjD9UqC=|9Q-w=}8>3La6BV*PtPPKzWBO#Fs#*h&~`>Vm5=XzkSz~w+)!5 z_p)}2j!*~%J#*5f6o{F*6Y1UR}@QumUX&Lzw2#7%AmVWo{ zd+xmbz6lz)_&aOA+wkq%g?qmH9Tz*p=D(Lwxz^Kc-OXTq88te38bLwu+m8V|w8wiiV}e4vjx%juk2dGB0j)`-K`V{BWZ1TF9BCN+=Q;N~r2SR+Z)6 z0b9A+_XqDhXWxru)^}BVZnl8-T;^ojv&Ygf&L~v{a#{g7?RSYf@7Q0E`EmA5oBx7R zkQgOJoL89b^@=yJY076P*KCShnRq#mOb0>2Pnru1VwArB1uP$sid|8g{g~t$x1zM% zYjq@J#f|lmKO{64Rhw(N4O%7!wI#>Yma-EsFEQ;bvaxnzWJv~S+EH4^nu~&34b1_# zde^vN5SmomYxu3g@P}qDLA7nMml0PlU?Qs7Yr61m8peeYfeFQ&bo7W1AX3eTG`lUM8Ql*gBQ~Ovb`np z*K%~oS#}<}2=TZEn?OFq#TGPo{Q=`jgGT{{4w94T>KSUb`TJa7>H1t0X8XuwyfL%3 z&#Is=-;O?+qd>$sD5ED5a#II$nHCL-$E*B3N0g z@Ujwr$dy(_q>87qLatp!zn6wia-&cQ)IOzFzJh#27iXf&5|6e|Ev`9;ZlN2BF)g~d zQ1%p`Ltyr8Mx6JZoPDmxkDTjH3T%&aQ33lv3 zknhSn2HMUIV=?rwDzf=-v;gFU$n0hd*-LlsVqTW!Lm;(mehT{0I7eE~>qQeeMbBqm z7&w4m=@oHQZJM>1rZj2*(4R(QlL~c3$ZfL^`LF%GINwMCMlxH{C?s1qRos{Xl*t*K`dF zQi+n1F_K2Aqf)_iy_iP4>4uVVWibycHyhXOB8QJ_7_tv3E=pWuq=ZVYQJd7Ajn?~8 zOT%wfKT5|I4H-(!3Md=x(CJjUCIkHzF+5xCZC#)O(-lQyivLXd{$yBr`@>W-fz1-kstXIUl0#TZDTsqW8E1p zMW`SiC*dByw>uj~EmWbGtB=Sl#vS_`Zz9GY-#d_PC>bT?yu-&g@jz^3*Adb~R*Wyg zG674&5Lt1=@qsWi#63ayQ0|Y(T-hwo#42+i$0P*esC_^#8P6}}n(mcLj>shxqtS^w z)8L}{ym(!H$yn1ZDg-1@aBsJb6r0lxt|R4&lUKxXpnK#)>Rkgw2BxrjI5NAVyc(DdL;^p*q^U`ww?TYJW!4i?|rHXRN* zjB4RQw5Y3Nz7iCWge+1AKgX@*gWVWqYai-Vgd9@v#9G?Qmu!nF94pnNt7b$XeOoraJ@kNf08vf=ei3zhY1_Qcn$q5-BCKt%*Ir1z(a>w3= zC+r78Ww^hyVsO9u`Xz*W_>fPn%S3(+JzRWn03RM4Ku4FqJN7qUMYtcwi#%{XTpXl_ zR`Vb3I=K6=kB3Xe$sOFi$nk!|?+*{!2M!-RJO~X`#rrZ2eQ{}h8Ize!if}osBcxyf zxy6y!IJFBOPIh7JtS*d07k-1=cn1dn?1KXU#$pD=!QEaBj zAStB9OrAsp$a)f&9MHO%@54G8Y?`fL0{XpFc~4RF#Wt%R6UX;)3TSjHx9VwB>i074j_d{8&d!8FE?43TqWYIYtVX5}lmVd0@iltr7q^?94cn zjDB5*SJ^W2tN5l8VAcSmHIhX7@fBDrEeOzeFarA#2J#~avZKH00t7&UvRnuRLOoVh zY|F%ohzrKPl5cnoAhry_UxKIIDqGIsVq1=V;E=!yTS0C%H(6i?pn}3fP$i->+-n$B z1e=-u14L%K;;HH@wwZLGyqcTtCdSuExtTo3pm`9X$uH!ZZpNX7#xf=wBZb^hAvbyb z;R&ooe~=FT{m|l!L-(oB!q3B186F4J-SEqfR%pi5iRvy)s|8hev#x19c7zmcCbec* z!5jVtG}FU1EsfvprT6`frMEr1eGfO}`48=vO}1Z_akw@^!MflNba8XTHVupeXQqHr zmjK3Fbm(0`82}?6e(!zvTcwmaLi!3$=^?s>ep%kcYCEOg9&fxH=slZ0o~~kLQS={doT=Um(4*>2rXgid z(f?XCO)9(GehhRe5c+QLqb1EOi({xVT@7*|Xeii+W@vmIb(=r@1C@<2Jgv($o{cIF zDpVigxOyxEM1mE31^u!Tvj!>9DPbD!1sYyyr{63Il>$#?HsX7^Qpfm#C#+d|U4PUx~ z-Bo2m**F&&s+Vgx2-+4DTs#coA=}dg;ZXL1Fhm_imWJQ>gL;IbT+c8Q^W$v7FlypD zlJ6RBszGoVET+vr(P8dpF652LC5I3U8!Cf2m_C*GY|>47F-3YHL)UZ>+iJiaI zuG8Vyn(Hh&gjA#exzuBI)N>67SW+JXL$xBtMphfUTZiR(7``zazMljB(|@nPv4eSr zzFdim#KqSpW`D3sC1Xkj0gUH6TI%rR9-iKFgn8H458!$NVKl$O(zFCcj3otzlBiwD zFXx)F?cHQudcGw^JfEyEJC>*L;><9Yg2qy%(1^>NZqzxT ze{&H`(Hye8LX3YTN%AzfmbpKAU}PNYEv5%QVaX|?%vF1W%$n6;)1#7dMw~`AGr1}n zu}O*|UW63bwCPW9i~b5D&R%C+>~$=I;VoouR=-?g>{Lvf(Ak$%^XTl2=kTc+7NqqS zK$c9LLgW?&wlOU0p{^ zn^>M?rx}Qm;VlEmp`gM=rMx6jK+07{mYF*bh%!>QwK_=-eXpW!Svt-xbFiI(DEP+{_Nu^O39SFq)Ek=i4}@O(xI$gaat62ysXozxUnqUT$z~C`rzlX8ew6EnYJGQ>Cds-v}r5j z(!9K4G~Z`wY6D6ouNVte+2o3tJUhSQNYe_&R)(8OfDgzkVq^(tD2Yk=dW2EDoUAi< z9uqGnEd>U^(8fa~0JJx{%m?k!E70cP;_U|zO-sgA&KI>hmJBz%g5<6e#VP1RR>u2#vL+Dl>*DfA%89{H^ z>dC|d3iF72l@_-F^Wi6z)<}h;nim#L)O!uoOI8@Qs_9#2SjF~pRE+U?wXiIgDlVg3 zu+>q6JLxNNCw-`MNc=6H6vsE=bHa5IuUUG9nXQ_5U%8)NphrCf#redpGAD67+YnC; zrPnc!<5d{Lj3-!=dh5tL@Ey#v4~5#D6#@F-5(G7dprUpI(mDW}_rzbJkg!5^0MIu5 zm$Ael{B*B~jSW=2HG%NY@ptncE&`poc!OWOVX|Og?4)Fj6MP+wOB#p2K zc!Sl{IV9Mm4HlhnB`_zHXkuGEm!jC~T`Ctm>Zo4@7~f=#WJs^S$+~{fQEO{{7j%Uq zeP~8#8~~yd=|jf@Q|IN1mX%>r#=+F1n~^sGO9x^;s>6gcwhHJ0NMV6|_qb{E zpBO!k<-3PXo42#yW61eyI79WgMf>A)083y-O6E@s5n z7%DwP`+Qh7Qzw*y=A_Jo)fOoRDQ|C<*OFI}Y5L(V$L=P3ncTv&LKiZRKScN_{4z^@ zhhP+M!W})4G6K%(ysaF`vr3J}RcZ{dK6o3F>28MoAP|nCpe6NXL$@5|K$b7X0a;m- zAUeQ+n2Fa$oK~O>m4tVy4YIv9h*tsW%92YTiMha5-8{IS`fdTQbFE zC~rB<`S)9zGC)`$Dd^$uaK{?cSUpLKGt&c$$Z@}=;UDT9sRek`Tm;In4`Es`0aNbd z`PH$8pTXqd_YZ&$4VgAwp~s9ydQ^v9!+6X=Z(|8o^d3Ng$`n9k*~V zgnPgj)#qAegP@E!jtjHdICPPDYMbd@D6^L@Cw;*7iNy>OB58n63szjd9Cs49Bxu=w zEWbX}w3+3ia79A}JIML>9>@kgh5-h|jUr%$p!|~R7_%)uPO8UI%pW90yj(qsPNt3` zuJHnL8xKS}&2byo$T@nO(iok?eIZSXF0?efhI;3iFcY=@^F!Qbk|W| zCMR}Gvyn$Q&DgZ%-ecLaYY?tv@m&L~Co3tnfeILP^fVYyzV};6G^;I|wIB7flW=dv zcowh;e@2!IZ^9_-!kc4Rs6*oz!$5&50HaWvX(%}Ycx6d84B3(#MI0q< zgTVTa--@_mR>VUfYLM!8;Xx>UI?cEhfv^haq2L7^<_M6{Ve81%aSCLau>Sc;V>T+L zMP}l&5X!1D03f3p`lJ zH1c~y-V!5dUV6R45gp1(!LoUpsH_3s$70h&p zF0+a*U$izYB%b9YeFvU|+jKeUCEiSjH@oRVMNAL5EWSXnZ(Y{L3atMv&>7^Cw{d$bW{%ubMWmI!=Rc1RliQw2A|S;U z+4Z)V(&w74i=;fXC}XQ@x>VAQQ6a9^WY<2~^)6Y&c8xtCOzgq!pb}V`Z=)^^l&{H5 z`tKdcmRhglL5}G%b)sBMmR`rn)q{?MxNHYW3CP5Um=|m3fN%+ER<}fRP@kFwD!!v1 ziecJQ7GlO6UfV1H{xj_A2wbJZ6*G6>qyrR8Yll_s@!_xln#&C5ytbxlyl32oIm4Z1 zgR6`9_$*(#;3oApQk5x-{lK6Ma&U7+jO02QwYIu4T|T3 zeCjB9x5)%!1!IW9cL|K=yQ=jWNAkg!8U5{+qKau#3yxXI+aYeF3?QRrg22F6^Nv9r z8NhcwYfx%4>u^SH8z#D<>F9o`hFPZ`NK8K5MycB*nUP453WF9Hm=O`I$ zZ@)aZnwTqT;X!i;43F5bja%H_jgZ+?}n{CyDv zz=WNaeuwkIW{62zgKQce$7y(!O@q(Y)P@i)#vHYV9*`Te>eGcFDTA5$3GuOsSkdix z2PdLiorn)edA8#N80dGh#z9L!w?f*w#qY=McI9eGV~D+J^`;@ zy9%`N2RQVED4~jLn7D>v@tPj|Ge%3IO>fJuVj97WZa{<#+6a`O&1(sWO@QpPDId?b~4vYj$q zq8-ARUozhCkjjm|jYdkG_|32Dn0>os90*QB$fHMP2gQiSr3l$xMvcc9H6BA*FgJxP z;~ahrQ$wrBC=&$7;?&p()Of|H(wF*0cQ%r%^!4dM9KGu%pN zyCBcNmDPX;p*E)DPIf-j-kg@-XfAGH=Uql*0)G?xwyq(k=__Sh8=Ls`FIat`50{ooj&BeVj47SLbd6GTN! z*wJKT&A#OWJ~3mW&6nVp&rd%Gt^cYCC^;VqD zId(iJTF%Xvg?tOfeWIh~9>z0)Wcn&omMDO%;&B_tDe3dS+VmqHd&U=TdHBm=B*pGEdriO($i zEc$Yzqj5?&&EJs96cwljPb-?@4PpBM+(P|nd{1dCTMFCQZU&kKK-*w2z7L=QC}9_k z1L;C*s7Ic$c zQ>(mp%{bdO9wS8=xRTh;aj3`!-3u^}NpzxBa4(i7_+dg>suNav#rrn4QQXxNF9rq> z!}z)NW*zImm#CJ!7hD5X*G62*d!Xe(dnj{t!{={3a!CP5v zG-82AOWp^)=H)1+(3U>^nG{}w-lP*wof^EIerSgF?}>YKqB{hJtJ2^83AflL1Cz;C zR1kr~OtUiwV`cw9ubN!8BTtXV0mRCKNkP3&ne#*FG24IXKLZ2yo4>>qX~SxrxHja$ z6L7{Rhk@O82W`}{00woeN*2}4lVd_Ez5ILXCJV;;1#@K2YWl@>35Akaq2--wA=$G= z5y#Yi(b!+1FF~&Bsc~riDrg-xg^J#62Usw??NldC9`1EE)!PTOrC2`5w0M$8C!TEC@RTwUn@@KFBE&P>zcO<&vwcw4bqHOJ-|A(7 zm1+|q4<}{lWG80-&>;~pV0#aGP_2pSFKmFY-oo+H&bz&5> zZ}KDx7^C&RKG&vXYW92Uy z*uY;vyt#OM&@4nVpBE4=6&SLWQF@=-G1hv(-c3qkwf4W(P}^+v2o2l>3%N6u9CE@J z3bfGltC(9@HtsK#dgb;!vSE!MV@G;6%2aPAJk& z<2a$7#$iB50;RzeYWk z-2(FLrS-!TH!I>FatfC9DTxk;MI<@HR6RGEl!;aFSlXk^gwp?GsJoqm~(2VM0E=gajx6E4hhy(R3lvSl!cTX>{-O+^SO z)#Jfkjh0aSg3Z1LEIkq(1nrpo`LCEXTTP{&r`!?o)0gwH#dl_XE_Hh75w7f;F*yrP7p3 zFT@+Qn%_Hai+QtQvU~-t7$J~LP7D9vokiiGt`ZAPqE zpTFE@Y@LP&raRez>8#{#CA6MbPmt+oh1N}-xPF6YszMdKnq_es3@T`4Ml=+Q^_Xj~ zf}2RLwHh9tJwY4FF;z~c<}HG5$@B+VY@%<>WD^Zwnn4k>u!Nd8;VOwOEpU|PwWSe% zuFO58xC=r{xGj2URWGK)5M}7XP!ne23oW%->D#p^Aai=*CUA5=v~_mRGLPMNEE)(D#y!{W{Dr zRH)^A1TlHUk%sK57v7lgX$AsHlNfiVSEIC}9rytCs;^a#TnmCy-7p63%>+^F=|LG| zcZGR=V47pp3sCs!BKVXp$0s0p)`XLXwHz0{K}**sr$7et{7Fr2AsU%@8!F?%soBs~ znrp+1>@mGun=RMo$hEn0ZCE7ptGaD zX&P;YCqTX;X!wGMSK|v?%-Eco;=^N2O!0B{IhMopp$8|jI}H2i$6HThLad4&kZ~uF z_#1l@A~32)SJxe1-e#iYqw#@9``t?X-HN=^;X)RhkSC3zJjk8IcjaK zhuEP78*I9E7akZ>G6u{G_i%@Vt5xNe?^rFIf-idc4`IFD$+NWWp++-ZP@vOQ(C=bR z@x1|C>#L}P-I=87U@P%imC_8d%No=Y@3!j58;B>`ff`+h)MPWG^w0}{jA~}Uu0t4) zt)74qTU<(pkU;Jx`HLowFFlO`A`w(qZchTK>J_lp^X5b#URk<55viav?WD% zy@6UV^CAQRU|j}D-y9(K+nhf+u8>_)!DgK>UMsW5EDl!XvE^%sHv_n;Ewo7gljCQk zf=8hk;U2AVxOg#1)u7`!jDGeqF6qko2y#4Wj^zb4X^wq>bz}0Z&Uv^M(b0L)Dp-`_ z40*Pu zp;b7bB_Q!Z^OX7Jdnvj#Ja<`{%Mc+<$#yJ?@=3CLhG98{&p@37NG-c4A z*w9xp6XZ7SRyy5`w;pDgtcUw7Lyht>!h}aqZXU-6<_glUoJUGC><98M5NDEh6{nQ^ z^hbq%S77Fl4HXsgj}ZZtIlshYJe$$;)X^KCv9l=;@=R0v5 zCAZqlz(R8zr-WQ48!kqLQwg%8Q+EAyEzBaEveEw$yalQiYm z^N=3;inX(XtnssgOv=kRxIsOAXEvHoKnTcOBhSFOt5Os1g7UF!d&`##o@8-Z+~-=@F*3AVhesU&d%OW8dlFTdss)7inw7oBlM($8=Y$#Yvqw;i zLVL)RYmE27lT$h3Ovi^oV;83fE_~|wn9?;Ir!WsZr=_!cSLMqWg|;DQBhxwufHbbt zlMk2(Wq0%l6_F9`M9RO+!#*P4jk9x8(5B=d1(oKh$#39i1P{6ydj>0}GjXGT2L5E3 zKCxuU=~GG-Yj%?1Q`@UZvn^fMVoIy>=h2kMQ{yE#9MVE8s}sK)2Y!tHdAkaH{Lvx? z4jE&f%sDg?I|Y>JpYT~#73Am-F2)(|mrGOw+<-6*EC2FB)x7o$zWM?SMxTUd(-5Au zHcUNjfM1k*PGxPW4+24W07R1BDQCA>XG}f2!#ONzI4C5E%*Vx*xCa~}d9tW{}o}(>>?gKoYTxRd4KgdC&Y!nD$ zN|B?+*7_d73)U^r_b`naZC`tq1LubObmDYC)7%-%Qo$q3sa96w;Z`3#tDTF&Nd86? zVu+0V-_g^EnpJC7j}%?gXbgg+U3L7Z)SVo;Yaz-ejeHL^NZqE0e_^-b`av&I}q@spKp?DeuCCNT?fU`0;Md@nbcHo=EC`n#^bD zbf)KA{FL!i1)~IeM^v+S6sa6EFUZ58{u2M?XZGa|*J(B@(6E8jWLzJUJn6l--hYjs9`eY(i|=>HV-ejQn%z zNrWTqr;+(?;gMcplHSBM;yi1O{Cg*^k^00n;ty>^SkyC8b4taOa=I8p=O&LB%utVX zJdt0`n>IbKV>{G}8ZQ$+n!KU{vryu>pYLJV;W-W^KZgK2(3~j1UfRx>NcB9l$P-tr zDpZ?G-jdxs-TE{Pu_p%w5%~tWiHn?%(a~@RGhBLd;G!PCuLOnfn<=2^3D!q6H67;juC04zRbCdJ6bH?k= zkuR|;?YWi-8>XKO(sh`s7tw{mB_UkPr)d}xZaodpT6Mw^@`A=S-<|{z&`g7Gnm@=> z$;d#{&I*a+(WbNT=zm{ASbb;FS~VN`7>h2%!-|C)^PAIEh@NXo9I5LOq748oAM(U# zaNF4jdjWyiJUBrHR2`SHlV`^K+CgjWHth^B03d!0Mm%u~0wJ7FFe6PurP5lPPHu~Z zBvqAD$-xpN2S5uJkUjKBwW6I|j=M2X0{2t2bIYYZ&Oh-cBN#I}j|pic1qcQ}R~(WM zJWXFv&ug|2F$QHW?r4XOT0=9bX*KlIfG35@4=ajxBQc_pNvG+`gmhY7o+z7|RoOJO z*_%L&qf)neo^4dTvJTOOrk{^uWwDLFv=x(Y3DYY4KKd*OY|Jzgjm+6v+o~QWA$#(G z;U?1`Y(PsTZfH-4P^#!%L5sdZH*fm4Yjiq|B;sn|j12-|CXhqQPom48%;vq9^9$f%6uw!qUcC^fDzgc9KQh*PnV2>bj3X$NzM0iLJGmX zjDN#l4p5y>=cv4E7RKOY=wvU4Iixjns{~I)cxQi2}yn~R&!|f&U{YGp_%h@mP1opVDzaSliPA= z0C_-$zu!`tj1+`=7{Z@^38EwwevX^Q2~;M2s>i}-(P&eX5ixb#jG-1QF#IQ=Q(+4N zNvXhu!=QT(|0mtkNL}kDH5sFi{=E6L!n%Txp(AfaDjpx8S^(n&mz_KSH0A>m4M_|Q z@pKr(KtS0UYY7Y#%*x0<6%x)HS_;D_t9QvPc|~lk6xBD)K*AGkOCiOZGh{u2EJbe- z%`hJ!g7`&00Wc%wxYM>&y_2tRUj&co=^41yX9iJ5_F4!`Z7H5TMkJg?$C!AY!)cCZ zM6$^2E_A6cCBCp+F+y-{s4L?-Qpnqng)G}hQp8BwV)-qckPi$PaR~+c8N|!`@bK9g zM`1Fw{%X7ZO#DR;fwq&{>7$*;#1xVvFNko&EYDRIbCK63<~9F%)EdP3s&R7NFBC<) zlPX=#^odD5fG}4d=z4p8=*MW+Bh&Dx2lPqnAU#ldKS=Z+u%!A95Gco>_S%)ldw@Y+ zBBlcWMHUHPKgP5J>dck<$=ECSdH@5ZxA_D>2eS`3z7mS^YdCQUapT74 zjTIrcnZEW3Bb%4SH`uS8*!i?+d!ea-z;pkB8#}%t$6)IKx|Vo+`7S_{$p6Go!W7Q~ z;M530bW8i*J#B$=Qwo%G|#(EKVzQCYMT z>P&-S|0j%PB$ucW*io`?ViZbUZmWB+v$Id4n zuf)Kwc!Dfk@mA#Q6#?M^wPG}6IWg;MXRmOrdJBGKu>3yZVq5w@Rr|h~j(U)*tIx%M z;Tyk&-x=byA+KIVE_Olz+!uG)yF(A7(O7j|Aai{zq)=H_5Gh#D9l1$;7&k=0V|Ld2 zvVzdVG~xbl85&OS?7kmSaU&3v9r`IGgx*@Io~xj~C!|k(^J;kW3V724Z$>VH31Yw? z&`x1yP>GKh`T)_HP*bA#rMl*mN3dRPwao8x^a+raU;*(na`aDV z3({8CScsX^=YOf%BE(S=whtsvFsrjdUQp&6`y9<&jvm33=XHvzXt1?JiOgcD81x~w zqS>IT-l40GX+;ZJ~ytY6?j?X0uJ-$yc32;y={= zzxam0JER{au!9#4%Xo$vR`mJ)Th9%u3U^ zWd5DFWPX>jlL_ug6qVkp$nsEGE?xJ-xLSmJ)K-!M)0iUNoudOfK2t7as?kiD4|{Uq z9bL$ir%9(1ta^gc`DsoB`X_D3(TTqzsUXtsRm?Rjks1atf|qM|fbXrgj9i*33CEdANCl{@u596(`JkcD*? z@p6$-E;3_UG+n6CN?#dRV@?Ms&POh(mdC_AGhTcrX@vPnY z>F9dkuFQAETHjDlE5J>gpOD8;?j<{Mla2}VyRx>ht-8nXldLe3Jz0<7uARISKlMq# z_w_Tgdh`bM+K^LKa%TeU-$-+ZSLCP{_fuL0~j_2c+TA)o}|0TSBR@N?P){W_nFJlh&W^o?N zyI>K|Q*H%xNl_v2d@vqQ z;Mi2q+a6OCdOIKodH79MdDC5LGvvo?H=G3(IrfWXpvjj6==XqH(o<#VEtUaqVgjA> z!<(@@8y3tBqrMVJkzZ%kuqzGnet{<}D*-Csns3=_Wq1sj_{&7!@c@0iY;yW`{ul)+ zmauCDpPwY04I26*)PSN)&X&}@4 zQO_b~QqRc^Sgr;{rJ0Up97qEPB1z;txt!B>lW)ZU;di3s)=VIAM#rLpRy^i(72w_j zd5&=FJkpR$gx6ygWH9Tal`XTf!cH$~;shQ@DzYCyrAdvc z1G0v#2G2-+d?nnTxkHafEaDO9=!4)VM?v$q4^W#f!Ju4Tu877EhUxO5l9=p@(PVt= z;s<(z1zlG3KY8lc$bN%1TE^iX5@z@c+Um^gq?HQH>1zd+OSdQ|Q3k+sw40<*jX!WlI)VS7{f`}*986+|$m0RlundI@5qsx4qK)T; zO8RO48bzr)!d~u=g!~i(ZJ@O|p_12V5TB_Pzc%t6Kt}otz)~td$vim}ag=`l??7D9 ziZbF;Sc=m`Q|x6=F;nbK6Q)@F_sj2o-Ddp7CC^Q<;f_4X_5yDDn!WiP?`CF9efNLa zyW76@ZpJy@T{H1+&<3xzCZUi5^C_n&*ZJtK(>RIdL^CqXwjcL5$x-D7`V02l7(dAE zI@&pWN{OH321+`}DN(-ZDmtJ~n6z>FPm4jP%`o%c#qpu9-*s9cX@uLDxFMsdOh+Y! zZ(buQNkXlp=!M%b4j+#>->7FZl|{mI?bIe7v>;xLC*Z}nkFJC!0)P>#kn+L;Ns-gL z*wNF(#mAFJglYQJn)1||Ca^N{;skJ*;{;$EPy)=Cp>s&!9D_~QI1UO|J)H;ZL3SB6 zzh(O6>~p~L)|%7k$>oSPO?R9SQlBn>cOtvt_4DC!w&SBl6TW)Y9U6K@cKhjFYLBPk zgFV2CB7cNlRN`@cEj&eBLdNzVU<(G`+}`P=?=l0*9;V66u5z znX6tjn3it8ZwevOP#Vy8B^wYwV zzCez+iVbDLBsQK07}r0EaX1fj>7Az;vh#F_`daGK%W($66BvleeW2EC+^~m&=-8K` zoejo=F39Y7(0vvAa}p1v24Qc8pO(x(A+!E46TgDGuB8~EvgP|MKN+AkkXhz|Cwq+c zp^zuT7f$`<&D&e#T28Jt%e5BmY;KnkGcniZ$+a!=lRrm3tENt2XY%6YDUm%}nXBH`C->l84^Jy&ih@*8-hxp+bTM8U@DfeSEJ08(886wkMHXJ7 z%f}XWR98A)@_31nYIFu%dXEp{kvALPz&kk8Teo1ACvmPaM=-M^D*gaW*YtX~`VJ{$ z%A4$Ir859M2|K)s^xF@Fif1avkCMl*S-He0KY~IBVB<^`68Vu1MBwv6#d8(=f#d8T zESU-JggvnzAbrP=$}Y3x9q}Uh5$3%9$W~Sei-wAMd`&b*{F6=Ld-4i1wBGH^>M=dH z%g}d~%N$y#koQc_tuV`5SS@T^3ODe1Y|^NVS(utA5XlTwA!g7vwX@jjs@50x(ZO0U;ha6WhEY?QeUt7>R>jpXBaj} ze>aFJWQ+_(E<*e3CDQO2Qg0^i45^O;5j2HaI@gmAsk9nK@}~-@{zH%&=GLkc z#WeUqpaHY70;a3cj>jekr-B_rd#m|gC7THOnPl+Zp=`rf?68V7rPQW8#9V4iGsGLA zkYTU#5UyNhae7Dl)lh+Ur$0NxQmq0}OY_q!o@Ez-_|Cu>9r$tFORypU{q8(@3Z271uR4!q zVMg(f_Tr8!i!`B^)nYV7@5;nv%u-2IQKSDybyCT=qFI2a13a|jVU}K*VIb3NbWJOX z-)tYszm0El=HJFOImiQ<(k*)NYEou1l%=yi&vTSS8>Wfp6U@UB6HRhSl%@0-i{Qb5 z*+o(~+i@G$(61#b+m-FujCD6Wi=lwxsGnvj%r1?@n8mRuEQ3SEGK?i*KxbC?$(Lw1 zDB%M#3sGSTg3RhkP!pcH3|;&z_91(BRsIwBu*rlwI(SBn+U8L;(cISR>3P*gVTNNf zF8ZeAKG$@;m;9JfjE5Pm*wxINp6i!O#v^8+6!$Bp=XRT(3mbY^_UUo0`5p1*C{Yz8lk_pR+HN+q(fnH8#BQQD z73-%-Vi|6#M=_|x!)a;LJeda9C`bviYglY8>}WFq%{?UEsJXgb7};i z6~mKDjwr6NB{KJ@_z5XF#7^ZI&9)C|06(}4CHcgsmCX7t9P~Bb*G4DPc6Hv@w;Nfop)*b zu7RKrSitbHQ)o@XG-xR2yNm()awq6BYnST6DJ=l9N|IFlPFJ?QCFzmvEotyq{yd0K z@O|dt?JcG7eFFZrY}?*)IecFLe{rVrxg1kM?u^NhT{V2R+SrtX>SV#=T1WlBD?=VU zYnW`vCQD~!LN{60o&{a8$Wd)<*hkuoXr|L+emUSGut4>%qvWDR?B4o)raoc*l)_AQ zI>=1rA(Up6#nwLORk~h9mx{cAW@hWl3U`y!Q90Hixz#0yhl3}w8-eiGIiIb=DQ7M}g$|M|j@4xZx|ohbo>HOVE+&#B zur#?x^V?z#N1&HRiV1H*m`gd|WdwQ;cRJnyZo=?UBiNy>G0bMCEanw<0t6XDY+09S zA0qyA`Ccv+xh6fY<|-i-^c~FgiE`rBn#_q~eqf5wwAqWU4tbG0 z5J__#ZleG^H%~6%BL=yeC(d+uJ}&fwI9GV1Rh>qF$Nl5d-~aSmOCzjo~{_H0{&qLRlg1VUG>Gt zi+h(iJVvTd3@x3;Q%oAw(O!rX@Cf2+ zqO`=evuyPHkHpz#Ii@yG_2YT~lq1cVh9Wad_>xIVZHCfxS&WHSD_;K6nF?W3oeE`G z>0?$*Y_!Bk78e){% zJhtL8@-VCAVOER#+_f+(BzYZ0czd^lt@EaA5>^*!)@Hn}AWtxy-fMd9E&BmSe*<^? zcqBEer|kG~L+?s8Bex+6!sdPJfZaqJaHsnAb?B&us2K%iR1MaQ>rh3VT9iNPODdUC z+4O~W47^l?yiBU!cS=E-p~}G?HGGPxfJXP8Qj{hOJ+hbWc9j}YKINpojCh>=b0JNj z10~K+c?259yGB`U(_{!M%YBPUNk{XUY@_BcsleQDHD&{_>9+Cw^;}bn+!iH8nEo0D z6qX?82&*d~8Bb#mJHlde#D)GIOENJHPw}kynvKDlZNnTNq|hsuP*gT&lXce6{Ty9_ zVQ;txPwh;wwgLGOU!BZT?kM6K$YkCQO)z+H4bZxkN>d*FWDoROb82H|OR>#KTA4?+96b&#-vvI#xy7$h0>9I`J%CDmXiAdDdXr%|4_h$lZeZkB^Wrtk?FbLV3>VwX*euP0_V8lTIknhk@zJDsi&sr~1>2@k{W)HV zyoGFat;SYyBcz^-O!`Ocjr&KueFbv{`AV5{AMUEB1{eb_O()z&das`O?CtJoX~6_d z{lwWyufCJ*fuw?x^1}TZGybV|Moc`nb|G`0+lr?*PIcR2*0yM0v2C`FzP<-IO|#XH zM;OJowS;M*$1xva=a7iG(TRygmHP-w1+IDE!;^=K|OtH9|P8pE$aJ<*6T6O-pEK;M=Jm!HUfdtqS!?b{B~M8yW4NJce9Yu z3R;NC`fh}Zgu^V+!7{>5NCF<;VvWpT{fPX5Y~aX-IB@BaYq1SpbzM!h4ka?2h*JI&lVF+`>W)IA(D7R06(1m#(P5{i~bsls+tLVrA3lMXani1InSp z(FgYKmGDwuuTtiOCo%jya zfbjIgw;d}e2w(;4d$jGhnPhPW`4KB60`r64g$@_Ex3fR`hV*&-7O2NXEWnA(~t3+1!vz-p+;xV0qih74gz6{>Z(zM z5!w+oLi@zPDJ4K71E=B<+HbSK`H%5NJoIeTh7M(Fbhhgh>Paj%du=>6yINMy)ydO| zm*N?2)ni&*wy$z(V77@=Bk5JcvagK1r7szW3ibIb#;3$&L!rcw?6y-nQzxGC_O*y? zpz9cr{hhM{vTM!}m-hMWDFNB8QEK@7ByN%Y=Ce~;JhllvX#Y>cu`j^nl%V)9VpMX7 zIxo{=vA;xkF%-Lc)U@ecmgjN2R`WRmY!84AN;x|wYPgE!LEy+tM}OPJfEa!rhS)fN zKP+L(54I$&1dt4c@=W!t1Z_)laKS|82B<7@Ug%+@Ipl`P$?ZHQepb^P+KAplFe9eU zUl>sGK;zg}6#lMgV``tluG9@fk^B~;xPbm*+goz}y}hOL z%k3>P{QV66egS`Dw{36nLwifV;W?&M2{oGi3G}xsPhmU2)5%+O1`bO!GEK{}BiF7D z&~M?&2(uh2|L+qyIN^$Ix&D0C)GgN+A`G7b^wj^wmTO?v|BFNp3F|-EFtPqmCaLTH z%cS`FzfoKNbK~n@^9oZ5z-KA}i2el$z-ND%AOLSM0`T|0BoKiAnxdgTP6qByOwnLF zD@B8^?i+G6^!@*yqhZMOwHys!jIcTU!6$K=@c%hSL*~Wj&e70wR*nXhIA9i+Pp?Jd zU~Ez*v$)J6TTx#8dLoyBYZDW>pjR3u)Go^=fy{TJx6WT-ba)C{Qx|lPun3g0T;R~n zd5*r;IhgGPbKthj%<6M`Cvu)_BzlmbWziT)--a9J$IyvVJ)Vw;Mn|8ildw^jdf2A< zEOI@1*#wmL&?DSa3=`eHDj7lbg=+Z<)$67r{{6`-uN@Rvww-)%O?%3C?tR2v{?8hRrLl5W0>0un% zfgq05#3MjJ5_5kIhS|7mUPY6~S~Gf;^?A3X#IsVAV>BGxYcnbprXO`P`!Q**H0Uv$ z@>u)jFfs;L^e4y9kPkqd6y`8tCG-m0F*9Q}c`?>HWbf8wX%H1LS_o{4eElBT)a`om*0BYjDeL5!_JIm;FajeE$s;GfnyiP?U0#>h)Im z1>WlwFATzv26d#FN6PTgPCQM7@7RimFzAVguL-9jEnKPXVcQSLUd$BBk|(Z}eHmo2 zlcdRB%mTkyFo&JiXBR!!5_YDOqUTy7GvPYf&8}VE=UNh7f3YWkxp zd!r2Qxj`L<=h1p5jfukxy4BeR{jZJpL&w9eF-D(D#zW%RuYUEb9r~Kcq4*X%@nVSC zI$OX##H7*-NEmYxoK%3<7lN9w4XRwGiKMt0us{_|jXHo*6$~#bZ=*)V#{)gN6d-52_OPfxBU3?- zhQx6Iy%u2yj84?=n7%MARLIlUkHZ2;rrm5a_W!V+$#ZE~AhBmPlnyFvK!1n3wqh)D zJ~5MiWto-q18te>XU`s1H+=tz!@+=Q_5p4=p}TmzmTQc)G$sdDlX9E}c?Gr_c_-dh z#n&2Jh>cZaV|}#fCHRevUBCh_>7}Pt!Cw5wf{BDzwOV_pWN{oOI4Rz{u^oDBwT;L2 z4*ci#{u)y|H-}pP7;jDO+6(vu6J7jsvbBlcK~w01f+uYMht|p~R4)?Jw_u1~q$a9M z-Ju_0E@h?00R8K0m?F@ums7~iLNN$az=kuKK|028_@Ti~L#ED$uy_etCIj>@ud$uhs#^OGh1TMlqX+z=VX$0r}m}v(-BfOXK;Z zxONe{)rGqQ1K2#PD`b}uK{Wa(hagrP0YuRZAokJZ=Mz>B6!^ypZS{P(pnIiu)&Z^_ zqq=&cTK#fK44OFC>QT)a2YR%r ztsQ!4B1JrLdSwGK$VDIq;c*vd@X({^U5zQ|@fx{Mif%yD7CTNLGGig~t_!=RZUZ~U z+bj(Ow;UC|Beg{hZFTUO)OIm^>c|q_^klc`9KBD zT}wLFU#b(izM>3$$NCHq;yJ;hH1cc1MHxCg_?W&EWjM`ZjOLLdkQnyRP!}c>!V@FM zSwY;gyVI;4&4IEFC_*++J7AzJa*=thT$GXTih??bq`)UmSlB zZw``PM!VXg6SV7BI5!Lh3@8ju!6LY;%?2rn`e8=M(EzBm@a7eh^8&pnr}L)tVN4r# zSbe5V*Wv4kUK~s0Y}OERd=eqYzmAaV&|9^$prbfyYhxeVT6vnl!BF*YR+IK-%k`e8QIk<@0M#Bi|)?@V0_ z4aeC_EQsHB95sO%Y4l3k(pDmGXW_z(zLOSXRdHp z2lbNnqzL-+Cwg2~V%l2p(ZvM33v_g;1RWK8bO3iI?hjnzIFwvpJXPMQ# zQg^B}99^h0>vT<+b)mP18puBBWX1+Av`Fab*0#O;flv!a!t|Fo3S-h4MYsZ_#!&GXN4%W<0Pe_={bZWL zW03Z)&I961Qt*=QmhCM8T82ruDohLdSxv8!Lw|?S;hOs357pNPlp8`NJY9LY zqEzAbA-6Fy4S+-2tQuDSCEJQ@K}}ad-b*}n`{}cK;Q~B-#uWp;Nbkr{6b!VYZ^L-n z6_&hX!pnzL^8q|f9MoQ=sUF+O2tIY}|CM;4pz*m9)XAYp;NLJ_H@Ya%51hHcokccEanyq)NJYHal?gVx`Q8X(fc| zWOE00&uNT2i-{%vR1Esf6KpTM-H3+`JOxZ&y4%Qhn$aOI7mupcjw0Vn2k>jMwX`58 z?)0DMp)-5s2-{6IpYgt$hPQsdNI!^Q}YxrPg%b?I(i7xG)kG}059 zOO8sP7^FQ$a*`RHZglhrsia>TjtT!nfVy5QeXgY!o%6)eks4g@c%q-cNvoM^O)s3K zZjo!{&IYvLJhaI=X>I*v&O>dFjk(D+^Y)?0RWQD-Rz-323iIVkKCi}^IA}Z$S|=XO zTxQI#HEuWnAMF+xnFcJ-4D6ZK=+W?LlvmlT@qM#M8rwPh{&axYV}b5SQC=9usRAw2 zT^HYez(oJJ! zWWTgqAF8#)^C$^C>3`NBu8n*`JiPrt$XmCFzL9M?jdzg+3q+hj$55aB$M1M6JnY#fzUdJY+ zusKw!koU+(6Irb+7>&?v6;i^C^4Pn#q=MK2!V8Ge3~BgK$tbN!gXu_h^tFGfDb7HN z-c82%zcuJ-3Dm}}pb^cKR>R5d#xM?+u0w9O05J-c=FxA>hhcAv$?cVL`zpD;PHq?I zVv{-*&RozX`{*zbIV^fVuqU}`IrW3~p5KxorjaOpe~yBwUYr@kS#T+)AFqcOF8TJP z%L$AlDCOTtVpDWIt%@Y!51Z6()R~^_&2L%`{d8AGrb{hAyOLhe6K5qbprf0H1Rmua zJ`H=mX{g5Z!)`T)ba?!%0Cx~>i!^x8X_kH^MR4u{-6B-c!gIOct1BmU=08C^mjDCct7 zy(P>9V_$v@Zwx7p^4l zshKB)w8)55TBnz5D(Uv;kj3ZMW{Ao3TNWfOI7=JXGT79a67QY#;8x=Y$x%{T7dY0F zBwB%P>`e;Q)G>^7K;0Ig4@m+AX|oyB6+d0_zsxm*QJQ$zY4sg3h4M3)sMVgK6I00< z`aCRm;>N}ujl7uVD5@7Z(i$$u_~?(&JRim34^+_0@Fc1*eLe`|pYL2QfW%Y>%YRcpCN!OTM@WiWYq68|ZhxrU z=wxAm%u^+_E-$D+uXch61z+PZK{XSxisj`YuTe`2cdN*Uym_=9=6wLO9OfV*-u3S! zq$s$pY}$k>9-y!Xa5#!bGoVXgi2E~&2@6poYMx6sVqi8#JzpM9dHZe51jR?@`sknk zpvqfmVcp@waJQRh76c|`k7BG@*fq+e>}zkIkg|tC${uD?cJ6f`<%pwdYTM=Z15$V1 zqEMNhMMna)oEd7#lM8+fkS%iTZ~Za|l47}^c3y_Fc+qqgjTo60R6u#;>DcPER!QS$j*czQMsKDyN&NwgY?ua z;0dc9K{}Jvu#7aX!fYmDM$k-my{(29%#f`pURLs3D~(JV8k0S%fM?!{!Km~PXl&u> zHZvaT^y7c4nMRZ{fp*oYZclsZrC9I{t>E3rNQ!r^j(muQ`Jucjb9@uFqGF_CLM=tU zZj;v7j4TVwiC2-N0#8m4HRo#V*Z>QKq)5>;VT|5^mG?W!c=206h~Xxe$c1++p>@cG z=Sn>q7uIhqW6La(7BtugSSVxuhUMa77{bUL5Ij(N7t=7^2gM`ONK++db-)7!a<=F@ zyYDZ!RIp_NIN1!A4BBh}Noa$G7mm{Rc+gle-{S?U6>t{QqnJBO*>VcAr)bVIFnI-C zt%#yee_IV%_=v7ROY4{{9--TQj}{=}sgzyAq$pRerhTq2baKi$gws-PLNYDo)HJ|y zV{T!{pQ~;VNl}A1ACKe{FyG7%p$QCg{OTG&karo~eAh>Ra4mB;(h1E_WF10LR7aLD z>1)FrwBkFL6DQ`L;MLPWgsT&88nq)rFwPXCm!LB!{Sp`oQcXdq3@8B(CB=>nW?dC# z#Z+@n;K3z$T;N@3Qvu`^bf$+{*iK^O9<7n-6D?zO$A52mme#UckfKRVpzEztyr@qS zCunb5BdHjXQoM1MQYw(QXyZ(_BHE0=a!T3Z^Mc{h7EW3#AXMs&KnE~Dp*)z3rwM(b zuY$pKs$p}AWoQ0bYiz!HR zBR4{i0b531L^51r1a@kbZig|y->i!-G2%Kx|AHq(p_F~=Z75}nM~I7(nP{~1<`VB} z;;S?4?i|=~9-u2(oYOe~Q(lVMiiG*Zxf*6<)x_X0gTaTdC4lU=r!eO3#QyA|`7svX z9jW*)VBE|M$w#Q$pb7m9oS=@r$V5Pjpg8Lc{hb4v_`fiP=_z#eno04$oaF$-Ev=9% z%8sS4&yf1O^s3v?))Uh;IjHAZHB%x?1JhneNlfoGMlK80$1v`vh1ZMI6fs6`a>IZc z$;&L%$`#YQS5GSFh%PSNa^w{!H>D$ewCMyI)M_SKHl$&w*|o--X6BQ|C_*D^70w6yEOMxEDU1p_&}BOHEk> zAnl9%4fmL4t9CzQz8DiyS>CP46{I~EA)ODv2p0zpRTFd>B&mO}aPoWvaWd3Z+S?V*XJ7+A7MY46al z?Df+x{*7D)O*VyivY6^-TfT~2sm=-jh{ijkijanTzno)VyFd!#O5HkDn7;$n31%P_ zDwrAnYxRk@kfMaTW@24wj(r=F?Z@2cqNP@8hR$Bhs+>4mYI+|d>PfJ+R`vo%^JdH5 z9NBvx=v=4^%V-ajy*!>k@Hhq(NCB27ve_CclMS3|-xL3aetW9Y zPY={iM(d$Qbl+{-L17?qN52BNBL0|~MEkBWmW1bMrJC#perwzCGtzHE*~VNBRP2 zpEO&fPh!H|(ix+02S~bpAN@y_+A%+^tHK!Nx1TztNOxOw(wkCsisvF0 zVp!=-P@3NR^T}erNGJyAEa+g8UtA7qL_MGfb@arY2DOww*Rm~J=T8+ytxNj#Dnz63 z=GA&;ihy5hv|pO{;5F6iafT^~KDS}bEa>|0-SL|7=<=9jOt_eiEk=@W#CbuHbx%0u zmp=jp#(yhTMvtswycgXf)1WHTa$IwD=($LL__fOB1nBp35Xgk#fjXXA6~}QHjX>!2 z)lA)|-}f*)e`p&rGMLc^`WQ>olZUZ)AcoA8JCHZaEFo&-1|qYot&Cr@GP0Tomi^YA zZk)U$Z^S!zZUbJ0Hlv5*WNeF3dAUxH!DJYJqWXzRVJ)e2YHdxT;b{_P(0%EUdkWaX zGSY?ZR1oH$uJ%DEKgr-w;oHU_dvp@+m5v_K>R&&Ot|AsFTQTl#aSolFI02HDRMKmyBXgP3a0<k+10Uczk3VmG*$j zJlT2DhkXx8IB#Q^x!lw_4M}4!oe9d{$eA;EzO#Ge7z))|2bZehkQ@F8>gUzBO@t?- z7Wz7>q|@AJy;7a(H)6d&4vh47e>f{!CZGc zT);jQPRS|U-Xgm#a*cIsO}bt2$n6<&jg5uJch%u3FPLs#O*EXTf-7TK-+xTeK1Q#Y zH<6-!%tzZZPAegIwvV_itTXNq=&EAYty;8)^{q^#hEt9p>i=mX0rUs{ zAM)M@EXpc-9DipRV1&^b6qS^e6wAUA8!d3KAe|9QbZB%&Qou?HCri`!RV+blAbEY8 za@*Rntv1(c-EzxzHElBmF;FwbKN+PZ71gDo!ZaNLdB5k}_s`5A(4Wue^E|)j`L!!E zbN}CS&pG#;bI(0jdb1Sfzsw*FAKxm~i=WU?r1^Q`T!WA?vYI~AmtfwXA)Htfq`&0`V_H;hOj-Q#*=M0f1&A_jj;uJf6^{kk|EYpFC0(fJ|un(@hP!HL& zyqPaws7Lx^b+Dx9m{_Z+gdx;s=o;yVKH;`Ei<%xB12nSwWkwN>5tPS*23fp5Fi3Ny zr)s3~-YNhqW273+KRPImrRw8KFFXln*4jq#xq86u2ZcLgVT^|IS5|B*V)f~u#TMV? z`^n{9S)+GJLEzB)G+eiKTTyYkr#ek}HUU}ylD*C)xtn@3_sTPi69x!HX?Bc`njqAA zYNhmAj(FzlnS5#iq0eT}^+{0fvB(zaGp-ujD2+EyFTY0R`0cG%>&2{g?vT{3frbNB zq@<;I0(QndiL|{-BN6b$^2X$sZr;`i7sxAU3t66A8%a?j{7P!EWSl|4DXeemB+)#l}vD|77$3`4deS=be zRi$sAJDF(SrGg*b%+Y{O=Jb)@I<}33jdGsAufLVO(uIlxdKcxms905^3HfKZw1|fOsQw&&i|cWfc0Xw7K_O0FGTe zBHNklvH5vHVffr);i(HjPeP4^F$TrmPm$v8gIrUI+tkkOZ8T8loWwZKyu@@ojvWWR zAqt(v6W3p^1MdGHJYcO2qhvi%BI}8itjQMOs94t*POsv5rY5GN3wxZ99>?Ru_mh+1 zBcTa*NQw`_^ik&UQW(B>Mq=!pZYGV7)BjxC)(XwvHvD|N5oiuFYdEA z=BWT2<;j4&;JAuTbf!V6bSqkO3TDB-8BAW_jvMly+KCuKmk}NsNuqn>o2T;a+viYA z`e2G=zedrb53sOs>Rf}-Xjr`*r#emwWQ#YiYYfWWTF0HzC2jqVzo0Nw0?p76HZ#3> zo#WPE^39$J`nC9!QEx(~(C7Fz;%|2f`FI@wrx7{Wp>c% z8Q`}0JfJr6Q9yFso3q+2n&q*Mz~ec4<;QapJwO70%Bzdpw?griM*+1lDvA_0CZ68Y zi$*xoJK*-jeERxL&^1Bor8O;<=k%R<_qCLFS6|M%jh>n`r_8%Mv10mWMKCM&Owh@o z`6yEY4gPilA9;7H5+=d|GjzCWBbp!Vf!WnYR6=%st!*T(H_#|(4zm%EdGwjC3g??e zr4PJSbY0QaZR+$&_U_u^vL zb+Op7L5q!Hsl7Ix6m280>IiP9@F}_%c^9`+d&#tsboI9eR{y##Rv)pt*tEI^8LNT- z=JWZ`4XnH~Xz6P;>oSyQPJ;k9iUKZ{&m8p|0M}v+23+QVAi$~bqGB&7#=NtxE0vh) zn14wN2#!y7E+)rj4!AN^Vc@8_I)=~Gugx;lGU>o=D>4mg0d!vWKBO~OrbcknQ!)%} zn2@1&C)mY@^x1_!0RE5zTF^Iuk<5|iN00K_rDucM=~3RjR`n>a#a?S|p+|WO`lI@f zG3)?#*eRz`cNRdBGq^+P$b->DV-ZNfLe8rc{rdpdS4egfXO= zcRY?xO7BjhF+Dtf&C*D6=%)_RcN3@?)F-eatC`PgueCNLFTr>+kmt>6z^B@J0qT)I z!Eb6e$!eeh3CXxD4EXDMvuG?77p9m1f^!YZLy~t+Ld7w497fybZrxF@y!uv~E~4Tk zXC%1e$h%wG=)>WC1V6a~5Nadl_#${(f**%9$$_bqEjkKEE(NmgzXd4*P1Gx#!nin? ze&HlYcZesX9lCH9{4(!z@J2Odv79nprzWjblJsiQ(@K&-P1>X+nUo~+K7M*^o=`ix z`fg?(vy8c$S6<4e)y*{EuPQt-EvneG8Vd^Oww6a`(OSCvywtEp;(7A6#-- zD#p>FzpVIJ6tiyfe3t&4y6C?9<}b;-`@wk&=VKWq-TL6&_bi-0b^fyX3z^U~+rqo= zTPk7a1_T|xEe#(|VX+=GqfGcpp1-wSw6w5z8Qwfj7VS>>!rZH7b5a+<;D~qA4TM%)W&*AQ`N}K(-h8`L$LPeDs#$$lul>yCGGZ_wTUt!4IuwJE<}ujX#+j zn?Q$uwKVp^bRL65xtYSmhv7?PA>rPF--WG6D$?ra0W0WT(mTLdQBPdAZsW7>T-cUn z$=dbM-lJvT=8UGjDv?wsDo8X*LLwa?5%xr8O1&E{#~|8ELQpRo>=c$OqH4qO8;W*{ zFNX3e!5~m?#?!@GZnTSZ7b7nKBmj6yylA0VPfnw-ykjiZ4}^b4n}zu#KZMWISD-+a zXL3+an>hH0hkpr*Swi2VS>nlF!5hqXMl?(4><(ukzYLfPIc#PaKEks%)@vJSmn0E1 zN>o#e!x_OZCCfLS`+8~;BnbBLRMzp6(|jX5Kb5+?C9z~&Q-^5Y@3ol%>l%3IND7rm zRm(V_p{A!`zTV|=!XbLST$mhNUP-NYgV8g%3SVZ3Wqf@w-ia16X&4k;Z3H#!)X0{+ z2zQD%rfvJ(1WO8I`{ zlW5X_?Pa-MHtnr4Fw#u{;WT|r_Q>by z_&fuwyw?`5ya>+cjjYHUMJA#`=+-zK3Kp1p!~#At7z@7aeMt;@A2LXph5>k|#bx)X zQnwIGNNazF)?8-M1^)#sWwnY%ZK+*VPRs z(^d;0j5Kk)i*7<+0(vXmAb*pTyk&9baMV$qVqLy^51EwyAm2x0_1;WL=kb;#y2!Xx z^ct9oLD8or@K@j)!dBTZ8dNqOQFmkjbz?(yWf_ZA_r}B^OBb#eKIT}LnyY6g3%!eY zRH7;KaO=D?+wXL1K$`||7Iw2Z&7?V81!uFJOfp~`Toc})%4*NHlS@hX${;jwO*ajk zB5wp5IOTgE_P3;Jc19+P@r+#F#X5YZ*Hbh$60gW&IAsQ7fF!KZ`wZgZIDXbxF`9*In~LO~-d7$vdcYa_!_=n(QM2-eps5itsCC)+nx*P)}cR^nDm|n<7?Pu4!?S zPA=WKEs;PX_Kac8^n5V@k}-IG3&RK-0-z9T$@N32`Fnt3qtKxsDAbS)#jtrGul&=?DSNQv@7pesHOCk0si9VXZs%J-Lq2@?%=V6@Ww)=rGY3rZf`a+XR5 z+>Lw|A$g;MPGiFH6nnB+^i8zf!s3N85a#hw-WJ#=p2OHJkH^z9`>TXn;RJd2E6^P# zs-bhN&B6{EruoJgJacLRM*ps0CAjVIDV|E5T&`(0Y$rkdA8&>sTZ2vD^G9*Ts?*iC z;sc@9p|Q@veaCIrULIwuj~*}}0)q$I;>{;5MRQ?8717ZoUYM02+xE?sL#f8|{@XVJTN`1CnE71Y1JNtR*;f zYFhL4!QtTR3WU2Fxr{9nCiW)ok?24%i_e_FSJ2iobv5b98X?^vn6lCPU575x6&C;t z({7@MIA94NtW12fs5UYl{z^{VB-sR$e5?T(>XlO|P2=Jplb-KWemsF;Gtie?<_>cu z#zozN$rD?Uy5BCP+C6#Z7Rl7kQ1@=>hz#jrD?X(D?{TOG8vug{j^CK1)hdtRW!>Xlqu59H0qjT;j4hlBiJ|LFZ|c=4sPc-XC<)tZZ1-z?Vemy2i4{f5Ef&szMN@6+qhy0R28eMuFSo9n&pY8i9yBeP zqk^twso}1dp;yz{xE)7?OA6R0CC1swS7E?ZCl}hCG+bVx#rsx~lV=lWkUc<%{Nx!h z7Yry=gQ((1R59-l$N0uloVLjckQ3%z?DtAh<7w#2IAN^bxP6Y^K9LMPR;d#Yu7U|al8)D)b6^6|o_o_7S}cC8HQEQ<%uhO?T!pO=J+cL>Unq)00EI~nueBBC z2R|I%tZY+HY|(-^)2s$sY>H7G)TIy_XSxC%%Ocpq0c@N*7-TKv=Z+2qWFQy}(fodp z&G}LS838PlS#R6x!q<&-^-kk4JjSavw1q?~q83JCnLR z(OC8v4#F1PImWw{Y5I;}nzlA0NoV|xk~IBo2VKKnKwtE0CL^D16$=Dw17sFh)}R+M z7cE#%S=Qj$9vdw#kM(k=$o3mVT1w#R3-~pXuktCLmf@Et)=rLR=(hwR%g>k0%QJs8 zn>b;&3x4!ldc&R5yI}VcJ{3Cfc#vSo!qQ?B!+{DfZz5ZMF3@$C#gaLlGVloLSY*5=`;!LSfiNv!lvPgsMVY-_Csavvo5KINCkBk;*BNXm-xl^1-}V?K$u3&&2x0I}7^ zvhR_b`nATgf6y;s#n?eI^Ic9k@=vpkg&- z;dz)*UwN71X%>Za>8d3R*Uxj`JO(W}vuY+kBs*%+6YM|}WHh?i?+2~m=_>3lgP)cz zE}sXT{so_rJtEjr zNw(4h#tYRwPdelYk?Ub{8atrCZkVrppe&BS$hB_!j;-h@Id6Qs7-Y&YpEsWr>ry#A zmn#&Tgyo})Q{NZ5a4o5h<0P0JWYB$;cZ+ zE>n7%6Y(u3;UOJvW%MD^5V#1X^)l}YvU0$d7z@&7>GrMom&qJ5!{2q|F+bD$k;yw)5fr{VMpA_jHK`K#Yp9KQ{7E!$zWfZZ2@HcS zX@EdXl0wM8MB(xhDki6l>}8A;@%z^&U~ui6qgUDvUj?PL)TG-pPOvI3^81Ya!P{4RE zPCUA=AzzH9m;@SrW3cW90yVvs4ur&AaTP|{Q{UfqeF#DeX0T^GCGlDgH1|Yt@f?Yn z+*pa{$t7bM?iWwZT&H)9+$}w`-|t<6&<*xf-j48;s&3KvssjieCkY7x!b>t(I$how zi3W5dssSN_=^0yN$u%tjY>l;(lJkHXptbm}cao_f&6 z$R)F`AHwR@ReN8ZYZp83m1Mw=q9kc%=!H@Qb+wRTQhhtH^~&JZo3ZsbHg?f^8MMx4 zNc*$RK)!{&(g!jenuaMA#DPH5q8fy$BIFdu&EWCDVl-o6(OC=e9jEe>Fxk?VQ78x# zCdT76lu8UVRb%a7(+r+>@p%V^i-DQ-=GWs7IUpOhTJZv18RIk7 zOJ>n>nC)Hq?q9GttHS_Esl_d@L#C%=Sch~ybY?jXYiK>kdyXTcDn18Ir0&2b*l{j= zsQe>?)NCnY19Ui4?StR(wA3Y^b&jW1KvEtC;{%q=wRk~KsEW7q#SMHh5wj5<5rUQQ z)^nxBmq1}lEk4Kf7E1KQ0dxbYFxJzo8B>keL(mBE@<6&S5q7*;#%C(|_s|p2Q$5N% ziP5eJGoQpN3q@z4)VgRCs&s<2#21&D!S0}!yKi-si0^c^GH3o++ z9T!fn20Fzh@af(2qyq)MxVh;Is)TefY&Z$W>t)kG;5bLklc0If&4@b(!g3P^aYR37 zVaiH$DM=7AMxk{gO2`Q&B6Aqe=H2xw`w&N6RBJDLHKA& zik3BP01!_DbJwoMMa2K)yBw9Cs!?k+s&Dxj^5O|#v(w@Q4L}~oshnWb_Q@|KKJu2OMf**>jXi@{@d z>457bD&+=uDi@s%It`RM7=|H+59Kw*0nB>Od z6^2G1ObZt?VtrQ$8F-paqcvsz1Y5!jGJ7BkTiV4;J|k9TTD3Oax#!+O^JDz_w~9Q? zc$DbkVenRiqc&pXBx<0XNZg2X{GJkZ!d z-`jYtSiG6%{A9dQ))%^yAa)W$)T%q1mQwXnB5*te#pANY>h6ih-nG_dl$XRhJQnn9 z1wz1bu*rrLlg2y7MamXHiEjGdOcnC-RxrYW{ zAcF%9zR>g<-m2-Jh;mTxm8#z3bVZ_1#jqj?XxMzKN54WR*ILiD5bSRr#BNW*_QCDL=6N#!lP zZ7g8BleeXibJ3`J8kii|e{DK?U=~jwYlTcx(7aLI_s$gWbcbith^BSs z$`q>z8>4bZyPM(>T^Fa$rJG?PN!vYvt28$jTz=nj(%^Qnun1;OK3vnu9cK)^Z&^1N zRIba+`9&-8esX_GeB?|jwVCQD9a*+YR1!^PcmTIG0YVBjn{wX&&V7Tha zP92=jke3g4NSBpM5}A_8c<`FlE~HhIi|l9&6zdj!al*uf77vaQO%&u{47Jq_!HDC2 zJL%XUMJC`rm0r}e*b-O1Yd#4Q9`!kIBPR+)kz_(I%AIwJjcBqnk{VbdaWN1Gxe-cH zThD5QExnZ&%XPYy2YS+C$ts{Y+WRHnYj(7E4c@Mk&k|hZDmTbFkKtRz3~!4+pU&#I zB=jCxNVf;yP}H%opAa1jZ+Oa%6oH5A@!8x9^U+DikLU)}5JR7six&2w^hs_#)B)Pg zl{g@Ec*lZ=cAfAfYRS?=xK5##BrE_uQ!Tmg!l&NF{=xf)pW#lR;FecQXM+FDrU47c zj~{_NahRmCz#9?A(Q}91P2^v~lJJ2usd#CQCisRTPNvfh#oU*a8;XB2Ty{fIlDbGV zqSiMCl(YhdS2gau0wZdF+D)$@8_N+f98R|m#u$4a;(~M)RcJ}FwgVRQwp1_el~nJL zq!Zz9ekeYX=h{7Ox{{G@-E7}*vJ9%3k9wcP=}2xIm7uREYmGXwK!IYZUU^JexiuI& z$HAaYxvNo?Efg_=q>@FC;A%(5t~g_c4b*5IF5Y{P#SPNEH|36X=Qo z6f+SB#190*qsIiBhvDBa!1FmfjiuZ2l6=x?6(%pVP_>i05_*2N4|Vr4V9Y?jflaR{g#P|E7zcqo}b5=9ApyK2X6c!xin7{ z1DBknsEDAN8yPa`UWqE%NGSGKG(}kT0)4FuHwP&e@1rm{EG^%mr2ba)ZNnCUvJKdL zhF&#Eii!A*@|IZXf1xcooBcuhmdRe1$&C@-L5V%x`vfXc)a?BC-gMFfg=tg8WeahC zjP*sKJu^aj0TX=-J@Od!Fvhhon9yg&U=hUrb71i{qN0hO$=!SR<(^iz!xlT)_Dws2 zW;L!r*Luj%xt-+#`jEB6gi#T-*D`Z}hm!O8SP*anse?8JV8&^jLBH71xa++}WpTll zUi;Y767e!ZiqzhF*akG$%g)D*484!#DFxe$Y|3t4(`3uf{aFJp(G?Zx=?Li$wM!y`5zbfBCck=wKd$9ng| zz>96@b4Ni$%`sLz&A`Ci$>g<5XueTo2Rt_(7Yl%Q6}G(cR?&M~(%z)5{S(m9pI&R| zVP&d64_=wUjj)MHe*F4ZB|rY&X!H|LNbLl!H16nl`~6~%(;O~V_81R`0~XBrNGm{1#Srx*rXJg6@V=}?^X4n%@0|a@B6hx1#ae`O zobuoTcEQ~X7d$-QzGTt8QcecTE?IC7K!eT>;IMce)a@c4@}%)~SC_H~Swg3T#OcMl z+z=maL|?ZKcqUbCFqb?wM}A}yW3k>7FQ4$quCB85MT(U|`^eV49T=3H3^s@&jkY5N zVI_B|MVkH&MWmXmn+^KlXAS9XRF_a};NL%Y0mRW>cvp%VFNGGd|0v3C*#2VwebnsV zo=*?)|1=k$5d_IP$5Rr|uny20%c;-hLSLBYu!#2}wqa`y$sxop8+i40X&bf6+p4`V zeG_b+<^Bs8o>2!f4SDSmjHErvHv-ylDb))%buPAw7K=Am7GSh9nqwEoH3LLR6mWd` z>v0=Ki!>BCV-DbWQQu48REQi#T!O z)S}RugP_V0s%1F97jkwTK(R92PENdp<{YY#&L&&)+7!{LbHf{&0WZp}dUhFfVS+9U zau;|!DR;2}L7#({YbJTUn3-)S%V_(cpWY~rJBM^It_A)QM?I`hIDXZ5`eQm1y@wkt zFWx|FyO`ta9kO`RBbNOF1Ehw06G$i^DMSc$8OkGI>@bv2Ypv~vJ7*76!ZxP=);GV?`b)$KQ6MKLuDy2Lno4bLsWbA?--HTQ!g@nJJ1J za3UzKxl#An8Kdwbr^_soqb^Z;Tyck>`f>a zS7^<^N(?(N1Ll%cYxn9Nv6D|gy?2naoD_0D51&Bu8YUKaR&+$MSGc2S=-OFJqYHQq zvWE<9ZZ-Vv$gTcObdF-Iqc)=Pu{Vlb7!q0UEm_Rx8R$c=d`Vob&2%fW*0287Cf4c_ zG1Xz`ZL!3^qE$rWZQe*eZ!Dje!Vd+;Q}I$MgZBkq!YF@2&Fp}*y(384etSme+r3sj zy)$%%x1|f2I(}Lvo{vmR=WUZHqb`|Rk+;HJ$>+_e$SZvo*OH^PVZl7m`H;G)hHC3aBodXZNb;!AjT244dI%z>`(J7NM6hd%W@vXJ4% zP_HkcWFGm`EK#f}l}r+g6CW`N!y!b8M72d2Z=z_`O38TqRv90p%-Ruq`adHzTG^t+B1p2r+h0 z45ZiJ=Nd4j8t)Rtcv>~w81vCJ^vlT7qDL5RsF0a1^jckoj~mPW1VVaxAVkp?P4$hS zA7CN#QK^^|ojx<&eAH8=r$2xfc`9{l^wqNEHCY*Smn@Z=1u0FPOva=l2b$W38 z=~@~()dgdZ7AB_Sk~u{CSYhG}m`1c4v?ZaM;Il7~>8UixgDHKah{0EnpnXqeta8P{ zF^8rI2a@iX$2;aZz3UK{AuCF7%*E6~-ccB?lhX8_dl%x7=i+!bBlyXViGV&IlA32=c@%FRaATxSz?-GB zpb3YkGLDEyK{Dl}W=>-7p(itA1pAEgquflvp3Y}Z0#NdqGdz1tIQ|Ss6Yexv4sbU+ zg~jP)^kV=&Uy|28Lx?Kh&kn<7nWBSQ^y%a^QOHMo)8$j{e*~1nPIqSS4BVO3FOwW6 z)eEzFydWzC?#N!P4JN--JoTvl^e9{*5#L8GkV#8gVX-f zTC(NENy(!XMi4MH{eRQEdcz{}cQ14iPs6TRO-Kw#*&O+Q6~cBy@$W$qyJ4P_&@W9()?=D6kM)denms z#peQ#&ZC)X_Hn$l6qlNzXZzwYM9fjuAB^}Kgo&AWTQyo(8s~jPM@|<56+IFsoF)HQ zsk$w@nH`fb0!(!n^+UoNSl$v~=sI}=GS%AjbOXA3eaHr6Aj_A+29(=Qcb`f4LIR!| zPL-eykuih%X zIgQVJ6gr6+iVL;muV@Xxn8!ByTe(JpM@Qn~bYXFd-3u6g#m)}A;G5et{EH6N69D*I ziZ^p)wx_0u{5%O=LUA*3nJayXr7L3bOhRMHh+Cyct~eCvGO1vLjky>0Rv)WXwW&&Waq7f@Mol(b>2Lk5GGjg7&l_N#lLd8 zcQU?(hfW5y!)+Lu8P+ga0REq)x5RK)gXDkURnSRu0Clbqtj!i{3l}bCwZK*)SMemS zW}q!GtJ&f&-Y!^M)`Zc^-O%aH&?#BnD-eFn>DG~}P?!+M#mzw98XcbL15*G!m4UBr z-9}88${?&Tel|ud&Z!QrA%QnI1qN-Tx;NS#G2(7+wYiekF2mZ2sShRv*fI!%fA=RZ zW&`~(1CW~9{94AdPt2{}uo?6gzBm5O4XRQ!#B)(wV(?rH<6<{F=fO<+!S|MaknBKP zZz*lnYMQTpv#9BuzFky=#@Yx>B40w1B04U+Jv5@AH(Td3dLPmRJvkJGyB_D0f(Pg0 zqcef?)_FlHqjdRYQ~7yhb(`?eyVkl%e+_i_Y>AdBG)2*~Z+orv1v-kwhucMWY>u~h z^VwHd7jM3I=>rX%+f%$VjGHG|D<>p`|M?2G|AedXqtAq4_z^jQNnfwwKxy13f!$NQ zmthM4W7&e|lLLc8)_2U*dl9oXDnw{cdg zb?34$-n!Fk#aHHbirf~F+bs6iL(AUz>(E$Eb*^uxqs~RXTBT(qJxYEti$vm!SvVIN ze(O+NW_ILV{$|#-J1E@>4;k>Z8{MyWw~J14F%y8FR~-m3s8{8CSocCxKH%az06TR1 zt}#(Fa;T7xwi!Kle1Q!fAAd^HVDWS{RWImsns|9q))er40cQA^MO<*!kEy|TrE@h?psBCCVjBNF5Mz{`%;@ohDA{FqDJr-e01uY zUG3DAbS2IWos-F$kEod>dGeb@^8gMd@J~~;x#;@sZ>c^7!ZaNQDI?Ll1DJVgawnw* zgVXz^kjQiayjgxS9__u;bRdAxP@aB1B9At-2S%|!2v80wo<|Do#OK*q7`#b(O`?|* z-*Ek|y-f^`2LtnfaicX_eHG>Mt~X%UAoxtvDP7Z^Gk0NRiGUkoHcB1l%EEYH7Q!LC zJP-lp35@}V{sXWE$!A~<`n4mP>UsRX99eN6+Z)6WUc+bTh#7Z`Z8}8-!V?kz_OA@_ z8I%DoAL6U;t3!0h?|2q_e=w?D#P_NB;^v94S&;!#+r(6QgV|X239^-Nnt%vyEU&?Y zb9caaY3$(DDOuAeHW(uQ1YD7PrvY zwui`#(e2d#6g`+LKny9S0djNb6VZBfWCrNn!k6gCZY?@v5I(orau{aw$xfV&FAZi5 zE$hf*)EB;4ljF^*hlH?NYdsA#P*kh9hGF zV0*o}5}AmBkF3?;p>3*F&-M~#>OEGyjvr*HXT#SHsOUFQjKo`Fw+!R@;*}FTg&$^o zy3z)TzOiCt`AH}iQ4ux~{#ZqPM9)T|&P(jU5R`4h!*%G49gEM{a&N)U1U3Tx_Tn}| zAzjTxkwNS%y@io3`>c&fOJH?ry|@UQ@9Z?+*EC}{!$^Fj7Yfce&Nxw{_8~@^kKSh9 z@6*~cN^hCNUc^D0&sS-{W6O`S*BN(J+R1gl;QUUq{a}uIX?9{H{q#j5q~Vts zX=_>plqWUHK%+4CrlFJ{=xI=(Fks4}Cg>dm7q#>4DqM-TTbP>gg~hOpVt)fH--c0> zG3r;sWNI3VEKOmAdQK~Rfx*+efWYUL0tOEf(sldVYCmorp zNHM2H%eV*30--}5ZE?J4vp5cR+cfVD{Mx}fF1D5Y3M7GYi;h$&qu(sTt4<9!yjj!>J}cl8 zt=Q@CiEoaP>)$Mj!F2fSi|O!5!}N@LvnUQei{KNZt&N1wLAXyRy;(FEJ~QDHZyEZp zd$VW=ro-n@_-wiM&7uTs4?eGg&*wmr!HvBLK3Bl!9Qb?~`+-li&!oU7PAm>S>9kfi zw`)Rd>eSHWB>0}J;5VPl;osN6s^ZmP41?_s1KI*%5i~qwS)`P3@fQ(^jqB~1P&92@ z5oBOcmDL^CpRw#FC{$?L^Hxz|Y#77rUJN+pA_e!hQRY-BJ-{sC-QOY0=_uIv`YL1^ zXk>n`O)EJe6gC1}9ZqtCQjawN561sU-ygO3?!eqOF&##gw(L45*$qFuI_oHCPuQ1q z$Q^d%Vxb;Jko2hoNat>9P=!|CFJvHb7Y@tm3N+2VDsICmL)suU?N5cK1PxR8+-Q7) z_UCh2H0Sv8@V<+qX$64FeNOD3h%P2Amb0u4AGFOdSFSZou6R6L4{a5s#!qJT=@Zb) zZV7RC%drEyw;c2A6}fFB;l5TeM@evs{S!<<$52r=luzB1e++Xc!!Zvs zEVF=lfSJ!EFji&>{BIHbyNbcGcKYi+_b2R(Xj6PFzCGrg{f5fa)D1>0cfFtEYbr__t}q^X+2O)$nJBiKfQUzp?ahJpG$8 z5&x!7g1;np5-@>%LdHT%c*E`6idd7-e-`v!p1qh0Cl5T?CWd1O%W5*Nr*c*-hY{?d zHygH{K>JCg<9TvDjA!b4)G8dpl^8YFxhpvfP*1o8)a-tq5}k(a?Glf<$hiry##b{r zo*Mn?4m-5I@)RwZPvfif!jc?@xCrEbD+P2Xg%jJPvw#7h?BhEpN;C^6xqUvBE5-hq z@be%j>&#wI)&(obqIW|r`V`3GS)zSL4*56b$jyRY=*4F>+Ox?!fL#D^VLlPLMgpUg zkMFwFw?C_XtLVpSSw3qa4jSVkfX3ya5Glc9} zynU9Ct;^=^I>G|t6RXPm94;D=qg(`*D@;^bZqxTvi}RH@$%b3n#KKtU!L1>Sb0~{P z9)CMoavMxw@j@s06jQCHoE*?^orUoL4-^|qX1E#NHcL9a!58XuusjAdj-hPf7hwW) zxI#SuHBH*|B0(nm2EJm~TA?;Cf40o!$%%|MdJ=3W`|m{s8if>P1xi1F73c*BMNVe~ zuYjFYc3y$8XM+vyJvlRx3WQuZwK~XAWIYpJ@PLHF>)B|b_a-z| zWaxb18!>Ss{#3;W$zPkzOC9$zBy(|vlK{NrMQn2;rdVpIFSljGEA$838%9-%t2z!7 zoKB}mD>^ASS3<@{OvgFy-+&pO%29a$K3Yhmb;SNf64XyCzXR?0o|f$zl6&I(bBu$! z-|As*P5%NM!!6u5bZuDL9QMkz(d-pzY=JGZ9J%H)k!t!Aftj*@FNzX(dvw4 z7vYRPW$1fkxo02e`+FB1bO^y2t|) zH2A6zKrsvSN@ydgC9KwIC-<#Vw)Jr?^4TS62Ygy6ZV|2L@QDeALgQ|B4@n+qg9T zjo7bn2o&BvLJV&brszDCI?G}1XFf%!fz$yocwnkGQDcYY0@uRRY8ZO8dfj!mGM%Vo zi$o=Y*wjcOAwW8p;WS&Qbn0cC7Xk&WOFc; z(6}20w2=EPXC)%p2&ju&nlH|{J;LLk!^V32qgkWJKb|#te$?`u0p{3P;JIZy7ww&_ zwX_(^(fe2~9EF9$p)eOIe35keNZUjgX$vBZ36wAza*;636~-pNNhx5_ZYG#8{0Ak% zn58BoVaPpPk0Tk0-B98v$_gfqx<9v5;&>2eQZEz7!I^lIh>ZhPwCKClxHGK+;y4S$ zp>>ke*#Y8kNTiYIL=r&KxC80ptXG#0gmKsvL>PrqKiqQwJ)!uVX!QeO^t+5O{9O=6 z$yZ1ir*cCG;~y5pyk8@XC|DL3X-0OS(gu}wfjkNsCXAu}>*n)L^7r$KE)a!br9fqY zUX@dYtXPMTW00&`mKJURISyNvdq5taOw2gAnkIDSPPH1AeIv{?RYX|oO0>eBbVyj< zhit;K!*Y1NaK=~X@|I|7B9evL@}nh#)<&1@XCsWe_D}SMxs!7$^y#;kA6?Z1-POR> zezd4>sbySoxG%bfNzN=yNo6l|Fr~MQ;lf4OSo*ri_}Q&ux*hoFn3;I2YNDZwbJgw8 z9YtfGkX3DXTx^mva($7a+nmsh*JBebThmw^_s4SH=1TB z)#$sk5jC3YGcMGlU(?YGK$@++mW7w@arweZ_as-uWB~GgSD~4*cEDOqMa-0n2pe$w zp92p}c#2I?0=_v3I28fve3UO7dol@G2J#%0>6B5JE3qsWk!sZ!YtkVfLmJ~{hlfIn z;dOtjp0+{nt_O0mv#I}}c|S%MHMmldps9$LRC1Ss^p43w(`p5^-FZHRUfU1V5Y4#u8pYZGp!*8n@Q8wok=?X+V9vHO^AAq?v{bjSavq?sC|n)NEJ0#wdus z0#=qE2xd*e7nEHclpTy12dB3MAqK1yBfFNifI@x!tO9)A+u*6uUO4Hg18Dfd@UWx9 zygysQj^$Gj90nB}9t4M{D!G8~1C;#h=DMgg=LS#-A-xwm9N(MGZkQ1op%JBy+5)w!L;z%eY;$e5(*zp0+{D z`jhJdxfOEmTsO4tB#+!lopAs?P>$+XTAl8-8Y*%P1+~`R^2DGV3kDbg|Q9p0?QZ0Pto+60>>#anBHl&7VX>x|n`kg&tYdHiz@|NM-9 ze#Ae`c9O6Yx3PF?@s-Xtqs&2`x|2f8mDD?cGl0kp5?g=rOE~DH(dgZTbl=X00Lmxvaue&o*m}#_hqjmf4ebg{&>)uZ~iEc zJaQvFYJHSu;=!r!OTxhqKG|0k<5mZ|9Wnqd(uKKlR@7 zI{NeP^rt~6KSjToyyXPbscqC#6YC^fmf*e{2P+dPR5#%h^%%L_MNFS6XTIG)2kj{R z$mAn9AhY) z?i!=Q=_`^ohQjI79^v%dr4TsrmqOt5@TFgYQ^i9voQ!1;sX+355&%+W_b7A~c_Uc` zMX!1aCS~0{$v7WCr<0ds6JNdq=L3Mg^)f&uBB1&rpazjFwRPXCBt2P$%yYz?Od<1R z7m#rTK(iJ7oDdciG`!P5{!UX&GIV%nELn&8hqs!vTQ?~i+2NK&1_Z$#~Jrd(Qa{X^*oZGHfm#5#y|8vG!pf(rGIB%oFAycjoVw`E$ z{|3gnt3&1h>T+#XXkT5f=cR>G*g}G`KF)Y~eG;AI!3Tml=sb0a4jubV9CY0%6-YlG z>%>9XK;F?~e*nM7jtYX9!a-w3QGmsb3c~N`QNIDdy_Y%z?2${I0k+`MuLEqtLKUG4 zRcO6@^f$87xk)Nab{!32r721Ob5^QVTL)J9%XLAllw9{)Sm})yLUx(q7eaQKSoo<> zzafbFZKfd7kCh=6=Oq2^zl?(#Rfs)!#tKmCL9UnpnU^bi;w&P3_E&l7aa7h zYbl1kb8QfY{qx%2fMMr;34z`>zl1>V#4o=OdLJ)P@$7CDYAJOJ)TmVY0!&TSy7UYe zS!=~mmkiB(IgqIwTf7oTTHQQD+XM64F;a!>fV#_=-{wm)a#s9C=C?p?E|~dQM^Z>n z9T~*@ZXKyGKkP(>H2y$HZ=qMC)}E6gy@URIsrGdOJ4oMhEfn`_ivOQyD`ZYm-Qh45#*HAbmUK50o z1FumqvIjWbb2bD{jif~t}pg8tX2&>DwI)v4Q zU=v@y>C$gubpzGbfz?G_9mMJ`jZj$KWsq?MKvSadHVPWt4i;XJp-_2A-VW}f3~!&b zDM%zsL8Xd26kZ|X&dV9WJP)T5BsD&Kum}7K;d!YeRM^I=pv9p~4R&};Bz}`Ch z>j2Z;(uL&q?Y`g0&R)Apg^6x|2sVMA8GS$|Boms95VrMs8^;_6k(GMY#jy6FK^XSa(BFVz${Dbn2_@_!wxxbHE7v5 z@>~^8p1yr_ZU}zG=T&@1=%dp?vLHR?$+=LUGq^HXfV`5-g8H5WhHM|AE>Qm(`DRMU zaE!`uZpPvGzJUHq*>F!o2U;mLc3O6`q^WVpg4h3Tmm)jpA~-$j)*)lq&G+_O%gdb68#4XfAxbO~c+;Q;yG4OfbF^VJ9u< z%urE&rUM5{)9ujI&+s`Odfg(;xR>WuoDSnt9Ggf?CAw$5Pv@FH8}CUxF#uf$%CVfx zA;(>*+9t@5c=X%i!rg{hxz*&_E2T>Y`hfq9!0F4+b5UgZUh3rQizYfO#PAV;wI=n0 zgIig-yW3XAsI9nm2esmkD`cAADk78O>76=Hhh}BCr(;k_0!iMDi(!c}K=th|te90V zDi`k1Ygn+>lL+Xj8d@HYE#ooxx<*m*JKHCQ-nTpJdrOH$ZV16P1ctdX-IwgCOm~lm zk(|Rpop;Z{4?i7SD_-an`|p#+7LFG;KSEDuv>yF=u)0Fh`Dgh__Id~TlPI~HTuZLl zrGkYUtb*m(9`rVA(cfZi zUztO`8-yJUgGRB%yDU~yNf>W6(F+gsSWEJT;Nr;pgB12j?sy-+qVu+Iq};ekZM+w> zQLGbceJUro{(Z&qZ9m?|BtHP=ybcqCCA)X9j0{R}3bGv)>Ai-adzI2MHgnPjwI}7IZ)&$)Wy@WWK~h)U^<#wRKgiuatPVb@7J zm0p^^9E06pCnI)Bu9!i`Ly87|yqhXNzt=?-C{^E0`8}7HH@+hYETtuy@h%Md;GL#j z8&ldkhYc@n9nD3QuFx=SZ`dw+?Pf|DrtByezB@ggVX7J;FVu~I6u?#BhVC1j(SGvS z)fz@-l#>fj{q$CmlN{(Hd2zAk!s9gIT_s^y;X#_PNlAz)+)ERll@quy&*5~Twu*## z8d9SD_$|WI5E)&Y4?O0Edlx-`@tAJ7=WdpM-unQJL3P9YqWO%xYb*?rGpIwp`6P{q zvF&BtieG#h{#wp*@#MMx!loKW`~y{ba_-hLO$Wmm#?r<%6vijN2?P5!Q6A|4atKP| z+d0W37po>WZGfT(LvGO8i1F2IcuJm@{ghrYNxK1RdB|K}IGG}}Nv=$3 zMZfegwig*t)@%&`?e;hwKq3K3|wbZh)3@=j2t)DhR>rEd)!UftW##&+X zSHF*<5RC0*&f)Q5LDAt(cSw`N302^|fLQd6dJ3uVeOkm=Rt`U0a?9G4T1NURw2+zG zs0))&(-cp~u@jQH-l;F5D8_~J`SCu^TmAw<&RdRo>Is3d>@bBSwWP^Kl(Pb3*?%4MmijKirWw?$U|-D zT`(EM2iWu{>xFVexSSk*a%?KaKsL(bMPe2C{8-#>C+py!0sch^l$J92NPeTc*M`4JYr9R$|GLy zr|^i>cPNjj?UqNxU8nMh5o>-Ek7(%3Bc>$u$Rl#YR30I{`sO6Rm{mS;K9cf@hl2RT z{7!tLzv1`s3H$2`pAZgE6dcLBM!~}K(-Zm0$^7)OP4*y`aX7#-Hb*1N*!4SE#)#q6?MlozV3lmYyTLn4*jQ0s|YBW zaSV_dM;`%FCnpG#0fLD^qw2&#vU(WRm_Ifuev>?^1m2Y>Gm!x@6Ol%hOh+{~XjGlJ z$@U(&$zK3+K#jlu7&kfl4|!C{ylZSQg)0mt;X(*Q0sZ@Yn9NiJFXC#yL%qQ8V>@-O%Re8QWdk~lfy26{tM@RO$?3qMjA-DKLDK!@dc5}>ixQ|NKTp+!f4fYx_if6r5{)Xqdh{LOR~JM1Ro{4( zU$yp8_!a*)u8l<*}{I6U(JvFef;X> zS3B`5M0gZ#rc;ufKB_x@)gJLj_|;3Rew|+>c!K!VlFbUg8s8!8(3xK)0PKXx0Kxd~`PGabMwRl%M)m1NMdIOI@t5-}7}ZERs!`qZt6e?t ztH1v-el>Ap5O42_UoHOm|3Uyd`mY|?)}<(kZ8=GE5+$tgB@0vwR{mO>M*ccutxXGG zv(iShQ@pmYwfY;6jz7TOQhIR?Ybd=qnvG0fs~>-W`zgi5UYibo67eTHsH^<&6uSmp zF4NU__I{h_b#XIgWjRqQE8G4yu(A(AS=r=)Dk~curLeMHn<*=MzFSuIVuH%bUN8P* z>e-`}^2f(o-SoSt3tSzB~0SUHeDWv#%ch zb@lA{>LA|r&Oa31)xS&iEU|m_Y+ny7V)Gwk5&A#L#F%)wdY0I|dUi_>BN_I`MpCj) zVWF3+XNldbXIpzzI`%N2@S(b&kOJ(KI8E=Vy8~;HgZWmFL-g2bY9RNt}>Uq{w19k9Af12g32J^ z0^kwi@_E5dQ|I#nt%K|stezJvsHJgv<@18scwQh?fnQnjCADyme>4|SV6kc0G42Uy zQa%SccH%n7FgeuRd_Ks|?34`6#H1V<;EaNf48HgWJu;xvm5vN%ci?@`ax5nw8EC^* z7|RDZ&;CP#!g-wvg&CWJ4{#byoepr8{iE9hoMvfk9|yU)$A$zDH__M_Xv{v(COJt3 zkqqJ25Ys{Fm4uR(VdCY>H+I`uhTK_wSkLzGnb1M7CH!1Bq&@i6ZY|;dZmN)1y0(P3cT;}H<>eJic!8%x ztC+%d#_cg4w1s8<kH9w5W`j#L2O4`KoG-%vLJ?EVNR!CMY)O!Vggd<`?QF$tfYeqVzlMY=Aj^lUqKMN z{-sbm`KC5W=-AvAWGAm^qk;wIU1le5lmwEBwh*C1wv%HJdr9c{@scE5G|Qjg%b(xM zpR^gWYE8gS{<1uhSK5NC)AC4O4jqZLhmll7HlU+tRRlIPHG}zoUA4y3Zj~bG~1P$h~%WGiC88ekG=6XDvwd* zxlA5A(cT)cldtTVVNg~v^>36_Y=(L=tB86f*zP2;ikrh!R`Dp5lvqW~-&DI(H>~1x zQZTEyxac>rimyAfivMbRWEIYbR90au;~BI)ZR@YHi}1yiU9dsyqOcRYXgc@1*~L|V zR@g<)=5mR1>?ikIT&ET{Dub9!53hkB#asWmJ+;>^XTbiF_pcFo++8j1B9;dmA3O;D0mWY5xj`3T`G71j?ziNyX&t#FqCrt?_?-# z&r^mn+pjW|cmEcAI4?1jZ&AyU7|IyG!cZ!oR}bgAVJJ^r6U-g2sHv-CVN;~{)p^x?~A`KdmLUA#6jMECe#Sj zS@uZmUiR461EFsIV}u&EDqsZaEPEt&FMHh5!$^kxv5~AO2^fJo%N~i{%N|>MU=z>% zF*fm2afs|OGL%Pr^7H>HkGSdC9(csv=TsiyX-*Ha?>|9h6MKu43ANRPw~0UeD2;npUF;i{aZy(t4ZN5uvZq#_`7xL_r~Z+vH46*YdvUlcVS%|br|`r`A8pwK()WaDczh9&S6 zqsKphyPt-#Bp*DlrDe#v*jCY7q|M=r>r!)@**+rIOjbf#m>-h_>v5iMk3wSC5a$en_8KC3>g%9oTfOT#9W76AtxQCbu>fg9?n)Y-{@q{{W)Nt$rpS|1jU z4V0!#U}0kQN!CVzJ09leiz|IYDJ;sMxE;TVH<5&P#5EJ}zATKkHH7;-thmmtagj&# z@>*cfXAKS?qiR5nr+v^FKZH1bR@dcAKuiRIv^GsQYVhUly=ZLnZM6$ENi|{CgLWsW zypeJqWL0^{_HHJvePLAq4FDT_*V_eagTN?-{~RbhMk@Rf6b@^FqBRi-wj3c6pnniR zT)^jvLUL;Eabwv(QKc@f7huMRllEl`y%7#!`?8%RM&BwX6*oeIVHbqrfUGu2I(0K--Vj{E5iYq3wY}RkOSNrJGmH++5vAp2*_7vLaIp?tOvcBK$OWk8vLvz zRj~SnI#0vUs@CwThKMlTGuY!=yYBoR6Q{3;0p=6w3$M7sV-!WJKLeMN^RGMr&7|at z>?ml1jIElQ ztLLtC!!~nDdhS_@dJaEy*j#S2F9Pc5xJXPp;R_RTPl>>BP6*Y$KBVqPg#=-_sN*ih z0U=9oK599@^(Sxrs4i;nzsP3flo<9q!D_JVVH;9R?5R8P&B1K)R}B*s9%Ng2=;slbNO^x>b$&^4N|BxElUrob-eIsHF zeZL(8gG&Znvc-?WU@Qg!{KQEEJGP?@SxaugK%ZPY2x#VSTj86ZBz=p~apLHE1u|`$ zFvuTY+)nM=N1N4dlcjE(A^_~Aw=(S459EQOt7lEEG)T7Zkx4rzNvFv%2yA$x_)j@ot zWWL75y5Q&V>y@A3-lbaK040K6+Ml&}7@)J%EQ3wflXXsX-{2&lUzCCcve&z5)W7uy zMJm4=%kj)dv3a+X#}%9Rxu$K56d-W70f!fk!>iNa@XT1ka+(H2#P_GFp%e`0j!`P# zT_;y9GazeE3nS}bD}x>90x-g`v$a-#X8yJ!46K>#bdi36dMP5RAEf<5X>cH5JmA-@ zF7~HE@s&e-ACXKSu9!*4LPo&r9puy{WxNhDuMs*a*5{-aw<9NSCyqwS$K!Ltk zK!*Yeh*j)2AbEUAp81{*nU901{!JP!Sq*8P6s@uR0NTz0TPniZ<>lC+p?E`~&zqo;=7KZIRk$Mlv)YkM2e0P%XzXU`xp9F9t;+-R(;qSbBppV(=siT462CgnP z;la?VD=5L~#r|<7j5OeZWG)-#?RFvk=V6q^8k$HvgZoyc5q1nzxmVPK;2K6DLH zk+J;AkbTG$v=5o=*@*i8qJarCT@E&l=qv0c)uxx_scE1x@_^OK#MI; z^w6TzM!Pn2N0Zu^7HDI0Pi=HHs=eCaQ6b^Vo6^N^)ka-XqrTzNrewNoQ^JOdD6cRD z?@I>Um);JvV|+s0mrQhDG6eP|e07$@{oKXAL^;u`zbG5i^QWba>8aCQZcGMcV@eJ5 z`>a>mn0f^^CR3Li)3LM4#QRjtBq;WS$ShhmNq7)M%kE-Q}UKx>cy>_RM5T2gnQEtd4m}qq3%s%=?J^oo8B6s zvXwu}RY|zKH=Sz?-J50w?M=%A^~Oni)7%d_?M+j0Z@LQhrp=AGXt8u}GQr-2L;m=Z zGGx$@KgaFK5ZIo!elBlM4t0C_BzSu=h3rs)h@2*nIq(j;`8?o(r55mDOrweiPYMzq z+zdF6tNt~5A6QPa(L$J|mJNp_D6a{W&zH+bKzY*iHG%>FK~8^7C&Q;W5BRW!_G5MN&IBEy3AE{*ZD9Er;onNRbkY0kz;8ydfuj}V}J^; z1uCGdL$-WH!9_P66J-@y?LqM39wtwGCG9^}a%DNxP3bS~L{GN01?@zu4ze6J zmSL!5S2ju7g9gzHU*rrvpJVeqL1 z**=5mHzP`+(CeIwaK)cvnBvXB?7ph|Vj*S>IS;9Mw2a zc>EDSL^~Uhi11+*a=Qo3WfAbz*GoKMCwF+z-hBvEc7sED*n{4wWyA5sDm{z=RLWwt zI*JJa{Y&1+^BPnV-ML2I?+Q|5s0IeR(&>U!6Z@I0Qf}-{rMx20$a1w259pNNNIK;g z1s>Q!>n~F(@9a{k{P48e+O>h!Qn59v1VL-SaO$NoUFb@!Q1p7 z7ija(Qk%YE1*rq5+6aeYt`~LL%Y0#*MB1I z;B}u|zJt^7wz!P~5-ksO`*FF%i7_-)fd@p*uOy(Hq+9sM6f}pOF83SSHA?f9g>7TUEhy1WLS(C0^7h3T8oS9NR$5%7_6@&&e9*&{Oi( ze2=VQp8P^t6T7To?pIT8k-Lm8@T}5;j){*77fzL!KIoXwG|98>X@)~ zM`4>H#OEKWGr&jB!(i*p;;#mO5 zc8dBLdM9^SI+kmNTH|&`=s<_jczn=cU}2`TTkpD{uI2M|x9;@|)e&KlNC!FxF29M*}Ey2y~`TRBN6nC#ZEfW_&_7it?tB5 zO~LH6OReadF5CR}Q?<=WJ+~>fGA>en7cZohmjkVIl>Y9KO$r%VkiIr0(Cj~?W_=@M zUT?ab*JD$+h0~_;RPvJ~n!-?t=0BfH7#=Fo{Jg4riKZNRX{bcgI66e4d9j{KG=HfN zm1y49waXIC%KFPBng#WWM3Y~yN;I(-5C?q!(3L-59TB1C+h zbNm~!eidC>qG4Fzw6lB|l{_zKC#3;fVmy+Or{{9C&2L|aHf>lq0>p1tW20|LtHogB zi6t+6qApc?34LH{AnoS$N87}%tI~~Fhc=CX>a*+^Wu9SHy{u#5*FYb)8#tDXke-*I;d_ zttG*uma2acTwRKv5=;JiLIuIaVgv!?ryD#CrcULS2Fe`?E|-vt4$)g~+)_jYfF{0P z+i0$Yfq_o+OeIzPQUX=~QY?uNn<-0tb_uK|AoqEjA)Q;~Bzu=o4-AnV?D2Q71BAM& z)^K5;=gXm0Uq*x(=wXg8{M|b1wxaVHiL1D4vFH_8G*;MGbuk*hhucwf1K+Q7>gbWi_ z@b#{UoXi)WN{iz5*F-X%SK+~rl~qVw+}1gs%)w6UFmf#QpRYu~w!ql%aa}zC2+(;T zdF7ZoSS<{82UhWoLMqWNZc}{h^<>cxz`cyyD}yism7xM0%$Lw6-ohZyEuv%T4j;CS zmGL2$h9@PH!g1;E5#^;f6#B=(2&WwnA|6A4c-oGt1I>9@BA%_3c8{a-C? zQD6usRs@LWDqPY&LZ7u(eTByOPNf*(i>BMz^zG6~?PWyt)-k$#Ak55vP+h)m;r1PX z#ce7~xEb$191B^$IMR4T9n)GCQG`=5NcIyRxExM(o#14=S;jB&y%yT7OrXzX<25do+2{JckTEc9P`f1M zk@tfN|8Pb7<%K~B+>i`2c9>71bi-TQ`P_4SuAk3s@o8+Owf%0m*2W#{3mheWe?X0j7KB#yn_L!#KLJVa+K=Qa4) zmETx?kU?J)zXpqbXlkm4&*;-lJK^Jeb-JJzsZt z8rnS{YNKmm%1xzM1s~LA)piqn4|>Q9_AU?U!&)=hWE@m~4~a0A<1616s+mfL;jGc0 z`6hdc&oP{3_55~mwg6e^)Fvzd1aB_5{`~IOL~*-fs7i;{G=%e zv6S^0cTLjj%;MzXwOp9*SVn0o61D~iTMvXiAZdxg`! zAIi_O^N~zR0_#8$nBXBdvoV?MHTWZz(!=c{VypqMoi8@{t_KY&Uif77*GLV%q428@ z{Q3~d-WOALa)pjQ>iBj0>JO3VGS+I!D%mjKbJB>+l_?rvj_5~;w`j?~S0Q)h>61ey z^6`f_LwsSp>06`{SXw?0of3JQDR^0g8gku7$|lrsU&y}Qd0iH!$?GB;-yZrPHd|D3rPCAxfEvg}Cst{qmG-hl`N8L?mh1j|V>z=u&P4&<*;qE6~M} z+Wji%Mm!WO9HizNB#52jkuC?~WB^1|PFUn3lM4Q?M5u9r5&e0wEJEcra>HHZiCV;C zv?@5LEw5jNhCxxnNuH|>krI@pa37GD;;Q*=J>Kgab*sD%UTds9psn`0G5DY^G_QB3 zJd<5jWq%&ieYNvrx~+CHBuhCdHMptRRR2BBFcvRLu7fqLONTYJ?Uy;Y`J}R-{mA)R z1yg1&qL`A-52d`(@}abzpX1t^r0++NOkx!>sZO~9Fk)M+3jfz@5sS!h=%mk1VFDI=EBLGo&a#dhh=g5hMqO^TY!IJ))oTuAIp!&eVtUA4PNE zY89rPbdSP-x`Xdkx2bp^K*Yk~+utrCA1aW$9Eqg@uXidC)WDOP%|-IzK-Ue>0t-S4 zAgdE>2G=GJyFw_AEytUHqJ|Ckh+3}D)9@1yC?{$-qm$TRBiC3BIWaR$X3t^?9;8ty=Nqs|!}i^zXZ!pUZ-Q`cO6+d;*#3dcvL2ipP-nVbzta;#_uY%t>Y!Zr%loc$=+p>{vsfo4W78w0-eth= zNB{wv+!fy@cDlv{d^K+_^$)Df;a?;_iDIfvR~*LMbZ(8^?jV29K*`i3`1$vnA(MZm z1^$*FU1eISD;JkWLFdtRO~=uBt{Npn9hD3{Uk~NW8=;`URDB&0R=qYeQM$ZIHzc6l^-di_NW1@((^B)CxzM1Z=2};4}yqlS=gb`{y9{{GKpl*=(p+`aagv z7|YRdzutyH;@CK`e|(H|cO{IL61QPL z;{*($R$qFtH`mX5yJ)Fe8t2W_l%_^;CZWn_Fut?jxQm~4&$gnls`T=cT%9zuq$=~# zu$q3KG@)zY`E*?=+h1_%eTLHUQCx&rj{V+dIkKchcm@X=O@YG=6=+7&Kn=#rE&7^l zs8aB%X#|W7_@BmDb^=k>iMIaetDd3d_d-h2G(C3XO^Y&?y};lplAA$jPzWr#l|sNm zSAt8lNZrK?Hoe!7;)^UziQ+Vq(=Am?egTAF5q4g?vuFI z9k|CCq&@C5HH0S3k+wM}iQk3vkG$PQ8Rv7L5CGG1I>^<#x|F#5mGVg6njNgib>>?y zWp*iVIj`+raJuW-ZUv`Xd#EDRJyWd`E zfvd%C_vg1Kyir6VdT4+a_(qkTVha~fMYG8+>W?j4;ENmZJqShU97tBbuY&3HJgRNu zrABk5#cCK_d7+;Df;{|FEA<4=qxR0+cCr8x&ZmPM^`M7^scGL%KC2O$VQ1B%yqPx#cDICB3)N8lw5cZCp--2H}hU?hzdLKE2_da;SI8iAt9>1wSn9M zOIU3_NsjzL7s|5{7pgOUc0M^9jVPVT@Y5`SDdQqk|m4uOUD@yu^v z&=uKO{&D~lZ=jgCmcb&%vPa<)U%IZ*A`&sQsUyeKrhG?boVyM<{?&Nyw9Ej|4AeN} z!y11Js&NEMao3|aM;LkfT@}TCo*Rl{TTe?UHc&*LMQAaU{WXw!07f-=z$Z<3$Oak7 zy2Gzs(>mkV6CGXQS5ZhReZgqlPS3WYe_}&yU-IP+bq#KXA^XBNVlF-O-GJY!h2DEH zp?@X(bqZD9u}6H7sVQuvr&4dJS@MgeCT~ehU4Fj*q~Rp4+BLOz5+cLmd*eydFoxX!lJo-V_>-J@+rzJ1xhjRM_0jW<1QF=c4)CMn1QW=MMUM@mwR%)$zFp-Mz@_S#6Y{28!HP^0X6geD5HSBi-aQWdekt z$h|vSslkBfHuKyjv40|7%`2|+u6%#1V03Gp4)4s$JMF>|G_+E7FB@9Lu~Bxhe}XJg zr^8^xo5>hxTCmpfbILb)tqpd;dQfD`x03TsvP;)-yzUr_0~bjWtUPbsf+;_NNJw2? z9bRj_oorn|XDNF>aZ!@z(}`Ry1F(-0to4>^U<~-C=?e zS2$>xCLaqVUx6g}`8LTHe4hgcvEVO~+dFr)*SgLw&yPlEz-yU}^d2Q%%jB&)o91Zn z-nHJX*^M4pF7o_`)F-Qj=U%a#ZT^!WhO~0t1!Z-s6 zpXZ^z6Y7#bVM94W*50sv0?haD(hOV*Ct;;J4y=URo0AN9Mh5BfEcN)`-h=6v2KxLQNdh zVS;@9Z}h^_aS?dP0W$cpf1?W*+AMXQ#q*S{(tHp9r?n@$WZq0V&txaev+_TR)}U9k zh9&=2XIPgb2{8GjsIgUaC(I73w!1atoP)~2983?AA&tDf1y{t6&e7B}V2Yf6!$Z`_ zO;V-bF0y$GYF=@jM_%$D+*$iUDLko$E~?fCB)IP85$6ojFXSCI?TTRk)}V~^P8kNf z&qmzXo=}-uqzRG(KH<;j_U0!V>2_uI6O zO;GYTC?8xYdF(5?WUv)M%o_^aNiFNtAntA`#C>Jv1`J_?3()kG_d%o$bRj-$oQqm`%R> zQW@S~p6w)ub;gs7SyawV$N^y2ZRsq!8v>$x(OW9WPdFva&*RZ@@W~yWMfa;D(LG|V zu|h9K_zX_tu1Gt%cC1M@ab=#oleXLlN1#lP69-!nXxz>eplTxc zJyyMzl*%Ps;ge(D=c3YGSv$P-EitD`HJC1*Lv?#;nVsL=XTZy~K;6By9P z&qxc{-TLY7qVwK?^ESP8nU!PDztkBg%#>Cwh3q>GLb{XOK3(Qis%?Y3i8>Ttil@G~GLONqWDKgZ06MO=3U8h{7Dm`83-Cb3h`%{OU>oygp z&jF~k{)^f+ypx+JG)FSbvN-au%_>l$XGq)BI}BcEBTxK``iR5kw83xRR%G#)nB6*; zIIbOjko{4LXj3Hg#%w;T!E(4bf{(Ar@SWu+(4^us5%RRfV;u;B%QlR~QoR@pthrOdEDm&5JYE1L<_dZpVp5>Y-{ms)9WbC(DE{tS9tPSfTBjWJ z@UOvMYi+)bL8>JHG-7mXq(cbHS#}UGmZC`D2^DcZ0~U3VpPoeY6KVq?KD^dAd&m*t zlLK%!iv5+T#E}7c0y13HIS4DkdJQ- z`z;+gjByH%aVFtsHsCZ`U>c|(#KMrIu4G=I#88<@gAn69>7`eRRv(tgQSq`?3n?R( zO3mn|l+qNX-O<0+6fM;&wA5v=vAx$S{yJo$J1?Mun9Yk zf*`2C;eJ>@PAw2`4>8h1iGOt}AwyBl-5VQBK$rFZYNu>iWk@|36LeEjNo)yCRAuD% zV!9EYPljY5i&0JZ60Hau+kzKQDxm;RDpJ^E5lSZ3&?4QVz)P>HD9}DtLV?1bP=Kzz zj0t4!f2j5@p+FQx0nyr^V8QKc5eqJ7-Z!r889Sn4JD>Xmuie7_J+7U%rZoh2E=P^U zgYj}sTz3r^`ggVXPuI1Imn}Zuuj|F1(XX4uXFFwt&TMJFPHf$!tk>B$sEmxOAy^Ia zBIHj98eOYln3@Q5!D)1ol&@%}dB3H~SiY9p6tCMu?R6<4Z#|CoI_^Hap76BlCcmvWs zs04!bRBHG^&$gnaQMKVK^S2eD+xkXK*nq!adc^TE-Xs)hE=^3b9!I2JwX8q6`DHX@ zIZ5Gq)G=UH>wPvipl)MQZCH&RB!4VVp0B4|8v)2=kua#^NnqTJlRQ-_M>E+#e)t<& zy?V3HlCS@UZ7LeUH4Ivl8%V@@v^dt0j_0T~w}zy_6soiFzb1SjM&RmUUNtYHzAz)p z&IIdkm7Vm4d4gLrMGG31Nq`?IgDx z4fFgb6d>ruUWiKzEgn-^WMvcoS*w=G&zr5X&7c?3oi5>``vw{hHvEVa}?xxDMPtv;2|Uj)+@)?AJ~>tI81)& zpvILPV>#Y|b-2jP5*c~EKz@9dn?TmR&??zpk=vrd#Wy=OZxm;A_aUz%V(_+6K!{%p z%SQ2>-UVHbmZnoN3Gb1c*mxJo-UWn>a56y?cxZ^!)W2egFGg}H`Q~$lp7tsjI*9HH zaYB}!*9%-D8UA9c2%Ox=C;LpWvuMc}oNcv`)qt@#NE!V4m3u#}?odCuBQL0X%xD;@ zd%&#RYP1^{A{Dx|iUiRS>>)v2P%&skp|}cQ&PXSZnH>QiRCj#gx(8tW-h5G7KRn^X zLp!~EXvf8pSuZHwx!Whg-iD#v_&Au+O%tTWHISQLpzDffE|88>$91K|Q#t|vGF&kw z8h2kO*>p+)UOqtgOUQj0i=@qA`vPC_M*6Xvd?6 z!H*&ZO+HRB75(Wc1!42+l;+uC(zMI-~TiccG0bRk!9n;aG$rqopv?0D6 zr*8F+)LQG21CqSI((BW%zfxW$Uvn`O-c}BK4c(lk^~TL9*5i+X5vYCG5({;R&pRT8uw|bYNh%wm!asWOt0fJPD@4rIvDc^SZgS$XI8|fk^Zi2R9W&ZWo z*310^_IAR%MGpk~F9Y(p)9o~d6rfB_jx0&IO^8UlG8w#(PMGoN{Ik-XljSXg*vEy#80Vz4nXkU8%xNOZ%y$+R4QTk+BgWEvzBCvEln zjxO_PqldcAqiz0MHGUcjXFUWUz4V+ULDI`121|!z7mg#ZKdXXMf2$<1qx(~k1KcSd zuU@vA+o%VeayjH0gw_9vUwm9(M9Xf`N`CQNBR9-N9(o2Zhp0N7i>@r*4O#h2m!68# zo|Dlp+#Fcg?(i@3A$472J1y;wkWC^apDx66-RINADC(4NQoZzO^EusS)7Hm#n@t-Z z+GQzubL(cq_DD+9^NKJ*|Fsx7oDzZ6VoRnBdU*+@B;qqpta* zX|fGY3cu9|T1qUayU;R^hPJM(Pd_?>tWSEN=^Efxy|JL1A$2Qc?REcULnQb0Kza98 zwsuLfG}G28Gnn+Q>!u>j>>ZLBfQO>nr&`gZ-v4P897`<{k1LXm(B=EtE9e!wGk;dg zOqa_zI@zItQmAnyt7M2Ax%vzk?O5O{ZVGI{zb zltYGbIb`iq=t$tR3vn*F;EPhhnn|~a43@rvhJqbP=E`+O5GU4&^Tor227^M3g-j%a zj)l6^{Vi21-94EdfJ!H;=0!@?ab1I#V$y(BzX80oWOGSZ;O*85_}rgVh@Vf95Nm;A z1?-X*L(G6Vf9uJ_G;vwiCZ?oioy~y3WO_#kndUZGL^(E7Cswy*XO~n-8PU_7@^i z%#7fh=J$JXru3#czS!NupBZzv&V7*s9}|$!sYWDDK#viF3Xl0G<_Umzi=nqK7tO+FJMXoTJbT$ozrXGXD26~pr{UD z>KA0i?>`2u_~^jA&r-u%>(GI@ZWeW5&UZPz<*0(%$;3^P1M~a%v$lL8;*sjW%+(87 zTd4!H7d@Lj#q}CYpgzprmAJ3UZp@`3caY56*h*FDI&v3$3-iA>3NXh(vNpC#Zp>QH z$>@F`i=NpqjVq`dbDhghmb@z2m1>OTw?eZH7y7I22qdCo4|GA3Hw2RVA)yT5C6nku zCw6`4l~z%4V=lAHvjlLsHBQJs4n@TNan$Q}N7FPkwxLm5c4I#F5;c4SI&89h!Hsm1 zbE{DTJWiVF=RVT7ns!Y#RvKXjT}hRNnOdOdX<%s+?baDHNlVJ=*asIsO%sc zBla~K!gc81dR*-5omOaT}?9C25SMW}n*a4K$DRQRE zLCRiHfpeZZF4vKdpn`+9K1cS_6A2;fxv+f#%#b=R*Fhb-RL6;x_CO^E;Wx@aT-+rP z9V!TMUWZF@Ui(WWH|67Gcd0b9?Q+UiVFGnr{zT*|#81e5Yh>67))!K_9c+IgtBg7> z@8$KriGua%CNx9y$s)Ixti7fUosxGVeE`D&N=b!p)IE7K8GE&q8I*JtCWY$td&#Gd zNglktD`fjz0)vqh6Bv9yx>4qv$({IU!fI;oVjczF*Rtj^f8M`8rZP9?2F0Iu(~cm2 z-o%mA=*G2^KkbnGJHISz3-RY&A*auLnqHwK3wFp}ny1RB1m4*`@#Y$Iqnik&WFNmX zG+tKEb~3X1a(~_@sPNRqA@ft6G8FgRhr6p#(oKbeuG6I>&GJuz#jGGdJeqr7r(C5f zJ?gx@yQ(yIl#=UCC`>7bg}eDN$?Sd#@2ccBy0xTXM4NOD`VG?UC@AFcS{v=;X@Gt#kmV56LoHn_ERGK{iEdPaI2UvEjGcP0z?2w;2-PV$eJCE~6&md}Ru zb&;Q6q`Y9V!V88VFSvs80$O2NpaODG9K%2gG03|skk?1V+X}C~h|!j9ycJLRc9#9t z)5}xj84r5ghn!xXQcf>dcu*mBlGj(tr-$26=Q=WvpXFUhcco{aRg(nHL3WB_MCn7;^ZTA`9FsUXuxGFD25n5iq}L(Ju6 zRFFIllZq~*9AE~M#n>tJcYl64w7jSD%gi!*ewkRNoL}n80_T^R()s1zs78n9mvg-~ zkd`w$JQwG%eLNROv%TDNate4oh&c2VQ|D%VTK7^pN!XWk=*UIk6L&(=kvr|=$wJ(v zZ!M;fp{JR#Qv1&-ry2Gb>HA1gK>=)YjbieXP5LAI+EHzt~>(b z@bZBq?NIb|bLmOxEVDV=PX4)48C5JszfC#~OSS1mWmwyk zkm8Szl#VjV8lcIIk19u*d8-3QnVG}df{!vM(yXR%>BuYj(cq&@8Pt)xGb!}*3R2~> z%&d;!v&;(}%2_7Q(6h{KFSLf7Wu}w8t2&=$-kqA4%=L9^-F-;yUnl|Cl7S~Q2+NY? z7j&FS-wcgVPBfo>K_)e>s=E`-?9xsrn)$1PPc;9% zT0YTK@U!zd!@)J7=b4&Pb%V;krrYz(`$DY2U7u%8?5s0L50?5Or{Low5$T}%{xSuW zf~2ZUxgE$gA9Y&xu#p&0=jY}9$kJ7cmUZ-Ms$~ro_NACuqhlHywKmsOaYaT-40m?6 zw$6Cqb>TBJM!!s`D+Il-#H*u4L|%J&C#!`NSJq0_$hsyt?VfGjWF|g`BAPn zFgn2==L;{ZWaDmIt2MsUVm>Jx8h&`LDhYa4He~Q733vuR>o|;By8lwF zV#SmZR44ljLv5hnz$)re_3Y;k7fkYZ%+P=XGyU&OkT(TQ4jm}af8x4gb$+lO7*yOT zw6MKAHQEBtEev-7urcY3aeGVwAKS-Mr7eIlol&u8rXCTq4owxjL5nIS%=3((#+ru8 zP?ZM0-X4*($HnzeIs+}ReQ3=37~^(bpQJtP5BYiJl`F!jrK$uYW(PIDr$C7E?4j-c z;`v^S_pqJpl>l|)_C8{kses2^9Blc1HX3ueZ_WE5HGtrhn4+f8aOueZp{1WY(Gxxy zB2=Z$G_hAWvC-5R)>r_26!2qUppecr{ceJ3ctjzXTZbzIgAHGS6w_}yYbp?OVfb+{ zqG_Orb4^G#dSCQ(I!H|U1t{kq$mcY^x0SwyD&dH;Ko~<;;yi$)NoP5-G|pw*eq(`< zTELITAp<~~w3Z`FzRwRDwNT}YDiCJEoMN!DCzat^g60I^OM+Zjn?%eLs@Q(F;}|&@ zW3ps`Vz?j7m9YG=Fe0K+r#E+)HYgUjkY$3V1_OMJ+v|iqRbSC(AfxvjX*&Bxzx@S( z@w?qZY1OR!yHG2{mzT{3@h!f*6h17U6;BmxvEd6Np^LiJUxhJD zDR1?!2*2HPp#3&3$~|xW>37qw+6^|4DYw(^gkxglmsgWJlCwL_(H+{RI)z^qAJGCL?M* zOj}Ni#xz4sM(^=L1SI9VqcF7(rW$K9Oi(+PCeQO!_LHl`U}j&;$h*+t$`kgiztDl{ zFz0s60%Tj!KVS8A&CYh>OEO~rk!TeH4px~IUNLi9(cs>X!(a3HFst8LXu5`B`W*uK zY4Sf?#lWjEBSVYAthpg5sE zXx+zK5AxQ%o(`Mw@wYS>`X9q(7@x$qmb?=IN285vo`vr)nt{eOUivN0VqD{)-{Rrh z3i{2YF+RQs>*!uYvJo=d1tzI#H`0T}YAUu0H6xI8R)lAI_O++Oj#(3+9PeXDUF(I@ z66K6UYR;|mv}ujy@lxYJJbmF?Se8j{2e$LjrgAP^grGABB;B|C9R|km6|aXIA`nzGkU(v&h!Vaj8ue*z)6=gr1@PCqSXEeUSl#_c-}O{ zSn=K|p5g?JcV}ej>X|F?N&A_nKtf>$K`L-LPw_s6jiCti%g1y$Y*<+FL7~<+NYN<9 z$7}K6OF_S2wWXC+@p^T0C1eKqbmAefS>)eGzGS(;4J9X8g{rVcp+<4q+(sKAA07eF z$$=h*?Uy0*JxcC0(cw-*;1}?=2ygi$EG5(Zi}=GOypNX^3mIGWaF>J3eigJ;YDlw$ zR{-~CUEXrLRA&^_L5G)XJbo>AMS-XIdxq_cVY697ZFs4ya{El45%`_)MS6PU7g3Cp} zL)+k;vFmr2W^0%W;_j(HrMxYEL1pQi9b(u%PjNfLh3}@cYl}A@y)AXJt~gq#zb!TX z*_Ev@=-YXxDM|M%trqZeS9B)E9Ll3VtYv1`Z*(+u>>(0HBgWfsy!u>7|!TUBsm;)sGU@fk9^uD z=S-K(qULkWUs<$VPDuZ9WHGfkKqni=skPV z;2SLgxDl6+1@!a>@6w1Ba!RifeN`g z$)7HTAWOrgu90PI&~9ly=@KsTxd&mFw6tOT-$kk@&~KpR^!GR}!1P#e#(PfKk34?c z@&|>c47*AgWpgEi{A>xg;*+Qp05hyB0PRg ziRLzMhPYtAvWe{RmT9hf0Y*RF;2o<+i)i)A5ta{EUTZm9Jc1-F3gZ5G662mt+8;#5 zZTYZR2ZcZpy?=k4wTb7l*H80qh{b6-BKHmQhU&Gcdr~T zSAX`^rcz};05#36A)Arf0*afPkx02Kt7M3X+n-E(069yJaE8~DH)1JoS_WCYK+s*< zizH+2Fkgd1dT6VEGHja0?bhb?Ae>xiGj7lN(H$jJgqUTSSF3QWkad$Xg3lQNrB|mAO<= zC7$6{{coAm!wUm*`lKJ7)55@<<}B=TPUk?o0UiNUIsbODJIc5{*AEjK4-*=X6Z!)) z`R4vkGnv@sOtQ0slozWXD!ZBRfRu0qr36r0@D51%(UrzkkrP%O6<008YjCJJ;1i&% zme}SWN=G>2?ad^(U)vdvq>KqitjV(m@F8mOS0sje14Kfr(r(^ zO}(Gjy2DO3QA*j7!&|p{i?@0w>+Dnx&-Kd}7OG_&f)n2jTNPeC~r!(3u@hVO%;a7yN4VS}W}okxt+q`7*NTE7KqkT_G?} zJcj8NRMy|;vXkQ%nmzc>@ca{SLgxWPI5RBxDy~(iu~Lzjjnyy zf?zH#+5Ga!O$!3LsbtQ+Q7uQ-nPk6CH{(t()RyA04;2H*kdx@83TVZh#yDuy!mYzK z@aN=sM3L)7e1#x$CHumFNHOzg91`Y4k|Txm7D#P=U`y;mDSo3+9Y!xxKq-E#kSfJR zI%ZWY?0_lE(%z(0E$XY6SZFb75D4yl^yiO1pmMxR?kv4WZBZh8YO6o0+G?Y!t$wCzt9Ac7+A2@A)qPZ3EmpPFrT-&sb=F>aYdiFwEGZ7u z$hs+~$d2JOrP5BGP|BR`q^*LkgHnW|s|IPQAXs=OMgFv2sOM+a2{r3I#OC=7m7+&> zwuxc&fb0GC@YXuO{q^!Mv*>ci*&XCAC?M>^*LFTz?+V-JPV4uXBb!W;Q{zHXZ@|>7 zc)>4NDc_yS-<_BFB^E6MrTwX_SHjvi`k??scm%0}n;4s2? z(|q6)!sOWXfUJA?tU6ECd%X3yr%rT&{JG|4$XsGreNm4rWzDs;s4C6d*1HX;vK&~i zvDWd^O))5=7WeT~89dfH5fVHzj{~+N%|NTrB({kD6@eB%K@?eT5_H>;=Q!LtkvqO# zg@1YiCt{<@Vd6J_RxrU~5@j!<{)^U(UkY_kNM;H)7qNTjvo(@fk z-ecXX@yy)E2po<=mh#LNaghGB9gKj`6KYY`zj@2|4y%Ur=66&BmJ5rSa;$wp*{fXM43l;wY=}lK+(U3 zivAoG{VO{c{S4^aH&AjPJfTPNzfRh_{;RZM!nK&GO8-6+dG>CQvJS`yC+tyGv4`#s z9NbaO;I_*&gXEqy11^?~zgtD+=k)X)r|BW0vfnc;0ca4uo#$;jl%0Vv;p1nZW%&3p zled{rOSm2}hquMT_ZBB_izBz66-D^bnj=fd)pLW);g^pj-0hm>%XN}|P;Cd5l)`MW zIVdg_2sTrJEHK#&lE7rsO9GQkCkaduC@+EBWRN5#9RDOL1%VjS-)?%n2=zdDAmf7v zGOr79b*m(^MSBpA}s~R<%kD?w>nyb1EDz*=CbS0nRD{;es?hp^Dzt7TgD$mRF0{Uy z*Okz^IsZDmj22G5RJE(xyEIBRUeJHj*)fT(*m`ge}9Eqof!=+Qhcsw;6D4iOb@Tf4F zo*KfYU?~|u{-!qGp`(psL|?&aaNuF$+%66i(=R_v)L(v>XaqzL942xD@rj?lc!;>l zKo1bH`v~YhLG6B()V&YjEFCABj}{nryWz?!4R=0|a(*L~t8)*>7-finV^%q-% zr~b^Wo~Q1arB3}zQKL>hZ`S2@f>~-&#ZE9&>0PoD#I*<83A!^i#u4MJfT?jIbP6mH zgJ@HB->ZO+SlWv;yQsbKWDhpf(X&u{!-GIXGn+}Maz|04Dm9~0M2w(#=+p|ekdjZ6 zY;u|&O^_F7LmK^!@{qHSD%#|`I$0q{{%(8fb*{i9*L2_{eI>y&jFM*fMQ4kud;DE6 z6qpw%Ye(U)3CLZV@_7f$&L29lD%4^fIi03Pr`z4!JkCyY8`JgNB+1}jr-~m{mo(i- z1|PT6yD}k?!8l*AWDxHRkqn0aqDlr@XJ>Pk)baN-)s8c}?RZWX9pC>;aK{^GhIIV; zIkjVc=4Ey?yjkYWY9Y_hK>4!8vtu)KuPGTo9|;Mg+LWH0GSK*TzP>`~K89YJG;Y^r zj==2F;&z6O6teuDyBA+lsBXf}ObmPz3lt~x-g3t_SY=%XEshhScN0V@d`hQL#DOi^GOTD^mb>Qzq?}5LL-s7^n0Br(Rc(_;BpiTHapiSr!w5yBtxa#}Z zS@aEpevI!_X4}c%9#Agu*iweD`pLGn5#y`5hTvMr=Vo^TSwwErqK7Vu5}49Dt&%)2 z%ULdtI(#Z9~62Wt7Np_;gASzj_fQ06%(xg9riTEckIPB+-qa?- z*m81wOB@c@Wz%k%-rcmP^0{~-H)d@_StWaoI{lXy%F~aYXgR&KpADa7XS1b+hXP~$N$V(?`!mE%7=y)^(Di38skl0>=UjNl}dQ{56$kaCWskb;qN zC;dZG6&e?(1*a-B+7z5B(aecyT{T)cO@a*9tKf2MR_FJ8Po+YnM(J%;SXo$kZ`LXD zP*$smw}BCX9Vp45VAx_O|9TNG3O2)cr=1+9k=RC_w>F1yNglt3i%H2PxUP-3uI{i| zzR_qej}J-J`3O|daNomUEq5q5k4iGqiNW@)fGnddqAg?*dmR41rv?q*tqF|(hZlmz zzn#7n`+qY23sWy2zcpn1YI2oy^)*1CZU)Kgr#o;8E%uO`@n6vq$9Bao%lDV)getV+ zc`CI|OWo3bq;x7uqNt9XK1=1)3EtZ}Ec;f0bm_9+iR~?lK}$d0pE}R>BKD~$IQdQp zSte-Z*6WQ=SMnM(3}+d5O~8HJ)0`ZvZX`H_+;}IRsq@wbJa{JhsfrqOpVaS66rGoS zhaTc5*#`6_a}U5fk7(PK=0^tH1_~1wC)jl`Kd<4&^7AFV%a4}yvK%T7e-}BC$TbKv z4Gx#!Ck9bbgd||Ey(35!3K?4*j4jp)<07+fmu(XFc}g-F*X~?2^!IU*qG{B@EfWSc zRall8JTB>aDM0*Pe9bp06GxVsWm*}=XTf?jd~VfaY~Sqc&*Kw;_kBt4F~y_24dQl9 zK8%hI?uXmE8yo^Ijv}s59FMR&53oCUy8^qUpF?05!2pBo8g`$hYZ(bVE~oGz0LX{N zyDWpJs>8E4!SZQ|R`|61WKp!&U@lE%*svOCSb^QN!0?>prYSOzriB8@k);C33_#j} z@JO{HB&-05IDmvS5(O50#86lSFek_(nxYKi`f(Bl{Q~BUAcv+Xv%mg~2E>vE##r_v zN($CfNMKE0!FN2HEyiU<_U{$}Q7N^5&HM#6$TPBj3$=BD-OjFV(Po$(r-6f~2H43$5``I{Q z=5gVvo!D{TwSC7QU?bDl>dW?XzpT}#!-w8?P3gt?O5!=8F0(WxnQdJgm3~_Vq_Dkw z=g6r{q&b`^vJpB<8n;!bL6vSxrn)*XbbvF3FkXs_Rh~MRfoxT7(B(G00+r+{6osG8 z4BA0F&2cm$brvDs$*maE!#hzk0mCgUHwsoF31%Q@&GK=uCs!5gK_4S_*kVVZf2N)E zo-c37eb-v`Zf%9tUy*yU#@fo{_@d1FD{}oc){9IAv;^Df;#k=K(dUx-^msANPCnm= z>@EQ<8!6UvIO!INclz*Tq-bZkDy6>x;ba1B)A|UD*%l^(t z53xPZ(A$y5vOm#%oJulfHi^|5--)o3QS(|+2!vtfuy+U*DBO51YDz|@W*Jsq=Wt|) zN;T|2C@7R}mSvg@nJ4>FI!id$;6Bu;?#bXnurVX^zD$;jES!7qwj!Tao#oI@vy26T zz$0|aX|`W#v5AW`S2m@?dM*p6FJt2MJIe)p|I4Pzadkfcz38cs5bMoqCLSw_a?O_0 z#xkU$)S_4}NvOq`GmjBOhj$Fg1$=aAS~4T~y{3DrwHB+%=sCck*$nsH2{@;KtosR` z%D;giriV)&|HIr=!D`}ZguH9lMu=H@kn|Fub4k*B=N=iYBB+p;V57!@PoU)_yx-UC zaKh{nrD)UKO!A6VktNNQDONvB)Hepl-OEYXf{S8~??#s^RPwgea#xZE)M}-8dJn)q zr>sz)OgSm*94=uXuvc!=Q_2x~SBs+T%@1@02L`}(LD16|c|g{V#GAvBz6AJKK7f2X zNnu?eN}!ocieQPi?4|qsT7Ia=#pBw1Hxbu{dX|BtYmzRt_ad**7JPPk3mWemlOO1F zw)AzmA(pc&UHu5MauQx+PRaGN_sSFV-IWg6>Db^{PjQ^cCQ2pmoTNw#Z~HW~h5{Yf zg|3FKYk`t+nEMO(skRO}$jFdl!KPq@Oz6u6(#y?p0qQjGht1q_Mjol#{-)?cb93e9_ zmW{X)pB>)LW`xN%iJRC4eGo(9IaRh>N80boW<#=AUCc^Zkq`t6g+B zE3DCO8b4SA%>A4ej$q>5C2E5UbH=9;liHG8Txy@`qB&-cY9KPdV*|s9&UQWu~ zyjktp=JVw9OY&uJL*Pmq))1_*iaTdNTDuNiX*fNlL0_zzSC^DbN7K!)r z_5`Z|_CcrlJPjzDlc6%k?L<<7w^2DG&I#KjhFOAX%}s9=Q@L0SU=(}24C4~%*^E@q zv~0P+(Jd0-Krglf%wx$FmsB9EoW|ZMTK!?U?fCGTm}O$EzJshyO6pwE%NfY$?P}fP zPvpH&+9Q!mUFpg4i?C6gggNkWA=!ZQv-4D*g>i`_u}*e(4f7a9`YaysBF+TD$q;mK zUEYu6q$|v5$9GiZoF_djQ5?&#Uj?xqU`P_Dk)q2|PSaPV1kyJhIgRg{bVwRx-VgHO z(mn-zpJjcB|5hrBy-4{6j0E_GbQxIY1i_B~hz+VDcThM>zPklwLu7~`^bH5;O1m+!YxC{Lq97@P zT4>~?ON#rT+ejfp7gobZ3Yvd{kj=2YKmGCZ+*6Q|$q)5S+O(QAmfmvx=iv=7e(uUo z8+=#%6B!Yk_<4ru(;q*r{QUdV4WEB{e($7*O;C;ebPEn112p*bpVvbfNGvU`XVfWt zZat++=38&MT!s9wqq7Prb5|4y(`fiMMGi!BMo{L^kW3K7K?>A+v-XlT6EFgd2}7!# zw0wxZ)0S$G#3$XN!ujvtAe`%PkOw76#eB4Bx)$ZASQhX!R=cte{wA(Euof?V?{)FH z&3tZ4MV9C-(s*`BOA&_>xbYpUh-$Mi&QTIauwR?9|3}-k$2V2w`I{zfFZ6Lk8%hBw zuw23l$#J_5tIRxRpcdvD8*7QwWdG{*VJx}RMgXPh0^l^JJrKcg1W zQ5!q9R6zk<#RZ|`bo9jAkuYRRfY|KsckUzirfGqh@ele5_q>0P^ZWkJ`JHnX@nb+S z@fvS&S$pP+t$fakTKb8_PcwZk@Z2oIC74qvpczanYK83fGJ~fFKr9|m0`+@py*~=5 zp+4@bI!x{@2W0N<^ZyL{=5rnOIs1K8q}>(gI)c#EYyAg2Uz&K9d;6s4+eP98y714J z2Ub73Vj}l;u-WJ##epro4#CpsKrHJjWF0QjP|?gH(sK4Ul=+?gcISYQ51)lxe0sgF z!P=be5RNuo2Dwf2q`m|6FYb!&VujUsr?{EVKxf<)^*IN8&Tt5?H*}DwFO`g6qKF973H&1K;bsu1w z$H6SR(k_G>?9KQg-n5uFR-BCwItvYw-11{Ru7SA66^@Gq@HLGeE3` ztCyThI9s$}pU(5jM$apMIrY4DoWseY@g~2Ec=o6>7Adb!t0e0%R1>~~isblrC{7tL z3`ooQci^c+uESIqdV#_otrh4B{hZE%M41H_r*z~>YLXnenAiPXw{r*_x$&fQxr(mA zr;Bwvzf*lWQ=lqZZ(Lu2ww1oV2yPzMOkR8v9lMzbD>h5G1ynoAkKsC8XM%lfR&Z3m zP9o>CGCBJca=tsHS-hrb0)TZLzKhx#KpS06DLO!cHlV05;4ih{D?Gw@skS<-&Qu4Q z$nEYFEA}9{>+CMJu~=H!{gIvedH_$@oB1XF$7QM-#98$Q93TE=rBRlXFHd5WRH2le zM@$ow(68(-XpygU1F**F#;d!it5s;jyN4u%4`OexwFnF9Kxg(KSLz(W=Cyto>oovE z&*;IZW*ra*C;2Aqmn28H<_yk7y#fA@_--jWIbEpGF}z)ff6td82FU|9g_ZEh=*f+f zyKRa~p!7WvzhTxcP-UZ@Q;UpHy`|6*6lT}~z~qxzHU%=Y9jB0Cb_I^l8L3YfGb`i^ zg#$vEd?!H#z-%xdgne16b^COgYTY2eSps;Vd)c_2;ps~+NArZoHE)l{^>_d1@PhnK z7xd0z?ez)$c=2(|KImv6d3c#>!TOF#ueS85xzlJBO@n|eiy@u2-q}OK!Ov|=!jWhfd zgAj({%*E@n`?=OTfcD~}Cw~U82TB;C+xAyTq|Df>~|fej?Rq=;&g{96^FT! zqE9ODGX6~X!jsS!&L{tOg(6G#FQ_clc#9BOe3c4%o=rHp9LRH;w}?D_1;w4y(5tmz zfh_pgb_Io;zx3Xg5tlqwM4X93SQ6E6nfSo^mHlE^CJp9d#kzK)*1vxu2|p!fQsDiWq|DJ*=q7{R)t`7o}mQ_l!V{& z1vwWz2grHpF(t5Ctw-pOncR;L!H;_Ltx$GBHF^3_WU5^;N|rVungagPJi3YDQJ48Iz!96rtvU#mP`J8b`yBfSOT&nlXf$QGl8l zfCv0Y^Vt-XiN{4%l!>CC6CO;jiiwkyVuiy*E3tevDh9QVhCvH10)u`t8V233OLT{t z+T>+@?V{tRwzK+0N7JH+f2^d#8lj&AJ zwF@s)lgV=M0#&12tSjEz<=Lw_xlk!%vIPs#vxLci#~B1SO%2x0nmUl9K+90wi~eAc zuwEy~COC>?Hri`h!JGtju12_-EpH*zr(s-Jy`hpkb!B?jd2tX=e<##d)i> zb5#e?);h0nkM50@Sts{@a zvz28w3gdKZ%ht}`uHhpA#^7$qNA1dBKB)7w3^8;xMrAZfEYXd|c>d8`sU1zdfqq0* zIzQJ6jhq1DxIq8is1~rKN`L~f!@BQjYJoqdN`L|%=)W73cm^!<@(5{A?8)RLW1`sB z5z?TTTc2c9!Yr`Z>JieQ*sSEFnnba&BcwsG6Q3oG*({g&G*to=cw7J7q*CvBeFCc{ zJeDc}YT|X@6R}D!&PNLy0qL5zW2@=Ej0_`8)3Eg_sThqWC{&{f>u|C7lf8hNe>H+aES~fCDa5TK%FRz( zhbu&yP9X}Q=5`Q^nZ?%L6~)|>K@=u`a0J1>Cbi&inXMgw@DCLHqn;$ex5EgEh4V>$ z&H1H%1nd3$tnX_m}E0csZ5wB?>FU?YY zqwEjyP=~kzV>KP;ggmk{krv)>V8p5N#_4}wio=iCZ)Sys`hWupcQK-r|C;OQl%zk) z-(BkH;j?T-{Gn3E>E`|7vE)#Oy=rGE1f}PqbnyrZ{<~C5_stir16xMGe>?Q}4;6qt zk40cB$k5Rg7J(hbA}kn%53?>+Y2CUni4S8FX?N|@@S$j?%7+E#=ffBvKIOtE$c2x@ zzGN=EujfK_G8bef?3$rv!ae^$;kRs05`JWaL1IJ!P$W;8hRiSxndy^Esay<+*rmf} zXiBqhrsE8q4JEF_5N*D!{YG}mV9>{IGgPT1~!+!RU6aTc{>YAC{yEj7ZldqNL$#fp}Mu zX9lA^dsIMPny##s<-AYhM!Fapt!-(88oM(X5>PV?^n&a);tOv;$6riSBAhbIN#L*2 z`8Iy@_J<6NFb6{mkMJ2i2%H&m1J*F_W*@5~g+D0X0tCWXg&Ek+rPGyQk=9-VqtDtc zE^Oz#x$Wx=-n9m(InQSlbK5h$8HrYs7@|EvSST+U4myS)hdbMG4949tO&QksUOH|J z5W!vS^zKX=fMmgKmERT9FedvJR+s_gt*jyc^h3HZhC2^(T!^&$K;uGuLeziS=VmeB zD5Es)VuiobsH2%sCn-@|d~UBn+F|j^J1il5btteQj)6VL8;YcDl^EB-l#?Y9nY3)& z6|=X*8o%^+&3DFj8F(%Zigv2UiTC3$KtaFD+-~q@l&DI$91Y_}8Rh5? z{*vG*Yj?B2(VqEE7An3J2n+2OA-M~tsw~iXQzAEtq+cR!tqg)3Vq}TgaZGv_)NJhx ztx01T$1y$~{twjMU8E4Npqo;6UYbN*D92BB3`rz@Ld_L^4@mqJ^-Mwi`^j@tki-NP z1K*~QhnP*^MQEZE?KElai@l z6@2?it>C*+@Ldt46v}>%|1>aRwJcoFZBGkKf1JwH*=zWj;z^7*X(~`ZvTIN&EW)?+ zSydBMP}&^HV);^Vh|RpBG501z;DB;M1Y0Dp?y@Yq1`*^y**jvhf|b|G2H2j z8k!acc;iq~g_}}VNM#D|_3th*#zB(8l*I$oij>z<;YNlqQxt*Q_VMhm^)kRPtMDZLt0)5#WEof#*c$gAcMK^L z0nBf|{x&hc9RmvX;QYq5y^O4OXHy}l@SHO=EeSkWG?Y|!ORBOwH8U&}?k*{c>!%K6 z_`}kiPPoYm!|!`{_zbfVpVjf4Nqev?ur;~aGk;FiES+24nDjTyEyb1ABh@uNci88S z;BJH&rY`)G&9#Ps^kO#N;z=K3I0wwGnWXTrBS@M3p~A&Ja_2U+5a{w)W zc$)5s+1vwM>(A*7La1SV5`R0*AYuS6r7#`9wTo?TGhc$Q#bUTmIT@=AchR8J0>TYU z?r~zrn1lCPLXR`JP)+li%gM0;DKFQG`=#C@^2><{dxxeN#>YK^*N4YlQR%5V+Xe%_ z-tLa@=`egTPpT`Uoct@T%IA!2QZWNl@zkF%Xm>}2xnPgH zh2*6|75{Z&P!?U1xFi@@ZXA*4o_YdZ`-AQgp!q%u6z>O|1$xs(cZ7P@T?YUmAz0}~PL4(L>iOU(@%rdNH3f5AH zT+RnBXEJ57z1FcgQ(^Pt8a79P%^zEm*nEwa%|v4Jp|4Z1c|5RL2iB3f8x^*T{lI3y zqOkdsHzjw5Yo(_eWS8aVgbQej?l7YFG~Z>3cCORe1_eUfm-bmfJOkrA9FESH=|P4A>UTPuXb0KHup|vyo5FP*#9u6*zazM*IUe6}=3|awE@0c{6T@`9o=5+hSo}KVK;{XYkWfG0qN+2p=lbpZQbOY%@&$C#ba^N?08 z9TS;Ju1;kCY0B&(9^Z+09smz7tGb4qU2q1dF5b&^a9l^QY_Oydm^h(5v$az&S$oBt z;2(guopo7zx!t{GFr9H72TBJ^CecgN&wa6o9&{ce&l2rF9s}i7(sz+UC;I z^wtdKQJL ze-pm6d06IqexmlT^9=N+lPls|On6*3n?7WD7h}eF>1=ux0e>e#;9^IYkV8JrR&U_? ziKlZ&D+9ws;Krnt{)DjpE(?V<0-fK$3Y?sM@G{DiZFs(`ODHCr)uu@AEBYY;^>4@V z9I+MhH`IVwigVh>aUEv|OO_#vmYW5(y~41(+`wA`6$aZRKF9yQXj*RO(^uOj3ZK9~ z3)cbj(PGG*z6vVAKf>;f^PTMIQoUsO=7kwIFT}AhpenZQ5Xj;yS!%z>y<*TG-=w_g z?UGN1&mxZ|^8V?!`m>CZlzS;|2-BsB|0nL+!=tFOe7}-(9;7-Dk|2*HJVcB*NV^)+ zK?ot;5g~SnB%mhf0uk^5VwJ&V#AdsD?Q~7+_&{fL8Fh5j(HZ^3Ac1wQkCC&WV977m>sy)aT_KW@5wrLXUz)C` zWUL97(7!hpag>ff$26saQ7onQvBqK15Y1X1(Y)`LP^CgZW&{BX`o%H0r$t%(#&<)@ zrBO{cN=@&rw|vZ9Q}6C!i(yRsXF;=saVc^~$Bu_MQP$4@30ic^L5qk4llq#iA`QJ4 zqXk@&?{AE%X1T)nbne`n_wi0>D-8R5=(YW z!B>=XadljaIGp&q=`fy&CAdF>adwF}1FWGC;oj;Z{M!rB$w{e;{+0wS)pnaZXH=;s zVCorM0V@`h#mHt{evGIOHzi=l5G*kY6TfpTpFfl_8$YOkh%60VaVu++rw# zmKe^t7@H*J2QtX+6&d*Ip1H;UA7svx2Ig8gktAcJ(sv0cd>L^zPU0Rrjnv}TjLC%W zGue+FO1uyPdFT81^XxJ&!!POvAER&c+Q;^^8Q2*x(vlJwi4xSTMCq1?G@T@ZCIYgX zh3{9STW$DWZIa=~mYsE?8mdVn;z0mHfvAyKk>;z@@Q`cR&RF4|za`>D{itN@UEf74-#uj_tD+5=#?c=LE^eQ062N|@eo<5g2;;C!91C9G0U3;jkr45^D)rOgx?h2LOzu9Jeebf zL9)%VI{YeT1YV990X5ym~*b5lAAnWwYVFUOZf0R}i&LShNjW8;h%W&P0$uZmEHy_`@0J88Q%Cd*9aE+hyFvnvr)tTrGbLcf@LF{= zVw_Ve`=!ysYdX2^$j#tHCdy02*MXc+H2SS38)+hajd1@#Eg~<<{Y1+FHcv1s4GK^F zF$OnsfEF$LID_Q?_Zb+O?iovs-2DqHtA*_ogLE?A&)Q{yuz9si)0gvl=30mqmM zV9UHE+Idm{wN+QN6D&d909;R9SXC|L=P9))tdR5aPedwwPL&jzctAtZhKU;yIZ>w>QyR3mQmyEQ-~}aYu_Y|__>`6k&Xq!!M(M(e zH%VYXsg(**y_TA2OCOYOQ%mQ?Eqx=g_X|KJlR-a2n6M_ZWkchF0J{PS9|aRJ5-Dh` z4KVHiR8`NP_kWJIX_3)8yyyUz0C&XOh!p_6-iwuN-&4<@g(~VTYfbCY;5fV?v0lG6 zQ@jp-XRRxMV@|^)xN?P<%WPj1hzAw%ah?m1d0>Z4BC4OYAyEnV3ddq^HqcyjfIX+Q zjdMaMRBtIK&6bm9%Sp54q}g)PY&mVVKYp|Q^*mPbF_&L&InO3TdyM-l60-^)K>|9d zslppw(ol)t$B54N7l+o9n+JlZ${;6d1gly&H(nN3#sFVHpuhD}m?wCKx?mGgsyKD4C>4ILf#2QgH+nmYP(gNsR$L>z1?=#c-ZFl+rwRJW^>gtEi?B9CA@ZaR_jk zE%aYhIK2H21ZaG#QG`5O_*T7bp|_i59CAuH)sLOqgY=Scg=r}MG=vW?QgG};_{$yVeSMMPr*aIH1 z2R!22{YsA|zZT;W#bVt3g`HC4&8Ps6od>3v%r4NQ592?t0B4v+CK-vdO8{hiwRF%q z1|jUeXrCx~u!TSu!3P;J9$5(pg+Zf#+Z6_lJ_}QbJsJhFP8K@)6wZF}YTV$|Z#4>p zohbZXEv|`MT)dBIN~iG1Du<#H;{hKl)070j$0moOvg3uKY>0Pq>*=R0fb&=5W#5O6 zNXgytZZ!O_yojE^NULo3c%`saEpB=#UQjPpOCod(8MQFfQA!j# z5tFdu!f?0&IENyRO9DEBBMz=VJSvAx&i0N_n!Gi<${0h1_9$;Kdvx!Qmu8Q$dz7K!wp_A3I{KqJ{#)YNqid7NA`!(NeS9|D9<`i} zwMSan9%b8Ad-TYU|5x^?Mlnd{gXWM>+#L*oNxBLZeS)A<*}LoCqfHWt#z=EO1vY+= zqr%4hVP5hh4PBv$>-D|+0SZ1)w2xziSGvQ2AVOUih8l2lMHASY27DWV`2Q9f3P>=A zY@*H_@??>1#M!Zf7D%^>hIVTRH!EbPiSOXQ>*Z?m$Wx2xQ@Y5f(7t{Fpfd@;rXFDD zB>~O1hC)U!9*#19O0wgVb3rxPF|F-+Vqy&5yET3C(@;O!+c{dN#Ge3isH(ZDi9b~C)yAjckSh>G%P z@mt|(sK0_X;29?-dy{avj?I%;VHsF=Zb(Tq3YO?_y1bt_FalgG>r%M5M&-i$n>buh zl^#X(`QS)-Bvfc2Pw9&GvDX5I$QKSm-S5d-+$x$tjX--6V5&xS5KfZClXQhaFf!H? zXQO*d#E^um*^)ys7EID4_nBa9+VIwqk!i#0yA-~ke*QA24cW_-HdI;KdOD1y5m21R z51DSrM4o(U(-06+&8j`4u=T-nm)Sb7|B!XKDt78cSo`9Kpc0(VlgchUA$@;h3FA7U zIt~nv!rT{x?BUZ2o1bou6Gn9uYPiU^ppV$POp=XHH>eI+s-9C{EXKr*T4h9q}pq zyz?{0+N_SF1~$nt3sX^6OvZc|J=sDh9Tn^sPeyj&M-gckq488O1YPKC$+JrZ#%S=p zt8m9dF8@8M<8!JoWP@86aoNJuAC!*ZpC$1MkCq00Pe>t;Q8RM6kwSu6ZW)yGSEx&# zvQ7o%B~RmPh@EJAAQX}#hlOF0!)vs-X%}}df1`L0fG2(z$W)247O?$8X|2*o@2y%e zC!@XDMOj=zQH7QrV-pj-Nkxh52wPz;n=bCN3G68(hCG)FISO>VXKi= zY(7JL$}R-xNr2d8C(F@)kJI<&V-+X97MV(+3@{S40QXc++?{JlyZ}A zJRO;vJl+vMH#z5I@Mq>Gb!u3DUD*)1$-JMHn|$J=JZ_kqbes(SKjtQt-3}-DgLJn; z@(SwR{7(A9y$9Fvo$}OPBoS(iBt`6ENMC`6E(_2#4P?7PIioAsh2pt#ueIYogVHX8 z`c{1X{S=0KOl%6%v6sIE?1uAz`5+fW<81c6F#P@yqaJ^e;a}d^J$N z1F4_TUL!|o*fl+KGsYU(l`~OgM|V1pwQOo@g`Wj&n}SI6x>RRNM(dGeXY0R;S*thR zZs{1^+55G#Ef=m8>`l<*Yc#2?KF{GpxYn??R-u`{@_A=4SmU&lo7DqbOs z`F;>ymqx}>vf^`_{M{uCm9PAWGg#MY9*|#y3+3|EG+$z|+KHtWF(Rxf*_yT!qw7Az-LME8G@?c~!O@?N?Ie7Y$eiKn) ziGKFW6bgpiEzFKG;}R~>dE~HDNRsYvI)y$fa<|DEbBI6N=*|JN=X3uOj*afcaNOm- z299-5ys^0$uQrb%$F*>@^l~}E`4b8vsQeY(j`D+HWbYuK&wy*G295_q-o$n{uDgax zSn_xw99=Dps{xAg1642&vJ1Pur5n*r4f(K;ZyO$kk?9(ZUFe5$OKikHzf6lUuKqMI zB%(K|coO4&2YwgYw2bS;H2mN|qZ?yC9`}ue4?8)|C&!iK`U-MfPLB7GV+}dhlj9mV zw!1gLQQPRA2LH@G&~z@-w9_tpa4|@T9LJNYLZsSzTT*;;bN18P{r`y`x37Xhxz*4W z_|Cqo*|p9-E8}{ToZZZtoPF~-c7U`(@@D`x9Kjp7aXy>QQFuL<6(TM3fyQmKk??JE z;V9nk2%14wvB-4Jzz4)T9l_;njU%{)CQga4}mdW)Be7#2L=MmF!i{ zzWdl=&b|gVYk=@7j`!KLj>7xc=P3;XgljQvz(>w}c;;xvlEA_MIlSjFZ8 zMT|QWE=i2@L~$ zRDtcKRla7t?c0VrEZj{$zlf@H66lwjwkb^$B|Lb6$T#bO%W~W z{tB-~P{g=U?xjl4tpa>WNTXGIsmA>Tfi1}jfj1}PI?_Q{r~T>mtyNi1(+^p5y;}1b zqeA8URS~Qo2}-Oxo3T!imHA-y=zy(2vBDo^j2mAWkLGCzh?PcrOVJb7vB`M6D zzbB$&9}h?!8`?%{Cgxq3LTD#F4b%I@Ns6X1?p=v;Z-(6HDX1r@w1ooQlr^@3o<78la0Zfpnd}S6kmg` zotTHvYM4QPvl_g8N?(8^*>0-k(ze!G+snSs^FNI~ax*$~|Ksq}T0E7z*VW11<4t!2 zYuUT3MI8?rJ;Zs5M$cya@9iie{~x;2ON8a>Hm2zU)3l%e1O|cM^}e?Ac&niI%}w~m zm}8sA2}~1toNtBOTeY3Xwp(x|*cv*r|q(*cDJ9iP!w+3$eal>^g@pfS=(72?LG~++5TI|`&QM^edl6Vhy z{u!&%QFp3+RH<@3R{1_0VA@VSz5j#$P{`)r)-NlTUAP6Cu?#S-$Mj*MeH!PBM5QHF zVq6DRX=hEm539rPu4w&jI+P-!@X+30bm(3QCX@YYvgB@6r#e^{FI%R%*J*?j*G%XV z*FnZTfOw4dC%c0FSj*liSxCF^U+04qY<{GaTf>c~?G^qHZC4*1MU~~d)1A$gnFBlvzc^RUvuRdQ)|{*Q`gzD&s%TOT3+s!!0t)N z@z)*o>tpfW9oAfnIbK=a(e$jZxqquQV4Nb-{ow%HAUsm`{ocE*z8eEfOTKi$+k{ce ztCFyy$ZwD`E75N#vXepy2(;ciOhs`jqQ_gfK1}$A%Y@fiCLC*bGyWZC%nw@T%a!JP z_`bTYe5w4OnY~jZ?Aq;7Hp)y^wC*dve#Hsf2(tU!ZMI0_pRqqb8^~2=%+L_bJDO#mcS_yZ>iLD*Bv5L+AsWRX}lUr7S%_l5YTvQGkf~;WyQ^+ zqVl#VFm*r}D~DCxiMPaayLM3*{obA%c4sM{>*>1XaDV zNeoF(H3M8iI1VbMEz2h#NNyT~Ko%Jng9R2%oUd}1P zpT&E9Jlyrmz7Ge?aMluI^CGVYMP+7|zTtM*Pd#IXbIELveZ=o3KcdwyJ}1~kP?TVo z29~ji8}h;)@#J7SGIB*&BjDTmnGJjwKFq-P+gvyB&HRjkFU>SY%#5KCDqa><8TK~q z<$*3VDep?~P+nqUHDsFBoGULOX8Ct}CHP&?7vPt#i6A5(FiOUk2nak)oD@oT&_EDa zw0;M*P8gW{c|19lE|cTs_$xkE@W(3txR*ax@(1H#n)*VQOUT-H>n)n3`T=jD*y536L5q)q>P0bU|t|&8o$=X}Usc6n6)y z2^1u5BPm!hgjnGN-0%mNCH%=fLz@IxkT3T}YC}zbzsPEu7WjtLSs^u6-$bt21EeOQ zaC)p7i++O1s&go~qSaO~yEj4V?z_(Re1hVN>^hKN50d*kl3&lZwLD#(*7d|cY)B26 zHl#)cg48(a5mM7I6Qu4zm&Z@rx?JYyvZCBs90aMQtTCNs>7~~F0#g4iU@6!mq^1`7 zxO)4e8TQ>4yEF3xc4vOOFwL;z3>#9DyUS>+{f?k$2O~UgIqz-m; zhSb^!hSW#T3aQDZ0m>mnc-=Z^C-k7uq8zRz%S@2BI2*dY$!B-3Zh<{k;>MY3I;!sP zV^lp(1)|RM0Z|iV=oV3b#HlH8^pb6*n3#uozFKD9o>7!Od>NHF^u3st|`!7%$ttKdwl z4YRX~q<8RT0cInWu=`U=FdKfFowRXnX4kPPshYer5-8z7?;z}#Qe6@D3l75ml|<~9PK0u4HMetr8`_g!M-B;WlO@@ zUbkhpI(5`>Fukw+O)f(?(jjnEI-~I2T$II#;%l#YseHRjMp)IhNvu zw2dUG0GRn}$D4mV&jq(pAv~}X+_r>P-frN%fE#?p9D$7&1x#w{RKqAz%D z{zr-BdfANks{$r2myR>2-*rSH?E_@yRgtmWnn2mRDKgIMu;^}OOP#OourE$UaVknx zFybc8um(%aCy&^Q-Y+L2^?Kj@*fDg4$x(_Jb$LVqhK{d%~rEVjvULvr1iEQwqIgHh_O5)XMiz^n@ zE3w}I+W-)CJk6nFh`hxJ0=4H#nF704vV!=Bo$!Oknd6q;`UT=Nb+7Hd={N3EK6?Lc zBYA~K`I}~hp8*~0lu`xy3Q#pYD$y@2bR0{^DRj)Ik)JP@`r?wFT7TX7$RKVR*a*zlM%)Nmy_fB9H>}6|yAm{R;IwksA z0I2(yaxZ&^vzPraWrt)Eji$U3`JR~R((GT{n*E-X zh^{qZeE0_m(5DtCwFS!d1hv^9OdI=_BAr_+fiOI_)IGCZQneUcXxzdc_51cbTfZqn zKett?+*XZ8zo$&CLvH*=Rx6gL-$w% z9nK3)cc#ixZ&$P6HMTrZxa(Y_r1!HGn&2q3b`~iV8dJ_X2|pScGl!*iD^vfiB`4C) zXcp_hH8u0oH3XE$S!#f6EO(Zx_h%i3_c}JS>oAx#B$h&uzKIA5 z8W#cbvjg_3R$|Ws38ltZE7v$G2wYW5|tArX3(rbRS|Pf z&e!|>1S$LXXI)Y*u!r?A3Yv8qJ1ohhnwVDg`%iqC2c?~cI ziJGx}APr!fMhZGi8jz0#FhvaWy8EP^(05`VzR;XNZUR(6`h0rKXB%kQPXV3aukKcA zo8lX3LeA$Bum-K!4`i#YeUR3Er{E1~fn1D%d8` z{J08@r}2bdO00z7Jn|2_iGu{^4^uvXz-0VSW>>3)mWl`{CUvh!Z*kH?xL7!?ZJ9TjpKV@Q%P4q7u+3VG)1zx#qnfY1^Wm>XOKktKbG8h9nF zwh^g`k)U{wMJO&}1+J_R5$Ru*S|zk(aYfPRV(Qn?m8J(WmD=4}Q#^shrlRQM)uXh! z(fAK@rS`A!7{#_d67l1SV_#rEL9v;j*entyy30)oh|7ihQvHbE0g<3Jq41&tJ&+ihSSJW6D zELL?m8!I~Y?_zWp;LoTLGu2m3=_r}dScCkiA;VbJW-ZXU`lYm=e?p0F^0^$&C|Z_q zGS-TW4)WP61v~=;GSARK`}ve%iT*be&p;WhlXie=pp+vmtLSm?=9i`=8OTD#d{{<= z1LuOVp@>D*d>u+wh9BaSpNCh+4l1z|7I-`Ki3GV{BJ-sxt0H4U1eXbYBINik?{kaB!hz}bBormdKL?D zznmu=VwBo?RWEqfOZEDZd~41*x3b1zm^Isl& z*5g?K%y{-Ua?0UZ0J4owi@`eaVtN)}3<>tfi@-7zafaVPuoFOL4E$*}P+`O;zDsSR z)4ZMvO%an?lsTEtAmR8bgcU9{?mjNUHU9LcMC!$HRDaAv&#{r0CsYsbuRr|;p?V}C zUKAqI>kqy`yZ`%1Z-MAPZL<(PbxR`pzO~&T`kT*!=&yE{OMo*yfW7+D#k^O)z|7KL zF2*>t$m=cP+)!YFzV~g$%#ni?TI1~&CouJFoB-mZkxJZLY2en4|gi<@3NP|l%)OG%GkwI#M-{hr2&Lf`86Gxje zSVxpW!?`E}VgSxg8LT79pdll*krnA68-OyPZwY*}DT8A~888t*V*<(m(+!jXav`D& zP{5P{nKNFECGDaNvLt1Is}^N|b1}lavybc?JNq}eclK%C{H2X|_UX&IsWPr%@>-Mu z&XuSa|1&}D^4#Gd%e(xNC)k%`r;gq1@*kj1QFqe%MZ5fv8z`(3O9>VS2?T6eP!RG!W{OrPR1oJuRj%{rhojdeglP(QM#2i9RE8F^@XI_m&G z<3@J4o~VHz(g*u&WH+ymozJ~8*`-+;>j1CQiFKehe+cVvt6M6wN6b3(pkHN%t>0V3 zIyhFTa$9BZ?z6&OYDXhA-Eg;uMtx4BQyePhnd>_nrCZhkS1s0oRzVM}1KPA%haMDq zz-ue?n!`F|Uhl9DXvtz7aAUVJ^-*4jb+{+nVjXB|o{M$ZG|AQbvkpV)2S4I4Jmj%^ z{f@&rD3iJlgIPmjnRU379xbp~hjjsaWs9tjSO+3k;+~G9Mp`Uel!cL!)q@x>oB&L^{4`~4p{cgI*^+Nenu4Z zy8EP^&}W`<9#tTh$Nr)#|DZYi!;y(FRa1N;IT&&8Ts56{m}~P6;2frP*Jw#jt?@b^w7<)6j+A_3sVPc&?{v{ca-idX7&Ot;>LiZdp z;@Im%Mo^(J-H;I!^+879u78@0xHHp= z&+#O+L<4I)+Ab-HdKaSopev%qs}cyUP+%{Fr69AZEr>Ric`2{}q&a$SJ*|jfseR+8iR{*%R_%hgSDG z8B6Pv_MniR4Ds131tbK7F$s}H4?9rKCK8brA|WUtcIP3WAt(n)1%AkYeR8d9z*I`C z#VnZTrXU7-c~`v>yXvbs;dT)Z74>f7!A&~Q>#~T=(Gp0IZPI~qW-3Z8XVL-ZW(^TO zz0ForR@q2ZZnB!JvYOm@t4%zlNteWfBVK=Jg9W&qC}R^3sMn9$u;yB4@Lh9_%R0s> z7xC~B@6lh*_R8_5_td5w990b6&d17 zRd`@wGN0msg+hsh?jr%K=dpzr)on!uf0_Q;<75fGAcoi>yC!V@;Z6_WS;L52}lx-CLhQMvYJne!ETa_7(v&ZM;_ptItb&6 zHO7!9RVXQm*`OpEEK1^dAt;GlP!gghMGmfy+QR*2ftXlf=*j;9+6%igD?72#)8yn{l#r~ih-7G`cn-3@DxK12XFn?_#s)9usu^9h%Ne-%|vw| z^C7AO>4p9@7g7_EJiT#+ESDVFwkWD2AlD(}P4rI}>?m3Vf-Q{K4 z5OoB-cV)iTye=O=Ly2tPGFpiUNds8dEo`o1wmkBc(YeB^TxJ|Y?MAxjtA z&qh>cxOouy!^(JX4_;Z2c&VL84jPX4=-5oh59z4Wv5AiF&~X=RvU1C5Q@?)eY2hJ$ zDZUpSjEvUDZnX{b`SU`ZYkY5NoX3qOe093gQB=(s~1H*7p6j?sNe z^hQyqSWTjW0LOn4AIg(sMOos;!;==S>8EVpscf&(b6=@c4=aTyQ~tt}g*%k(0z)dV zREB!hwfIG@umx(>DUW}Qto-uW^DEx0R1@fpXzf2--TBbf!DhRI#&h`J;q5zBeW>sa zBGdKR%0BlSciZP)V}Uq-l+NKWjT!c(3}ccwuWB6? z$yXcwY<0TGe`Acv@BK^6=oQ)-_7(@qpDIk2o_bQLSisWMHu@ z%=5^>GL{7XkG-o6kD|)bouuhBU#S3!j2}Zp99=TFk)V=E7UM92SzQUDhKPbYGC?8> zxK?XW48iJ>bczC0aDa!5_`z&uWFO584XhGL2&TnPL=hBMXJ%ckvUPM7k`5iLJ@2_y z)z!f$>a+V}pZ!s{s&3u7_uO;OJ@-B5+^%-$&o!sH*LmjUbls3ua&hR#b@yb94~-z) zX}??NzI!&6DJkR<<3x$gC}Azdg3cu@g6So5q;MC`^Q7=+I4_jKAK@I5!tdi;BZUv* zJVO`&ktpOrS0U`>(ze~@wj9sg#R|Sbh3HkvXX-4KM)4USbdqQg+eAm=ZSia*Ju~mg z7UGeZBdszrV1zFQ$WBa`R(*lRBa9(ejLYO)5}6rv1PMW_ivfUl=fzYAAk>u*dBL7i zT|IJ~u`;DQ@KKzIx`{4}cx)vdF7_CYPyphzDHAp~UMzs?L16h6;lt3g%q@Wdu$g1u9E%K76zqT^(0LKl=9^EWZN!v?sRy2EZHKCEF%Pdyim zaXDmqIcA;-Aeik+J{f|=+75|?Hp_Sdi2z)L=&0?$59nMIA9 zRiv>5F6+37N?vc=@>sXkz;a7k7J8coDo1cC5{1CV0}+0)u#?(sL_ReeQS0eKo6-|e zR~&a|qQ$D1CXyu<8s-A!ZsH<%gV6rfk7W3?7W7BibLO&-Ftg;-#HTy0sa; zO2R)NY;I^6$!zM`A@QtuY-othdFlzxZqPQ}JnJj={T~;D6Vb5<#z!m09a0zuBTV1N zzhaA5QnzPd5~9R;zFs4n%ky5rzDzFQpVpziN#}xk^}unofHfu)BR2ah9-3TCTWl_+ z@$YQQlShc|cea(v0cqEip12=VWPN-XaSfSS>*JY)`%{I^D1K|{X-oXY9d`^NoV(xG z^-ijDw#cuhaeqfkgfj7~C-l&F?;y{hpRTZO?P6_{E&y6=V0$QVHWRIfz7$J^#i~PX zT^hh+d921AUw=*c^a^9<)7_TRRuE~#*l7vv9i_XdHju^Ktk1bR-lAXrWq!wGn=saH zV(I7U_)8L^Z0*&$q5`?X;u)%O+L2LE8o#uxit@9S*39BpLto~zsJ)(upU-;fs$P02 zo?V8&w&_<&Wsi!oq9vsTQ-)anIA%j5$mnb@l`kT^P8qPsFj`~dr+2nzICuH+L7nZH z@(^*CC&rL{4vZV}8NKA&=wTDrI4D%0eWiBRj;YR@YCJfG1W!p=@~IY2T!CpUd9Z5L z_K*L&qVuiA6=*H1!E0`bbRn-bN3r?8va>xXU)b61mq*6OR2N)rl=N9^r>9n>1FQV8Z(=qc6oI`zx)WbvJQ=1Nr2qw++b_n(78pNEuUpYaTW!coGKlkz>vp)6j1~O}<{>6W!#?x5qp%`Q0H8vB0-4V$d}XGwU<07J`m5%e@GZ?X zTj>}&r@3Yp9rNgTijFtv8KY<$pz}xljzAMRp9K_v9{DTN@~N=MzAG)8K)m7$YC-L? zgf^|mh3NZ&>7mM8=VQTzmad1pAj4abwp_vK)12PQ=~bNGNU3pX9kED%q6;9mWe##S ztOrCd=;EklzM-vmA-Y)#h`zeZ=Fp0+6hw~=@%g4lTGpZB=y4E!2Xi-T)VWbZr@18{`i|)Z7u4)t+l_jEu8x~#8+zdiyFz_hMnnahB5TpT(D}6D z*MERd*A$H=V%cv!N7DDxMX?I5LMP)~xL5l^TZ=a}?i-mkY8{z1iJ%mIf?vzN^L_Iw ziFCP+mO_J^k(z)!Ze3FO>oS4~glp-6iLc8jdJ~VBUJAl=7Al=bN@p?CS>$wxnfY-J zz;8QjqcYe=7p2=qFXf;*ln$>C^No@lH zVL1w&s<8C?H9l#jJ^T@K51%_W^ucGr;j6&m`vNwH7YD!qu#vbtKhT@YS*`kt=P}kw zkzc-*3HoN7@_>8j3o?&+nvSV>B_q zE1qwqJ2S|nH}xW&>HQQ@;UuiXB&@?EtivQ(hX)2ZL(29?2Z~K3>u|E23G0xjv-#v8 zhf}%cBkpyc`OqMIpP#M6TDA^5GVOQk+;mHaY`&2>rb4flvzzYPW_uGxz8;03-MF&$+^T0s2|AZ&Zj)7v1z(CxR31qlF%Qg_-W}w*MOBsms90T!5 z_yyYYA}yGOLOTlXl-e^{{V2V7SJ z0Fyx4?sF{7tzC?+ur8GwnGZUa<^r*f#;f|K=2E7nBt^}rUeshqip;BBO3V%Y(kZCt zw#AYt=pg$Z1-E5?!^F?yRy#7>Q4fFY^VF;PireG~Wap}T z+S*#QA5-NnVM>MfOOa>jyzE8B`~1}WU7u=hQBQml_x6y_$7XI&X$?jksfy?m=pw$bh-<*Yhx0DB z`T6bIRxOINWAiV3kmIlAU2N?)3XzFKL5FrI{*YKrY2E3odlp6SN@s<8Y;|A(oa zo@ei=9?PF+hp_xX|8z5W9O8=lAg(bL;5r-p6xRRoW5W9X@VIULBi1NNo#)C{osbz} z{dd6n-~zDzYr5F_U)+UOfTCDgJ$BsT5q@>GJboO^DvGkWhip+Vx)8d}kK)<%(lxd? z`AzIKC6T1ohMLVJ4=_+v-DSZ4}p{M0MPs?I~vt@wPk=g8XCQ$F1NV)R20z5Un)sw18{)JP^ zc3*q}D4K8AbE3AK>Z#*FPaUDLE+3>YzB*?!pkkVFM}<2-#}cc}nF!rD*lB%yVxMh| z^0f66FTHD0&}+Ytk+1jFIO1rl?KEEv%lN%#3*ls6&EH0f?1enCMu4K#$lS-jf29GC}xyc zoJd0qWLJ(9hUI1PLZopDcaGWWvti5U$Kc)O@fqjQ^E5{4kLXb>BMVZ&d~&*O<5R_# zwg#pkh0sj$_~H)NWzDS%g~h92yniou5+1#ti2t;R`{25iQt$P0DG42nUJE6g5gKfR za4Gy6#)Ov@f?&UeqlrmtDwW6m&T%tAEsTwiXv%PI^5X-W*vW*OJfSLtlW7s2MrUx! zW*95TrZgYZ1tU`4lyyuLQjd>`Z!LabYbof0f<;5{{?N&I;6LDN29?3eO8Fvj>H3=X zoz_?Fou1ki=B!U+!Et;|ED;`G(=WOlUsFT9$Jc~x_?mFGol+p{*k8Q9CT@j&O`Es_ zT;$+igs+Kx1@JWq%ExKUCE;tzJxThpw6U)V7skeA@HKH>rN(weL%@aqYQ<+j21QXA z6WA|{bGD?Ook&SUg)P@pi=wb8mrT*tZehY%V6W&YlYd+$pHU`{D3f!_+D_JD}^fu zFlvB{v4{&xD=tJl`ZC)m)z;f5g-Sd=sWyYa&mKn3K`A_&B(6Fe9}QprqKHmUTJhbY z(~oj=x~;G1wCssa-|2}?S9_wAvatm}sNq5CdL zT^&q|el81&*mSNZHf_!iI)vw%H@nw)=CwgPDm{Xu(nHgt(&oiM_Bf2-c=XV;c(i$X zP(-4K21O)#Ku|=YGlL=$eawzTe_=gpM*EiWNh*2UKZAY7%d*J7u zl)JMrj8P9h%tPfk2K=ES3S zr{d8ozH>bK-DTe^9(~Rai$}kgibucEJ0AViV=&csI`L>R;QKOcQ~mef5RYE@-Qv-g z>=UN+zkY{!^x0#-$p0M;@J1o8Nq{GrnP9iKl!xNUC^?xNSz2|}d4@;jAmQD`*VPWZ62fr>X zy}8d?e?VAz_L6T8OTYHkw}z#+y!Gv2>Hi$?eZtbWhrD6wR1mtb(-WS~Zg|1mc@%3W z7Jt|bIFrqA?McO{JMIB`m%`08qwZ_2xrUCX=}6KMP?~G5q~n(GiRfMHBTjwC*T<=E z?3Eofwt^wFCgW#=ZJ~Zjj&&0Oc{gS)`+Fhzw4n8tBne;RpOCh+a+=`uO-_$+`YxwC zIXy(FaoXL4wB0X8K%3bUX#_3mVJR{$Rbv-tnt6=haS6P=-QEkz zkNTnTX7tQwb_eZ>f0ADD5xe5I8B;SC%vTt~AL&&k?#(|0MduS`a)pw(Ri3OQ=Hr_Z zgyMf4bUMYDv7Q^8I6rh+R+pX4QACWHd0WuwMy@&Az0NbIbM*lwadBuQe!RFc333_2_Z9~~2;g_}1tI)T`GO$+2Yf*oU&QSRbnfRM z3uMH1jUI)V#dy@ApN&lb>rZ9d93Ze>z+sj)2dtk7onOm9oy4J%j$nRFQup9@vDi4e z(XL3ywCHoG!qds8M|+YEQjdO~gtTE2NcuyJ)KP|!`e%&P@uooP9LTY`*W&X&!%tp0 zU*i@Oi!k~w#^~+-PA-+_kpna-h2JHupdNNtKx8VLK?l$hom5L&c@*)}0IZofT?t(w zZEKxYZoKr6Z3Sgao@_DsMJ=KeZB#P;BExzFrHCuxHAF17njR&v0xtc=558s*afO|e z9r~i+y{(AUCQ9X>*Q+llu`;09a*aMJ18`35hZ)9M0#mK(zxy2TQ-Sc*h3ujAQO z5q>60h)zbLpwm1Zs6We{Ak>qeiRfIRV5^Jz@s2!{P=87~%mYB~n{n$iUrK@g&zX#8 z9tP;&+Q%l;#~jel##B1!zn?)r>x!5Pxp6>06C^-?p-Zyy^F0Fe-^ZYzxyYa&qHKeH zRO&uLi!!(x&^B_Mo{FpnoW7lewW=glIDIt0>}Ypgo+I zvDg9n*Z!Rl!DTUk{tDy0ySgon=K$&B7X+ho?SWu{(LRq45EbKfj0i#gHFz+IApg3g zJN{M_bBed1@}k5%pxjTm2;R7!#8So9;Z3pU7yRIZr*AEN1HU_9j$R$Y@{3l4+y;r9CX(sZFTc8118kK>MDNO>IKOkNzU+ zKJ=K2Dy`G#et*(gcg`ic`^+=IeRt3a?z5cOaNjEc4%`R9dNc`4#Q7F4GhW1x&I`o9U$qhc>SP+?2NUcd z1}b9E?@R!pDaBaGH>5!SREy`S#wmmTOIWv+yYo`xG&P6g1Ey9f1oUAR(C~ke&(B{^ zh!zwEJH+q@Fd4Y{rcyayga-W8W8*nZ8FbE%2bwbFQ>ycvA&fBx7~%7ho!|Hgr^prK zKPWUxOA?qqvpP>KI#Z4DooEo(R;BD$t)(AWQM{nF09)x?F)29|FqWL+2oiz-`Qp;= z)>+`x`KP13>r@!I`9dL$d#JY7-lnyP)dyo@=^rF}=~_q0LK%}Gy*kn1;Sv$ zh!RE#!ypVI3<$#DWO;*$JVZ1+v`Qxtq}}e?&{K3|O=hEslP~!`O^lyOAe#ZBj>|A{ z2E{ds#t>sRF}g|BcCx}I1M|S?y}#eRRbAEdfX(LHZ~y4->U!MAx#!+{&iS1>N5`3p z?GV)$w<}Tos_mYrUN;(|`ZwDZF&=BriRxdI?xp)saN|rC|2W42-(ff{q6#g!as>92 zT2z-v@0CBY^2RuBI6N)Yw4aY};rCj8@8b6X7M^T}+<#U}o))W|)r6-qQH!Uy7OaVM zmmNv*WVwmPX#1{*pXm~z26V2xZ>V;-HD}eg(x^jP^kbeyALkI@#Qb##lIrXdEqN5e z6g!YLJ`JZm(5ZTb{rugX%Ek0k7SD-QVuDdBJDu|}1pr?_pufloAJd!n(Z{sSL`hs% zh9{oDu_`=lto0Rst0>$`}P`IoG-)G^l)pwrqtpB9dsPU{V`{YcUJ`Q|=v)btbd;BBe9#q0ZP=s2-1dk4G6exlGcN9js*5f94)KYVwrFM=T6?Qd7Z<3qq;QSeoca-m&Z(p~DGH>fxKq(Gv&DC^NS(B}9_Q4@ zvJ>U?T;t6msMz`Q4R9J2J-j<8`jh+vJ6WV95uOAcd|KR5cH)LIv9^JHa>szvrmrMA zZO&qspa7S%qsBO2*UHO{qSOcn(QoCXbN-A8_mY+T*CW#3=nVTZHf2_eAASB_b7MQT zf_4fs_-Qpafqt$`gX==3f~~LG{?+}i;yAL6%N!SSD38yx7<>zzv>jZqUtM-yFGANO zefpS~W7#2KCgt_$kj%B{wb=?)9M#K#Grg=&l_TM4oTx}?oG#BfYk)|ShR!WHhtMb4 z{Dpg=a8|eup$KNHCG7&GaDKvd2>rHHIJM+4jFRUNI=BeJ>ND&dm=9`A@|Rlb$KvauKX*@?3)Qk$&5A9c8x&zaJGbog90M962P>fQFj01HF*Xawo*%&P}P_+AY zW3@a_zFShxV{AP5Ro@gCFwWjmqL%w z7I=jAw_Yw~84r797?bms4|s!${)d;@BWt|bxwrr}^>e*hmL8$~)is`VP0Y3TE4>~s zJAvl*WixR(P%YX4n+f@P_e9@hzE;Xo?rxlVOo~unfjN*OiEB;qPkGmx0#-{mK6e(~ zqjZ**5?jui0=AXbl%%e#DbrCv&YA-DRn`;~L~9BgPHPG#YpU@{jy^i&a|t~if=eha zS^TC;=v(e!J6$=p86}Q=8=JQmb06DQan!RhElzCT8%QG%;gwkV>vJF>l%J zH!a;lNLS7%;Ljgca$M^`DDW@eYyqhQbS!{)1<+uchuv89{KhwITuOGTIQ=TcsU z-3Mj6O50FZ+)6%+k)m?#S%XmVd96X8VVyhLU26~|{L+T~{Ki>HrGPaE8{ATR;`?X@ zj&s%^JYY2~ZpX@sf`#OQ8|-Zb?h>Y~K{yOpgJhW}-pemDK}!LxL1>e-2FZE?wVB!b`P~^Ybg^tH9baqi5gN8 z@;pXwSqD4Sn_oLvJNWAdYb(U(5%Jmc*uh#$d~RqE=f2U~PPMNek5RpEkI{93)4_O0 zpS0%1!~&naxwzWC<6UvzyjVeER(6$`M6)Lu!?w=0Yu-f+Cj>ouvv<&=KG5m1Gcb8s zyJIdo4NG3Ck?3?|2fqo}N+5u?Qi%jg5Xt`hn$E1fV6BHv_ZX#i`C3ne`)Z_Lbowcx z(^`tAM~M`r8eiczf#Nhwh?^^3)snXpppn(e~XDWX^vN44Y%n9e$iv@gCP0qf@u^#NG3pZx*W-zH#9f71m_Ls5EE zpHSMWkD~0e3*d7-Zg+P3;Od<|xO!6%u3nBONOETp;c9xDO4#guuOVVqrLO9QR$m%K zt6%b=)sWR(wE9mmRc=Y3+&`;w-}Hgi$AVz>OFpprdokJZj)nR%7#=EfhKGlPV09v< z3=bnBJvp#iIi2FK>vW1^>2#`04r8Rr;bNK`_@JPXQ>FmwG1Az;s~$k@e8h#Oi+Lcl zuLuveOQ%yp)2Cf%+IFGoUlW=JTZESNf~E)kTNX}*MXJLawTG^w!QrTjO(O+@i%qxc ztHdW(+N!YWW;(-j zK>eNqs6Pi=!35e09?SvMsd_L^pWrl_Yd`UXROl1u2d97Hw)%)4oJR3|gVT|>t+^dm z%{dR8?)(eE={c)9k%m&b96aCsR{*D*U2yvS?%v?^)yppkoW6CjQijp4d~o{E;(@^F zae{wS^(aun&OSx>fpcW-95~$=?h~9wr56NFH=YNa1_jOwPP;e#KLMxFd8tlacl-Ym zoL-d!rC6{oE|K}>1d=?jQ*eUv1v@0|08Vrk)LNT7@G#>d?DEMjlIeo0Gq~; zWI06#!lp0VJPCe9v=nAzFdJaGv!GYa%6SNVYTV=^T zj^ZM95=xU~lw-~hr~`1%hgyOIp_ahhc7CjS$uj5w`@es%cK_1{YmbW0<=;D4dx!WK z;`5yNJpKLNI>7z?VAUJ?#;O0nS)mc@GMU~tNL5uy^(!6djx9OB6&qX#EbS+bkY%m}9vcZh*VP7-j(^cX4^t<%0$~N?{ z*T@9Je`)-SH{njvg&zJvX<|r`H5!>a2+TO_wmbZ^sdN_1}!oY;N8K1zGgwwB!hYuB_E!E3GsJN9KXqD0}iOAkFUW+CyJp_LjecqS3uUOAaZN z*;`I($vF@jzm$X~(0%&@Rf%q7Z|UBqnL8QspH#5O*qV70>~%&qtFm955d8==;2-C?4d9f0 zeG^&VsIyE=b(NnbR2P!-X@UIHT$jEpM~33afF&=_m3(|~UR&#K43OdFjfxD3CX_6=Xg%$pxM34m4F~fyb3wQ_ z9j@g$4u4@}(||4d$vs|+%JW;)F*nEelZ{fZ#L4+3+7Gy;7{i?Zin+}!HLeM^40%{^ zl=*0&p8a!kJ^!nbI&-(zF&Gg76<#qnK#7vVCBi z2nX6^_2ji`wrUk8F4Ct28KEwc*YlDa0!!Jl2p1-OaiSZkR5}Ll8l31Ds(q=Pn&(*K zTCqTi?g7Srp#f8;>;!Xh-0;S4tz*rd5$%P=L*FSoZgE*PU)PeMh>t8XdFyIPuoL3X zUURHV^P7jujXB5({~)@v*M#nr=H#!IYe1U`;bgX;jw@`U95L`XU2Q+PrZX!z1b+l& zg-XlZ6+oC>&U=UP4>!>_Ek0|VQT)So^IPZreysBi?8fyF z9k?;ipE=psnLMF?u5MVobDh3<{*2e}iS6Y!nThSK$8X*1Gx&SYRdwX1Nk_FqUD~17 zte=V@vD%OzrP+?8ZyfYa?2@XR3|kJ1c8G=e)2i^6;Zsj!F4qnnt9y-?0#C~)>!LH) zY37Ekl&UTr1Va=zJhHWO8>|xDrN-5g-`pe}*oecKF@bUY{%o-hpG>KJYl>#ph4UD5 zpYM)%cddSFynCg-I?;r7)V6WPV|F0LtK{`zs@A9!G}^c`GR5oFVZddaHQkQvtH4T+5aU>EQj`d_ndU6f3i*90xC(TuVf~O$FHVDK~nO08*z= z3djt)z#+M8mTyR!KF$UuXNJ)NDUO*PkRjy&=pl>2$DFf|J)P-aQ7xrfSIm6Y@|8)udn zQK4EH;i^FM^$Ij!EYDT43-b1g7UNzLD0-3HMoIz?%@z-7hcs1o#W7=AU>7w;#%Grp zH(MsI#%sgdj5$_ET~YFgUhcG>_HN6J4$R><%emEd=L-hU%k$KtnE?fIfSsPf3@Ea` zNfs$3M2sQR*=r*rRN8pcaFV6ON*AMXpWX3cW{i9CBFp4SS{U%Khcf=5(o2r=;2+`z zd5~Q(+|EqPC2;YgIH9zMGpef5h)o{65BSzPHcdBPqV`F5|A)O|v`2x?BnD z4zKu|XN*#NccrJ8|MK@>g#+$;xKFG1aG|SzOsGF+<`-8Q>zCW1U-f4rIMv)EZ3K-0 z8^PBv#6}?0q%5NQR{TOs-Y$;nMyAqy2|djiDQUbQo=cAM1;nB$JCka_JICdG)mz)Q z$;*zS1+t^`_4+E}9pLrV;){p|o;u-+J+qe-mXUWzPZfcN6z0pz&d-1a_MaK$kQW{% z<~)@P72|xvI}SS!4-UkhNqCnEocjj*Go$b==so9Bd}kafOjhPfJlnwF81o4S;?SSS zgqKPOd%%cr;UEG9JyOn2bj=J|^SQFkVfqidVBz#;Zv+ zf*;1Is5F>i%#UKvSsloXGTurv<^uW6{^XTiS>{JUwUDO{z*owDhbHcDo+=7?SyB!W z4qEAgiHHj-By|;$W`Y}$=BsXo6fh!WB+2*A@`7M=8OWPHia!+k3!}?;oWKYK$^QU2 zm~->Vg>Pu%Dnc~r8K6mk`G!RpU1kqnK}GM-{+|O~W2t>q)MtztX1uU#D||6#o>x@` zV$7UWCj&8NwxGHqkEg}s6%x!FFvg766@;jfBkx>Nm=|Ma(ORH+@DU`M_X%RbBMKCn z_koM%9fnlUafiMOXx`OC^WKPfAxI=CWaDEP@w|n<^`&deXd$;9< zn*m2DaF6R^G)E(6(jbrGZqOOj);s9TD-e|3pfereyr%{XcH~AUp?~crnLK5^FJQ?n z{RgZ0R4`c0#Or;@Q?Ra^{UlFOL2BYSM39=~hu-wea+UTaO(FS$L2B^aifVi0jDdsH zu)G!ajZ^vvQlqXtHf5k7H8O+@%ER!c(%6qPlZXudy$Vu8nH)RAa~{pxxNl2*kbEM? zOyYy{dtlna4ZT9uU@t1bppWm#!GjRha-nKa{CPvwxHnyDp4c;*@Zj*XCr7^3Po$bI z>!eXomd6Sl_kjY{vSd%w<-v{nKZ(Mhi1zTUysBYR7gJY-4aVNMI%b+ND6-5wZTZcJ7^L zD?dtp$(!@-eb2r3{Uo-tX0lf9yZ5~FyYD&s?6cqA`;%d8uCWJ|Lbb?*B&ylbh9LFh z#{Ag~{ESFxD-GJs6G2s;q@H8F>^N?1rymXXUQ7~_nJGIz8eTRNBK;4K?Q8nMk$p{X ziT`Ow_cb+%|B(1UD*k(p**_ZA?BJL&JF5J1Hrc?Ged6Y#m?3kvsQx)MYIg8sEGu~O zmgc@KYJPJc5{`<5qx1BWjMJ$f

t9teJgCT|8&4I(^PY^_n@FS~{mmoj7NOI)3(P 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

5m#wu>!qWJgGr#|yE*4|Ul0NdXO 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%0A_g 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

{Xx4$Msa#~CzK-4)$`C?rwg7`@|w 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 diff --git a/dump1090.dsp b/dump1090.dsp deleted file mode 100644 index 6bd819b18..000000000 --- a/dump1090.dsp +++ /dev/null @@ -1,152 +0,0 @@ -# Microsoft Developer Studio Project File - Name="dump1090" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=dump1090 - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "dump1090.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "dump1090.mak" CFG="dump1090 - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "dump1090 - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "dump1090 - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "dump1090 - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c -# ADD CPP /nologo /W3 /GX /O2 /I ".\pthreads\." /I ".\rtlsdr\." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /c -# SUBTRACT CPP /YX /Yc /Yu -# ADD BASE RSC /l 0x809 /d "NDEBUG" -# ADD RSC /l 0x809 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 ws2_32.lib /nologo /subsystem:console /machine:I386 /out:"./dump1090.exe" - -!ELSEIF "$(CFG)" == "dump1090 - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".\pthreads\." /I ".\rtlsdr\." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c -# SUBTRACT CPP /YX /Yc /Yu -# ADD BASE RSC /l 0x809 /d "_DEBUG" -# ADD RSC /l 0x809 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "dump1090 - Win32 Release" -# Name "dump1090 - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\anet.c -# End Source File -# Begin Source File - -SOURCE=.\dump1090.c -# End Source File -# Begin Source File - -SOURCE=.\interactive.c -# End Source File -# Begin Source File - -SOURCE=.\mode_ac.c -# End Source File -# Begin Source File - -SOURCE=.\mode_s.c -# End Source File -# Begin Source File - -SOURCE=.\net_io.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\anet.h -# End Source File -# Begin Source File - -SOURCE=.\dump1090.h -# End Source File -# Begin Source File - -SOURCE=.\winstubs.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# Begin Source File - -SOURCE=.\Dump1090.rc -# End Source File -# End Group -# Begin Group "Library Files" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=.\pthreads\pthreadVC2.lib -# End Source File -# Begin Source File - -SOURCE=.\rtlsdr\rtlsdr.lib -# End Source File -# End Group -# End Target -# End Project diff --git a/dump1090.dsw b/dump1090.dsw deleted file mode 100644 index c29f0f19e..000000000 --- a/dump1090.dsw +++ /dev/null @@ -1,41 +0,0 @@ -Microsoft Developer Studio Workspace File, Format Version 6.00 -# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! - -############################################################################### - -Project: "dump1090"=.\dump1090.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Project: "view1090"=.\view1090.dsp - Package Owner=<4> - -Package=<5> -{{{ -}}} - -Package=<4> -{{{ -}}} - -############################################################################### - -Global: - -Package=<5> -{{{ -}}} - -Package=<3> -{{{ -}}} - -############################################################################### - diff --git a/dump1090.rc b/dump1090.rc deleted file mode 100644 index bde02275d..000000000 --- a/dump1090.rc +++ /dev/null @@ -1,115 +0,0 @@ -//Microsoft Developer Studio generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#define APSTUDIO_HIDDEN_SYMBOLS -#include "windows.h" -#undef APSTUDIO_HIDDEN_SYMBOLS -#include "ntverp.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) -#endif //_WIN32 - -#ifndef _MAC -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,10,2910,14 - PRODUCTVERSION 1,10,2910,14 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x3L - FILESUBTYPE 0x7L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "Comments", "Dump1090 for Win32 \0" - VALUE "CompanyName", "\0" - VALUE "FileDescription", "Mode A/C/S decoder for RTL Dongles\0" - VALUE "FileVersion", "1, 10, 2910, 14\0" - VALUE "InternalName", "DUMP1090.EXE\0" - VALUE "LegalCopyright", "Copyright © 2012 by Salvatore Sanfilippo \r\nCopyright © 2014 by Malcolm Robb \0" - VALUE "LegalTrademarks", "\0" - VALUE "OriginalFilename", "DUMP1090.EXE\0" - VALUE "PrivateBuild", "\0" - VALUE "ProductName", "DUMP1090\0" - VALUE "ProductVersion", "1, 10, 2910, 14\0" - VALUE "SpecialBuild", "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - -#endif // !_MAC - - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE DISCARDABLE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE DISCARDABLE -BEGIN - "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" - "#include ""windows.h""\r\n" - "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" - "#include ""ntverp.h""\r\n" - "\0" -END - -3 TEXTINCLUDE DISCARDABLE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - -#endif // English (U.S.) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - diff --git a/view1090.dsp b/view1090.dsp deleted file mode 100644 index de97af863..000000000 --- a/view1090.dsp +++ /dev/null @@ -1,149 +0,0 @@ -# Microsoft Developer Studio Project File - Name="view1090" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=view1090 - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "view1090.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "view1090.mak" CFG="view1090 - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "view1090 - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "view1090 - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "view1090 - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c -# ADD CPP /nologo /W3 /GX /O2 /I ".\pthreads\." /I ".\rtlsdr\." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /c -# SUBTRACT CPP /YX /Yc /Yu -# ADD BASE RSC /l 0x809 /d "NDEBUG" -# ADD RSC /l 0x809 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:console /machine:I386 -# SUBTRACT LINK32 /pdb:none - -!ELSEIF "$(CFG)" == "view1090 - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c -# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".\pthreads\." /I ".\rtlsdr\." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c -# SUBTRACT CPP /YX /Yc /Yu -# ADD BASE RSC /l 0x809 /d "_DEBUG" -# ADD RSC /l 0x809 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept - -!ENDIF - -# Begin Target - -# Name "view1090 - Win32 Release" -# Name "view1090 - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\anet.c -# End Source File -# Begin Source File - -SOURCE=.\interactive.c -# End Source File -# Begin Source File - -SOURCE=.\mode_ac.c -# End Source File -# Begin Source File - -SOURCE=.\mode_s.c -# End Source File -# Begin Source File - -SOURCE=.\net_io.c -# End Source File -# Begin Source File - -SOURCE=.\view1090.c -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\dump1090.h -# End Source File -# Begin Source File - -SOURCE=.\view1090.h -# End Source File -# Begin Source File - -SOURCE=.\winstubs.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# End Group -# Begin Group "Library Files" - -# PROP Default_Filter "" -# Begin Source File - -SOURCE=.\pthreads\pthreadVC2.lib -# End Source File -# Begin Source File - -SOURCE=.\rtlsdr\rtlsdr.lib -# End Source File -# End Group -# End Target -# End Project diff --git a/winstubs.h b/winstubs.h deleted file mode 100644 index f3ab63fe7..000000000 --- a/winstubs.h +++ /dev/null @@ -1,111 +0,0 @@ -// dump1090, a Mode S messages decoder for RTLSDR devices. -// -// Copyright (C) 2014 by Malcolm Robb -// -// 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. -// -// This file provides basic Windows implementation of Linux specific functions -// used in the dump1090 project. This allows dump1090 to be compiled and debugged -// using Microsoft Visual C++ 6.0 -// -// Note that not all functions actually provide equivalent functionality to their -// Linux equivalents. They are simply stubs to allow the project to compile. -// -#ifndef __WINSTUBS_H -#define __WINSTUBS_H - -#include -#include -#include - -typedef UCHAR uint8_t; -typedef USHORT uint16_t; -typedef UINT32 uint32_t; -typedef UINT64 uint64_t; -typedef UINT32 mode_t; -typedef long ssize_t; -typedef int socklen_t; - -#include -#include -#include -#include -#include -#include -#include - -#define M_PI 3.14159265358979323846 -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -//Functions not included in the MSVC maths library. This will do for our use. -_inline double round(double d) {return floor(d + 0.5);} -_inline double trunc(double d) {return (d>0) ? floor(d):ceil(d) ;} - -//usleep works in microseconds, and isn't supported in Windows. This will do for our use. -_inline void usleep(UINT32 ulSleep) {Sleep(ulSleep/1000);} -_inline uint64_t strtoll(const char *p, void *e, UINT32 base) {return _atoi64(p);} -_inline int inet_aton(const char * cp, DWORD * ulAddr) { *ulAddr = inet_addr(cp); return (INADDR_NONE != *ulAddr);} -#define snprintf _snprintf -#define vsnprintf _vsnprintf -#define realpath(N,R) _fullpath((R),(N),_MAX_PATH) - -_inline void cls() { - HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); - COORD coord = {0, 0}; - DWORD count; - - CONSOLE_SCREEN_BUFFER_INFO csbi; - GetConsoleScreenBufferInfo(hStdOut, &csbi); - - FillConsoleOutputCharacter(hStdOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, coord, &count); - - SetConsoleCursorPosition(hStdOut, coord); -} - -_inline int gettimeofday(struct timeval *tv, struct timezone *tz) { - SYSTEMTIME stSystemTime; - GetLocalTime(&stSystemTime); - - tv->tv_sec = stSystemTime.wSecond + (60 * (stSystemTime.wMinute + (60 * stSystemTime.wHour))); - tv->tv_usec = stSystemTime.wMilliseconds * 1000; - - return 0; - } - -#define STDIN_FILENO 0 -#define EINPROGRESS WSAEINPROGRESS -#define EWOULDBLOCK WSAEWOULDBLOCK - -#ifdef __cplusplus -} -#endif - -#endif // __WINSTUBS_H From fc45eded0b9f3f002245f1e76d9dd00ec9f514bc Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Dec 2014 20:16:56 +0000 Subject: [PATCH 074/610] Removing COAA/PlanePlotter stuff (+ binary-only object file) that I can't support or test. --- coaa.h | 6 -- coaa1090.obj | Bin 36956 -> 0 bytes dump1090.c | 1 - makeppup1090 | 27 ------ ppup1090.c | 261 --------------------------------------------------- ppup1090.h | 110 ---------------------- ppup1090.sh | 85 ----------------- view1090.c | 1 - 8 files changed, 491 deletions(-) delete mode 100644 coaa.h delete mode 100644 coaa1090.obj delete mode 100644 makeppup1090 delete mode 100644 ppup1090.c delete mode 100644 ppup1090.h delete mode 100644 ppup1090.sh diff --git a/coaa.h b/coaa.h deleted file mode 100644 index 3d0770442..000000000 --- a/coaa.h +++ /dev/null @@ -1,6 +0,0 @@ -// coaa.h configuration file for Plane Plotter Uploader -// -// You MUST apply via the COAA website for your own personal version of this file -// Do not disclose the contents of this file to anyone thereafter as it uniquely -// identifies you to the PlanePlotter system -// diff --git a/coaa1090.obj b/coaa1090.obj deleted file mode 100644 index ea54fdef29f4353db8b144b17d44db0ff1a7324a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36956 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 -// -// 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 "coaa.h" -#include "ppup1090.h" -// -// ============================= Utility functions ========================== -// -void sigintHandler(int dummy) { - NOTUSED(dummy); - signal(SIGINT, SIG_DFL); // reset signal handler - bit extra safety - Modes.exit = 1; // Signal to threads that we are done -} -// -// =============================== Initialization =========================== -// -void ppup1090InitConfig(void) { - - int iErr; - - // Default everything to zero/NULL - memset(&Modes, 0, sizeof(Modes)); - memset(&ppup1090, 0, sizeof(ppup1090)); - - // Now initialise things that should not be 0/NULL to their defaults - Modes.check_crc = 1; - Modes.quiet = 1; - Modes.bEnableDFLogging = 1; - strcpy(ppup1090.net_input_beast_ipaddr,PPUP1090_NET_OUTPUT_IP_ADDRESS); - Modes.net_input_beast_port = MODES_NET_OUTPUT_BEAST_PORT; - Modes.interactive_delete_ttl = MODES_INTERACTIVE_DELETE_TTL; - Modes.interactive_display_ttl = MODES_INTERACTIVE_DISPLAY_TTL; - Modes.fUserLat = MODES_USER_LATITUDE_DFLT; - Modes.fUserLon = MODES_USER_LONGITUDE_DFLT; - - if ((iErr = openCOAA())) - { - fprintf(stderr, "Error 0x%X initialising uploader\n", iErr); - exit(1); - } -} -// -//========================================================================= -// -void ppup1090Init(void) { - - int iErr; - - pthread_mutex_init(&Modes.pDF_mutex,NULL); - pthread_mutex_init(&Modes.data_mutex,NULL); - pthread_cond_init(&Modes.data_cond,NULL); - - // Allocate the various buffers used by Modes - if ( NULL == (Modes.icao_cache = (uint32_t *) malloc(sizeof(uint32_t) * MODES_ICAO_CACHE_LEN * 2))) - { - fprintf(stderr, "Out of memory allocating data buffer.\n"); - exit(1); - } - - // Clear the buffers that have just been allocated, just in-case - memset(Modes.icao_cache, 0, sizeof(uint32_t) * MODES_ICAO_CACHE_LEN * 2); - - // Validate the users Lat/Lon home location inputs - if ( (Modes.fUserLat > 90.0) // Latitude must be -90 to +90 - || (Modes.fUserLat < -90.0) // and - || (Modes.fUserLon > 360.0) // Longitude must be -180 to +360 - || (Modes.fUserLon < -180.0) ) { - Modes.fUserLat = Modes.fUserLon = 0.0; - } else if (Modes.fUserLon > 180.0) { // If Longitude is +180 to +360, make it -180 to 0 - Modes.fUserLon -= 360.0; - } - // If both Lat and Lon are 0.0 then the users location is either invalid/not-set, or (s)he's in the - // Atlantic ocean off the west coast of Africa. This is unlikely to be correct. - // Set the user LatLon valid flag only if either Lat or Lon are non zero. Note the Greenwich meridian - // is at 0.0 Lon,so we must check for either fLat or fLon being non zero not both. - // Testing the flag at runtime will be much quicker than ((fLon != 0.0) || (fLat != 0.0)) - Modes.bUserFlags &= ~MODES_USER_LATLON_VALID; - if ((Modes.fUserLat != 0.0) || (Modes.fUserLon != 0.0)) { - Modes.bUserFlags |= MODES_USER_LATLON_VALID; - } - - // Prepare error correction tables - modesInitErrorInfo(); - - // Setup the uploader - read the user paramaters from the coaa.h header file - coaa1090.ppIPAddr = ppup1090.net_pp_ipaddr; - coaa1090.fUserLat = MODES_USER_LATITUDE_DFLT; - coaa1090.fUserLon = MODES_USER_LONGITUDE_DFLT; - strcpy(coaa1090.strAuthCode,STR(USER_AUTHCODE)); - strcpy(coaa1090.strRegNo, STR(USER_REGNO)); - strcpy(coaa1090.strVersion, MODES_DUMP1090_VERSION); - - if ((iErr = initCOAA (coaa1090))) - { - fprintf(stderr, "Error 0x%X initialising uploader\n", iErr); - exit(1); - } -} -// -// ================================ Main ==================================== -// -void showHelp(void) { - printf( -"-----------------------------------------------------------------------------\n" -"| ppup1090 RPi Uploader for COAA Planeplotter Ver : "MODES_DUMP1090_VERSION " |\n" -"-----------------------------------------------------------------------------\n" - "--net-bo-ipaddr TCP Beast output listen IPv4 (default: 127.0.0.1)\n" - "--net-bo-port TCP Beast output listen port (default: 30005)\n" - "--net-pp-ipaddr Plane Plotter LAN IPv4 Address (default: 0.0.0.0)\n" - "--quiet Disable output to stdout. Use for daemon applications\n" - "--help Show this help\n" - ); -} - -#ifdef _WIN32 -void showCopyright(void) { - uint64_t llTime = time(NULL) + 1; - - printf( -"-----------------------------------------------------------------------------\n" -"| ppup1090 RPi Uploader for COAA Planeplotter Ver : "MODES_DUMP1090_VERSION " |\n" -"-----------------------------------------------------------------------------\n" -"\n" -" Copyright (C) 2012 by Salvatore Sanfilippo \n" -" Copyright (C) 2014 by Malcolm Robb \n" -"\n" -" All rights reserved.\n" -"\n" -" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" -" ""AS IS"" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" -" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" -" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" -" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" -" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" -" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" -" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" -" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" -" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" -" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" -"\n" -" For further details refer to \n" -"\n" - ); - - // delay for 1 second to give the user a chance to read the copyright - while (llTime >= time(NULL)) {} -} -#endif -// -//========================================================================= -// -int main(int argc, char **argv) { - int j, fd; - struct client *c; - - // Set sane defaults - - ppup1090InitConfig(); - signal(SIGINT, sigintHandler); // Define Ctrl/C handler (exit program) - - // Parse the command line options - for (j = 1; j < argc; j++) { - int more = ((j + 1) < argc); // There are more arguments - - if (!strcmp(argv[j],"--net-bo-port") && more) { - Modes.net_input_beast_port = atoi(argv[++j]); - } else if (!strcmp(argv[j],"--net-bo-ipaddr") && more) { - strcpy(ppup1090.net_input_beast_ipaddr, argv[++j]); - } else if (!strcmp(argv[j],"--net-pp-ipaddr") && more) { - inet_aton(argv[++j], (void *)&ppup1090.net_pp_ipaddr); - } else if (!strcmp(argv[j],"--quiet")) { - ppup1090.quiet = 1; - } else if (!strcmp(argv[j],"--help")) { - showHelp(); - exit(0); - } else { - fprintf(stderr, "Unknown or not enough arguments for option '%s'.\n\n", argv[j]); - showHelp(); - exit(1); - } - } - -#ifdef _WIN32 - // Try to comply with the Copyright license conditions for binary distribution - if (!ppup1090.quiet) {showCopyright();} -#endif - - // Initialization - ppup1090Init(); - - // Try to connect to the selected ip address and port. We only support *ONE* input connection which we initiate.here. - if ((fd = anetTcpConnect(Modes.aneterr, ppup1090.net_input_beast_ipaddr, Modes.net_input_beast_port)) == ANET_ERR) { - fprintf(stderr, "Failed to connect to %s:%d\n", ppup1090.net_input_beast_ipaddr, Modes.net_input_beast_port); - exit(1); - } - // - // Setup a service callback client structure for a beast binary input (from dump1090) - // This is a bit dodgy under Windows. The fd parameter is a handle to the internet - // socket on which we are receiving data. Under Linux, these seem to start at 0 and - // count upwards. However, Windows uses "HANDLES" and these don't nececeriy start at 0. - // dump1090 limits fd to values less than 1024, and then uses the fd parameter to - // index into an array of clients. This is ok-ish if handles are allocated up from 0. - // However, there is no gaurantee that Windows will behave like this, and if Windows - // allocates a handle greater than 1024, then dump1090 won't like it. On my test machine, - // the first Windows handle is usually in the 0x54 (84 decimal) region. - - c = (struct client *) malloc(sizeof(*c)); - c->next = NULL; - c->buflen = 0; - c->fd = - c->service = - Modes.bis = fd; - Modes.clients = c; - - // Keep going till the user does something that stops us - while (!Modes.exit) { - modesReadFromClient(c,"",decodeBinMessage); - interactiveRemoveStaleAircrafts(); - postCOAA (); - } - - // The user has stopped us, so close any socket we opened - if (fd != ANET_ERR) - {close(fd);} - - closeCOAA (); -#ifndef _WIN32 - pthread_exit(0); -#else - return (0); -#endif -} -// -//========================================================================= -// diff --git a/ppup1090.h b/ppup1090.h deleted file mode 100644 index 6d0756d8f..000000000 --- a/ppup1090.h +++ /dev/null @@ -1,110 +0,0 @@ -// ppup1090, a Mode S PlanePlotter Uploader for dump1090 devices. -// -// Copyright (C) 2013 by Malcolm Robb -// -// 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 __PPUP1090_H -#define __PPUP1090_H - -// ============================= Include files ========================== - -#include "dump1090.h" - -#ifndef _WIN32 - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include "rtl-sdr.h" - #include "anet.h" - #include -#else - #include "winstubs.h" //Put everything Windows specific in here -#endif - -// ============================= #defines =============================== - -#define PPUP1090_NET_OUTPUT_IP_ADDRESS "127.0.0.1" - -#define NOTUSED(V) ((void) V) - -#define STR_HELPER(x) #x -#define STR(x) STR_HELPER(x) - -// ======================== structure declarations ======================== - -// Program global state -struct { // Internal state - int quiet; - // Networking - uint32_t net_pp_ipaddr; // IPv4 address of PP instance - char net_input_beast_ipaddr[32]; // IPv4 address or network name of server/RPi -} ppup1090; - - -// COAA Initialisation structure -struct _coaa1090 { - uint32_t ppIPAddr; - double fUserLat; - double fUserLon; - char strAuthCode[16]; - char strRegNo[16]; - char strVersion[16]; -} coaa1090; - -// ======================== function declarations ========================= - -#ifdef __cplusplus -extern "C" { -#endif - -// -// Functions exported from coaa1090.c -// -int openCOAA (void); -int closeCOAA (void); -int initCOAA (struct _coaa1090 coaa1090); -void postCOAA (void); - -#ifdef __cplusplus -} -#endif - -#endif // __PPUP1090_H diff --git a/ppup1090.sh b/ppup1090.sh deleted file mode 100644 index 8ed4ad3f3..000000000 --- a/ppup1090.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/bin/bash -### BEGIN INIT INFO -# -# Provides: dump1090 -# Required-Start: $remote_fs -# Required-Stop: $remote_fs -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: dump1090 initscript - -# -### END INIT INFO -## Fill in name of program here. -PROG="dump1090" -PROG_PATH="/home/pi/dump1090" -PROG_ARGS="--quiet --net --net-ro-size 500 --net-ro-rate 5 --net-buffer 5" -PIDFILE="/var/run/dump1090.pid" -PROG2="ppup1090" -PROG2_ARGS="--quiet --net-pp-addr 192.168.1.64" -PIDFILE2="/var/run/$PROG2.pid" -DELAY=5 - -start() { - if [ -e $PIDFILE ]; then - ## Program is running, exit with error. - echo "Error! $PROG is currently running!" 1>&2 - exit 1 - else - ## Change from /dev/null to something like /var/log/$PROG if you want to save output. - cd $PROG_PATH - ./$PROG $PROG_ARGS 2>&1 >/dev/null & - echo "$PROG started, waiting $DELAY seconds" - touch $PIDFILE - sleep $DELAY - echo "Attempting to start $PROG2.." - ./$PROG2 $PROG2_ARGS 2>1 >/dev/null & - echo "$PROG2 started" - touch $PIDFILE2 - fi -} - -stop() { - if [ -e $PIDFILE ]; then - ## Program is running, so stop it - echo "$PROG is running" - killall $PROG2 - killall $PROG - rm -f $PIDFILE2 - rm -f $PIDFILE - echo "$PROG stopped" - else - ## Program is not running, exit with error. - echo "Error! $PROG not started!" 1>&2 - exit 1 - fi -} - -## Check to see if we are running as root first. -## Found at http://www.cyberciti.biz/tips/shell-root-user-check-script.html -if [ "$(id -u)" != "0" ]; then - echo "This script must be run as root" 1>&2 - exit 1 -fi - -case "$1" in - start) - start - exit 0 - ;; - stop) - stop - exit 0 - ;; - reload|restart|force-reload) - stop - start - exit 0 - ;; - **) - echo "Usage: $0 {start|stop|reload}" 1>&2 - exit 1 - ;; -esac -# - diff --git a/view1090.c b/view1090.c index d0fc39032..1e2bb073a 100644 --- a/view1090.c +++ b/view1090.c @@ -27,7 +27,6 @@ // (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 "coaa.h" #include "view1090.h" // // ============================= Utility functions ========================== From 63303b4d073bd2a8a59913cbdeb783be75b4da4e Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Dec 2014 20:17:53 +0000 Subject: [PATCH 075/610] Remove legacy makefiles / startup scripts that are unused in the Debian package. --- dump1090.sh | 74 ---------------------------------------------------- makedump1090 | 27 ------------------- makeview1090 | 27 ------------------- 3 files changed, 128 deletions(-) delete mode 100644 dump1090.sh delete mode 100644 makedump1090 delete mode 100644 makeview1090 diff --git a/dump1090.sh b/dump1090.sh deleted file mode 100644 index d1b63e515..000000000 --- a/dump1090.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash -### BEGIN INIT INFO -# -# Provides: dump1090 -# Required-Start: $remote_fs -# Required-Stop: $remote_fs -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: dump1090 initscript - -# -### END INIT INFO -## Fill in name of program here. -PROG="dump1090" -PROG_PATH="/home/pi/dump1090" -PROG_ARGS="--quiet --net --net-ro-size 500 --net-ro-rate 5 --net-buffer 5" -PIDFILE="/var/run/dump1090.pid" - -start() { - if [ -e $PIDFILE ]; then - ## Program is running, exit with error. - echo "Error! $PROG is currently running!" 1>&2 - exit 1 - else - ## Change from /dev/null to something like /var/log/$PROG if you want to save output. - cd $PROG_PATH - ./$PROG $PROG_ARGS 2>&1 >/dev/null & - echo "$PROG started" - touch $PIDFILE - fi -} - -stop() { - if [ -e $PIDFILE ]; then - ## Program is running, so stop it - echo "$PROG is running" - killall $PROG - rm -f $PIDFILE - echo "$PROG stopped" - else - ## Program is not running, exit with error. - echo "Error! $PROG not started!" 1>&2 - exit 1 - fi -} - -## Check to see if we are running as root first. -## Found at http://www.cyberciti.biz/tips/shell-root-user-check-script.html -if [ "$(id -u)" != "0" ]; then - echo "This script must be run as root" 1>&2 - exit 1 -fi - -case "$1" in - start) - start - exit 0 - ;; - stop) - stop - exit 0 - ;; - reload|restart|force-reload) - stop - start - exit 0 - ;; - **) - echo "Usage: $0 {start|stop|reload}" 1>&2 - exit 1 - ;; -esac -# - diff --git a/makedump1090 b/makedump1090 deleted file mode 100644 index 4d3fccbba..000000000 --- a/makedump1090 +++ /dev/null @@ -1,27 +0,0 @@ -# -# When building a package or installing otherwise in the system, make -# sure that the variable PREFIX is defined, e.g. make PREFIX=/usr/local -# -PROGNAME=dump1090 - -ifdef PREFIX -BINDIR=$(PREFIX)/bin -SHAREDIR=$(PREFIX)/share/$(PROGNAME) -EXTRACFLAGS=-DHTMLPATH=\"$(SHAREDIR)\" -endif - -CFLAGS=-O2 -g -Wall -W `pkg-config --cflags librtlsdr` -LIBS=`pkg-config --libs librtlsdr` -lpthread -lm -CC=gcc - - -all: dump1090 - -%.o: %.c - $(CC) $(CFLAGS) $(EXTRACFLAGS) -c $< - -dump1090: dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o - $(CC) -g -o dump1090 dump1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o $(LIBS) $(LDFLAGS) - -clean: - rm -f *.o dump1090 diff --git a/makeview1090 b/makeview1090 deleted file mode 100644 index 6cb76af58..000000000 --- a/makeview1090 +++ /dev/null @@ -1,27 +0,0 @@ -# -# When building a package or installing otherwise in the system, make -# sure that the variable PREFIX is defined, e.g. make PREFIX=/usr/local -# -PROGNAME=view1090 - -ifdef PREFIX -BINDIR=$(PREFIX)/bin -SHAREDIR=$(PREFIX)/share/$(PROGNAME) -EXTRACFLAGS=-DHTMLPATH=\"$(SHAREDIR)\" -endif - -CFLAGS=-O2 -g -Wall -W `pkg-config --cflags librtlsdr` -LIBS=`pkg-config --libs librtlsdr` -lpthread -lm -CC=gcc - - -all: view1090 - -%.o: %.c - $(CC) $(CFLAGS) $(EXTRACFLAGS) -c $< - -view1090: view1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o - $(CC) -g -o view1090 view1090.o anet.o interactive.o mode_ac.o mode_s.o net_io.o $(LIBS) $(LDFLAGS) - -clean: - rm -f *.o view1090 From 4cc009478101d66de5939900882106fcf11d5864 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Dec 2014 20:19:05 +0000 Subject: [PATCH 076/610] Remove copies of headers etc that were only needed by Windows builds. --- pthreads/pthread.h | 1368 --------------------------------------- pthreads/sched.h | 183 ------ pthreads/semaphore.h | 169 ----- rtlsdr/rtl-sdr.h | 366 ----------- rtlsdr/rtl-sdr_export.h | 47 -- 5 files changed, 2133 deletions(-) delete mode 100644 pthreads/pthread.h delete mode 100644 pthreads/sched.h delete mode 100644 pthreads/semaphore.h delete mode 100644 rtlsdr/rtl-sdr.h delete mode 100644 rtlsdr/rtl-sdr_export.h diff --git a/pthreads/pthread.h b/pthreads/pthread.h deleted file mode 100644 index b4072f72c..000000000 --- a/pthreads/pthread.h +++ /dev/null @@ -1,1368 +0,0 @@ -/* This is an implementation of the threads API of POSIX 1003.1-2001. - * - * -------------------------------------------------------------------------- - * - * Pthreads-win32 - POSIX Threads Library for Win32 - * Copyright(C) 1998 John E. Bossom - * Copyright(C) 1999,2005 Pthreads-win32 contributors - * - * Contact Email: rpj@callisto.canberra.edu.au - * - * The current list of contributors is contained - * in the file CONTRIBUTORS included with the source - * code distribution. The list can also be seen at the - * following World Wide Web location: - * http://sources.redhat.com/pthreads-win32/contributors.html - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library in the file COPYING.LIB; - * if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - -#if !defined( PTHREAD_H ) -#define PTHREAD_H - -/* - * See the README file for an explanation of the pthreads-win32 version - * numbering scheme and how the DLL is named etc. - */ -#define PTW32_VERSION 2,9,1,0 -#define PTW32_VERSION_STRING "2, 9, 1, 0\0" - -/* There are three implementations of cancel cleanup. - * Note that pthread.h is included in both application - * compilation units and also internally for the library. - * The code here and within the library aims to work - * for all reasonable combinations of environments. - * - * The three implementations are: - * - * WIN32 SEH - * C - * C++ - * - * Please note that exiting a push/pop block via - * "return", "exit", "break", or "continue" will - * lead to different behaviour amongst applications - * depending upon whether the library was built - * using SEH, C++, or C. For example, a library built - * with SEH will call the cleanup routine, while both - * C++ and C built versions will not. - */ - -/* - * Define defaults for cleanup code. - * Note: Unless the build explicitly defines one of the following, then - * we default to standard C style cleanup. This style uses setjmp/longjmp - * in the cancelation and thread exit implementations and therefore won't - * do stack unwinding if linked to applications that have it (e.g. - * C++ apps). This is currently consistent with most/all commercial Unix - * POSIX threads implementations. - */ -#if !defined( __CLEANUP_SEH ) && !defined( __CLEANUP_CXX ) && !defined( __CLEANUP_C ) -# define __CLEANUP_C -#endif - -#if defined( __CLEANUP_SEH ) && ( !defined( _MSC_VER ) && !defined(PTW32_RC_MSC)) -#error ERROR [__FILE__, line __LINE__]: SEH is not supported for this compiler. -#endif - -/* - * Stop here if we are being included by the resource compiler. - */ -#if !defined(RC_INVOKED) - -#undef PTW32_LEVEL - -#if defined(_POSIX_SOURCE) -#define PTW32_LEVEL 0 -/* Early POSIX */ -#endif - -#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309 -#undef PTW32_LEVEL -#define PTW32_LEVEL 1 -/* Include 1b, 1c and 1d */ -#endif - -#if defined(INCLUDE_NP) -#undef PTW32_LEVEL -#define PTW32_LEVEL 2 -/* Include Non-Portable extensions */ -#endif - -#define PTW32_LEVEL_MAX 3 - -#if ( defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112 ) || !defined(PTW32_LEVEL) -#define PTW32_LEVEL PTW32_LEVEL_MAX -/* Include everything */ -#endif - -#if defined(_UWIN) -# define HAVE_STRUCT_TIMESPEC 1 -# define HAVE_SIGNAL_H 1 -# undef HAVE_PTW32_CONFIG_H -# pragma comment(lib, "pthread") -#endif - -/* - * ------------------------------------------------------------- - * - * - * Module: pthread.h - * - * Purpose: - * Provides an implementation of PThreads based upon the - * standard: - * - * POSIX 1003.1-2001 - * and - * The Single Unix Specification version 3 - * - * (these two are equivalent) - * - * in order to enhance code portability between Windows, - * various commercial Unix implementations, and Linux. - * - * See the ANNOUNCE file for a full list of conforming - * routines and defined constants, and a list of missing - * routines and constants not defined in this implementation. - * - * Authors: - * There have been many contributors to this library. - * The initial implementation was contributed by - * John Bossom, and several others have provided major - * sections or revisions of parts of the implementation. - * Often significant effort has been contributed to - * find and fix important bugs and other problems to - * improve the reliability of the library, which sometimes - * is not reflected in the amount of code which changed as - * result. - * As much as possible, the contributors are acknowledged - * in the ChangeLog file in the source code distribution - * where their changes are noted in detail. - * - * Contributors are listed in the CONTRIBUTORS file. - * - * As usual, all bouquets go to the contributors, and all - * brickbats go to the project maintainer. - * - * Maintainer: - * The code base for this project is coordinated and - * eventually pre-tested, packaged, and made available by - * - * Ross Johnson - * - * QA Testers: - * Ultimately, the library is tested in the real world by - * a host of competent and demanding scientists and - * engineers who report bugs and/or provide solutions - * which are then fixed or incorporated into subsequent - * versions of the library. Each time a bug is fixed, a - * test case is written to prove the fix and ensure - * that later changes to the code don't reintroduce the - * same error. The number of test cases is slowly growing - * and therefore so is the code reliability. - * - * Compliance: - * See the file ANNOUNCE for the list of implemented - * and not-implemented routines and defined options. - * Of course, these are all defined is this file as well. - * - * Web site: - * The source code and other information about this library - * are available from - * - * http://sources.redhat.com/pthreads-win32/ - * - * ------------------------------------------------------------- - */ - -/* Try to avoid including windows.h */ -#if (defined(__MINGW64__) || defined(__MINGW32__)) && defined(__cplusplus) -#define PTW32_INCLUDE_WINDOWS_H -#endif - -#if defined(PTW32_INCLUDE_WINDOWS_H) -#include -#endif - -#if defined(_MSC_VER) && _MSC_VER < 1300 || defined(__DMC__) -/* - * VC++6.0 or early compiler's header has no DWORD_PTR type. - */ -typedef unsigned long DWORD_PTR; -typedef unsigned long ULONG_PTR; -#endif -/* - * ----------------- - * autoconf switches - * ----------------- - */ - -#if defined(HAVE_PTW32_CONFIG_H) -#include "config.h" -#endif /* HAVE_PTW32_CONFIG_H */ - -#if !defined(NEED_FTIME) -#include -#else /* NEED_FTIME */ -/* use native WIN32 time API */ -#endif /* NEED_FTIME */ - -#if defined(HAVE_SIGNAL_H) -#include -#endif /* HAVE_SIGNAL_H */ - -#include - -/* - * Boolean values to make us independent of system includes. - */ -enum { - PTW32_FALSE = 0, - PTW32_TRUE = (! PTW32_FALSE) -}; - -/* - * This is a duplicate of what is in the autoconf config.h, - * which is only used when building the pthread-win32 libraries. - */ - -#if !defined(PTW32_CONFIG_H) -# if defined(WINCE) -# define NEED_ERRNO -# define NEED_SEM -# endif -# if defined(__MINGW64__) -# define HAVE_STRUCT_TIMESPEC -# define HAVE_MODE_T -# elif defined(_UWIN) || defined(__MINGW32__) -# define HAVE_MODE_T -# endif -#endif - -/* - * - */ - -#if PTW32_LEVEL >= PTW32_LEVEL_MAX -#if defined(NEED_ERRNO) -#include "need_errno.h" -#else -#include -#endif -#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ - -/* - * Several systems don't define some error numbers. - */ -#if !defined(ENOTSUP) -# define ENOTSUP 48 /* This is the value in Solaris. */ -#endif - -#if !defined(ETIMEDOUT) -# define ETIMEDOUT 10060 /* Same as WSAETIMEDOUT */ -#endif - -#if !defined(ENOSYS) -# define ENOSYS 140 /* Semi-arbitrary value */ -#endif - -#if !defined(EDEADLK) -# if defined(EDEADLOCK) -# define EDEADLK EDEADLOCK -# else -# define EDEADLK 36 /* This is the value in MSVC. */ -# endif -#endif - -/* POSIX 2008 - related to robust mutexes */ -#if !defined(EOWNERDEAD) -# define EOWNERDEAD 43 -#endif -#if !defined(ENOTRECOVERABLE) -# define ENOTRECOVERABLE 44 -#endif - -#include - -/* - * To avoid including windows.h we define only those things that we - * actually need from it. - */ -#if !defined(PTW32_INCLUDE_WINDOWS_H) -#if !defined(HANDLE) -# define PTW32__HANDLE_DEF -# define HANDLE void * -#endif -#if !defined(DWORD) -# define PTW32__DWORD_DEF -# define DWORD unsigned long -#endif -#endif - -#if !defined(HAVE_STRUCT_TIMESPEC) -#define HAVE_STRUCT_TIMESPEC -#if !defined(_TIMESPEC_DEFINED) -#define _TIMESPEC_DEFINED -struct timespec { - time_t tv_sec; - long tv_nsec; -}; -#endif /* _TIMESPEC_DEFINED */ -#endif /* HAVE_STRUCT_TIMESPEC */ - -#if !defined(SIG_BLOCK) -#define SIG_BLOCK 0 -#endif /* SIG_BLOCK */ - -#if !defined(SIG_UNBLOCK) -#define SIG_UNBLOCK 1 -#endif /* SIG_UNBLOCK */ - -#if !defined(SIG_SETMASK) -#define SIG_SETMASK 2 -#endif /* SIG_SETMASK */ - -#if defined(__cplusplus) -extern "C" -{ -#endif /* __cplusplus */ - -/* - * ------------------------------------------------------------- - * - * POSIX 1003.1-2001 Options - * ========================= - * - * Options are normally set in , which is not provided - * with pthreads-win32. - * - * For conformance with the Single Unix Specification (version 3), all of the - * options below are defined, and have a value of either -1 (not supported) - * or 200112L (supported). - * - * These options can neither be left undefined nor have a value of 0, because - * either indicates that sysconf(), which is not implemented, may be used at - * runtime to check the status of the option. - * - * _POSIX_THREADS (== 200112L) - * If == 200112L, you can use threads - * - * _POSIX_THREAD_ATTR_STACKSIZE (== 200112L) - * If == 200112L, you can control the size of a thread's - * stack - * pthread_attr_getstacksize - * pthread_attr_setstacksize - * - * _POSIX_THREAD_ATTR_STACKADDR (== -1) - * If == 200112L, you can allocate and control a thread's - * stack. If not supported, the following functions - * will return ENOSYS, indicating they are not - * supported: - * pthread_attr_getstackaddr - * pthread_attr_setstackaddr - * - * _POSIX_THREAD_PRIORITY_SCHEDULING (== -1) - * If == 200112L, you can use realtime scheduling. - * This option indicates that the behaviour of some - * implemented functions conforms to the additional TPS - * requirements in the standard. E.g. rwlocks favour - * writers over readers when threads have equal priority. - * - * _POSIX_THREAD_PRIO_INHERIT (== -1) - * If == 200112L, you can create priority inheritance - * mutexes. - * pthread_mutexattr_getprotocol + - * pthread_mutexattr_setprotocol + - * - * _POSIX_THREAD_PRIO_PROTECT (== -1) - * If == 200112L, you can create priority ceiling mutexes - * Indicates the availability of: - * pthread_mutex_getprioceiling - * pthread_mutex_setprioceiling - * pthread_mutexattr_getprioceiling - * pthread_mutexattr_getprotocol + - * pthread_mutexattr_setprioceiling - * pthread_mutexattr_setprotocol + - * - * _POSIX_THREAD_PROCESS_SHARED (== -1) - * If set, you can create mutexes and condition - * variables that can be shared with another - * process.If set, indicates the availability - * of: - * pthread_mutexattr_getpshared - * pthread_mutexattr_setpshared - * pthread_condattr_getpshared - * pthread_condattr_setpshared - * - * _POSIX_THREAD_SAFE_FUNCTIONS (== 200112L) - * If == 200112L you can use the special *_r library - * functions that provide thread-safe behaviour - * - * _POSIX_READER_WRITER_LOCKS (== 200112L) - * If == 200112L, you can use read/write locks - * - * _POSIX_SPIN_LOCKS (== 200112L) - * If == 200112L, you can use spin locks - * - * _POSIX_BARRIERS (== 200112L) - * If == 200112L, you can use barriers - * - * + These functions provide both 'inherit' and/or - * 'protect' protocol, based upon these macro - * settings. - * - * ------------------------------------------------------------- - */ - -/* - * POSIX Options - */ -#undef _POSIX_THREADS -#define _POSIX_THREADS 200809L - -#undef _POSIX_READER_WRITER_LOCKS -#define _POSIX_READER_WRITER_LOCKS 200809L - -#undef _POSIX_SPIN_LOCKS -#define _POSIX_SPIN_LOCKS 200809L - -#undef _POSIX_BARRIERS -#define _POSIX_BARRIERS 200809L - -#undef _POSIX_THREAD_SAFE_FUNCTIONS -#define _POSIX_THREAD_SAFE_FUNCTIONS 200809L - -#undef _POSIX_THREAD_ATTR_STACKSIZE -#define _POSIX_THREAD_ATTR_STACKSIZE 200809L - -/* - * The following options are not supported - */ -#undef _POSIX_THREAD_ATTR_STACKADDR -#define _POSIX_THREAD_ATTR_STACKADDR -1 - -#undef _POSIX_THREAD_PRIO_INHERIT -#define _POSIX_THREAD_PRIO_INHERIT -1 - -#undef _POSIX_THREAD_PRIO_PROTECT -#define _POSIX_THREAD_PRIO_PROTECT -1 - -/* TPS is not fully supported. */ -#undef _POSIX_THREAD_PRIORITY_SCHEDULING -#define _POSIX_THREAD_PRIORITY_SCHEDULING -1 - -#undef _POSIX_THREAD_PROCESS_SHARED -#define _POSIX_THREAD_PROCESS_SHARED -1 - - -/* - * POSIX 1003.1-2001 Limits - * =========================== - * - * These limits are normally set in , which is not provided with - * pthreads-win32. - * - * PTHREAD_DESTRUCTOR_ITERATIONS - * Maximum number of attempts to destroy - * a thread's thread-specific data on - * termination (must be at least 4) - * - * PTHREAD_KEYS_MAX - * Maximum number of thread-specific data keys - * available per process (must be at least 128) - * - * PTHREAD_STACK_MIN - * Minimum supported stack size for a thread - * - * PTHREAD_THREADS_MAX - * Maximum number of threads supported per - * process (must be at least 64). - * - * SEM_NSEMS_MAX - * The maximum number of semaphores a process can have. - * (must be at least 256) - * - * SEM_VALUE_MAX - * The maximum value a semaphore can have. - * (must be at least 32767) - * - */ -#undef _POSIX_THREAD_DESTRUCTOR_ITERATIONS -#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4 - -#undef PTHREAD_DESTRUCTOR_ITERATIONS -#define PTHREAD_DESTRUCTOR_ITERATIONS _POSIX_THREAD_DESTRUCTOR_ITERATIONS - -#undef _POSIX_THREAD_KEYS_MAX -#define _POSIX_THREAD_KEYS_MAX 128 - -#undef PTHREAD_KEYS_MAX -#define PTHREAD_KEYS_MAX _POSIX_THREAD_KEYS_MAX - -#undef PTHREAD_STACK_MIN -#define PTHREAD_STACK_MIN 0 - -#undef _POSIX_THREAD_THREADS_MAX -#define _POSIX_THREAD_THREADS_MAX 64 - - /* Arbitrary value */ -#undef PTHREAD_THREADS_MAX -#define PTHREAD_THREADS_MAX 2019 - -#undef _POSIX_SEM_NSEMS_MAX -#define _POSIX_SEM_NSEMS_MAX 256 - - /* Arbitrary value */ -#undef SEM_NSEMS_MAX -#define SEM_NSEMS_MAX 1024 - -#undef _POSIX_SEM_VALUE_MAX -#define _POSIX_SEM_VALUE_MAX 32767 - -#undef SEM_VALUE_MAX -#define SEM_VALUE_MAX INT_MAX - - -#if defined(__GNUC__) && !defined(__declspec) -# error Please upgrade your GNU compiler to one that supports __declspec. -#endif - -/* - * When building the library, you should define PTW32_BUILD so that - * the variables/functions are exported correctly. When using the library, - * do NOT define PTW32_BUILD, and then the variables/functions will - * be imported correctly. - */ -#if !defined(PTW32_STATIC_LIB) -# if defined(PTW32_BUILD) -# define PTW32_DLLPORT __declspec (dllexport) -# else -# define PTW32_DLLPORT __declspec (dllimport) -# endif -#else -# define PTW32_DLLPORT -#endif - -/* - * The Open Watcom C/C++ compiler uses a non-standard calling convention - * that passes function args in registers unless __cdecl is explicitly specified - * in exposed function prototypes. - * - * We force all calls to cdecl even though this could slow Watcom code down - * slightly. If you know that the Watcom compiler will be used to build both - * the DLL and application, then you can probably define this as a null string. - * Remember that pthread.h (this file) is used for both the DLL and application builds. - */ -#define PTW32_CDECL __cdecl - -#if defined(_UWIN) && PTW32_LEVEL >= PTW32_LEVEL_MAX -# include -#else -/* - * Generic handle type - intended to extend uniqueness beyond - * that available with a simple pointer. It should scale for either - * IA-32 or IA-64. - */ -typedef struct { - void * p; /* Pointer to actual object */ - unsigned int x; /* Extra information - reuse count etc */ -} ptw32_handle_t; - -typedef ptw32_handle_t pthread_t; -typedef struct pthread_attr_t_ * pthread_attr_t; -typedef struct pthread_once_t_ pthread_once_t; -typedef struct pthread_key_t_ * pthread_key_t; -typedef struct pthread_mutex_t_ * pthread_mutex_t; -typedef struct pthread_mutexattr_t_ * pthread_mutexattr_t; -typedef struct pthread_cond_t_ * pthread_cond_t; -typedef struct pthread_condattr_t_ * pthread_condattr_t; -#endif -typedef struct pthread_rwlock_t_ * pthread_rwlock_t; -typedef struct pthread_rwlockattr_t_ * pthread_rwlockattr_t; -typedef struct pthread_spinlock_t_ * pthread_spinlock_t; -typedef struct pthread_barrier_t_ * pthread_barrier_t; -typedef struct pthread_barrierattr_t_ * pthread_barrierattr_t; - -/* - * ==================== - * ==================== - * POSIX Threads - * ==================== - * ==================== - */ - -enum { -/* - * pthread_attr_{get,set}detachstate - */ - PTHREAD_CREATE_JOINABLE = 0, /* Default */ - PTHREAD_CREATE_DETACHED = 1, - -/* - * pthread_attr_{get,set}inheritsched - */ - PTHREAD_INHERIT_SCHED = 0, - PTHREAD_EXPLICIT_SCHED = 1, /* Default */ - -/* - * pthread_{get,set}scope - */ - PTHREAD_SCOPE_PROCESS = 0, - PTHREAD_SCOPE_SYSTEM = 1, /* Default */ - -/* - * pthread_setcancelstate paramters - */ - PTHREAD_CANCEL_ENABLE = 0, /* Default */ - PTHREAD_CANCEL_DISABLE = 1, - -/* - * pthread_setcanceltype parameters - */ - PTHREAD_CANCEL_ASYNCHRONOUS = 0, - PTHREAD_CANCEL_DEFERRED = 1, /* Default */ - -/* - * pthread_mutexattr_{get,set}pshared - * pthread_condattr_{get,set}pshared - */ - PTHREAD_PROCESS_PRIVATE = 0, - PTHREAD_PROCESS_SHARED = 1, - -/* - * pthread_mutexattr_{get,set}robust - */ - PTHREAD_MUTEX_STALLED = 0, /* Default */ - PTHREAD_MUTEX_ROBUST = 1, - -/* - * pthread_barrier_wait - */ - PTHREAD_BARRIER_SERIAL_THREAD = -1 -}; - -/* - * ==================== - * ==================== - * Cancelation - * ==================== - * ==================== - */ -#define PTHREAD_CANCELED ((void *)(size_t) -1) - - -/* - * ==================== - * ==================== - * Once Key - * ==================== - * ==================== - */ -#define PTHREAD_ONCE_INIT { PTW32_FALSE, 0, 0, 0} - -struct pthread_once_t_ -{ - int done; /* indicates if user function has been executed */ - void * lock; - int reserved1; - int reserved2; -}; - - -/* - * ==================== - * ==================== - * Object initialisers - * ==================== - * ==================== - */ -#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -1) -#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -2) -#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -3) - -/* - * Compatibility with LinuxThreads - */ -#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP PTHREAD_RECURSIVE_MUTEX_INITIALIZER -#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP PTHREAD_ERRORCHECK_MUTEX_INITIALIZER - -#define PTHREAD_COND_INITIALIZER ((pthread_cond_t)(size_t) -1) - -#define PTHREAD_RWLOCK_INITIALIZER ((pthread_rwlock_t)(size_t) -1) - -#define PTHREAD_SPINLOCK_INITIALIZER ((pthread_spinlock_t)(size_t) -1) - - -/* - * Mutex types. - */ -enum -{ - /* Compatibility with LinuxThreads */ - PTHREAD_MUTEX_FAST_NP, - PTHREAD_MUTEX_RECURSIVE_NP, - PTHREAD_MUTEX_ERRORCHECK_NP, - PTHREAD_MUTEX_TIMED_NP = PTHREAD_MUTEX_FAST_NP, - PTHREAD_MUTEX_ADAPTIVE_NP = PTHREAD_MUTEX_FAST_NP, - /* For compatibility with POSIX */ - PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_FAST_NP, - PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP, - PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP, - PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL -}; - - -typedef struct ptw32_cleanup_t ptw32_cleanup_t; - -#if defined(_MSC_VER) -/* Disable MSVC 'anachronism used' warning */ -#pragma warning( disable : 4229 ) -#endif - -typedef void (* PTW32_CDECL ptw32_cleanup_callback_t)(void *); - -#if defined(_MSC_VER) -#pragma warning( default : 4229 ) -#endif - -struct ptw32_cleanup_t -{ - ptw32_cleanup_callback_t routine; - void *arg; - struct ptw32_cleanup_t *prev; -}; - -#if defined(__CLEANUP_SEH) - /* - * WIN32 SEH version of cancel cleanup. - */ - -#define pthread_cleanup_push( _rout, _arg ) \ - { \ - ptw32_cleanup_t _cleanup; \ - \ - _cleanup.routine = (ptw32_cleanup_callback_t)(_rout); \ - _cleanup.arg = (_arg); \ - __try \ - { \ - -#define pthread_cleanup_pop( _execute ) \ - } \ - __finally \ - { \ - if( _execute || AbnormalTermination()) \ - { \ - (*(_cleanup.routine))( _cleanup.arg ); \ - } \ - } \ - } - -#else /* __CLEANUP_SEH */ - -#if defined(__CLEANUP_C) - - /* - * C implementation of PThreads cancel cleanup - */ - -#define pthread_cleanup_push( _rout, _arg ) \ - { \ - ptw32_cleanup_t _cleanup; \ - \ - ptw32_push_cleanup( &_cleanup, (ptw32_cleanup_callback_t) (_rout), (_arg) ); \ - -#define pthread_cleanup_pop( _execute ) \ - (void) ptw32_pop_cleanup( _execute ); \ - } - -#else /* __CLEANUP_C */ - -#if defined(__CLEANUP_CXX) - - /* - * C++ version of cancel cleanup. - * - John E. Bossom. - */ - - class PThreadCleanup { - /* - * PThreadCleanup - * - * Purpose - * This class is a C++ helper class that is - * used to implement pthread_cleanup_push/ - * pthread_cleanup_pop. - * The destructor of this class automatically - * pops the pushed cleanup routine regardless - * of how the code exits the scope - * (i.e. such as by an exception) - */ - ptw32_cleanup_callback_t cleanUpRout; - void * obj; - int executeIt; - - public: - PThreadCleanup() : - cleanUpRout( 0 ), - obj( 0 ), - executeIt( 0 ) - /* - * No cleanup performed - */ - { - } - - PThreadCleanup( - ptw32_cleanup_callback_t routine, - void * arg ) : - cleanUpRout( routine ), - obj( arg ), - executeIt( 1 ) - /* - * Registers a cleanup routine for 'arg' - */ - { - } - - ~PThreadCleanup() - { - if ( executeIt && ((void *) cleanUpRout != (void *) 0) ) - { - (void) (*cleanUpRout)( obj ); - } - } - - void execute( int exec ) - { - executeIt = exec; - } - }; - - /* - * C++ implementation of PThreads cancel cleanup; - * This implementation takes advantage of a helper - * class who's destructor automatically calls the - * cleanup routine if we exit our scope weirdly - */ -#define pthread_cleanup_push( _rout, _arg ) \ - { \ - PThreadCleanup cleanup((ptw32_cleanup_callback_t)(_rout), \ - (void *) (_arg) ); - -#define pthread_cleanup_pop( _execute ) \ - cleanup.execute( _execute ); \ - } - -#else - -#error ERROR [__FILE__, line __LINE__]: Cleanup type undefined. - -#endif /* __CLEANUP_CXX */ - -#endif /* __CLEANUP_C */ - -#endif /* __CLEANUP_SEH */ - -/* - * =============== - * =============== - * Methods - * =============== - * =============== - */ - -/* - * PThread Attribute Functions - */ -PTW32_DLLPORT int PTW32_CDECL pthread_attr_init (pthread_attr_t * attr); - -PTW32_DLLPORT int PTW32_CDECL pthread_attr_destroy (pthread_attr_t * attr); - -PTW32_DLLPORT int PTW32_CDECL pthread_attr_getdetachstate (const pthread_attr_t * attr, - int *detachstate); - -PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstackaddr (const pthread_attr_t * attr, - void **stackaddr); - -PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstacksize (const pthread_attr_t * attr, - size_t * stacksize); - -PTW32_DLLPORT int PTW32_CDECL pthread_attr_setdetachstate (pthread_attr_t * attr, - int detachstate); - -PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstackaddr (pthread_attr_t * attr, - void *stackaddr); - -PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstacksize (pthread_attr_t * attr, - size_t stacksize); - -PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedparam (const pthread_attr_t *attr, - struct sched_param *param); - -PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedparam (pthread_attr_t *attr, - const struct sched_param *param); - -PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedpolicy (pthread_attr_t *, - int); - -PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedpolicy (const pthread_attr_t *, - int *); - -PTW32_DLLPORT int PTW32_CDECL pthread_attr_setinheritsched(pthread_attr_t * attr, - int inheritsched); - -PTW32_DLLPORT int PTW32_CDECL pthread_attr_getinheritsched(const pthread_attr_t * attr, - int * inheritsched); - -PTW32_DLLPORT int PTW32_CDECL pthread_attr_setscope (pthread_attr_t *, - int); - -PTW32_DLLPORT int PTW32_CDECL pthread_attr_getscope (const pthread_attr_t *, - int *); - -/* - * PThread Functions - */ -PTW32_DLLPORT int PTW32_CDECL pthread_create (pthread_t * tid, - const pthread_attr_t * attr, - void *(PTW32_CDECL *start) (void *), - void *arg); - -PTW32_DLLPORT int PTW32_CDECL pthread_detach (pthread_t tid); - -PTW32_DLLPORT int PTW32_CDECL pthread_equal (pthread_t t1, - pthread_t t2); - -PTW32_DLLPORT void PTW32_CDECL pthread_exit (void *value_ptr); - -PTW32_DLLPORT int PTW32_CDECL pthread_join (pthread_t thread, - void **value_ptr); - -PTW32_DLLPORT pthread_t PTW32_CDECL pthread_self (void); - -PTW32_DLLPORT int PTW32_CDECL pthread_cancel (pthread_t thread); - -PTW32_DLLPORT int PTW32_CDECL pthread_setcancelstate (int state, - int *oldstate); - -PTW32_DLLPORT int PTW32_CDECL pthread_setcanceltype (int type, - int *oldtype); - -PTW32_DLLPORT void PTW32_CDECL pthread_testcancel (void); - -PTW32_DLLPORT int PTW32_CDECL pthread_once (pthread_once_t * once_control, - void (PTW32_CDECL *init_routine) (void)); - -#if PTW32_LEVEL >= PTW32_LEVEL_MAX -PTW32_DLLPORT ptw32_cleanup_t * PTW32_CDECL ptw32_pop_cleanup (int execute); - -PTW32_DLLPORT void PTW32_CDECL ptw32_push_cleanup (ptw32_cleanup_t * cleanup, - ptw32_cleanup_callback_t routine, - void *arg); -#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ - -/* - * Thread Specific Data Functions - */ -PTW32_DLLPORT int PTW32_CDECL pthread_key_create (pthread_key_t * key, - void (PTW32_CDECL *destructor) (void *)); - -PTW32_DLLPORT int PTW32_CDECL pthread_key_delete (pthread_key_t key); - -PTW32_DLLPORT int PTW32_CDECL pthread_setspecific (pthread_key_t key, - const void *value); - -PTW32_DLLPORT void * PTW32_CDECL pthread_getspecific (pthread_key_t key); - - -/* - * Mutex Attribute Functions - */ -PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_init (pthread_mutexattr_t * attr); - -PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_destroy (pthread_mutexattr_t * attr); - -PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getpshared (const pthread_mutexattr_t - * attr, - int *pshared); - -PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setpshared (pthread_mutexattr_t * attr, - int pshared); - -PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_settype (pthread_mutexattr_t * attr, int kind); -PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_gettype (const pthread_mutexattr_t * attr, int *kind); - -PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setrobust( - pthread_mutexattr_t *attr, - int robust); -PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getrobust( - const pthread_mutexattr_t * attr, - int * robust); - -/* - * Barrier Attribute Functions - */ -PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_init (pthread_barrierattr_t * attr); - -PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_destroy (pthread_barrierattr_t * attr); - -PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_getpshared (const pthread_barrierattr_t - * attr, - int *pshared); - -PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_setpshared (pthread_barrierattr_t * attr, - int pshared); - -/* - * Mutex Functions - */ -PTW32_DLLPORT int PTW32_CDECL pthread_mutex_init (pthread_mutex_t * mutex, - const pthread_mutexattr_t * attr); - -PTW32_DLLPORT int PTW32_CDECL pthread_mutex_destroy (pthread_mutex_t * mutex); - -PTW32_DLLPORT int PTW32_CDECL pthread_mutex_lock (pthread_mutex_t * mutex); - -PTW32_DLLPORT int PTW32_CDECL pthread_mutex_timedlock(pthread_mutex_t * mutex, - const struct timespec *abstime); - -PTW32_DLLPORT int PTW32_CDECL pthread_mutex_trylock (pthread_mutex_t * mutex); - -PTW32_DLLPORT int PTW32_CDECL pthread_mutex_unlock (pthread_mutex_t * mutex); - -PTW32_DLLPORT int PTW32_CDECL pthread_mutex_consistent (pthread_mutex_t * mutex); - -/* - * Spinlock Functions - */ -PTW32_DLLPORT int PTW32_CDECL pthread_spin_init (pthread_spinlock_t * lock, int pshared); - -PTW32_DLLPORT int PTW32_CDECL pthread_spin_destroy (pthread_spinlock_t * lock); - -PTW32_DLLPORT int PTW32_CDECL pthread_spin_lock (pthread_spinlock_t * lock); - -PTW32_DLLPORT int PTW32_CDECL pthread_spin_trylock (pthread_spinlock_t * lock); - -PTW32_DLLPORT int PTW32_CDECL pthread_spin_unlock (pthread_spinlock_t * lock); - -/* - * Barrier Functions - */ -PTW32_DLLPORT int PTW32_CDECL pthread_barrier_init (pthread_barrier_t * barrier, - const pthread_barrierattr_t * attr, - unsigned int count); - -PTW32_DLLPORT int PTW32_CDECL pthread_barrier_destroy (pthread_barrier_t * barrier); - -PTW32_DLLPORT int PTW32_CDECL pthread_barrier_wait (pthread_barrier_t * barrier); - -/* - * Condition Variable Attribute Functions - */ -PTW32_DLLPORT int PTW32_CDECL pthread_condattr_init (pthread_condattr_t * attr); - -PTW32_DLLPORT int PTW32_CDECL pthread_condattr_destroy (pthread_condattr_t * attr); - -PTW32_DLLPORT int PTW32_CDECL pthread_condattr_getpshared (const pthread_condattr_t * attr, - int *pshared); - -PTW32_DLLPORT int PTW32_CDECL pthread_condattr_setpshared (pthread_condattr_t * attr, - int pshared); - -/* - * Condition Variable Functions - */ -PTW32_DLLPORT int PTW32_CDECL pthread_cond_init (pthread_cond_t * cond, - const pthread_condattr_t * attr); - -PTW32_DLLPORT int PTW32_CDECL pthread_cond_destroy (pthread_cond_t * cond); - -PTW32_DLLPORT int PTW32_CDECL pthread_cond_wait (pthread_cond_t * cond, - pthread_mutex_t * mutex); - -PTW32_DLLPORT int PTW32_CDECL pthread_cond_timedwait (pthread_cond_t * cond, - pthread_mutex_t * mutex, - const struct timespec *abstime); - -PTW32_DLLPORT int PTW32_CDECL pthread_cond_signal (pthread_cond_t * cond); - -PTW32_DLLPORT int PTW32_CDECL pthread_cond_broadcast (pthread_cond_t * cond); - -/* - * Scheduling - */ -PTW32_DLLPORT int PTW32_CDECL pthread_setschedparam (pthread_t thread, - int policy, - const struct sched_param *param); - -PTW32_DLLPORT int PTW32_CDECL pthread_getschedparam (pthread_t thread, - int *policy, - struct sched_param *param); - -PTW32_DLLPORT int PTW32_CDECL pthread_setconcurrency (int); - -PTW32_DLLPORT int PTW32_CDECL pthread_getconcurrency (void); - -/* - * Read-Write Lock Functions - */ -PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_init(pthread_rwlock_t *lock, - const pthread_rwlockattr_t *attr); - -PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_destroy(pthread_rwlock_t *lock); - -PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_tryrdlock(pthread_rwlock_t *); - -PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_trywrlock(pthread_rwlock_t *); - -PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_rdlock(pthread_rwlock_t *lock); - -PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedrdlock(pthread_rwlock_t *lock, - const struct timespec *abstime); - -PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_wrlock(pthread_rwlock_t *lock); - -PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedwrlock(pthread_rwlock_t *lock, - const struct timespec *abstime); - -PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_unlock(pthread_rwlock_t *lock); - -PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_init (pthread_rwlockattr_t * attr); - -PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_destroy (pthread_rwlockattr_t * attr); - -PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * attr, - int *pshared); - -PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr, - int pshared); - -#if PTW32_LEVEL >= PTW32_LEVEL_MAX - 1 - -/* - * Signal Functions. Should be defined in but MSVC and MinGW32 - * already have signal.h that don't define these. - */ -PTW32_DLLPORT int PTW32_CDECL pthread_kill(pthread_t thread, int sig); - -/* - * Non-portable functions - */ - -/* - * Compatibility with Linux. - */ -PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setkind_np(pthread_mutexattr_t * attr, - int kind); -PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getkind_np(pthread_mutexattr_t * attr, - int *kind); - -/* - * Possibly supported by other POSIX threads implementations - */ -PTW32_DLLPORT int PTW32_CDECL pthread_delay_np (struct timespec * interval); -PTW32_DLLPORT int PTW32_CDECL pthread_num_processors_np(void); -PTW32_DLLPORT unsigned __int64 PTW32_CDECL pthread_getunique_np(pthread_t thread); - -/* - * Useful if an application wants to statically link - * the lib rather than load the DLL at run-time. - */ -PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_attach_np(void); -PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_detach_np(void); -PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_attach_np(void); -PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_detach_np(void); - -/* - * Features that are auto-detected at load/run time. - */ -PTW32_DLLPORT int PTW32_CDECL pthread_win32_test_features_np(int); -enum ptw32_features { - PTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE = 0x0001, /* System provides it. */ - PTW32_ALERTABLE_ASYNC_CANCEL = 0x0002 /* Can cancel blocked threads. */ -}; - -/* - * Register a system time change with the library. - * Causes the library to perform various functions - * in response to the change. Should be called whenever - * the application's top level window receives a - * WM_TIMECHANGE message. It can be passed directly to - * pthread_create() as a new thread if desired. - */ -PTW32_DLLPORT void * PTW32_CDECL pthread_timechange_handler_np(void *); - -#endif /*PTW32_LEVEL >= PTW32_LEVEL_MAX - 1 */ - -#if PTW32_LEVEL >= PTW32_LEVEL_MAX - -/* - * Returns the Win32 HANDLE for the POSIX thread. - */ -PTW32_DLLPORT HANDLE PTW32_CDECL pthread_getw32threadhandle_np(pthread_t thread); -/* - * Returns the win32 thread ID for POSIX thread. - */ -PTW32_DLLPORT DWORD PTW32_CDECL pthread_getw32threadid_np (pthread_t thread); - - -/* - * Protected Methods - * - * This function blocks until the given WIN32 handle - * is signaled or pthread_cancel had been called. - * This function allows the caller to hook into the - * PThreads cancel mechanism. It is implemented using - * - * WaitForMultipleObjects - * - * on 'waitHandle' and a manually reset WIN32 Event - * used to implement pthread_cancel. The 'timeout' - * argument to TimedWait is simply passed to - * WaitForMultipleObjects. - */ -PTW32_DLLPORT int PTW32_CDECL pthreadCancelableWait (HANDLE waitHandle); -PTW32_DLLPORT int PTW32_CDECL pthreadCancelableTimedWait (HANDLE waitHandle, - DWORD timeout); - -#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ - -/* - * Thread-Safe C Runtime Library Mappings. - */ -#if !defined(_UWIN) -# if defined(NEED_ERRNO) - PTW32_DLLPORT int * PTW32_CDECL _errno( void ); -# else -# if !defined(errno) -# if (defined(_MT) || defined(_DLL)) - __declspec(dllimport) extern int * __cdecl _errno(void); -# define errno (*_errno()) -# endif -# endif -# endif -#endif - -/* - * Some compiler environments don't define some things. - */ -#if defined(__BORLANDC__) -# define _ftime ftime -# define _timeb timeb -#endif - -#if defined(__cplusplus) - -/* - * Internal exceptions - */ -class ptw32_exception {}; -class ptw32_exception_cancel : public ptw32_exception {}; -class ptw32_exception_exit : public ptw32_exception {}; - -#endif - -#if PTW32_LEVEL >= PTW32_LEVEL_MAX - -/* FIXME: This is only required if the library was built using SEH */ -/* - * Get internal SEH tag - */ -PTW32_DLLPORT DWORD PTW32_CDECL ptw32_get_exception_services_code(void); - -#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ - -#if !defined(PTW32_BUILD) - -#if defined(__CLEANUP_SEH) - -/* - * Redefine the SEH __except keyword to ensure that applications - * propagate our internal exceptions up to the library's internal handlers. - */ -#define __except( E ) \ - __except( ( GetExceptionCode() == ptw32_get_exception_services_code() ) \ - ? EXCEPTION_CONTINUE_SEARCH : ( E ) ) - -#endif /* __CLEANUP_SEH */ - -#if defined(__CLEANUP_CXX) - -/* - * Redefine the C++ catch keyword to ensure that applications - * propagate our internal exceptions up to the library's internal handlers. - */ -#if defined(_MSC_VER) - /* - * WARNING: Replace any 'catch( ... )' with 'PtW32CatchAll' - * if you want Pthread-Win32 cancelation and pthread_exit to work. - */ - -#if !defined(PtW32NoCatchWarn) - -#pragma message("Specify \"/DPtW32NoCatchWarn\" compiler flag to skip this message.") -#pragma message("------------------------------------------------------------------") -#pragma message("When compiling applications with MSVC++ and C++ exception handling:") -#pragma message(" Replace any 'catch( ... )' in routines called from POSIX threads") -#pragma message(" with 'PtW32CatchAll' or 'CATCHALL' if you want POSIX thread") -#pragma message(" cancelation and pthread_exit to work. For example:") -#pragma message("") -#pragma message(" #if defined(PtW32CatchAll)") -#pragma message(" PtW32CatchAll") -#pragma message(" #else") -#pragma message(" catch(...)") -#pragma message(" #endif") -#pragma message(" {") -#pragma message(" /* Catchall block processing */") -#pragma message(" }") -#pragma message("------------------------------------------------------------------") - -#endif - -#define PtW32CatchAll \ - catch( ptw32_exception & ) { throw; } \ - catch( ... ) - -#else /* _MSC_VER */ - -#define catch( E ) \ - catch( ptw32_exception & ) { throw; } \ - catch( E ) - -#endif /* _MSC_VER */ - -#endif /* __CLEANUP_CXX */ - -#endif /* ! PTW32_BUILD */ - -#if defined(__cplusplus) -} /* End of extern "C" */ -#endif /* __cplusplus */ - -#if defined(PTW32__HANDLE_DEF) -# undef HANDLE -#endif -#if defined(PTW32__DWORD_DEF) -# undef DWORD -#endif - -#undef PTW32_LEVEL -#undef PTW32_LEVEL_MAX - -#endif /* ! RC_INVOKED */ - -#endif /* PTHREAD_H */ diff --git a/pthreads/sched.h b/pthreads/sched.h deleted file mode 100644 index f36a97a66..000000000 --- a/pthreads/sched.h +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Module: sched.h - * - * Purpose: - * Provides an implementation of POSIX realtime extensions - * as defined in - * - * POSIX 1003.1b-1993 (POSIX.1b) - * - * -------------------------------------------------------------------------- - * - * Pthreads-win32 - POSIX Threads Library for Win32 - * Copyright(C) 1998 John E. Bossom - * Copyright(C) 1999,2005 Pthreads-win32 contributors - * - * Contact Email: rpj@callisto.canberra.edu.au - * - * The current list of contributors is contained - * in the file CONTRIBUTORS included with the source - * code distribution. The list can also be seen at the - * following World Wide Web location: - * http://sources.redhat.com/pthreads-win32/contributors.html - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library in the file COPYING.LIB; - * if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ -#if !defined(_SCHED_H) -#define _SCHED_H - -#undef PTW32_SCHED_LEVEL - -#if defined(_POSIX_SOURCE) -#define PTW32_SCHED_LEVEL 0 -/* Early POSIX */ -#endif - -#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309 -#undef PTW32_SCHED_LEVEL -#define PTW32_SCHED_LEVEL 1 -/* Include 1b, 1c and 1d */ -#endif - -#if defined(INCLUDE_NP) -#undef PTW32_SCHED_LEVEL -#define PTW32_SCHED_LEVEL 2 -/* Include Non-Portable extensions */ -#endif - -#define PTW32_SCHED_LEVEL_MAX 3 - -#if ( defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112 ) || !defined(PTW32_SCHED_LEVEL) -#define PTW32_SCHED_LEVEL PTW32_SCHED_LEVEL_MAX -/* Include everything */ -#endif - - -#if defined(__GNUC__) && !defined(__declspec) -# error Please upgrade your GNU compiler to one that supports __declspec. -#endif - -/* - * When building the library, you should define PTW32_BUILD so that - * the variables/functions are exported correctly. When using the library, - * do NOT define PTW32_BUILD, and then the variables/functions will - * be imported correctly. - */ -#if !defined(PTW32_STATIC_LIB) -# if defined(PTW32_BUILD) -# define PTW32_DLLPORT __declspec (dllexport) -# else -# define PTW32_DLLPORT __declspec (dllimport) -# endif -#else -# define PTW32_DLLPORT -#endif - -/* - * This is a duplicate of what is in the autoconf config.h, - * which is only used when building the pthread-win32 libraries. - */ - -#if !defined(PTW32_CONFIG_H) -# if defined(WINCE) -# define NEED_ERRNO -# define NEED_SEM -# endif -# if defined(__MINGW64__) -# define HAVE_STRUCT_TIMESPEC -# define HAVE_MODE_T -# elif defined(_UWIN) || defined(__MINGW32__) -# define HAVE_MODE_T -# endif -#endif - -/* - * - */ - -#if PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX -#if defined(NEED_ERRNO) -#include "need_errno.h" -#else -#include -#endif -#endif /* PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX */ - -#if (defined(__MINGW64__) || defined(__MINGW32__)) || defined(_UWIN) -# if PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX -/* For pid_t */ -# include -/* Required by Unix 98 */ -# include -# else - typedef int pid_t; -# endif -#else - typedef int pid_t; -#endif - -/* Thread scheduling policies */ - -enum { - SCHED_OTHER = 0, - SCHED_FIFO, - SCHED_RR, - SCHED_MIN = SCHED_OTHER, - SCHED_MAX = SCHED_RR -}; - -struct sched_param { - int sched_priority; -}; - -#if defined(__cplusplus) -extern "C" -{ -#endif /* __cplusplus */ - -PTW32_DLLPORT int __cdecl sched_yield (void); - -PTW32_DLLPORT int __cdecl sched_get_priority_min (int policy); - -PTW32_DLLPORT int __cdecl sched_get_priority_max (int policy); - -PTW32_DLLPORT int __cdecl sched_setscheduler (pid_t pid, int policy); - -PTW32_DLLPORT int __cdecl sched_getscheduler (pid_t pid); - -/* - * Note that this macro returns ENOTSUP rather than - * ENOSYS as might be expected. However, returning ENOSYS - * should mean that sched_get_priority_{min,max} are - * not implemented as well as sched_rr_get_interval. - * This is not the case, since we just don't support - * round-robin scheduling. Therefore I have chosen to - * return the same value as sched_setscheduler when - * SCHED_RR is passed to it. - */ -#define sched_rr_get_interval(_pid, _interval) \ - ( errno = ENOTSUP, (int) -1 ) - - -#if defined(__cplusplus) -} /* End of extern "C" */ -#endif /* __cplusplus */ - -#undef PTW32_SCHED_LEVEL -#undef PTW32_SCHED_LEVEL_MAX - -#endif /* !_SCHED_H */ - diff --git a/pthreads/semaphore.h b/pthreads/semaphore.h deleted file mode 100644 index c6e9407e2..000000000 --- a/pthreads/semaphore.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Module: semaphore.h - * - * Purpose: - * Semaphores aren't actually part of the PThreads standard. - * They are defined by the POSIX Standard: - * - * POSIX 1003.1b-1993 (POSIX.1b) - * - * -------------------------------------------------------------------------- - * - * Pthreads-win32 - POSIX Threads Library for Win32 - * Copyright(C) 1998 John E. Bossom - * Copyright(C) 1999,2005 Pthreads-win32 contributors - * - * Contact Email: rpj@callisto.canberra.edu.au - * - * The current list of contributors is contained - * in the file CONTRIBUTORS included with the source - * code distribution. The list can also be seen at the - * following World Wide Web location: - * http://sources.redhat.com/pthreads-win32/contributors.html - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library in the file COPYING.LIB; - * if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ -#if !defined( SEMAPHORE_H ) -#define SEMAPHORE_H - -#undef PTW32_SEMAPHORE_LEVEL - -#if defined(_POSIX_SOURCE) -#define PTW32_SEMAPHORE_LEVEL 0 -/* Early POSIX */ -#endif - -#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309 -#undef PTW32_SEMAPHORE_LEVEL -#define PTW32_SEMAPHORE_LEVEL 1 -/* Include 1b, 1c and 1d */ -#endif - -#if defined(INCLUDE_NP) -#undef PTW32_SEMAPHORE_LEVEL -#define PTW32_SEMAPHORE_LEVEL 2 -/* Include Non-Portable extensions */ -#endif - -#define PTW32_SEMAPHORE_LEVEL_MAX 3 - -#if !defined(PTW32_SEMAPHORE_LEVEL) -#define PTW32_SEMAPHORE_LEVEL PTW32_SEMAPHORE_LEVEL_MAX -/* Include everything */ -#endif - -#if defined(__GNUC__) && ! defined (__declspec) -# error Please upgrade your GNU compiler to one that supports __declspec. -#endif - -/* - * When building the library, you should define PTW32_BUILD so that - * the variables/functions are exported correctly. When using the library, - * do NOT define PTW32_BUILD, and then the variables/functions will - * be imported correctly. - */ -#if !defined(PTW32_STATIC_LIB) -# if defined(PTW32_BUILD) -# define PTW32_DLLPORT __declspec (dllexport) -# else -# define PTW32_DLLPORT __declspec (dllimport) -# endif -#else -# define PTW32_DLLPORT -#endif - -/* - * This is a duplicate of what is in the autoconf config.h, - * which is only used when building the pthread-win32 libraries. - */ - -#if !defined(PTW32_CONFIG_H) -# if defined(WINCE) -# define NEED_ERRNO -# define NEED_SEM -# endif -# if defined(__MINGW64__) -# define HAVE_STRUCT_TIMESPEC -# define HAVE_MODE_T -# elif defined(_UWIN) || defined(__MINGW32__) -# define HAVE_MODE_T -# endif -#endif - -/* - * - */ - -#if PTW32_SEMAPHORE_LEVEL >= PTW32_SEMAPHORE_LEVEL_MAX -#if defined(NEED_ERRNO) -#include "need_errno.h" -#else -#include -#endif -#endif /* PTW32_SEMAPHORE_LEVEL >= PTW32_SEMAPHORE_LEVEL_MAX */ - -#define _POSIX_SEMAPHORES - -#if defined(__cplusplus) -extern "C" -{ -#endif /* __cplusplus */ - -#if !defined(HAVE_MODE_T) -typedef unsigned int mode_t; -#endif - - -typedef struct sem_t_ * sem_t; - -PTW32_DLLPORT int __cdecl sem_init (sem_t * sem, - int pshared, - unsigned int value); - -PTW32_DLLPORT int __cdecl sem_destroy (sem_t * sem); - -PTW32_DLLPORT int __cdecl sem_trywait (sem_t * sem); - -PTW32_DLLPORT int __cdecl sem_wait (sem_t * sem); - -PTW32_DLLPORT int __cdecl sem_timedwait (sem_t * sem, - const struct timespec * abstime); - -PTW32_DLLPORT int __cdecl sem_post (sem_t * sem); - -PTW32_DLLPORT int __cdecl sem_post_multiple (sem_t * sem, - int count); - -PTW32_DLLPORT int __cdecl sem_open (const char * name, - int oflag, - mode_t mode, - unsigned int value); - -PTW32_DLLPORT int __cdecl sem_close (sem_t * sem); - -PTW32_DLLPORT int __cdecl sem_unlink (const char * name); - -PTW32_DLLPORT int __cdecl sem_getvalue (sem_t * sem, - int * sval); - -#if defined(__cplusplus) -} /* End of extern "C" */ -#endif /* __cplusplus */ - -#undef PTW32_SEMAPHORE_LEVEL -#undef PTW32_SEMAPHORE_LEVEL_MAX - -#endif /* !SEMAPHORE_H */ diff --git a/rtlsdr/rtl-sdr.h b/rtlsdr/rtl-sdr.h deleted file mode 100644 index bb028fee0..000000000 --- a/rtlsdr/rtl-sdr.h +++ /dev/null @@ -1,366 +0,0 @@ -/* - * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver - * Copyright (C) 2012 by Steve Markgraf - * Copyright (C) 2012 by Dimitri Stolnikov - * - * This program is free software: you can redistribute it 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 program 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 . - */ - -#ifndef __RTL_SDR_H -#define __RTL_SDR_H - -#ifdef __cplusplus -extern "C" { -#endif - -//#include -#include "rtl-sdr_export.h" - -typedef struct rtlsdr_dev rtlsdr_dev_t; - -RTLSDR_API uint32_t rtlsdr_get_device_count(void); - -RTLSDR_API const char* rtlsdr_get_device_name(uint32_t index); - -/*! - * Get USB device strings. - * - * NOTE: The string arguments must provide space for up to 256 bytes. - * - * \param index the device index - * \param manufact manufacturer name, may be NULL - * \param product product name, may be NULL - * \param serial serial number, may be NULL - * \return 0 on success - */ -RTLSDR_API int rtlsdr_get_device_usb_strings(uint32_t index, - char *manufact, - char *product, - char *serial); - -/*! - * Get device index by USB serial string descriptor. - * - * \param serial serial string of the device - * \return device index of first device where the name matched - * \return -1 if name is NULL - * \return -2 if no devices were found at all - * \return -3 if devices were found, but none with matching name - */ -RTLSDR_API int rtlsdr_get_index_by_serial(const char *serial); - -RTLSDR_API int rtlsdr_open(rtlsdr_dev_t **dev, uint32_t index); - -RTLSDR_API int rtlsdr_close(rtlsdr_dev_t *dev); - -/* configuration functions */ - -/*! - * Set crystal oscillator frequencies used for the RTL2832 and the tuner IC. - * - * Usually both ICs use the same clock. Changing the clock may make sense if - * you are applying an external clock to the tuner or to compensate the - * frequency (and samplerate) error caused by the original (cheap) crystal. - * - * NOTE: Call this function only if you fully understand the implications. - * - * \param dev the device handle given by rtlsdr_open() - * \param rtl_freq frequency value used to clock the RTL2832 in Hz - * \param tuner_freq frequency value used to clock the tuner IC in Hz - * \return 0 on success - */ -RTLSDR_API int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq, - uint32_t tuner_freq); - -/*! - * Get crystal oscillator frequencies used for the RTL2832 and the tuner IC. - * - * Usually both ICs use the same clock. - * - * \param dev the device handle given by rtlsdr_open() - * \param rtl_freq frequency value used to clock the RTL2832 in Hz - * \param tuner_freq frequency value used to clock the tuner IC in Hz - * \return 0 on success - */ -RTLSDR_API int rtlsdr_get_xtal_freq(rtlsdr_dev_t *dev, uint32_t *rtl_freq, - uint32_t *tuner_freq); - -/*! - * Get USB device strings. - * - * NOTE: The string arguments must provide space for up to 256 bytes. - * - * \param dev the device handle given by rtlsdr_open() - * \param manufact manufacturer name, may be NULL - * \param product product name, may be NULL - * \param serial serial number, may be NULL - * \return 0 on success - */ -RTLSDR_API int rtlsdr_get_usb_strings(rtlsdr_dev_t *dev, char *manufact, - char *product, char *serial); - -/*! - * Write the device EEPROM - * - * \param dev the device handle given by rtlsdr_open() - * \param data buffer of data to be written - * \param offset address where the data should be written - * \param len length of the data - * \return 0 on success - * \return -1 if device handle is invalid - * \return -2 if EEPROM size is exceeded - * \return -3 if no EEPROM was found - */ - -RTLSDR_API int rtlsdr_write_eeprom(rtlsdr_dev_t *dev, uint8_t *data, - uint8_t offset, uint16_t len); - -/*! - * Read the device EEPROM - * - * \param dev the device handle given by rtlsdr_open() - * \param data buffer where the data should be written - * \param offset address where the data should be read from - * \param len length of the data - * \return 0 on success - * \return -1 if device handle is invalid - * \return -2 if EEPROM size is exceeded - * \return -3 if no EEPROM was found - */ - -RTLSDR_API int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data, - uint8_t offset, uint16_t len); - -RTLSDR_API int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq); - -/*! - * Get actual frequency the device is tuned to. - * - * \param dev the device handle given by rtlsdr_open() - * \return 0 on error, frequency in Hz otherwise - */ -RTLSDR_API uint32_t rtlsdr_get_center_freq(rtlsdr_dev_t *dev); - -/*! - * Set the frequency correction value for the device. - * - * \param dev the device handle given by rtlsdr_open() - * \param ppm correction value in parts per million (ppm) - * \return 0 on success - */ -RTLSDR_API int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm); - -/*! - * Get actual frequency correction value of the device. - * - * \param dev the device handle given by rtlsdr_open() - * \return correction value in parts per million (ppm) - */ -RTLSDR_API int rtlsdr_get_freq_correction(rtlsdr_dev_t *dev); - -enum rtlsdr_tuner { - RTLSDR_TUNER_UNKNOWN = 0, - RTLSDR_TUNER_E4000, - RTLSDR_TUNER_FC0012, - RTLSDR_TUNER_FC0013, - RTLSDR_TUNER_FC2580, - RTLSDR_TUNER_R820T -}; - -/*! - * Get the tuner type. - * - * \param dev the device handle given by rtlsdr_open() - * \return RTLSDR_TUNER_UNKNOWN on error, tuner type otherwise - */ -RTLSDR_API enum rtlsdr_tuner rtlsdr_get_tuner_type(rtlsdr_dev_t *dev); - -/*! - * Get a list of gains supported by the tuner. - * - * NOTE: The gains argument must be preallocated by the caller. If NULL is - * being given instead, the number of available gain values will be returned. - * - * \param dev the device handle given by rtlsdr_open() - * \param gains array of gain values. In tenths of a dB, 115 means 11.5 dB. - * \return <= 0 on error, number of available (returned) gain values otherwise - */ -RTLSDR_API int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains); - -/*! - * Set the gain for the device. - * Manual gain mode must be enabled for this to work. - * - * Valid gain values (in tenths of a dB) for the E4000 tuner: - * -10, 15, 40, 65, 90, 115, 140, 165, 190, - * 215, 240, 290, 340, 420, 430, 450, 470, 490 - * - * Valid gain values may be queried with \ref rtlsdr_get_tuner_gains function. - * - * \param dev the device handle given by rtlsdr_open() - * \param gain in tenths of a dB, 115 means 11.5 dB. - * \return 0 on success - */ -RTLSDR_API int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain); - -/*! - * Get actual gain the device is configured to. - * - * \param dev the device handle given by rtlsdr_open() - * \return 0 on error, gain in tenths of a dB, 115 means 11.5 dB. - */ -RTLSDR_API int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev); - -/*! - * Set the intermediate frequency gain for the device. - * - * \param dev the device handle given by rtlsdr_open() - * \param stage intermediate frequency gain stage number (1 to 6 for E4000) - * \param gain in tenths of a dB, -30 means -3.0 dB. - * \return 0 on success - */ -RTLSDR_API int rtlsdr_set_tuner_if_gain(rtlsdr_dev_t *dev, int stage, int gain); - -/*! - * Set the gain mode (automatic/manual) for the device. - * Manual gain mode must be enabled for the gain setter function to work. - * - * \param dev the device handle given by rtlsdr_open() - * \param manual gain mode, 1 means manual gain mode shall be enabled. - * \return 0 on success - */ -RTLSDR_API int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual); - -/* this will select the baseband filters according to the requested sample rate */ -RTLSDR_API int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate); - -/*! - * Get actual sample rate the device is configured to. - * - * \param dev the device handle given by rtlsdr_open() - * \return 0 on error, sample rate in Hz otherwise - */ -RTLSDR_API uint32_t rtlsdr_get_sample_rate(rtlsdr_dev_t *dev); - -/*! - * Enable test mode that returns an 8 bit counter instead of the samples. - * The counter is generated inside the RTL2832. - * - * \param dev the device handle given by rtlsdr_open() - * \param test mode, 1 means enabled, 0 disabled - * \return 0 on success - */ -RTLSDR_API int rtlsdr_set_testmode(rtlsdr_dev_t *dev, int on); - -/*! - * Enable or disable the internal digital AGC of the RTL2832. - * - * \param dev the device handle given by rtlsdr_open() - * \param digital AGC mode, 1 means enabled, 0 disabled - * \return 0 on success - */ -RTLSDR_API int rtlsdr_set_agc_mode(rtlsdr_dev_t *dev, int on); - -/*! - * Enable or disable the direct sampling mode. When enabled, the IF mode - * of the RTL2832 is activated, and rtlsdr_set_center_freq() will control - * the IF-frequency of the DDC, which can be used to tune from 0 to 28.8 MHz - * (xtal frequency of the RTL2832). - * - * \param dev the device handle given by rtlsdr_open() - * \param on 0 means disabled, 1 I-ADC input enabled, 2 Q-ADC input enabled - * \return 0 on success - */ -RTLSDR_API int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on); - -/*! - * Get state of the direct sampling mode - * - * \param dev the device handle given by rtlsdr_open() - * \return -1 on error, 0 means disabled, 1 I-ADC input enabled - * 2 Q-ADC input enabled - */ -RTLSDR_API int rtlsdr_get_direct_sampling(rtlsdr_dev_t *dev); - -/*! - * Enable or disable offset tuning for zero-IF tuners, which allows to avoid - * problems caused by the DC offset of the ADCs and 1/f noise. - * - * \param dev the device handle given by rtlsdr_open() - * \param on 0 means disabled, 1 enabled - * \return 0 on success - */ -RTLSDR_API int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on); - -/*! - * Get state of the offset tuning mode - * - * \param dev the device handle given by rtlsdr_open() - * \return -1 on error, 0 means disabled, 1 enabled - */ -RTLSDR_API int rtlsdr_get_offset_tuning(rtlsdr_dev_t *dev); - -/* streaming functions */ - -RTLSDR_API int rtlsdr_reset_buffer(rtlsdr_dev_t *dev); - -RTLSDR_API int rtlsdr_read_sync(rtlsdr_dev_t *dev, void *buf, int len, int *n_read); - -typedef void(*rtlsdr_read_async_cb_t)(unsigned char *buf, uint32_t len, void *ctx); - -/*! - * Read samples from the device asynchronously. This function will block until - * it is being canceled using rtlsdr_cancel_async() - * - * NOTE: This function is deprecated and is subject for removal. - * - * \param dev the device handle given by rtlsdr_open() - * \param cb callback function to return received samples - * \param ctx user specific context to pass via the callback function - * \return 0 on success - */ -RTLSDR_API int rtlsdr_wait_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx); - -/*! - * Read samples from the device asynchronously. This function will block until - * it is being canceled using rtlsdr_cancel_async() - * - * \param dev the device handle given by rtlsdr_open() - * \param cb callback function to return received samples - * \param ctx user specific context to pass via the callback function - * \param buf_num optional buffer count, buf_num * buf_len = overall buffer size - * set to 0 for default buffer count (32) - * \param buf_len optional buffer length, must be multiple of 512, - * set to 0 for default buffer length (16 * 32 * 512) - * \return 0 on success - */ -RTLSDR_API int rtlsdr_read_async(rtlsdr_dev_t *dev, - rtlsdr_read_async_cb_t cb, - void *ctx, - uint32_t buf_num, - uint32_t buf_len); - -/*! - * Cancel all pending asynchronous operations on the device. - * - * \param dev the device handle given by rtlsdr_open() - * \return 0 on success - */ -RTLSDR_API int rtlsdr_cancel_async(rtlsdr_dev_t *dev); - -#ifdef __cplusplus -} -#endif - -#endif /* __RTL_SDR_H */ diff --git a/rtlsdr/rtl-sdr_export.h b/rtlsdr/rtl-sdr_export.h deleted file mode 100644 index 69e178d12..000000000 --- a/rtlsdr/rtl-sdr_export.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver - * Copyright (C) 2012 by Hoernchen - * - * This program is free software: you can redistribute it 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 program 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 . - */ - -#ifndef RTLSDR_EXPORT_H -#define RTLSDR_EXPORT_H - -#if defined __GNUC__ -# if __GNUC__ >= 4 -# define __SDR_EXPORT __attribute__((visibility("default"))) -# define __SDR_IMPORT __attribute__((visibility("default"))) -# else -# define __SDR_EXPORT -# define __SDR_IMPORT -# endif -#elif _MSC_VER -# define __SDR_EXPORT __declspec(dllexport) -# define __SDR_IMPORT __declspec(dllimport) -#else -# define __SDR_EXPORT -# define __SDR_IMPORT -#endif - -#ifndef rtlsdr_STATIC -# ifdef rtlsdr_EXPORTS -# define RTLSDR_API __SDR_EXPORT -# else -# define RTLSDR_API __SDR_IMPORT -# endif -#else -#define RTLSDR_API -#endif -#endif /* RTLSDR_EXPORT_H */ From bbcfbb8279605439cf24593e56e2cf6262ddbf4d Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Dec 2014 20:21:36 +0000 Subject: [PATCH 077/610] Changelog update. --- debian/changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/debian/changelog b/debian/changelog index 93087f4e8..8886b6bc8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,8 @@ dump1090-mutability (1.08.2302.14+1mu-4) UNRELEASED; urgency=medium * Remove half-implemented --no-decode option. + * Remove Windows support files that aren't needed for the Debian package. + * Remove some legacy support scripts not needed by the Debian package. -- Oliver Jowett Sat, 27 Dec 2014 20:08:44 +0000 From 58dfbdcb6bee0b4151659aa177b1a45b55a83396 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Dec 2014 20:52:56 +0000 Subject: [PATCH 078/610] Add support for controlling the accuracy of the receiver location written in the JSON metadata used by the webmap. --- debian/changelog | 2 ++ debian/config-template | 3 +++ debian/dump1090-mutability.config | 2 ++ debian/dump1090-mutability.init | 6 ++++++ debian/dump1090-mutability.postinst | 1 + debian/dump1090-mutability.templates | 21 +++++++++++++++++++++ dump1090.c | 4 ++++ dump1090.h | 1 + net_io.c | 17 ++++++++++++----- 9 files changed, 52 insertions(+), 5 deletions(-) diff --git a/debian/changelog b/debian/changelog index 8886b6bc8..00440f6a6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -3,6 +3,8 @@ dump1090-mutability (1.08.2302.14+1mu-4) UNRELEASED; urgency=medium * Remove half-implemented --no-decode option. * Remove Windows support files that aren't needed for the Debian package. * Remove some legacy support scripts not needed by the Debian package. + * Add support for controlling the accuracy of the receiver location + written in the JSON metadata used by the webmap. -- Oliver Jowett Sat, 27 Dec 2014 20:08:44 +0000 diff --git a/debian/config-template b/debian/config-template index 4c723fcf9..615c6faa4 100644 --- a/debian/config-template +++ b/debian/config-template @@ -112,5 +112,8 @@ JSON_DIR= # Interval between writing json state (in seconds). 0 disables. JSON_INTERVAL= +# Accuracy of receiver location to write to json state, one of "exact" / "approximate" / "none" +JSON_LOCATION_ACCURACY= + # Additional options that are passed to the Daemon. EXTRA_ARGS= diff --git a/debian/dump1090-mutability.config b/debian/dump1090-mutability.config index 48df016a3..68bc998c5 100644 --- a/debian/dump1090-mutability.config +++ b/debian/dump1090-mutability.config @@ -47,6 +47,7 @@ if [ -e $CONFIGFILE ]; then db_set $NAME/stats-interval "$STATS_INTERVAL" db_set $NAME/json-dir "$JSON_DIR" db_set $NAME/json-interval "$JSON_INTERVAL" + db_set $NAME/json-location-accuracy "$JSON_LOCATION_ACCURACY" db_set $NAME/extra-args "$EXTRA_ARGS" fi @@ -202,6 +203,7 @@ db_go || true; db_get $NAME/auto-start; if [ "$RET" = "true" ]; then db_go || true; db_get $NAME/json-interval; if [ -n "$RET" ] && [ "$RET" -gt 0 ]; then # only if json-interval was given and non-zero db_input low $NAME/json-dir || true + db_input low $NAME/json-location-accuracy || true fi db_input low $NAME/extra-args || true diff --git a/debian/dump1090-mutability.init b/debian/dump1090-mutability.init index 1c9d8d3e4..ed900eb71 100644 --- a/debian/dump1090-mutability.init +++ b/debian/dump1090-mutability.init @@ -83,6 +83,12 @@ if [ -n "$NET_BIND_ADDRESS" ]; then ARGS="$ARGS --net-bind-address $NET_BIND_ADD if [ -n "$STATS_INTERVAL" ]; then ARGS="$ARGS --stats-every $STATS_INTERVAL"; fi if [ -n "$JSON_DIR" ]; then ARGS="$ARGS --write-json $JSON_DIR"; fi if [ -n "$JSON_INTERVAL" ]; then ARGS="$ARGS --write-json-every $JSON_INTERVAL"; fi +case "x$JSON_LOCATION_ACCURACY" in + xexact) ARGS="$ARGS --json-location-accuracy 2" ;; + xapproximate) ARGS="$ARGS --json-location-accuracy 1" ;; + *) ARGS="$ARGS --json-location-accuracy 0" ;; +esac + if [ -n "$EXTRA_ARGS" ]; then ARGS="$ARGS $EXTRA_ARGS"; fi # Load the VERBOSE setting and other rcS variables diff --git a/debian/dump1090-mutability.postinst b/debian/dump1090-mutability.postinst index 2402a190a..04fac1e25 100644 --- a/debian/dump1090-mutability.postinst +++ b/debian/dump1090-mutability.postinst @@ -84,6 +84,7 @@ case "$1" in subvar stats-interval STATS_INTERVAL subvar json-dir JSON_DIR subvar json-interval JSON_INTERVAL + subvar json-location-accuracy JSON_LOCATION_ACCURACY subvar extra-args EXTRA_ARGS cp -a -f $CONFIGFILE $CONFIGFILE.tmp diff --git a/debian/dump1090-mutability.templates b/debian/dump1090-mutability.templates index 2e14b57b5..68b6a7088 100644 --- a/debian/dump1090-mutability.templates +++ b/debian/dump1090-mutability.templates @@ -234,6 +234,27 @@ Description: Interval between writing JSON aircraft state, in seconds: Type: string Default: 1 +Template: dump1090-mutability/json-location-accuracy +Description: Receiver location accuracy to show in the web interface: + dump1090 can provide the configured receiver location to the web map, + so that the map can show distances from the receiver. + . + For privacy reasons, if you are making the map publicly available you + may not want to show the exact location of the receiver. There are three + options available to control what is shown: + . + approximate: dump1090 will provide the receiver location rounded to the + nearest 0.01 degree of latitude and longitude. This gives a location + that is accurate to within about 0.5 - 1km. + . + exact: dump1090 will provide the exact receiver location. + . + none: dump1090 will not provide the receiver location at all; distance + display will be disabled. +Type: select +Choices: approximate, exact, none +Default: approximate + Template: dump1090-mutability/extra-args Description: Extra arguments to pass to dump1090: Here you can add any extra arguments you want to pass to dump1090. diff --git a/dump1090.c b/dump1090.c index db43499c0..249689bd4 100644 --- a/dump1090.c +++ b/dump1090.c @@ -83,6 +83,7 @@ void modesInitConfig(void) { Modes.fUserLat = MODES_USER_LATITUDE_DFLT; Modes.fUserLon = MODES_USER_LONGITUDE_DFLT; Modes.json_interval = 1; + Modes.json_location_accuracy = 1; } // //========================================================================= @@ -448,6 +449,7 @@ void showHelp(void) { "--no-decode Don't decode the message contents beyond the minimum necessary\n" "--write-json

Periodically write json output to (for serving by a separate webserver)\n" "--write-json-every Write json output every t seconds (default 1)\n" +"--json-location-accuracy Accuracy of receiver location in json metadata: 0=no location, 1=approximate, 2=exact\n" "--help Show this help\n" "\n" "Debug mode flags: d = Log frames decoded with errors\n" @@ -844,6 +846,8 @@ int main(int argc, char **argv) { strcat(Modes.json_metadata_path, "/receiver.json"); } else if (!strcmp(argv[j], "--write-json-every") && more) { Modes.json_interval = atoi(argv[++j]); + } else if (!strcmp(argv[j], "--json-location-accuracy") && more) { + Modes.json_location_accuracy = atoi(argv[++j]); #endif } else { fprintf(stderr, diff --git a/dump1090.h b/dump1090.h index d6df11433..8ea6a2a33 100644 --- a/dump1090.h +++ b/dump1090.h @@ -371,6 +371,7 @@ struct { // Internal state char *json_aircraft_path; // Path to json aircraft file to write, or NULL not to. char *json_metadata_path; // Path to json metadata file to write, or NULL not to. int json_interval; // Interval between rewriting the json aircraft file + int json_location_accuracy; // Accuracy of location metadata: 0=none, 1=approx, 2=exact // User details double fUserLat; // Users receiver/antenna lat/lon needed for initial surface location diff --git a/net_io.c b/net_io.c index a55b0e26f..1d60c837a 100644 --- a/net_io.c +++ b/net_io.c @@ -739,11 +739,18 @@ char *generateReceiverJson(int *len) "\"refresh\" : %d", MODES_DUMP1090_VERSION, Modes.json_interval * 1000); - if (Modes.fUserLat != 0.0 || Modes.fUserLon != 0.0) { - p += sprintf(p, ", " \ - "\"lat\" : %.2f, " - "\"lon\" : %.2f", - Modes.fUserLat, Modes.fUserLon); // round to 2dp - about 0.5-1km accuracy - for privacy reasons + if (Modes.json_location_accuracy && (Modes.fUserLat != 0.0 || Modes.fUserLon != 0.0)) { + if (Modes.json_location_accuracy == 1) { + p += sprintf(p, ", " \ + "\"lat\" : %.2f, " + "\"lon\" : %.2f", + Modes.fUserLat, Modes.fUserLon); // round to 2dp - about 0.5-1km accuracy - for privacy reasons + } else { + p += sprintf(p, ", " \ + "\"lat\" : %.6f, " + "\"lon\" : %.6f", + Modes.fUserLat, Modes.fUserLon); // exact location + } } p += sprintf(p, " }\n"); From 727a59e8ebc1b5e3efa4ea0751e3e537e5929f64 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Dec 2014 21:22:43 +0000 Subject: [PATCH 079/610] Oversampling is now less scary. --- debian/changelog | 1 + dump1090.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 00440f6a6..d644ec6fa 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,6 +5,7 @@ dump1090-mutability (1.08.2302.14+1mu-4) UNRELEASED; urgency=medium * Remove some legacy support scripts not needed by the Debian package. * Add support for controlling the accuracy of the receiver location written in the JSON metadata used by the webmap. + * Oversampling is now less scary! -- Oliver Jowett Sat, 27 Dec 2014 20:08:44 +0000 diff --git a/dump1090.c b/dump1090.c index 249689bd4..f90c6f272 100644 --- a/dump1090.c +++ b/dump1090.c @@ -833,7 +833,6 @@ int main(int argc, char **argv) { Modes.interactive_rtl1090 = 1; } else if (!strcmp(argv[j],"--oversample")) { Modes.oversample = 1; - fprintf(stderr, "Oversampling enabled. Be very afraid.\n"); #ifndef _WIN32 } else if (!strcmp(argv[j], "--write-json") && more) { ++j; From 2db9d62c1c6b1f2695ac7ec148ed2233b0886b9e Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Dec 2014 21:25:32 +0000 Subject: [PATCH 080/610] Warn if --modeac is used together with --oversample. --- debian/changelog | 1 + dump1090.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/debian/changelog b/debian/changelog index d644ec6fa..4ff113fdb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -6,6 +6,7 @@ dump1090-mutability (1.08.2302.14+1mu-4) UNRELEASED; urgency=medium * Add support for controlling the accuracy of the receiver location written in the JSON metadata used by the webmap. * Oversampling is now less scary! + * Warn if --modeac is used together with --oversample. -- Oliver Jowett Sat, 27 Dec 2014 20:08:44 +0000 diff --git a/dump1090.c b/dump1090.c index f90c6f272..47f31f802 100644 --- a/dump1090.c +++ b/dump1090.c @@ -867,6 +867,12 @@ int main(int argc, char **argv) { if (Modes.interactive) {signal(SIGWINCH, sigWinchCallback);} #endif + if (Modes.mode_ac && Modes.oversample) { + fprintf(stderr, + "Warning: --modeac is currently ignored when --oversample is used;\n" + " no ModeA/C messages will be decoded.\n"); + } + // Initialization modesInit(); From 2aa37f06daffa64ecd77d68cc14c04af0c4d673d Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Dec 2014 21:40:18 +0000 Subject: [PATCH 081/610] Link to github from the webmap. --- debian/changelog | 1 + public_html/script.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 4ff113fdb..c1d3476d4 100644 --- a/debian/changelog +++ b/debian/changelog @@ -7,6 +7,7 @@ dump1090-mutability (1.08.2302.14+1mu-4) UNRELEASED; urgency=medium written in the JSON metadata used by the webmap. * Oversampling is now less scary! * Warn if --modeac is used together with --oversample. + * Link to github from the webmap. -- Oliver Jowett Sat, 27 Dec 2014 20:08:44 +0000 diff --git a/public_html/script.js b/public_html/script.js index 8cbbc2ac2..1216fb4ee 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -272,7 +272,7 @@ function refreshSelected() { html += '' + selected.flight + ''; } else { - html += 'DUMP1090 ' + Dump1090Version + ''; + html += 'DUMP1090 ' + Dump1090Version + ' [GitHub]'; } if (selected && selected.squawk == 7500) { // Lets hope we never see this... Aircraft Hijacking From 0fa383f63392aa23f41f05b3ff0905b9b49a379a Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Dec 2014 22:24:51 +0000 Subject: [PATCH 082/610] Release changelog. --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index c1d3476d4..eaf25c8fa 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -dump1090-mutability (1.08.2302.14+1mu-4) UNRELEASED; urgency=medium +dump1090-mutability (1.08.2302.14+1mu-4) unstable; urgency=medium * Remove half-implemented --no-decode option. * Remove Windows support files that aren't needed for the Debian package. @@ -9,7 +9,7 @@ dump1090-mutability (1.08.2302.14+1mu-4) UNRELEASED; urgency=medium * Warn if --modeac is used together with --oversample. * Link to github from the webmap. - -- Oliver Jowett Sat, 27 Dec 2014 20:08:44 +0000 + -- Oliver Jowett Sat, 27 Dec 2014 22:24:39 +0000 dump1090-mutability (1.08.2302.14+1mu-3) unstable; urgency=medium From 9e0800e00a6e9c6b46d2057d33d03918c4f600b2 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 27 Dec 2014 22:35:57 +0000 Subject: [PATCH 083/610] add pbuilder config notes --- README.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d0c4a95a0..0be3110e6 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,20 @@ $ sudo apt-get install librtlsdr-dev libusb-1.0-0-dev pkg-config debhelper $ dpkg-buildpackage -b ```` -Or you can use debuild, pdebuild, etc. -I find building via qemubuilder quite effective for building images for Raspbian (it's actually faster to build -on an emulated ARM running on my PC than to build directly on real hardware) +Or you can use debuild/pdebuild. I find building via qemubuilder quite effective for building images for Raspbian (it's actually faster to build on an emulated ARM running on my PC than to build directly on real hardware). + +Here's the pbuilder config I use to build the Raspbian packages: + +```` +MIRRORSITE=http://mirrordirector.raspbian.org/raspbian/ +PDEBUILD_PBUILDER=cowbuilder +BASEPATH=/var/cache/pbuilder/armhf-raspbian-wheezy-base.cow +DISTRIBUTION=wheezy +OTHERMIRROR="deb http://repo.mutability.co.uk/raspbian wheezy rpi" +ARCHITECTURE=armhf +DEBOOTSTRAP=qemu-debootstrap +DEBOOTSTRAPOPTS="--variant=buildd --keyring=/usr/share/keyrings/raspbian-archive-keyring.gpg" +COMPONENTS="main contrib non-free rpi" +EXTRAPACKAGES="eatmydata debhelper fakeroot" +ALLOWUNTRUSTED="yes" +```` From 9a1b4f34698a3c26a07a4f9bec19519b8e15d344 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sun, 28 Dec 2014 17:53:13 +0000 Subject: [PATCH 084/610] Update for the new repository signing setup --- README.md | 49 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 0be3110e6..55bd0cf1f 100644 --- a/README.md +++ b/README.md @@ -18,21 +18,51 @@ a Debian/Raspbian package. # Simple install via apt-get -There is a repository that contains the current releases. -To install from it: +There is a repository that contains the current releases. To set up the repository: ```` -$ sudo bash -# echo "deb http://repo.mutability.co.uk/raspbian wheezy rpi" >/etc/apt/sources.list.d/mutability.list -# apt-get update && apt-get install dump1090-mutability -# dpkg-reconfigure dump1090-mutability # for detailed configuration -# apt-get install lighttpd && lighty-enable-mod dump1090 # if you want to use the external webserver integration +$ wget https://github.com/mutability/mutability-repo/releases/download/v0.1.0/mutability-repo_0.1.0_armhf.deb +$ sudo dpkg -i mutability-repo_0.1.0_armhf.deb ```` -The repository and packages are (currently) unsigned, you will have to confirm installing from an unsigned source. +Then you can install and upgrade packages via apt-get as needed: + +```` +$ sudo apt-get update && sudo apt-get install dump1090-mutability +$ sudo dpkg-reconfigure dump1090-mutability # for detailed configuration +$ sudo apt-get install lighttpd && sudo lighty-enable-mod dump1090 # if you want to use the external webserver integration +```` + +Installing the mutability-repo package also installs the public key used to sign the packages; the signatures will be verified automatically by apt-get. + +# Manual repository setup + +Add a suitable entry to sources.list: + +```` +# echo "deb http://repo.mutability.co.uk/raspbian wheezy rpi" >/etc/apt/sources.list.d/mutabiltiy.list +```` + +Obtain the public key used to sign the repository release by a method of your choice. This is the signing key: + +```` +pub 2048R/4D731812 2014-12-28 [expires: 2015-12-28] + Key fingerprint = 2098 7C8D D31A 6107 E033 7CC3 80D5 57AA 4D73 1812 +uid Oliver Jowett (repo.mutability.co.uk archive signing key) +```` + +which is available from: + + * [GitHub](https://github.com/mutability/mutability-repo/raw/master/mutability.gpg) + * [repo.mutability.co.uk](http://repo.mutability.co.uk/mutability.gpg) (caution - not HTTPS!) + * keys.gnupg.net (`gpg --keyserver keys.gnupg.net --recv-keys 4D731812`) + +Install the key with `apt-key add` or by placing the keyring in `/etc/apt/trusted.gpg.d/` # Manual installation +To install from packages directly: + You will need a librtlsdr0 package for Raspbian. There is no standard build of this. I have built suitable packages that are available from @@ -116,5 +146,6 @@ DEBOOTSTRAP=qemu-debootstrap DEBOOTSTRAPOPTS="--variant=buildd --keyring=/usr/share/keyrings/raspbian-archive-keyring.gpg" COMPONENTS="main contrib non-free rpi" EXTRAPACKAGES="eatmydata debhelper fakeroot" -ALLOWUNTRUSTED="yes" +ALLOWUNTRUSTED="no" +APTKEYRINGS=("/home/oliver/ppa/mutability.gpg") ```` From 9356c899add40be37346cbc7a7903498bf14cc3d Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Mon, 29 Dec 2014 22:59:47 +0000 Subject: [PATCH 085/610] Add Provides: fatsv-data-source --- debian/changelog | 6 ++++++ debian/control | 1 + 2 files changed, 7 insertions(+) diff --git a/debian/changelog b/debian/changelog index eaf25c8fa..2e8fe825c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +dump1090-mutability (1.08.2302.14+1mu-5) UNRELEASED; urgency=medium + + * Add Provides: fatsv-data-source + + -- Oliver Jowett Mon, 29 Dec 2014 22:58:17 +0000 + dump1090-mutability (1.08.2302.14+1mu-4) unstable; urgency=medium * Remove half-implemented --no-decode option. diff --git a/debian/control b/debian/control index 82f2d88ab..d5025188d 100644 --- a/debian/control +++ b/debian/control @@ -11,6 +11,7 @@ Package: dump1090-mutability Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, adduser Suggests: lighttpd +Provides: fatsv-data-source Description: ADS-B Ground Station System for RTL-SDR Networked Aviation Mode S / ADS-B decoder/translator with RTL-SDR software defined radio USB device support. From 6e3238d30376f76ab6bbacde664be8a7ee2cb8c8 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 30 Dec 2014 00:01:58 +0000 Subject: [PATCH 086/610] Release changelog. --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 2e8fe825c..11e18a4d7 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,8 @@ -dump1090-mutability (1.08.2302.14+1mu-5) UNRELEASED; urgency=medium +dump1090-mutability (1.08.2302.14+1mu-5) unstable; urgency=medium * Add Provides: fatsv-data-source - -- Oliver Jowett Mon, 29 Dec 2014 22:58:17 +0000 + -- Oliver Jowett Tue, 30 Dec 2014 00:01:51 +0000 dump1090-mutability (1.08.2302.14+1mu-4) unstable; urgency=medium From a9ca260e7672afcf449fd2dfd010f33063774707 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 30 Dec 2014 16:40:15 +0000 Subject: [PATCH 087/610] Add support for LOG_DECODED_MESSAGES option to log all messages (disables --quiet). --- debian/changelog | 6 ++++++ debian/config-template | 4 ++++ debian/dump1090-mutability.config | 4 +++- debian/dump1090-mutability.init | 4 +++- debian/dump1090-mutability.templates | 7 +++++++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 11e18a4d7..4b25bc24c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +dump1090-mutability (1.08.2302.14+1mu-6) UNRELEASED; urgency=medium + + * Add support for LOG_DECODED_MESSAGES option to log all messages (disables --quiet). + + -- Oliver Jowett Tue, 30 Dec 2014 16:38:50 +0000 + dump1090-mutability (1.08.2302.14+1mu-5) unstable; urgency=medium * Add Provides: fatsv-data-source diff --git a/debian/config-template b/debian/config-template index 615c6faa4..a9d4b9923 100644 --- a/debian/config-template +++ b/debian/config-template @@ -115,5 +115,9 @@ JSON_INTERVAL= # Accuracy of receiver location to write to json state, one of "exact" / "approximate" / "none" JSON_LOCATION_ACCURACY= +# Set to yes to log all decoded messages +# This can get large fast! +LOG_DECODED_MESSAGES= + # Additional options that are passed to the Daemon. EXTRA_ARGS= diff --git a/debian/dump1090-mutability.config b/debian/dump1090-mutability.config index 68bc998c5..824e1cc94 100644 --- a/debian/dump1090-mutability.config +++ b/debian/dump1090-mutability.config @@ -48,6 +48,7 @@ if [ -e $CONFIGFILE ]; then db_set $NAME/json-dir "$JSON_DIR" db_set $NAME/json-interval "$JSON_INTERVAL" db_set $NAME/json-location-accuracy "$JSON_LOCATION_ACCURACY" + db_set_yn $NAME/log-decoded-messages "$LOG_DECODED_MESSAGES" db_set $NAME/extra-args "$EXTRA_ARGS" fi @@ -205,7 +206,8 @@ db_go || true; db_get $NAME/auto-start; if [ "$RET" = "true" ]; then db_input low $NAME/json-dir || true db_input low $NAME/json-location-accuracy || true fi - + + db_input low $NAME/log-decoded-messages || true db_input low $NAME/extra-args || true db_go || True diff --git a/debian/dump1090-mutability.init b/debian/dump1090-mutability.init index ed900eb71..f28bbd4fe 100644 --- a/debian/dump1090-mutability.init +++ b/debian/dump1090-mutability.init @@ -18,7 +18,7 @@ PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="dump1090-mutability daemon" NAME=dump1090-mutability DAEMON=/usr/bin/$NAME -ARGS="--quiet" +ARGS="" PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME @@ -89,6 +89,8 @@ case "x$JSON_LOCATION_ACCURACY" in *) ARGS="$ARGS --json-location-accuracy 0" ;; esac +if [ "x$LOG_DECODED_MESSAGES" != "xyes" ]; then ARGS="$ARGS --quiet"; fi + if [ -n "$EXTRA_ARGS" ]; then ARGS="$ARGS $EXTRA_ARGS"; fi # Load the VERBOSE setting and other rcS variables diff --git a/debian/dump1090-mutability.templates b/debian/dump1090-mutability.templates index 68b6a7088..b88ca113a 100644 --- a/debian/dump1090-mutability.templates +++ b/debian/dump1090-mutability.templates @@ -255,6 +255,13 @@ Type: select Choices: approximate, exact, none Default: approximate +Template: dump1090-mutability/log-decoded-messages +Description: Log all decoded messages? + dump1090 can log all decoded messages as text to the logfile. + This can result in a very large logfile! Usually you don't need this. +Type: boolean +Default: false + Template: dump1090-mutability/extra-args Description: Extra arguments to pass to dump1090: Here you can add any extra arguments you want to pass to dump1090. From 9d0df29dbb9094840d0dbc7923c914a0b29482a2 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 30 Dec 2014 17:08:55 +0000 Subject: [PATCH 088/610] Fix postinst handling of LOG_DECODED_MESSAGES. --- debian/dump1090-mutability.postinst | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/dump1090-mutability.postinst b/debian/dump1090-mutability.postinst index 04fac1e25..5306279b5 100644 --- a/debian/dump1090-mutability.postinst +++ b/debian/dump1090-mutability.postinst @@ -85,6 +85,7 @@ case "$1" in subvar json-dir JSON_DIR subvar json-interval JSON_INTERVAL subvar json-location-accuracy JSON_LOCATION_ACCURACY + subvar_yn log-decoded-messages LOG_DECODED_MESSAGES subvar extra-args EXTRA_ARGS cp -a -f $CONFIGFILE $CONFIGFILE.tmp From 5dd1c397103207db11e3731909f8b21cecc285af Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 30 Dec 2014 17:09:44 +0000 Subject: [PATCH 089/610] Release changelog. --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 4b25bc24c..ddfce4728 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,8 @@ -dump1090-mutability (1.08.2302.14+1mu-6) UNRELEASED; urgency=medium +dump1090-mutability (1.08.2302.14+1mu-6) unstable; urgency=medium * Add support for LOG_DECODED_MESSAGES option to log all messages (disables --quiet). - -- Oliver Jowett Tue, 30 Dec 2014 16:38:50 +0000 + -- Oliver Jowett Tue, 30 Dec 2014 17:09:36 +0000 dump1090-mutability (1.08.2302.14+1mu-5) unstable; urgency=medium From 07bc76205501693618cfebf5df6a97c00516c132 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 2 Jan 2015 22:29:29 +0000 Subject: [PATCH 090/610] Notice if we lose the RTLSDR device and reconnect. Fix some of the more glaring pthread bugs. --- debian/changelog | 10 ++++++++++ dump1090.c | 50 ++++++++++++++++++++++++++++++++++++------------ dump1090.h | 2 +- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/debian/changelog b/debian/changelog index ddfce4728..c9d5ec58a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,13 @@ +dump1090-mutability (1.08.2302.14+1mu-7) UNRELEASED; urgency=medium + + * Notice if we lose the RTLSDR device, and try to reconnect when that happens. + This lets you unplug/replug the RTLSDR without dump1090 exiting, and also + lets dump1090 tolerate occasional USB glitches that make the dongle reset + itself. + * Fix some of the more glaring pthread bugs. + + -- Oliver Jowett Fri, 02 Jan 2015 22:27:44 +0000 + dump1090-mutability (1.08.2302.14+1mu-6) unstable; urgency=medium * Add support for LOG_DECODED_MESSAGES option to log all messages (disables --quiet). diff --git a/dump1090.c b/dump1090.c index 47f31f802..d2da11d68 100644 --- a/dump1090.c +++ b/dump1090.c @@ -200,28 +200,33 @@ void modesInit(void) { // // =============================== RTLSDR handling ========================== // -void modesInitRTLSDR(void) { +int modesInitRTLSDR(void) { int j; - int device_count; + int device_count, dev_index = 0; char vendor[256], product[256], serial[256]; + if (Modes.dev_name) { + if ( (dev_index = verbose_device_search(Modes.dev_name)) < 0 ) + return -1; + } + device_count = rtlsdr_get_device_count(); if (!device_count) { fprintf(stderr, "No supported RTLSDR devices found.\n"); - exit(1); + return -1; } fprintf(stderr, "Found %d device(s):\n", device_count); for (j = 0; j < device_count; j++) { rtlsdr_get_device_usb_strings(j, vendor, product, serial); fprintf(stderr, "%d: %s, %s, SN: %s %s\n", j, vendor, product, serial, - (j == Modes.dev_index) ? "(currently selected)" : ""); + (j == dev_index) ? "(currently selected)" : ""); } - if (rtlsdr_open(&Modes.dev, Modes.dev_index) < 0) { + if (rtlsdr_open(&Modes.dev, dev_index) < 0) { fprintf(stderr, "Error opening the RTLSDR device: %s\n", strerror(errno)); - exit(1); + return -1; } // Set gain, frequency, sample rate, and reset the device @@ -357,19 +362,36 @@ void readDataFromFile(void) { // We read data using a thread, so the main thread only handles decoding // without caring about data acquisition // + void *readerThreadEntryPoint(void *arg) { MODES_NOTUSED(arg); if (Modes.filename == NULL) { - rtlsdr_read_async(Modes.dev, rtlsdrCallback, NULL, + while (!Modes.exit) { + rtlsdr_read_async(Modes.dev, rtlsdrCallback, NULL, MODES_ASYNC_BUF_NUMBER, MODES_ASYNC_BUF_SIZE); + + if (!Modes.exit) { + fprintf(stderr, "Warning: lost the connection to the RTLSDR device.\n"); + rtlsdr_close(Modes.dev); + + do { + sleep(5); + fprintf(stderr, "Trying to reconnect to the RTLSDR device..\n"); + } while (!Modes.exit && modesInitRTLSDR() < 0); + } + } } else { readDataFromFile(); } - // Signal to the other thread that new data is ready - dummy really so threads don't mutually lock + + // Wake the main thread (if it's still waiting) + pthread_mutex_lock(&Modes.data_mutex); + Modes.exit = 1; pthread_cond_signal(&Modes.data_cond); pthread_mutex_unlock(&Modes.data_mutex); + #ifndef _WIN32 pthread_exit(NULL); #else @@ -721,7 +743,7 @@ int main(int argc, char **argv) { int more = j+1 < argc; // There are more arguments if (!strcmp(argv[j],"--device-index") && more) { - Modes.dev_index = verbose_device_search(argv[++j]); + Modes.dev_name = strdup(argv[++j]); } else if (!strcmp(argv[j],"--gain") && more) { Modes.gain = (int) (atof(argv[++j])*10); // Gain is in tens of DBs } else if (!strcmp(argv[j],"--enable-agc")) { @@ -879,7 +901,9 @@ int main(int argc, char **argv) { if (Modes.net_only) { fprintf(stderr,"Net-only mode, no RTL device or file open.\n"); } else if (Modes.filename == NULL) { - modesInitRTLSDR(); + if (modesInitRTLSDR() < 0) { + exit(1); + } } else { if (Modes.filename[0] == '-' && Modes.filename[1] == '\0') { Modes.fd = STDIN_FILENO; @@ -991,9 +1015,11 @@ int main(int argc, char **argv) { rtlsdr_cancel_async(Modes.dev); // Cancel rtlsdr_read_async will cause data input thread to terminate cleanly rtlsdr_close(Modes.dev); } - pthread_cond_destroy(&Modes.data_cond); // Thread cleanup - pthread_mutex_destroy(&Modes.data_mutex); + pthread_join(Modes.reader_thread,NULL); // Wait on reader thread exit + pthread_cond_destroy(&Modes.data_cond); // Thread cleanup - only after the reader thread is dead! + pthread_mutex_destroy(&Modes.data_mutex); + #ifndef _WIN32 pthread_exit(0); #else diff --git a/dump1090.h b/dump1090.h index 8ea6a2a33..b8a4b0077 100644 --- a/dump1090.h +++ b/dump1090.h @@ -311,7 +311,7 @@ struct { // Internal state int exit; // Exit from the main loop when true // RTLSDR - int dev_index; + char * dev_name; int gain; int enable_agc; rtlsdr_dev_t *dev; From 85eee61358fd44f6e89f7ad7ade4ea4534b911f5 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 2 Jan 2015 22:34:35 +0000 Subject: [PATCH 091/610] Oops, one unsaved file. --- dump1090.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dump1090.c b/dump1090.c index d2da11d68..dfbc7af9e 100644 --- a/dump1090.c +++ b/dump1090.c @@ -331,8 +331,9 @@ void readDataFromFile(void) { while(toread) { nread = read(Modes.fd, p, toread); if (nread <= 0) { + // Done. Modes.exit = 1; // Signal the other threads to exit. - break; + goto OUT; } p += nread; toread -= nread; @@ -355,6 +356,9 @@ void readDataFromFile(void) { // Signal to the other thread that new data is ready pthread_cond_signal(&Modes.data_cond); } + + OUT: + pthread_mutex_unlock(&Modes.data_mutex); } // //========================================================================= @@ -388,7 +392,7 @@ void *readerThreadEntryPoint(void *arg) { // Wake the main thread (if it's still waiting) pthread_mutex_lock(&Modes.data_mutex); - Modes.exit = 1; + Modes.exit = 1; // just in case pthread_cond_signal(&Modes.data_cond); pthread_mutex_unlock(&Modes.data_mutex); From 69532b3c531ffd676c450b595f6636ef2abb192e Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 2 Jan 2015 22:48:27 +0000 Subject: [PATCH 092/610] Center the webmap on the receiver location. Mark config.js as a conffile, so user changes won't get overwritten. --- debian/changelog | 2 ++ debian/dump1090-mutability.conffiles | 1 + public_html/script.js | 2 ++ 3 files changed, 5 insertions(+) create mode 100644 debian/dump1090-mutability.conffiles diff --git a/debian/changelog b/debian/changelog index c9d5ec58a..19a344643 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,6 +5,8 @@ dump1090-mutability (1.08.2302.14+1mu-7) UNRELEASED; urgency=medium lets dump1090 tolerate occasional USB glitches that make the dongle reset itself. * Fix some of the more glaring pthread bugs. + * Center the webmap on the receiver location. + * Mark config.js as a conffile, so user changes won't get overwritten. -- Oliver Jowett Fri, 02 Jan 2015 22:27:44 +0000 diff --git a/debian/dump1090-mutability.conffiles b/debian/dump1090-mutability.conffiles new file mode 100644 index 000000000..dd0d21754 --- /dev/null +++ b/debian/dump1090-mutability.conffiles @@ -0,0 +1 @@ +usr/share/dump1090-mutability/html/config.js diff --git a/public_html/script.js b/public_html/script.js index 1216fb4ee..2d95fb56c 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -60,6 +60,8 @@ function initialize() { SiteShow = true; SiteLat = data.lat; SiteLon = data.lon; + CONST_CENTERLAT = data.lat; + CONST_CENTERLON = data.lon; } Dump1090Version = data.version; From 66c088fa70e03eec5e7dad50195524eaadf6b20f Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Fri, 2 Jan 2015 22:53:58 +0000 Subject: [PATCH 093/610] Release changelog. --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 19a344643..7fc110dd0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -dump1090-mutability (1.08.2302.14+1mu-7) UNRELEASED; urgency=medium +dump1090-mutability (1.08.2302.14+1mu-7) unstable; urgency=medium * Notice if we lose the RTLSDR device, and try to reconnect when that happens. This lets you unplug/replug the RTLSDR without dump1090 exiting, and also @@ -8,7 +8,7 @@ dump1090-mutability (1.08.2302.14+1mu-7) UNRELEASED; urgency=medium * Center the webmap on the receiver location. * Mark config.js as a conffile, so user changes won't get overwritten. - -- Oliver Jowett Fri, 02 Jan 2015 22:27:44 +0000 + -- Oliver Jowett Fri, 02 Jan 2015 22:53:55 +0000 dump1090-mutability (1.08.2302.14+1mu-6) unstable; urgency=medium From 14a8e71629b01ac3c0425f4b062c4d940cf0254a Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sun, 4 Jan 2015 12:32:36 +0000 Subject: [PATCH 094/610] Version bump to reflect upstream version. --- debian/changelog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/debian/changelog b/debian/changelog index 7fc110dd0..50b30fbd8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +dump1090-mutability (1.10.3010.14mu-8) unstable; urgency=medium + + * Version-only rebuild to reflect the upstream dump1090 version we + have merged from. Retaining the trailing build number to avoid + confusion (as far as that's possible given this versioning system!) + + -- Oliver Jowett Sun, 04 Jan 2015 12:32:30 +0000 + dump1090-mutability (1.08.2302.14+1mu-7) unstable; urgency=medium * Notice if we lose the RTLSDR device, and try to reconnect when that happens. From 76474f58ab46d0cd29681a9d8a976a4ebcf3d8c6 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sun, 4 Jan 2015 20:08:33 +0000 Subject: [PATCH 095/610] Fix warnings. Add -Werror so they break the build in future. --- Makefile | 2 +- debian/changelog | 6 ++++++ dump1090.c | 5 +++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2e0f2c01f..698cd1538 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ SHAREDIR=$(PREFIX)/share/$(PROGNAME) EXTRACFLAGS=-DHTMLPATH=\"$(SHAREDIR)\" endif -CFLAGS+=-O2 -g -Wall -W `pkg-config --cflags librtlsdr` +CFLAGS+=-O2 -g -Wall -Werror -W `pkg-config --cflags librtlsdr` LIBS=-lpthread -lm -lrt LIBS_RTL=`pkg-config --libs librtlsdr` CC=gcc diff --git a/debian/changelog b/debian/changelog index 50b30fbd8..8cf5eb2cf 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +dump1090-mutability (1.10.3010.14mu-9) UNRELEASED; urgency=medium + + * Fix warnings. Add -Werror so they break the build in future. + + -- Oliver Jowett Sun, 04 Jan 2015 20:08:01 +0000 + dump1090-mutability (1.10.3010.14mu-8) unstable; urgency=medium * Version-only rebuild to reflect the upstream dump1090 version we diff --git a/dump1090.c b/dump1090.c index dfbc7af9e..a481e4478 100644 --- a/dump1090.c +++ b/dump1090.c @@ -28,6 +28,9 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #include "dump1090.h" + +static int verbose_device_search(char *s); + // // ============================= Utility functions ========================== // @@ -255,6 +258,8 @@ int modesInitRTLSDR(void) { rtlsdr_reset_buffer(Modes.dev); fprintf(stderr, "Gain reported by device: %.2f\n", rtlsdr_get_tuner_gain(Modes.dev)/10.0); + + return 0; } // //========================================================================= From 43ec58c78ec7a24b55adc8ed10dfc5a6655c8212 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sun, 4 Jan 2015 20:09:38 +0000 Subject: [PATCH 096/610] Remove dead tracking code related to the removed PlanePlotter feed. --- debian/changelog | 1 + dump1090.c | 1 - dump1090.h | 16 --------- interactive.c | 87 ------------------------------------------------ view1090.c | 1 - 5 files changed, 1 insertion(+), 105 deletions(-) diff --git a/debian/changelog b/debian/changelog index 8cf5eb2cf..ee8e4bfe3 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,7 @@ dump1090-mutability (1.10.3010.14mu-9) UNRELEASED; urgency=medium * Fix warnings. Add -Werror so they break the build in future. + * Remove dead tracking code related to the removed PlanePlotter feed. -- Oliver Jowett Sun, 04 Jan 2015 20:08:01 +0000 diff --git a/dump1090.c b/dump1090.c index a481e4478..ec81c9f57 100644 --- a/dump1090.c +++ b/dump1090.c @@ -94,7 +94,6 @@ void modesInitConfig(void) { void modesInit(void) { int i, q; - pthread_mutex_init(&Modes.pDF_mutex,NULL); pthread_mutex_init(&Modes.data_mutex,NULL); pthread_cond_init(&Modes.data_cond,NULL); diff --git a/dump1090.h b/dump1090.h index b8a4b0077..b9ebcb792 100644 --- a/dump1090.h +++ b/dump1090.h @@ -250,16 +250,6 @@ struct aircraft { struct aircraft *next; // Next aircraft in our linked list }; -struct stDF { - struct stDF *pNext; // Pointer to next item in the linked list - struct stDF *pPrev; // Pointer to previous item in the linked list - struct aircraft *pAircraft; // Pointer to the Aircraft structure for this DF - time_t seen; // Dos/UNIX Time at which the this packet was received - uint64_t llTimestamp; // Timestamp at which the this packet was received - uint32_t addr; // Timestamp at which the this packet was received - unsigned char msg[MODES_LONG_MSG_BYTES]; // the binary -} tDF; - // Common stats for non-phase-corrected vs phase-corrected cases struct demod_stats { unsigned int demodulated0; @@ -383,11 +373,6 @@ struct { // Internal state uint64_t interactive_last_update; // Last screen update in milliseconds time_t last_cleanup_time; // Last cleanup time in seconds - // DF List mode - int bEnableDFLogging; // Set to enable DF Logging - pthread_mutex_t pDF_mutex; // Mutex to synchronize pDF access - struct stDF *pDF; // Pointer to DF list - // Statistics unsigned int stat_preamble_no_correlation; unsigned int stat_preamble_not_quiet; @@ -487,7 +472,6 @@ void interactiveShowData(void); void interactiveRemoveStaleAircrafts(void); int decodeBinMessage (struct client *c, char *p); struct aircraft *interactiveFindAircraft(uint32_t addr); -struct stDF *interactiveFindDF (uint32_t addr); // // Functions exported from net_io.c diff --git a/interactive.c b/interactive.c index 95af5510a..75a6f185a 100644 --- a/interactive.c +++ b/interactive.c @@ -41,87 +41,7 @@ static uint64_t mstime(void) { mst += tv.tv_usec/1000; return mst; } -// -//========================================================================= -// -// Add a new DF structure to the interactive mode linked list -// -void interactiveCreateDF(struct aircraft *a, struct modesMessage *mm) { - struct stDF *pDF = (struct stDF *) malloc(sizeof(*pDF)); - - if (pDF) { - // Default everything to zero/NULL - memset(pDF, 0, sizeof(*pDF)); - - // Now initialise things - pDF->seen = a->seen; - pDF->llTimestamp = mm->timestampMsg; - pDF->addr = mm->addr; - pDF->pAircraft = a; - memcpy(pDF->msg, mm->msg, MODES_LONG_MSG_BYTES); - - if (!pthread_mutex_lock(&Modes.pDF_mutex)) { - if ((pDF->pNext = Modes.pDF)) { - Modes.pDF->pPrev = pDF; - } - Modes.pDF = pDF; - pthread_mutex_unlock(&Modes.pDF_mutex); - } else { - free(pDF); - } - } -} -// -// Remove stale DF's from the interactive mode linked list -// -void interactiveRemoveStaleDF(time_t now) { - struct stDF *pDF = NULL; - struct stDF *prev = NULL; - - // Only fiddle with the DF list if we gain possession of the mutex - // If we fail to get the mutex we'll get another chance to tidy the - // DF list in a second or so. - if (!pthread_mutex_trylock(&Modes.pDF_mutex)) { - pDF = Modes.pDF; - while(pDF) { - if ((now - pDF->seen) > Modes.interactive_delete_ttl) { - if (Modes.pDF == pDF) { - Modes.pDF = NULL; - } else { - prev->pNext = NULL; - } - - // All DF's in the list from here onwards will be time - // expired, so delete them all - while (pDF) { - prev = pDF; pDF = pDF->pNext; - free(prev); - } - - } else { - prev = pDF; pDF = pDF->pNext; - } - } - pthread_mutex_unlock (&Modes.pDF_mutex); - } -} - -struct stDF *interactiveFindDF(uint32_t addr) { - struct stDF *pDF = NULL; - if (!pthread_mutex_lock(&Modes.pDF_mutex)) { - pDF = Modes.pDF; - while(pDF) { - if (pDF->addr == addr) { - pthread_mutex_unlock (&Modes.pDF_mutex); - return (pDF); - } - pDF = pDF->pNext; - } - pthread_mutex_unlock (&Modes.pDF_mutex); - } - return (NULL); -} // //========================= Interactive mode =============================== // @@ -396,11 +316,6 @@ struct aircraft *interactiveReceiveData(struct modesMessage *mm) { } } - // If we are Logging DF's, and it's not a Mode A/C - if ((Modes.bEnableDFLogging) && (mm->msgtype < 32)) { - interactiveCreateDF(a,mm); - } - return (a); } // @@ -539,8 +454,6 @@ void interactiveRemoveStaleAircrafts(void) { if (Modes.last_cleanup_time != now) { Modes.last_cleanup_time = now; - interactiveRemoveStaleDF(now); - while(a) { if ((now - a->seen) > Modes.interactive_delete_ttl) { // Remove the element from the linked list, with care diff --git a/view1090.c b/view1090.c index 1e2bb073a..e45dcb876 100644 --- a/view1090.c +++ b/view1090.c @@ -82,7 +82,6 @@ void view1090InitConfig(void) { // void view1090Init(void) { - pthread_mutex_init(&Modes.pDF_mutex,NULL); pthread_mutex_init(&Modes.data_mutex,NULL); pthread_cond_init(&Modes.data_cond,NULL); From 99bca13844df37fe2a1d15811a039313aac9d43b Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sun, 4 Jan 2015 20:23:30 +0000 Subject: [PATCH 097/610] Release changelog. --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index ee8e4bfe3..884b953e9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,9 +1,9 @@ -dump1090-mutability (1.10.3010.14mu-9) UNRELEASED; urgency=medium +dump1090-mutability (1.10.3010.14mu-9) unstable; urgency=medium * Fix warnings. Add -Werror so they break the build in future. * Remove dead tracking code related to the removed PlanePlotter feed. - -- Oliver Jowett Sun, 04 Jan 2015 20:08:01 +0000 + -- Oliver Jowett Sun, 04 Jan 2015 20:23:26 +0000 dump1090-mutability (1.10.3010.14mu-8) unstable; urgency=medium From 1f06abf67d9c4587c08b7e3edfc44e8ae08a6735 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Mon, 5 Jan 2015 23:20:03 +0000 Subject: [PATCH 098/610] WIP webmap cleanup --- debian/changelog | 6 + net_io.c | 77 ++++----- public_html/planeObject.js | 124 ++++++-------- public_html/script.js | 319 +++++++++++++++++-------------------- 4 files changed, 240 insertions(+), 286 deletions(-) diff --git a/debian/changelog b/debian/changelog index 884b953e9..8ed7acff8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +dump1090-mutability (1.10.3010.14mu-10) UNRELEASED; urgency=medium + + * JSON / webmap cleanups. + + -- Oliver Jowett Mon, 05 Jan 2015 21:43:58 +0000 + dump1090-mutability (1.10.3010.14mu-9) unstable; urgency=medium * Fix warnings. Add -Werror so they break the build in future. diff --git a/net_io.c b/net_io.c index 1d60c837a..eeefb211c 100644 --- a/net_io.c +++ b/net_io.c @@ -671,58 +671,59 @@ char *generateAircraftJson(int *len) { time_t now = time(NULL); struct aircraft *a = Modes.aircrafts; int buflen = 1024; // The initial buffer is incremented as needed - char *buf = (char *) malloc(buflen), *p = buf; - int l; + char *buf = (char *) malloc(buflen), *p = buf, *end = buf+buflen; + int first = 1; - l = snprintf(p,buflen,"[\n"); - p += l; buflen -= l; - while(a) { - int position = 0; - int track = 0; + p += snprintf(p, end-p, + "{ \"now\" : %d,\n" + " \"aircraft\" : [", + (int)now); + while(a) { if (a->modeACflags & MODEAC_MSG_FLAG) { // skip any fudged ICAO records Mode A/C a = a->next; continue; } - if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) { - position = 1; - } - - if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) { - track = 1; - } - - // No metric conversion - l = snprintf(p,buflen, - "{\"hex\":\"%06x\", \"squawk\":\"%04x\", \"flight\":\"%s\", \"lat\":%f, " - "\"lon\":%f, \"validposition\":%d, \"altitude\":%d, \"vert_rate\":%d,\"track\":%d, \"validtrack\":%d," - "\"speed\":%d, \"messages\":%ld, \"seen\":%d},\n", - a->addr, a->modeA, a->flight, a->lat, a->lon, position, a->altitude, a->vert_rate, a->track, track, - a->speed, a->messages, (int)(now - a->seen)); - p += l; buflen -= l; + if (first) + first = 0; + else + *p++ = ','; + + p += snprintf(p, end-p, "\n {\"hex\":\"%06x\"", a->addr); + if (a->bFlags & MODES_ACFLAGS_SQUAWK_VALID) + p += snprintf(p, end-p, ",\"squawk\":\"%04x\"", a->modeA); + if (a->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) + p += snprintf(p, end-p, ",\"flight\":\"%s\"", a->flight); + if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) + p += snprintf(p, end-p, ",\"lat\":%f,\"lon\":%f", a->lat, a->lon); + if ((a->bFlags & MODES_ACFLAGS_AOG_VALID) && (a->bFlags & MODES_ACFLAGS_AOG)) + p += snprintf(p, end-p, ",\"altitude\":\"ground\""); + else if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) + p += snprintf(p, end-p, ",\"altitude\":%d", a->altitude); + if (a->bFlags & MODES_ACFLAGS_VERTRATE_VALID) + p += snprintf(p, end-p, ",\"vert_rate\":%d", a->vert_rate); + if (a->bFlags & MODES_ACFLAGS_HEADING_VALID) + p += snprintf(p, end-p, ",\"track\":%d", a->track); + if (a->bFlags & MODES_ACFLAGS_SPEED_VALID) + p += snprintf(p, end-p, ",\"speed\":%d", a->speed); + + p += snprintf(p, end-p, ",\"messages\":%ld, \"seen\":%d}", + a->messages, (int)(now - a->seen)); - //Resize if needed - if (buflen < 256) { - int used = p-buf; - buflen += 1024; // Our increment. - buf = (char *) realloc(buf,used+buflen); + // If we're getting near the end of the buffer, expand it. + if ((end - p) < 256) { + int used = p - buf; + buflen *= 2; + buf = (char *) realloc(buf, buflen); p = buf+used; + end = buf + buflen; } a = a->next; } - //Remove the final comma if any, and closes the json array. - if (*(p-2) == ',') { - *(p-2) = '\n'; - p--; - buflen++; - } - - l = snprintf(p,buflen,"]\n"); - p += l; buflen -= l; - + p += snprintf(p, end-p, "\n ]\n}\n"); *len = p-buf; return buf; } diff --git a/public_html/planeObject.js b/public_html/planeObject.js index 98153fd57..a4748af4f 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -20,10 +20,6 @@ var planeObject = { messages : null, seen : null, - // Vaild... - vPosition : false, - vTrack : false, - // GMap Details marker : null, markerColor : MarkerColor, @@ -130,85 +126,69 @@ var planeObject = { }, // Update our data - funcUpdateData : function(data){ - // So we can find out if we moved - var oldlat = this.latitude; - var oldlon = this.longitude; - var oldalt = this.altitude; - - // Update all of our data - this.updated = new Date().getTime(); + funcUpdateData : function(receiver_now,data){ + // So we can find out if we moved + var oldlat = this.latitude; + var oldlon = this.longitude; + var oldalt = this.altitude; + + // Update all of our data + this.updated = new Date().getTime(); + this.icao = data.hex; + this.messages = data.messages; + this.seen = data.seen; + + if (typeof data.altitude !== "undefined") this.altitude = data.altitude; - this.speed = data.speed; - this.track = data.track; + if (typeof data.speed !== "undefined") + this.speed = data.speed; + if (typeof data.track !== "undefined") + this.track = data.track; + if (typeof data.lat !== "undefined") { this.latitude = data.lat; this.longitude = data.lon; + } + if (typeof data.flight !== "undefined") this.flight = data.flight; + if (typeof data.squawk !== "undefined") this.squawk = data.squawk; - this.icao = data.hex; - this.messages = data.messages; - this.seen = data.seen; - // If no packet in over 58 seconds, consider the plane reapable - // This way we can hold it, but not show it just in case the plane comes back - if (this.seen > 58) { - this.reapable = true; - if (this.marker) { - this.marker.setMap(null); - this.marker = null; - } - if (this.line) { - this.line.setMap(null); - this.line = null; - } - if (SelectedPlane == this.icao) { - if (this.is_selected) { - this.is_selected = false; - } - SelectedPlane = null; - } - } else { - if (this.reapable == true) { + // If no packet in over 58 seconds, consider the plane reapable + // This way we can hold it, but not show it just in case the plane comes back + if (this.seen > 58) { + this.reapable = true; + if (this.marker) { + this.marker.setMap(null); + this.marker = null; + } + if (this.line) { + this.line.setMap(null); + this.line = null; + } + if (SelectedPlane == this.icao) { + if (this.is_selected) { + this.is_selected = false; } - this.reapable = false; + SelectedPlane = null; } + } else { + this.reapable = false; + } - // Is the position valid? - if ((data.validposition == 1) && (this.reapable == false)) { - this.vPosition = true; - - // Detech if the plane has moved - changeLat = false; - changeLon = false; - changeAlt = false; - if (oldlat != this.latitude) { - changeLat = true; - } - if (oldlon != this.longitude) { - changeLon = true; + // Is the position valid? + if (!this.reapable && typeof data.lat !== "undefined") { + // Detech if the plane has moved + if (oldlat != this.latitude || oldlon != this.longitude) { + this.funcAddToTrack(); + if (this.is_selected) { + this.line = this.funcUpdateLines(); } - if (oldalt != this.altitude) { - changeAlt = true; - } - // Right now we only care about lat/long, if alt is updated only, oh well - if ((changeLat == true) || (changeLon == true)) { - this.funcAddToTrack(); - if (this.is_selected) { - this.line = this.funcUpdateLines(); - } - } - this.marker = this.funcUpdateMarker(); - PlanesOnMap++; - } else { - this.vPosition = false; } - // Do we have a valid track for the plane? - if (data.validtrack == 1) - this.vTrack = true; - else - this.vTrack = false; - }, + this.marker = this.funcUpdateMarker(); + PlanesOnMap++; + } + }, // Update our marker on the map funcUpdateMarker: function() { @@ -231,7 +211,7 @@ var planeObject = { } // Setting the marker title - if (this.flight.length == 0) { + if (this.flight === null || this.flight.length == 0) { this.marker.setTitle(this.hex); } else { this.marker.setTitle(this.flight+' ('+this.icao+')'); diff --git a/public_html/script.js b/public_html/script.js index 2d95fb56c..81b500298 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -7,10 +7,9 @@ var PlanesToReap = 0; var SelectedPlane = null; var SpecialSquawk = false; -var iSortCol=-1; -var bSortASC=true; -var bDefaultSortASC=true; -var iDefaultSortCol=3; +var sortColumn = 3; +var sortAscending = true; +var sortNumeric = true; // Get current map settings CenterLat = Number(localStorage['CenterLat']) || CONST_CENTERLAT; @@ -22,32 +21,34 @@ RefreshInterval = 1000; function fetchData() { $.getJSON('data/aircraft.json', function(data) { - PlanesOnMap = 0 + PlanesOnMap = 0; SpecialSquawk = false; // Loop through all the planes in the data packet - for (var j=0; j < data.length; j++) { + console.log("I was called\n"); + var now = data.now; + var acs = data.aircraft; + for (var j=0; j < acs.length; j++) { + var ac = acs[j]; // Do we already have this plane object in Planes? // If not make it. - if (Planes[data[j].hex]) { - var plane = Planes[data[j].hex]; + if (Planes[ac.hex]) { + var plane = Planes[ac.hex]; } else { var plane = jQuery.extend(true, {}, planeObject); + Planes[ac.hex] = plane; } // Set SpecialSquawk-value - if (data[j].squawk == '7500' || data[j].squawk == '7600' || data[j].squawk == '7700') { + if (ac.squawk == '7500' || ac.squawk == '7600' || ac.squawk == '7700') { SpecialSquawk = true; } // Call the function update - plane.funcUpdateData(data[j]); - - // Copy the plane into Planes - Planes[plane.icao] = plane; + plane.funcUpdateData(now, ac); } - PlanesOnTable = data.length; + PlanesOnTable = acs.length; }); } @@ -291,15 +292,17 @@ function refreshSelected() { } html += ''; - if (selected) { - if (Metric) { - html += 'Altitude: ' + Math.round(selected.altitude / 3.2828) + ' m'; + if (selected && selected.altitude !== null) { + if (selected.altitude === "ground") + html += 'Altitude: on ground'; + else if (Metric) { + html += 'Altitude: ' + Math.round(selected.altitude / 3.2828) + ' m'; + } else { + html += 'Altitude: ' + selected.altitude + ' ft'; + } } else { - html += 'Altitude: ' + selected.altitude + ' ft'; + html += 'Altitude: n/a'; } - } else { - html += 'Altitude: n/a'; - } if (selected && selected.squawk != '0000') { html += 'Squawk: ' + selected.squawk + ''; @@ -326,15 +329,15 @@ function refreshSelected() { } html += 'Track: ' - if (selected && selected.vTrack) { - html += selected.track + '°' + ' (' + normalizeTrack(selected.track, selected.vTrack)[1] +')'; + if (selected && selected.track !== null) { + html += selected.track + '°' + ' (' + trackLongName(selected.track) +')'; } else { html += 'n/a'; } html += ' '; html += 'Lat/Long: '; - if (selected && selected.vPosition) { + if (selected && selected.latitude !== null) { html += selected.latitude + ', ' + selected.longitude + ''; // Let's show some extra data if we have site coordinates @@ -366,73 +369,38 @@ function refreshSelected() { document.getElementById('plane_detail').innerHTML = html; } -// Right now we have no means to validate the speed is good -// Want to return (n/a) when we dont have it -// TODO: Edit C code to add a valid speed flag -// TODO: Edit js code to use said flag -function normalizeSpeed(speed, valid) { - return speed +function trackShortName(track) { + var trackIndex = Math.floor((track+22.5) / 45); + if ((trackIndex < 0) || (trackIndex >= 8)) + return "n/a"; + return ["N","NE","E","SE","S","SW","W","NW"][trackIndex]; } -// Returns back a long string, short string, and the track if we have a vaild track path -function normalizeTrack(track, valid){ - x = [] - if ((track > -1) && (track < 22.5)) { - x = ["North", "N", track] - } - if ((track > 22.5) && (track < 67.5)) { - x = ["North East", "NE", track] - } - if ((track > 67.5) && (track < 112.5)) { - x = ["East", "E", track] - } - if ((track > 112.5) && (track < 157.5)) { - x = ["South East", "SE", track] - } - if ((track > 157.5) && (track < 202.5)) { - x = ["South", "S", track] - } - if ((track > 202.5) && (track < 247.5)) { - x = ["South West", "SW", track] - } - if ((track > 247.5) && (track < 292.5)) { - x = ["West", "W", track] - } - if ((track > 292.5) && (track < 337.5)) { - x = ["North West", "NW", track] - } - if ((track > 337.5) && (track < 361)) { - x = ["North", "N", track] - } - if (!valid) { - x = [" ", "n/a", ""] - } - return x +function trackLongName(track) { + var trackIndex = Math.floor((track+22.5) / 45); + if ((trackIndex < 0) || (trackIndex >= 8)) + return "n/a"; + return ["North","Northeast","East","Southeast","South","Southwest","West","Northwest"][trackIndex]; } // Refeshes the larger table of all the planes + function refreshTableInfo() { var html = ''; html += ''; - html += ''; - html += ''; - html += ''; - html += ''; - html += ''; - // Add distance column header to table if site coordinates are provided + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + // Add distance column header to table if site coordinates are provided if (SiteShow && (typeof SiteLat !== 'undefined' || typeof SiteLon !== 'undefined')) { - html += ''; + html += ''; } - html += ''; - html += ''; - html += ''; + html += ''; + html += ''; + html += ''; + for (var tablep in Planes) { var tableplane = Planes[tablep] if (!tableplane.reapable) { @@ -454,70 +422,76 @@ function refreshTableInfo() { specialStyle += " squawk7700"; } - if (tableplane.vPosition == true) { + if (tableplane.latitude !== null) html += ''; - } else { + else html += ''; - } - + html += ''; - html += ''; - if (tableplane.squawk != '0000' ) { - html += ''; - } else { - html += ''; - } - - if (Metric) { - html += ''; - html += ''; - } else { - html += ''; - html += ''; - } + + if (tableplane.flight !== null) + html += ''; + else + html += ''; + + if (tableplane.squawk !== null) + html += ''; + else + html += ''; + + if (tableplane.altitude === null) + html += ''; + else if (tableplane.altitude === "ground") + html += ''; + else if (Metric) + html += ''; + else + html += ''; + + if (tableplane.speed === null) + html += ''; + else if (Metric) + html += ''; + else + html += ''; + // Add distance column to table if site coordinates are provided if (SiteShow && (typeof SiteLat !== 'undefined' || typeof SiteLon !== 'undefined')) { - html += ''; + html += ''; } html += ''; + if (tableplane.track !== null) + html += tableplane.track; + html += ''; html += ''; html += ''; html += ''; } } html += '
ICAOFlightSquawkAltitudeSpeedICAOFlightSquawkAltitudeSpeedDistanceDistanceTrackMsgsSeen
TrackMsgsSeen
' + tableplane.icao + '' + tableplane.flight + '' + tableplane.squawk + ' ' + Math.round(tableplane.altitude / 3.2828) + '' + Math.round(tableplane.speed * 1.852) + '' + tableplane.altitude + '' + tableplane.speed + '' + tableplane.flight + '' + tableplane.squawk + ' ground' + Math.round(tableplane.altitude / 3.2828) + '' + tableplane.altitude + ' ' + Math.round(tableplane.speed * 1.852) + '' + tableplane.speed + ''; - if (tableplane.vPosition) { - var siteLatLon = new google.maps.LatLng(SiteLat, SiteLon); - var planeLatLon = new google.maps.LatLng(tableplane.latitude, tableplane.longitude); - var dist = google.maps.geometry.spherical.computeDistanceBetween (siteLatLon, planeLatLon); - if (Metric) { - dist /= 1000; - } else { - dist /= 1852; - } - dist = (Math.round((dist)*10)/10).toFixed(1); - html += dist; - } else { - html += '0'; - } - html += ''; + if (tableplane.latitude !== null) { + var siteLatLon = new google.maps.LatLng(SiteLat, SiteLon); + var planeLatLon = new google.maps.LatLng(tableplane.latitude, tableplane.longitude); + var dist = google.maps.geometry.spherical.computeDistanceBetween (siteLatLon, planeLatLon); + if (Metric) { + dist /= 1000; + } else { + dist /= 1852; + } + dist = (Math.round((dist)*10)/10).toFixed(1); + html += dist; + } + html += ''; - if (tableplane.vTrack) { - html += normalizeTrack(tableplane.track, tableplane.vTrack)[2]; - // html += ' (' + normalizeTrack(tableplane.track, tableplane.vTrack)[1] + ')'; - } else { - html += ' '; - } - html += '' + tableplane.messages + '' + tableplane.seen + '
'; - + document.getElementById('planes_table').innerHTML = html; - + if (SpecialSquawk) { - $('#SpecialSquawkWarning').css('display', 'inline'); - } else { - $('#SpecialSquawkWarning').css('display', 'none'); - } - + $('#SpecialSquawkWarning').css('display', 'inline'); + } else { + $('#SpecialSquawkWarning').css('display', 'none'); + } + // Click event for table $('#planes_table').find('tr').click( function(){ var hex = $(this).find('td:first').text(); @@ -527,65 +501,59 @@ function refreshTableInfo() { refreshSelected(); } }); - - sortTable("tableinfo"); + + resortTable(); } -// Credit goes to a co-worker that needed a similar functions for something else -// we get a copy of it free ;) -function setASC_DESC(iCol) { - if(iSortCol==iCol) { - bSortASC=!bSortASC; - } else { - bSortASC=bDefaultSortASC; - } +function sortBy(colName,numeric) { + var header_cells = document.getElementById('tableinfo').tHead.rows[0].cells; + for (var i = 0; i < header_cells.length; ++i) { + if (header_cells[i].id === colName) { + if (i == sortColumn) + sortAscending = !sortAscending; + else { + sortColumn = i; + sortNumeric = numeric; + sortAscending = true; + } + + resortTable(); + return; + } + } + + console.warn("column not found: " + colName); } -function sortTable(szTableID,iCol) { - //if iCol was not provided, and iSortCol is not set, assign default value - if (typeof iCol==='undefined'){ - if(iSortCol!=-1){ - var iCol=iSortCol; - } else if (SiteShow && (typeof SiteLat !== 'undefined' || typeof SiteLon !== 'undefined')) { - var iCol=5; - } else { - var iCol=iDefaultSortCol; - } - } +function resortTable() { + sortTable('tableinfo', sortColumn, sortAscending, sortNumeric); +} +function sortTable(tableId, col, asc, numeric) { //retrieve passed table element - var oTbl=document.getElementById(szTableID).tBodies[0]; + var oTbl=document.getElementById(tableId).tBodies[0]; var aStore=[]; - - //If supplied col # is greater than the actual number of cols, set sel col = to last col - if (typeof oTbl.rows[0] !== 'undefined' && oTbl.rows[0].cells.length <= iCol) { - iCol=(oTbl.rows[0].cells.length-1); - } - - //store the col # - iSortCol=iCol; - - //determine if we are delaing with numerical, or alphanumeric content - var bNumeric = false; - if ((typeof oTbl.rows[0] !== 'undefined') && - (!isNaN(parseFloat(oTbl.rows[0].cells[iSortCol].textContent || - oTbl.rows[0].cells[iSortCol].innerText)))) { - bNumeric = true; - } - + //loop through the rows, storing each one inro aStore - for (var i=0,iLen=oTbl.rows.length;i Date: Tue, 6 Jan 2015 01:00:44 +0000 Subject: [PATCH 099/610] More WIP on the webmap, mostly fixing up track history. --- net_io.c | 2 +- public_html/planeObject.js | 119 +++++++++++++++++++++++-------------- public_html/script.js | 5 +- 3 files changed, 79 insertions(+), 47 deletions(-) diff --git a/net_io.c b/net_io.c index eeefb211c..5d2a1a89f 100644 --- a/net_io.c +++ b/net_io.c @@ -696,7 +696,7 @@ char *generateAircraftJson(int *len) { if (a->bFlags & MODES_ACFLAGS_CALLSIGN_VALID) p += snprintf(p, end-p, ",\"flight\":\"%s\"", a->flight); if (a->bFlags & MODES_ACFLAGS_LATLON_VALID) - p += snprintf(p, end-p, ",\"lat\":%f,\"lon\":%f", a->lat, a->lon); + p += snprintf(p, end-p, ",\"lat\":%f,\"lon\":%f,\"seen_pos\":%d", a->lat, a->lon, (int)(now - a->seenLatLon)); if ((a->bFlags & MODES_ACFLAGS_AOG_VALID) && (a->bFlags & MODES_ACFLAGS_AOG)) p += snprintf(p, end-p, ",\"altitude\":\"ground\""); else if (a->bFlags & MODES_ACFLAGS_ALTITUDE_VALID) diff --git a/public_html/planeObject.js b/public_html/planeObject.js index a4748af4f..bbd14cc28 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -1,8 +1,4 @@ var planeObject = { - oldlat : null, - oldlon : null, - oldalt : null, - // Basic location information altitude : null, speed : null, @@ -24,28 +20,72 @@ var planeObject = { marker : null, markerColor : MarkerColor, lines : [], - trackdata : new Array(), - trackline : new Array(), + tracklinesegs : [], + last_position_time : null, + // When was this last updated? updated : null, reapable : false, // Appends data to the running track so we can get a visual tail on the plane // Only useful for a long running browser session. - funcAddToTrack : function(){ - // TODO: Write this function out - this.trackdata.push([this.latitude, this.longitude, this.altitude, this.track, this.speed]); - this.trackline.push(new google.maps.LatLng(this.latitude, this.longitude)); - }, + updateTrack : function() { + var here = new google.maps.LatLng(this.latitude, this.longitude); + if (this.tracklinesegs.length == 0) { + // Brand new track + //console.log(this.icao + " new track"); + var newseg = { track : new google.maps.MVCArray([here,here]), + line : null, + head_update : this.last_position_time, + tail_update : this.last_position_time, + estimated : false }; + this.tracklinesegs.push(newseg); + } else { + var lastseg = this.tracklinesegs[this.tracklinesegs.length - 1]; + var lastpos = lastseg.track.getAt(lastseg.track.getLength() - 1); + var elapsed = (this.last_position_time - lastseg.head_update); + if (elapsed > 5) { + // >5s gap in data, put an estimated segment in + //console.log(this.icao + " discontinuity seen: " + lastpos.lat() + "," + lastpos.lng() + " -> " + here.lat() + "," + here.lng()); + var estseg = { track : new google.maps.MVCArray([lastpos, here]), + line : null, + estimated : true }; + var newseg = { track : new google.maps.MVCArray([here,here]), + line : null, + head_update : this.last_position_time, + tail_update : this.last_position_time, + estimated : false }; + this.tracklinesegs.push(estseg); + this.tracklinesegs.push(newseg); + } else if (elapsed > 0) { + // New position data + // We only retain some historical points, at 5+ second intervals, + // plus the most recent point + if (this.last_position_time - lastseg.tail_update >= 5) { + // enough time has elapsed; retain the last point and add a new one + //console.log(this.icao + " retain last point"); + lastseg.track.push(here); + lastseg.tail_update = lastseg.head_update; + } else { + // replace the last point with the current position + lastseg.track.setAt(lastseg.track.getLength()-1, here); + } + lastseg.head_update = this.last_position_time; + } + } + }, // This is to remove the line from the screen if we deselect the plane funcClearLine : function() { - if (this.line) { - this.line.setMap(null); - this.line = null; - } - }, + for (var i = 0; i < this.tracklinesegs.length; ++i) { + var seg = this.tracklinesegs[i]; + if (seg.line !== null) { + seg.line.setMap(null); + seg.line = null; + } + } + }, // Should create an icon for us to use on the map... funcGetIcon : function() { @@ -127,11 +167,6 @@ var planeObject = { // Update our data funcUpdateData : function(receiver_now,data){ - // So we can find out if we moved - var oldlat = this.latitude; - var oldlon = this.longitude; - var oldalt = this.altitude; - // Update all of our data this.updated = new Date().getTime(); this.icao = data.hex; @@ -147,6 +182,7 @@ var planeObject = { if (typeof data.lat !== "undefined") { this.latitude = data.lat; this.longitude = data.lon; + this.last_position_time = receiver_now - data.seen_pos; } if (typeof data.flight !== "undefined") this.flight = data.flight; @@ -161,10 +197,7 @@ var planeObject = { this.marker.setMap(null); this.marker = null; } - if (this.line) { - this.line.setMap(null); - this.line = null; - } + this.funcClearLines(); if (SelectedPlane == this.icao) { if (this.is_selected) { this.is_selected = false; @@ -177,12 +210,9 @@ var planeObject = { // Is the position valid? if (!this.reapable && typeof data.lat !== "undefined") { - // Detech if the plane has moved - if (oldlat != this.latitude || oldlon != this.longitude) { - this.funcAddToTrack(); - if (this.is_selected) { - this.line = this.funcUpdateLines(); - } + this.updateTrack(); + if (this.is_selected) { + this.funcUpdateLines(); } this.marker = this.funcUpdateMarker(); @@ -223,18 +253,21 @@ var planeObject = { // TODO: Make this multi colored based on options // altitude (default) or speed funcUpdateLines: function() { - if (this.line) { - var path = this.line.getPath(); - path.push(new google.maps.LatLng(this.latitude, this.longitude)); - } else { - this.line = new google.maps.Polyline({ - strokeColor: '#000000', + for (var i = 0; i < this.tracklinesegs.length; ++i) { + var seg = this.tracklinesegs[i]; + if (seg.line === null) { + // console.log("create line for seg " + i + " with " + seg.track.getLength() + " points" + (seg.estimated ? " (estimated)" : "")); + // for (var j = 0; j < seg.track.getLength(); j++) { + // console.log(" point " + j + " at " + seg.track.getAt(j).lat() + "," + seg.track.getAt(j).lng()); + // } + + seg.line = new google.maps.Polyline({ + strokeColor: (seg.estimated ? '#804040' : '#000000'), strokeOpacity: 1.0, - strokeWeight: 3, + strokeWeight: (seg.estimated ? 2 : 3), map: GoogleMap, - path: this.trackline - }); - } - return this.line; - } + path: seg.track }); + } + } + } }; diff --git a/public_html/script.js b/public_html/script.js index 81b500298..e5a176731 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -25,7 +25,6 @@ function fetchData() { SpecialSquawk = false; // Loop through all the planes in the data packet - console.log("I was called\n"); var now = data.now; var acs = data.aircraft; for (var j=0; j < acs.length; j++) { @@ -370,8 +369,8 @@ function refreshSelected() { } function trackShortName(track) { - var trackIndex = Math.floor((track+22.5) / 45); - if ((trackIndex < 0) || (trackIndex >= 8)) + var trackIndex = Math.floor((track+22.5) / 45) % 8; + if (trackIndex < 0) return "n/a"; return ["N","NE","E","SE","S","SW","W","NW"][trackIndex]; } From 43d29389f2a7cede0c4598bc5f9203fff1ccf5c1 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Tue, 6 Jan 2015 20:15:25 +0000 Subject: [PATCH 100/610] Lots more map work, mostly around switching from "construct a big HTML string" to working directly with the DOM to update the table / selected plane info. Seems to speed things up (and deflicker them) a lot. Also stable sorts, allow disabling the clocks, draw ground tracks in a different color, put "last seen" info on the selected plane infobox, if position updates are infrequent then combine them into a single estimated line so that dash placement works properly, probably a bunch of other things.. --- public_html/config.js | 3 + public_html/gmap.html | 112 ++++++- public_html/planeObject.js | 152 ++++++--- public_html/script.js | 617 ++++++++++++++++++------------------- public_html/style.css | 12 +- 5 files changed, 529 insertions(+), 367 deletions(-) diff --git a/public_html/config.js b/public_html/config.js index f7d8e2eb1..7077a31df 100644 --- a/public_html/config.js +++ b/public_html/config.js @@ -32,3 +32,6 @@ SiteCircles = true; // true or false (Only shown if SiteShow is true) // In nautical miles or km (depending settings value 'Metric') SiteCirclesDistances = new Array(100,150,200); + +// You can disable the clocks if you want here: +ShowClocks = false; diff --git a/public_html/gmap.html b/public_html/gmap.html index 9ab2dc28d..1e8422b0d 100644 --- a/public_html/gmap.html +++ b/public_html/gmap.html @@ -49,16 +49,116 @@ -
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ DUMP1090 + + +
 [GitHub]
(no aircraft selected) 
  
Tracked aircraft (total): n/a
Tracked aircraft (with positions): n/a
+
+ +
-
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ICAOFlightSquawkAltitudeSpeedDistanceTrackMsgsAge
+
-
- Squak 7x00 is reported and shown.
- This is most likely an error in reciving or decoding.
- Please do not call the local authorities, they already know about it if it is valid squak. +
diff --git a/public_html/planeObject.js b/public_html/planeObject.js index bbd14cc28..1c27de48e 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -19,7 +19,6 @@ var planeObject = { // GMap Details marker : null, markerColor : MarkerColor, - lines : [], tracklinesegs : [], last_position_time : null, @@ -39,41 +38,83 @@ var planeObject = { line : null, head_update : this.last_position_time, tail_update : this.last_position_time, - estimated : false }; + estimated : false, + ground : (this.altitude === "ground") + }; this.tracklinesegs.push(newseg); - } else { - var lastseg = this.tracklinesegs[this.tracklinesegs.length - 1]; - var lastpos = lastseg.track.getAt(lastseg.track.getLength() - 1); - var elapsed = (this.last_position_time - lastseg.head_update); - if (elapsed > 5) { - // >5s gap in data, put an estimated segment in - //console.log(this.icao + " discontinuity seen: " + lastpos.lat() + "," + lastpos.lng() + " -> " + here.lat() + "," + here.lng()); - var estseg = { track : new google.maps.MVCArray([lastpos, here]), - line : null, - estimated : true }; - var newseg = { track : new google.maps.MVCArray([here,here]), - line : null, - head_update : this.last_position_time, - tail_update : this.last_position_time, - estimated : false }; - this.tracklinesegs.push(estseg); - this.tracklinesegs.push(newseg); - } else if (elapsed > 0) { - // New position data - // We only retain some historical points, at 5+ second intervals, - // plus the most recent point - if (this.last_position_time - lastseg.tail_update >= 5) { - // enough time has elapsed; retain the last point and add a new one - //console.log(this.icao + " retain last point"); - lastseg.track.push(here); - lastseg.tail_update = lastseg.head_update; - } else { - // replace the last point with the current position - lastseg.track.setAt(lastseg.track.getLength()-1, here); - } - lastseg.head_update = this.last_position_time; + return; + } + + var lastseg = this.tracklinesegs[this.tracklinesegs.length - 1]; + var lastpos = lastseg.track.getAt(lastseg.track.getLength() - 1); + var elapsed = (this.last_position_time - lastseg.head_update); + + var new_data = (here !== lastpos); + var est_track = (elapsed > 5); + var ground_track = (this.altitude === "ground"); + + if (!new_data) + return; + + if (est_track) { + if (!lastseg.estimated) { + // >5s gap in data, create a new estimated segment + //console.log(this.icao + " switching to estimated"); + this.tracklinesegs.push({ track : new google.maps.MVCArray([lastpos, here]), + line : null, + head_update : this.last_position_time, + estimated : true }); + return; } + + // Append to ongoing estimated line + //console.log(this.icao + " extending estimated (" + lastseg.track.getLength() + ")"); + lastseg.track.push(here); + lastseg.head_update = this.last_position_time; + return; + } + + if (lastseg.estimated) { + // We are back to good data. + //console.log(this.icao + " switching to good track"); + this.tracklinesegs.push({ track : new google.maps.MVCArray([lastpos, here]), + line : null, + head_update : this.last_position_time, + tail_update : this.last_position_time, + estimated : false, + ground : (this.altitude === "ground") }); + return; + } + + if ( (lastseg.ground && this.altitude !== "ground") || + (!lastseg.ground && this.altitude === "ground") ) { + console.log(this.icao + " ground state changed"); + // Create a new segment as the ground state changed. + // assume the state changed halfway between the two points + var midpoint = google.maps.geometry.spherical.interpolate(lastpos,here,0.5); + lastseg.track.push(midpoint); + this.tracklinesegs.push({ track : new google.maps.MVCArray([midpoint,here,here]), + line : null, + head_update : this.last_position_time, + tail_update : this.last_position_time, + estimated : false, + ground : (this.altitude === "ground") }); + return; } + + // Add more data to the existing track. + // We only retain some historical points, at 5+ second intervals, + // plus the most recent point + if (this.last_position_time - lastseg.tail_update >= 5) { + // enough time has elapsed; retain the last point and add a new one + //console.log(this.icao + " retain last point"); + lastseg.track.push(here); + lastseg.tail_update = lastseg.head_update; + } else { + // replace the last point with the current position + lastseg.track.setAt(lastseg.track.getLength()-1, here); + } + lastseg.head_update = this.last_position_time; }, // This is to remove the line from the screen if we deselect the plane @@ -161,9 +202,9 @@ var planeObject = { }, // TODO: Trigger actions of a selecting a plane - funcSelectPlane : function(selectedPlane){ - selectPlaneByHex(this.icao); - }, + selectPlane : function(){ + selectPlaneByHex(this.icao); + }, // Update our data funcUpdateData : function(receiver_now,data){ @@ -182,6 +223,7 @@ var planeObject = { if (typeof data.lat !== "undefined") { this.latitude = data.lat; this.longitude = data.lon; + this.seen_pos = data.seen_pos; this.last_position_time = receiver_now - data.seen_pos; } if (typeof data.flight !== "undefined") @@ -197,7 +239,7 @@ var planeObject = { this.marker.setMap(null); this.marker = null; } - this.funcClearLines(); + this.funcClearLine(); if (SelectedPlane == this.icao) { if (this.is_selected) { this.is_selected = false; @@ -216,7 +258,6 @@ var planeObject = { } this.marker = this.funcUpdateMarker(); - PlanesOnMap++; } }, @@ -230,14 +271,14 @@ var planeObject = { position: new google.maps.LatLng(this.latitude, this.longitude), map: GoogleMap, icon: this.funcGetIcon(), - visable: true + visible: true }); // This is so we can match icao address this.marker.icao = this.icao; // Trap clicks for this marker. - google.maps.event.addListener(this.marker, 'click', this.funcSelectPlane); + google.maps.event.addListener(this.marker, 'click', this.selectPlane); } // Setting the marker title @@ -261,12 +302,31 @@ var planeObject = { // console.log(" point " + j + " at " + seg.track.getAt(j).lat() + "," + seg.track.getAt(j).lng()); // } - seg.line = new google.maps.Polyline({ - strokeColor: (seg.estimated ? '#804040' : '#000000'), - strokeOpacity: 1.0, - strokeWeight: (seg.estimated ? 2 : 3), - map: GoogleMap, - path: seg.track }); + if (seg.estimated) { + var lineSymbol = { + path: 'M 0,-1 0,1', + strokeOpacity : 1, + strokeColor : '#804040', + strokeWeight : 2, + scale: 2 + }; + + seg.line = new google.maps.Polyline({ + path: seg.track, + strokeOpacity: 0, + icons: [{ + icon: lineSymbol, + offset: '0', + repeat: '10px' }], + map : GoogleMap }); + } else { + seg.line = new google.maps.Polyline({ + path: seg.track, + strokeOpacity: 1.0, + strokeColor: (seg.ground ? '#408040' : '#000000'), + strokeWeight: 3, + map: GoogleMap }); + } } } } diff --git a/public_html/script.js b/public_html/script.js index e5a176731..c71df5a38 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -1,15 +1,14 @@ // Define our global variables var GoogleMap = null; var Planes = {}; -var PlanesOnMap = 0; -var PlanesOnTable = 0; -var PlanesToReap = 0; +var PlanesOrdered = []; var SelectedPlane = null; -var SpecialSquawk = false; -var sortColumn = 3; -var sortAscending = true; -var sortNumeric = true; +var EmergencySquawks = { + '7500' : 'Aircraft Hijacking', + '7600' : 'Radio Failure', + '7700' : 'General Emergency' +}; // Get current map settings CenterLat = Number(localStorage['CenterLat']) || CONST_CENTERLAT; @@ -19,39 +18,54 @@ ZoomLvl = Number(localStorage['ZoomLvl']) || CONST_ZOOMLVL; Dump1090Version = "unknown version"; RefreshInterval = 1000; +PlaneRowTemplate = null; + +TrackedAircraft = 0 +TrackedPositions = 0 + function fetchData() { $.getJSON('data/aircraft.json', function(data) { - PlanesOnMap = 0; - SpecialSquawk = false; - // Loop through all the planes in the data packet var now = data.now; var acs = data.aircraft; for (var j=0; j < acs.length; j++) { var ac = acs[j]; + var hex = ac.hex; + var plane = null; + // Do we already have this plane object in Planes? // If not make it. - if (Planes[ac.hex]) { - var plane = Planes[ac.hex]; + + if (Planes[hex]) { + plane = Planes[hex]; } else { - var plane = jQuery.extend(true, {}, planeObject); - Planes[ac.hex] = plane; + plane = jQuery.extend(true, {}, planeObject); + + plane.tr = PlaneRowTemplate.cloneNode(true); + plane.tr.cells[0].textContent = hex; // this won't change + plane.tr.addEventListener('click', $.proxy(plane.selectPlane, plane)); + plane.sitedist = null; + + Planes[hex] = plane; + PlanesOrdered.push(plane); } - // Set SpecialSquawk-value - if (ac.squawk == '7500' || ac.squawk == '7600' || ac.squawk == '7700') { - SpecialSquawk = true; - } - // Call the function update plane.funcUpdateData(now, ac); } - PlanesOnTable = acs.length; + refreshTableInfo(); + refreshSelected(); }); } function initialize() { + PlaneRowTemplate = document.getElementById("plane_row_template"); + + if (!ShowClocks) { + $('#timestamps').addClass('hidden'); + } + // Get receiver metadata, reconfigure using it, then continue // with initialization $.getJSON('data/receiver.json') @@ -72,6 +86,18 @@ function initialize() { // Initalizes the map and starts up our timers to call various functions function initialize_after_config() { + // Set SitePosition, initialize sorting + + if (SiteShow && (typeof SiteLat !== 'undefined') && (typeof SiteLon !== 'undefined')) { + SitePosition = new google.maps.LatLng(SiteLat, SiteLon); + sortByDistance(); + } else { + SitePosition = null; + PlaneRowTemplate.cells[5].className = "hidden"; // hide distance column + document.getElementById("distance").className = "hidden"; // hide distance header + sortByAltitude(); + } + // Make a list of all the available map IDs var mapTypeIds = []; for(var type in google.maps.MapTypeId) { @@ -186,15 +212,14 @@ function initialize_after_config() { }); // Add home marker if requested - if (SiteShow && (typeof SiteLat !== 'undefined' || typeof SiteLon !== 'undefined')) { - var siteMarker = new google.maps.LatLng(SiteLat, SiteLon); + if (SitePosition) { var markerImage = new google.maps.MarkerImage( 'http://maps.google.com/mapfiles/kml/pal4/icon57.png', new google.maps.Size(32, 32), // Image size new google.maps.Point(0, 0), // Origin point of image new google.maps.Point(16, 16)); // Position where marker should point var marker = new google.maps.Marker({ - position: siteMarker, + position: SitePosition, map: GoogleMap, icon: markerImage, title: 'My Radar Site', @@ -220,350 +245,320 @@ function initialize_after_config() { extendedInitalize(); // Setup our timer to poll from the server. - window.setInterval(function() { - fetchData(); - refreshTableInfo(); - refreshSelected(); - reaper(); - extendedPulse(); - }, RefreshInterval); + window.setInterval(fetchData, RefreshInterval); + window.setInterval(reaper, 60000); } // This looks for planes to reap out of the master Planes variable function reaper() { - PlanesToReap = 0; - // When did the reaper start? reaptime = new Date().getTime(); + + console.log("Reaping started.."); + // Loop the planes - for (var reap in Planes) { - // Is this plane possibly reapable? - if (Planes[reap].reapable == true) { - // Has it not been seen for 5 minutes? - // This way we still have it if it returns before then - // Due to loss of signal or other reasons - if ((reaptime - Planes[reap].updated) > 300000) { - // Reap it. - delete Planes[reap]; - } - PlanesToReap++; + var newPlanes = []; + for (var i = 0; i < PlanesOrdered.length; ++i) { + var plane = PlanesOrdered[i]; + if ((reaptime - plane.updated) > 300000) { + // Reap it. + console.log("Reaped " + plane.icao); + delete Planes[plane.icao]; + } else { + // Keep it. + newPlanes.push(plane); } }; + + PlanesOrdered = newPlanes; + refreshTableInfo(); + refreshSelected(); } // Refresh the detail window about the plane function refreshSelected() { - var selected = false; + var selected = false; if (typeof SelectedPlane !== 'undefined' && SelectedPlane != "ICAO" && SelectedPlane != null) { - selected = Planes[SelectedPlane]; - } - - var columns = 2; - var html = ''; - - if (selected) { - html += ''; - } else { - html += '
'; - } - - // Flight header line including squawk if needed - if (selected && selected.flight == "") { - html += ''; - - if (selected && selected.altitude !== null) { - if (selected.altitude === "ground") - html += ''; - else if (Metric) { - html += ''; - } else { - html += ''; - } + selected = Planes[SelectedPlane]; + } + + if (!selected) { + $('#dump1090_infoblock').removeClass('hidden'); + $('#dump1090_version').text(Dump1090Version); + $('#dump1090_total_ac').text(TrackedAircraft); + $('#dump1090_total_ac_positions').text(TrackedAircraftPositions); + $('#selected_infoblock').addClass('hidden'); + return; + } + + $('#dump1090_infoblock').addClass('hidden'); + $('#selected_infoblock').removeClass('hidden'); + + if (selected.flight !== null && selected.flight !== "") { + $('#selected_callsign').text(selected.flight); + $('#selected_links').removeClass('hidden'); + $('#selected_fr24_link').attr('href','http://fr24.com/'+selected.flight); + $('#selected_flightstats_link').attr('href','http://www.flightstats.com/go/FlightStatus/flightStatusByFlight.do?flightNumber='+selected.flight); + $('#selected_flightaware_link').attr('href','http://flightaware.com/live/flight/'+selected.flight); } else { - html += ''; + $('#selected_callsign').text('n/a (' + selected.icao + ')'); + $('#selected_links').addClass('hidden'); + } + + var emerg = document.getElementById('selected_emergency'); + if (selected.squawk in EmergencySquawks) { + emerg.className = 'squawk' + selected.squawk; + emerg.textContent = '\u00a0Squawking: ' + EmergencySquawks[selected.squawk] + '\u00a0'; + } else { + emerg.className = 'hidden'; + } + + if (selected.altitude === null) + $("#selected_altitude").text("n/a"); + else if (selected.altitude === "ground") + $("#selected_altitude").text("on ground"); + else if (Metric) + $("#selected_altitude").text(Math.round(selected.altitude / 3.2828) + ' m'); + else + $("#selected_altitude").text(Math.round(selected.altitude) + ' ft'); + + if (selected.squawk === null || selected.squawk === '0000') { + $('#selected_squawk').text('n/a'); + } else { + $('#selected_squawk').text(selected.squawk); } - - if (selected && selected.squawk != '0000') { - html += ''; - } else { - html += ''; - } - html += ''; - - if (selected) { - html += ''; - } else { - html += ''; // Something is wrong if we are here - } - - html += ''; - - html += ''; - - // Let's show some extra data if we have site coordinates - if (SiteShow) { - var siteLatLon = new google.maps.LatLng(SiteLat, SiteLon); - var planeLatLon = new google.maps.LatLng(selected.latitude, selected.longitude); - var dist = google.maps.geometry.spherical.computeDistanceBetween (siteLatLon, planeLatLon); - - if (Metric) { - dist /= 1000; - } else { - dist /= 1852; - } - dist = (Math.round((dist)*10)/10).toFixed(1); - html += ''; - } // End of SiteShow - } else { - if (SiteShow) { - html += ''; - } else { - html += 'n/a'; - } + + if (selected.seen <= 1) { + $('#selected_seen').text('now'); + } else { + $('#selected_seen').text(selected.seen + 's ago'); + } + + if (selected.latitude === null) { + $('#selected_position').text('n/a'); + } else { + if (selected.seen_pos > 1) { + $('#selected_position').text(selected.latitude + ', ' + selected.longitude + " (" + selected.seen_pos + "s ago)"); + } else { + $('#selected_position').text(selected.latitude + ', ' + selected.longitude); + } } - html += '
N/A (' + - selected.icao + ')'; - } else if (selected && selected.flight != "") { - html += '
' + - selected.flight + ''; - } else { - html += '
DUMP1090 ' + Dump1090Version + ' [GitHub]'; - } - - if (selected && selected.squawk == 7500) { // Lets hope we never see this... Aircraft Hijacking - html += '  Squawking: Aircraft Hijacking '; - } else if (selected && selected.squawk == 7600) { // Radio Failure - html += '  Squawking: Radio Failure '; - } else if (selected && selected.squawk == 7700) { // General Emergency - html += '  Squawking: General Emergency '; - } else if (selected && selected.flight != '') { - html += ' [FR24]'; - html += ' [FlightStats]'; - html += ' [FlightAware]'; - } - html += '
Altitude: on ground
Altitude: ' + Math.round(selected.altitude / 3.2828) + ' m
Altitude: ' + selected.altitude + ' ft
Altitude: n/aSquawk: ' + selected.squawk + '
Squawk: n/a
Speed: ' - if (selected) { - if (Metric) { - html += Math.round(selected.speed * 1.852) + ' km/h'; - } else { - html += selected.speed + ' kt'; - } + if (selected.speed === null) { + $('#selected_speed').text('n/a'); + } else if (Metric) { + $('#selected_speed').text(Math.round(selected.speed * 1.852) + ' km/h'); } else { - html += 'n/a'; + $('#selected_speed').text(Math.round(selected.speed) + ' kt'); } - html += 'ICAO (hex): ' + selected.icao + '
ICAO (hex): n/a
Track: ' - if (selected && selected.track !== null) { - html += selected.track + '°' + ' (' + trackLongName(selected.track) +')'; + + $('#selected_icao').text(selected.icao); + + if (selected.track === null) { + $('#selected_track').text('n/a'); } else { - html += 'n/a'; + $('#selected_track').text(selected.track + '\u00b0' + ' (' + trackLongName(selected.track) + ')'); } - html += ' 
Lat/Long: '; - if (selected && selected.latitude !== null) { - html += selected.latitude + ', ' + selected.longitude + '
Distance from Site: ' + dist + - (Metric ? ' km' : ' NM') + '
Distance from Site: n/a ' + - (Metric ? ' km' : ' NM') + '
'; - - document.getElementById('plane_detail').innerHTML = html; + if (selected.sitedist !== null) { + var dist = selected.sitedist; + if (Metric) { + dist /= 1000; + } else { + dist /= 1852; + } + + dist = (Math.round((dist)*10)/10).toFixed(1); + + $('#selected_sitedist').text(dist + (Metric ? ' km' : ' NM')); + } else { + $('#selected_sitedist').text("n/a"); + } } function trackShortName(track) { - var trackIndex = Math.floor((track+22.5) / 45) % 8; - if (trackIndex < 0) - return "n/a"; + var trackIndex = Math.floor((360 + track % 360 + 22.5) / 45) % 8; return ["N","NE","E","SE","S","SW","W","NW"][trackIndex]; } function trackLongName(track) { - var trackIndex = Math.floor((track+22.5) / 45); - if ((trackIndex < 0) || (trackIndex >= 8)) - return "n/a"; + var trackIndex = Math.floor((360 + track % 360 + 22.5) / 45) % 8; return ["North","Northeast","East","Southeast","South","Southwest","West","Northwest"][trackIndex]; } // Refeshes the larger table of all the planes -function refreshTableInfo() { - var html = ''; - html += ''; - html += ''; - html += ''; - html += ''; - html += ''; - html += ''; - // Add distance column header to table if site coordinates are provided - if (SiteShow && (typeof SiteLat !== 'undefined' || typeof SiteLon !== 'undefined')) { - html += ''; +function format_altitude(alt) { + if (alt === null) + return ""; + else if (alt === "ground") + return "ground"; + else if (Metric) + return Math.round(alt / 3.2828); + else + return Math.round(alt); +} + +function format_speed(speed) { + if (speed === null) + return ""; + else if (Metric) + return Math.round(speed * 1.852); + else + return Math.round(speed); +} + +function format_distance(dist) { + if (Metric) { + return (Math.round(dist/100) / 10).toFixed(1); + } else { + return (Math.round(dist/185.2) / 10).toFixed(1); } - html += ''; - html += ''; - html += ''; - - for (var tablep in Planes) { - var tableplane = Planes[tablep] - if (!tableplane.reapable) { - var specialStyle = ""; - // Is this the plane we selected? - if (tableplane.icao == SelectedPlane) { - specialStyle += " selected"; - } - // Lets hope we never see this... Aircraft Hijacking - if (tableplane.squawk == 7500) { - specialStyle += " squawk7500"; - } - // Radio Failure - if (tableplane.squawk == 7600) { - specialStyle += " squawk7600"; - } - // Emergancy - if (tableplane.squawk == 7700) { - specialStyle += " squawk7700"; - } - +} + +function refreshTableInfo() { + var show_squawk_warning = false; + + TrackedAircraft = 0 + TrackedAircraftPositions = 0 + + for (var i = 0; i < PlanesOrdered.length; ++i) { + var tableplane = PlanesOrdered[i]; + if (tableplane.reapable) { + tableplane.tr.className = "hidden"; + } else { + TrackedAircraft++; + + var classes = "plane_table_row"; + if (tableplane.latitude !== null) - html += ''; - else - html += ''; - - html += ''; - - if (tableplane.flight !== null) - html += ''; - else - html += ''; + classes += " vPosition"; + if (tableplane.icao == SelectedPlane) + classes += " selected"; - if (tableplane.squawk !== null) - html += ''; - else - html += ''; - - if (tableplane.altitude === null) - html += ''; - else if (tableplane.altitude === "ground") - html += ''; - else if (Metric) - html += ''; - else - html += ''; - - if (tableplane.speed === null) - html += ''; - else if (Metric) - html += ''; - else - html += ''; + if (tableplane.squawk in EmergencySquawks) { + classes += ' squawk' + tableplane.squawk; + show_squawk_warning = true; + } + + tableplane.tr.className = classes; + // ICAO doesn't change + tableplane.tr.cells[1].textContent = (tableplane.flight !== null ? tableplane.flight : ""); + tableplane.tr.cells[2].textContent = (tableplane.squawk !== null ? tableplane.squawk : ""); + tableplane.tr.cells[3].textContent = format_altitude(tableplane.altitude); + tableplane.tr.cells[4].textContent = format_speed(tableplane.speed); + + if (tableplane.latitude !== null) + ++TrackedAircraftPositions; // Add distance column to table if site coordinates are provided - if (SiteShow && (typeof SiteLat !== 'undefined' || typeof SiteLon !== 'undefined')) { - html += ''; + if (SitePosition !== null && tableplane.latitude !== null) { + var planeLatLon = new google.maps.LatLng(tableplane.latitude, tableplane.longitude); + var dist = google.maps.geometry.spherical.computeDistanceBetween (SitePosition, planeLatLon); + tableplane.tr.cells[5].textContent = format_distance(dist); + tableplane.sitedist = dist; + } else { + tableplane.tr.cells[5].textContent = ""; } - html += ''; - html += ''; - html += ''; - html += ''; + tableplane.tr.cells[6].textContent = (tableplane.track !== null ? tableplane.track : ""); + tableplane.tr.cells[7].textContent = tableplane.messages; + tableplane.tr.cells[8].textContent = tableplane.seen; } } - html += '
ICAOFlightSquawkAltitudeSpeedDistanceTrackMsgsSeen
' + tableplane.icao + '' + tableplane.flight + '' + tableplane.squawk + ' ground' + Math.round(tableplane.altitude / 3.2828) + '' + tableplane.altitude + ' ' + Math.round(tableplane.speed * 1.852) + '' + tableplane.speed + ''; - if (tableplane.latitude !== null) { - var siteLatLon = new google.maps.LatLng(SiteLat, SiteLon); - var planeLatLon = new google.maps.LatLng(tableplane.latitude, tableplane.longitude); - var dist = google.maps.geometry.spherical.computeDistanceBetween (siteLatLon, planeLatLon); - if (Metric) { - dist /= 1000; - } else { - dist /= 1852; - } - dist = (Math.round((dist)*10)/10).toFixed(1); - html += dist; - } - html += ''; - if (tableplane.track !== null) - html += tableplane.track; - html += '' + tableplane.messages + '' + tableplane.seen + '
'; - - document.getElementById('planes_table').innerHTML = html; - - if (SpecialSquawk) { - $('#SpecialSquawkWarning').css('display', 'inline'); + + if (show_squawk_warning) { + $("#SpecialSquawkWarning").removeClass('hidden'); } else { - $('#SpecialSquawkWarning').css('display', 'none'); + $("#SpecialSquawkWarning").addClass('hidden'); } - - // Click event for table - $('#planes_table').find('tr').click( function(){ - var hex = $(this).find('td:first').text(); - if (hex != "ICAO") { - selectPlaneByHex(hex); - refreshTableInfo(); - refreshSelected(); - } - }); - - resortTable(); + + resortTable(); } -function sortBy(colName,numeric) { - var header_cells = document.getElementById('tableinfo').tHead.rows[0].cells; - for (var i = 0; i < header_cells.length; ++i) { - if (header_cells[i].id === colName) { - if (i == sortColumn) - sortAscending = !sortAscending; - else { - sortColumn = i; - sortNumeric = numeric; - sortAscending = true; - } +// +// ---- table sorting ---- +// + +function compareAlpha(xa,xo,ya,yo) { + if (xa === ya) + return xo - yo; + if (xa === null) + return 1; + if (ya === null) + return -1; + if (xa < ya) + return -1; + return 1; +} - resortTable(); - return; - } - } +function compareNumeric(xf,xo,yf,yo) { + if (xf === null) xf = 1e9; + if (yf === null) yf = 1e9; + + if (Math.abs(xf - yf) < 1e-9) + return xo - yo; - console.warn("column not found: " + colName); + return xf - yf; } -function resortTable() { - sortTable('tableinfo', sortColumn, sortAscending, sortNumeric); +function compareAltitude(xf,xo,yf,yo) { + if (xf === null) xf = 1e9; + else if (xf === "ground") xf = -1e9; + if (yf === null) yf = 1e9; + else if (yf === "ground") yf = -1e9; + + if (Math.abs(xf - yf) < 1e-9) + return xo - yo; + + return xf - yf; } -function sortTable(tableId, col, asc, numeric) { - //retrieve passed table element - var oTbl=document.getElementById(tableId).tBodies[0]; - var aStore=[]; +function sortByICAO() { sortBy('icao', function(x,y){return compareAlpha(x.icao, x.sort_pos, y.icao, y.sort_pos)}); } +function sortByFlight() { sortBy('flight', function(x,y){return compareAlpha(x.flight, x.sort_pos, y.flight, y.sort_pos)}); } +function sortBySquawk() { sortBy('squawk', function(x,y){return compareAlpha(x.squawk, x.sort_pos, y.squawk, y.sort_pos)}); } +function sortByAltitude() { sortBy('altitude',function(x,y){return compareAltitude(x.altitude, x.sort_pos, y.altitude, y.sort_pos)}); } +function sortBySpeed() { sortBy('speed', function(x,y){return compareNumeric(x.speed, x.sort_pos, y.speed, y.sort_pos)}); } +function sortByDistance() { sortBy('sitedist',function(x,y){return compareNumeric(x.sitedist, x.sort_pos, y.sitedist, y.sort_pos)}); } +function sortByTrack() { sortBy('track', function(x,y){return compareNumeric(x.track, x.sort_pos, y.track, y.sort_pos)}); } +function sortByMsgs() { sortBy('msgs', function(x,y){return compareNumeric(x.msgs, x.sort_pos, y.msgs, y.sort_pos)}); } +function sortBySeen() { sortBy('seen', function(x,y){return compareNumeric(x.seen, x.sort_pos, y.seen, y.sort_pos)}); } + +sortId = ''; +sortByFunc = null; +sortAscending = true; + +function resortTable() { + // make it a stable sort + for (var i = 0; i < PlanesOrdered.length; ++i) { + PlanesOrdered[i].sort_pos = i; + } + + PlanesOrdered.sort(sortByFunc); - //loop through the rows, storing each one inro aStore - for (var i=0; i < oTbl.rows.length; ++i){ - var oRow=oTbl.rows[i]; - var sortKey; - if (numeric) { - sortKey = parseFloat(oRow.cells[col].textContent||oRow.cells[col].innerText); - if (isNaN(sortKey)) { - sortKey = -999999; - } - } else { - sortKey = String(oRow.cells[col].textContent||oRow.cells[col].innerText); - } - aStore.push([sortKey,oRow]); - } + var tbody = document.getElementById('tableinfo').tBodies[0]; + for (var i = 0; i < PlanesOrdered.length; ++i) { + tbody.appendChild(PlanesOrdered[i].tr); + } +} - if (numeric) { //numerical sort - aStore.sort(function(x,y){ return sortAscending ? x[0]-y[0] : y[0]-x[0]; }); - } else { //alpha sort - aStore.sort(); - if (!sortAscending) { - aStore.reverse(); - } - } +function sortBy(id,sortfunc) { + if (id === sortId) { + sortAscending = !sortAscending; + } else { + sortAscending = true; + } - //rewrite the table rows to the passed table element - for(var i=0,iLen=aStore.length;i Date: Wed, 7 Jan 2015 01:18:36 +0000 Subject: [PATCH 101/610] Zoom in a bit more by default. --- public_html/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public_html/config.js b/public_html/config.js index 7077a31df..0b32e4621 100644 --- a/public_html/config.js +++ b/public_html/config.js @@ -14,7 +14,7 @@ Metric = false; // true or false CONST_CENTERLAT = 45.0; CONST_CENTERLON = 9.0; // The google maps zoom level, 0 - 16, lower is further out -CONST_ZOOMLVL = 5; +CONST_ZOOMLVL = 7; // -- Marker settings ------------------------------------- // The default marker color From feb8c55bac85cef8a12da838907dd3fbc32094d9 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 7 Jan 2015 01:19:05 +0000 Subject: [PATCH 102/610] Display the number of history points stored. Rejuggle how markers are handled so that we avoid creating lots of new icons all the time. Rearrange reaping / update times so that it is all based on timestamps from the receiver. --- public_html/gmap.html | 6 +- public_html/planeObject.js | 311 ++++++++++++++++++++----------------- public_html/script.js | 93 ++++++----- 3 files changed, 224 insertions(+), 186 deletions(-) diff --git a/public_html/gmap.html b/public_html/gmap.html index 1e8422b0d..261a2e0d5 100644 --- a/public_html/gmap.html +++ b/public_html/gmap.html @@ -76,11 +76,13 @@ - Tracked aircraft (total): n/a + Aircraft (total): n/a +   - Tracked aircraft (with positions): n/a + (with positions): n/a + History: n/a positions
diff --git a/public_html/planeObject.js b/public_html/planeObject.js index 1c27de48e..1104130c8 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -1,3 +1,36 @@ +var planeSvg = "M 0,0 " + + "M 1.9565564,41.694305 C 1.7174505,40.497708 1.6419973,38.448747 " + + "1.8096508,37.70494 1.8936398,37.332056 2.0796653,36.88191 2.222907,36.70461 " + + "2.4497603,36.423844 4.087816,35.47248 14.917931,29.331528 l 12.434577," + + "-7.050718 -0.04295,-7.613412 c -0.03657,-6.4844888 -0.01164,-7.7625804 " + + "0.168134,-8.6194061 0.276129,-1.3160905 0.762276,-2.5869575 1.347875," + + "-3.5235502 l 0.472298,-0.7553719 1.083746,-0.6085497 c 1.194146,-0.67053522 " + + "1.399524,-0.71738842 2.146113,-0.48960552 1.077005,0.3285939 2.06344," + + "1.41299352 2.797602,3.07543322 0.462378,1.0469993 0.978731,2.7738408 " + + "1.047635,3.5036272 0.02421,0.2570284 0.06357,3.78334 0.08732,7.836246 0.02375," + + "4.052905 0.0658,7.409251 0.09345,7.458546 0.02764,0.04929 5.600384,3.561772 " + + "12.38386,7.805502 l 12.333598,7.715871 0.537584,0.959688 c 0.626485,1.118378 " + + "0.651686,1.311286 0.459287,3.516442 -0.175469,2.011604 -0.608966,2.863924 " + + "-1.590344,3.127136 -0.748529,0.200763 -1.293144,0.03637 -10.184829,-3.07436 " + + "C 48.007733,41.72562 44.793806,40.60197 43.35084,40.098045 l -2.623567," + + "-0.916227 -1.981212,-0.06614 c -1.089663,-0.03638 -1.985079,-0.05089 -1.989804," + + "-0.03225 -0.0052,0.01863 -0.02396,2.421278 -0.04267,5.339183 -0.0395,6.147742 " + + "-0.143635,7.215456 -0.862956,8.845475 l -0.300457,0.680872 2.91906,1.361455 " + + "c 2.929379,1.366269 3.714195,1.835385 4.04589,2.41841 0.368292,0.647353 " + + "0.594634,2.901439 0.395779,3.941627 -0.0705,0.368571 -0.106308,0.404853 " + + "-0.765159,0.773916 L 41.4545,62.83158 39.259237,62.80426 c -6.030106,-0.07507 " + + "-16.19508,-0.495041 -16.870991,-0.697033 -0.359409,-0.107405 -0.523792," + + "-0.227482 -0.741884,-0.541926 -0.250591,-0.361297 -0.28386,-0.522402 -0.315075," + + "-1.52589 -0.06327,-2.03378 0.23288,-3.033615 1.077963,-3.639283 0.307525," + + "-0.2204 4.818478,-2.133627 6.017853,-2.552345 0.247872,-0.08654 0.247455," + + "-0.102501 -0.01855,-0.711959 -0.330395,-0.756986 -0.708622,-2.221756 -0.832676," + + "-3.224748 -0.05031,-0.406952 -0.133825,-3.078805 -0.185533,-5.937448 -0.0517," + + "-2.858644 -0.145909,-5.208974 -0.209316,-5.222958 -0.06341,-0.01399 -0.974464," + + "-0.0493 -2.024551,-0.07845 L 23.247235,38.61921 18.831373,39.8906 C 4.9432155," + + "43.88916 4.2929558,44.057819 3.4954426,43.86823 2.7487826,43.690732 2.2007966," + + "42.916622 1.9565564,41.694305 z" + + var planeObject = { // Basic location information altitude : null, @@ -14,18 +47,29 @@ var planeObject = { // Data packet numbers messages : null, - seen : null, - - // GMap Details - marker : null, - markerColor : MarkerColor, + // Track history tracklinesegs : [], - last_position_time : null, + - // When was this last updated? - updated : null, - reapable : false, + // When was this last updated (receiver timestamp) + last_message_time : null, + last_position_time : null, + + historySize : 0, + visible : true, + + // GMap Details + marker : null, + icon : { + strokeWeight: 1, + path: planeSvg, + scale: 0.4, + fillColor: MarkerColor, + fillOpacity: 0.9, + anchor: new google.maps.Point(32, 32), // Set anchor to middle of plane. + rotation: 0 + }, // Appends data to the running track so we can get a visual tail on the plane // Only useful for a long running browser session. @@ -42,6 +86,7 @@ var planeObject = { ground : (this.altitude === "ground") }; this.tracklinesegs.push(newseg); + this.historySize += 2; return; } @@ -54,7 +99,7 @@ var planeObject = { var ground_track = (this.altitude === "ground"); if (!new_data) - return; + return false; if (est_track) { if (!lastseg.estimated) { @@ -64,14 +109,16 @@ var planeObject = { line : null, head_update : this.last_position_time, estimated : true }); - return; + this.historySize += 2; + return true; } // Append to ongoing estimated line //console.log(this.icao + " extending estimated (" + lastseg.track.getLength() + ")"); lastseg.track.push(here); lastseg.head_update = this.last_position_time; - return; + this.historySize++; + return true; } if (lastseg.estimated) { @@ -83,12 +130,13 @@ var planeObject = { tail_update : this.last_position_time, estimated : false, ground : (this.altitude === "ground") }); - return; + this.historySize += 2; + return true; } if ( (lastseg.ground && this.altitude !== "ground") || (!lastseg.ground && this.altitude === "ground") ) { - console.log(this.icao + " ground state changed"); + //console.log(this.icao + " ground state changed"); // Create a new segment as the ground state changed. // assume the state changed halfway between the two points var midpoint = google.maps.geometry.spherical.interpolate(lastpos,here,0.5); @@ -99,7 +147,8 @@ var planeObject = { tail_update : this.last_position_time, estimated : false, ground : (this.altitude === "ground") }); - return; + this.historySize += 4; + return true; } // Add more data to the existing track. @@ -110,15 +159,17 @@ var planeObject = { //console.log(this.icao + " retain last point"); lastseg.track.push(here); lastseg.tail_update = lastseg.head_update; + this.historySize ++; } else { // replace the last point with the current position lastseg.track.setAt(lastseg.track.getLength()-1, here); } lastseg.head_update = this.last_position_time; + return true; }, // This is to remove the line from the screen if we deselect the plane - funcClearLine : function() { + clearLines : function() { for (var i = 0; i < this.tracklinesegs.length; ++i) { var seg = this.tracklinesegs[i]; if (seg.line !== null) { @@ -128,78 +179,39 @@ var planeObject = { } }, - // Should create an icon for us to use on the map... - funcGetIcon : function() { - this.markerColor = MarkerColor; - // If this marker is selected we should make it lighter than the rest. - if (this.is_selected == true) { - this.markerColor = SelectedColor; - } + updateIcon : function() { + var col = MarkerColor; + + // If this marker is selected we should make it lighter than the rest. + if (this.is_selected) + col = SelectedColor; - // If we have not seen a recent update, change color - if (this.seen > 15) { - this.markerColor = StaleColor; - } + // If we have not seen a recent update, change color + if (this.seen > 15) + col = StaleColor; - // Plane marker - var baseSvg = { - planeData : "M 1.9565564,41.694305 C 1.7174505,40.497708 1.6419973,38.448747 " + - "1.8096508,37.70494 1.8936398,37.332056 2.0796653,36.88191 2.222907,36.70461 " + - "2.4497603,36.423844 4.087816,35.47248 14.917931,29.331528 l 12.434577," + - "-7.050718 -0.04295,-7.613412 c -0.03657,-6.4844888 -0.01164,-7.7625804 " + - "0.168134,-8.6194061 0.276129,-1.3160905 0.762276,-2.5869575 1.347875," + - "-3.5235502 l 0.472298,-0.7553719 1.083746,-0.6085497 c 1.194146,-0.67053522 " + - "1.399524,-0.71738842 2.146113,-0.48960552 1.077005,0.3285939 2.06344," + - "1.41299352 2.797602,3.07543322 0.462378,1.0469993 0.978731,2.7738408 " + - "1.047635,3.5036272 0.02421,0.2570284 0.06357,3.78334 0.08732,7.836246 0.02375," + - "4.052905 0.0658,7.409251 0.09345,7.458546 0.02764,0.04929 5.600384,3.561772 " + - "12.38386,7.805502 l 12.333598,7.715871 0.537584,0.959688 c 0.626485,1.118378 " + - "0.651686,1.311286 0.459287,3.516442 -0.175469,2.011604 -0.608966,2.863924 " + - "-1.590344,3.127136 -0.748529,0.200763 -1.293144,0.03637 -10.184829,-3.07436 " + - "C 48.007733,41.72562 44.793806,40.60197 43.35084,40.098045 l -2.623567," + - "-0.916227 -1.981212,-0.06614 c -1.089663,-0.03638 -1.985079,-0.05089 -1.989804," + - "-0.03225 -0.0052,0.01863 -0.02396,2.421278 -0.04267,5.339183 -0.0395,6.147742 " + - "-0.143635,7.215456 -0.862956,8.845475 l -0.300457,0.680872 2.91906,1.361455 " + - "c 2.929379,1.366269 3.714195,1.835385 4.04589,2.41841 0.368292,0.647353 " + - "0.594634,2.901439 0.395779,3.941627 -0.0705,0.368571 -0.106308,0.404853 " + - "-0.765159,0.773916 L 41.4545,62.83158 39.259237,62.80426 c -6.030106,-0.07507 " + - "-16.19508,-0.495041 -16.870991,-0.697033 -0.359409,-0.107405 -0.523792," + - "-0.227482 -0.741884,-0.541926 -0.250591,-0.361297 -0.28386,-0.522402 -0.315075," + - "-1.52589 -0.06327,-2.03378 0.23288,-3.033615 1.077963,-3.639283 0.307525," + - "-0.2204 4.818478,-2.133627 6.017853,-2.552345 0.247872,-0.08654 0.247455," + - "-0.102501 -0.01855,-0.711959 -0.330395,-0.756986 -0.708622,-2.221756 -0.832676," + - "-3.224748 -0.05031,-0.406952 -0.133825,-3.078805 -0.185533,-5.937448 -0.0517," + - "-2.858644 -0.145909,-5.208974 -0.209316,-5.222958 -0.06341,-0.01399 -0.974464," + - "-0.0493 -2.024551,-0.07845 L 23.247235,38.61921 18.831373,39.8906 C 4.9432155," + - "43.88916 4.2929558,44.057819 3.4954426,43.86823 2.7487826,43.690732 2.2007966," + - "42.916622 1.9565564,41.694305 z" - }; + // If the squawk code is one of the international emergency codes, + // match the info window alert color. + var squawk_col = { '7500' : 'rgb(255, 85, 85)', + '7600' : 'rgb(0, 255, 255)', + '7700' : 'rgb(255, 255, 0)' }; + if (this.squawk in squawk_col) + col = squawk_col[this.squawk]; - // If the squawk code is one of the international emergency codes, - // match the info window alert color. - if (this.squawk == 7500) { - this.markerColor = "rgb(255, 85, 85)"; - } - if (this.squawk == 7600) { - this.markerColor = "rgb(0, 255, 255)"; - } - if (this.squawk == 7700) { - this.markerColor = "rgb(255, 255, 0)"; - } + var weight = this.is_selected ? 2 : 1; + var rotation = (this.track === null ? 0 : this.track); + + if (col === this.icon.fillColor && weight === this.icon.strokeWeight && rotation === this.icon.rotation) + return false; // no changes - // If we have not overwritten color by now, an extension still could but - // just keep on trucking. :) + this.icon.fillColor = col; + this.icon.strokeWeight = weight; + this.icon.rotation = rotation; + if (this.marker) + this.marker.setIcon(this.icon); - return { - strokeWeight: (this.is_selected ? 2 : 1), - path: "M 0,0 "+ baseSvg["planeData"], - scale: 0.4, - fillColor: this.markerColor, - fillOpacity: 0.9, - anchor: new google.maps.Point(32, 32), // Set anchor to middle of plane. - rotation: this.track - }; - }, + return true; + }, // TODO: Trigger actions of a selecting a plane selectPlane : function(){ @@ -207,12 +219,11 @@ var planeObject = { }, // Update our data - funcUpdateData : function(receiver_now,data){ + updateData : function(receiver_timestamp, data){ // Update all of our data - this.updated = new Date().getTime(); this.icao = data.hex; this.messages = data.messages; - this.seen = data.seen; + this.last_message_time = receiver_timestamp - data.seen; if (typeof data.altitude !== "undefined") this.altitude = data.altitude; @@ -223,77 +234,88 @@ var planeObject = { if (typeof data.lat !== "undefined") { this.latitude = data.lat; this.longitude = data.lon; - this.seen_pos = data.seen_pos; - this.last_position_time = receiver_now - data.seen_pos; + this.last_position_time = receiver_timestamp - data.seen_pos; } if (typeof data.flight !== "undefined") this.flight = data.flight; if (typeof data.squawk !== "undefined") this.squawk = data.squawk; + }, + + updateTick : function(receiver_timestamp) { + // recompute seen and seen_pos + this.seen = receiver_timestamp - this.last_message_time; + this.seen_pos = (this.last_position_time === null ? null : receiver_timestamp - this.last_position_time); - // If no packet in over 58 seconds, consider the plane reapable - // This way we can hold it, but not show it just in case the plane comes back + // If no packet in over 58 seconds, clear the plane. if (this.seen > 58) { - this.reapable = true; - if (this.marker) { - this.marker.setMap(null); - this.marker = null; - } - this.funcClearLine(); - if (SelectedPlane == this.icao) { - if (this.is_selected) { - this.is_selected = false; - } - SelectedPlane = null; - } + if (this.visible) { + //console.log("hiding " + this.icao); + this.clearMarker(); + this.visible = false; + if (SelectedPlane == this.icao) + selectPlaneByHex(null); + } } else { - this.reapable = false; + this.visible = true; + if (this.latitude !== null) { + if (this.updateTrack()) { + this.updateLines(); + this.updateMarker(true); + } else { + this.updateMarker(false); // didn't move + } + } } + }, - // Is the position valid? - if (!this.reapable && typeof data.lat !== "undefined") { - this.updateTrack(); - if (this.is_selected) { - this.funcUpdateLines(); - } - - this.marker = this.funcUpdateMarker(); + clearMarker: function() { + if (this.marker) { + this.marker.setMap(null); + this.marker = null; } - }, + }, // Update our marker on the map - funcUpdateMarker: function() { - if (this.marker) { - this.marker.setPosition(new google.maps.LatLng(this.latitude, this.longitude)); - this.marker.setIcon(this.funcGetIcon()); - } else { - this.marker = new google.maps.Marker({ - position: new google.maps.LatLng(this.latitude, this.longitude), - map: GoogleMap, - icon: this.funcGetIcon(), - visible: true - }); - - // This is so we can match icao address - this.marker.icao = this.icao; - - // Trap clicks for this marker. - google.maps.event.addListener(this.marker, 'click', this.selectPlane); - } - - // Setting the marker title - if (this.flight === null || this.flight.length == 0) { - this.marker.setTitle(this.hex); - } else { - this.marker.setTitle(this.flight+' ('+this.icao+')'); - } - return this.marker; - }, + updateMarker: function(moved) { + if (!this.visible) { + this.clearMarker(); + return; + } + if (this.marker) { + if (moved) + this.marker.setPosition(new google.maps.LatLng(this.latitude, this.longitude)); + this.updateIcon(); + } else { + this.updateIcon(); + this.marker = new google.maps.Marker({ + position: new google.maps.LatLng(this.latitude, this.longitude), + map: GoogleMap, + icon: this.icon, + visible: true + }); + + // This is so we can match icao address + this.marker.icao = this.icao; + + // Trap clicks for this marker. + google.maps.event.addListener(this.marker, 'click', this.selectPlane); + } + + // Setting the marker title + if (this.flight === null || this.flight.length == 0) { + this.marker.setTitle(this.hex); + } else { + this.marker.setTitle(this.flight+' ('+this.icao+')'); + } + }, + // Update our planes tail line, - // TODO: Make this multi colored based on options - // altitude (default) or speed - funcUpdateLines: function() { + updateLines: function() { + if (!this.is_selected) + return; + for (var i = 0; i < this.tracklinesegs.length; ++i) { var seg = this.tracklinesegs[i]; if (seg.line === null) { @@ -329,5 +351,10 @@ var planeObject = { } } } + }, + + destroy : function() { + this.clearLines(); + this.clearMarker(); } }; diff --git a/public_html/script.js b/public_html/script.js index c71df5a38..25e38dd77 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -51,8 +51,14 @@ function fetchData() { } // Call the function update - plane.funcUpdateData(now, ac); + plane.updateData(now, ac); } + + // update timestamps, visibility, history track for all planes - not only those updated + for (var i = 0; i < PlanesOrdered.length; ++i) { + var plane = PlanesOrdered[i]; + plane.updateTick(now); + } refreshTableInfo(); refreshSelected(); @@ -63,7 +69,7 @@ function initialize() { PlaneRowTemplate = document.getElementById("plane_row_template"); if (!ShowClocks) { - $('#timestamps').addClass('hidden'); + $('#timestamps').css('display','none'); } // Get receiver metadata, reconfigure using it, then continue @@ -93,8 +99,8 @@ function initialize_after_config() { sortByDistance(); } else { SitePosition = null; - PlaneRowTemplate.cells[5].className = "hidden"; // hide distance column - document.getElementById("distance").className = "hidden"; // hide distance header + PlaneRowTemplate.cells[5].style.display = 'none'; // hide distance column + document.getElementById("distance").style.display = 'none'; // hide distance header sortByAltitude(); } @@ -251,18 +257,20 @@ function initialize_after_config() { // This looks for planes to reap out of the master Planes variable function reaper() { - reaptime = new Date().getTime(); - - console.log("Reaping started.."); + //console.log("Reaping started.."); - // Loop the planes + // Look for planes where we have seen no messages for >300 seconds var newPlanes = []; for (var i = 0; i < PlanesOrdered.length; ++i) { var plane = PlanesOrdered[i]; - if ((reaptime - plane.updated) > 300000) { + if (plane.seen > 300) { // Reap it. - console.log("Reaped " + plane.icao); + //console.log("Reaping " + plane.icao); + //console.log("parent " + plane.tr.parentNode); + plane.tr.parentNode.removeChild(plane.tr); + plane.tr = null; delete Planes[plane.icao]; + plane.destroy(); } else { // Keep it. newPlanes.push(plane); @@ -282,26 +290,27 @@ function refreshSelected() { } if (!selected) { - $('#dump1090_infoblock').removeClass('hidden'); + $('#selected_infoblock').css('display','none'); + $('#dump1090_infoblock').css('display','block'); $('#dump1090_version').text(Dump1090Version); $('#dump1090_total_ac').text(TrackedAircraft); $('#dump1090_total_ac_positions').text(TrackedAircraftPositions); - $('#selected_infoblock').addClass('hidden'); + $('#dump1090_total_history').text(TrackedHistorySize); return; } - $('#dump1090_infoblock').addClass('hidden'); - $('#selected_infoblock').removeClass('hidden'); + $('#dump1090_infoblock').css('display','none'); + $('#selected_infoblock').css('display','block'); if (selected.flight !== null && selected.flight !== "") { $('#selected_callsign').text(selected.flight); - $('#selected_links').removeClass('hidden'); + $('#selected_links').css('display','inline'); $('#selected_fr24_link').attr('href','http://fr24.com/'+selected.flight); $('#selected_flightstats_link').attr('href','http://www.flightstats.com/go/FlightStatus/flightStatusByFlight.do?flightNumber='+selected.flight); $('#selected_flightaware_link').attr('href','http://flightaware.com/live/flight/'+selected.flight); } else { $('#selected_callsign').text('n/a (' + selected.icao + ')'); - $('#selected_links').addClass('hidden'); + $('#selected_links').css('display','none'); } var emerg = document.getElementById('selected_emergency'); @@ -420,27 +429,27 @@ function refreshTableInfo() { TrackedAircraft = 0 TrackedAircraftPositions = 0 + TrackedHistorySize = 0 for (var i = 0; i < PlanesOrdered.length; ++i) { var tableplane = PlanesOrdered[i]; - if (tableplane.reapable) { - tableplane.tr.className = "hidden"; + TrackedHistorySize += tableplane.historySize; + if (!tableplane.visible) { + tableplane.tr.className = "plane_table_row hidden"; } else { TrackedAircraft++; - var classes = "plane_table_row"; - + if (tableplane.latitude !== null) - classes += " vPosition"; + classes += " vPosition"; if (tableplane.icao == SelectedPlane) - classes += " selected"; + classes += " selected"; if (tableplane.squawk in EmergencySquawks) { - classes += ' squawk' + tableplane.squawk; + classes += (" squawk" + tableplane.squawk); show_squawk_warning = true; - } - - tableplane.tr.className = classes; + } + // ICAO doesn't change tableplane.tr.cells[1].textContent = (tableplane.flight !== null ? tableplane.flight : ""); tableplane.tr.cells[2].textContent = (tableplane.squawk !== null ? tableplane.squawk : ""); @@ -463,13 +472,16 @@ function refreshTableInfo() { tableplane.tr.cells[6].textContent = (tableplane.track !== null ? tableplane.track : ""); tableplane.tr.cells[7].textContent = tableplane.messages; tableplane.tr.cells[8].textContent = tableplane.seen; + + tableplane.tr.className = classes; + } } if (show_squawk_warning) { - $("#SpecialSquawkWarning").removeClass('hidden'); + $("#SpecialSquawkWarning").css('display','block'); } else { - $("#SpecialSquawkWarning").addClass('hidden'); + $("#SpecialSquawkWarning").css('display','none'); } resortTable(); @@ -562,26 +574,23 @@ function selectPlaneByHex(hex) { // If SelectedPlane has something in it, clear out the selected if (SelectedPlane != null) { Planes[SelectedPlane].is_selected = false; - Planes[SelectedPlane].funcClearLine(); - Planes[SelectedPlane].markerColor = MarkerColor; - // If the selected has a marker, make it not stand out - if (Planes[SelectedPlane].marker) { - Planes[SelectedPlane].marker.setIcon(Planes[SelectedPlane].funcGetIcon()); - } - Planes[SelectedPlane].tr.classList.remove("selected"); + Planes[SelectedPlane].clearLines(); + Planes[SelectedPlane].updateMarker(); + $(Planes[SelectedPlane].tr).removeClass("selected"); } // If we are clicking the same plane, we are deselected it. - if (String(SelectedPlane) != String(hex)) { + if (SelectedPlane === hex) { + hex = null; + } + + if (hex !== null) { // Assign the new selected SelectedPlane = hex; Planes[SelectedPlane].is_selected = true; - // If the selected has a marker, make it stand out - if (Planes[SelectedPlane].marker) { - Planes[SelectedPlane].funcUpdateLines(); - Planes[SelectedPlane].marker.setIcon(Planes[SelectedPlane].funcGetIcon()); - } - Planes[SelectedPlane].tr.classList.add("selected"); + Planes[SelectedPlane].updateLines(); + Planes[SelectedPlane].updateMarker(); + $(Planes[SelectedPlane].tr).addClass("selected"); } else { SelectedPlane = null; } From 58e5485c2ab94ef31640d43fa362c8f0e71e8e32 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 7 Jan 2015 16:32:58 +0000 Subject: [PATCH 103/610] Stylistic cleanups. Unify special squawk data. --- public_html/planeObject.js | 54 +++++++++++++++++++------------------- public_html/script.js | 36 ++++++++++++------------- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/public_html/planeObject.js b/public_html/planeObject.js index 1104130c8..50586471c 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -1,4 +1,4 @@ -var planeSvg = "M 0,0 " + +var PlaneSvg = "M 0,0 " + "M 1.9565564,41.694305 C 1.7174505,40.497708 1.6419973,38.448747 " + "1.8096508,37.70494 1.8936398,37.332056 2.0796653,36.88191 2.222907,36.70461 " + "2.4497603,36.423844 4.087816,35.47248 14.917931,29.331528 l 12.434577," + @@ -48,22 +48,25 @@ var planeObject = { // Data packet numbers messages : null, - // Track history - tracklinesegs : [], - + // Track history as a series of line segments + track_linesegs : [], // When was this last updated (receiver timestamp) last_message_time : null, last_position_time : null, - historySize : 0, + // When was this last updated (seconds before last update) + seen : null, + seen_pos : null, + + history_size : 0, visible : true, // GMap Details marker : null, icon : { strokeWeight: 1, - path: planeSvg, + path: PlaneSvg, scale: 0.4, fillColor: MarkerColor, fillOpacity: 0.9, @@ -75,7 +78,7 @@ var planeObject = { // Only useful for a long running browser session. updateTrack : function() { var here = new google.maps.LatLng(this.latitude, this.longitude); - if (this.tracklinesegs.length == 0) { + if (this.track_linesegs.length == 0) { // Brand new track //console.log(this.icao + " new track"); var newseg = { track : new google.maps.MVCArray([here,here]), @@ -85,12 +88,12 @@ var planeObject = { estimated : false, ground : (this.altitude === "ground") }; - this.tracklinesegs.push(newseg); - this.historySize += 2; + this.track_linesegs.push(newseg); + this.history_size += 2; return; } - var lastseg = this.tracklinesegs[this.tracklinesegs.length - 1]; + var lastseg = this.track_linesegs[this.track_linesegs.length - 1]; var lastpos = lastseg.track.getAt(lastseg.track.getLength() - 1); var elapsed = (this.last_position_time - lastseg.head_update); @@ -105,11 +108,11 @@ var planeObject = { if (!lastseg.estimated) { // >5s gap in data, create a new estimated segment //console.log(this.icao + " switching to estimated"); - this.tracklinesegs.push({ track : new google.maps.MVCArray([lastpos, here]), + this.track_linesegs.push({ track : new google.maps.MVCArray([lastpos, here]), line : null, head_update : this.last_position_time, estimated : true }); - this.historySize += 2; + this.history_size += 2; return true; } @@ -117,20 +120,20 @@ var planeObject = { //console.log(this.icao + " extending estimated (" + lastseg.track.getLength() + ")"); lastseg.track.push(here); lastseg.head_update = this.last_position_time; - this.historySize++; + this.history_size++; return true; } if (lastseg.estimated) { // We are back to good data. //console.log(this.icao + " switching to good track"); - this.tracklinesegs.push({ track : new google.maps.MVCArray([lastpos, here]), + this.track_linesegs.push({ track : new google.maps.MVCArray([lastpos, here]), line : null, head_update : this.last_position_time, tail_update : this.last_position_time, estimated : false, ground : (this.altitude === "ground") }); - this.historySize += 2; + this.history_size += 2; return true; } @@ -141,13 +144,13 @@ var planeObject = { // assume the state changed halfway between the two points var midpoint = google.maps.geometry.spherical.interpolate(lastpos,here,0.5); lastseg.track.push(midpoint); - this.tracklinesegs.push({ track : new google.maps.MVCArray([midpoint,here,here]), + this.track_linesegs.push({ track : new google.maps.MVCArray([midpoint,here,here]), line : null, head_update : this.last_position_time, tail_update : this.last_position_time, estimated : false, ground : (this.altitude === "ground") }); - this.historySize += 4; + this.history_size += 4; return true; } @@ -159,7 +162,7 @@ var planeObject = { //console.log(this.icao + " retain last point"); lastseg.track.push(here); lastseg.tail_update = lastseg.head_update; - this.historySize ++; + this.history_size ++; } else { // replace the last point with the current position lastseg.track.setAt(lastseg.track.getLength()-1, here); @@ -170,8 +173,8 @@ var planeObject = { // This is to remove the line from the screen if we deselect the plane clearLines : function() { - for (var i = 0; i < this.tracklinesegs.length; ++i) { - var seg = this.tracklinesegs[i]; + for (var i = 0; i < this.track_linesegs.length; ++i) { + var seg = this.track_linesegs[i]; if (seg.line !== null) { seg.line.setMap(null); seg.line = null; @@ -192,11 +195,8 @@ var planeObject = { // If the squawk code is one of the international emergency codes, // match the info window alert color. - var squawk_col = { '7500' : 'rgb(255, 85, 85)', - '7600' : 'rgb(0, 255, 255)', - '7700' : 'rgb(255, 255, 0)' }; - if (this.squawk in squawk_col) - col = squawk_col[this.squawk]; + if (this.squawk in SpecialSquawks) + col = SpecialSquawks[this.squawk].markerColor; var weight = this.is_selected ? 2 : 1; var rotation = (this.track === null ? 0 : this.track); @@ -316,8 +316,8 @@ var planeObject = { if (!this.is_selected) return; - for (var i = 0; i < this.tracklinesegs.length; ++i) { - var seg = this.tracklinesegs[i]; + for (var i = 0; i < this.track_linesegs.length; ++i) { + var seg = this.track_linesegs[i]; if (seg.line === null) { // console.log("create line for seg " + i + " with " + seg.track.getLength() + " points" + (seg.estimated ? " (estimated)" : "")); // for (var j = 0; j < seg.track.getLength(); j++) { diff --git a/public_html/script.js b/public_html/script.js index 25e38dd77..b8e682de2 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -4,24 +4,24 @@ var Planes = {}; var PlanesOrdered = []; var SelectedPlane = null; -var EmergencySquawks = { - '7500' : 'Aircraft Hijacking', - '7600' : 'Radio Failure', - '7700' : 'General Emergency' +var SpecialSquawks = { + '7500' : { cssClass: 'squawk7500', markerColor: 'rgb(255, 85, 85)', text: 'Aircraft Hijacking' }, + '7600' : { cssClass: 'squawk7600', markerColor: 'rgb(0, 255, 255)', text: 'Radio Failure' }, + '7700' : { cssClass: 'squawk7700', markerColor: 'rgb(255, 255, 0)', text: 'General Emergency' } }; // Get current map settings -CenterLat = Number(localStorage['CenterLat']) || CONST_CENTERLAT; -CenterLon = Number(localStorage['CenterLon']) || CONST_CENTERLON; -ZoomLvl = Number(localStorage['ZoomLvl']) || CONST_ZOOMLVL; +var CenterLat = Number(localStorage['CenterLat']) || CONST_CENTERLAT; +var CenterLon = Number(localStorage['CenterLon']) || CONST_CENTERLON; +var ZoomLvl = Number(localStorage['ZoomLvl']) || CONST_ZOOMLVL; -Dump1090Version = "unknown version"; -RefreshInterval = 1000; +var Dump1090Version = "unknown version"; +var RefreshInterval = 1000; -PlaneRowTemplate = null; +var PlaneRowTemplate = null; -TrackedAircraft = 0 -TrackedPositions = 0 +var TrackedAircraft = 0; +var TrackedPositions = 0; function fetchData() { $.getJSON('data/aircraft.json', function(data) { @@ -314,9 +314,9 @@ function refreshSelected() { } var emerg = document.getElementById('selected_emergency'); - if (selected.squawk in EmergencySquawks) { - emerg.className = 'squawk' + selected.squawk; - emerg.textContent = '\u00a0Squawking: ' + EmergencySquawks[selected.squawk] + '\u00a0'; + if (selected.squawk in SpecialSquawks) { + emerg.className = SpecialSquawks[selected.squawk].cssClass; + emerg.textContent = '\u00a0Squawking: ' + SpecialSquawks[selected.squawk].text + '\u00a0'; } else { emerg.className = 'hidden'; } @@ -433,7 +433,7 @@ function refreshTableInfo() { for (var i = 0; i < PlanesOrdered.length; ++i) { var tableplane = PlanesOrdered[i]; - TrackedHistorySize += tableplane.historySize; + TrackedHistorySize += tableplane.history_size; if (!tableplane.visible) { tableplane.tr.className = "plane_table_row hidden"; } else { @@ -445,8 +445,8 @@ function refreshTableInfo() { if (tableplane.icao == SelectedPlane) classes += " selected"; - if (tableplane.squawk in EmergencySquawks) { - classes += (" squawk" + tableplane.squawk); + if (tableplane.squawk in SpecialSquawks) { + classes = classes + " " + SpecialSquawks[tableplane.squawk].cssClass; show_squawk_warning = true; } From 29509d96333b8ad2d49dfaf98cff765a21222628 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 7 Jan 2015 17:18:33 +0000 Subject: [PATCH 104/610] Make PlaneObject a class. Rearrange internal storage of positions. Maintain sitedist within PlaneObject, not externally. Clean up speed/dist/etc formatting. Show both metric & imperial values in the plane detail infobox. --- public_html/gmap.html | 2 +- public_html/planeObject.js | 584 ++++++++++++++++++------------------- public_html/script.js | 208 ++++++------- 3 files changed, 400 insertions(+), 394 deletions(-) diff --git a/public_html/gmap.html b/public_html/gmap.html index 261a2e0d5..20dc878b5 100644 --- a/public_html/gmap.html +++ b/public_html/gmap.html @@ -117,7 +117,7 @@ - Lat/Long: n/a + Position: n/a diff --git a/public_html/planeObject.js b/public_html/planeObject.js index 50586471c..8af6f242f 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -28,333 +28,329 @@ var PlaneSvg = "M 0,0 " + "-2.858644 -0.145909,-5.208974 -0.209316,-5.222958 -0.06341,-0.01399 -0.974464," + "-0.0493 -2.024551,-0.07845 L 23.247235,38.61921 18.831373,39.8906 C 4.9432155," + "43.88916 4.2929558,44.057819 3.4954426,43.86823 2.7487826,43.690732 2.2007966," + - "42.916622 1.9565564,41.694305 z" + "42.916622 1.9565564,41.694305 z"; +function PlaneObject(icao) { + // Info about the plane + this.icao = icao; + this.flight = null; + this.squawk = null; + this.selected = false; -var planeObject = { // Basic location information - altitude : null, - speed : null, - track : null, - latitude : null, - longitude : null, - - // Info about the plane - flight : null, - squawk : null, - icao : null, - is_selected : false, + this.altitude = null; + this.speed = null; + this.track = null; + this.position = null; + this.sitedist = null; // Data packet numbers - messages : null, + this.messages = null; // Track history as a series of line segments - track_linesegs : [], - + this.track_linesegs = []; + this.history_size = 0; + // When was this last updated (receiver timestamp) - last_message_time : null, - last_position_time : null, + this.last_message_time = null; + this.last_position_time = null; // When was this last updated (seconds before last update) - seen : null, - seen_pos : null, - - history_size : 0, - visible : true, + this.seen = null; + this.seen_pos = null; - // GMap Details - marker : null, - icon : { - strokeWeight: 1, - path: PlaneSvg, - scale: 0.4, - fillColor: MarkerColor, - fillOpacity: 0.9, - anchor: new google.maps.Point(32, 32), // Set anchor to middle of plane. - rotation: 0 - }, + // Display info + this.visible = true; + this.marker = null; + this.icon = { strokeWeight: 1, + path: PlaneSvg, + scale: 0.4, + fillColor: MarkerColor, + fillOpacity: 0.9, + anchor: new google.maps.Point(32, 32), // Set anchor to middle of plane. + rotation: 0 }; +} - // Appends data to the running track so we can get a visual tail on the plane - // Only useful for a long running browser session. - updateTrack : function() { - var here = new google.maps.LatLng(this.latitude, this.longitude); - if (this.track_linesegs.length == 0) { - // Brand new track - //console.log(this.icao + " new track"); - var newseg = { track : new google.maps.MVCArray([here,here]), - line : null, - head_update : this.last_position_time, - tail_update : this.last_position_time, - estimated : false, - ground : (this.altitude === "ground") - }; - this.track_linesegs.push(newseg); - this.history_size += 2; - return; - } - - var lastseg = this.track_linesegs[this.track_linesegs.length - 1]; - var lastpos = lastseg.track.getAt(lastseg.track.getLength() - 1); - var elapsed = (this.last_position_time - lastseg.head_update); - - var new_data = (here !== lastpos); - var est_track = (elapsed > 5); - var ground_track = (this.altitude === "ground"); - - if (!new_data) - return false; +// Appends data to the running track so we can get a visual tail on the plane +// Only useful for a long running browser session. +PlaneObject.prototype.updateTrack = function() { + var here = this.position; - if (est_track) { - if (!lastseg.estimated) { - // >5s gap in data, create a new estimated segment - //console.log(this.icao + " switching to estimated"); - this.track_linesegs.push({ track : new google.maps.MVCArray([lastpos, here]), - line : null, - head_update : this.last_position_time, - estimated : true }); - this.history_size += 2; - return true; - } - - // Append to ongoing estimated line - //console.log(this.icao + " extending estimated (" + lastseg.track.getLength() + ")"); - lastseg.track.push(here); - lastseg.head_update = this.last_position_time; - this.history_size++; - return true; - } - - if (lastseg.estimated) { - // We are back to good data. - //console.log(this.icao + " switching to good track"); + if (this.track_linesegs.length == 0) { + // Brand new track + //console.log(this.icao + " new track"); + var newseg = { track : new google.maps.MVCArray([here,here]), + line : null, + head_update : this.last_position_time, + tail_update : this.last_position_time, + estimated : false, + ground : (this.altitude === "ground") + }; + this.track_linesegs.push(newseg); + this.history_size += 2; + return; + } + + var lastseg = this.track_linesegs[this.track_linesegs.length - 1]; + var lastpos = lastseg.track.getAt(lastseg.track.getLength() - 1); + var elapsed = (this.last_position_time - lastseg.head_update); + + var new_data = (here !== lastpos); + var est_track = (elapsed > 5); + var ground_track = (this.altitude === "ground"); + + if (!new_data) + return false; + + if (est_track) { + if (!lastseg.estimated) { + // >5s gap in data, create a new estimated segment + //console.log(this.icao + " switching to estimated"); this.track_linesegs.push({ track : new google.maps.MVCArray([lastpos, here]), - line : null, - head_update : this.last_position_time, - tail_update : this.last_position_time, - estimated : false, - ground : (this.altitude === "ground") }); + line : null, + head_update : this.last_position_time, + estimated : true }); this.history_size += 2; return true; } - - if ( (lastseg.ground && this.altitude !== "ground") || - (!lastseg.ground && this.altitude === "ground") ) { - //console.log(this.icao + " ground state changed"); - // Create a new segment as the ground state changed. - // assume the state changed halfway between the two points - var midpoint = google.maps.geometry.spherical.interpolate(lastpos,here,0.5); - lastseg.track.push(midpoint); - this.track_linesegs.push({ track : new google.maps.MVCArray([midpoint,here,here]), - line : null, - head_update : this.last_position_time, - tail_update : this.last_position_time, - estimated : false, - ground : (this.altitude === "ground") }); - this.history_size += 4; - return true; - } - - // Add more data to the existing track. - // We only retain some historical points, at 5+ second intervals, - // plus the most recent point - if (this.last_position_time - lastseg.tail_update >= 5) { - // enough time has elapsed; retain the last point and add a new one - //console.log(this.icao + " retain last point"); - lastseg.track.push(here); - lastseg.tail_update = lastseg.head_update; - this.history_size ++; - } else { - // replace the last point with the current position - lastseg.track.setAt(lastseg.track.getLength()-1, here); - } + + // Append to ongoing estimated line + //console.log(this.icao + " extending estimated (" + lastseg.track.getLength() + ")"); + lastseg.track.push(here); lastseg.head_update = this.last_position_time; + this.history_size++; return true; - }, - - // This is to remove the line from the screen if we deselect the plane - clearLines : function() { - for (var i = 0; i < this.track_linesegs.length; ++i) { - var seg = this.track_linesegs[i]; - if (seg.line !== null) { - seg.line.setMap(null); - seg.line = null; - } - } - }, - - updateIcon : function() { - var col = MarkerColor; - - // If this marker is selected we should make it lighter than the rest. - if (this.is_selected) - col = SelectedColor; - - // If we have not seen a recent update, change color - if (this.seen > 15) - col = StaleColor; - - // If the squawk code is one of the international emergency codes, - // match the info window alert color. - if (this.squawk in SpecialSquawks) - col = SpecialSquawks[this.squawk].markerColor; - - var weight = this.is_selected ? 2 : 1; - var rotation = (this.track === null ? 0 : this.track); - - if (col === this.icon.fillColor && weight === this.icon.strokeWeight && rotation === this.icon.rotation) - return false; // no changes - - this.icon.fillColor = col; - this.icon.strokeWeight = weight; - this.icon.rotation = rotation; - if (this.marker) - this.marker.setIcon(this.icon); - + } + + if (lastseg.estimated) { + // We are back to good data. + //console.log(this.icao + " switching to good track"); + this.track_linesegs.push({ track : new google.maps.MVCArray([lastpos, here]), + line : null, + head_update : this.last_position_time, + tail_update : this.last_position_time, + estimated : false, + ground : (this.altitude === "ground") }); + this.history_size += 2; return true; - }, - - // TODO: Trigger actions of a selecting a plane - selectPlane : function(){ - selectPlaneByHex(this.icao); - }, + } + + if ( (lastseg.ground && this.altitude !== "ground") || + (!lastseg.ground && this.altitude === "ground") ) { + //console.log(this.icao + " ground state changed"); + // Create a new segment as the ground state changed. + // assume the state changed halfway between the two points + var midpoint = google.maps.geometry.spherical.interpolate(lastpos,here,0.5); + lastseg.track.push(midpoint); + this.track_linesegs.push({ track : new google.maps.MVCArray([midpoint,here,here]), + line : null, + head_update : this.last_position_time, + tail_update : this.last_position_time, + estimated : false, + ground : (this.altitude === "ground") }); + this.history_size += 4; + return true; + } + + // Add more data to the existing track. + // We only retain some historical points, at 5+ second intervals, + // plus the most recent point + if (this.last_position_time - lastseg.tail_update >= 5) { + // enough time has elapsed; retain the last point and add a new one + //console.log(this.icao + " retain last point"); + lastseg.track.push(here); + lastseg.tail_update = lastseg.head_update; + this.history_size ++; + } else { + // replace the last point with the current position + lastseg.track.setAt(lastseg.track.getLength()-1, here); + } + lastseg.head_update = this.last_position_time; + return true; +}; - // Update our data - updateData : function(receiver_timestamp, data){ - // Update all of our data - this.icao = data.hex; - this.messages = data.messages; - this.last_message_time = receiver_timestamp - data.seen; - - if (typeof data.altitude !== "undefined") - this.altitude = data.altitude; - if (typeof data.speed !== "undefined") - this.speed = data.speed; - if (typeof data.track !== "undefined") - this.track = data.track; - if (typeof data.lat !== "undefined") { - this.latitude = data.lat; - this.longitude = data.lon; - this.last_position_time = receiver_timestamp - data.seen_pos; +// This is to remove the line from the screen if we deselect the plane +PlaneObject.prototype.clearLines = function() { + for (var i = 0; i < this.track_linesegs.length; ++i) { + var seg = this.track_linesegs[i]; + if (seg.line !== null) { + seg.line.setMap(null); + seg.line = null; } - if (typeof data.flight !== "undefined") - this.flight = data.flight; - if (typeof data.squawk !== "undefined") - this.squawk = data.squawk; - }, + } +}; - updateTick : function(receiver_timestamp) { - // recompute seen and seen_pos - this.seen = receiver_timestamp - this.last_message_time; - this.seen_pos = (this.last_position_time === null ? null : receiver_timestamp - this.last_position_time); +PlaneObject.prototype.updateIcon = function() { + var col = MarkerColor; + + // If this marker is selected we should make it lighter than the rest. + if (this.is_selected) + col = SelectedColor; + + // If we have not seen a recent update, change color + if (this.seen > 15) + col = StaleColor; + + // If the squawk code is one of the international emergency codes, + // match the info window alert color. + if (this.squawk in SpecialSquawks) + col = SpecialSquawks[this.squawk].markerColor; + + var weight = this.is_selected ? 2 : 1; + var rotation = (this.track === null ? 0 : this.track); + + if (col === this.icon.fillColor && weight === this.icon.strokeWeight && rotation === this.icon.rotation) + return false; // no changes + + this.icon.fillColor = col; + this.icon.strokeWeight = weight; + this.icon.rotation = rotation; + if (this.marker) + this.marker.setIcon(this.icon); + + return true; +}; - // If no packet in over 58 seconds, clear the plane. - if (this.seen > 58) { - if (this.visible) { - //console.log("hiding " + this.icao); - this.clearMarker(); - this.visible = false; - if (SelectedPlane == this.icao) - selectPlaneByHex(null); - } - } else { - this.visible = true; - if (this.latitude !== null) { - if (this.updateTrack()) { - this.updateLines(); - this.updateMarker(true); - } else { - this.updateMarker(false); // didn't move - } - } - } - }, +// Update our data +PlaneObject.prototype.updateData = function(receiver_timestamp, data) { + // Update all of our data + this.icao = data.hex; + this.messages = data.messages; + this.last_message_time = receiver_timestamp - data.seen; + + if (typeof data.altitude !== "undefined") + this.altitude = data.altitude; + if (typeof data.speed !== "undefined") + this.speed = data.speed; + if (typeof data.track !== "undefined") + this.track = data.track; + if (typeof data.lat !== "undefined") { + this.position = new google.maps.LatLng(data.lat, data.lon); + this.last_position_time = receiver_timestamp - data.seen_pos; - clearMarker: function() { - if (this.marker) { - this.marker.setMap(null); - this.marker = null; - } - }, + if (SitePosition !== null) { + this.sitedist = google.maps.geometry.spherical.computeDistanceBetween (SitePosition, this.position); + } + } + if (typeof data.flight !== "undefined") + this.flight = data.flight; + if (typeof data.squawk !== "undefined") + this.squawk = data.squawk; +}; - // Update our marker on the map - updateMarker: function(moved) { - if (!this.visible) { +PlaneObject.prototype.updateTick = function(receiver_timestamp) { + // recompute seen and seen_pos + this.seen = receiver_timestamp - this.last_message_time; + this.seen_pos = (this.last_position_time === null ? null : receiver_timestamp - this.last_position_time); + + // If no packet in over 58 seconds, clear the plane. + if (this.seen > 58) { + if (this.visible) { + //console.log("hiding " + this.icao); this.clearMarker(); - return; + this.visible = false; + if (SelectedPlane == this.icao) + selectPlaneByHex(null); + } + } else { + this.visible = true; + if (this.position !== null) { + if (this.updateTrack()) { + this.updateLines(); + this.updateMarker(true); + } else { + this.updateMarker(false); // didn't move + } } + } +}; - if (this.marker) { - if (moved) - this.marker.setPosition(new google.maps.LatLng(this.latitude, this.longitude)); - this.updateIcon(); - } else { - this.updateIcon(); - this.marker = new google.maps.Marker({ - position: new google.maps.LatLng(this.latitude, this.longitude), - map: GoogleMap, - icon: this.icon, - visible: true - }); - - // This is so we can match icao address - this.marker.icao = this.icao; - - // Trap clicks for this marker. - google.maps.event.addListener(this.marker, 'click', this.selectPlane); - } +PlaneObject.prototype.clearMarker = function() { + if (this.marker) { + this.marker.setMap(null); + google.maps.event.clearListeners(this.marker, 'click'); + this.marker = null; + } +}; + +// Update our marker on the map +PlaneObject.prototype.updateMarker = function(moved) { + if (!this.visible) { + this.clearMarker(); + return; + } + + if (this.marker) { + if (moved) + this.marker.setPosition(this.position); + this.updateIcon(); + } else { + this.updateIcon(); + this.marker = new google.maps.Marker({ + position: this.position, + map: GoogleMap, + icon: this.icon, + visible: true + }); - // Setting the marker title - if (this.flight === null || this.flight.length == 0) { - this.marker.setTitle(this.hex); - } else { - this.marker.setTitle(this.flight+' ('+this.icao+')'); - } - }, + // This is so we can match icao address + this.marker.icao = this.icao; + + // Trap clicks for this marker. + google.maps.event.addListener(this.marker, 'click', selectPlaneByHex.bind(undefined,this.icao)); + } - // Update our planes tail line, - updateLines: function() { - if (!this.is_selected) - return; - - for (var i = 0; i < this.track_linesegs.length; ++i) { - var seg = this.track_linesegs[i]; - if (seg.line === null) { - // console.log("create line for seg " + i + " with " + seg.track.getLength() + " points" + (seg.estimated ? " (estimated)" : "")); - // for (var j = 0; j < seg.track.getLength(); j++) { - // console.log(" point " + j + " at " + seg.track.getAt(j).lat() + "," + seg.track.getAt(j).lng()); - // } - - if (seg.estimated) { - var lineSymbol = { - path: 'M 0,-1 0,1', - strokeOpacity : 1, - strokeColor : '#804040', - strokeWeight : 2, - scale: 2 - }; + // Setting the marker title + if (this.flight === null || this.flight.length == 0) { + this.marker.setTitle(this.hex); + } else { + this.marker.setTitle(this.flight+' ('+this.icao+')'); + } +}; - seg.line = new google.maps.Polyline({ - path: seg.track, - strokeOpacity: 0, - icons: [{ - icon: lineSymbol, - offset: '0', - repeat: '10px' }], - map : GoogleMap }); - } else { - seg.line = new google.maps.Polyline({ - path: seg.track, - strokeOpacity: 1.0, - strokeColor: (seg.ground ? '#408040' : '#000000'), - strokeWeight: 3, - map: GoogleMap }); - } +// Update our planes tail line, +PlaneObject.prototype.updateLines = function() { + if (!this.is_selected) + return; + + for (var i = 0; i < this.track_linesegs.length; ++i) { + var seg = this.track_linesegs[i]; + if (seg.line === null) { + // console.log("create line for seg " + i + " with " + seg.track.getLength() + " points" + (seg.estimated ? " (estimated)" : "")); + // for (var j = 0; j < seg.track.getLength(); j++) { + // console.log(" point " + j + " at " + seg.track.getAt(j).lat() + "," + seg.track.getAt(j).lng()); + // } + + if (seg.estimated) { + var lineSymbol = { + path: 'M 0,-1 0,1', + strokeOpacity : 1, + strokeColor : '#804040', + strokeWeight : 2, + scale: 2 + }; + + seg.line = new google.maps.Polyline({ + path: seg.track, + strokeOpacity: 0, + icons: [{ + icon: lineSymbol, + offset: '0', + repeat: '10px' }], + map : GoogleMap }); + } else { + seg.line = new google.maps.Polyline({ + path: seg.track, + strokeOpacity: 1.0, + strokeColor: (seg.ground ? '#408040' : '#000000'), + strokeWeight: 3, + map: GoogleMap }); } } - }, - - destroy : function() { - this.clearLines(); - this.clearMarker(); } }; + +PlaneObject.prototype.destroy = function() { + this.clearLines(); + this.clearMarker(); +}; diff --git a/public_html/script.js b/public_html/script.js index b8e682de2..f0464ce6c 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -39,12 +39,10 @@ function fetchData() { if (Planes[hex]) { plane = Planes[hex]; } else { - plane = jQuery.extend(true, {}, planeObject); - + plane = new PlaneObject(hex); plane.tr = PlaneRowTemplate.cloneNode(true); plane.tr.cells[0].textContent = hex; // this won't change - plane.tr.addEventListener('click', $.proxy(plane.selectPlane, plane)); - plane.sitedist = null; + plane.tr.addEventListener('click', selectPlaneByHex.bind(undefined,hex)); Planes[hex] = plane; PlanesOrdered.push(plane); @@ -282,6 +280,98 @@ function reaper() { refreshSelected(); } +// +// formatting helpers +// + +var TrackDirections = ["North","Northeast","East","Southeast","South","Southwest","West","Northwest"]; + +// track in degrees (0..359) +function format_track_brief(track) { + return Math.round(track); +} + +// track in degrees (0..359) +function format_track_long(track) { + var trackDir = Math.floor((360 + track % 360 + 22.5) / 45) % 8; + return Math.round(track) + "\u00b0 (" + TrackDirections[trackDir] + ")"; +} + +// alt in ft +function format_altitude_brief(alt) { + if (alt === null) + return ""; + if (alt === "ground") + return "ground"; + + if (Metric) + return Math.round(alt / 3.2828); + else + return Math.round(alt); +} + +// alt in ft +function format_altitude_long(alt) { + if (alt === null) + return "n/a"; + if (alt === "ground") + return "on ground"; + + if (Metric) + return Math.round(alt / 3.2828) + " m / " + Math.round(alt) + " ft"; + else + return Math.round(alt) + " ft / " + Math.round(alt / 3.2828) + " m"; +} + +// speed in kts +function format_speed_brief(speed) { + if (speed === null) + return ""; + + if (Metric) + return Math.round(speed * 1.852); + else + return Math.round(speed); +} + +// speed in kts +function format_speed_long(speed) { + if (speed === null) + return "n/a"; + + if (Metric) + return Math.round(speed * 1.852) + " km/h / " + Math.round(speed) + " kt"; + else + return Math.round(speed) + " kt / " + Math.round(speed * 1.852) + " km/h"; +} + +// dist in metres +function format_distance_brief(dist) { + if (dist === null) + return ""; + + if (Metric) + return (dist/1000).toFixed(1); + else + return (dist/1852).toFixed(1); +} + +// dist in metres +function format_distance_long(dist) { + if (dist === null) + return "n/a"; + + if (Metric) + return (dist/1000).toFixed(1) + " km / " + (dist/1852).toFixed(1) + " NM"; + else + return (dist/1852).toFixed(1) + " NM / " + (dist/1000).toFixed(1) + " km"; +} + +// p as a LatLng +function format_latlng(p) { + return p.lat().toFixed(5) + "\u00b0, " + p.lng().toFixed(5) + "\u00b0"; +} + // Refresh the detail window about the plane function refreshSelected() { var selected = false; @@ -321,14 +411,7 @@ function refreshSelected() { emerg.className = 'hidden'; } - if (selected.altitude === null) - $("#selected_altitude").text("n/a"); - else if (selected.altitude === "ground") - $("#selected_altitude").text("on ground"); - else if (Metric) - $("#selected_altitude").text(Math.round(selected.altitude / 3.2828) + ' m'); - else - $("#selected_altitude").text(Math.round(selected.altitude) + ' ft'); + $("#selected_altitude").text(format_altitude_long(selected.altitude)); if (selected.squawk === null || selected.squawk === '0000') { $('#selected_squawk').text('n/a'); @@ -336,21 +419,9 @@ function refreshSelected() { $('#selected_squawk').text(selected.squawk); } - if (selected.speed === null) { - $('#selected_speed').text('n/a'); - } else if (Metric) { - $('#selected_speed').text(Math.round(selected.speed * 1.852) + ' km/h'); - } else { - $('#selected_speed').text(Math.round(selected.speed) + ' kt'); - } - + $('#selected_speed').text(format_speed_long(selected.speed)); $('#selected_icao').text(selected.icao); - - if (selected.track === null) { - $('#selected_track').text('n/a'); - } else { - $('#selected_track').text(selected.track + '\u00b0' + ' (' + trackLongName(selected.track) + ')'); - } + $('#selected_track').text(format_track_long(selected.track)); if (selected.seen <= 1) { $('#selected_seen').text('now'); @@ -358,72 +429,20 @@ function refreshSelected() { $('#selected_seen').text(selected.seen + 's ago'); } - if (selected.latitude === null) { + if (selected.position === null) { $('#selected_position').text('n/a'); } else { if (selected.seen_pos > 1) { - $('#selected_position').text(selected.latitude + ', ' + selected.longitude + " (" + selected.seen_pos + "s ago)"); + $('#selected_position').text(format_latlng(selected.position) + " (" + selected.seen_pos + "s ago)"); } else { - $('#selected_position').text(selected.latitude + ', ' + selected.longitude); + $('#selected_position').text(format_latlng(selected.position)); } } - - if (selected.sitedist !== null) { - var dist = selected.sitedist; - if (Metric) { - dist /= 1000; - } else { - dist /= 1852; - } - - dist = (Math.round((dist)*10)/10).toFixed(1); - - $('#selected_sitedist').text(dist + (Metric ? ' km' : ' NM')); - } else { - $('#selected_sitedist').text("n/a"); - } -} - -function trackShortName(track) { - var trackIndex = Math.floor((360 + track % 360 + 22.5) / 45) % 8; - return ["N","NE","E","SE","S","SW","W","NW"][trackIndex]; -} - -function trackLongName(track) { - var trackIndex = Math.floor((360 + track % 360 + 22.5) / 45) % 8; - return ["North","Northeast","East","Southeast","South","Southwest","West","Northwest"][trackIndex]; -} - -// Refeshes the larger table of all the planes - -function format_altitude(alt) { - if (alt === null) - return ""; - else if (alt === "ground") - return "ground"; - else if (Metric) - return Math.round(alt / 3.2828); - else - return Math.round(alt); -} - -function format_speed(speed) { - if (speed === null) - return ""; - else if (Metric) - return Math.round(speed * 1.852); - else - return Math.round(speed); -} - -function format_distance(dist) { - if (Metric) { - return (Math.round(dist/100) / 10).toFixed(1); - } else { - return (Math.round(dist/185.2) / 10).toFixed(1); - } + + $('#selected_sitedist').text(format_distance_long(selected.sitedist)); } +// Refreshes the larger table of all the planes function refreshTableInfo() { var show_squawk_warning = false; @@ -440,7 +459,7 @@ function refreshTableInfo() { TrackedAircraft++; var classes = "plane_table_row"; - if (tableplane.latitude !== null) + if (tableplane.position !== null) classes += " vPosition"; if (tableplane.icao == SelectedPlane) classes += " selected"; @@ -453,23 +472,14 @@ function refreshTableInfo() { // ICAO doesn't change tableplane.tr.cells[1].textContent = (tableplane.flight !== null ? tableplane.flight : ""); tableplane.tr.cells[2].textContent = (tableplane.squawk !== null ? tableplane.squawk : ""); - tableplane.tr.cells[3].textContent = format_altitude(tableplane.altitude); - tableplane.tr.cells[4].textContent = format_speed(tableplane.speed); + tableplane.tr.cells[3].textContent = format_altitude_brief(tableplane.altitude); + tableplane.tr.cells[4].textContent = format_speed_brief(tableplane.speed); - if (tableplane.latitude !== null) + if (tableplane.position !== null) ++TrackedAircraftPositions; - // Add distance column to table if site coordinates are provided - if (SitePosition !== null && tableplane.latitude !== null) { - var planeLatLon = new google.maps.LatLng(tableplane.latitude, tableplane.longitude); - var dist = google.maps.geometry.spherical.computeDistanceBetween (SitePosition, planeLatLon); - tableplane.tr.cells[5].textContent = format_distance(dist); - tableplane.sitedist = dist; - } else { - tableplane.tr.cells[5].textContent = ""; - } - - tableplane.tr.cells[6].textContent = (tableplane.track !== null ? tableplane.track : ""); + tableplane.tr.cells[5].textContent = format_distance_brief(tableplane.sitedist); + tableplane.tr.cells[6].textContent = format_track_brief(tableplane.track); tableplane.tr.cells[7].textContent = tableplane.messages; tableplane.tr.cells[8].textContent = tableplane.seen; From df79f7c9898d5fb07443e05962b93f69ad53793e Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 7 Jan 2015 17:32:20 +0000 Subject: [PATCH 105/610] Fix up selected vs is_selected. --- public_html/planeObject.js | 6 +++--- public_html/script.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/public_html/planeObject.js b/public_html/planeObject.js index 8af6f242f..08b16f2e2 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -184,7 +184,7 @@ PlaneObject.prototype.updateIcon = function() { var col = MarkerColor; // If this marker is selected we should make it lighter than the rest. - if (this.is_selected) + if (this.selected) col = SelectedColor; // If we have not seen a recent update, change color @@ -196,7 +196,7 @@ PlaneObject.prototype.updateIcon = function() { if (this.squawk in SpecialSquawks) col = SpecialSquawks[this.squawk].markerColor; - var weight = this.is_selected ? 2 : 1; + var weight = this.selected ? 2 : 1; var rotation = (this.track === null ? 0 : this.track); if (col === this.icon.fillColor && weight === this.icon.strokeWeight && rotation === this.icon.rotation) @@ -310,7 +310,7 @@ PlaneObject.prototype.updateMarker = function(moved) { // Update our planes tail line, PlaneObject.prototype.updateLines = function() { - if (!this.is_selected) + if (!this.selected) return; for (var i = 0; i < this.track_linesegs.length; ++i) { diff --git a/public_html/script.js b/public_html/script.js index f0464ce6c..7399c6db0 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -583,7 +583,7 @@ function selectPlaneByHex(hex) { //console.log("select: " + hex); // If SelectedPlane has something in it, clear out the selected if (SelectedPlane != null) { - Planes[SelectedPlane].is_selected = false; + Planes[SelectedPlane].selected = false; Planes[SelectedPlane].clearLines(); Planes[SelectedPlane].updateMarker(); $(Planes[SelectedPlane].tr).removeClass("selected"); @@ -597,7 +597,7 @@ function selectPlaneByHex(hex) { if (hex !== null) { // Assign the new selected SelectedPlane = hex; - Planes[SelectedPlane].is_selected = true; + Planes[SelectedPlane].selected = true; Planes[SelectedPlane].updateLines(); Planes[SelectedPlane].updateMarker(); $(Planes[SelectedPlane].tr).addClass("selected"); From 139ed5cb363b546cb98d6f9909e05421ce752628 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 7 Jan 2015 17:35:32 +0000 Subject: [PATCH 106/610] Turn on strict mode, fix a few errors. --- public_html/planeObject.js | 2 ++ public_html/script.js | 13 +++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/public_html/planeObject.js b/public_html/planeObject.js index 08b16f2e2..c5e7214a1 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -1,3 +1,5 @@ +"use strict"; + var PlaneSvg = "M 0,0 " + "M 1.9565564,41.694305 C 1.7174505,40.497708 1.6419973,38.448747 " + "1.8096508,37.70494 1.8936398,37.332056 2.0796653,36.88191 2.222907,36.70461 " + diff --git a/public_html/script.js b/public_html/script.js index 7399c6db0..12a3cf35e 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -1,3 +1,5 @@ +"use strict"; + // Define our global variables var GoogleMap = null; var Planes = {}; @@ -21,7 +23,10 @@ var RefreshInterval = 1000; var PlaneRowTemplate = null; var TrackedAircraft = 0; -var TrackedPositions = 0; +var TrackedAircraftPositions = 0; +var TrackedHistorySize = 0; + +var SitePosition = null; function fetchData() { $.getJSON('data/aircraft.json', function(data) { @@ -545,9 +550,9 @@ function sortByTrack() { sortBy('track', function(x,y){return compareNumeri function sortByMsgs() { sortBy('msgs', function(x,y){return compareNumeric(x.msgs, x.sort_pos, y.msgs, y.sort_pos)}); } function sortBySeen() { sortBy('seen', function(x,y){return compareNumeric(x.seen, x.sort_pos, y.seen, y.sort_pos)}); } -sortId = ''; -sortByFunc = null; -sortAscending = true; +var sortId = ''; +var sortByFunc = null; +var sortAscending = true; function resortTable() { // make it a stable sort From e6a70345b5cbbf33478adbecf0d44defdc84a6ef Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 7 Jan 2015 17:37:34 +0000 Subject: [PATCH 107/610] Remove options / extension support (no plans to work on those in the current form). --- public_html/extension.js | 19 ------------------- public_html/gmap.html | 6 +----- public_html/options.js | 17 ----------------- public_html/script.js | 6 ------ 4 files changed, 1 insertion(+), 47 deletions(-) delete mode 100644 public_html/extension.js delete mode 100644 public_html/options.js diff --git a/public_html/extension.js b/public_html/extension.js deleted file mode 100644 index f71fe170c..000000000 --- a/public_html/extension.js +++ /dev/null @@ -1,19 +0,0 @@ -// ----------------------------------------------------- -// -// This file is so users can modify how the page acts -// without diving to deep in the code. This way we can -// also try out or hold custom code for ourselves and -// not check it into the repo. -// -// There is a div id'ed as plane_extension for use with -// this javascript file. -// ----------------------------------------------------- - -function extendedInitalize() { - // Write your initalization here - // Gets called just before the 1-sec function call loop is setup -} - -function extendedPulse() { - // This will get called every second after all the main functions -} diff --git a/public_html/gmap.html b/public_html/gmap.html index 20dc878b5..65344abec 100644 --- a/public_html/gmap.html +++ b/public_html/gmap.html @@ -8,8 +8,6 @@ - - @@ -44,7 +42,7 @@   - [ Settings ] +   @@ -125,7 +123,6 @@ -
@@ -154,7 +151,6 @@
-
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ICAOFlightSquawkAltitudeSpeedDistanceTrackMsgsAge
-
- - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ICAOFlightSquawkAltitudeSpeedDistanceTrackMsgsAge
+ + + + + + Problem fetching data from dump1090.
+
+ The displayed map data will be out of date. +
diff --git a/public_html/script.js b/public_html/script.js index 87d46ef98..ff125bb88 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -9,9 +9,9 @@ var SelectedPlane = null; var FollowSelected = false; var SpecialSquawks = { - '7500' : { cssClass: 'squawk7500', markerColor: 'rgb(255, 85, 85)', text: 'Aircraft Hijacking' }, - '7600' : { cssClass: 'squawk7600', markerColor: 'rgb(0, 255, 255)', text: 'Radio Failure' }, - '7700' : { cssClass: 'squawk7700', markerColor: 'rgb(255, 255, 0)', text: 'General Emergency' } + '7500' : { cssClass: 'squawk7500', markerColor: 'rgb(255, 85, 85)', text: 'Aircraft Hijacking' }, + '7600' : { cssClass: 'squawk7600', markerColor: 'rgb(0, 255, 255)', text: 'Radio Failure' }, + '7700' : { cssClass: 'squawk7700', markerColor: 'rgb(255, 255, 0)', text: 'General Emergency' } }; // Get current map settings @@ -40,716 +40,715 @@ var MessageRate = 0; var NBSP='\u00a0'; function processReceiverUpdate(data) { - // Loop through all the planes in the data packet - var now = data.now; - var acs = data.aircraft; - - // Detect stats reset - if (MessageCountHistory.length > 0 && MessageCountHistory[MessageCountHistory.length-1].messages > data.messages) { - MessageCountHistory = [{'time' : MessageCountHistory[MessageCountHistory.length-1].time, - 'messages' : 0}]; - } - - // Note the message count in the history - MessageCountHistory.push({ 'time' : now, 'messages' : data.messages}); - // .. and clean up any old values - if ((now - MessageCountHistory[0].time) > 30) - MessageCountHistory.shift(); - - for (var j=0; j < acs.length; j++) { - var ac = acs[j]; - var hex = ac.hex; - var plane = null; - - // Do we already have this plane object in Planes? - // If not make it. - - if (Planes[hex]) { - plane = Planes[hex]; - } else { - plane = new PlaneObject(hex); - plane.tr = PlaneRowTemplate.cloneNode(true); - - if (hex[0] === '~') { - // Non-ICAO address - plane.tr.cells[0].textContent = hex.substring(1); - $(plane.tr).css('font-style', 'italic'); - } else { - plane.tr.cells[0].textContent = hex; - } - - // set flag image if available - if (ShowFlags && plane.icaorange.flag_image !== null) { - $('img', plane.tr.cells[1]).attr('src', FlagPath + plane.icaorange.flag_image); - $('img', plane.tr.cells[1]).attr('title', plane.icaorange.country); - } else { - $('img', plane.tr.cells[1]).css('display', 'none'); - } - - plane.tr.addEventListener('click', selectPlaneByHex.bind(undefined,hex,false)); - plane.tr.addEventListener('dblclick', selectPlaneByHex.bind(undefined,hex,true)); - - Planes[hex] = plane; - PlanesOrdered.push(plane); - } - - // Call the function update - plane.updateData(now, ac); - } + // Loop through all the planes in the data packet + var now = data.now; + var acs = data.aircraft; + + // Detect stats reset + if (MessageCountHistory.length > 0 && MessageCountHistory[MessageCountHistory.length-1].messages > data.messages) { + MessageCountHistory = [{'time' : MessageCountHistory[MessageCountHistory.length-1].time, + 'messages' : 0}]; + } + + // Note the message count in the history + MessageCountHistory.push({ 'time' : now, 'messages' : data.messages}); + // .. and clean up any old values + if ((now - MessageCountHistory[0].time) > 30) + MessageCountHistory.shift(); + + for (var j=0; j < acs.length; j++) { + var ac = acs[j]; + var hex = ac.hex; + var plane = null; + + // Do we already have this plane object in Planes? + // If not make it. + + if (Planes[hex]) { + plane = Planes[hex]; + } else { + plane = new PlaneObject(hex); + plane.tr = PlaneRowTemplate.cloneNode(true); + + if (hex[0] === '~') { + // Non-ICAO address + plane.tr.cells[0].textContent = hex.substring(1); + $(plane.tr).css('font-style', 'italic'); + } else { + plane.tr.cells[0].textContent = hex; + } + + // set flag image if available + if (ShowFlags && plane.icaorange.flag_image !== null) { + $('img', plane.tr.cells[1]).attr('src', FlagPath + plane.icaorange.flag_image); + $('img', plane.tr.cells[1]).attr('title', plane.icaorange.country); + } else { + $('img', plane.tr.cells[1]).css('display', 'none'); + } + + plane.tr.addEventListener('click', selectPlaneByHex.bind(undefined,hex,false)); + plane.tr.addEventListener('dblclick', selectPlaneByHex.bind(undefined,hex,true)); + + Planes[hex] = plane; + PlanesOrdered.push(plane); + } + + // Call the function update + plane.updateData(now, ac); + } } function fetchData() { - if (FetchPending !== null && FetchPending.state() == 'pending') { - // don't double up on fetches, let the last one resolve - return; - } - - FetchPending = $.ajax({ url: 'data/aircraft.json', - timeout: 5000, - cache: false, - dataType: 'json' }); - FetchPending.done(function(data) { - var now = data.now; - - processReceiverUpdate(data); - - // update timestamps, visibility, history track for all planes - not only those updated - for (var i = 0; i < PlanesOrdered.length; ++i) { - var plane = PlanesOrdered[i]; - plane.updateTick(now, LastReceiverTimestamp); - } - - refreshTableInfo(); - refreshSelected(); - - if (ReceiverClock) { - var rcv = new Date(now * 1000); - ReceiverClock.render(rcv.getUTCHours(),rcv.getUTCMinutes(),rcv.getUTCSeconds()); - } - - // Check for stale receiver data - if (LastReceiverTimestamp === now) { - StaleReceiverCount++; - if (StaleReceiverCount > 5) { - $("#update_error_detail").text("The data from dump1090 hasn't been updated in a while. Maybe dump1090 is no longer running?"); - $("#update_error").css('display','block'); - } - } else { - StaleReceiverCount = 0; - LastReceiverTimestamp = now; - $("#update_error").css('display','none'); - } - }); - - FetchPending.fail(function(jqxhr, status, error) { - $("#update_error_detail").text("AJAX call failed (" + status + (error ? (": " + error) : "") + "). Maybe dump1090 is no longer running?"); - $("#update_error").css('display','block'); - }); + if (FetchPending !== null && FetchPending.state() == 'pending') { + // don't double up on fetches, let the last one resolve + return; + } + + FetchPending = $.ajax({ url: 'data/aircraft.json', + timeout: 5000, + cache: false, + dataType: 'json' }); + FetchPending.done(function(data) { + var now = data.now; + + processReceiverUpdate(data); + + // update timestamps, visibility, history track for all planes - not only those updated + for (var i = 0; i < PlanesOrdered.length; ++i) { + var plane = PlanesOrdered[i]; + plane.updateTick(now, LastReceiverTimestamp); + } + + refreshTableInfo(); + refreshSelected(); + + if (ReceiverClock) { + var rcv = new Date(now * 1000); + ReceiverClock.render(rcv.getUTCHours(),rcv.getUTCMinutes(),rcv.getUTCSeconds()); + } + + // Check for stale receiver data + if (LastReceiverTimestamp === now) { + StaleReceiverCount++; + if (StaleReceiverCount > 5) { + $("#update_error_detail").text("The data from dump1090 hasn't been updated in a while. Maybe dump1090 is no longer running?"); + $("#update_error").css('display','block'); + } + } else { + StaleReceiverCount = 0; + LastReceiverTimestamp = now; + $("#update_error").css('display','none'); + } + }); + + FetchPending.fail(function(jqxhr, status, error) { + $("#update_error_detail").text("AJAX call failed (" + status + (error ? (": " + error) : "") + "). Maybe dump1090 is no longer running?"); + $("#update_error").css('display','block'); + }); } var PositionHistorySize = 0; function initialize() { - // Set page basics - document.title = PageName; - $("#infoblock_name").text(PageName); - - PlaneRowTemplate = document.getElementById("plane_row_template"); - - if (!ShowClocks) { - $('#timestamps').css('display','none'); - } else { - // Create the clocks. - new CoolClock({ - canvasId: "utcclock", - skinId: "classic", - displayRadius: 40, - showSecondHand: true, - gmtOffset: "0", // this has to be a string! - showDigital: false, - logClock: false, - logClockRev: false - }); - - ReceiverClock = new CoolClock({ - canvasId: "receiverclock", - skinId: "classic", - displayRadius: 40, - showSecondHand: true, - gmtOffset: null, - showDigital: false, - logClock: false, - logClockRev: false - }); - - // disable ticking on the receiver clock, we will update it ourselves - ReceiverClock.tick = (function(){}) - } - - $("#loader").removeClass("hidden"); - - // Get receiver metadata, reconfigure using it, then continue - // with initialization - $.ajax({ url: 'data/receiver.json', - timeout: 5000, - cache: false, - dataType: 'json' }) - - .done(function(data) { - if (typeof data.lat !== "undefined") { - SiteShow = true; - SiteLat = data.lat; - SiteLon = data.lon; - DefaultCenterLat = data.lat; - DefaultCenterLon = data.lon; - } - - Dump1090Version = data.version; - RefreshInterval = data.refresh; - PositionHistorySize = data.history; - }) - - .always(function() { - initialize_map(); - start_load_history(); - }); + // Set page basics + document.title = PageName; + $("#infoblock_name").text(PageName); + + PlaneRowTemplate = document.getElementById("plane_row_template"); + + if (!ShowClocks) { + $('#timestamps').css('display','none'); + } else { + // Create the clocks. + new CoolClock({ + canvasId: "utcclock", + skinId: "classic", + displayRadius: 40, + showSecondHand: true, + gmtOffset: "0", // this has to be a string! + showDigital: false, + logClock: false, + logClockRev: false + }); + + ReceiverClock = new CoolClock({ + canvasId: "receiverclock", + skinId: "classic", + displayRadius: 40, + showSecondHand: true, + gmtOffset: null, + showDigital: false, + logClock: false, + logClockRev: false + }); + + // disable ticking on the receiver clock, we will update it ourselves + ReceiverClock.tick = (function(){}) + } + + $("#loader").removeClass("hidden"); + + // Get receiver metadata, reconfigure using it, then continue + // with initialization + $.ajax({ url: 'data/receiver.json', + timeout: 5000, + cache: false, + dataType: 'json' }) + + .done(function(data) { + if (typeof data.lat !== "undefined") { + SiteShow = true; + SiteLat = data.lat; + SiteLon = data.lon; + DefaultCenterLat = data.lat; + DefaultCenterLon = data.lon; + } + + Dump1090Version = data.version; + RefreshInterval = data.refresh; + PositionHistorySize = data.history; + }) + + .always(function() { + initialize_map(); + start_load_history(); + }); } var CurrentHistoryFetch = null; var PositionHistoryBuffer = [] function start_load_history() { - if (PositionHistorySize > 0) { - $("#loader_progress").attr('max',PositionHistorySize); - console.log("Starting to load history (" + PositionHistorySize + " items)"); - load_history_item(0); - } else { - end_load_history(); - } + if (PositionHistorySize > 0) { + $("#loader_progress").attr('max',PositionHistorySize); + console.log("Starting to load history (" + PositionHistorySize + " items)"); + load_history_item(0); + } else { + end_load_history(); + } } function load_history_item(i) { - if (i >= PositionHistorySize) { - end_load_history(); - return; - } - - console.log("Loading history #" + i); - $("#loader_progress").attr('value',i); - - $.ajax({ url: 'data/history_' + i + '.json', - timeout: 5000, - cache: false, - dataType: 'json' }) - - .done(function(data) { - PositionHistoryBuffer.push(data); - load_history_item(i+1); - }) - - .fail(function(jqxhr, status, error) { - // No more history - end_load_history(); - }); + if (i >= PositionHistorySize) { + end_load_history(); + return; + } + + console.log("Loading history #" + i); + $("#loader_progress").attr('value',i); + + $.ajax({ url: 'data/history_' + i + '.json', + timeout: 5000, + cache: false, + dataType: 'json' }) + + .done(function(data) { + PositionHistoryBuffer.push(data); + load_history_item(i+1); + }) + + .fail(function(jqxhr, status, error) { + // No more history + end_load_history(); + }); } function end_load_history() { - $("#loader").addClass("hidden"); + $("#loader").addClass("hidden"); - console.log("Done loading history"); + console.log("Done loading history"); - if (PositionHistoryBuffer.length > 0) { - var now, last=0; + if (PositionHistoryBuffer.length > 0) { + var now, last=0; - // Sort history by timestamp - console.log("Sorting history"); - PositionHistoryBuffer.sort(function(x,y) { return (x.now - y.now); }); + // Sort history by timestamp + console.log("Sorting history"); + PositionHistoryBuffer.sort(function(x,y) { return (x.now - y.now); }); - // Process history - for (var h = 0; h < PositionHistoryBuffer.length; ++h) { - now = PositionHistoryBuffer[h].now; - console.log("Applying history " + h + "/" + PositionHistoryBuffer.length + " at: " + now); - processReceiverUpdate(PositionHistoryBuffer[h]); + // Process history + for (var h = 0; h < PositionHistoryBuffer.length; ++h) { + now = PositionHistoryBuffer[h].now; + console.log("Applying history " + h + "/" + PositionHistoryBuffer.length + " at: " + now); + processReceiverUpdate(PositionHistoryBuffer[h]); - // update track - console.log("Updating tracks at: " + now); - for (var i = 0; i < PlanesOrdered.length; ++i) { - var plane = PlanesOrdered[i]; - plane.updateTrack((now - last) + 1); - } + // update track + console.log("Updating tracks at: " + now); + for (var i = 0; i < PlanesOrdered.length; ++i) { + var plane = PlanesOrdered[i]; + plane.updateTrack((now - last) + 1); + } - last = now; - } + last = now; + } - // Final pass to update all planes to their latest state - console.log("Final history cleanup pass"); - for (var i = 0; i < PlanesOrdered.length; ++i) { - var plane = PlanesOrdered[i]; - plane.updateTick(now); - } + // Final pass to update all planes to their latest state + console.log("Final history cleanup pass"); + for (var i = 0; i < PlanesOrdered.length; ++i) { + var plane = PlanesOrdered[i]; + plane.updateTick(now); + } - LastReceiverTimestamp = last; - } + LastReceiverTimestamp = last; + } - PositionHistoryBuffer = null; + PositionHistoryBuffer = null; - console.log("Completing init"); + console.log("Completing init"); - refreshTableInfo(); - refreshSelected(); - reaper(); + refreshTableInfo(); + refreshSelected(); + reaper(); - // Setup our timer to poll from the server. - window.setInterval(fetchData, RefreshInterval); - window.setInterval(reaper, 60000); + // Setup our timer to poll from the server. + window.setInterval(fetchData, RefreshInterval); + window.setInterval(reaper, 60000); - // And kick off one refresh immediately. - fetchData(); + // And kick off one refresh immediately. + fetchData(); } function generic_gettile(template, coord, zoom) { - return template.replace('{x}', coord.x).replace('{y}', coord.y).replace('{z}', zoom) + return template.replace('{x}', coord.x).replace('{y}', coord.y).replace('{z}', zoom) } // Initalizes the map and starts up our timers to call various functions function initialize_map() { - // Load stored map settings if present - CenterLat = Number(localStorage['CenterLat']) || DefaultCenterLat; - CenterLon = Number(localStorage['CenterLon']) || DefaultCenterLon; - ZoomLvl = Number(localStorage['ZoomLvl']) || DefaultZoomLvl; - MapType = localStorage['MapType'] || google.maps.MapTypeId.ROADMAP; - - // Set SitePosition, initialize sorting - if (SiteShow && (typeof SiteLat !== 'undefined') && (typeof SiteLon !== 'undefined')) { - SitePosition = new google.maps.LatLng(SiteLat, SiteLon); - sortByDistance(); - } else { - SitePosition = null; - PlaneRowTemplate.cells[6].style.display = 'none'; // hide distance column - document.getElementById("distance").style.display = 'none'; // hide distance header - sortByAltitude(); - } - - // Maybe hide flag info - if (!ShowFlags) { - PlaneRowTemplate.cells[1].style.display = 'none'; // hide flag column - document.getElementById("flag").style.display = 'none'; // hide flag header - document.getElementById("infoblock_country").style.display = 'none'; // hide country row - } - - // Make a list of all the available map IDs - var mapTypeIds = []; - for(var type in google.maps.MapTypeId) { - mapTypeIds.push(google.maps.MapTypeId[type]); - } - - mapTypeIds.push("dark_map"); - - for (var type in ExtraMapTypes) { - mapTypeIds.push(type); - } - - // Styled Map to outline airports and highways - var styles = [ - { - "featureType": "administrative", - "stylers": [ - { "visibility": "off" } - ] - },{ - "featureType": "landscape", - "stylers": [ - { "visibility": "off" } - ] - },{ - "featureType": "poi", - "stylers": [ - { "visibility": "off" } - ] - },{ - "featureType": "road", - "stylers": [ - { "visibility": "off" } - ] - },{ - "featureType": "transit", - "stylers": [ - { "visibility": "off" } - ] - },{ - "featureType": "landscape", - "stylers": [ - { "visibility": "on" }, - { "weight": 8 }, - { "color": "#000000" } - ] - },{ - "featureType": "water", - "stylers": [ - { "lightness": -74 } - ] - },{ - "featureType": "transit.station.airport", - "stylers": [ - { "visibility": "on" }, - { "weight": 8 }, - { "invert_lightness": true }, - { "lightness": 27 } - ] - },{ - "featureType": "road.highway", - "stylers": [ - { "visibility": "simplified" }, - { "invert_lightness": true }, - { "gamma": 0.3 } - ] - },{ - "featureType": "road", - "elementType": "labels", - "stylers": [ - { "visibility": "off" } - ] - } - ] - - // Add our styled map - var styledMap = new google.maps.StyledMapType(styles, {name: "Dark Map"}); - - // Define the Google Map - var mapOptions = { - center: new google.maps.LatLng(CenterLat, CenterLon), - zoom: ZoomLvl, - mapTypeId: MapType, - mapTypeControl: true, - streetViewControl: false, - zoomControl: true, - scaleControl: true, - mapTypeControlOptions: { - mapTypeIds: mapTypeIds, - position: google.maps.ControlPosition.TOP_LEFT, - style: google.maps.MapTypeControlStyle.DROPDOWN_MENU - } - }; - - GoogleMap = new google.maps.Map(document.getElementById("map_canvas"), mapOptions); - GoogleMap.mapTypes.set("dark_map", styledMap); - - // Define the extra map types - for (var type in ExtraMapTypes) { - GoogleMap.mapTypes.set(type, new google.maps.ImageMapType({ - getTileUrl: generic_gettile.bind(null, ExtraMapTypes[type]), - tileSize: new google.maps.Size(256, 256), - name: type, - maxZoom: 18 - })); - } - - // Listeners for newly created Map - google.maps.event.addListener(GoogleMap, 'center_changed', function() { - localStorage['CenterLat'] = GoogleMap.getCenter().lat(); - localStorage['CenterLon'] = GoogleMap.getCenter().lng(); - if (FollowSelected) { - // On manual navigation, disable follow - var selected = Planes[SelectedPlane]; - if (Math.abs(GoogleMap.getCenter().lat() - selected.position.lat()) > 0.0001 && - Math.abs(GoogleMap.getCenter().lng() - selected.position.lng()) > 0.0001) { - FollowSelected = false; - refreshSelected(); - } - } - }); - - google.maps.event.addListener(GoogleMap, 'zoom_changed', function() { - localStorage['ZoomLvl'] = GoogleMap.getZoom(); - }); - - google.maps.event.addListener(GoogleMap, 'maptypeid_changed', function() { - localStorage['MapType'] = GoogleMap.getMapTypeId(); - }); - - // Add home marker if requested - if (SitePosition) { - var markerImage = new google.maps.MarkerImage( - 'http://maps.google.com/mapfiles/kml/pal4/icon57.png', - new google.maps.Size(32, 32), // Image size - new google.maps.Point(0, 0), // Origin point of image - new google.maps.Point(16, 16)); // Position where marker should point - var marker = new google.maps.Marker({ - position: SitePosition, - map: GoogleMap, - icon: markerImage, - title: SiteName, - zIndex: -99999 - }); - - if (SiteCircles) { - for (var i=0;i 0.0001 && Math.abs(GoogleMap.getCenter().lng() - selected.position.lng()) > 0.0001) { + FollowSelected = false; + refreshSelected(); + } + } + }); + + google.maps.event.addListener(GoogleMap, 'zoom_changed', function() { + localStorage['ZoomLvl'] = GoogleMap.getZoom(); + }); + + google.maps.event.addListener(GoogleMap, 'maptypeid_changed', function() { + localStorage['MapType'] = GoogleMap.getMapTypeId(); + }); + + // Add home marker if requested + if (SitePosition) { + var markerImage = new google.maps.MarkerImage( + 'http://maps.google.com/mapfiles/kml/pal4/icon57.png', + new google.maps.Size(32, 32), // Image size + new google.maps.Point(0, 0), // Origin point of image + new google.maps.Point(16, 16)); // Position where marker should point + var marker = new google.maps.Marker({ + position: SitePosition, + map: GoogleMap, + icon: markerImage, + title: SiteName, + zIndex: -99999 + }); + + if (SiteCircles) { + for (var i=0;i300 seconds - var newPlanes = []; - for (var i = 0; i < PlanesOrdered.length; ++i) { - var plane = PlanesOrdered[i]; - if (plane.seen > 300) { - // Reap it. - //console.log("Reaping " + plane.icao); - //console.log("parent " + plane.tr.parentNode); - plane.tr.parentNode.removeChild(plane.tr); - plane.tr = null; - delete Planes[plane.icao]; - plane.destroy(); - } else { - // Keep it. - newPlanes.push(plane); - } - }; - - PlanesOrdered = newPlanes; - refreshTableInfo(); - refreshSelected(); + //console.log("Reaping started.."); + + // Look for planes where we have seen no messages for >300 seconds + var newPlanes = []; + for (var i = 0; i < PlanesOrdered.length; ++i) { + var plane = PlanesOrdered[i]; + if (plane.seen > 300) { + // Reap it. + //console.log("Reaping " + plane.icao); + //console.log("parent " + plane.tr.parentNode); + plane.tr.parentNode.removeChild(plane.tr); + plane.tr = null; + delete Planes[plane.icao]; + plane.destroy(); + } else { + // Keep it. + newPlanes.push(plane); + } + }; + + PlanesOrdered = newPlanes; + refreshTableInfo(); + refreshSelected(); } // Page Title update function function refreshPageTitle() { - if (!PlaneCountInTitle && !MessageRateInTitle) - return; + if (!PlaneCountInTitle && !MessageRateInTitle) + return; - var subtitle = ""; + var subtitle = ""; - if (PlaneCountInTitle) { - subtitle += TrackedAircraftPositions + '/' + TrackedAircraft; - } + if (PlaneCountInTitle) { + subtitle += TrackedAircraftPositions + '/' + TrackedAircraft; + } - if (MessageRateInTitle) { - if (subtitle) subtitle += ' | '; - subtitle += MessageRate.toFixed(1) + '/s'; - } + if (MessageRateInTitle) { + if (subtitle) subtitle += ' | '; + subtitle += MessageRate.toFixed(1) + '/s'; + } - document.title = PageName + ' - ' + subtitle; + document.title = PageName + ' - ' + subtitle; } // Refresh the detail window about the plane function refreshSelected() { - if (MessageCountHistory.length > 1) { - var message_time_delta = MessageCountHistory[MessageCountHistory.length-1].time - MessageCountHistory[0].time; - var message_count_delta = MessageCountHistory[MessageCountHistory.length-1].messages - MessageCountHistory[0].messages; - if (message_time_delta > 0) - MessageRate = message_count_delta / message_time_delta; - } else { - MessageRate = null; - } - - refreshPageTitle(); - - var selected = false; - if (typeof SelectedPlane !== 'undefined' && SelectedPlane != "ICAO" && SelectedPlane != null) { - selected = Planes[SelectedPlane]; - } - - if (!selected) { - $('#selected_infoblock').css('display','none'); - $('#dump1090_infoblock').css('display','block'); - $('#dump1090_version').text(Dump1090Version); - $('#dump1090_total_ac').text(TrackedAircraft); - $('#dump1090_total_ac_positions').text(TrackedAircraftPositions); - $('#dump1090_total_history').text(TrackedHistorySize); - - if (MessageRate !== null) { - $('#dump1090_message_rate').text(MessageRate.toFixed(1)); - } else { - $('#dump1090_message_rate').text("n/a"); - } - - return; - } - - $('#dump1090_infoblock').css('display','none'); - $('#selected_infoblock').css('display','block'); + if (MessageCountHistory.length > 1) { + var message_time_delta = MessageCountHistory[MessageCountHistory.length-1].time - MessageCountHistory[0].time; + var message_count_delta = MessageCountHistory[MessageCountHistory.length-1].messages - MessageCountHistory[0].messages; + if (message_time_delta > 0) + MessageRate = message_count_delta / message_time_delta; + } else { + MessageRate = null; + } + + refreshPageTitle(); + + var selected = false; + if (typeof SelectedPlane !== 'undefined' && SelectedPlane != "ICAO" && SelectedPlane != null) { + selected = Planes[SelectedPlane]; + } + + if (!selected) { + $('#selected_infoblock').css('display','none'); + $('#dump1090_infoblock').css('display','block'); + $('#dump1090_version').text(Dump1090Version); + $('#dump1090_total_ac').text(TrackedAircraft); + $('#dump1090_total_ac_positions').text(TrackedAircraftPositions); + $('#dump1090_total_history').text(TrackedHistorySize); + + if (MessageRate !== null) { + $('#dump1090_message_rate').text(MessageRate.toFixed(1)); + } else { + $('#dump1090_message_rate').text("n/a"); + } - $('#selected_flightaware_link').attr('href','http://flightaware.com/live/modes/'+selected.icao+'/redirect'); + return; + } - if (selected.flight !== null && selected.flight !== "") { - $('#selected_callsign').text(selected.flight); - $('#selected_links').css('display','inline'); - $('#selected_fr24_link').attr('href','http://fr24.com/'+selected.flight); - $('#selected_flightstats_link').attr('href','http://www.flightstats.com/go/FlightStatus/flightStatusByFlight.do?flightNumber='+selected.flight); + $('#dump1090_infoblock').css('display','none'); + $('#selected_infoblock').css('display','block'); + + $('#selected_flightaware_link').attr('href','http://flightaware.com/live/modes/'+selected.icao+'/redirect'); + + if (selected.flight !== null && selected.flight !== "") { + $('#selected_callsign').text(selected.flight); + $('#selected_links').css('display','inline'); + $('#selected_fr24_link').attr('href','http://fr24.com/'+selected.flight); + $('#selected_flightstats_link').attr('href','http://www.flightstats.com/go/FlightStatus/flightStatusByFlight.do?flightNumber='+selected.flight); $('#selected_planefinder_link').attr('href','https://planefinder.net/flight/'+selected.flight); - } else { - $('#selected_callsign').text('n/a'); - $('#selected_links').css('display','none'); - } - - if (selected.registration !== null) { - $('#selected_registration').text(selected.registration); - } else { - $('#selected_registration').text(""); - } - - if (selected.icaotype !== null) { - $('#selected_icaotype').text(selected.icaotype); - } else { - $('#selected_icaotype').text(""); - } - - var emerg = document.getElementById('selected_emergency'); - if (selected.squawk in SpecialSquawks) { - emerg.className = SpecialSquawks[selected.squawk].cssClass; - emerg.textContent = NBSP + 'Squawking: ' + SpecialSquawks[selected.squawk].text + NBSP ; - } else { - emerg.className = 'hidden'; - } - - $("#selected_altitude").text(format_altitude_long(selected.altitude, selected.vert_rate)); - - if (selected.squawk === null || selected.squawk === '0000') { - $('#selected_squawk').text('n/a'); - } else { - $('#selected_squawk').text(selected.squawk); - } - - $('#selected_speed').text(format_speed_long(selected.speed)); - $('#selected_icao').text(selected.icao.toUpperCase()); - $('#airframes_post_icao').attr('value',selected.icao); - $('#selected_track').text(format_track_long(selected.track)); - - if (selected.seen <= 1) { - $('#selected_seen').text('now'); - } else { - $('#selected_seen').text(selected.seen.toFixed(1) + 's'); - } - - $('#selected_country').text(selected.icaorange.country); - if (ShowFlags && selected.icaorange.flag_image !== null) { - $('#selected_flag').removeClass('hidden'); - $('#selected_flag img').attr('src', FlagPath + selected.icaorange.flag_image); - $('#selected_flag img').attr('title', selected.icaorange.country); - } else { - $('#selected_flag').addClass('hidden'); - } - - if (selected.position === null) { - $('#selected_position').text('n/a'); - $('#selected_follow').addClass('hidden'); - } else { - var mlat_bit = (selected.position_from_mlat ? "MLAT: " : ""); - if (selected.seen_pos > 1) { - $('#selected_position').text(mlat_bit + format_latlng(selected.position) + " (" + selected.seen_pos.toFixed(1) + "s)"); - } else { - $('#selected_position').text(mlat_bit + format_latlng(selected.position)); - } - $('#selected_follow').removeClass('hidden'); - if (FollowSelected) { - $('#selected_follow').css('font-weight', 'bold'); - GoogleMap.panTo(selected.position); - } else { - $('#selected_follow').css('font-weight', 'normal'); - } - } - - $('#selected_sitedist').text(format_distance_long(selected.sitedist)); - $('#selected_rssi').text(selected.rssi.toFixed(1) + ' dBFS'); + } else { + $('#selected_callsign').text('n/a'); + $('#selected_links').css('display','none'); + } + + if (selected.registration !== null) { + $('#selected_registration').text(selected.registration); + } else { + $('#selected_registration').text(""); + } + + if (selected.icaotype !== null) { + $('#selected_icaotype').text(selected.icaotype); + } else { + $('#selected_icaotype').text(""); + } + + var emerg = document.getElementById('selected_emergency'); + if (selected.squawk in SpecialSquawks) { + emerg.className = SpecialSquawks[selected.squawk].cssClass; + emerg.textContent = NBSP + 'Squawking: ' + SpecialSquawks[selected.squawk].text + NBSP ; + } else { + emerg.className = 'hidden'; + } + + $("#selected_altitude").text(format_altitude_long(selected.altitude, selected.vert_rate)); + + if (selected.squawk === null || selected.squawk === '0000') { + $('#selected_squawk').text('n/a'); + } else { + $('#selected_squawk').text(selected.squawk); + } + + $('#selected_speed').text(format_speed_long(selected.speed)); + $('#selected_icao').text(selected.icao.toUpperCase()); + $('#airframes_post_icao').attr('value',selected.icao); + $('#selected_track').text(format_track_long(selected.track)); + + if (selected.seen <= 1) { + $('#selected_seen').text('now'); + } else { + $('#selected_seen').text(selected.seen.toFixed(1) + 's'); + } + + $('#selected_country').text(selected.icaorange.country); + if (ShowFlags && selected.icaorange.flag_image !== null) { + $('#selected_flag').removeClass('hidden'); + $('#selected_flag img').attr('src', FlagPath + selected.icaorange.flag_image); + $('#selected_flag img').attr('title', selected.icaorange.country); + } else { + $('#selected_flag').addClass('hidden'); + } + + if (selected.position === null) { + $('#selected_position').text('n/a'); + $('#selected_follow').addClass('hidden'); + } else { + var mlat_bit = (selected.position_from_mlat ? "MLAT: " : ""); + if (selected.seen_pos > 1) { + $('#selected_position').text(mlat_bit + format_latlng(selected.position) + " (" + selected.seen_pos.toFixed(1) + "s)"); + } else { + $('#selected_position').text(mlat_bit + format_latlng(selected.position)); + } + $('#selected_follow').removeClass('hidden'); + if (FollowSelected) { + $('#selected_follow').css('font-weight', 'bold'); + GoogleMap.panTo(selected.position); + } else { + $('#selected_follow').css('font-weight', 'normal'); + } + } + + $('#selected_sitedist').text(format_distance_long(selected.sitedist)); + $('#selected_rssi').text(selected.rssi.toFixed(1) + ' dBFS'); } // Refreshes the larger table of all the planes function refreshTableInfo() { - var show_squawk_warning = false; - - TrackedAircraft = 0 - TrackedAircraftPositions = 0 - TrackedHistorySize = 0 - - for (var i = 0; i < PlanesOrdered.length; ++i) { - var tableplane = PlanesOrdered[i]; - TrackedHistorySize += tableplane.history_size; - if (!tableplane.visible) { - tableplane.tr.className = "plane_table_row hidden"; - } else { - TrackedAircraft++; - var classes = "plane_table_row"; - - if (tableplane.position !== null && tableplane.seen_pos < 60) { - ++TrackedAircraftPositions; - if (tableplane.position_from_mlat) - classes += " mlat"; - else - classes += " vPosition"; - } - if (tableplane.icao == SelectedPlane) - classes += " selected"; - - if (tableplane.squawk in SpecialSquawks) { - classes = classes + " " + SpecialSquawks[tableplane.squawk].cssClass; - show_squawk_warning = true; - } - - // ICAO doesn't change - tableplane.tr.cells[2].textContent = (tableplane.flight !== null ? tableplane.flight : ""); - tableplane.tr.cells[3].textContent = (tableplane.squawk !== null ? tableplane.squawk : ""); - tableplane.tr.cells[4].textContent = format_altitude_brief(tableplane.altitude, tableplane.vert_rate); - tableplane.tr.cells[5].textContent = format_speed_brief(tableplane.speed); - tableplane.tr.cells[6].textContent = format_distance_brief(tableplane.sitedist); - tableplane.tr.cells[7].textContent = format_track_brief(tableplane.track); - tableplane.tr.cells[8].textContent = tableplane.messages; - tableplane.tr.cells[9].textContent = tableplane.seen.toFixed(0); - tableplane.tr.className = classes; - } - } - - if (show_squawk_warning) { - $("#SpecialSquawkWarning").css('display','block'); - } else { - $("#SpecialSquawkWarning").css('display','none'); - } - - resortTable(); + var show_squawk_warning = false; + + TrackedAircraft = 0 + TrackedAircraftPositions = 0 + TrackedHistorySize = 0 + + for (var i = 0; i < PlanesOrdered.length; ++i) { + var tableplane = PlanesOrdered[i]; + TrackedHistorySize += tableplane.history_size; + if (!tableplane.visible) { + tableplane.tr.className = "plane_table_row hidden"; + } else { + TrackedAircraft++; + var classes = "plane_table_row"; + + if (tableplane.position !== null && tableplane.seen_pos < 60) { + ++TrackedAircraftPositions; + if (tableplane.position_from_mlat) + classes += " mlat"; + else + classes += " vPosition"; + } + if (tableplane.icao == SelectedPlane) + classes += " selected"; + + if (tableplane.squawk in SpecialSquawks) { + classes = classes + " " + SpecialSquawks[tableplane.squawk].cssClass; + show_squawk_warning = true; + } + + // ICAO doesn't change + tableplane.tr.cells[2].textContent = (tableplane.flight !== null ? tableplane.flight : ""); + tableplane.tr.cells[3].textContent = (tableplane.squawk !== null ? tableplane.squawk : ""); + tableplane.tr.cells[4].textContent = format_altitude_brief(tableplane.altitude, tableplane.vert_rate); + tableplane.tr.cells[5].textContent = format_speed_brief(tableplane.speed); + tableplane.tr.cells[6].textContent = format_distance_brief(tableplane.sitedist); + tableplane.tr.cells[7].textContent = format_track_brief(tableplane.track); + tableplane.tr.cells[8].textContent = tableplane.messages; + tableplane.tr.cells[9].textContent = tableplane.seen.toFixed(0); + tableplane.tr.className = classes; + } + } + + if (show_squawk_warning) { + $("#SpecialSquawkWarning").css('display','block'); + } else { + $("#SpecialSquawkWarning").css('display','none'); + } + + resortTable(); } // @@ -757,18 +756,18 @@ function refreshTableInfo() { // function compareAlpha(xa,ya) { - if (xa === ya) - return 0; - if (xa < ya) - return -1; - return 1; + if (xa === ya) + return 0; + if (xa < ya) + return -1; + return 1; } function compareNumeric(xf,yf) { - if (Math.abs(xf - yf) < 1e-9) - return 0; + if (Math.abs(xf - yf) < 1e-9) + return 0; - return xf - yf; + return xf - yf; } function sortByICAO() { sortBy('icao', compareAlpha, function(x) { return x.icao; }); } @@ -788,133 +787,133 @@ var sortExtract = null; var sortAscending = true; function sortFunction(x,y) { - var xv = x._sort_value; - var yv = y._sort_value; + var xv = x._sort_value; + var yv = y._sort_value; - // always sort missing values at the end, regardless of - // ascending/descending sort - if (xv == null && yv == null) return x._sort_pos - y._sort_pos; - if (xv == null) return 1; - if (yv == null) return -1; + // always sort missing values at the end, regardless of + // ascending/descending sort + if (xv == null && yv == null) return x._sort_pos - y._sort_pos; + if (xv == null) return 1; + if (yv == null) return -1; - var c = sortAscending ? sortCompare(xv,yv) : sortCompare(yv,xv); - if (c !== 0) return c; + var c = sortAscending ? sortCompare(xv,yv) : sortCompare(yv,xv); + if (c !== 0) return c; - return x._sort_pos - y._sort_pos; + return x._sort_pos - y._sort_pos; } function resortTable() { - // number the existing rows so we can do a stable sort - // regardless of whether sort() is stable or not. - // Also extract the sort comparison value. - for (var i = 0; i < PlanesOrdered.length; ++i) { - PlanesOrdered[i]._sort_pos = i; - PlanesOrdered[i]._sort_value = sortExtract(PlanesOrdered[i]); - } - - PlanesOrdered.sort(sortFunction); - - var tbody = document.getElementById('tableinfo').tBodies[0]; - for (var i = 0; i < PlanesOrdered.length; ++i) { - tbody.appendChild(PlanesOrdered[i].tr); - } + // number the existing rows so we can do a stable sort + // regardless of whether sort() is stable or not. + // Also extract the sort comparison value. + for (var i = 0; i < PlanesOrdered.length; ++i) { + PlanesOrdered[i]._sort_pos = i; + PlanesOrdered[i]._sort_value = sortExtract(PlanesOrdered[i]); + } + + PlanesOrdered.sort(sortFunction); + + var tbody = document.getElementById('tableinfo').tBodies[0]; + for (var i = 0; i < PlanesOrdered.length; ++i) { + tbody.appendChild(PlanesOrdered[i].tr); + } } function sortBy(id,sc,se) { - if (id === sortId) { - sortAscending = !sortAscending; - PlanesOrdered.reverse(); // this correctly flips the order of rows that compare equal - } else { - sortAscending = true; - } - - sortId = id; - sortCompare = sc; - sortExtract = se; - - resortTable(); + if (id === sortId) { + sortAscending = !sortAscending; + PlanesOrdered.reverse(); // this correctly flips the order of rows that compare equal + } else { + sortAscending = true; + } + + sortId = id; + sortCompare = sc; + sortExtract = se; + + resortTable(); } function selectPlaneByHex(hex,autofollow) { - //console.log("select: " + hex); - // If SelectedPlane has something in it, clear out the selected - if (SelectedPlane != null) { - Planes[SelectedPlane].selected = false; - Planes[SelectedPlane].clearLines(); - Planes[SelectedPlane].updateMarker(); - $(Planes[SelectedPlane].tr).removeClass("selected"); - } - - // If we are clicking the same plane, we are deselected it. - if (SelectedPlane === hex) { - hex = null; - } - - if (hex !== null) { - // Assign the new selected - SelectedPlane = hex; - Planes[SelectedPlane].selected = true; - Planes[SelectedPlane].updateLines(); - Planes[SelectedPlane].updateMarker(); - $(Planes[SelectedPlane].tr).addClass("selected"); - } else { - SelectedPlane = null; - } - - if (SelectedPlane !== null && autofollow) { - FollowSelected = true; - if (GoogleMap.getZoom() < 8) - GoogleMap.setZoom(8); - } else { - FollowSelected = false; - } - - refreshSelected(); + //console.log("select: " + hex); + // If SelectedPlane has something in it, clear out the selected + if (SelectedPlane != null) { + Planes[SelectedPlane].selected = false; + Planes[SelectedPlane].clearLines(); + Planes[SelectedPlane].updateMarker(); + $(Planes[SelectedPlane].tr).removeClass("selected"); + } + + // If we are clicking the same plane, we are deselected it. + if (SelectedPlane === hex) { + hex = null; + } + + if (hex !== null) { + // Assign the new selected + SelectedPlane = hex; + Planes[SelectedPlane].selected = true; + Planes[SelectedPlane].updateLines(); + Planes[SelectedPlane].updateMarker(); + $(Planes[SelectedPlane].tr).addClass("selected"); + } else { + SelectedPlane = null; + } + + if (SelectedPlane !== null && autofollow) { + FollowSelected = true; + if (GoogleMap.getZoom() < 8) + GoogleMap.setZoom(8); + } else { + FollowSelected = false; + } + + refreshSelected(); } function toggleFollowSelected() { - FollowSelected = !FollowSelected; - if (FollowSelected && GoogleMap.getZoom() < 8) - GoogleMap.setZoom(8); - refreshSelected(); + FollowSelected = !FollowSelected; + if (FollowSelected && GoogleMap.getZoom() < 8) + GoogleMap.setZoom(8); + refreshSelected(); } function resetMap() { - // Reset localStorage values and map settings - localStorage['CenterLat'] = CenterLat = DefaultCenterLat; - localStorage['CenterLon'] = CenterLon = DefaultCenterLon; - localStorage['ZoomLvl'] = ZoomLvl = DefaultZoomLvl; - localStorage['MapType'] = MapType = google.maps.MapTypeId.ROADMAP; - - // Set and refresh - GoogleMap.setZoom(ZoomLvl); - GoogleMap.setCenter(new google.maps.LatLng(CenterLat, CenterLon)); - - selectPlaneByHex(null,false); + // Reset localStorage values and map settings + localStorage['CenterLat'] = CenterLat = DefaultCenterLat; + localStorage['CenterLon'] = CenterLon = DefaultCenterLon; + localStorage['ZoomLvl'] = ZoomLvl = DefaultZoomLvl; + localStorage['MapType'] = MapType = google.maps.MapTypeId.ROADMAP; + + // Set and refresh + GoogleMap.setZoom(ZoomLvl); + GoogleMap.setCenter(new google.maps.LatLng(CenterLat, CenterLon)); + + selectPlaneByHex(null,false); } function drawCircle(marker, distance) { - if (typeof distance === 'undefined') { - return false; - } - - distance = parseFloat(distance); - if (isNaN(distance) || !isFinite(distance) || distance < 0) { - return false; - } - - distance *= 1000.0; - if (!Metric) { - distance *= 1.852; - } - - // Add circle overlay and bind to marker - var circle = new google.maps.Circle({ - map: GoogleMap, - radius: distance, // In meters - fillOpacity: 0.0, - strokeWeight: 1, - strokeOpacity: 0.3 - }); - circle.bindTo('center', marker, 'position'); + if (typeof distance === 'undefined') { + return false; + } + + distance = parseFloat(distance); + if (isNaN(distance) || !isFinite(distance) || distance < 0) { + return false; + } + + distance *= 1000.0; + if (!Metric) { + distance *= 1.852; + } + + // Add circle overlay and bind to marker + var circle = new google.maps.Circle({ + map: GoogleMap, + radius: distance, // In meters + fillOpacity: 0.0, + strokeWeight: 1, + strokeOpacity: 0.3 + }); + circle.bindTo('center', marker, 'position'); } From e509e76294786387b01e39c09ccd216fb109c01d Mon Sep 17 00:00:00 2001 From: Chris Bowles Date: Tue, 22 Mar 2016 16:11:46 -0400 Subject: [PATCH 458/610] Avoid mixed-content warnings when served over HTTPS (in a protocol-relative way) --- public_html/gmap.html | 4 ++-- public_html/script.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public_html/gmap.html b/public_html/gmap.html index 49ff67abf..76be3e1f2 100644 --- a/public_html/gmap.html +++ b/public_html/gmap.html @@ -2,10 +2,10 @@ - + - + diff --git a/public_html/script.js b/public_html/script.js index ff125bb88..699c5d55d 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -468,7 +468,7 @@ function initialize_map() { // Add home marker if requested if (SitePosition) { var markerImage = new google.maps.MarkerImage( - 'http://maps.google.com/mapfiles/kml/pal4/icon57.png', + '//maps.google.com/mapfiles/kml/pal4/icon57.png', new google.maps.Size(32, 32), // Image size new google.maps.Point(0, 0), // Origin point of image new google.maps.Point(16, 16)); // Position where marker should point @@ -613,7 +613,7 @@ function refreshSelected() { $('#dump1090_infoblock').css('display','none'); $('#selected_infoblock').css('display','block'); - $('#selected_flightaware_link').attr('href','http://flightaware.com/live/modes/'+selected.icao+'/redirect'); + $('#selected_flightaware_link').attr('href','//flightaware.com/live/modes/'+selected.icao+'/redirect'); if (selected.flight !== null && selected.flight !== "") { $('#selected_callsign').text(selected.flight); From ebce0fdda65547e2f3817c06303866329f94f0c9 Mon Sep 17 00:00:00 2001 From: Chris Bowles Date: Tue, 22 Mar 2016 17:22:41 -0400 Subject: [PATCH 459/610] Pedantic tweaks for HTML5 validation --- public_html/gmap.html | 63 ++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/public_html/gmap.html b/public_html/gmap.html index 76be3e1f2..b3f6cd49e 100644 --- a/public_html/gmap.html +++ b/public_html/gmap.html @@ -1,3 +1,4 @@ + @@ -21,7 +22,7 @@ @@ -46,23 +47,23 @@