From e9b69620cf358c086053ab7cd60b2ef5b06828cf Mon Sep 17 00:00:00 2001 From: Xerbo Date: Fri, 22 Nov 2019 19:11:37 +0000 Subject: [PATCH] A few changes here and there. --- .gitignore | 53 ++++++++ Makefile | 14 +- README | 79 ------------ README.md | 82 +++++++----- dsp.c | 151 +++++++++++++--------- fcolor.c | 18 +-- filter.c | 27 ++-- filter.h | 2 +- filtercoeff.h | 9 +- gvipalette.h | 2 +- image.c | 155 ++++++++++++---------- main.c | 348 ++++++++++++++++++++++++++++++++------------------ messages.h | 6 + offsets.h | 4 + reg.c | 17 ++- satcal.h | 188 ++++++++++++++------------- temppalette.h | 3 +- textlogo.png | Bin 0 -> 66482 bytes version.h | 2 - w32util.c | 123 ------------------ w32util.h | 12 -- 21 files changed, 649 insertions(+), 646 deletions(-) create mode 100644 .gitignore delete mode 100644 README create mode 100644 messages.h create mode 100644 textlogo.png delete mode 100644 version.h delete mode 100755 w32util.c delete mode 100755 w32util.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a75882b --- /dev/null +++ b/.gitignore @@ -0,0 +1,53 @@ +# Created by https://www.gitignore.io/api/c +# Edit at https://www.gitignore.io/?templates=c + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Program specifics +*.png +*.wav +!textlogo.png +*.pnm +aptdec + +# VSCode +.vscode \ No newline at end of file diff --git a/Makefile b/Makefile index b521492..c6df7be 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,16 @@ -CC=gcc -BIN=/usr/bin -INCLUDES=-I. -CFLAGS= -O3 -Wall $(INCLUDES) -OBJS= main.o image.o dsp.o filter.o reg.o fcolor.o +CC = gcc +BIN = /usr/bin +INCLUDES = -I. +CFLAGS = -O3 -Wall $(INCLUDES) +OBJS = main.o image.o dsp.o filter.o reg.o fcolor.o aptdec: $(OBJS) $(CC) -o $@ $(OBJS) -lm -lsndfile -lpng -main.o: main.c version.h temppalette.h gvipalette.h offsets.h +main.o: main.c temppalette.h gvipalette.h offsets.h messages.h dsp.o: dsp.c filtercoeff.h filter.h filter.o: filter.c filter.h -image.o: image.c satcal.h offsets.h +image.o: image.c satcal.h offsets.h messages.h fcolor.o: fcolor.c offsets.h clean: diff --git a/README b/README deleted file mode 100644 index 1b6b6d2..0000000 --- a/README +++ /dev/null @@ -1,79 +0,0 @@ - -ATPDEC README (Thierry Leconte F4DWV (c) 2004-2009) - -DESCRIPTION - -Atpdec is an open source program that decodes images transmitted by POES -NOAA weather satellite series. -These satellites transmit continuously, among other things, medium -resolution images of the earth on 137Mhz. -These transmissions could be easily received with an inexpensive antenna -and dedicated receiver. -Output from such a receiver, is an audio signal that could be recorded -into a soundfile with any soundcard. - -Atpdec will convert these sounfiles into .png images. - -For each soundfile up to 6 images could be generated : - - 1. Raw image : contains the 2 transmitted channel images + telemetry -and synchro pulses. - 2. Calibrated channel A image - 3. Calibrated channel B image - 4. Temperature compensed I.R image - 5. False color image - -Input soundfiles must be mono signal sampled at 11025 Hz. -Atpdec use libsndfile to read soundfile, so any sound file format supported by libsndfile -could be read.(Only tested with .wav file). - -COMPILATION - -Atpdec is written in plain standart C and must be very portable. -It was only tested on Linux Fedora , but must work on any Unix platform. -Just adapt the Makefile and type make (sorry no configure). - -atpdec use libsndfile, libpng and libm. -snd.h and png.h header must be present on your system. -If they are not on standard path, edit the include path in the Makefile. - -USAGE - -atpdec [options] soundfiles ... - -OPTIONS - - -i [r|a|b|c|t] - Toggle raw (r) , channel A (a) , channel B (b) , false color (c) , - or temperature (t) output. - Default : "ac" - - -d directory - Optional images destination directory. - Default : soundfile directory. - - -s n - Satellite number 15 to 19 - Used for Temperature compensation. - Default : NOAA-19 - - -c conf_file - Use configuration file for false color generation. - Default : Internal parameters. - -OUTPUT - -Generated image are in png format, 8bits greyscale for raw and channel A|B images, -24bits RVB for false color. - -Image names are soundfilename-x.png, where x is : - -r for raw images - -satellite instrument number (1,2,3A,3B,4,5) for channel A|B images - -c for false colors. - -EXAMPLE - -atpdec -d image -i ac *.wav - -Will process all .wav files in the current directory, generate only channel A and false color images and put them in the image directory. - diff --git a/README.md b/README.md index 977e21a..ed449e5 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,31 @@ -# Aptdec +![Aptdec logo](textlogo.png) Thierry Leconte F4DWV (c) 2004-2009 ## Description -Aptec is an FOSS program that decodes images transmitted by POES NOAA weather satellites. -These satellites transmit continuously (among other things), medium resolution (1px/4km) images of the earth on 137 MHz. -These transmissions could be easily received with an simple antenna and cheap SDR. -Then the transimssion can easily be decoded in narrow band FM mode. +Aptdec is an FOSS program that decodes images transmitted by NOAA weather satellites. These satellites transmit continuously (among other things), medium resolution (1px/4km) images of the earth on 137 MHz. +These transmissions could be easily received with an simple antenna and cheap SDR. Then the transmission can easily be decoded in narrow band FM mode. -Aptdec can convert these recordings into .png images. +Aptdec can convert these audio files into .png images. -For each recording up to 6 images can be generated: +For each audio file up to 6 images can be generated: -1. Raw image: contains the 2 transmitted channel images + telemetry -and synchronisation pulses. +1. Raw image: contains the 2 transmitted channel images + telemetry and synchronization pulses. 2. Calibrated channel A image 3. Calibrated channel B image -4. Temperature compensated I.R image +4. Temperature compensated IR image 5. False color image +6. Layered image, boosts cloud visibility -Input recordings must be mono with a sample rate of 11025 Hz. -Aptdec uses `libsndfile` to read the input recording, so any format supported by `libsndfile` may be used (only tested with .wav files). +The input audio file must be mono with a sample rate in the range of 4160-62400 Hz, lower samples rates will process faster. +Aptdec uses `libsndfile` to read the input audio, so any format supported by `libsndfile` may be used (however it has only tested with `.wav` files). ## Compilation -Aptdec is written is portable since it is written in standard C. -It has only tested on Fedora and Debian, but will work on any Unix platform. -Just edit the Makefile and run `make` (no configure script as of now). +Aptdec is portable since it is written in standard C. +It has successfully compiled and ran on Debian with both `gcc` and `clang` and will most likely work on any Unix platform. +Just edit the Makefile and run `make` (no configure script as of right now). Aptdec uses `libsndfile`, `libpng` and `libm`. The `snd.h` and `png.h` headers must be present on your system. @@ -35,55 +33,75 @@ If they are not on standard path, edit the include path in the Makefile. ## Usage +To compile +`make` + To run without installing -`./Aptdec [options] recordings ...` +`./aptdec [options] audio files...` To install `sudo make install` +To run once installed +`aptdec [options] audio files...` + To uninstall `sudo make uninstall` -To run once installed -`Aptdec [options] recordings ...` - ## Options ``` --i [r|a|b|c|t] -Toggle raw (r), channel A (a), channel B (b), false color (c), -or temperature (t) output. -Default: ac +-i [r|a|b|c|t|l] +Output image type +Raw (r), Channel A (a), Channel B (b), False Color (c), Temperature (t), Layered (l) +Default: ab -d -Optional images destination directory. -Default: Recording directory. +Images destination directory (optional). +Default: Current directory -s [15|16|17|18|19] Satellite number For temperature and false color generation Default: 19 +-e [c|t] +Enhancements +Contrast (c) or Crop Telemetry (t) +Defaults: ct + -c Use configuration file for false color generation. -Default: Internal parameters. +Default: Internal parameters ``` ## Output Generated images are outputted in PNG, 8 bit greyscale for raw and channel A|B images, 24 bit RGB for false color. -Image names are `recordingname-x.png`, where `x` is: +Image names are `audiofile-x.png`, where `x` is: + + - `r` for raw images + - Sensor ID (1, 2, 3A, 3B, 4, 5) for channel A|B images + - `c` for false color. + - `t` for temperature calibrated images + - `l` for layered images + +Currently there are 2 available enchancements: - - r for raw images - - satellite instrument number (1, 2, 3A, 3B, 4, 5) for channel A|B images - - c for false colors. + - `c` for contrast enhancements, on by default + - `t` for crop telemetry, on by default, only has effects on raw images ## Example -`Aptdec -d images -i ac *.wav` +`aptdec -d images -i ab *.wav` + +This will process all `.wav` files in the current directory, generate contrast enhanced channel A and B images and put them in the `images` directory. + +## Further reading -This will process all .wav files in the current directory, generate channel A and false color images and put them in the `images` directory. +[https://noaasis.noaa.gov/NOAASIS/pubs/Users_Guide-Building_Receive_Stations_March_2009.pdf](https://noaasis.noaa.gov/NOAASIS/pubs/Users_Guide-Building_Receive_Stations_March_2009.pdf) +[https://web.archive.org/web/20141220021557/https://www.ncdc.noaa.gov/oa/pod-guide/ncdc/docs/klm/tables.htm](https://web.archive.org/web/20141220021557/https://www.ncdc.noaa.gov/oa/pod-guide/ncdc/docs/klm/tables.htm) ## License diff --git a/dsp.c b/dsp.c index 670b3bf..c9ec059 100755 --- a/dsp.c +++ b/dsp.c @@ -1,5 +1,5 @@ /* - * Aptec + * Aptdec * Copyright (c) 2004 by Thierry Leconte (F4DWV) * * $Id$ @@ -20,17 +20,25 @@ * */ #include +#include #include #include #ifndef M_PI -#define M_PI 3.14159265358979323846 /* for OS's that don't include it */ +#define M_PI 3.1415926535 // For OS's that don't include it #endif #include "filter.h" #include "filtercoeff.h" +/* WARNING + * The comments in this file are at best educated guesses. + * I am not a DSP god and can not figure out what a complicated + * math function does just by looking at it. + */ + extern int getsample(float *inbuff, int nb); #define BLKAMP 1024 +// Block size #define BLKIN 1024 #define Fc 2400.0 @@ -48,10 +56,10 @@ static double FreqLine = 1.0; static double FreqOsc; static double K1, K2; - +// Check if the input sample rate is correct int init_dsp(double F) { - if(F > Fi) return (1); - if(F < Fp) return (-1); + if(F > Fi) return(1); + if(F < Fp) return(-1); Fe = F; K1 = DFc / Fe; @@ -61,9 +69,10 @@ int init_dsp(double F) { return(0); } -/* fast phase estimator */ -static inline double Phase(double I,double Q) { - double angle,r; +// Fast phase estimator +// Calculates the phase angle of a signal from a IQ sample +static inline double Phase(double I, double Q) { + double angle, r; int s; if(I == 0.0 && Q == 0.0) @@ -90,68 +99,79 @@ static inline double Phase(double I,double Q) { return(-angle); } +// Phase locked loop +// Used to get value from an IQ sample with noise reduction static double pll(double I, double Q) { - /* pll coeff */ + // PLL coefficient static double PhaseOsc = 0.0; double Io, Qo; double Ip, Qp; double DPhi; - /* quadrature oscillator */ + // Quadrature oscillator / reference Io = cos(PhaseOsc); Qo = sin(PhaseOsc); - /* phase detector */ + // Phase detector Ip = I * Io + Q * Qo; Qp = Q * Io - I * Qo; DPhi = Phase(Ip, Qp); - /* loop filter */ + // Loop filter PhaseOsc += 2.0 * M_PI * (K1 * DPhi + FreqOsc); if (PhaseOsc > M_PI) - PhaseOsc -= 2.0 * M_PI; + PhaseOsc -= 2.0 * M_PI; if (PhaseOsc <= -M_PI) - PhaseOsc += 2.0 * M_PI; + PhaseOsc += 2.0 * M_PI; FreqOsc += K2 * DPhi; if (FreqOsc > ((Fc + DFc) / Fe)) - FreqOsc = (Fc + DFc) / Fe; + FreqOsc = (Fc + DFc) / Fe; if (FreqOsc < ((Fc - DFc) / Fe)) - FreqOsc = (Fc - DFc) / Fe; + FreqOsc = (Fc - DFc) / Fe; - return (Ip); + return(Ip); } +// Convert audio samples into a pixel static int getamp(double *ambuff, int nb) { static float inbuff[BLKIN]; - static int idxin=0; - static int nin=0; - - int n; + static int idxin = 0; + static int nin = 0; + int n; for (n = 0; n < nb; n++) { double I, Q; + // If the amount of samples is small enough to be processed if (nin < IQFilterLen * 2 + 2) { + // Number of samples read int res; memmove(inbuff, &(inbuff[idxin]), nin * sizeof(float)); idxin = 0; - res = getsample(&(inbuff[nin]), BLKIN - nin); + + // Read some samples + res = getsample(&(inbuff[nin]), BLKIN - nin); nin += res; + // If we haven't read any more samples, return how far we got if (nin < IQFilterLen * 2 + 2) - return (n); + return(n); } + // Process read samples into a brightness value iqfir(&inbuff[idxin], iqfilter, IQFilterLen, &I, &Q); ambuff[n] = pll(I, Q); idxin += 1; nin -= 1; } - return (n); + + return(n); } +// Get an entire row of pixels, without alignment int getpixelv(float *pvbuff, int nb) { + // Amplitude buffer static double ambuff[BLKAMP]; static int nam = 0; static int idxam = 0; @@ -160,7 +180,6 @@ int getpixelv(float *pvbuff, int nb) { double mult; mult = (double) Fi / Fe * FreqLine; - m = RSFilterLen / mult + 1; for (n = 0; n < nb; n++) { @@ -173,23 +192,24 @@ int getpixelv(float *pvbuff, int nb) { res = getamp(&(ambuff[nam]), BLKAMP - nam); nam += res; if (nam < m) - return (n); + return(n); } + // Denoise pvbuff[n] = rsfir(&(ambuff[idxam]), rsfilter, RSFilterLen, offset, mult) * mult * 256.0; - //printf("%g\n",pvbuff[n]); - shift = ((int) floor((RSMULT - offset) / mult)) + 1; - offset = shift * mult + offset - RSMULT ; + offset = shift * mult + offset - RSMULT; idxam += shift; nam -= shift; } - return (nb); + return(nb); } +// Align this line based off of the synchronisation markers int getpixelrow(float *pixelv) { + // Create an array for this row static float pixels[PixelLine + SyncFilterLen]; static int npv = 0; static int synced = 0; @@ -205,61 +225,70 @@ int getpixelrow(float *pixelv) { res = getpixelv(&(pixelv[npv]), SyncFilterLen + 2 - npv); npv += res; if (npv < SyncFilterLen + 2) - return (0); + return(0); } - /* test sync */ + // Test current synchronisation ecorr = fir(pixelv, Sync, SyncFilterLen); - corr = fir(&(pixelv[1]), Sync, SyncFilterLen); - lcorr = fir(&(pixelv[2]), Sync, SyncFilterLen); + corr = fir(&(pixelv[1]), Sync, SyncFilterLen - 1); + lcorr = fir(&(pixelv[2]), Sync, SyncFilterLen - 2); + // Calculate the per pixel offset FreqLine = 1.0+((ecorr-lcorr) / corr / PixelLine / 4.0); + // Maximum acceptable offset if (corr < 0.75 * max) { synced = 0; FreqLine = 1.0; } max = corr; + if (synced < 8) { - int shift, mshift; + int mshift; - if (npv < PixelLine + SyncFilterLen) { - res = getpixelv(&(pixelv[npv]), PixelLine + SyncFilterLen - npv); - npv += res; - if (npv < PixelLine + SyncFilterLen) - return (0); - } + if (npv < PixelLine + SyncFilterLen) { + res = getpixelv(&(pixelv[npv]), PixelLine + SyncFilterLen - npv); + npv += res; + if (npv < PixelLine + SyncFilterLen) + return(0); + } - /* lookup sync start */ - mshift = 0; - for (shift = 1; shift < PixelLine; shift++) { - double corr; + // Shift this line until we see the best results + mshift = 0; + for (int shift = 1; shift < PixelLine; shift++) { + double corr; - corr = fir(&(pixelv[shift + 1]), Sync, SyncFilterLen); - if (corr > max) { - mshift = shift; - max = corr; - } + corr = fir(&(pixelv[shift + 1]), Sync, SyncFilterLen); + if (corr > max) { + mshift = shift; + max = corr; + } + } + + // Shift memory, shifting this row + if (mshift != 0) { + memmove(pixelv, &(pixelv[mshift]), (npv - mshift) * sizeof(float)); + npv -= mshift; + synced = 0; + FreqLine = 1.0; + } else + synced += 1; } - if (mshift != 0) { - memmove(pixelv, &(pixelv[mshift]), (npv - mshift) * sizeof(float)); - npv -= mshift; - synced = 0; - FreqLine = 1.0; - } else - synced += 1; - } + // If there are not enough pixels try to grab some more if (npv < PixelLine) { res = getpixelv(&(pixelv[npv]), PixelLine - npv); npv += res; + // If we fail this then exit with 0 (which breaks the loop at main.c:338) if (npv < PixelLine) - return (0); + return(0); } + + // If we're finished reset npv to 0 if (npv == PixelLine) { npv = 0; - } else { + } else { // Move the pixel build buffer to the output buffer memmove(pixels, &(pixelv[PixelLine]), (npv - PixelLine) * sizeof(float)); npv -= PixelLine; } - return (1); + return(1); } diff --git a/fcolor.c b/fcolor.c index 10aafbb..e3608c8 100644 --- a/fcolor.c +++ b/fcolor.c @@ -104,18 +104,18 @@ void falsecolor(double v, double t, float *r, float *g, float *b) { if (t > fcinfo.Threshold) { if (v < fcinfo.Seathreshold) { - /* sea */ + // Sea top = fcinfo.SeaTop, bot = fcinfo.SeaBot; scv = v / fcinfo.Seathreshold; sct = (256.0 - t) / (256.0 - fcinfo.Threshold); } else { - /* ground */ + // Ground top = fcinfo.GroundTop, bot = fcinfo.GroundBot; scv = (v - fcinfo.Seathreshold) / (fcinfo.Landthreshold - fcinfo.Seathreshold); sct = (256.0 - t) / (256.0 - fcinfo.Threshold); } } else { - /* clouds */ + // Clouds top = fcinfo.CloudTop, bot = fcinfo.CloudBot; scv = v / 256.0; sct = (256.0 - t) / 256.0; @@ -129,27 +129,21 @@ void falsecolor(double v, double t, float *r, float *g, float *b) { }; void Ngvi(float **prow, int nrow) { - int n; - printf("GVI... "); fflush(stdout); - for (n = 0; n < nrow; n++) { + for (int n = 0; n < nrow; n++) { float *pixelv; - int i; pixelv = prow[n]; - for (i = 0; i < CH_WIDTH; i++) { + for (int i = 0; i < CH_WIDTH; i++) { float pv; double gvi; gvi = (pixelv[i + CHA_OFFSET] - pixelv[i + CHB_OFFSET]) / (pixelv[i + CHA_OFFSET] + pixelv[i + CHB_OFFSET]); pv = (gvi + 0.1) * 340.0; - if (pv > 255.0) - pv = 255.0; - if (pv < 0.0) - pv = 0.0; + pv = CLIP(pv, 0, 255); pixelv[i + CHB_OFFSET] = pv; } diff --git a/filter.c b/filter.c index b821533..b655cda 100755 --- a/filter.c +++ b/filter.c @@ -1,5 +1,5 @@ /* - * Aptec + * Aptdec * Copyright (c) 2004 by Thierry Leconte (F4DWV) * * $Id$ @@ -22,37 +22,39 @@ #include "filter.h" #include +// Sum of a matrix multiplication of 2 arrays float fir(float *buff, const float *coeff, const int len) { - int i; double r; r = 0.0; - for (i = 0; i < len; i++) { + for (int i = 0; i < len; i++) { r += buff[i] * coeff[i]; } - return r; + return(r); } +// Create an IQ sample from a sample buffer void iqfir(float *buff, const float *coeff, const int len, double *I, double *Q) { - int k; double i, q; i = q = 0.0; - for (k = 0; k < len; k++) { + for (int k = 0; k < len; k++) { q += buff[2*k] * coeff[k]; + // Average out the I samples, which gives us the DC offset i += buff[2*k]; } - i= buff[len-1] - i / len; - *I=i, *Q=q; + // Grab the peak value of the wave and subtract the DC offset + i = buff[len-1] - (i / len); + *I = i, *Q = q; } +// Denoise, I don't know how it works, but it does float rsfir(double *buff, const float *coeff, const int len, const double offset, const double delta) { - int i; - double n; double out; out = 0.0; - for (i = 0, n = offset; i < (len-1)/delta-1; n += delta, i++) { + double n = offset; + for (int i = 0; i < (len-1)/delta-1; n += delta, i++) { int k; double alpha; @@ -60,6 +62,5 @@ float rsfir(double *buff, const float *coeff, const int len, const double offset alpha = n - k; out += buff[i] * (coeff[k] * (1.0 - alpha) + coeff[k + 1] * alpha); } - return out; + return(out); } - diff --git a/filter.h b/filter.h index 4a3ecb9..8d94d5d 100755 --- a/filter.h +++ b/filter.h @@ -1,5 +1,5 @@ /* - * Aptec + * Aptdec * Copyright (c) 2003 by Thierry Leconte (F4DWV) * * $Id$ diff --git a/filtercoeff.h b/filtercoeff.h index dfed057..d76a573 100755 --- a/filtercoeff.h +++ b/filtercoeff.h @@ -1,5 +1,5 @@ /* - * Aptec + * Aptdec * Copyright (c) 2003 by Thierry Leconte (F4DWV) * * $Id$ @@ -26,8 +26,9 @@ const float iqfilter[IQFilterLen] = { 0.0205361, 0.0219524, 0.0235785, 0.0254648 -0.63662, -0.212207, -0.127324, -0.0909457, -0.0707355, -0.0578745, -0.0489708, -0.0424413, -0.0374482, -0.0335063, -0.0303152, -0.0276791, -0.0254648, -0.0235785, -0.0219524, -0.0205361 }; - +// Length of Sync #define SyncFilterLen 32 +// Pattern of a NOAA sync line const float Sync[SyncFilterLen] = { -14, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, 18, 18, -14, -14, -14 }; @@ -95,6 +96,4 @@ const float rsfilter[RSFilterLen] = { -3.37279e-04, -8.80292e-06, -3.96418e-04, -6.46202e-04, -8.02450e-04, -9.64235e-04, -1.08660e-03, -1.15302e-03, -1.23904e-03, -1.20955e-03, -1.26937e-03, -1.15443e-03, -1.19836e-03, -1.01377e-03, -1.05669e-03, -8.15327e-04, -8.79730e-04, -5.93148e-04, -6.95337e-04, -3.75376e-04, -5.27511e-04, -1.78544e-04, -3.96418e-04, -8.80292e-06, --3.37279e-04 }; - - +-3.37279e-04 }; \ No newline at end of file diff --git a/gvipalette.h b/gvipalette.h index 8efaed1..a9ab41f 100644 --- a/gvipalette.h +++ b/gvipalette.h @@ -1,4 +1,4 @@ - unsigned char GviPalette[256*3] = { +unsigned char GviPalette[256*3] = { "\230t\17\233x\22\236{\27\241\200\33\244\203\37\247\210#\252\214'\255\220" ",\260\2240\264\2305\267\2358\272\240=\274\245A\300\251E\303\255I\306\262" "M\311\266Q\314\272V\317\276Z\322\302^\325\306b\330\312g\334\317k\337\323" diff --git a/image.c b/image.c index 34c5323..744ef76 100644 --- a/image.c +++ b/image.c @@ -1,5 +1,5 @@ /* - * Aptec + * Aptdec * Copyright (c) 2004 by Thierry Leconte (F4DWV) * * $Id$ @@ -26,6 +26,7 @@ #include #include "offsets.h" +#include "messages.h" #define REGORDER 3 typedef struct { @@ -33,7 +34,7 @@ typedef struct { } rgparam; static void rgcomp(double x[16], rgparam * rgpr) { - /*{ 0.106,0.215,0.324,0.433,0.542,0.652,0.78,0.87 ,0.0 }; */ + //const double y[9] = { 0.106, 0.215, 0.324, 0.433, 0.542, 0.652, 0.78, 0.87, 0.0 }; const double y[9] = { 31.07, 63.02, 94.96, 126.9, 158.86, 191.1, 228.62, 255.0, 0.0 }; extern void polyreg(const int m, const int n, const double x[], const double y[], double c[]); @@ -48,15 +49,53 @@ static double rgcal(float x, rgparam * rgpr) { y += rgpr->cf[i] * p; p = p * x; } - return (y); + return(y); } - static double tele[16]; static double Cs; static int nbtele; -int Calibrate(float **prow, int nrow, int offset) { +// Contrast enchance +void equalise(float **prow, int nrow, int offset, int telestart, rgparam regr[30]){ + for (int n = 0; n < nrow; n++) { + float *pixelv; + int i; + + pixelv = prow[n]; + for (i = 0; i < CH_WIDTH; i++) { + float pv; + int k, kof; + + pv = pixelv[i + offset]; + + k = (n - telestart) / 128; + if (k >= nbtele) + k = nbtele - 1; + kof = (n - telestart) % 128; + + if (kof < 64) { + if (k < 1) { + pv = rgcal(pv, &(regr[k])); + } else { + pv = rgcal(pv, &(regr[k])) * (64 + kof) / 128.0 + rgcal(pv, &(regr[k - 1])) * (64 - kof) / 128.0; + } + } else { + if ((k + 1) >= nbtele) { + pv = rgcal(pv, &(regr[k])); + } else { + pv = rgcal(pv, &(regr[k])) * (192 - kof) / 128.0 + rgcal(pv, &(regr[k + 1])) * (kof - 64) / 128.0; + } + } + + pv = CLIP(pv, 0, 255); + pixelv[i + offset] = pv; + } + } +} + +// Get telemetry data for thermal calibration +int calibrate(float **prow, int nrow, int offset, int contrastBoost) { double teleline[3000]; double wedge[16]; rgparam regr[30]; @@ -66,58 +105,77 @@ int Calibrate(float **prow, int nrow, int offset) { int channel = -1; float max; - printf("Calibration... "); - fflush(stdout); - - /* build telemetry values lines */ + // Extract telemetry data, for a single pixel row for (n = 0; n < nrow; n++) { int i; teleline[n] = 0.0; + // Average the 40 center pixels from the telemetry block for (i = 3; i < 43; i++) { teleline[n] += prow[n][i + offset + CH_WIDTH]; } + // Compute the average teleline[n] /= 40.0; } + // A good minimum amount of pixels to find the telemetry start if (nrow < 192) { - fprintf(stderr, " not possible, not enough rows!\n"); - return (0); + fprintf(stderr, ERR_TELE_ROW); + return(0); } - /* find telemetry start in the 2nd third */ + // Find the biggest contrast in the telemetry max = 0.0; mtelestart = 0; + + // Only check the center of the image, where the signal is most likely strongest for (n = nrow / 3 - 64; n < 2 * nrow / 3 - 64; n++) { float df; + // Calculate the contrast, in other words + // (sum 4px below) / (sum 4px above) df = (teleline[n - 4] + teleline[n - 3] + teleline[n - 2] + teleline[n - 1]) / (teleline[n] + teleline[n + 1] + teleline[n + 2] + teleline[n + 3]); + // Find the biggest contrast if (df > max) { mtelestart = n; max = df; } } - mtelestart -= 64; - telestart = mtelestart % 128; + // Calculate relative offset + telestart = (mtelestart - 64) % 128; + // If we cannot find the start of the telemetry or if there is not enough of it if (mtelestart < 0 || nrow < telestart + 128) { - fprintf(stderr, " impossible, not enough row\n"); - return (0); + fprintf(stderr, ERR_TELE_ROW); + return(0); } - /* compute wedges and regression */ + /* Compute wedges and regression + * + * This loop loops every 128 pixels after the relative telemetry start. + * Gets the values of where the wedges should be and then feeds into a + * regression algorithm which calculates the amount of noise on the + * telemetry. + * + * It then finds the part of the telemetry data with the least noise and + * turns it into digital values. + */ for (n = telestart, k = 0; n < nrow - 128; n += 128, k++) { int j; + // Loop through the 16 wedges for (j = 0; j < 16; j++) { int i; wedge[j] = 0.0; - for (i = 1; i < 7; i++) - wedge[j] += teleline[n + j * 8 + i]; + // Center 6 pixels + for (i = 1; i < 7; i++){ + wedge[j] += teleline[(j * 8) + n + i]; + } + // Average wedge[j] /= 6; } @@ -126,12 +184,12 @@ int Calibrate(float **prow, int nrow, int offset) { if (k == nrow / 256) { int i, l; - /* telemetry calibration */ + // Telemetry calibration for (j = 0; j < 16; j++) { tele[j] = rgcal(wedge[j], &(regr[k])); } - /* channel ID */ + // Channel ID for (j = 0, max = 10000.0, channel = -1; j < 6; j++) { float df; @@ -162,49 +220,13 @@ int Calibrate(float **prow, int nrow, int offset) { } nbtele = k; - /* calibrate */ - for (n = 0; n < nrow; n++) { - float *pixelv; - int i; - - pixelv = prow[n]; - for (i = 0; i < CH_WIDTH; i++) { - float pv; - int k, kof; - - pv = pixelv[i + offset]; - - k = (n - telestart) / 128; - if (k >= nbtele) - k = nbtele - 1; - kof = (n - telestart) % 128; - - if (kof < 64) { - if (k < 1) { - pv = rgcal(pv, &(regr[k])); - } else { - pv = rgcal(pv, &(regr[k])) * (64 + kof) / 128.0 + rgcal(pv, &(regr[k - 1])) * (64 - kof) / 128.0; - } - } else { - if ((k + 1) >= nbtele) { - pv = rgcal(pv, &(regr[k])); - } else { - pv = rgcal(pv, &(regr[k])) * (192 - kof) / 128.0 + rgcal(pv, &(regr[k + 1])) * (kof - 64) / 128.0; - } - } + // Image contrast + if(contrastBoost) equalise(prow, nrow, offset, telestart, regr); - if (pv > 255.0) - pv = 255.0; - if (pv < 0.0) - pv = 0.0; - pixelv[i + offset] = pv; - } - } - printf("Done\n"); - return (channel + 1); + return(channel + 1); } -/* ------------------------------temperature calibration -----------------------*/ +// --- Temperature Calibration --- // extern int satnum; #include "satcal.h" @@ -241,7 +263,7 @@ static void tempcomp(double t[16], int ch, tempparam * tpr) { /* compute radiance Black body */ C = satcal[satnum].rad[tpr->ch].vc; - tpr->Nbb = c1 * C * C * C / (exp(c2 * C / Tbb) - 1.0); + tpr->Nbb = c1 * C * C * C / (expm1(c2 * C / Tbb)); /* store Count Blackbody and space */ tpr->Cs = Cs * 4.0; @@ -261,13 +283,13 @@ static double tempcal(float Ce, tempparam * rgpr) { Ne = Nl + Nc; vc = satcal[satnum].rad[rgpr->ch].vc; - T = c2 * vc / log(c1 * vc * vc * vc / Ne + 1.0); + T = c2 * vc / log1p(c1 * vc * vc * vc / Ne); T = (T - satcal[satnum].rad[rgpr->ch].A) / satcal[satnum].rad[rgpr->ch].B; /* rescale to range 0-255 for -60 +40 'C */ T = (T - 273.15 + 60.0) / 100.0 * 256.0; - return (T); + return(T); } void Temperature(float **prow, int nrow, int channel, int offset) { @@ -289,10 +311,7 @@ void Temperature(float **prow, int nrow, int channel, int offset) { pv = tempcal(pixelv[i + offset], &temp); - if (pv > 255.0) - pv = 255.0; - if (pv < 0.0) - pv = 0.0; + pv = CLIP(pv, 0, 255); pixelv[i + offset] = pv; } } diff --git a/main.c b/main.c index f390f1f..169c6fe 100644 --- a/main.c +++ b/main.c @@ -25,16 +25,12 @@ #include #include -#ifdef WIN32 -#include "w32util.h" -#else #include -#endif #include #include -#include "version.h" +#include "messages.h" #include "offsets.h" #include "temppalette.h" @@ -49,70 +45,74 @@ static int initsnd(char *filename) { SF_INFO infwav; int res; - /* open wav input file */ + // Open audio file infwav.format = 0; inwav = sf_open(filename, SFM_READ, &infwav); if (inwav == NULL) { - fprintf(stderr, "Could not open %s for reading\n", filename); - return (1); + fprintf(stderr, ERR_FILE_READ, filename); + return(0); } res = init_dsp(infwav.samplerate); + printf("Input file: %s\n", filename); if(res < 0) { - fprintf(stderr, "Sample rate too low: %d\n", infwav.samplerate); - return (1); + fprintf(stderr, "Input sample rate too low: %d\n", infwav.samplerate); + return(0); + }else if(res > 0) { + fprintf(stderr, "Input sample rate too high: %d\n", infwav.samplerate); + return(0); } - if(res > 0) { - fprintf(stderr, "Sample rate too hight: %d\n", infwav.samplerate); - return (1); - } - fprintf(stderr, "Sample rate: %d\n", infwav.samplerate); + printf("Input sample rate: %d\n", infwav.samplerate); if (infwav.channels != 1) { fprintf(stderr, "Too many channels in input file: %d\n", infwav.channels); - return (1); + return(0); } - return (0); + return(1); } +// Get a sample from the wave file int getsample(float *sample, int nb) { - return (sf_read_float(inwav, sample, nb)); + return(sf_read_float(inwav, sample, nb)); } static png_text text_ptr[] = { - {PNG_TEXT_COMPRESSION_NONE, "Software", version, sizeof(version)}, + {PNG_TEXT_COMPRESSION_NONE, "Software", VERSION}, {PNG_TEXT_COMPRESSION_NONE, "Channel", NULL, 0}, - {PNG_TEXT_COMPRESSION_NONE, "Description", "NOAA POES satellite image", 25} + {PNG_TEXT_COMPRESSION_NONE, "Description", "NOAA satellite image", 20} }; -static int ImageOut(char *filename, char *chid, float **prow, int nrow, int width, int offset, png_color *palette) { +static int ImageOut(char *filename, char *chid, float **prow, int nrow, int width, int offset, png_color *palette, int croptele, int layered) { FILE *pngfile; png_infop info_ptr; png_structp png_ptr; - int n; - /* init png lib */ + // Reduce the width of the image to componsate for the missing telemetry + if(croptele) width -= TOTAL_TELE; + + // Initalise the PNG writer png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { - fprintf(stderr, "Could not create a PNG write struct\n"); - return (1); + fprintf(stderr, ERR_PNG_WRITE); + return(1); } + // Metadata info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp) NULL); - fprintf(stderr, "Could not create a PNG info struct\n"); - return (1); + fprintf(stderr, ERR_PNG_INFO); + return(1); } if(palette == NULL) { - /* greyscale */ + // Greyscale image png_set_IHDR(png_ptr, info_ptr, width, nrow, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); } else { - /* palette color mage */ + // Palleted color image png_set_IHDR(png_ptr, info_ptr, width, nrow, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); @@ -124,57 +124,77 @@ static int ImageOut(char *filename, char *chid, float **prow, int nrow, int widt png_set_text(png_ptr, info_ptr, text_ptr, 3); png_set_pHYs(png_ptr, info_ptr, 4000, 4000, PNG_RESOLUTION_METER); - printf("Writing %s... ", filename); + printf("Writing %s ", filename); fflush(stdout); pngfile = fopen(filename, "wb"); if (pngfile == NULL) { - fprintf(stderr, "Could not open %s for writing\n", filename); - return (1); + fprintf(stderr, ERR_FILE_WRITE, filename); + return(1); } png_init_io(png_ptr, pngfile); png_write_info(png_ptr, info_ptr); - for (n = 0; n < nrow; n++) { + for (int n = 0; n < nrow; n++) { float *pixelv; png_byte pixel[2*IMG_WIDTH]; - int i; pixelv = prow[n]; - for (i = 0; i < width; i++) { - pixel[i] = pixelv[i + offset]; + int f = 0; + for (int i = 0; i < width; i++) { + // Skip parts of the image that are telemtry + if(croptele){ + switch (i) { + case 0: + f += SYNC_WIDTH + SPC_WIDTH; + break; + case CH_WIDTH: + f += TELE_WIDTH + SYNC_WIDTH + SPC_WIDTH; + break; + case CH_WIDTH*2: + f += TELE_WIDTH; + } + } + + if(layered){ + // Layered image, basically overlay highlights in channel B over channel A + float cloud = CLIP(pixelv[i+CHB_OFFSET]-141, 0, 255)/114*255; + pixel[i] = CLIP(pixelv[i+CHA_OFFSET] + cloud, 0, 255); + }else{ + pixel[i] = pixelv[i + offset + f]; + } } png_write_row(png_ptr, pixel); + } png_write_end(png_ptr, info_ptr); fclose(pngfile); - printf("Done\n"); + printf("\nDone\n"); png_destroy_write_struct(&png_ptr, &info_ptr); - return (0); + return(0); } static int ImageRGBOut(char *filename, float **prow, int nrow) { FILE *pngfile; png_infop info_ptr; png_structp png_ptr; - int n; extern void falsecolor(double v, double t, float *r, float *g, float *b); - /* init png lib */ + // Initalise the PNG writer png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { - fprintf(stderr, "Could not create a PNG write struct\n"); - return (1); + fprintf(stderr, ERR_PNG_WRITE); + return(1); } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp) NULL); - fprintf(stderr, "Could not create a PNG info struct\n"); - return (1); + fprintf(stderr, ERR_PNG_WRITE); + return(1); } - png_set_IHDR(png_ptr, info_ptr, CH_WIDTH , nrow , + png_set_IHDR(png_ptr, info_ptr, CH_WIDTH, nrow, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); @@ -188,20 +208,19 @@ static int ImageRGBOut(char *filename, float **prow, int nrow) { fflush(stdout); pngfile = fopen(filename, "wb"); if (pngfile == NULL) { - fprintf(stderr, "Could not open %s for writing\n", filename); - return (1); + fprintf(stderr, ERR_FILE_WRITE, filename); + return(1); } png_init_io(png_ptr, pngfile); png_write_info(png_ptr, info_ptr); - for (n = 0; n < nrow ; n++) { + for (int n = 0; n < nrow ; n++) { png_color pix[CH_WIDTH]; - float *pixelc; - int i; + float *pixelc; pixelc = prow[n]; - for (i = 0; i < CH_WIDTH - 1; i++) { + for (int i = 0; i < CH_WIDTH - 1; i++) { float v, t; float r, g, b; @@ -220,11 +239,11 @@ static int ImageRGBOut(char *filename, float **prow, int nrow) { fclose(pngfile); printf("Done\n"); png_destroy_write_struct(&png_ptr, &info_ptr); - return (0); + return(0); } - -static void Distrib(char *filename,float **prow,int nrow) { +// Distribution of values between Channel A and Channel B +static void Distrib(char *filename, float **prow, int nrow) { unsigned int distrib[256][256]; int n; int x, y; @@ -253,152 +272,229 @@ static void Distrib(char *filename,float **prow,int nrow) { fprintf(df,"P2\n#max %d\n",max); fprintf(df,"256 256\n255\n"); - for(y=0;y<256;y++) - for(x=0;x<256;x++) + for(y = 0; y < 256; y++) + for(x = 0; x < 256; x++) fprintf(df, "%d\n", (int)((255.0 * (double)(distrib[y][x])) / (double)max)); fclose(df); } -extern int Calibrate(float **prow, int nrow, int offset); +extern int calibrate(float **prow, int nrow, int offset, int contrastBoost); extern void Temperature(float **prow, int nrow, int ch, int offset); extern int Ngvi(float **prow, int nrow); extern void readfconf(char *file); -extern int optind, opterr; +extern int optind; extern char *optarg; + +// Default to NOAA 19 int satnum = 4; static void usage(void) { - fprintf(stderr, "Aptdec [options] recordings ...\n"); - fprintf(stderr, "Options:\n -i [r|a|b|c|t] Output image type\n r: Raw\n a: A channel\n b: B channel\n c: False color\n t: Temperature\n -d Image destination directory.\n -s [15|16|17|18|19] Satellite number\n -c False color config file\n"); + printf("Aptdec [options] audio files ...\n" + "Options:\n" + " -e [c|t] Enhancements\n" + " c: Contrast enhance\n" + " t: Crop telemetry\n" + " -i [r|a|b|c|t] Output image type\n" + " r: Raw\n" + " a: A channel\n" + " b: B channel\n" + " c: False color\n" + " t: Temperature\n" + " l: Layered\n" + " -d Image destination directory.\n" + " -s [15-19] Satellite number\n" + " -c False color config file\n"); exit(1); } int main(int argc, char **argv) { char pngfilename[1024]; - char name[1024]; - char pngdirname[1024] = ""; - char imgopt[20] = "ac"; + char name[500]; + char pngdirname[500] = ""; + + // Images to create, default to a channel A and channel B image with contrast enhancement and cropped telemetry + char imgopt[20] = "ab"; + char enchancements[20] = "ct"; + + // Image buffer float *prow[3000]; - char *chid[] = { "?", "1", "2", "3A", "4", "5", "3B" }; - int nrow; + int nrow; + + // Mapping between wedge value and channel ID + char *chid[] = { "?", "1", "2", "3A", "4", "5", "3B" }; + char *chname[] = { "unknown", "visble", "near-infrared", "near-infrared", "thermal-infrared", "thermal-infrared", "thermal-infrared" }; + + // The active sensor in each channel int chA, chB; - int c; - printf("%s\n", version); + // Print version + printf("%s\n", VERSION); - opterr = 0; - if(argc == 1){ + // Print usage if there are no arguments + if(argc == 1) usage(); - } - while ((c = getopt(argc, argv, "c:d:i:s:")) != EOF) { + + int c; + while ((c = getopt(argc, argv, "c:d:i:s:e:")) != EOF) { switch (c) { + // Output directory name case 'd': strcpy(pngdirname, optarg); break; + // False color config file case 'c': readfconf(optarg); break; + // Output image type case 'i': strcpy(imgopt, optarg); break; + // Satellite number (for calibration) case 's': satnum = atoi(optarg)-15; + // Check if it's within the valid range if (satnum < 0 || satnum > 4) { - fprintf(stderr, "Invalid satellite number, must be in the range [15-19]\n"); + fprintf(stderr, "Invalid satellite number, it must be the range [15-19]\n"); exit(1); } break; + // Enchancements + case 'e': + strcpy(enchancements, optarg); + break; default: usage(); } } - for (nrow = 0; nrow < 3000; nrow++) - prow[nrow] = NULL; + if(optind == argc){ + printf("No audio files provided.\n"); + usage(); + } + + // Pull this in from filtercoeff.h + extern float Sync[32]; + // Loop through the provided files for (; optind < argc; optind++) { chA = chB = 0; + // Generate output name strcpy(pngfilename, argv[optind]); strcpy(name, basename(pngfilename)); strtok(name, "."); - if (pngdirname[0] == '\0') { - strcpy(pngfilename, argv[optind]); + + if (pngdirname[0] == '\0') strcpy(pngdirname, dirname(pngfilename)); - } - /* open snd input */ - if (initsnd(argv[optind])) + // Open sound file, exit if that fails + if (initsnd(argv[optind]) == 0) exit(1); - /* main loop */ - printf("Decoding: %s \n", argv[optind]); + // Main image building loop for (nrow = 0; nrow < 3000; nrow++) { - if (prow[nrow] == NULL) + // Allocate 2150 floats worth of memory for every line of the image prow[nrow] = (float *) malloc(sizeof(float) * 2150); + + // Read into prow and break the loop once we reach the end of the image if (getpixelrow(prow[nrow]) == 0) break; + + // Signal level + double signal = 0; + for (int i = 0; i < 32; i++) signal += prow[nrow][i] - Sync[i]; - printf("%d\r", nrow); + // Signal level bar + char bar[30]; + for (int i = 0; i < 30; i++) { + bar[i] = ' '; + if(i < signal/2000*30) bar[i] = '#'; + } + + printf("Row: %d, Signal Strength: %s\r", nrow, bar); fflush(stdout); } - printf("\nDone\n"); + printf("\nTotal rows: %d\n", nrow); + sf_close(inwav); - /* raw image */ - if (strchr(imgopt, (int) 'r') != NULL) { + // Layered images require contrast enhancements + int contrastBoost = CONTAINS(enchancements, 'c') || CONTAINS(imgopt, 'l'); + + chA = calibrate(prow, nrow, CHA_OFFSET, 0); + chB = calibrate(prow, nrow, CHB_OFFSET, 0); + printf("Channel A: %s (%s)\n", chid[chA], chname[chA]); + printf("Channel B: %s (%s)\n", chid[chB], chname[chB]); + + // Temperature + if (CONTAINS(imgopt, 't') && chB >= 4) { + Temperature(prow, nrow, chB, CHB_OFFSET); + sprintf(pngfilename, "%s/%s-t.png", pngdirname, name); + ImageOut(pngfilename, "Temperature", prow, nrow, CH_WIDTH, CHB_OFFSET, (png_color*)TempPalette, 0, 0); + } + + // We have to run the contrast enhance here because the temperature function requires real data + // Yes, this is bodgy, yes I should replace this with something more elegant + chA = calibrate(prow, nrow, CHA_OFFSET, contrastBoost); + chB = calibrate(prow, nrow, CHB_OFFSET, contrastBoost); + + // Layered + if (CONTAINS(imgopt, 'l')){ + sprintf(pngfilename, "%s/%s-l.png", pngdirname, name); + ImageOut(pngfilename, "Layered", prow, nrow, CH_WIDTH, 0, NULL, 0, 1); + } + + // Raw image + if (CONTAINS(imgopt, 'r')) { + int croptele = CONTAINS(enchancements, 't'); + char channelstr[45]; + sprintf(channelstr, "%s (%s) & %s (%s)", chid[chA], chname[chA], chid[chB], chname[chB]); + sprintf(pngfilename, "%s/%s-r.png", pngdirname, name); - ImageOut(pngfilename, "raw", prow, nrow, IMG_WIDTH, 0, NULL); + ImageOut(pngfilename, channelstr, prow, nrow, IMG_WIDTH, 0, NULL, croptele, 0); } - /* Channel A */ - if (((strchr(imgopt, (int) 'a') != NULL) - || (strchr(imgopt, (int) 'c') != NULL) - || (strchr(imgopt, (int) 'd') != NULL))) { - chA = Calibrate(prow, nrow, CHA_OFFSET); - if (chA >= 0 && strchr(imgopt, (int) 'a') != NULL) { - sprintf(pngfilename, "%s/%s-%s.png", pngdirname, name, chid[chA]); - ImageOut(pngfilename, chid[chA], prow, nrow, CH_WIDTH , CHA_OFFSET, NULL); - } + // Channel A + if (CONTAINS(imgopt, 'a')) { + char channelstr[21]; + sprintf(channelstr, "%s (%s)", chid[chA], chname[chA]); + + sprintf(pngfilename, "%s/%s-%s.png", pngdirname, name, chid[chA]); + ImageOut(pngfilename, channelstr, prow, nrow, CH_WIDTH, CHA_OFFSET, NULL, 0, 0); } - /* Channel B */ - if ((strchr(imgopt, (int) 'b') != NULL) - || (strchr(imgopt, (int) 'c') != NULL) - || (strchr(imgopt, (int) 't') != NULL) - || (strchr(imgopt, (int) 'd') != NULL)) { - chB = Calibrate(prow, nrow, CHB_OFFSET); - if (chB >= 0 && strchr(imgopt, (int) 'b') != NULL) { - sprintf(pngfilename, "%s/%s-%s.png", pngdirname, name, chid[chB]); - ImageOut(pngfilename, chid[chB], prow, nrow, CH_WIDTH , CHB_OFFSET ,NULL); - } - if (chB > 3) { - Temperature(prow, nrow, chB, CHB_OFFSET); - if (strchr(imgopt, (int) 't') != NULL) { - sprintf(pngfilename, "%s/%s-t.png", pngdirname, name); - ImageOut(pngfilename, "Temperature", prow, nrow, CH_WIDTH, CHB_OFFSET, (png_color*)TempPalette); - } - } + // Channel B + if (CONTAINS(imgopt, 'b')) { + char channelstr[21]; + sprintf(channelstr, "%s (%s)", chid[chB], chname[chB]); + + sprintf(pngfilename, "%s/%s-%s.png", pngdirname, name, chid[chB]); + ImageOut(pngfilename, channelstr, prow, nrow, CH_WIDTH , CHB_OFFSET, NULL, 0, 0); } - /* distribution */ - if (chA && chB && strchr(imgopt, (int) 'd') != NULL) { + // Distribution map + if (CONTAINS(imgopt, 'd')) { sprintf(pngfilename, "%s/%s-d.pnm", pngdirname, name); Distrib(pngfilename, prow, nrow); } - /* color image */ - if (chA == 2 && chB == 4 && strchr(imgopt, (int) 'c') != NULL) { - sprintf(pngfilename, "%s/%s-c.png", pngdirname, name); - ImageRGBOut(pngfilename, prow, nrow); + // False color image + if(CONTAINS(imgopt, 'c')){ + if (chA == 2 && chB == 4) { // Normal false color + sprintf(pngfilename, "%s/%s-c.png", pngdirname, name); + ImageRGBOut(pngfilename, prow, nrow); + } else if (chA == 1 && chB == 2) { // GVI false color + Ngvi(prow, nrow); + sprintf(pngfilename, "%s/%s-c.png", pngdirname, name); + ImageOut(pngfilename, "GVI False Color", prow, nrow, CH_WIDTH, CHB_OFFSET, (png_color*)GviPalette, 0, 0); + } else { + fprintf(stderr, "Lacking channels required for false color computation.\n"); + } } - /* GVI image */ - if (chA == 1 && chB == 2 && strchr(imgopt, (int) 'c') != NULL) { - Ngvi(prow, nrow); - sprintf(pngfilename, "%s/%s-c.png", pngdirname, name); - ImageOut(pngfilename, "GVI", prow, nrow, CH_WIDTH, CHB_OFFSET, (png_color*)GviPalette); - } - } + // Free the allocated memory + for(int i = 0; i < 3000; i++) free(prow[i]); + } + exit(0); } diff --git a/messages.h b/messages.h new file mode 100644 index 0000000..702ade2 --- /dev/null +++ b/messages.h @@ -0,0 +1,6 @@ +#define ERR_FILE_WRITE "Could not open %s for writing\n" +#define ERR_FILE_READ "Could not open %s for reading\n" +#define ERR_PNG_WRITE "Could not create a PNG write struct\n" +#define ERR_PNG_INFO "Could not create a PNG info struct\n" +#define ERR_TELE_ROW "Telemetry decoding error, not enough rows.\n" +#define VERSION "Aptdec CVS version (c) 2004-2005 Thierry Leconte F4DWV" \ No newline at end of file diff --git a/offsets.h b/offsets.h index f034cb9..d8980bb 100644 --- a/offsets.h +++ b/offsets.h @@ -6,3 +6,7 @@ #define IMG_WIDTH 2080 #define CHA_OFFSET (SYNC_WIDTH+SPC_WIDTH) #define CHB_OFFSET (SYNC_WIDTH+SPC_WIDTH+CH_WIDTH+TELE_WIDTH+SYNC_WIDTH+SPC_WIDTH) +#define TOTAL_TELE (SYNC_WIDTH+SPC_WIDTH+TELE_WIDTH+SYNC_WIDTH+SPC_WIDTH+TELE_WIDTH) + +#define CLIP(val, bottom, top) (val > top ? top : (val > bottom ? val : bottom)) +#define CONTAINS(str, char) (strchr(str, (int) char) != NULL) \ No newline at end of file diff --git a/reg.c b/reg.c index 508d96c..316856d 100644 --- a/reg.c +++ b/reg.c @@ -12,10 +12,11 @@ #include -#define DMAX 5 /* Maximum degree of polynomial */ -#define NMAX 10 /* Maximum number of points */ +#define DMAX 5 /* Maximum degree of polynomial */ +#define NMAX 10 /* Maximum number of points */ static void FactPiv(int N, double A[DMAX][DMAX], double B[], double Cf[]); + void polyreg(const int M, const int N, const double X[], const double Y[], double C[]) { int R, K, J; /* Loop counters */ double A[DMAX][DMAX]; /* A */ @@ -41,7 +42,8 @@ void polyreg(const int M, const int N, const double X[], const double Y[], doubl /* Zero the array */ for (J = 1; J <= 2 * M; J++) - P[J] = 0; + P[J] = 0; + P[0] = N; /* Compute the sum of powers of x_(K-1) */ @@ -74,9 +76,8 @@ static void FactPiv(int N, double A[DMAX][DMAX], double B[], double Cf[]) { double SUM, DET = 1.0; int T; - /* Initialize the pointer vector */ - for (J = 0; J < N; J++) + for (J = 0; J < NMAX; J++) Row[J] = J; /* Start LU factorization */ @@ -108,13 +109,15 @@ static void FactPiv(int N, double A[DMAX][DMAX], double B[], double Cf[]) { A[Row[K]][C] -= A[Row[K]][P] * A[Row[P]][C]; } } - } /* End of L*U factorization routine */ + } + /* End of L*U factorization routine */ DET = DET * A[Row[N - 1]][N - 1]; /* Start the forward substitution */ for (K = 0; K < N; K++) Y[K] = B[K]; - Y[0] = B[Row[0]]; + + Y[0] = B[Row[0]]; for (K = 1; K < N; K++) { SUM = 0; diff --git a/satcal.h b/satcal.h index 643d173..ebe897c 100644 --- a/satcal.h +++ b/satcal.h @@ -6,103 +6,101 @@ const struct { struct { float Ns; float b[3]; - } cor[3]; -} satcal[] = -{ -/* calibration coeff from NOAA KLM POES satellite user guide */ -/* http://www.ncdc.noaa.gov/oa/pod-guide/ncdc/docs/klm/tables.htm */ - -{/* NOAA-15 */ -{ /* PRT coeff d0,d1,d2 */ -{276.60157 , 0.051045 , 1.36328E-06}, -{276.62531 , 0.050909 , 1.47266E-06}, -{276.67413 , 0.050907 , 1.47656E-06}, -{276.59258 , 0.050966 , 1.47656E-06} -}, -{ /* channel radiance coeff vc,A,B*/ -{925.4075 , 0.337810 , 0.998719}, /* channel 4 */ -{839.8979 , 0.304558 , 0.999024}, /* channel 5 */ -{2695.9743 , 1.621256 , 0.998015} /* channel 3B */ -}, -{ /* nonlinear radiance correction Ns,b0,b1,b2 */ -{-4.50 , {4.76 , -0.0932 , 0.0004524}}, /* channel 4 */ -{-3.61 , {3.83 , -0.0659 , 0.0002811}}, /* channel 5*/ -{0.0,{0.0,0.0,0.0}} /* channel 3B*/ -} -}, -{/* NOAA-16 */ -{ /* PRT coeff d0,d1,d2 */ -{276.355 , 5.562E-02 ,-1.590E-05}, -{276.142 , 5.605E-02 ,-1.707E-05}, -{275.996 , 5.486E-02 ,-1.223E-05}, -{276.132 , 5.494E-02 ,-1.344E-05} -}, -{ /* channel radiance coeff vc,A,B*/ -{917.2289 ,0.332380 ,0.998522}, /* channel 4 */ -{838.1255 ,0.674623 ,0.998363}, /* channel 5 */ -{2700.1148 ,1.592459,0.998147} /* channel 3B */ -}, -{ /* nonlinear radiance correction Ns,b0,b1,b2 */ -{-2.467, {2.96,-0.05411,0.00024532}}, /* channel 4 */ -{-2.009,{2.25,-0.03665,0.00014854}}, /* channel 5*/ -{0.0,{0.0,0.0,0.0}} /* channel 3B*/ -} -}, -{/* NOAA 17 */ -{ /* PRT coeff d0,d1,d2 */ -{276.628 , 0.05098 , 1.371e-06}, -{276.538 , 0.05098 , 1.371e-06}, -{276.761 , 0.05097 , 1.369e-06}, -{276.660 , 0.05100 , 1.348e-06} -}, -{ /* channel radiance coeff vc,A,B*/ -{926.2947 , 0.271683 , 0.998794}, /* channel 4 */ -{839.8246 , 0.309180 , 0.999012}, /* channel 5 */ -{2669.3554 , 1.702380 , 0.997378} /* channel 3B */ -}, -{ /* nonlinear radiance correction Ns,b0,b1,b2 */ -{-8.55 , {8.22 , -0.15795 , 0.00075579}}, /* channel 4 */ -{-3.97 , {4.31 , -0.07318 , 0.00030976}}, /* channel 5 */ -{0.0,{0.0,0.0,0.0}} /* channel 3B*/ -} -}, -{/* NOAA 18 */ -{ /* PRT coeff d0,d1,d2 */ -{276.601 , 0.05090 , 1.657e-06}, -{276.683 , 0.05101 , 1.482e-06}, -{276.565 , 0.05117 , 1.313e-06}, -{276.615 , 0.05103 , 1.484e-06} -}, -{ /* channel radiance coeff vc,A,B*/ -{928.1460 , 0.436645 , 0.998607}, /* channel 4 */ -{833.2532 , 0.253179 , 0.999057}, /* channel 5 */ -{2659.7952 , 1.698704 , 0.996960} /* channel 3B */ + } cor[3]; +/* Calibration corficcients taken from the NOAA KLM satellite user guide + * https://web.archive.org/web/20141220021557/https://www.ncdc.noaa.gov/oa/pod-guide/ncdc/docs/klm/tables.htm + */ +} satcal[] = { +{ // NOAA 15 + { // PRT coefficient d0, d1, d2 + {276.60157, 0.051045, 1.36328E-06}, + {276.62531, 0.050909, 1.47266E-06}, + {276.67413, 0.050907, 1.47656E-06}, + {276.59258, 0.050966, 1.47656E-06} + }, + { // Channel radiance coefficient vc, A, B + {925.4075, 0.337810, 0.998719}, // Channel 4 + {839.8979, 0.304558, 0.999024}, // Channel 5 + {2695.9743, 1.621256, 0.998015} // Channel 3B + }, + { // Nonlinear radiance correction Ns, b0, b1, b2 + {-4.50, {4.76, -0.0932, 0.0004524}}, // Channel 4 + {-3.61, {3.83, -0.0659, 0.0002811}}, // Channel 5 + {0.0, {0.0, 0.0, 0.0}} // Channel 3B + } }, -{ /* nonlinear radiance correction Ns,b0,b1,b2 */ -{-5.53 , {5.82 , -0.11069 , 0.00052337}}, /* channel 4 */ -{-2.22 , {2.67 , -0.04360 , 0.00017715}}, /* channel 5 */ -{0.0,{0.0,0.0,0.0}} /* channel 3B*/ -} +{ // NOAA 16 + { // PRT coeff d0, d1, d2 + {276.355, 5.562E-02, -1.590E-05}, + {276.142, 5.605E-02, -1.707E-05}, + {275.996, 5.486E-02, -1.223E-05}, + {276.132, 5.494E-02, -1.344E-05} + }, + { // Channel radiance coefficient vc, A, B + {917.2289, 0.332380, 0.998522}, // Channel 4 + {838.1255, 0.674623, 0.998363}, // Channel 5 + {2700.1148, 1.592459, 0.998147} // Channel 3B + }, + { // Nonlinear radiance correction Ns, b0, b1, b2 + {-2.467, {2.96, -0.05411, 0.00024532}}, // Channel 4 + {-2.009, {2.25, -0.03665, 0.00014854}}, // Channel 5 + {0.0, {0.0, 0.0, 0.0}} // Channel 3B + } }, -{/* NOAA 19 */ -{ /* PRT coeff d0,d1,d2 */ -{276.6067 , 0.051111 , 1.405783E-06}, -{276.6119 , 0.051090 , 1.496037E-06}, -{276.6311 , 0.051033 , 1.496990E-06}, -{276.6268 , 0.051058 , 1.493110E-06} +{ // NOAA 17 + { // PRT coefficient d0, d1, d2 + {276.628, 0.05098, 1.371e-06}, + {276.538, 0.05098, 1.371e-06}, + {276.761, 0.05097, 1.369e-06}, + {276.660, 0.05100, 1.348e-06} + }, + { // Channel radiance coefficient vc, A, B + {926.2947, 0.271683, 0.998794}, // Channel 4 + {839.8246, 0.309180, 0.999012}, // Channel 5 + {2669.3554, 1.702380, 0.997378} // Channel 3B + }, + { // Nonlinear radiance correction Ns, b0, b1, b2 + {-8.55, {8.22, -0.15795, 0.00075579}}, // Channel 4 + {-3.97, {4.31, -0.07318, 0.00030976}}, // Channel 5 + {0.0, {0.0, 0.0, 0.0}} // Channel 3B + } }, -{ /* channel radiance coeff vc,A,B*/ -{928.9 , 0.53959 , 0.998534}, /* channel 4 */ -{831.9 , 0.36064 , 0.998913}, /* channel 5 */ -{2670.0 , 1.67396 , 0.997364} /* channel 3B */ +{ // NOAA 18 + { // PRT coefficient d0, d1, d2 + {276.601, 0.05090, 1.657e-06}, + {276.683, 0.05101, 1.482e-06}, + {276.565, 0.05117, 1.313e-06}, + {276.615, 0.05103, 1.484e-06} + }, + { // Channel radiance coefficient vc, A, B + {928.1460, 0.436645, 0.998607}, // Channel 4 + {833.2532, 0.253179, 0.999057}, // Channel 5 + {2659.7952, 1.698704, 0.996960} // Channel 3B + }, + { // Nonlinear radiance correction Ns, b0, b1, b2 + {-5.53, {5.82, -0.11069, 0.00052337}}, // Channel 4 + {-2.22, {2.67, -0.04360, 0.00017715}}, // Channel 5 + {0.0, {0.0, 0.0, 0.0}} // Channel 3B + } }, -{ /* nonlinear radiance correction Ns,b0,b1,b2 */ -{-5.49 , {5.70 -0.11187 , 0.00054668}}, /* channel 4 */ -{-3.39 , {3.58 -0.05991 , 0.00024985}}, /* channel 5 */ -{0.0,{0.0,0.0,0.0}} /* channel 3B*/ -} -} -}; +{ // NOAA 19 + { // PRT coefficient d0, d1, d2 + {276.6067, 0.051111, 1.405783E-06}, + {276.6119, 0.051090, 1.496037E-06}, + {276.6311, 0.051033, 1.496990E-06}, + {276.6268, 0.051058, 1.493110E-06} + }, + { // Channel radiance coefficient vc, A, B + {928.9, 0.53959, 0.998534}, // Channel 4 + {831.9, 0.36064, 0.998913}, // Channel 5 + {2670.0, 1.67396, 0.997364} // Channel 3B + }, + { // Nonlinear radiance correction Ns, b0, b1, b2 + {-5.49, {5.70 -0.11187, 0.00054668}}, // Channel 4 + {-3.39, {3.58 -0.05991, 0.00024985}}, // Channel 5 + {0.0, {0.0, 0.0, 0.0}} // Channel 3B + } +}}; -const float c1=1.1910427e-5; -const float c2=1.4387752; \ No newline at end of file +const float c1 = 1.1910427e-5; +const float c2 = 1.4387752; \ No newline at end of file diff --git a/temppalette.h b/temppalette.h index 954a683..6e6d716 100644 --- a/temppalette.h +++ b/temppalette.h @@ -1,4 +1,4 @@ -unsigned char TempPalette[256*3] = { +unsigned char TempPalette[256*3] = { "\376\376\376\376\376\376\375\375\376\374\375\376\374\375\375\374\373\375" "\373\373\375\372\373\375\372\373\374\372\372\374\371\372\374\371\371\375" "\370\371\374\367\370\375\367\370\374\367\367\374\366\367\373\366\366\373" @@ -40,4 +40,3 @@ unsigned char TempPalette[256*3] = { "\2261\373\2161\373\206.\373}-\374u,\375k*\374b(\375Y'\375O%\375E#\375;\"" "\3762\40\376(\37\376\35\35", }; - diff --git a/textlogo.png b/textlogo.png new file mode 100644 index 0000000000000000000000000000000000000000..95207f93ed78d01847b0411ad2e02138818a2545 GIT binary patch literal 66482 zcmYgY2RzjO|G$KUvaU!r>C~C2E~H^ZMo~icjEtNWAyQ<^=wzMjQjyWwBkMA9p*kGd zU9w#nS^t;5-{1fD`=%a!wLYKs`}Kakp6f0As_vzI%)HD91Y)12hN?aSu^W#->_RXx z!e=P{FE`0^-hUG@a*bvAEWfb$}x>3}fq%jt+~! z+&&O-HS;s`8--`4e;9Ol-eaCaV&X>MQ8FT4Lmie1i_tZahWfok*ahZpGCFVr_{2S_DrH+c9DH4(|xDAJPh z3iXG9yZCN@kwtXxzDS>kKKFWi&dhT?Hqz4%BZU)LtArLs$#zM@7;e6UKK36p^1p|x zG+SpzPoA<@Q#pbc*?+u1eeGhaT?p;qt#_t&AN`iN89fHt<;adPLNRJ+;k_Jk1*l9` z7QLG%^|f;x&V3&h^xn{PZ<{#wAkJpjffrBs&aDb=%zf>=qFbT)p6An=HARr)xf8d; zXXo|xKEmOnLEDEadTwrA=6|*(R{OQ5@zVL>Q*PLyo{6&~?t6Id^&C-g7dxjToU!K{ z=|^fwLxbd}+V9I>2Z>X=pX3#Co^E(AEOYbuj92X4Jp+F0E83;?f(94dQzsl8bYWW< zt+g(xBDQzFJ+IAAfY0o8(=hfxAf!ZhzVGtMJ?D);2q838FI@K-n5p%BV)xj1(s!<| zc14a>YkIp|O3c`Zj?EQU+$`ViW2K;kQR)?7igFPb=Nox`i*Ao9S2I1UEPrn&{g5{0 zgse>WCwY_92liF(8KaD&?)LY!M!vOudHI;Z(*pOv^`WFSQJTM(h33=vxa;jD^%kn? zHzosohXYpZG`)VN&HK0B+5CFvWS-+#MX&rvug$t7x?XuJLct!pgNWg#ZLORm#Fs2yYW%`YHrnR`{Kvb=lzdHWchq0 z;+1?k>law+gD9@m`yReW*cp%Y6>$x3=E#>=DYFjA-H5yX4`%DH3f}hK9sN$TtqwMD(B>N ztWv~H$!ca~cpqcj`x3ORF(X2Tb+<9DjkZW7mY^qmrHX0`oqT5w53j24j>z&iv*!6j zG|@w|JL{5YCy`Z$Vutl_ zEsrcHP_2kn7k!qumxvvylFCBGud^a9us6_@t{X6rXrJa+ic3o95F%2kc^7y>Q5~Ey z;~#3N9;bCgx0%jkZ&6P5$cNY&zbIgKOlc>H#WGwFTFFFfCED#pz^eG-Bg4GR3{^e! zkSx{;9dbxDVe1MMGd#dbL}Su$0`<%o=Ib`yv4|`bd>m7HS|oUXe&zoBhw(cB(X`#|IK#fvi0VPbZ7l(5t9vM)XKomR;tdGS&iT5GkAtx4w7tar<1 z_G&g$WEK+?Pw&)oM_#nFbhab!v%K&Uv=!x4L>4<%v)CBPphV)ja62WBseM_N`9q$g zrSU;$%E1SR>30LGy^c@`? zJo_JWdYPGC<7h8Q4D(cMzbxA}uN6;UIGshA6^XIAZg6D(NrnO!iW}dhK}Y5r z>MoI4=aFihlP)QYuyCv@P-@g{q2&NUFEde1PL7JGEq$SDH9Zf~NmQu!-l^+>n19|T zaXR>A`FY=$YduMIo)e21tKk_9mNHM)G!SqS;7uCBxsJO1`Q`Zf%^S_;G8FUcqM~f_ zG#pm!A56W*`=?@i#S`|TJA$XH;Ndmn5kZ}S_QvAa8)Lo#jWS59KKbZQJp$E5;^)8| z3ejnQf+tj1Xxm%|s>!$xv=j(2bx7+5K1j4w@gRCrEt#*4)jCFg~sBHj`Rj7>5!06L>#exlTyzQ1z2)0 zwD8DKm%Ps6M8Xw1gl-x}xHKAJd!YU0wS3DI@vd~mqr@qH_eoZpQe?SvjNLBwbLHum zN1mU=#Ege{9fMUtYr(3>nvlI(;+#|LN7sUdp7?4PUd|lJv7AgvTh?|^RG6tM^uOX4 z!jf*(m2gT&)X8dfT};!~Jh4l5zWm=^ez9=f)!{qs%+|z}t%(mrrPYy#EY&w8J2=Zf z5nMbK_|udFcqtSLoSiWv_iv@c^W)9oT+j`n!W~m0K3F$D^?0&Tm**&xtPM3**8itY zy87g+f}2JZs7`2Q^Xo4PE;C1=5-BHgXOlm^BBz)RJ<@v6Ha=^tA7Nir4PEZ{yCJRn zg6W+~q@C?S_~BQg(u-WWU;n#$(8Ug?um!F*T@ncFF!>hz<%)9wy?)c-na5SjCA{r9BQ}=j7g)hchm(D8dC+Z`X z(vVens&I@AN8qfHbM;3;ZBr?Kt(`VulZW}zLCNB)ALMm>@JCD2O8Z$0r!O}riImoI zhURJ_*Z-}&CH-t;#mOV1GySEP=E64$=l@6z-d~^nXJV$r$?Mo1OjOo#A1% zq@05PU__}sd1@!|N~56QB2TECVq5FYQxx9N2SQBH*BfP;EoIKL(~F2xNFB7LssWwy z8mU%8UrEoS5*W(95@Zarm~?|(wYz=T<3JSE7dowbA-aQ|C>xP=o{uP^eMs#)-_i80 zf`Xg|jay3p?f3h%3=DQQ@nBY=A#J(5o*$7j`ji%i)bQN9#-py+E8lYM%Go1@BU@X_ zt>eXIpWOHN5L}wE=q|hqrQ~7=!#O=i+5) zZ`Zl<6NQegEIdwXBac@V$`2h>6Mj?a+;);xMY;5agK|dXhumk3Z{LQ7xW1|oUT<(mt%6hJEo(>^GyhdiOsi8E)m zXa1!`QmY8HLaijP1Z9=MWP7#-ga7a9PTqU2V%{Fvwf=3TPBb>Kquf7|sY{8}61RJQ zR?h52fx^v^dvU*idtqW80@hO6S{(=Q2x!u@jq9lH^FpVYxj7M%2lQ^wYO_8*irp_YrD8nQA3Hi8Ex@WY%UjH?JPYgpC{V-H0&m^61%@i<$7i ze)~dj5!0j_UQtVQw@=WsM2OTFzffICj2l^2I?itg6O&k6cM;ESRrRR>NoncGPujgmEMJqC z8O(SaFE6NmOC~eU>)iBWV|Ivs7zCE)2sPbaCXzca^+qR5CdQ9i(RxIoAO74cZ&NYeenH4Fz`EpIh zxFd`YcfoXMlzHTs@=RMCdihwDsa+$+shrZ1tCb{^`cZ~&{|*e!a}+D=FiS&*0uq<% zf=%d;(=;p@vhOf^=6Ql*oj^-PG+RwNS=vb2RC*c!3h5D1cy21FLQPWBMmElx;Sm!9 zs2txY#VeH3cmFo89SD-i6HD}&h`R(J(8-*}J2OAuL{oZ}C*hHtRyrZ2g^+uwk*T_< z_V{gWcET$Yz@xH8+Y6oSD%22yBdQ7)D@g%!ABZ)7 zz7*}OjjHK8n>Vv#sbZR{3*GNcZ{FlQjRk-=m7fthjw!i32{3@EiTxG{f8fF%zOou` z1^(*>A%L5T2hkk|Bm7XofR^irlTMlS;8)NQbTQ)^6Hmtyx{lfu&YI;QvyoZ;d<+t| zF2M?5UR&|>L{RQ4A3Uh{`i4P>46E9C6WkGxV&}3+8exc>z5M!LLem}@4o@x33?(o4 z=hNdt-;M97cUp(;4ohBqAsLKn-Z@;C?#5(AE28szU%o~<7Tp|;`e7*pXq%mQN1oRz zFc83c2OqVoE2bjh0=otadc{gVM^DN9+z)@{q<6>D+MKI%kp-So60a#;C^tJcROars zQznQp9Ko=1VcmcDv7NDdtxj))msvRagtB&d!r0R}N6J%!e|lNs^n;zF)p+ULpKj6E zVb5M2=<_9hy#yCky+?2+uxBN5cuv}_v9mlZL3439P)-4`AH%2{<0{5u6fINontgyl z_sWksfA@`%VJ)tzIyPJ3$!ol?8_mdBWjQYykUZB-p zQhnuKX$ot|_Iwu=ppsiUar{k$T%3=K!Wmn<2=ek+z`vwPh{uQA702iGL3kxGoIHu&4wZq$>I}m)BCu({^ znw$VwvSLd!H}3guURLC!;OCuF)ae~fY?s#dYK5RQ`hO{Akkn*ih&CMKXw}>06H7IK zH2WCe3i(jV%nbi~(DD^=v=!I*^G@UD3D3l z#jY*crJV~DNY~wSdf5MjctUvMnw#JYD;_9dXvQVzU-xQ5yu=gQR7GB-iRMOXTJ1Tn zdIwJ}IhZ?St%dEZO@sDnT}u)d_pOs*wM!Bouc;yAIZoU>10+XcN$HX?PDI{U$J1Wo znHl-1Uz?MqY6XA4U58XroX6Y$_Odfy<#iqs1^i|{5dDdF{U2AjLLKKhT0>FlAvMbq zPsE^+*1On4>4q%MP@p&(2m{00(!Ilput*f|D%-yVuT-zChEs-4(}`D2yU`UThpR3tqo)u!LNi~V8lBULupo3S zy#uL1h)Hz>@F=;R?#M`kvE55{H+I9q>6@y@)ua&N3rkzT)Ug(0zIf#6ciHVOaC<)Lo=Bnq_i^xr&fWVJyG^!O3V_vU>lxanf;(QGkh!gN& zloB2Xd0x%Icam@8#W=dJBNcCG^SYBDltq2nY zAz~rG!n!m~GS}vVhGb;;55AuFwls8$8wex8mha;xOvUfIpVE1#MB)~CJHBYd*Ik0P zGcT1yDyHvWQc9Y(+ofk>NhnG^?!-gNJ^nxZ$A%=h*)7QLyFsA1!s+T88hR=ssBW^p zsoW!ffE?>|Q9YX@v`H9YdF$35m~+r4c%6-Vnw}NFk?iXOunvC$gg7`Jpp-0OPIWV0 z2=btIh4a|a;~HbSj+0|#dM+2YX}DC^>6gy^$J7P+HN@WnrDwj0M zM#Tr7)JXy)qtYu`w1GTRB}= ztty0VAx`z?a}Z^rO1!Ja<<4!TntGFY7HkEfElA*eu#7`hy?d=h&*PsSxngDw}LQ#)RSK+KP z&>hXYa)oloKYWoPxM)ozkzk+whq*a^R!NOvaKXZ@w*rQ_ixtv!^^hT$rVWi=q#iQ- zMV^UPre?QpF+cr-E%xL8KOM(ET5Pmf@cC}|)lvNxT!xojc0?PlD@qTb6E>8fjWc^@ zy?EX)iq=*U{{vY8nXt1yR#E!(N#&CA z_Xm3f&c};Y_$-Q)pqu_`4tdSilq{4c=OZg7BGxv2FN3&f)~uH(=)Wu9{G@>ws&2@7 zx1T(YOh>P0e&{ICj(7yS2rQ>jMir?xrhgCr7!~np`ZvNd^Ce+cq^i(wVb9BVoO%3j zCf9g;e=A+FS6egcveVBYq&i6Ct)=<90QjPVAt z)5k3$*o#|ur1{$m3V_451>rhRCcu; z(ksMRp_4@7W5hCIiyjb-8CBo^s4A>+jWAL^-EyKM9;Wu=gsga1iCdOuP7L5#Tofz* zecC1ctarF}vNoRTvpV`11hdrP4SS`{U%fsv#l)-Rlh7t$TbN#7%h$C64LN4q^3EN$ z>~0M{b=(H@Z}XzZhILC%D>j!Yqw94Q>3T@v2oWRP4i>uFf{n`0xXmykoD<#GtAgJ|fPrEp!8ncqkc07E61#EZ2D~sJH$LJRvwK}H zmX9L{{c}{Ho1l0*MI74G3>)+?7}PN~;azg}+U4=qm9>8!U3{Wvg4@+ki0*cvC(&$U z5~-uV;bG7n6Zb$W8^?gWtB3q&I#vcMjKBPc)PqD9bFzN%a6NWnkzBv^8_4_JAusa9 z$Qjt2)>cMnh|oC*<>lO}!X|bg&$bSXx`nmbia1@GtD)&~n40^$CV2oL0-%uN z7}4*{IJ~m6S&c|#NlafI*jJK9&gskXsTnvk zxze*Y_ldnfH^$-JJQ||`zDSPYayYg*n+Bmyz-ns zJ2z~is1&WjP7nKmm^fiZU0(aQqISTfffQrwq3_g5s8FXjs`<+%djH(_|WDbtYPNhe`^W>dUhH?Au z-O1O5h1LN9W;Y71zL1?`Mr_^}G%$cZ@W={vT>pnNq2;#UT*=wUe)(ujgo_n1&xBb| zAshJstQqpO6#y37(@b=qy(D98Sd(S#6q=EMQ6nr}BF)u$}xentBT+BC5 zHJ3!=Bnh*dnaB_ZSHroHmu7MPL`#7+4MfTo7Y@iNOs zG5g*(x2bjxnGoC*g#@RobONSirRd}xO(w>hVe0zZSAcV$>YYC4Uk7;tw%*=$bPq2!q ziIJn>=~+nX6o$fY7K3F*>_2{ZoWw- zDkRJaye$$Hff!x^B}TlsAEv>MC=TN_)M{j7jfU~~&I<4lbkONrrG?W~V=sC%q50XL zm-vO6K}i9Pd`8WAo9(gpyt$~_adIlOM0p+Vuhphz=bQPT&r)NvH0IPyo}6$^6$`H^ z1Oc!;5i|^i|DljR*u^omjoIO?*^{yLV+V>FfRjg;pyTphmUWga?7_JGTz%K=AVy1b zemVR59vFQ$0$?)Gs3aRD0cgk^`f%z`r3#Zgu(EICY@x?gGZkKwIPc$A1v{ zQ_&tWhceS*R_wY;Fig2>q>t{w+Rh|(p)xW~JTou|N7#xH`1^szSn;%b&~Bno5)ErU zU2#jYo-w`_zHe%z@K?zH_EZ#^C3@f(DU$8}!b9*CdQ-nW0*R5s5M&BR-Cp?*CE9ge za)QGvEpMR7E-0}bKYpA=)`YnU#sg@KP&S^SuBBH0$pTcHap-xf9?Hz6RF#y7j4kho zB=hS#ej|y>Ndi9*yjIXpv&uXbc4^46iPqK#L|mQYvs;%;^_F0tG6FZRG!&(Hb!iXBV)k3`gpTpK z$Mndsf?lltUE-lo5L8EutFtqrp;3Wz3lCP^4T-IwhfA3-F(?GJRr9kXKuMsH`V&~9 zKyE44>i+QV*wUWQ1Uvgu$yS|S;QzZ3*i%ylgpL@LzZv-e@la4j>;FKygZ^_K8H!7* zU^iW{mf=CGD$|sR-@XaNi@oEZ=l4jsd!VrrW$ zU6TyZ-~SSV3n_URzvY>@*tPe98yCW}0sDfL662vH&~ZDl^cHEXuzHTuikQXMdLc3k z)F&|A<9vR&VVc39Y%h7VV~R1Gn3w>P6}ea`W#{6uZ%5(?SZX`k(m(B~05-Pr?kB#V zLEr`n*ayD{!9pfh>LOTl(Jc3Y@|6?L-a3;((la=)1NHCeKPpjm1ab4x_oXGdh%DH{ zuyG&ZdmVkwe$uFn;Sw~QH2|24K4>R{3WZjA9q{!)PEfnzTxF+K_&=p|1E{d_)?zH6 zJ(J--HwN!bzY%$1RG1*P7l7~LIA%ZmI?THrA_LPOmU;a|WY&`x4oEfq1{fmXwZoPn zIS!}^ztV=J0Xz-t>fD?ZFlBJNimR)S(~>;*f-+q{{Ma!CM2;jfSawIL6CV1XOVpBN z2LK6GPJQTkU*=-c)wC*OQ|u|+kJbX*JUU!4bxNLrQx=r1P*>P@9Zl8@Q&dGd0?IJQysc5?DA2J6b^Wc>#eABc#&hQD*drgE^0=y zF`y2@E}4=;rmI+rjl1sY3(3({7Z(d-4O_#;G3rP)NV!<>;?wF2$8zZp>gc930ef`(PgQ(>exfQGWqG$lv;tamkwOF4`VEvfH;I_=?&eI_W>zJPE%6xUBGrmVf_h{nBdmq*vG7&A zcP;@13ZLMCcTABoefU2cagY^Jzg8&?p6-sdd*Hz>@ruwLa14oPX z{Qj2#gU0ph)hjU6ARN*m2h0Lw`<(z4Ko3BGsyw0kohOpD_tVoxO|uHpQ5?Fp>V*I1K%?fYr4Msp_YGpGd z{s<9!@H<9AOys+T5aD=n**G7MFsvZTV-9lQ@WI)zKExlxj4b1&L^41?c9oJ&%{LQ->cY1BR zR%h%khr)5dHBK=~k@l zb!A-iTTl9N4DOBM6V%^_Ene{gK&$J-#+rb&o#r@~FGAh0=!piRv^uPKwU#6t(r05`J zt-!FB`HJ$I7>I~R=(O~jTMda=86H-!)&5FWkEy8Id|)X;=4~awxSEbnzHj;7 z*&PYq*Y^;Bf&mXo*HJgS)A`d??)yNYLEkPib7V1zxUF17^<^1L?lx1m>Tm6M;YJzF zVQHgk#jBdyC^R7^)=_Z{ ztY-lyQx(M^Kt0p{g5~_9a>o>wq7VixQ#vW z-#ZfLK>!9sjl@g|T>1C$L-LnO!0TJ+7PTfI7IvRo*e#dPmaH{5;#?g#*mOvZW5?qJ z-_^DJc(uZz-rQ$$I$R*8-9MYI8)tV2;zSSNBtw0)@kxXrsT#(d^nA_E^Yz>JjRP(| zNTZ&CqYd#sZhA2W(Zd$Tbj=5(J)I7bC!Hrs&qU6?JDRqaAqbK*aVCXPj-*ESdWV#} zvCncT9pv}q%f$FB+zJWH2Q3b)oMJO(GxdjxfJ0uZ>*btVAU&|+=|D*BUWjcwvUx|R zcO$Cq#NVFuH)*t!&H^Dd|NhT>5aq;_+pUEo3QoZwc$9Bv+_Q06?<6?N;4+n!iCs+0 zin7LnRt9k$>z7dr0ugk{T)&mrz)}YS@f}@x5!QZuhu#Ek)l|F%a|hbe+$X}-iI`8` zdRI(T4GoSQIg88y@>p=J|0FL_Hdu(gJaL6NXd@-~ote1mXcyza^0%w%J`^eO`q()c zQcEDu$T$zpPU%^<#pIc_2R32~yGC3>tv0Uc^!||I`@1u)sIo6NATijS6#HzJK4>TF z11^$I?}w-na1h51GfOZRFXJ!IP2@0Hze20At3mW)Y@=I`wTT@8XBzg#`|e#e=OdcI zi$1Jy(m{bXI1rK5@cp~^sne&iQ$0suS^o(D3PZq*eg&+lGuQD6uA)N*UBNxGfVm9} z+NusHM0)C1(lG=fC9vWNbm_rK`=!&nO3+6JI>v-sohdqHkgB^e)D{P-6%J``fgi3(enuMEvOd#BB~&AXpuD|exO*dmPU8mtJKmJ3E) zL=cb4cc;l1-Gj*PFD@zSrk}hYB~x?4CIcqOe8jl^u4Pm7yd%!e-6e`M-|^V|70O8i ze7F_Q-Ce7s*q1z89s!U4>I0Q=SBW&^!QA3(7PrisNoM_cPE%!Awcq*8= zDXcklJ<)ea&D#qZz(+uJ;0dg2kfTxP@>`dDEm)hLc$?#7Ap#U`eo7fh4T>=}{W4L7b!XdUsp|-N^5Xb`dmZ3)#hfN~EjpuFf_7yWc0X(F_^xnc z*wX&09&&YjrG7hwcqKzQ+OYPgfk;%}r-5%4nv1q6;yv;$5J*$Q^sMnkt3xIX{Kz3! zZah_!HK{cD%GuwEGu=?P;FBjN>6C*0yr6U?tGIUx2_6%uKLAEyegcy@eE6{3IR(D` zxs|x!k20(fwIk7>u))WZ|3udS8d(Y2ggpT*n6=L?N z*R~%av60Dj59m*#T{vjCfAh$|4LWg*7leZ2so7zkX53IIkV|e43Kjw@2mW5J)A2>g zwA788)w4LscgT0MS%T_55d~}?rK9*>k4iOfpGbnOa*^tBV%H+*nc{3wQ zqlMGwWsNP4Ad{HI-?b_s|0HvdDQ(wXKTVgY8~oXvQTaHh3}sYk=?}vod1`o|E$_t_vT0wYpr~*~Aaq z?`t|=M23hiA_!vrXAD8gBT+#*it03j?C9{72OW(9%w~U6;XsO>P|rZ-+lr64cyV1S zL}0w!xeQ+85x<&n$Z<6ZwPP)(>OqZ^kk0bSRcxEi{2}x#c+-hjUfveAZLR^VHYyN1Jv9noW2Kd?7n(kc-7J@4s^b6jIb4Byd{=V#KI%0w8 zi&NV2_F628$|*#_3k#o!=DgjG#$j8lZ2PH~Gx(j99(gQEG|02j|9ICtwXf6T&DFI@ zi!D2t^5HMWX6)cjK+Iu={fDhVP=Sy?jH3$FcnLgLIRT3fy))3&I0WYHK7n> zA+ZZcAzdK^k}1Ys-gyDUo!Q)%i3@POgLe4c}&4I{U>5M0c8L)NF~_ zGb!hF+4j<QAV@aL6(E-W}?h`jxe`?S6bw z6qWR+Mn>Dnulj}6^JLxbwU%v;IZ=zylf32}siC`8!il2+i>0S$*mf>9?09IRg0um0 z-3@!r%G2>8VIE=VI;)3HIJ+Gc{;vQ-z_@HEx9S`^X;+gd5|yljVy7BZ-eLAPWlTvyfq{*Ancxi0e}Kx?7_tD8)UCs&oJ6k&$|>?TLXio zfG8dqUkqBazmIMRFxM})A7pOB#8?66i8%O;tIJ+WTDn6CfYj1AH(cVJEX!l<-b5q0 zmZJ~l{q-@PjRYgT_}a7~7?V&S$J-J7d-@z3Z6$CjbaU5^Oy9(I&i~{1{5^6t5ODNnw^}1&32fGbj z71FK2l>vvwF=Zz`XI%-65bB4Uqn+`FLNI(1GF9+#Ds>bjPaI9=RL6Jlo|quRp~ev} zb|Js_0#Xe!5vH}?l)`ZgaO1jpEp6Ob{8+Lcq)>DH!)WnQANBjXy>gz64l zI4d;y;yZpG`bLr0nRG&?&l;1v9m?3JCO7jnTohAtX5|bG-n}hUbKG2Mm2s_p-#n=J z%k0{J7OiV5n=E;3!02Gs%Cx6QNU8=yK^vdKQy!%GCQ!`YGH$KWN%t&eLeGT%>{c-f(Iz<+o;7j9GL5{nAKVT1dc{Z))E_c?- z)``UOZ}ilSGY#kViY49oaQYmu-5)A~3QMv@0kclIvQIst(7W4de2{3cEJ17CG(HrV ztJoepzJnam!eI3^LDuV zS{UBBn3!HQzvFjGF2=^Vv`1=ww05KtijAEfnw!XKUlv3x$1tIgD@Ip=8kFn=WQ{}(k{8LIsETrkp zN3EHC^x1DichR~Z>!}|tlu%epU;b+MES1V0FtVaI=;0&R6tune_(|PQN)hFE(kL&Y zVPkyw0@v?5Mx$#}R|5Zh7@zm2D#E-}_2g97F%D{~kT5$N!5^Wa?gx{1dPd;*YnkgiJSQk^SLF=C#bu}ez| zPSyMRik$zp8e{)F6C$H|kX zF4<*U&WBqcBIas#NpJEW`zL;XX0B#rRQH5V{c`=t?Ad+u!`sq^bS@;ace-x(#NPJ& zI%b~^nK2&3)p_T$DgwM}SQ+Vi(HfNL>|N)kU-dG1S_)q)!eLmBQ+i9~UH1?N=vHXjk- zaYLU$ap@&$YHuqBOl9{x+N|&YwzZfG!R9ztBiQK&l;!Sd= z+cl&So(ky?GrF6sGcYlBF$XYVD?M>`*PpFKj6HQdI{pqZaeu+r$6~q)oS7xR`_Wkm zSZn3jl7`s9=j6>;)}{*MVdcM+DCXUQvo=fg;>za?9K*`)?fC;MOKZm^uMrK-y|)2# z^}{4fbYlP8sC-mJ$TPIrS==!63AldOL~uzqpcg7k8^;(K8yolWU&iv%@A^t6w_Kp# z&+Lujqcpy%{rbowC2CY(On*?XS-*4f#P}N!k08FR$qA9-HnhYoZOXb5<&{3ryM6-& zTn!G)$v>+~QKBAF3Dm4G+o-#vS2UbL6N)ltS!s2Q7Z7ksY}WgCeb5r|6BD9ll5uNK zbNN*Di@zPDc#9?S_ZPOte*Zu70l@>yTXlh76xsJ?3#UC+UfsZVL?3+Q@TK~|$NX(h zula@JxR4(zf%Ib6FoGNW@_Bpx*Jz}lsIPgx>@#*VE7^e?+7AX0@5oJB3TJNfG+eu4 zN-oD&#oJv(8Y0j7;+y1kAR%yZtO)5c8~pr!z4Va_ zVC3bz+lhLhbSgbhPMJco1TrfSifDW$@}|}>k=zsWQXgGfkE#hqy6J-u;Whq# zLLgR_H+rH@rcmo{LTswG_!wWWEEsqq4gAFSAZ0CEn(w}wxpX3==I`~35Ina!bFe0Q|D z)RjW8eK~7syvI9&F z(_J65vjD+5K;!@-qdC8bko0&<2WUUR!`+LS|GaW2UChVVkU3(a8-&m@OV=@kWBAh* z!jY1JIXgTRZb~qFLwar{?v?hB+j3p)W1?MfnJyA+&_OJbA2&TD8mh>@|tPlsM`TTA|{*5Gg3l!bHKhtm9hz7(|~-0^8T>_+=|TI&&x zE>t9DU%}w0=AOh8IM4kcEuXnO(jePe!U{eSWYU7DzCF)6mT%mD>#zp_Mmq$I!>Xa$ zAa4c&*(-y4wfIhls>0(fN@VZd22Yj`l5bS^YeYTT*;S#(pB|K(ocpKCHHi_ z=0e2!Gy9DAwN#$`c`wskVztjgc(ZHIAj67>(l^Na_=pne*2Dh(g@pmaRwd+>5eCu`t~p4cE(bj*S>m5&Ph`ZDmm?x1fVry z6)D47dy;}<_tg$|x1BK{EG}(;dMkQwVO|8+j6cKHz9iNfPVp$ez>dbGdK&p=l$mPy z95#QH_0Fb&jW=|WOQ+ZN5ALtm>pp0-+JW2k9LtB{&ImqvXm{v?5w`oZL%NnW{V!4& zNqwcRH-j7nR1H6?qaFW~1;}X8S^TzsV$bCH$H>XZS0yu7HuiQBmFs=(_tv;eb{`Ag z{+*FkjFvBaykJp(_~W0avwJ78;R0VzzV2Qy4jE^0Y;a-S9Xq=(gNEHHL0j?oMeWuL z5_nWj#l;Z1IFX}FbUS*}h%-@BLgt~+c_e@5ECd+|F2=VS%@Wfpb4FYul_9BfmMKKm z>;fo^KvoEx7`O9q)o`?ZO%q~+Q%^mTJ#R{6Q5FpjqGjni)~+0swKFEXJqoT`_+F1% z7Bw;cn>ZmoOix^nPYz;oevL;VAGw5l3jWw2_cs;%@V!HPTQ=>KdR=p(T+rz$%HLE) zdoedY+C>yp7_0j;BrDS@ro5sqKvQ)DUP3clfp8NhTUjj=AkERRvK1iiY5_Ebs(vqyDB1C|QXHsS(D(u(T%r0$J znLTPId1^{DLV}PdbZJ1s_K??izb0C-ZCIh)Fjp6qVED>E+lHt0E}~*(`o|H+!GEWP zv{E{a`pJJ|b5Ch#=&lnwmYW|18_&}E-Y>FHN1V+su1#={Zg&?o@t89XvcLEHwvUIg zpl#y3wyQF0y`&rKZhOw%xb+SC34;uaCpSs)W}9K-57+HGGOXi|>fZd-5cr%%jr;fW zj-UT5e;=`Jcz^%zsddZk#}s#Z55KQ#RCD@w|u7lgs0k*|BO&JO&JYv>w{Q^g*tw z%F>6IVtFh!r!ux2IUaeA46LJ4^Efh_`Y*OxQanP_%5yNxO;-mjD<77gy{pm9qZY>` zj}X}avmSiQoCgHTEQDn&AsGoG9`NWLfBfWBVo&AdejW9@m7)n`?bAR5<{${eQhXyq zCBi`c&eTsD^3)6WH>@GY&!O}s?5Es%j<|9&>e*gykvZubw9LF&7=1={pe-Lreg7a;9!PS+Z>88GV*7>ISO9u++bRG`OA{>J1 zH!(lm4v-ecgTK8O=zNnORR5>xVM#{9N)~Op{!yzJhtelXrrzuwasH9r00uEL7~ z-s9APb6A<^B>c5k`#JWhb=PE*5hhsQ~sk!yhxV?DgFg|D4tSw}{X`>7o! zUidY?b%gy5Ki{bgN{jsd&->oihTk$OBmE5KZKy1TuPYI&jQv&jX^()uRL51KU$4?@UTZDqBYOa4ZftxXqVzVT<+L$ zHL>fn#-sokrS; z?CbM3lZk6x1a|NiA(H^{ZcxlMdOa`jm8gc%#aY8|<)}>~IxA)`v8gOW+e}D$zl`1t z{;?kJ#me0b!-#jcc?aKBMKK25s#iLauy6lz^u7AuB)xS1N!oT^)4ZQL$2GI;!k@<* zud3!pb`@O+g3e;8u&PxCizf=PO9Lc>tBMf#4J1#vLPm z|Kk$rGUr*ReN9)#O4Rhwuf-Pw1mP|t=5Vz7TNjhT^XdV{3e9py6q09OIN_;dK_Cef z6`3_OJBSNd8ik+_x+U9n>MU9~|cV6H3%Sp={F9-wXh7WLb3q`#i zLw9&u%TMaid^aryf49g)2?VY0s##hXH%B-j?jfokXY51tekC#>V(EP78-{nS!u`yU zZ#t6<3p$9c6Nn$Q*P z7j+&Bp8h|czB($(FY0;-DQQu3khPCG2=RW72bM`)aGX|PrkeZyVVJbm-A{ad(RuB#PzznJD>%+C6 z$VAFyW0IAssR+g*V%cnLQu=nqz@5)TOj6fMkX;Ng@#c9hlPPfxe#TFt3oMiA#+Mmw zb3tSxs#Kq;N5*gQ)Ak3q{C$%Jo_2S7P~}NbT{aiwlR|Rocd8#W`O>8<)>SDog z+Z>P#9}a!+Yygtdvgp=vX`|y3+*e~!+d~2|$Y&nm?0DaILDsmHC6{V*f2&lA)8c0F zj{5v!1x`^U8kHwLmwzdB@Z2ipl@lep3By+=gX9(?3LgIA;rqLm*ZFu-c2=_`r#}4R8+w zW{;qEB#=Al;FBG$Qe9uO>}<3?#FsVrj-b=A@)mc=kzf3}GgtGbzrsqWoBx)e6Bpy! za!NS)FCX`K$lLQ3p}_CA@b;}cyMvJpc8TFPcW>X8l`fA7Ua2Mxwj(f^oxTLWqvFTo zA~lqckNg-K%!B#+BtJ)A^YQ!OS#Fc(hQj#od7c{LE_z5Ze;YG^U&{^;y2>*aq8V=Lkxq--fMAe2sNUAKjiw{rQ@(q3lw z!{G=$X-KaL@qdi|_vSn}${ClD``dO5p%-5ur&4B7`#-{q+s|Crr2p8(?bJpmRD_JX zm3R%x?=1yI_uV%qyKehMhtM6{v#5_2UOyz0-W?;Gl9oVGYs~e0SPmy13G5wCj!1tarDk zETnjNdziK}d}EUbA4^uWDc&`k=q{L$1vs z+0&ABS>};=6rU}T*=+D-M@Xy0rqwe3>T^dg?Vanx9T5mI!6e}pQ^V@xmQ~Xl*lud)-s)W&+Rp=w&Rg!UMnYs5vKn$n zjJSRf(9XCVpw!%!Pu~0;rtS3CsR*&}mv5QZXL1QjIY9Bc+_$41e)MaFS)RAPKZhF`@IJuKTutJZ%Oi89)8{ZTkTmx zOwl}tn^G@WI`wY(jOp+ER=#h4%*2vA4a_w_q&b1|$&-As7E1Ku?Y5@#b{tE#BB#FG zQK!tWcyEIy`yi%;PQuXT(*GDB5C86r$Ts4(Me;pqnCd3kO4c`th9a*Ft8<(t_BA7GXgKSi)VKVvl7hRbJ%OYZr2;|#oL;mj5As)#Y>Pxhr%Mg~ z{v2sfBxZ8SlMK($awG7gFl;FCpyCSmEZo7-#DPU*XibD;?rH>*-sEA`CgrrBvFp%8 z{<``5)=gcCvF#5Bmo{OV6wIp>Bj^5egS#8XUuSQ*0)GU^4fa`t9~X#aME-Ve$ZJ?L zfe=i`EJrPCbVMR%BgFjp+C5j=AI<`vo9}-i$|WY`g7^|Thj?y+6$wv7NFYDA{u33| zVaHdSguI75gV@|~V5mxN;fUc7H}%wrv48n^Tu=kmwz3(focSau(x}OmqXVOcWTvEa zF1Nh}+$L+n&|=smg;E;DCp{flC&(#_%wQ#9hLzN_DaU966;x`;h#-6NPsQ@JAMOHm z$qoXuA3Rxpon8fC_3ThF@ICSPNCWv!9(X4hEDcdtA*~cTG-=!Ui315jqO`MT1o^Pt z(zSknhL}~<-J1F6&6s|}xW)+9XDXgjm864oP`B5&Qg>!E7n_f-_vGQNUXt!Ii2U$e z!doT5XcYyDQTKxns`5SS@H-3j-%&KBrdkby)1t& zLNvJd(5oXH%ozappMN}cDgNW0jIlv6IAWTy5EnGZo|6kEEZgu~F$!g=UY_QlZ!9 zTp9$Vq#P4p)EBRr%uBL27CL#h9?__6XVK#N%=f+P@ABboO$GS+XnP~Ed;fRuM`G;o zW!|->uMnBo)kY0ng!72ZVy3X$z9u4oyz{MAm^!sS#(9#~ZxWPN(=K5}x-UGK5#no# zn1FT@GyiCTFl1(?J-FPx@yGVAJz&8273JT9+@Pci1gEkfmn2)G@nxG$oso#FC!dZ^iNz$Cs@?cGn(n@hPF@#? z@dHYbi|;&|qMR4&Q)`#Q!|(m@kWf`u{}*N_Bm72m#BF#vW>a}}4X%{CIa2#ZHc-J_ zPZDy5HY@`8X5WkC!a1+sw%WeVeDVyV^O&6OkE7Af)jDa~w*VPz@Arlo=O=%xAq}ib zrZn{=QNEq!Be1 z2!j=QMIvKqrMQe)s4k9{FsZHNQ1?Xn-kR03Ma4b(QvKU%_g7MAO;03y_*IWg59P6+ zK{)2ZB*yE9A*=MSE1ce+|5B7)kjr+8VF?L8RD88YBOs!GJ7$)KEq$Bmoh1A%_od=V z!d1L|OW5Mm*%)N4*jj6GAo-C0N=)$^VS)8I@=m;ptSxF*hJJU|0DF*sfBQxu>NFRu zUW7G%T;146rF?|e8n&{`o&@A&B3jXBZ^CMDP>xK|f#)qw9V~D2J;nspl7H9k+9Qe! z$VV;)+Ou~W+mE42V}_3)F35JA->%%~eM~nO`HdC4Rr-0mwg;hO`Vp=Z-Q_SDjLz36 zt&ZZlTU(!*K0X*;lxZdPy2K;00#g|lY%*CXXT_-P!@6@nD<6}SL+?KlYqLvMZmJqj zEeh&bTbyQBk>Qz-Uv|}NLLVVp7Cwtnt?m*f^gEpjnbuoIFkXu3()~#6ny2Lmu^M?a zMEUWB17A|?W9*XZ@c4(~agXi~Y*C0NI%l+43M#csFVHc5u{W*@ZPglp=ET;t6rs%1 z-wC|t6a8wm_WhfK2z=i?M#$}feoyfP*}65N99s!Y1LF{|50LZEw}&~P%Tp6SI(Dpx zalpfPm^6)fhd&nNkgK!Ty1QVjH&1!MFM7R<_w779$R9j-;6}$e$*G;j=e*KR55&YO zG0ZSC53c%J2Wog!H8gPdLV8Z7_k2eEAyk{X^|`t0gWidS3V(1QPlN&!a)1!$%0d2; z3s5fcH1lKFlq~O7n$&p}%vvmWS;qU=M9QwzhLE zf5{p=P9)(q$|Hxr$; zRvX_+2J>h9>G64RjW)i$d5DGT{v_E-QUELV2oc(W1)lGnUd;)-AVE#jdGW>2Kk)X# z#mbNr`QXwK<2)@VLeCXWe?su_f?xM_EK;RyrnAq%Pwn*P!xc&8`T64Vsi+l_6~!;l z5u#q$GxSE@Wqc)7Vb~dzeiN^RqZVfNeAHw$V9`21Qo4q>hAGT3CoTB-9XPhSbadoX zoY>`G*~qHTNDigx(2y!Cn$|N~M#E6uAVR!pb3uOAE3=#`R4dkjfuU)z?I{@PM_6zSH@b3oY~7#-ig(E+@Xhu;Bfgbas+F!Ib3%N26>Wmdp@jn zNYy**1?U&eGzeK3+KSt)xIS?a@#_a&F$;NhaaNykgpw7#N>n zD|8eQGMe`xu5SMqKiVP-O5&6G??(5vbWvKx??lqDD!wwlQe8l^Ch7frSyFSfqH$^Q z5#9Zpbx$4xTy0%Mm<@~e{qHgVXc3^xIPlADUne7oJ7D$_>eC zMOJMVF)2M<;qUR}Vb-);L4MVK)sF>l&>NaFD0@;yhod>CQ)Ss~MC8Vk+`p~;fUBmD zM`v5u;?mWnEXf_w7OGZ9bNPle1RXM?C~|2yJOSb04xye-`uJbS%$%s9Vs8zSz`N#= zF!UGPvj+LWLjsfP=$B*k)bHttu6MzL?)yhP3?JubvVS>^oWJD$nWDPIQe!&%=Q%9XRP z4s{@C{QI~xe06D91M}l{cAp$)`TjvOOD#IIR?&1X!{;EFNb(uRDR{lrTXrxzn(MOma8WGFz=e zL_M>3E%f1~7l#k(VXDpRAKzw#ia0_^?waUClQSPMC{`Vwy&M^Mzc^`)gExY#pN2&^ zi#Z#o6zgb?RD3V0o!{H&zDh=3oW050Oe>SUm27VPMi$=}`vtzK4z+Q`BmWh#V@vaK z(Dwid7!MB*Q1Y3Ah4(%47!y8?D;XyDwb?5Pa~eM;#!@UNzWj{B!xf;EQ9g9vxvnv_5Rs!iO@*YyC9@ z{&obpJqu~|r_b!(gUCdVq$u=-<4wBkfz~xy$kB@S=cxoiUlu2|`0-8tfW+p}CwwtN zg`5KK(Y2-i7BFLzmV2*) za-t`s>``tD-@>*P7ibBqzkbSjg4Sik95RuqSpgA7nu`#U(PT-mDR1Wgp2!tK7`heY zXzSkVXBK#|IoBR=W$BPpF3r|D*et&-JZz*WpXU@K&jdpuxRQstC9WgjcuNLc~WlT@6|}TezpST#L6V#-2OmmQAXcm5f9&_O~?h=;xsk-Ps)8 zs;t05o5U=v5^^|BMVx#nf4+S6cej=h%eRmDZ*j~f!ykzNq6{J3h@nklv1{tQ3B1ey zaDs7nGs-h#1*H9Dc#_{pVp+pZk?`lxNfK zgyJ)Z=i=c{ot>>0_Ppvo?&)%>W{#|V8^oXD<0vkfZgiWXKwQu*H#ypfE$&p_mkAn5 zqNn3D)8Nt-!&dv6$-JfXGghO86Bum5hBGr`BT_nB@D0{0&Ld#wIH0iYgoH_&{Pa;@#@HisvBsz8 z{JwUjYM)q%_9$~(2Wg@nueU~P_ls<`&oo*b3p?20k}TGcVL^;{L=2Lm<-c|z2POID zTA5AU!tqznA$p?fIsxOfP;{d@sezjGxVnH zB|b@Q3-{Q`8cC2iU(0UIduF09&kMcDB}<~n+zRZGPTlgIar`jj*ookX>Tla#EQe2v z^NvgDA{`vQAL&ZAcytIxCl)GCx+%&;z?dz8_zft)x_}~e)+2&@R*+}0PIX4MQ6_wL zi5ByUiJC)38ePIM$#O#y%D;+s%AGJ+qd%mmk9e6CQV$!M-l|rk|G+SO=zBHM^B?b; z0`PvD$A9dHp~ps-PiL0iUbpa@*t-}$+c<&L+zo$4%JyGsdTWT9;jb=G^ao)CCtDX; zMtu>gj-(f%ynB%0$wwlGp$Zi(ttJi}+k62vVC zAwGFS58&6ime;O5B}rBYZ|!=#xFLpYPK?y!cxVgu3kLmMO@0fG5I(ajYFAl#-K3KG zdgt^ceuyRvH8KvzDH%eL+79tU64X^}5)Yx%YNc`H@$4Ch=cu|g_hXjAt3QR9JBl`r zG^u$~`?-mwSiTCOPx)A0vAdPW)ecS&4ZLjYO@)}H^LA_YUEn!LeWqGEXNOb`C9S1w zEa4&O;*lzlRw3itPq^be$UpYy2BHKK!ZGUzXS<$uA2E~fpBih@_fYy~_F9Vgyyazy z_UU2m;*deJSbtS{eZFL&y8l{y_PW;BUl!cEKlK(PWkJbMiblgwgtF8v1SQRjHd0bF zoR@iadK1+Ul$!=qiz!g4g4df>*(GY07i8wOM>uD#a}$U=qEufc$3azxRKKxtkll*Ys0UqAn|ar$C`+e-RHhby$8&% zj$(V=dKW(h8Y82g?m`YV3?glJ`zf6vcf_Deb)$W6Y}pxkBa9Y^)gijNX@7By zm#1~ahS!Jj0@I{yy6fAwM1dc1>XmfK)f}TAtXOzeh=q}g} z3mSvO@i99b^T~nc>6hJ;H)P@Q!BAb@Z42%l3HHMwJ+4VQs7q^460iHo>~U-UDG%x@ z{zgvX%$xjasx9}}8eQ->n|Q|*F!rQGw_L}(3!1xNat4^`_ct3E#*zo=KH>$KGTFkQ@5 z)PimCbG042C0F)%y5GPzEqf&j*EN9Wj(6P9{(sU7%rI%)BbiSp{f*%cQG;bux%?@Swf3Lh`0j?oSkAI}e) zb0vka%Ziex@=^arRbAH-+=|KU|PS!NU<2l)<6Y`QI=3XZ!H23{ST&_b91E zVu$Fac+m@e6b4;PA%kZ(O3qxQ#b7G&IfZQ33_97&A@P}^m6BY7eFgc5WagNqNP@?* zJEd`Hmd?}p?s&+k`v_Z`E4+o?Zo+@gMat3pY7}cRLf@?6elUc{K8syAxysF*at$}PFMP8}`t8lF zf{&zj45x+o$g6fbv)P95*@L(|>cBmshM?EJywcPmh9e0xYZt>KQTvFh(4b9d(92dT=1@;5TUYL1O}MK663 zDvu!78rY?qL=c%8#Fo1lX7A-7-na3UCvF=;SmQi1d_UWWmoKK;xLuBS?PRa`!(Y^^ zV*p)5R*?)H26n2!v}Yv8E?2udc zi*JWUDgn=qykXFSLmlA=fTp6NiMgHpG$k2`rA!HZ#@(q)Lqb%!#{}yz}5g zYeBV+2RpFd*V^VvkU_9e*lx?hOtnLL67Vd6vlKtf(8R=omtw(HStD)v6f)vApFHRw zhtDcb80{-GF0n#qE(0r0gt|y@?9sG|kXA0Kx{|(mNTss-^<%6?ijYt94n3URHvt}* z8M5I_qaxeB0>K@I8v?iYLI_fIzRAa$mH94_nmm?-yV_y-vW8AZ)rBpBkt%A4Y|I1h zo54<@$OvTBZno*s5s)J#mh#!&a@o^7#`)c2y7A?ptnn|L2l*t!^A zBavltM~pigkzT{A04<_OS!7NjOn_e>u{UBS9YaF`Y1DGOLF?X8txbkioPG&;tf)a3dol7Vhq3_0EcNC?+;tT~ND_Bj)0NXU))z+8*;an3jYg zRuggLCRg7dJ{Mx`Vwo*8;Gxk?*sjyz#rHE|q&WxE8t>O>s}$J_JoZWgC6WIDbS5Wh zJkC}_N66monNrMbJGn48=#`A9E%l-Y?qm!Nnh10p;*Hiv^h`6+s+BU|gPN=?#YmiB zPREJ^qcV)znFf}&+h{w?Mi!~jlIKRNcLCVa(Muu=L6n`I?!H6|jn5|)L{{Yf3@r_l zS^Kk*Es_=lRQVKC_}oH5FedD@vig&sp0_eQ$@Ia=GR!ccd-e8w6-!YcUe$i-@DJN1 zYcMR0boXVYBlE@650fbj8j@5lQ5X_7mDgxlQpo=b1dnRw)uH#mX23X&Bl(w-$G1$@ zf1~Nrf69{oG3Z91Vyl%z5;h(K52Ym+MjT5cN31O522=KUz5!2K6Ztor0Pg;TJwvv? z9(h`5!tm2hfn;s>u@BUzM^wQpF~AQ|_fV z+F8`MlojJ_Rj2l4kZ}2CboZ(P_p8wzZ}3@xSJZry24^MS7uo1^=y>Ns!`QEPRUNqf zf3Gv|ua;u>?_iyKvl+j}$A+fgaep~7YaY5<4C!>@tT@LB^~i2}>a}+(gqa=k+GaJi zSGiaJv?9ay?Wo8sphgVIMR+-)aGsLO7xPS3JQDv=K%-BMFC|z^03NCBXZzo9{DAU= z#7+fB9gqM=m4Em_hacn>W;0as{hb5D*7zsJD8a;G8VO)81&tg zd|Rh6`!rbRy)r&0YQ>D3J!d_7Y_vndDZb22R$#?WKQ4Evb}(yo?Sa_1#R$G4r`cf& z){ol0c3(>zjJ(_COS8Amr!817tb{FNoK4PKOis;05kP%&2ig0|U9DDFJ@ylD_?~ z?rfXVa^cCix?&*B2Oh9EM=TkgglNnQ8j4Sq39I)yi_5W1UTBN!)9vY9cP@)>}Q{H*^Itt_*&8>H&K#kuw|~| zqd@FTb7besfDGnjY8Q1#gn7Z>;T+l-n0Ext$l!jBleHt#-uQac@#=$yKMP;$p?p44 z`MnhZr|x4zPN;G6fQ~_`nF?3@;_#cNGY=Q)qXAB20f5rNeb2x^nYUOB>U@l-~QX zA= zW7vm>*!@=|Y(-j(yz#AM%|@$!1eo_kqJ8OurD~F!3E};MZ?K|Y`yUj6JNBbK_B>yh zXeedKi_?@u7%el;GN<6J7$z@DSssQ_4nTh{Vp_V^y}u;7F;AdkEPFb@@%jgbRgC7Si|L)F4g52OwS ze)e~BaGu83uGfj{;KcZ4VIGC69_n7xCz#CaV$-s6(x8 zIpS5Q+sHfr!gjZ|m5QQP9q}&hZR0|=eEVXyk`IGBJW78|>zfBqy&8JN6viS?IQ2Xg zb7%`7e@EZ+yR6{6$qqn+9q{{r@=nf+pES`pKex7kciTS&FmQD|b0FSm6TlCusp4Wz zy@AcY44dee)WENR^BH!-raqz{IDuMheLc?|iP|nzwb_)xe;X$GKi%CClg6ZSYTo~8 zqj9KRyb@{fAk0z><=!!-HhJ6y!bRcP&Sa`D7V=#$#noiCs$ za^PIk?xOsnT$%-_V`wdW(7-ct1Q;R^V%c5kU>vo5VC2|Xd4JaXMTyR8bLERQKr!nX zO**zh@NVTM%7g2qY#R8z^)2bn*bPY_rsGzD7An|70U@FPE-w6d`1lkYMC=z>F{)M$ z(vR?(wO(*w5BabopmX)~?)lLTC}yW)-wjx0mStg_j9yuIV23MV)UQ01 zY2BGR_s&n>=lN-nCm4Yef?1IFFPh;kKuId*e0gnb3_h%s)ac%h=kCF@ce^x*l zk1MSHT3&y}N1oV}w_zQ9>LYnTxAUT`I!1WXNtR@_<8yK&cs)cHZZO)LkTBPU07PqDn|`a8b@yEFH%&na^?xhz)N z0~+scufBp5%1Kl}Oe_KmmzYY-1T)#on)gv8j?4V--s4uKNQy4w))TxK6<92}JSBi$ zn&%zrqG*gtbO;GO+Yo4Sp5c$ho!}nXO|Bl27|t%6hB+-F3#Nxel1sk8lYC=z{aAy6 z(jc=wCjV@leSPJQf`cx)OY(PaVhx6g7;$x8-7PHG6u*PSqyuTL)q*$jXc8G@&dFcP z@D!s_d`;jr*17voe*icleXG}*1Qu^YH;5kee9vAr;Jlx;h>?-hMRQ|%N}e3DWzgTc zwUL1PDCU0~@PN4RoX!c2RjSI3s>>8?(+$m5HAStmJ`|TH>t}8KN>{YFmj{?9U@JEI zqQPw4T-#f*e@|XOz;YEykf(D$RR%HOTxUc^Wm~0&KUf}+*r8&hPjhfM2OubZF)`eg zGu9iijR1Lokqq(n^E`@-^WHq4v`<>WFQ9uW&+4eaZY`LZvh}P|N>^t)YdFoYt4UaN zorbaG_4fPzDGXH(aWfJ}W2uvnuvu5>mc>9*$2MW?Px4Qu-^WP%Bepg36u7)4dGbLrZHAD#ZIE4h)-a#x|49kwz7d+=Wp5%5{+^ z@ilE9g~=qf93SHA2|m;-{6OB9Hd+`WTp9{o@ql_5=%7mh?NY!&NYqM6P5m-C86Ps! zNKJa>?bBk4rC*-RgF+N8Czf>8>?yU%ppLmI2#Tj208g$DG;ii9Eu$ek#sw4GNhiRx zgL2PDO3M?9(vvSS5!!a(&J5j9P>1WmzL1w2J|3#ulEmOA<%tfn=BC9go3YYG#tMrE z=sC6}C2cDg1)Aj~Hv4C`7X&&}apcyu(+-{mwon;H?MMb$DIsrYt=w+oU8MhZGfSTO zgj%Vs|Cqd+dPR1Ek8LPqNM}V+!%kI^awh4!rEyl-IGT|4mkmqqNb-w(u0^ndWET0e zi%hwv1|Q3E{M*YPpa^obhHBX???!Du1681EKoFz&a&vW-uT?@-4RAgHU*kj-;A^$& zb4;0|mDS;T_kOlFnnU`Q>H)oM$tC?0!~$q#)ftV-iD5{Bj1t@)B0#=F9BDXbwLT3j5~%d!r6~h zi`H*q$QGxssu;7IfQhSsDq~bfxb>2)#?wa|&12nvI^e`rP~}z_^|uFxkE+(OmVQ#7jI7tE`6$B8FEMyYNlc=ZcM-(=bSX0T7OTJ(7qmng?e zZ)2mzx&|)TT&anP_KI$xLG4qc=kXTC{P(#tdR1$h!Otp;>V5>jdiz>uZ_6 z9bYS*vz^yGB#Z$HKY2Q^)C*E!{66yU>|V&Yvb_^XJdeRitg6E(FMidAXQCF``@H6I zgE?(D6F;Vb-Y{xIa)+W4o!@H)i6)Z`DzZyHR4VtTv9&iJQ#ZuPvZ8gVin)7pxcVt` zj8uf1HG!W&<;` ze{2#`3=0<*Vq6l2o)9eD>Y%&8k+Crv&6h)5A@_qr_zP2q7z#Yv%d6NlweMLwlGkgl z$X7O}h2GK?7%)|Zz!qj&Y}zL?ZC2(2miMAemfoo^lk7l4)} zQ6_HV3C$y9_5oQ;!Iab8Q^z*=yHyFI)#RD^;Zhv8anjMPBCuqUE4o-Ewmq6QlEFeH z8J+J!>szjNKKL=%z8nErwsGYbTA)s1qdnsjIjRWHmvYK>S8)3rA;*dlHC8(5_Fg3C zk82oZXqxIhR9$uriS_mCuGlTVLWQ^hcEkHkt*wOM_N$tGQK3iV%L{}IK{+WTBm}1I z+7D<^+J&EfdPrcs$A*PkTt~_&-Ai}}$5yBi0ld5bX`8D@GYR!Zyr3hb?K=3^y0kzm zB=MbmnCw^rwD}L+z#GF#0iu|K&sF99n=*3@=-HjrQ$9enhDhIWSSIhcOq#ydc?$KW z4vr*e5RJQ3sW{1mxG;LY*N`H9Rnf+rHeMOl&kdQ2Z)-u)`|i`!Y^;cMZB+*I2r&(c z@3D<(Pu|(BjH~Pad*oOZN26Gt+^75gJ**5EtFcW{<*WrqM1XA3y=Q~qjeb)AQ%Ff1 zXH7(CZc-UJg0l^h0N`W?-fk;!)4dW+Co4w(R?g1`Y?Vm`N=iz=4h;kKHJ0VBt1@1l z^P-%o$tJ2#3R`kt5qb}pZUl`2NN{!PEMUV-r8fb=+`o2Q5X#f>gT`GAZ;RZ`a2EPD zS!=#vr$W^=Dd4#Ta?6iQB*B4q&5*$pwB=!sw?xZ>Av&ZAIj6i#R z&xiKy+5YqVuX5lRIs^NJFW1*_pv1hbY*?bhQWqPo&|Ms=t*wpIULX;#dV&%u)#5E6 zAAF1dj#0C z37=DBm(RksuzYtU{p{)MNqU5!QQOuB)m9CCLi65j4ll;|R^ki}gvX|^=C>tp2PnqL zK2+@+UmSn!X1oQwGz~0>$!v7v1Og?v-2AlOIG5#-zTDMKV8Jr~a)b7al*f0VfeBcP zKxx&d#rZe9Nv+MSzo!Qq6bAxK>;9>iWrq7e_%f>bd6LiUsml9G}_brKX=#Nt=S@#3DAV;k;Z!h{huTgO zxP_ey_0Hc7*w4LzD0Sp>P#sWhCT#p^Qq@PNac({JWWf~Cj!8{Y=}rE~91^docoKiS zwxQ~WdWYD(H&mB9#I@Lxh(LFLQ{A2$&X%a(Wryv!;K!>X+>^ihO*U1rOAa}WWWuoR14EQVLjncXEbnw3GZpfGkuWI*sMQ2+DgbP-Q|Rb$7J~wGZcJzMDdwY7 z3E?r1%zT=WvNbx3WbYsE85@oUZP2dZ&3XevZ!mAVu;+vQ7Z0I&X1$^$kAKHO?}*ms z)fEHjQ&6%<>h-+0sSfPn$oRO`c$W05IwPFE6iF5F-)C0}afuVkCzU#|wJ*4gJzxIA z{q*|43=H|vddH2`2=b=5DNwOnOx~c>t98`y9fc|K)<~{mhSHQ^5tD88CVaFOwn3_} z%E`HiAwg!XN=oe<{E9G5KXt$wxr&YH*N(PXf=+A96oMLJ}vm=>)bs4R78YH?5PViF(l}tXDY(o?Gbd|?# z--2=6xNH)kWns_c@t-7?B$3A#2y%8jK&H^Z94Tx@i&W<;bYTAy(i)rlFciVR(zSNc z8Mvhb6L(;M=DwjY^AW5FAT-@jpnqi3;i|>hDDcOt>kA(ZF(lOxoI-RWoGxSb#xmz5wCulUR zXHS&grJJ_1VqAh_tV%TA>3RJ!Xt;aJR=;vMJ1y;Ffeyl3?X)xzN2a{wZ@@}$DYn`? zO?UufY8Rq9L9R!S6E>oE^yM1LV&;eu8`Ibt=Qs)D5D~6McWS)qwET_l-M6H z&&I8?hTIoVTpwg|5?~hw16P1ttEvhtWI&L~F{+SIf*7T^ORW8w71oZ>?Un3y|lnL#ONk znyjqBE-o0LKl5_v=ttUDc7rsbqb6*t($Db6S|y2#!*xGLA;|tu=l;VrEMiP)qqaT0 zDdV)3OhYgCHj)#yri^wj1Z4W&hyvaY-P6V@n^|w1ENb6MKc}Ijn{(E(Bf9mJWB=Bj zqPH=AZ%g`=MdC#wF8r6jNq9&F%Em7k!cfFJ>CnX#I()6B7!Y{-JWN4I&I57D!(+18 zZQ9`>IHZbbtq?5&P-y{xwy+KHc|L50hE~j_2doCX`oOjfNVb7b*|cCMX9u25nBz8| zN`!g6S1vQjTEc_^rLjHa*?XG>K8#rJVr=OW4iDodWCrCH>s?=V>PM9c_K~49Q~Xj7 z!ZEZa7u7L);jzcz{{KQV;(6bK zr0OAKi>uAAR)7gYm?mY(<*+qV^nk^%a)eD=wwER9Mrd;dozBgyi)3AVqZ!RgI65-I zlss$+M(^Zemz#l({O0E7dx03?1^b4@LWW5&Wv8pk$Szl-uGPh%7H?gpw*6?wFY5&k zej30P0jD$A5Zu9_-UJjaHfPj?(&5izw1<)=$}{dHPe-+hq_m1$(=bT+o?*R0+@8b7 zUKXcCQBT6$kqrICdJ^rz5T=Kz`}M484sVHqE9T3dz7*L1{pTlPm_WMePe%sNo$Gf) zePlAP!FO7a^hodpB`L_b z=W}#P2EXs-$sYEym*HE>vKaD&&8FEZ&?Soz`4XxwZW0mP%7g-%)9=_pJKxY$^T0$j&kLSxn&bdN^; z@v|q3i@;VqU@QEAc)-;3lUye?R7F>(@Zygx1f@DwGPRwu_XBQE%s!PKup(vw`s!@Y z4GPZ@5td`@E@8rm+{85O!`j_x<|8V*X>AN~$M-)y0aQRQSNM~S?Ck>vSE+Zy=Re9p zIoML8A}*z&_+Aa7KAI$rH69l;W)XZqXEHy&0j#caF&>T|#}035f@)`#-y7w|x`Sg= zMrv54q{KQfNMVF8QzIHX>9hNDHJd0M=-0YYX~<$~)+FaS;)l`fZ<@CU&T|DVS%vHCJ$1M8gRm{vw!lo)C2JZnyvhNyWsoXeyz+YEdmybi zzWeXGWH_ykNR`RJCM=D>?)EihX-KeOH})N%81ubT#22_$7yn2*?PdbTO@CS-FLEU_f-KxYN_pL6$1p#d;YP$?~9G~4eO9u*kF>L%&Cvq<;)vwm1 zF4mXjt*jzr}&%S{#?$u*_QpmTgFM>&kiP>qr zVBhUCZdN)>;8i&n+kXlT_h=$qtX~sH3T(%b4qWO~l60tOGvj*-S~E^I!UjbWp(Es^ z>~ljK@ukow*(cdGNZryj9nZ})6|4Lgm>TGBO%R5JHc9J=4xFPD=BB=J4F1f+YS{t4 zy=Uu+(5Q=Ius}tdt%Jrq-TLP3>@O}a!a4)2sL^*|JYJ%Nwc(N^MhW#mpXz#1y_920MO zU53o4wW7`S5YzpC=NKvy)2KrIOftJtR>uFWSOzkTA0xqINT46{<>Km1*M^Olp$1QT zT2~4!1kP%FNeEXIJry>&o;C68b@ti+PW@sM?@5?EW-c~KX*_niI9&OjcA6QWs~L8nssAL#Of1@@`^YsP`D9aG{BFwq6cImjkH6a$EpSg60X4{y&0U|CIK9Un^!Q(cgiljgLfq(q6AgY=tE-(0q|Jc^?VZdEAgb`% zDQ^>hTo|hMP-kDIGy1QFIO1e8GRJyx61(6Ox836&Mn?Yje?))C#bKdj)WWidj*|Ybv@B7N) z?W@z>y^U}>oXHbJW7eZ*-m#Xyz6LCaHn#lb15*#QdLyx-DI6b@w?UOY#-2VNp9nKm z!{s9ihh%7+yL<`@%ec|%o(P6!Hou>zZ!S6*{DB#1ks+lE01iazLZip&v07z6_b@zw zIJ>+CRi!_g$#bcHFF7G<3f{js>D2yC zvD}8oSjrDU1Ks+lPdA@T9f7I5b9D|W7)H*HgMbS-fjs~%myYCuz4@?nGfC|8KWU`E~;SfQwjQ|rLcL8qok2aLf7KPs@sgwG8V=hr;I^)roxQ4aEZ|q?BaoQ z+Un@y=b-#l{*l#OHL#!)R=m+z1Y|-f4yLi$y8r4{k3a6DWJWFPjj^k+?KR!F!)m`I zMIJ&n^yTELw-66)4sS)cW(JvJ&-!QBH-oQ335XmAJ3s_%Y-|8iVbuSySeZ?f=V}Vt zNVf{-+gv;@ed$!XNIk3?ZT*iSCYimz^YmBm7z3>BU4Pbn9<6@4NzJVC=&(9D_5uMh z;s&3aTG6{>d1T2^g-JBC_u&}?`m{%}VI2${mW6fy;j0Nl8KEnfXf~GAGRWxW;jz^V zscN$EwElyE@h)NHy-&4eUnd;g$P6TX0H$FAYyGdUGIDceI!*Yo^4}pn$x{V--lk*# zO6`s|RAZ28Zh>+xnf296rEaS>=IP)bn8$NVg;mPZ+WwooLEg1uuXLuZPF6z(XH9AN?R9H>3M~D2$YV z|2-;;&-lQZkFLy$LcbfavU?>%bx~aB0HBHTHN$mIfT$lo2eR$lkr_RECdw zOc@08HS7p`@?w}#*%|zmip<`eTe3+mN}Bq6dtXZ(t@6dLN{vajFVbKPkq~1F~99-FS z#A5_0E!COw27LWYNuiRLS=?cOo>wN ziTA?XaU!}$PWqT*Ze$?V^gL4=hiz1_OS9fpO@0bxy18V0H z%oVpca?|%0kXw@AaqdGS*vk2}_dMmlJ>#RvX(5KQTsj*O7VtbcQ^3Sas)du`a#qP( zHA?ynQT=`fge{iaHKFG8UI0?D85 z+YwRl1!RUB%^m@{^ni$=9dbfA^!h$iNgeaHa#KC&U7Vsdc#x%$lL&l*tQw}Z#sBfk za`d@~yI=WZ&JM0IL+)ERGNjZYy1F^JUoxDIB|zy1eH0iyP=v>riG$V8Ey)R9S5%i2 z6T$)zu7Us=oB8Ts7FlWXpC*NWm)MxUZ~}=7ixL6azLavx+?Uq)S4k480o6nj@4&<_t*cJ`V2nK6hiZjf%pAP5GYebZ9^ zg*3<|A2P5ZTCfIpV>+c{zt?m{%Vn0e=IZgnU+DU4+YQ=C1B62u zRv#SO7h@-qTusJqqizbu23KFC-*h=8t2@4`E=a!ChgiR@!PN&^5>U8uxMdD-4a%G7ZQGKS~Qph@@+v9!D%TR5Mn( zJItvDk_!MA{(MP~PYTb5-GOwk*sR}AeDEowQdUeec|k?Ysz8E6dG8NncdH(3aI_YDTA0f5(xPKdFySq#`c$&K|V{!@W%=j{7fY(WN(r+ z%2-N7vEh`=ff$=KYLtl|gf#^E?Jgyps6!3vbi;c*OfJ7IzHVc_^@==9(}7a;>_Ku{ z6>i#JEG%nI4ZyyN1~->-?OQ$Bv@nCYAEn+D888leV?qI;)%tKqY=Yd^w`a z(&-P)8wGF#V6EL&L9R>2nF|H1SV)&8MQ$g{9J?g>NtZbTC4bys`>3fo*?d0wyQQO0 zk3&LZ@!zq3IZ7s2Q#8gIokjABl6@Zs3nM}4)>nO~gVBK#hLM@do-zdaVhaCsAHo%u zKRg5p+4g>2&y_X^R1tEY@Joh-u4v)cc1#!rC3ighF+Bg`)?2CV6?-EJkvN*pIpC z9V&aHhbmw2L z?uxewZx(zna*3(M0GEdiAwaewCkRc{8Ug-9dDfH~1_@u5Gq+r1X8I+MYwNgEEp7mW8=TO8^Bz^Mh~!6fZU*o z0nxCpS^r8FOO5-dk~d257rzP5Em^8i4NI}y?!X|7FK@-`2&FgA*kNd8c1E zylRL@lTp{0v{ZPhZOBr1bZu076yIML&RHto52W`8;DtWGF4y(qT;@~rV)rLX41<~u zdFHcai0EUuABKiJ0`jF7?|>LGkALdgP7y-4PWO`Hg+2;>9mF3+pMtq7xM!1Y&vybA zX^M_t?0T;Yj3_3MAq?!o?8CE5YzP9Sg)pSD!GbbR3@;=xHzGutpobdyk{9)?G$_vU zl?fkR`D%a$2DC4bvV%VSuQ}37M*oA3m$whsPlKJF+c-5b-Wp&~pFooFlW~&!E>g$) z>hds`Ek~$2<=AJ?+hR8$q+05z zU{jRIqAfFvr^%8{=!mI|S_4i~DqRH-Bu0ewNDE+KaAK@SfMB+W=z9rvF`VyUdvviV z5gIt14eZ7PXr-b<3cK20{9y;5j(4lC8FYGmp`EEPsZIJdaLme-w2?+yWMUq*rdnm3 zNW)WYzmH*jXzGNDHFzjC&hqf)KmieEJIGU_P@w{5SJT}V zYx1?`Q1W7uPT(R)`j|IO3`0yxnf9ai?X%$d2ult_{4e`0qQdMjie_4ytfPtK{^f}L zh=Vf>ZWB=qxEqA5N+1FU-iz-_MY7~B5cGXyJdVIpq{9;fu@R2nT|3tgNUS#tux<#UY%IZ9hC}= zhSa2O275N3@;)mTVhPEEeG^n8UZ*guN!8b7$w8H^%sO5mC|>s>qdnRP3BNagTrzC4&60LN$OzK;7JdT0-VLx7eN>BaJaANhxTi6s69OGiA(Aboo6?^W*P$zC2aF{^0fRw! ztZ>y##ioo*{!#KsM)5&ATWD03`sc<#37ZSo{whIZ=l-&zdtzi^B$2 zMJ)JLl|qFs5Mu0V2b8aJ#;ZKYA?u*!%EqSA%FS}TReYJ~!Wq0Q)u!@;`xryxhB_lB zQ|=3Xx^2LtkPlhX9}+Simk^1i8%z1U_k23)JJtB9h`m59Lg{@9nmDTJ>kp`^8LwGa z)TX!;CN<7a)+mafsZzKK@X^X;-T)q`?O#{1vhrJCv<6EcyB6ppkteWnQk(x8^Do`0L4zLZJ?;0y9wv)FRZVWB2aMInr?iKjoDHnMmhAoa8j!iN4 z9T}e`L2`3D3-LU5vA(^kxx^24^dyTV6_bFwX8+ji2V*qt{UKMx&7H@CtayGL-#*m8{-HoNBge$xjQ_$)s5X4on}szb8lR#0V>400p61`C z`0%fu?J+=(k|m_dk`H96fVG}*BIK|o`tGii!AGWRuv#jJeIEl;kxRPc$j}(_0IXpT zQ!`kE(eNvNyT1=P*|k)(m-EFfgeTrEO+61L5#ne>di zQ};Csv8t_!)MhHyQGK|^_8+;fKq-_K4+<00FHZnR+|p&? z{+T}#R^yOvc?=^GVs&ZcoQB`Dtmgf*xkkK1Q!U|5HS%JzA9#~D^F7ptjVBAnYh6OL z0pj?4MSgK`0{Hanfdt;Qns0C_!n|u%s&QV@i>rtKd>FnVg08!O0LfA~mI|BkBh^a8 z>wFDw)xZ9YnvU$xvAJ3(YO_L5Dhj*6<5}XMs1| zhY~Gyeft5EEJ7(k)seVEWo>>@+1$q?(J&I#k8eM)Pq$6)JX2$6X*4b8VM^Q@l6-0l z`K8-5)ehU_6^SvJG*we@ApdiAjT(FdyA-+SOmG~P>o-jJ@uv_IE{Ne<9cyvQ1+6Hz zCl(F;73bG5YICD(PQ_f$UET7`MnK=1qOdGu5K3vAT-?ms;7hdml7jSdej9S3d@ z+%I!8P4(e-HGF7`xSpJR&Ih;8_cIK8!iJP7Y?>0tPm!4Xw`eC+Ai!>)aH`JY-0(Fa zPMw+Fz1~DEbd~_Yo;>0mr-GSL-Aw;mjwabI57nrI2qeZRb-w+q_Z?`uw^%Eh`uk(Z z)j86JSCx>~NtIku4goFn)BR0`%Fr1w{(6-H7$4kaw<8EwN^iE^KLb3bm4y&&0gWG) zLN&WP%;O$bne{tu38L9N@`-n#Mt~dkZSS9enkib$O{84srYjDJYwV=Kqv=AN%8wbb z@u0@cdV84Sz5rF>+$`wZtqqSHrsMib`a=K%o|aJiyhZ281J!{ff>tyxKS1uOY^)Q? z(H?H* zO;^S*6ZD^p34uPA=vRDd8)`j&@dsRsX|j{t zAgPv2Cq9>hUT&k6s0>Xk^i(ouVPkh+5$rGh0Vj$eIwo5~&)t|p&yFU|b8t_Rsji(Q z6@GTJxr3A`D{yZSbE_WP7_WJGx}VZ1rZ>;*r%rf(X?(s2B{WjdB(Dt7np_jzTvHG) zGJwI|u3lY`96bC)s1YYSC{|9gunlo<=d20<(lk9Y^EF3WAGlwU43LS5)q1(+tL0K+ z=fQSQl~iM7iNh2pd45Z8sql)r7j!Q7tEZkNDiy3-SSB;#lT52s!8yny#1jnTAp^Sm^ekhiz+Wys*i zf;DF$Fa3>G=@!r5luTIBVdp)3a>w!3d1U~poisQgYw!s`>JhV~ZjTLo_I$JGYW<~P zE@$itPL4(N4FrnRgV(6&Tiz!dzjwU!j#&eB)NkYIpPg$pwV%xICF(v(Jm5#MwX*Lz zH6>_bnbZYsPBtv}g(57R=;SyjJ2^V;3t)ze@S`vFnvt+WxT{*b;HGCj`d#00jj%Bj zDns60V36|0aZ?y;RSu{3x5v-B72TeJF8c>qys7PvH%@ z&9JjwkdmAFeryeRM-dC)V!?+Ijf0>rHuIA%^f*XE4xZ9 zyQ{``kLhrNixrd*y0Z_7I|E;gSn{>F^)F@}n};)fawBDmLV(Zg(+A5T;!N`O6Cbxv zN{Bsop%=udr4r%pnP@+pxPDIY9_3akp*!-B^-H9~CV{sAL@l-Ln@?+I79`Ih!!tdQ zZREu#ypR@JRUE0RH`?x~WR}gppu_bIcFcK&u8b{Zgjl7?CC*{0GPeR*L1(?j4U(53qsJow!9Pz&pFtM?crJr9Rj4IFHbBsE#;Ou6oK)flp$g-+O{ z{eFWPk5ur8YfNsYL7z;94dqub`+Hb=2yL%9U)bki?w15+VV|Gfb4iJR>ak&qVe6q& zq`|a*DYyMTbz*V*j@_$*VS3Q&zFer=i+j1Vxw+XhNcDTn#ePk-QK0_ab>OjoH{v6c zu`s>Y!R~7AVt|%av^2%4Vq-}djAuyAVlPxwAdc^0)ucVoD`#Hp`&rfeLs|Dsx1iBs zQDx=fkDR*`u2rAQg<0cW`q>hZG!%EC0q2X`O`Sr7*uxbB4v~=zz))jr)hhcaA?RP1 z`sSFo?d`U&t#=ICZyVp5ScX4TDpV)}rq2f*GCYS)P6N7EevKg9=;cFcFr0U1UMjtK z(wukhuYtU7#^uahA6|N%t=g7H##elzTvh2>@@4z*`1aY)B>HH-QM;yI*VgZEK0DXX z99$UV6AZm+3IYM(UN~&+lLTBwPRJmv5OlDxor|mux61YbeF>+9`b8V**zleS) zW|re_FQnJ5Cl=MNgLLNx>9TdJ%NsC2zHEmC;_AB9Wnk*1g?0b9H*U*@Gw+*rkAIUq z!MK?v^ViXixw)|RcJ7ig?J?&cRwHIzvx+X~jp~{goSBCu?R5&b z>osK$YHQOi-5z(!y|VO);0UQ;K?3@l)s>LWJCqw*&ZpSZko^o`&^ z_fkJ}NJBrh&90kuIubr|udV#NRC35dNJQz$jw zFFGQhuV113G?EmjkYZ_{Z{7(eWnt|jwbZyoldFm~%7$vBbsUcS-?&X_N3&GkejbE- z2r(UF69>2RJHDGfIh;nE!wY1LQ=@V%%YR!)SUdSrl-nn3$2?sBT*1?Xj`mFlEKUnD z$&XKjAVUxhloLfH5d~rK=YIN$ir(~UgxeG9+WEAHUtr1jP)swjwZ3@Tdw)>)V4K<2 zFeecES&uEA9ut&$+wi#de0O7@FSXq^OF#su<(0j7u#01?la>5PiY)b0Wt>Pql~=(H z^Ak1-kb~*5J^k5$4XmL>e||2aus1X49pBF4DYR%tCL#J=SB|`Q;r;>?g_SJWXkjv{ zjCCa|ewJrxzm1 zQwP+@-uzRTQM&D(kdD^c9eZY`)hCcHvL4EF`S50@*o2sjYf0=?6#`eMa;2!x-1!hdm`BGt=H;RbioL7#? z7-D+!lqLu(IuAO!UKG`JhaR$tt-|mbnPID-Kyk&xI`mso1|Ir~oP5@%N4gTA(C!9(Wt57(Fl6ZD)n?^RO%UhMm9{=8TO6>_zs>Lns6+a`qm zYiY%jvc>paU&OQu8bwAVttIWK%Md;syHT&Wh-EN!-KQ{m*&e?=u)s%k)WRBn=i|U; zt4;cXzOt{Pq*JI=+K-vnHg%r6BK0nQcYK2I&B)59)tQ{;gBuQ}=#owYjp`ypcad4$ zftx=&XGVG3pal5*oDVHbVl|+{vlfN&oe6qh7%BbtMEfq#`iZ{d?v2^N7Lcn_76Y$j zr5!!-g>>y;QCxR=7>hl*w{~UdX$*S5zf}@CisuC14A7meNPz$)D*1L>*8`c!U!t9t ztaS_iCamdmEcLmt>lY9_6D!0y&6L&n1M0?_h)Dq14%JNEx2Dy%rqw}>_t-nZpE>*5 z{;udcpT*|Rq90m7a@z$*%Z92EH=lEBZeHnPL-P(fKeA&86X|*#^c*jWMPA+nA?xLO zh>bLSS&5$lo}?eETb&%0zE4cPL{_rJlQ0|?il-ue4h{Zs&Sdbmd{$7M3xDY2JK_5r zt$Ykw^NB#;h&!P*(Jt%`ARG!h<^1y)?QoEE{rn~LI^y7mthKCgx#|77=@O|=?~+8S z&e$Td^EZJfsj7Bn_!3cE@{Fnl+Cv)i%l4cFFUo3lSTA1n<|A8|jk~OfK=amJqZb#A z(Z7V73Kc>q=qY(vlk#CFWnq<#JU;hESzm>J1=v&DQ^0VA1gYNF56~O-(#d8MNv|l_ z_!xxxL&ab||E81t0Y1>8G}SX5F2)l>bjB9x|ww|Ewo~o?6S8qn0aH zkUU*+k(0;3j?xwC%Nney3*xeOxD7)}{g7^(S)Dfz0y1k>!#s?;adwWYs#ZuI@Q^MC z+iHW_YIn+pIB!7uhYEzVch0I_Fp6Zm`;Bq!)lkc^?PrGIpcoI;1@yfsD02W&4?ptd z+^}TYDVSlbk!~Z0gXYU1{)-zYe(WM_!eLFXZj2yeela=i_ZQcBNdDx(2&7A0$ayUe zNCnag+L;e|=R@C7vsH#(#}-@&>4@>$@VOj&_t{f!s}RYXMMsW<3j?0wMple`CiREghA&&~ptC9qaBBtjXo!Y3S?cf?=& zG3f8h=WAY$AJJaBJ(}~(tO;e3aBMPYRcDq~2U(5HV#rD0A+$;nNknJZbd7znqL}3A zvB5NCJ+D@2KV$Ovk$39NTm1;85AmhUpzz0E6b5`p)yiPn3hhbxX7`g|rG5E;4qMW_ z-FRA9fj{jc5)Wtd)Xsn_L>E@I>AT^x?<xAe#fSZq+iR+X*!BZpx~>9RjQ={USTi5T9F3?#^z#9|1xgGz|z(wJ}@O?xc_t_gO)A8|T! zFcif0_{>}q_F@L3>9xYh36&&&S{~%37By5S<1306B+1cC1bnVei6=n~VU^cL=l<|~ z&1auRT~Ys8ZJ&m=$C>xtuJnXYA=KIysobOr)omGl)C=YgR1;!ypBUfTNI?1mq8&tv|cvp3a>XSFEKB2y}Z_~P|f z$9YIQZk)mM^&Fb_=8)w$kIK`MF^ZmUl*6FEbMF4NDUnzYZ%@uTaG3m`OfgI)RUP`f zRm&p$T~E%AhA&hvf{IwG{oEf&d%WF(x}NP2$c?u5BAz-?S#~&`K;VkHtH|evQmh`h z*K#!)?j|QM;0r}jOUD=Zw}Wv#tgh%0IlJmwMNjO1?s~V}{i1>%ycYL&CRpv?LPem* zd&_-9_R3ofS>AiYDUUSTZnM=$IogNt^8CH!s35P70xYVKvQg3SC;SU@5V%%) z&DO=uzMcfW8I-D>-c(f2Y=A>CYHLDA`#l#n0qo`Z#f8iMf*?P~3v5Yx`M>)jb#Zv; z-iwBYhPuZjmy5%k(WQ#*BX}sAa|%YM9?JaPpfoC`{f4hvcA#VbdG!V1j_ce5He77V z<1W8Oz!j3W=%0}Zx$Z`}l&_XFSZcU>bea0G{@{kMyQd4UC+M4|TTW2GiIuN^oApWk zmZ`-e|HEWfo$k9Xlkg$_o3@$k(HzMB+^vuBRK?cSt6NcaqzM-bCkkA@Rg{);VE zh!W5>Q}_FZ|6#1>`0;Sr-Wbs2QrI0C`B)iy&lMXyKn6C#eBg3i&fhTOf(n5I6QoZ8-${@l$3onJN%X@ zEbJg)dZ)!8#Y8_JqIPj}xOC3}y-cHa>FyA#Z;t!Iq+THCxZzGjXZ90M(^ER{>ertq zI3^F*)2m5&xAeg$=Hfk9qAIZzwsE+B)M+yZvGUcoD``sN<((%94fl)oR0vt-8)-eh zf9S34eTL8?d@gA4eie!l!lo}0cq;(fW64%zjYx3h#^UKIpY%1YT;!;fB@|ZSvfoGx zK7GF>@dEX}HB?qrbLs4e$aaL7(L2e*IqbNi@#VoRcts2<$D>wr!^ zG2)d_)-V@UoXf_qXC%KT0_*efnEO{<=;Ws@xkVT-?kJ{IuOHqce8z`4$vGGOJ2C97 z9UHFZ=5FJhqdneiA@2mr+`Zp_GRmd9bUKk@zUO=k8@}L#999k^4t`{rMm4XyZ8nRB zy@Htb_;w8MWsRPQe67}fuLF|Kt_qQ>HFcdH9lnE%CWxsbVIX0zjhdYtsB~Q!IEcP- zt5!7g;t$$?XSRWK<%^Ljbmx8cemHHrHR}P_>D^)(IpotC)lr}a-v!eTq2D&>J5#Z* z^mNJnCGHJH0@yh;I1}PDy||@Yvyrsl?>aruv{B!1ZnAlD{h|taaYVD6aOg?#^2zGv z!;%wpqjX?8h^_-_ns;$ZTIm1C$!&KtKBH)$Qrz`BbZq%Q_P8Ts5@tl7If|pt{r1l>oYFq|%Xf@@$SwH?ys;J}bFZ?7<3CsocNv6|)rB}R zn_(?M1^x^ld)FaIap{E>JR8)zqG-m{3h$eqwV-rV_@uK_V zZ4Vn>a?vnS(|yY~(psKCFPv*#b;S4F>GIpq=>SYJs8M6%CYP?zdt3xD%g1y$&YpsX+Wd zD_fnhr1!*UBetg;zao3eU=wF6Go)>$t4A}fs`JWY(ceDwyAkG5 z(9s-wg_((rLL(BB?cj!CSo_Iu(FtBhpm+jUQI4tMvLVo!svc}Q-`ZS?LE!+|RToavnC77dPEWZanXX!XN zGCqfKz9x*@^0nbFsc`3P4KgNquNHy+!{rmx2QE;8oj00LYFqR@{f6=OMDOH^0<@CL zC(6XJhoq=DkVfEU_i;&eFMDwmM@YY1Nv*9;-Ed{+ZHb;gBGWe+7JuIBY&=aGBa;sH zyBjD~9OdN+i07}(BCo_F|93_IUD#4ZtX$sNSS|@fU9I|!Xra@csQVb|$DrGfwdh~S zuvaE|J1S@1kuXsfVD&}#ZPw`M-EMOB2{*bxst9Kt#A58)aFT~JsykKCy>jQnmiPXS zxcNn%hfx)8voA*G)6T# zj)6=o>E^GonzxxwZhdMqb%vJo_EkYQt3WiwAAq=@V5=yKnaclnMN3Qqsednu`lC3# zI{(gU5b#MlWZPjG=fExKss(qA_>o;*(=r))>6+&==Tp?a@WixEtRmritjWycX;_8M zA;z~KWXYXx9wk58ES*9(Ue;7Io^ClA5vKe}*>=gzn8ZPq!;MT0PC}l>#lC?h`ASO1 zBCg7tjX(^cU6-}d#k+V{9Y{=rRw%&@`#f`FxH>!C+n_ZwE}NFmnS)h`0*)8W=9t_o z32=pU3tNtmOp#1zGuu%1PL*vJ8NJqYldvoVHxs<}z}Z>EmKNk{iKXf9BZl$G;J!M( z5T4xxx2srCMbv+LXqqm!9=O7 z9JoG8O1O5@^%&PKtr8av#Y@?o)TF-FfNyAr4&Tm9lQbemytIKWfgbZO5cxH^npI4X z*q{FgP2P)he6f(5!D z`!p)>bh&R{=x8n0wDz~pfPL_BN28vX(BUBJm7$|#z-@%bYB{2y(JuA0lr!K_$8^Ax zjz4@I&X*H9E-8g4gWZ@~J01dl=WO@eb@$u9hnH_R_)&t%1$#|Ey)gy$7g^XpOf)J! z0#jOj0`d)*Iw3STj|zp$V87iBATa^_P8U~Z&-eD|(;H4T{Zu0RXl07ji%`=b(!m=P zs=evCbkmWwm7&aj?5&==NHK*NAh(Q0B~LV_(R%bbNYIfEp_h-h$}n^irT=Yd2pO4N zyE^w3`6NT6Lr$YLhyI$?E;HRH>g7 zaj?yc8^<8jwL+$PBMtyp)pvF}JRoQO@4>`@=RH*yNSW9i1bgB)-OqM9h_zMKO^DCG z&!i1gExc~1)9XVwe?baq?z(gdbCf(1EH=IKrO9a>KZZ=e9Ho86*%6~vO!dBqhdHLQ zGabRix@!Y1#=My0wTiMYQ9FXAPM%f+L8r1o?rZ0DzLQlH!M3x-=aei0+j$N)^xw-Z z%1S_m_$KVGuD?_nki@25VmRPt9%MrS!=X73SdL)@3j{|++Q4UDPMe zn^aSf&QP?zMhAGPP*9gRYh4oQaep`Qq$38n&4fx>dOyeh1?STLJX0JZ-mzc70Ij-f z>=EPGjo9!K-EV$`#Dx)}LSmfyUWYhwI;d~eRk=NB@8qX}?q*)gBPP~-mOsm)p9iEW z{|LK?R61kC1>}0f{Jk7_9J``DDgCTSV@N1l&<&0H;Sv_NaPLv)w-^!m5zX5ULyeq^ z_?(CNslf7hh1;w75nA)q+MLo%D%9kK-1xGsuWbjOLG&x-ppKh|jQ4FcVLnkYDaUsR zRqs=CEkqKb-aP!5$6&*+VYGF&As%H;Ce95q!d{G=?CEFH3GFIE86L*MaIJ@AVvPT4 z0WQ8CQ746SyRh>xq&YGisLt=L8T7mCPDRu)SG$?26A8lKz5!Vl(nNr=Pmne8`JAd? zKj5l9qh#~HbHcb4@+jj?|87a0)Z#@RSN&fG(-7Ys`UaBDYRrZ$DqM;jji)_TUAU0= zj=T2XIiOxz=3q(q^X)zL`!wrK{~zR^FZaFrSal9RAP>z4LQHZ|>h8089-ADmeH$|z zz4L!gXDoXCCywgsI_}{zuANYJZ=l$1nyRK(Z1RVeGt$&R^W5sJii39kWc~AV^{=om zKiQ^^I$yl_>$_~xU~-X5Bs!34o%^y!SWX`F_6(iGBbx7G`l#JHGjQ*rOQ|4 z$O5Zmg8ykf)X>e%JU=qxsiwTt4f&%73%vH6z^MT%a@h7k>}qEjgJ+A z|8T4BZD~2v$8TMrhgi+uO*l+`n>d+jP)E*)IA?qMSa%K!j*PMDM=T@wqNLUy-Pw0f zh{Lkg2wyTxL<6pQx=u?tDcjnoyUv@ury6tD`;BcX*cGiXBNj>*6)}6x*pHjIu*o4+ zW<~3HSRd#3E0&*lx?`i$#@!UPD~M0r@#Q}Ic~<`}nV}ZT4^@txcEjZveX)hwV1ra< zX>FbxpjAx9{xcaGQ`2j@HCd*ZLanL7?DH`j!$lqYG75b=-OW96ka!d_n!bM(G36cN zmQtT!x^fVMnDr#an(DJXl^dTp8+FaKz6k^lyfk;k_&k-ebHM5T&y>B@P~r8G@dK1< z64JKyPDHp7Cq&vuT-$z+ra^PM`~&j(@^$;p{jajh%GYOYbz;8>7<9dP;WZnZ{S1R} zQn-g&5z5-L%DRWQ?)QCl0-NmzT{mEi#S>XzHN8@B2i;J5I37>7QVAla%DhF5pjvDh{LZ@Kb~l~2U0V) z#J^ISpg}%+B{`OqE{%0ETmnQFkcHRAyo)+dJOY+sHaxw6Fabn-Ufn7z&VnzC^0L0? zf42|IHhTE%j<4QIwEIg@UY{!Bztq=^G2ydx%@_Q`74v-hkuvM)o$N zMd#I!TANYBI(fmxhzs83kWKsL@OzjTvJ`<(-UE*>*slAt2#VucOs|7Z_=8S<_K6?_ zM_bLZqf;BHhz$Cs1%)+ekD+4xNlx>)^dVJ?S#lVqPs-yf@GC?L_Xya8mXly(Zly^u zQF+Hv3wr^AANdsfrizaGzHNGJ_3iCDQ!(?0cRh2MQ>{e?9Mvn*4g&OYXcF*jh0xDv ztORReG|p;r7X}6Dw7CVM;pRfA1{;9OQ~bWJM_eno=os<5>092k_B1ab@Bn)UMq^5| zCq{yfKmPA}u)&`N>|7KYrlwt#$2B%rH!7%gta<1e2chU3T$df{RC)O9S*d|F?akpI zlD;bY?@s=YI~sy@wGRP?yma(L&gA-oOx`x7*TPZyO~+Pg7Fq3!Ar)fJIjc-9zldU? zICmJ4_hkSe!cZd}%T}d%KFKYrAeY6;t@u2_ID>jSVDFmu;CJ(EM$-J)c>__M$C*qv z3@owzKrg@0_D4Er_$@BfW2*eM8CZ}Q+SybmJ&GhN^9OjXN6Q@RImji1 zue;~S?bNvD&l~F^PxtsOo#et682mVijWAf;e-Zs9dc%7an4>T6^^vQj zk-N3C*UGP}4B5!9zf8z?7pApo)q|5{N4$aIZQ%Lq?9Z{mYr|Z`$l0nNINRRwxfSVS z)V0Y#Id1qc6QN$YW#_rB?pULW)`5gVK8`|VNjH{aOcLq%!*^~fU|IIbMQnr-YP5<% z57>s{>7(Z(ZpPi;ECmI`_e5gcvKh3*|KITtg3$KS(T?5~4kTP}%*E+usr$+7EgiCH zEY{?yKTw!dBNOd}lkfHSeTx?#jO70Maa^k^<)TE3=FC{2Dsn+A`=6^+vfq;8#eS74 zmFMnuSoZBosX$SsUt79p*Mk!hB@TRVeMb|Y8T@k)GO}r~m9*Gob4*tAm!od{S(=oc z4QvDvw1A4PWeL&ovuF)Ug&%vgMRoj(L8ik%#zsRy@7vX7P~v?0_`3`C@>!qSA*SWv_|zqC)aa6WK=}cE!=pte zs3z8^JEeZ<5S*fu50sciGPknrXs4yw%Qq+3moxmh7Omwqcj6*#SzY&uoqY$WsD(TG zi|?)zj}sZD^S;(a%759#Rg6PFe1wI=1-kJK^Al4m%QOFKzfpBvs(8GrJop z@zaq;$H^I;A;vAzv1e9S)s96se;V}IS0*(K1A=Z2#NE~u{iq$RPwRb(u}A-8q)OoP zgoe!7e-9`hAJETr#{zmLuF}|d!se-v+1&X7-h7nu1B56>skX;Rnm}&My;5l8#uA|_c=VhyvRpD z$xh$?e(l|3oBw_+w9vAHBC42>O4k$7D&^?a$;wyOqkl)*_k~W+vK-sCZr@Nte7D~; z+$K6p&Y)(}X%Stv)VwAfVs0v{Tdb=nQlGmE-uuI{C#q_K5;UdKVRdEGJnzvnaA!xO zK3}sMsDG~@Zpil3F`$siHCP6i&e)dy@Y&p?qHAeankl!XeonY*pL-#TXM&{vr~5*B z_iaWT#&sO#8Pl)fbaM6?b+H{QquXts60&o6gA;~(C^eOX!}8i`9RR#I5(R|8&xT(q zv?oIUolr`L3aTn$jbNxyzqlr);6|L-P2}AU{45l z1|MKkXrIOiR4Q4&iR>!~dv%{}uL!q#C~gbz?ShIeq3j7Vd?zTM?N%VsI4}pNDjvo$ z)C)0$U-VURa7?%N)pXiU(R$mhAmZcx-X8B@y-ytGFCYPpX?TVkDVJFMXMXD@uqbHe zzi$xV{$R#!vL%+F*KVS9#o&89LIm9-WF6hMCo1|cuw*ks2M>txiBE~7T5{S73|4vp-C_o1i^z>hu2s)s;{T@fOV!_O_^Co(>-=39YMT zhD=utXlOaAicjAwM4)e#le~U48s}JaKGP7MT6ZLRs5t&^c6H#wtKC>i-Tj*l*^+K5 z;ZAvmO4({}$`{V`Sf=GHdd%&%es>`cxU$-Y^><(Bf7kp`rNiRdgqL{K_lJ>j{&(w~ zK|Xyi=_o4NrI|Fj7lpG?r3lr%E>+hxr-GZ}Qk!{*i8M3T?UlEW2#c zrf>O^VSC`5k6N7(j6zjgdNOi0p~m=Pi%`6gt?&N6Yd*G?Lo(7E*Fj*-_i#=x(@>kZ zm&<+nrw8~dVs82fZG=`_DCYnFT42rPZGvJ@qjUK?Qq+Boug+)KzP_<>waZ%2a=vxq z*++-6Bz!qtRiwH-=VQOpe!D0FS{Y*<XC-E%OoCU zV?n#Jaocc=KfLr1!mr-L2C+)@YfVo$h8n(Ga@z(oe*0u!7t1yjI%_ci%#9#APf!ja z3Vtx71&_H+)%c*4@qS!>U4cYfY1p@()?%#X4%2J#*T!bbbT8rwh0a#O{|gLT!hMK$ zKAPS>2f#62z+GvUW3RKo_pR|VaMaCArV2y?pnC|aH>kTA)q ziI69hBKDpwliQ><+xkf?WV67y$ivP#sbThBKXqj+dg6{1pB3Vp8wpNZkWr%5y)2tZ z4`7Rty~m+Fg57&ZoLEOjs_cxV!WCToH(&JF7I?wJWvL$_5SueD&#yU@sJ}h8bH)X@ zM!zi;`v-QE$W7Pdj;)CXrS$dIy-4B?l8!R}&gmUN-K6+I-f4^fZuv@Rxw-|FmAxcg zhmX>p`^7I^Pc{Gj!5QdMbedeNM;hTMM)5zTy+tRc{rbb3fv~65+M^e#-Vamj|(^ zEZPdW>&nl_(s4NZ$*dSvXR03yTM?j*(uh!2K8e5PnD5!_O(m7|kVg@IfWVXsqd>WC z=-lK8Rp(dSha1ycHZ=9{AtvyWI%#%VOvBdtBE{c#ep$Y5f4|TmA@*Ro+ts1r0`atM z)r6kwKT%lxq@Q&J^n^srA z?8XA(kiwNTuaQVbACs}FE9M6yx^+Y5nMx@+`_IPo{wg#XFi!ZQ8m&{NR&@SO(rZ`UA16jZlH^C7cFqazW9zqi*5nGBjn(U3bM%Ih#ntIR_Um|!wB3?B)OEIGUkq3b z+P#u#e{Th3op>$Bq~#U^LE49O&1=yAs)6|ATbpzp0W91a~WegQrvZ3=UcAPn5f(Lh7GyDULHx)Ah{SO zP}Ep?j(v@4@EzS5Q810L!1`ky7W?g>JH9d`l(>^7 zq3xI$jE_G9g<02Jzic@uu*6Q-$gnqTSiXVfZ5*i^oW0IN{^{me*&p_k`EBx(`J@_} z2{OKhr21tl6ZWW?UyYH<)kLdC?`Op6eYyX!&(M##{#JVNPco;ID^m+x3*(a8*cx+h zqz7VCiRw2)%hr(dZ~kVX@ZJ)88-_GJWnNGs%E_WsF{H9D4Ab5NcFH~+#%_LKh4!qL zu9V-n!lTY%8r0*#g9`HIHy0nh%WQtOGmaaJtF$z2mFw}MGP%PptsHh@ROQR;sSkHn z#x)uqWcL;Hd3EkMEhNO~PLJ!7T?&hm5i+t(?_Jze=$G_aR&(?U4xW@P3MkKXeV49- z`Z$`=6?lP83!PtF1W`4dxMwDstdT%5;f`0Tu}=OY1V3Gbf#r2OUoo-c->)@qU9&o$ zIqx5Bt$$Fd*L!0(Kjv5O9ig-IDdr_tqbmf%6Ygp-cWL;uhjRR8r#r$xG1jU;p_Dgs z)(OF1A06ZDoE(byi~2T&AvVf|i*HYb=B{?joCfo1Vqq0+AnJL0v=h^*{& z8!*`Jih)cmc0S%Ah1`By@2v1SOBU@#{mNI#rNTX~^_D0x$3jVbM(#&%#jW%zp`^G@ z>*2dx?g{+vZjyZ=-*FuB)!K;9;hk5wN@&~Drv-k{5^+s z?taevtiY$mL!D3k`qyu8WrlK+%*G>? z6XX3zaFui07Y-+hX2I-u2i##4c=A_y@bU9-<~aDz#yQK1kus7*zI*AVu=u2A-d4>Z z94Zi-Sb=IdfEQm#8QRycHhh#*CzBvDPgqjAZ3k5zubL#<0Ymj*et&A2&~Kr``(2(l ziApI1*#%HKp|b%t>Q*9E3|Q51FQxuXEO7KfAg>T=CxaH2-x6n_W~uiYdi_`8H*Kv0 zZL}>7tA#R+=UC?;2DvY+1dWdcBN7$`80%n!T8=7kEm_8rf!V}{OI&#u- z1!|kyLRO}w74F4$Mz8a+V}MMA7&}Jl3bKHI9{L+7AbuBN6*mtCtV)V#;evTge*Jn^ zU=n6WZS}7biCnT#!~YhNFujgUYk-v){Cf|+)b|&+KYC^CjejhNy<5jL9V@z^Co~v~v#D^H zq4t@$o?13D2y5czWdU3zci)ZxI1LD*1|Jti*8k`VIB%I(i`jzq@xl9_ z+`g{I_}XAFjm(_Jcqu@YCN3hYO=S39F_luQde>1WyZLHy7*6h-7RwSn&7s;5>rGgQ zWUiqOp}6LbU!9z-AeC{{Q?GJjJi`-zrW7gSe*Jjs!`?zsPV}wC(4}4HhvL(ZT0ZW( z$`-cCI$|K!-wZ>1JBfIoU{xBAJ$CN(dR_Xwtql7`EjOwAS5<;uvzy^bNuYldt;2E- z%<%3D%8Mzj7g zz_Mluj|bJirF{s^vP~2W;?*;VM%&y+l4N9A2ng6N)AMhk}Pd^lUf`79&>&0fn}nDzb@@G1jf&= z27Ly(x!e3M{YCAB71{Jbg+bOK?QJ>`esl_-HPvl6BK!z^kRBVen#Lv(y8){|b!k76;r{7i`49 zP|^nf^ppFzy1Gi~F!jfiu?>Fx%BisaP>=oR&yw1&kCNZ4IM9pPO!8ZuV^g|fYkWVd ztQvibjeSUJ24nU}%cne^;VYdShTthAZ9SL&FMsYT6C?ee^&RNC=&e|37#eDodRfDFOa zjUSd&)@4|_%~81Y^68_^=&4(ir+u=Ac!sE`*mv6q>e=z#~t#W@9$7$qne^WRA2)T2S8%Fza=e_x3NZxg6cPOp`8d;t1wK5=hY)!av_tI~Vs?z2qv zmpLDZ+=c-|eS=d15oYp23f@j-%eOARvlj=otGn(J*bgA9?Nhn`+Y+ob^%pIl3jPrl z><4{jAi)3q8SgrptJY6vRmmKUfar)!>#Q-?Bqq;`qL2#XAk_(1DkDkQD~=!a}W@uNS?#+ zq}I^xoj-D#w#AJq^?dS8O{vznULE|gkQWY}Jz+80eX|w>%{g)k9l_z{p^ZhD#mh>0 z7@v^(J}>`m2L#D`&gEcM>#24ogMYs)%aHR&+{TQJF;OUG2kR-VzkkT$j-lV=iDTkk z_+fBLQ1wJVLa*WOFKg1(s*L?x*~bmha*H{AfkvPDDs|rKq3?|a=Z})8?8GqT&Fg^k ztf$)Wgx&R=hLUg}R3g{_{IkQ=-Y_&gS?WZv(>PgD5d!CAoy<#dO;9hnU(Ca5Qz`Xo z6wKWjds79)&yG1u(4zjph>5i2p5CI3s|_El79(A1O$JT1)){Wv^4Oi46q*XvSTv_#0U+53b?;YN*r@a0VS%|suzA;HGz z5H@eA9QD>3`ZPBx-PQ-XxiQi_EkWB=89pBQc5>8@Qa^O0>$(@u=+xys{?N7+=c!74 z{gTtw!nWo}xoVaGsfcItFT7(;{!*>=IV?5Hk>(}~x&wpqV!e0HoeEXGJoNSY5d^cK zPYxYECaw#U$D>7PIZN-%+a1$x2m1Q=cbtT=xnUn0Ob~ z=0=U8wOqMiqW*L~HLsBT%xdg>*N#gZg7oon>!4NX94J5HKANWzS_DGZJY>A>jJzl6 zZd%?}7PA$nQ<=ZV5T!!(&ifKHH+6@I#nTR#F9>`nQy0N!@F<6wm2{g>kI}Ss?8{}f zXLE0T>>1|^`rUL6J#Y0)y#MD(-tMh7W{#U|p_~6ggP@6C%yq}72CYA`-2PT)>es6- z>$0ezW@5D1?)diK620#$?(kH|O~R90oMIOTGJrJc}w=H_n6mfydSxo9$~hT^zO zwZ$};F@D95HLXYAmSYW&L~2aPlWBmAW~V${5*m|6T8RKI+q^fE!jIUw*WZlx6a=Oq zCyeWy$vtIiAN{KZV3;nQ;L|$crcxo;ji4RVtS9uFw@D~HRYVUAwzBm|z)Syuu;1l` z>+5Zedd7ZSxxm;-AUx8R8W5zxfkThyiP(0W?VPL#aLcr>T3+>j+Hra)Qg+QYZTSP=Dg0>dQ25u2 zpFG&EoxxsnhiSqzE*9SlE*I%?kx+5b1|ci#{d1127j&unO=JGr>T{a?m{4G(hS!~U ztQpYrNqWHfc)k>uhRZRQHg>#q_Q~e21^XJb6NFPQk&&6A%tnh6P}X>@Q|pfBO%mVj z1nzuT=1&@+ob4IqQKQfdbpZd~8n7R&>P^S6GnMu=fxoNSP1Q@BWXs*NMUm(jS#^^# z6@1)Iy!$Ss$Muk<^syuQF>Qdb-PV}^KSp-g$Jb?P85>p*wLQ?1Saz$-z6nJsI=T#;piGB1MM8G)X z<$IiC51YbK6Y2PBGbCXB%ZwtA$oBR?r}rxU3EByt;Pi&k(x~+QcA(*?R~P2xez~_0 z%}lsqLrs%!b{}=VZdw~;|63OuNs9c_f3ZKG`IpszjzIbBBeLf8)6I8gFEYxlk#PD= z-4|-Qy=}GR#Dp?Io^(?=!}Cx~ZOv&1fRKFL-k^de_=mZf2PH|b8r_h(U=rK9i4x|( zDw^i&%%h8rqHT-|vZ^`|qsT5Gv_Y-}oKaj}VVEAhs9}idPPx^=*xS4smX6Cg;7rAx z(M0$%)&-NLqTW+9^*I4l34ZUm`FeEI(DLmlkqY~(ED4GH&Pf*~VE$RjTOd!1Q$O|dBY|0wpMWjAs@m6!H?*cNQS#V*= z3Bl3P>Q)_4n`VIyS>D-Wcn!q}NwHYYo2RGvb3{3IdR$G+=49vnzj}QCat`D-FN|Qf z^EkV9Xj2TvHaLf@5xn8GBl{g1CoU%sqO7f)tiie3QAm zHznoK-1Hq9;>O0ukTTxrs~cFkSQ9;DRGBECT?H&7dL)&CIOH<-(_f112X}t`#N*<* zYRv>#y!x$SxztN^_eMnvJkW9pPB6CE^}pK#Xq1}##wWfX0MUAMY?rO_gs7$5JdN;H zJ<^;%Ga}02P*fsV&~O^riVBEsr+dc)P)z89*N!F=nh~t1N284z>IN9Ak&FVd!WfaV zOFBkWDN7CF3;Dvdrr7Scy4&wu3Ye`dI2N-4>i|02O$p$d_x3DFq3#Y`@?!ytx_aZ6 zpOuLTPmG-@Z`WJ1wjAjHp7+`g^V@7UECtR74!E9Ti1NdErH6r67*b{wSI~(|moB-} z*B>GDaQA%v??Kj@o;1_no>pFqYfo$kL8LY?tz1@6&Sy3(haYY@({Hg?ti3E zac9UtdcXhD11Re7Bj%KTE=|s*xaywtY{W6F0k=j0SP0m;%pE0C} zaj0>kWz!)o`lnw~*XxoUR3W#6C!%G^Ce?u)S3m- zOmr&e-Y~rDy$@QyRf(4~CWBVr9Yf1pL&ds+cfoGmzJEOfs6agzDSbv+$t zfdY?7?>1;krG4}PMkiHrr+DgvnM)i}c)7s=Ab&8QxPaPh@&~g&E z=vxB(luA=S#fG*(2F>snwKKdN+Y_iCSx39~q|$UxS81D{>QbzCr+rExeb8}7(woiS zjpFKCJ&r-BAk0|lmHN4b1F73a@|FsYUAYeyDGJg<$bg>)JaYJu2}qAE&u!B47AXT8 z2^-FTe!>NEt{*M&WwGruM?XGVI1j>~?5g8qF;3>#=#wu-Yx8p}DY#G>Du?j1J${mkaBQ#C2|Cah$T;WsAa3}@SZH;5WxrE99#4{&sk5V}p5Y`e+Nv81@q4^U zCsb(fJdNSNBC7$bl(ng&9!6j4!2{QKEgbS?+|=x>{giVxZu!ZsdYAc~fIM<76-aY= zo9Fs@@6~)46^ZSM8yL)KSoGC3hrnPk44xxp5QE?cTNcEJig3xW5AOY!jUZawxMC2_ z7`ULK?k>U3a9T7zvi5awykmVfu9{-%-s*yW9}l1KqIUasleP~}z24-`L=c-L7%c2A zGQ7C08Q1S=O878~8P%-*)o7gzQ!AcGO~Z^2i=b{d^Y5J*KsrIma=8MDmZptZ?lL$_ zaYOvc%{SABHK&4&GOmx-p-GPhr4kRz|?Lc7yeQJhI5a;N$-XBf3L%)#X zZU^6W#?Z{^22zjGo2Oqd++&t`J%}X=gjD|4XK@(wlA}q6n)8cm?58P=?72!|S}OZf zYbuuqg0v|$DT``~3)^`$7X6Mz+#oXn!Zg7|-c;l?Y*K?ecJlf7bzu3S@YX*`aeqvj z!vx!fI2h%#004)n9yAV@lgo>listH9*c>#0e27-H^g;Q{ZCi_5neIu~pWjYdLU}3z z)^7EHi~okEADSIzRsw zb7@t^^9ArTmm}gb9f~O=DP>T86za;!2OUAW%G~qHXV=!BA@TfcFj~R1QuEmqW^C>? zp7J-%Rp9}|7|f%DHtkfjr9RWVOFxan z4<-zck1E<63zALCd+Yt^yZB`U6cx*s>5f-iKXp)^9gU&6Any>$*$^eDv{gT zMr7NdUxTc@)`|Im!38dp0~S245-Z_|tD|DGyp}FO+yZX++~;)AM*Z5&wKcOm_vf8+ zf}tJ3>&JudEGz@QpPOii;!EHiWNuh$P&*NcbLO=#c=6$@a%dt}88J-lh2$C9jfKPC zf+DSzM}=K}Z){9?gOZVqrZowVkJQLK`6y>oVXR7ADqJ%xg7wzPfNU{U$D03P)<1H> zx)F0?bQ3;^PmL3zK;*e8kr(ZHU5nt?B>d1yBRNZvbBj`zMZ^4*PzO zW$N363O-tjQw`Z3TD4j)b=8X&>R8;fCD_?!J1f06FVo!nX`P-ODsr9Aem&r%$2%_< zp}%UBl9-0Rp1+Pyun9G|wC)Sy0^c~%yck;G(V-u9w~|>6Z@+m6R_#a_N7>4O@P@UFT(ceFemXjLZwl8=1g>GMcsZnw&o*9#dsv_$R~y)VMp5v|CNIP7=g|la*3GySu%<8 zJl$YMK|v`@KS%>t?=15XHW$dTFHtSKhGSudhCWYdd!FjI_G>m2(l)oCs0*#OyNKqc zok1!(cjms(`@A3JQCXti2uK+$)_;|)VYZ}oc}SlF8-|#L4fSnes^Bw1F8*L1rcMio zfwb}b5BI8I>PnOfT$H~%@$(vAtwfVgFhs#&74AYnJOw~uRpy?azqL8m_Tr1_cy-P@ zE$syA_@-LqNOabxrvsiG%MEwWbg*+}Gm0G2PEaSpGKmWy!wO9Kd=tm^y`gU+cQ5eQ zk2^WeORG{uJiNzNHMz(?Dq@-~vzN+nk3o1&7EWyxjwHdEXPwjJc21V)ufK6yaN-iB znHE}gSDwQty$h&cv3$|o?K6lh>WiqG;c*|hx#?y5>$7OfXanVZ+qh!ey>9rQ9k^Z} z1Au;#nc$G82uw+lPo!=CW$k!>doR$`y0NM%4CH{Xx=>Y zG)V6{93*hARe!z$#zOgWnH6=fOM&_a-e&2F4E!%@+Q+esiryK2#90ADE4&|2>X_M! zz>sS@) zqJ>rr@^%APpu*uC5B|L$^|n+#m@epLwkAGt^O)?%@D>f5KGkchR5Fvb9sTGwO9R$Y zVocmHgVeV@Xp;Blky`2MAe?Sa@)#%X96IUp6JJV>=Wm-M^!;K^-OZjcl61!6OZOv@ zMnir4zb>Ha^-n+{8{(qOwe!wvdW$(J`G0c&I1w=6E7=(BG2AE8besaSTFik`HN%Su zVMXWtV?cdO^1{x-FT?DZ+qCjOpE$28*D+_t+=OivQZ8TZ$mpm`cf(-6v9iu34Rxby zG&DruNugEUT24rg$A;gpY z;lcQ(9KJb)|6!Ow=~4VsJDo$O(dVRKPDe$!3kq|O;4Pf;tBe1}YVw%);zjDvqp9|& zP{@@fBE&RoF`~CV7J7K+202GEVXIYs%|k&8(lC^}z1lB*UMv6jV@lXI4R*#*?#l+O z53)Xe{ynQgj-#8C{q7&LzaMC7IKI3E&GNYvTxGWa*#ONSZQ=QN=os4-57zl=ZE1yH z>c6Z^lR;PH8#o#+o!{}6_F<>y5ZP8QTF#wT^=_e72&Yj_-+oXTW!&X#K3tqR7I|~q zus_1Shqo+@M2mX(K$mdGq36clFPcej0jaraXs6n=cH($sngsLKD^L;&>YRI$r({2+>gEIdBop}a_1-OZ#h`I3 za+W}!VPOt89m1EqXWG1;aSh#tq z9_szPRfNSNr%O>8s-5Ch<&I6H&{B{V1pz3Iy%04->_z+(_Bp+B?R^ zN*5h*PKKyDIJIIgKI>z1y{^(!Y0vzhPYsJlV%c|5m?>dG@L&eZYgQa}7GeF=S+Xop zt~=L9HzZInoO3OoTYG`md(j2vFWv~iO9RhW^4oST@dfExi4xug#WvjuU0 zKpr=7;+la7iB2*NeH^o=@4@qBX@MbfN9@akH&4880d~m1(`8tFK51;{-kXn>#-S&t zlN?&)xMDQ4aec~02}$Y9Q#q0 z$4oG2K{FVG6n003<0bygR=rV*zoz-FJ6gPoTwOcvtK(xrrQIIZYjp!rD)HM9g0DX~@Zp~5IYCn9_2sEh~5}jUyftvwl zoi7~BvnLZ>-+O-FHeow-cCB`Dvi*gd>UW zgzX1)Fby0?Ye<5*1p(LD+uaoeG0M>QFbM96L&r7dU>*sLH(Yn=(VR)*rMU6YQDr8I z==wrn`=Q*0+J+OWdu&zln0ez=oT-qDjOoBr?P9~NmcG3$rW z-6uD*pPyz;>Dl^8I3mQ_Sh`FX?`=pu3TeCkgQkvaybfm%3B2=u4iWI&qC?RwD?9@y z&G*#Y4k|JVCF|=ZCWkL5wQ6)VZSD}Hq{nVA*`4A%#&?! zQG9Y#+t0x>JOA}Rto%s&adSq+Ut+7^{!zIuWW!V^DT|HE(X9Pt)OCK>I*n3OK(l{= zcq-% zFp@;e5l)7>=B>G#E9P%j?}AaddHOOKN%KA+`XwzNv}o4ZJ{0S6m+$c^Cz}dITvyJC z;?@+)QEiPa51lNga+l z@sn4>qCr0Br7e3bcVKY}h)D3c!#7I<9LaYI^X)wi{&puuEjd1eoM5MuK!Lv={O0sT z){c3sp1LQre2+hnLAysO(09d=h~#=w9%ZCn>mkX=b#LupeVVc?YI*G7)xQY~Br19a z{^>KmLG!*0foTGj$m2uCrpigmFzW<2m*tx45bvfzKK$k}`&O%+%Xy*L&qOx4>fl7N z!WrX1a})jV3X)dIah>HHK3-#d+TO>6W6N5VDU{Nd3vu?KFf8S@x~2@H&lv6<-o%=L zfO;Ynl0W})244qoe}|0~k=PjY5$@kq|4!gM!(kqdV7nlr#c~h&NZoX~hebt)a}zjY zfhwO^7V|Pe6Y0RkfbcZIy~Ip-~ZTc zOn8@@J-+D86#4RWbT%WyV9EDog6!)*zP8a|K1g)h$0J)Z=L%NnXV342i{TphJ(#{` z3A;RL=4O4}`gBs_%vB7}{Y^dt>N}`o5xttvX6yUrjG1_xn5@|r7Tzvy)pmvXTVkD; z_QpJPY7Ns&Ku9oON|gXiK4S3!BR$(Ug&7$&h)tiL$#w1JPHS2Y97g7JtA-JjVv=iC zV98-<2JQoH`A!Hv^I;RkFl6se@5L2J%^z;uKm4zEOP!yC&wVmY$f^VW6@Uj|1#Re| zz-Yiy)qY90?m!1EF-3CkQUB}S?q^A;+I|J}t`GJ+{xgsLHP*a82J=JcC)eof)<2#m zO(jkqI_dc-r3o3X$#Zv69C9F%`_V$B&>Nz8(3UY@fAqZp_+a-l-AL>`7&y?Y?K}Hw znhscpQhuX7rJ)kz5dM|EJA)N`OEHylMqLp!sPSe&(oIM2<6~7CTla=WbOr_~cbJg= ztKp+S-viVda|;Z=Cn9BhmXf^4>-A8hbW=RkRT)oLrS?#}oDB`$Na%J;ff-x1KBVsk z7Ht64X%%-&fQ~9TyvyLy=5AAbXcI^$o<()K;!l<(cI2$LgspWf8&aDJy->ur@C6D( zT>D>debP_iO5h^;I;yg^3coz<--AgH<3~NG)h8I7{gu9I4&87$bx648Bil zwB8g_xZv*1aV75Q$GykeFTj?VFlho8Kb?3%lvt63k`D)~zW)Kmu?cJS*X6eoiGB4s z;bv_t9B4}Kw|Z&1SQ!>tnjQ8G@r)h=tbkx5jc%$8>7kVu#>4^pTbdCs@D}iBrnx|C z;pAZid5wyl!X=GR$YbiQo5+7>Z_e?7eos%>A7_C1 zuaTah?N9jYFpWvN)AjyltD?p8f>lJY+Lu9VeaiZ65IQp3CcH!u`uM|5V?Jj#eqcHo zD)Q2#S?HN?<^naN$no#h2x@;MS^RcmjcvSZo_vYLhGxr|M{Uu32Er!2j&U(Dl+(}G z=aIDnBnYH=9!TarAZi^~21Px)cfZMZQy94D4q@TT*cbqS;KE~=1##oC$CU8*&8if1 z31RD===%rriZ4DNGCd$4g#P@haN1@?FI8;LJE$7r+GxNj&?=LM4ZzWr_F zjh9|vkyN86^~{aTDZcH=z27MQ+XYuj`go_UE!OMi&F8nOM!pd4UR$|iVsaHzom>7VnL?n!&YNog(u50`m-Iw?Fn#mRa5R3+)oDFm@ldJ+Myg^3%Vw$;ikEPWO`I zoOmxG4+qa7@X_;!C_s?Hu)3EwoM^#Qj!TY+-U~sd<@h;-=pxL=Q7E27l zjmOp!3_YommMd=hNbjDWdKn+1EIT=~?&lX^Hj|#3zpi&6&F8?{RHwJXw>-V4$jLZa zRk|$q6NV5T$GeRUlQs}Br`!7g)slYx>=xExxTfSYDBQa~r0IW5DgT=1{OilKN4f2Z zaZ<&M9))eyW{C<1)Y69X3Y3*d~#qwz^~08i!fZ`)LP9Lq3J5Zh6M32?JWB%*X0oE2&s^p z1SDc4_grC0y(?85$-Ne+L{>$~JV!$`exKKQY?h6B>Lj1d>Xe?>ae8pgUsZ%R!@$@0 z(K_uV5)#rInyO01FA#U=qD)(#xmRz>ogcSyK|+3TGn#|0x@TlKi5L=B2~mt=gDsOOYcbF OFU>2us-?;{q5lsFAkd5e literal 0 HcmV?d00001 diff --git a/version.h b/version.h deleted file mode 100644 index 835ebdb..0000000 --- a/version.h +++ /dev/null @@ -1,2 +0,0 @@ -char version[] = "Aptec CVS version (c) 2004-2005 Thierry Leconte F4DWV"; - diff --git a/w32util.c b/w32util.c deleted file mode 100755 index 7c23419..0000000 --- a/w32util.c +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include - -char * basename (const char *filename) { - const char *p = filename + strlen (filename); - while (p != filename) { - p--; - if (*p == '/' || *p == '\\') - return ((char *) p + 1); - } - return ((char *) filename); -} - -char *dirname(const char *path) { - static char bname[1024]; - register const char *endp; - - /* Empty or NULL string gets treated as "." */ - if (path == NULL || *path == '\0') { - (void)strncpy(bname, ".", sizeof bname); - return(bname); - } - - /* Strip trailing slashes */ - endp = path + strlen(path) - 1; - while (endp > path && *endp == '\\') - endp--; - - /* Find the start of the dir */ - while (endp > path && *endp != '\\') - endp--; - - /* Either the dir is "/" or there are no slashes */ - if (endp == path) { - (void)strncpy(bname, *endp == '\\' ? "\\": ".", sizeof bname); - return(bname); - } else { - do { - endp--; - } while (endp > path && *endp == '\\'); - } - - if (endp - path + 2 > sizeof(bname)) { - return(NULL); - } - strncpy(bname, path, endp - path + 2); - return(bname); -} - -int opterr = 1, /* if error message should be printed */ - optind = 1, /* index into parent argv vector */ - optopt, /* character checked for validity */ - optreset; /* reset getopt */ -char *optarg; /* argument associated with option */ - -#define BADCH (int)'?' -#define BADARG (int)':' -#define EMSG "" - -/* - * getopt -- - * Parse argc/argv argument vector. - */ -int getopt(nargc, nargv, ostr) - int nargc; - char * const *nargv; - const char *ostr; -{ - #define __progname nargv[0] - static char *place = EMSG; /* option letter processing */ - char *oli; /* option letter list index */ - - if (optreset || !*place) { /* update scanning pointer */ - optreset = 0; - if (optind >= nargc || *(place = nargv[optind]) != '-') { - place = EMSG; - return (-1); - } - if (place[1] && *++place == '-') { /* found "--" */ - ++optind; - place = EMSG; - return (-1); - } - } /* option letter okay? */ - if ((optopt = (int)*place++) == (int)':' || - !(oli = strchr(ostr, optopt))) { - /* - * if the user didn't specify '-' as an option, - * assume it means -1. - */ - if (optopt == (int)'-') - return (-1); - if (!*place) - ++optind; - if (opterr && *ostr != ':') - (void)printf("%s: illegal option -- %c\n", __progname, optopt); - return (BADCH); - } - if (*++oli != ':') { /* don't need argument */ - optarg = NULL; - if (!*place) - ++optind; - } - else { /* need an argument */ - if (*place) /* no white space */ - optarg = place; - else if (nargc <= ++optind) { /* no arg */ - place = EMSG; - if (*ostr == ':') - return (BADARG); - if (opterr) - (void)printf( - "%s: option requires an argument -- %c\n", - __progname, optopt); - return (BADCH); - } else /* white space */ - optarg = nargv[optind]; - place = EMSG; - ++optind; - } - return (optopt); /* dump back option letter */ -} - diff --git a/w32util.h b/w32util.h deleted file mode 100755 index c6cd387..0000000 --- a/w32util.h +++ /dev/null @@ -1,12 +0,0 @@ -extern char * basename(const char *filename); - -extern char *dirname(const char *path); - -extern int opterr, /* if error message should be printed */ - optind , /* index into parent argv vector */ - optopt, /* character checked for validity */ - optreset; /* reset getopt */ -extern char *optarg; /* argument associated with option */ - - -extern int getopt(int nargc, char * const *nargv, const char *ostr);