From a3e44b5626065c5cca72b8274252a340aeac5af3 Mon Sep 17 00:00:00 2001 From: Junzi Sun Date: Fri, 22 Jun 2018 22:52:11 +0200 Subject: [PATCH 01/10] fixing code --- pyModeS/decoder/adsb.py | 201 ++++++++++++++++++------------------- pyModeS/streamer/stream.py | 24 ++--- 2 files changed, 108 insertions(+), 117 deletions(-) diff --git a/pyModeS/decoder/adsb.py b/pyModeS/decoder/adsb.py index 0421d65..dacce31 100644 --- a/pyModeS/decoder/adsb.py +++ b/pyModeS/decoder/adsb.py @@ -166,53 +166,6 @@ def speed_heading(msg): return spd, trk_or_hdg -def nic(msg): - """Calculate NIC, navigation integrity category - - Args: - msg (string): 28 bytes hexadecimal message string - - Returns: - int: NIC number (from 0 to 11), -1 if not applicable - """ - if typecode(msg) < 9 or typecode(msg) > 18: - raise RuntimeError("%s: Not a airborne position message, expecting 8 22: - raise RuntimeError("%s: Not a surface position message (5 22: - raise RuntimeError("%s: Not a surface position message (5 18: - raise RuntimeError("%s: Not a airborne position message, expecting 8 18: + raise RuntimeError("%s: Not a airborne position message, expecting 8 Date: Sat, 23 Jun 2018 00:20:38 +0200 Subject: [PATCH 02/10] update pmstream, add nic/nac/sil. --- pyModeS/decoder/adsb.py | 2 + pyModeS/streamer/__init__.py | 0 pyModeS/streamer/pmstream.py | 15 +++-- pyModeS/streamer/screen.py | 39 +++++++---- pyModeS/streamer/stream.py | 122 ++++++++++++++++++----------------- 5 files changed, 98 insertions(+), 80 deletions(-) create mode 100644 pyModeS/streamer/__init__.py diff --git a/pyModeS/decoder/adsb.py b/pyModeS/decoder/adsb.py index dacce31..5a72a35 100644 --- a/pyModeS/decoder/adsb.py +++ b/pyModeS/decoder/adsb.py @@ -452,6 +452,8 @@ def sil(msg, version): elif tc == 31: sil = common.bin2int(msg[82:84]) + sil_sup = None + if version == 2: if version == 29: sil_sup = common.bin2int(msgbin[39]) diff --git a/pyModeS/streamer/__init__.py b/pyModeS/streamer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyModeS/streamer/pmstream.py b/pyModeS/streamer/pmstream.py index 450d4e9..8b7e14b 100644 --- a/pyModeS/streamer/pmstream.py +++ b/pyModeS/streamer/pmstream.py @@ -14,8 +14,8 @@ from pyModeS.streamer.screen import Screen LOCK = Lock() ADSB_MSG = [] ADSB_TS = [] -EHS_MSG = [] -EHS_TS = [] +COMMB_MSG = [] +COMMB_TS = [] parser = argparse.ArgumentParser() parser.add_argument('--server', help='server address or IP', required=True) @@ -58,11 +58,12 @@ class ModesClient(BaseClient): LOCK.acquire() ADSB_MSG.extend(local_buffer_adsb_msg) ADSB_TS.extend(local_buffer_adsb_ts) - EHS_MSG.extend(local_buffer_ehs_msg) - EHS_TS.extend(local_buffer_ehs_ts) + COMMB_MSG.extend(local_buffer_ehs_msg) + COMMB_TS.extend(local_buffer_ehs_ts) LOCK.release() +# redirect all stdout to null, avoiding messing up with the screen sys.stdout = open(os.devnull, 'w') client = ModesClient(host=SERVER, port=PORT) @@ -79,11 +80,11 @@ try: while True: if len(ADSB_MSG) > 200: LOCK.acquire() - stream.process_raw(ADSB_TS, ADSB_MSG, EHS_TS, EHS_MSG) + stream.process_raw(ADSB_TS, ADSB_MSG, COMMB_TS, COMMB_MSG) ADSB_MSG = [] ADSB_TS = [] - EHS_MSG = [] - EHS_TS = [] + COMMB_MSG = [] + COMMB_TS = [] LOCK.release() acs = stream.get_aircraft() diff --git a/pyModeS/streamer/screen.py b/pyModeS/streamer/screen.py index cc45e38..2992425 100644 --- a/pyModeS/streamer/screen.py +++ b/pyModeS/streamer/screen.py @@ -4,7 +4,24 @@ import numpy as np import time from threading import Thread -COLUMNS = ['lat', 'lon', 'alt', 'gs', 'tas', 'ias', 'mach', 'roc', 'trk', 'hdg', 't'] +COLUMNS = [ + ('lat', 10), + ('lon', 10), + ('alt', 7), + ('gs', 5), + ('tas', 5), + ('ias', 5), + ('mach', 7), + ('roc', 7), + ('trk', 10), + ('hdg', 10), + ('ver', 4), + ('NIC', 5), + ('NACv', 5), + ('NACp', 5), + ('SIL', 5), + ('updated', 12), +] class Screen(Thread): def __init__(self): @@ -44,10 +61,10 @@ class Screen(Thread): row = 1 - header = 'icao' - for c in COLUMNS: + header = ' icao' + for c, cw in COLUMNS: c = 'updated' if c=='t' else c - header += '%10s' % c + header += (cw-len(c))*' ' + c if len(header) > self.scr_w - 2: header = header[:self.scr_w-3] + '>' @@ -74,19 +91,15 @@ class Screen(Thread): line += icao - for c in COLUMNS: - - if c == 't': - val = str(int(ac[c])) - line += '%12s' % val - else: - val = '' if ac[c] is None else ac[c] - line += '%10s' % val + for c, cw in COLUMNS: + val = '' if ac[c] is None else ac[c] + val_str = str(val) + line += (cw-len(val_str))*' ' + val_str if len(line) > self.scr_w - 2: line = line[:self.scr_w-3] + '>' - if self.lock_icao == icao: + if (icao is not None) and (self.lock_icao == icao): self.screen.addstr(row, 1, line, curses.A_STANDOUT) elif row == self.y: self.screen.addstr(row, 1, line, curses.A_BOLD) diff --git a/pyModeS/streamer/stream.py b/pyModeS/streamer/stream.py index 3dee46d..728815d 100644 --- a/pyModeS/streamer/stream.py +++ b/pyModeS/streamer/stream.py @@ -1,7 +1,7 @@ from __future__ import absolute_import, print_function, division import numpy as np import time -from pyModeS.decoder import adsb, ehs +import pyModeS as pms class Stream(): def __init__(self, lat0, lon0): @@ -18,8 +18,8 @@ class Stream(): self.cache_timeout = 60 # seconds - def process_raw(self, adsb_ts, adsb_msgs, ehs_ts, ehs_msgs, tnow=None): - """process a chunk of adsb and ehs messages recieved in the same + def process_raw(self, adsb_ts, adsb_msgs, commb_ts, commb_msgs, tnow=None): + """process a chunk of adsb and commb messages recieved in the same time period. """ if tnow is None: @@ -31,11 +31,12 @@ class Stream(): # process adsb message for t, msg in zip(adsb_ts, adsb_msgs): - icao = adsb.icao(msg) - tc = adsb.typecode(msg) + icao = pms.icao(msg) + tc = pms.adsb.typecode(msg) if icao not in self.acs: self.acs[icao] = { + 'updated': None, 'lat': None, 'lon': None, 'alt': None, @@ -46,20 +47,20 @@ class Stream(): 'ias': None, 'mach': None, 'hdg': None, - 'adsb_version' : None, - 'nic_s' : None, - 'nic_a' : None, - 'nic_b' : None, - 'nic_c' : None + 'ver' : None, + 'NIC' : None, + 'NACp' : None, + 'NACv' : None, + 'SIL' : None } - self.acs[icao]['t'] = t + self.acs[icao]['updated'] = int(t) if 1 <= tc <= 4: - self.acs[icao]['callsign'] = adsb.callsign(msg) + self.acs[icao]['callsign'] = pms.adsb.callsign(msg) if (5 <= tc <= 8) or (tc == 19): - vdata = adsb.velocity(msg) + vdata = pms.adsb.velocity(msg) if vdata is None: continue @@ -75,7 +76,7 @@ class Stream(): self.acs[icao]['tv'] = t if (5 <= tc <= 18): - oe = adsb.oe_flag(msg) + oe = pms.adsb.oe_flag(msg) self.acs[icao][oe] = msg self.acs[icao]['t'+str(oe)] = t @@ -83,21 +84,21 @@ class Stream(): # use single message decoding rlat = self.acs[icao]['lat'] rlon = self.acs[icao]['lon'] - latlon = adsb.position_with_ref(msg, rlat, rlon) + latlon = pms.adsb.position_with_ref(msg, rlat, rlon) elif ('t0' in self.acs[icao]) and ('t1' in self.acs[icao]) and \ (abs(self.acs[icao]['t0'] - self.acs[icao]['t1']) < 10): # use multi message decoding - try: - latlon = adsb.position( - self.acs[icao][0], - self.acs[icao][1], - self.acs[icao]['t0'], - self.acs[icao]['t1'], - self.lat0, self.lon0 - ) - except: - # mix of surface and airborne position message - continue + # try: + latlon = pms.adsb.position( + self.acs[icao][0], + self.acs[icao][1], + self.acs[icao]['t0'], + self.acs[icao]['t1'], + self.lat0, self.lon0 + ) + # except: + # # mix of surface and airborne position message + # continue else: latlon = None @@ -105,70 +106,71 @@ class Stream(): self.acs[icao]['tpos'] = t self.acs[icao]['lat'] = latlon[0] self.acs[icao]['lon'] = latlon[1] - self.acs[icao]['alt'] = adsb.altitude(msg) + self.acs[icao]['alt'] = pms.adsb.altitude(msg) local_updated_acs_buffer.append(icao) # Uncertainty & accuracy if (5 <= tc <= 8): - if self.acs[icao]['adsb_version'] == 1: + if self.acs[icao]['ver'] == 1: if self.acs[icao]['nic_s'] != None: - self.nic = adsb.nic_v1(msg, self.acs[icao]['nic_s']) - elif self.acs[icao]['adsb_version'] == 2: + self.acs[icao]['NIC'] = pms.adsb.nic_v1(msg, self.acs[icao]['nic_s']) + elif self.acs[icao]['ver'] == 2: if self.acs[icao]['nic_a'] != None and self.acs[icao]['nic_b'] != None: - self.nic = adsb.nic_v2(msg, self.nic_a, self.acs[icao]['nic_b'], self.acs[icao]['nic_c']) + self.acs[icao]['NIC'] = pms.adsb.nic_v2(msg, self.acs[icao]['nic_a'], self.acs[icao]['nic_b'], self.acs[icao]['nic_c']) if (9 <= tc <= 18): - if self.acs[icao]['adsb_version'] == 1: + if self.acs[icao]['ver'] == 1: if self.acs[icao]['nic_s'] != None: - self.nic = adsb.nic_v1(msg, self.acs[icao]['nic_s']) - elif self.acs[icao]['adsb_version'] == 2: - self.acs[icao]['nic_b'] = adsb.nic_b(msg) + self.acs[icao]['NIC'] = pms.adsb.nic_v1(msg, self.acs[icao]['nic_s']) + elif self.acs[icao]['ver'] == 2: + self.acs[icao]['nic_b'] = pms.adsb.nic_b(msg) if self.acs[icao]['nic_a'] != None and self.acs[icao]['nic_b'] != None: - self.nic = adsb.nic_v2(msg, self.acs[icao]['nic_a'], self.nic_b, self.acs[icao]['nic_c']) + self.acs[icao]['NIC'] = pms.adsb.nic_v2(msg, self.acs[icao]['nic_a'], self.acs[icao]['nic_b'], self.acs[icao]['nic_c']) if tc == 19: - self.acs[icao]['nac_v'] = adsb.nac_v(msg) + if self.acs[icao]['ver'] in [1, 2]: + self.acs[icao]['NACv'] = pms.adsb.nac_v(msg) if (20 <= tc <= 22): - if self.acs[icao]['adsb_version'] == 1: + if self.acs[icao]['ver'] == 1: if self.acs[icao]['nic_s'] != None: - self.nic = adsb.nic_v1(msg, self.acs[icao]['nic_s']) - elif self.acs[icao]['adsb_version'] == 2: + self.acs[icao]['NIC'] = pms.adsb.nic_v1(msg, self.acs[icao]['nic_s']) + elif self.acs[icao]['ver'] == 2: if self.acs[icao]['nic_a'] != None and self.acs[icao]['nic_b'] != None: - self.nic = adsb.nic_v2(msg, self.acs[icao]['nic_a'], self.acs[icao]['nic_b'], self.acs[icao]['nic_c']) + self.acs[icao]['NIC'] = pms.adsb.nic_v2(msg, self.acs[icao]['nic_a'], self.acs[icao]['nic_b'], self.acs[icao]['nic_c']) if tc == 29: - if self.acs[icao]['adsb_version'] != None: - self.acs[icao]['sil'] = adsb.sil(msg, self.acs[icao]['adsb_version']) - self.acs[icao]['nac_p'] = adsb.nac_p(msg) + if self.acs[icao]['ver'] != None: + self.acs[icao]['SIL'], self.acs[icao]['sil_s'] = pms.adsb.sil(msg, self.acs[icao]['ver']) + self.acs[icao]['NACp'] = pms.adsb.nac_p(msg) if tc == 31: - self.acs[icao]['adsb_version'] = adsb.version(msg) - self.acs[icao]['sil'] = adsb.version(msg) - self.acs[icao]['nac_p'] = adsb.nac_p(msg) - if self.acs[icao]['adsb_version'] == 1: - self.acs[icao]['nic_s'] = adsb.nic_s(msg) - elif self.acs[icao]['adsb_version'] == 2: - self.acs[icao]['nic_a'], self.acs[icao]['nic_c'] = adsb.nic_a_c(msg) + self.acs[icao]['ver'] = pms.adsb.version(msg) + self.acs[icao]['SIL'] = pms.adsb.version(msg) + self.acs[icao]['NACp'] = pms.adsb.nac_p(msg) + if self.acs[icao]['ver'] == 1: + self.acs[icao]['nic_s'] = pms.adsb.nic_s(msg) + elif self.acs[icao]['ver'] == 2: + self.acs[icao]['nic_a'], self.acs[icao]['nic_c'] = pms.adsb.nic_a_c(msg) - # process ehs message - for t, msg in zip(ehs_ts, ehs_msgs): - icao = ehs.icao(msg) + # process commb message + for t, msg in zip(commb_ts, commb_msgs): + icao = pms.icao(msg) if icao not in self.acs: continue - bds = ehs.BDS(msg) + bds = pms.bds.infer(msg) if bds == 'BDS50': - tas = ehs.tas50(msg) + tas = pms.commb.tas50(msg) if tas: self.acs[icao]['t50'] = t self.acs[icao]['tas'] = tas elif bds == 'BDS60': - ias = ehs.ias60(msg) - hdg = ehs.hdg60(msg) - mach = ehs.mach60(msg) + ias = pms.commb.ias60(msg) + hdg = pms.commb.hdg60(msg) + mach = pms.commb.mach60(msg) if ias or hdg or mach: self.acs[icao]['t60'] = t @@ -181,7 +183,7 @@ class Stream(): # clear up old data for icao in list(self.acs.keys()): - if self.t - self.acs[icao]['t'] > self.cache_timeout: + if self.t - self.acs[icao]['updated'] > self.cache_timeout: del self.acs[icao] continue From 70b3af2c8bdbd02ddc61cf7793f01168c81058eb Mon Sep 17 00:00:00 2001 From: Junzi Sun Date: Sat, 23 Jun 2018 01:24:15 +0200 Subject: [PATCH 03/10] add script: pmslive --- README.rst | 2 +- pyModeS/streamer/{pmstream.py => pmslive} | 206 +++++++++++----------- pyModeS/streamer/screen.py | 1 + setup.py | 2 + 4 files changed, 107 insertions(+), 104 deletions(-) rename pyModeS/streamer/{pmstream.py => pmslive} (89%) mode change 100644 => 100755 diff --git a/README.rst b/README.rst index e082a2d..cd780e8 100644 --- a/README.rst +++ b/README.rst @@ -40,7 +40,7 @@ New features in v2.0 --------------------- - New structure of the libraries - ADS-B and Comm-B data streaming -- Active aircraft viewing (terminal cursor) +- Active aircraft viewing (terminal curses) - Improved BDS identification - Optimizing decoding speed diff --git a/pyModeS/streamer/pmstream.py b/pyModeS/streamer/pmslive old mode 100644 new mode 100755 similarity index 89% rename from pyModeS/streamer/pmstream.py rename to pyModeS/streamer/pmslive index 8b7e14b..7221825 --- a/pyModeS/streamer/pmstream.py +++ b/pyModeS/streamer/pmslive @@ -1,103 +1,103 @@ -from __future__ import print_function, division -import os -import sys -import argparse -import curses -import numpy as np -import time -from threading import Lock -import pyModeS as pms -from pyModeS.extra.beastclient import BaseClient -from pyModeS.streamer.stream import Stream -from pyModeS.streamer.screen import Screen - -LOCK = Lock() -ADSB_MSG = [] -ADSB_TS = [] -COMMB_MSG = [] -COMMB_TS = [] - -parser = argparse.ArgumentParser() -parser.add_argument('--server', help='server address or IP', required=True) -parser.add_argument('--port', help='Raw beast port', required=True) -parser.add_argument('--lat0', help='Latitude of receiver', required=True) -parser.add_argument('--lon0', help='Longitude of receiver', required=True) -args = parser.parse_args() - -SERVER = args.server -PORT = int(args.port) -LAT0 = float(args.lat0) # 51.9899 for TU Delft -LON0 = float(args.lon0) # 4.3754 - -class ModesClient(BaseClient): - def __init__(self, host, port): - super(ModesClient, self).__init__(host, port) - - def handle_messages(self, messages): - local_buffer_adsb_msg = [] - local_buffer_adsb_ts = [] - local_buffer_ehs_msg = [] - local_buffer_ehs_ts = [] - - for msg, t in messages: - if len(msg) < 28: # only process long messages - continue - - df = pms.df(msg) - - if df == 17 or df == 18: - local_buffer_adsb_msg.append(msg) - local_buffer_adsb_ts.append(t) - elif df == 20 or df == 21: - local_buffer_ehs_msg.append(msg) - local_buffer_ehs_ts.append(t) - else: - continue - - - LOCK.acquire() - ADSB_MSG.extend(local_buffer_adsb_msg) - ADSB_TS.extend(local_buffer_adsb_ts) - COMMB_MSG.extend(local_buffer_ehs_msg) - COMMB_TS.extend(local_buffer_ehs_ts) - LOCK.release() - - -# redirect all stdout to null, avoiding messing up with the screen -sys.stdout = open(os.devnull, 'w') - -client = ModesClient(host=SERVER, port=PORT) -client.daemon = True -client.start() - -stream = Stream(lat0=LAT0, lon0=LON0) - -try: - screen = Screen() - screen.daemon = True - screen.start() - - while True: - if len(ADSB_MSG) > 200: - LOCK.acquire() - stream.process_raw(ADSB_TS, ADSB_MSG, COMMB_TS, COMMB_MSG) - ADSB_MSG = [] - ADSB_TS = [] - COMMB_MSG = [] - COMMB_TS = [] - LOCK.release() - - acs = stream.get_aircraft() - # try: - screen.update_data(acs) - screen.update() - # except KeyboardInterrupt: - # raise - # except: - # continue - -except KeyboardInterrupt: - sys.exit(0) - -finally: - curses.endwin() +#!/usr/bin/env python + +from __future__ import print_function, division +import os +import sys +import argparse +import curses +from threading import Lock +import pyModeS as pms +from pyModeS.extra.beastclient import BaseClient +from pyModeS.streamer.stream import Stream +from pyModeS.streamer.screen import Screen + +LOCK = Lock() +ADSB_MSG = [] +ADSB_TS = [] +COMMB_MSG = [] +COMMB_TS = [] + +parser = argparse.ArgumentParser() +parser.add_argument('--server', help='server address or IP', required=True) +parser.add_argument('--port', help='Raw beast port', required=True) +parser.add_argument('--lat0', help='Latitude of receiver', required=True) +parser.add_argument('--lon0', help='Longitude of receiver', required=True) +args = parser.parse_args() + +SERVER = args.server +PORT = int(args.port) +LAT0 = float(args.lat0) # 51.9899 for TU Delft +LON0 = float(args.lon0) # 4.3754 + +class ModesClient(BaseClient): + def __init__(self, host, port): + super(ModesClient, self).__init__(host, port) + + def handle_messages(self, messages): + local_buffer_adsb_msg = [] + local_buffer_adsb_ts = [] + local_buffer_ehs_msg = [] + local_buffer_ehs_ts = [] + + for msg, t in messages: + if len(msg) < 28: # only process long messages + continue + + df = pms.df(msg) + + if df == 17 or df == 18: + local_buffer_adsb_msg.append(msg) + local_buffer_adsb_ts.append(t) + elif df == 20 or df == 21: + local_buffer_ehs_msg.append(msg) + local_buffer_ehs_ts.append(t) + else: + continue + + + LOCK.acquire() + ADSB_MSG.extend(local_buffer_adsb_msg) + ADSB_TS.extend(local_buffer_adsb_ts) + COMMB_MSG.extend(local_buffer_ehs_msg) + COMMB_TS.extend(local_buffer_ehs_ts) + LOCK.release() + + +# redirect all stdout to null, avoiding messing up with the screen +sys.stdout = open(os.devnull, 'w') + +client = ModesClient(host=SERVER, port=PORT) +client.daemon = True +client.start() + +stream = Stream(lat0=LAT0, lon0=LON0) + +try: + screen = Screen() + screen.daemon = True + screen.start() + + while True: + if len(ADSB_MSG) > 200: + LOCK.acquire() + stream.process_raw(ADSB_TS, ADSB_MSG, COMMB_TS, COMMB_MSG) + ADSB_MSG = [] + ADSB_TS = [] + COMMB_MSG = [] + COMMB_TS = [] + LOCK.release() + + acs = stream.get_aircraft() + try: + screen.update_data(acs) + screen.update() + except KeyboardInterrupt: + raise + except: + continue + +except KeyboardInterrupt: + sys.exit(0) + +finally: + curses.endwin() diff --git a/pyModeS/streamer/screen.py b/pyModeS/streamer/screen.py index 2992425..4d601e4 100644 --- a/pyModeS/streamer/screen.py +++ b/pyModeS/streamer/screen.py @@ -28,6 +28,7 @@ class Screen(Thread): Thread.__init__(self) self.screen = curses.initscr() curses.noecho() + curses.mousemask(1) self.screen.keypad(True) self.y = 3 self.x = 1 diff --git a/setup.py b/setup.py index af6289c..6a53144 100644 --- a/setup.py +++ b/setup.py @@ -117,4 +117,6 @@ setup( # 'sample=sample:main', # ], # }, + + scripts=['pyModeS/streamer/pmslive'], ) From 140b68afbceb4754cc4a83267a7ae82e7de6bba2 Mon Sep 17 00:00:00 2001 From: Junzi Sun Date: Sat, 23 Jun 2018 01:28:41 +0200 Subject: [PATCH 04/10] add pmslive command reference --- README.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.rst b/README.rst index cd780e8..efb1862 100644 --- a/README.rst +++ b/README.rst @@ -65,6 +65,17 @@ To install latest development version (dev-2.0) from the GitHub: pip install git+https://github.com/junzis/pyModeS + +Live view traffic +---------------------------------------------------- +Supports Mode-S Beast raw stream + +.. code:: python + + pmslive --server URL/IP --port PORT --lat0 RECEIVER_LAT --lon0 RECEIVER_LON + + + Use the library --------------- From c0476f5e16b93f737920287027f54309d5d8841d Mon Sep 17 00:00:00 2001 From: Junzi Sun Date: Sat, 23 Jun 2018 01:29:58 +0200 Subject: [PATCH 05/10] readme code format --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index efb1862..97a2f44 100644 --- a/README.rst +++ b/README.rst @@ -70,7 +70,7 @@ Live view traffic ---------------------------------------------------- Supports Mode-S Beast raw stream -.. code:: python +:: pmslive --server URL/IP --port PORT --lat0 RECEIVER_LAT --lon0 RECEIVER_LON From 01a573a1af5c01005ce4ccf6e68452cd7c781b32 Mon Sep 17 00:00:00 2001 From: Junzi Sun Date: Sat, 23 Jun 2018 14:40:27 +0200 Subject: [PATCH 06/10] update live screen --- pyModeS/decoder/adsb.py | 47 +++++++++++++++----------------------- pyModeS/streamer/screen.py | 28 +++++++++++++++++++---- pyModeS/streamer/stream.py | 43 +++++++++++----------------------- 3 files changed, 55 insertions(+), 63 deletions(-) diff --git a/pyModeS/decoder/adsb.py b/pyModeS/decoder/adsb.py index 5a72a35..3230706 100644 --- a/pyModeS/decoder/adsb.py +++ b/pyModeS/decoder/adsb.py @@ -176,14 +176,25 @@ def oe_flag(msg): msgbin = common.hex2bin(msg) return int(msgbin[53]) -# Uncertainty & accuracy -def nic(msg, *argv): - if len(argv) == 1: - # assume ads-b v1, only one supplement bit - return nic_v1(msg, *argv) - elif len(argv) == 3: - # assume ads-b v2, three supplement bits - return nic_v2(msg, *argv) + +def version(msg): + """ADS-B Version + + Args: + msg (string): 28 bytes hexadecimal message string, TC = 31 + + Returns: + int: version number + """ + tc = typecode(msg) + + if tc != 31: + raise RuntimeError("%s: Not a status operation message, expecting TC = 31" % msg) + + msgbin = common.hex2bin(msg) + version = common.bin2int(msgbin[72:75]) + + return version def nic_v1(msg, nic_sup_b): @@ -461,23 +472,3 @@ def sil(msg, version): sil_sup = common.bin2int(msgbin[86]) return sil, sil_sup - - -def version(msg): - """ADS-B Version - - Args: - msg (string): 28 bytes hexadecimal message string, TC = 31 - - Returns: - int: version number - """ - tc = typecode(msg) - - if tc != 31: - raise RuntimeError("%s: Not a status operation message, expecting TC = 31" % msg) - - msgbin = common.hex2bin(msg) - version = common.bin2int(msgbin[72:75]) - - return version diff --git a/pyModeS/streamer/screen.py b/pyModeS/streamer/screen.py index 4d601e4..1e9494d 100644 --- a/pyModeS/streamer/screen.py +++ b/pyModeS/streamer/screen.py @@ -1,4 +1,5 @@ from __future__ import print_function, division +import os import curses import numpy as np import time @@ -20,7 +21,7 @@ COLUMNS = [ ('NACv', 5), ('NACp', 5), ('SIL', 5), - ('updated', 12), + ('live', 6), ] class Screen(Thread): @@ -45,7 +46,7 @@ class Screen(Thread): def draw_frame(self): self.screen.border(0) - self.screen.addstr(0, 2, "Online aircraft ('crtl+c' to exit, 'enter' to select)") + self.screen.addstr(0, 2, "Online aircraft ('ESC' to exit, 'Enter' to lock one)") def update(self): if len(self.acs) == 0: @@ -64,12 +65,15 @@ class Screen(Thread): header = ' icao' for c, cw in COLUMNS: - c = 'updated' if c=='t' else c header += (cw-len(c))*' ' + c + # fill end with spaces + header += (self.scr_w - 2 - len(header)) * ' ' + if len(header) > self.scr_w - 2: header = header[:self.scr_w-3] + '>' + self.screen.addstr(row, 1, header) row +=1 @@ -93,10 +97,18 @@ class Screen(Thread): line += icao for c, cw in COLUMNS: - val = '' if ac[c] is None else ac[c] + if c=='live': + val = int(time.time() - ac[c]) + elif ac[c] is None: + val = '' + else: + val = ac[c] val_str = str(val) line += (cw-len(val_str))*' ' + val_str + # fill end with spaces + line += (self.scr_w - 2 - len(line)) * ' ' + if len(line) > self.scr_w - 2: line = line[:self.scr_w-3] + '>' @@ -122,7 +134,10 @@ class Screen(Thread): while True: c = self.screen.getch() - if c == curses.KEY_HOME: + if c == 27: + curses.endwin() + os._exit(1) + elif c == curses.KEY_HOME: self.x = 1 self.y = 1 elif c == curses.KEY_NPAGE: @@ -145,3 +160,6 @@ class Screen(Thread): self.y = y_intent elif c == curses.KEY_ENTER or c == 10 or c == 13: self.lock_icao = (self.screen.instr(self.y, 1, 6)).decode() + elif c == curses.KEY_F5: + self.screen.refresh() + self.draw_frame() diff --git a/pyModeS/streamer/stream.py b/pyModeS/streamer/stream.py index 728815d..25013e9 100644 --- a/pyModeS/streamer/stream.py +++ b/pyModeS/streamer/stream.py @@ -36,7 +36,7 @@ class Stream(): if icao not in self.acs: self.acs[icao] = { - 'updated': None, + 'live': None, 'lat': None, 'lon': None, 'alt': None, @@ -54,7 +54,7 @@ class Stream(): 'SIL' : None } - self.acs[icao]['updated'] = int(t) + self.acs[icao]['live'] = int(t) if 1 <= tc <= 4: self.acs[icao]['callsign'] = pms.adsb.callsign(msg) @@ -110,34 +110,19 @@ class Stream(): local_updated_acs_buffer.append(icao) # Uncertainty & accuracy - if (5 <= tc <= 8): - if self.acs[icao]['ver'] == 1: - if self.acs[icao]['nic_s'] != None: - self.acs[icao]['NIC'] = pms.adsb.nic_v1(msg, self.acs[icao]['nic_s']) - elif self.acs[icao]['ver'] == 2: - if self.acs[icao]['nic_a'] != None and self.acs[icao]['nic_b'] != None: - self.acs[icao]['NIC'] = pms.adsb.nic_v2(msg, self.acs[icao]['nic_a'], self.acs[icao]['nic_b'], self.acs[icao]['nic_c']) - if (9 <= tc <= 18): - if self.acs[icao]['ver'] == 1: - if self.acs[icao]['nic_s'] != None: - self.acs[icao]['NIC'] = pms.adsb.nic_v1(msg, self.acs[icao]['nic_s']) - elif self.acs[icao]['ver'] == 2: - self.acs[icao]['nic_b'] = pms.adsb.nic_b(msg) - if self.acs[icao]['nic_a'] != None and self.acs[icao]['nic_b'] != None: - self.acs[icao]['NIC'] = pms.adsb.nic_v2(msg, self.acs[icao]['nic_a'], self.acs[icao]['nic_b'], self.acs[icao]['nic_c']) + ac = self.acs[icao] + + if (5 <= tc <= 8) or (9 <= tc <= 18) or (20 <= tc <= 22): + if (ac['ver'] == 1) and ('nic_s' in ac.keys()): + self.acs[icao]['NIC'] = pms.adsb.nic_v1(msg, ac['nic_s']) + elif (ac['ver'] == 2) and ('nic_a' in ac.keys()) and ('nic_b' in ac.keys()): + self.acs[icao]['NIC'] = pms.adsb.nic_v2(msg, ac['nic_a'], ac['nic_b'], ac['nic_c']) if tc == 19: - if self.acs[icao]['ver'] in [1, 2]: + if ac['ver'] in [1, 2]: self.acs[icao]['NACv'] = pms.adsb.nac_v(msg) - if (20 <= tc <= 22): - if self.acs[icao]['ver'] == 1: - if self.acs[icao]['nic_s'] != None: - self.acs[icao]['NIC'] = pms.adsb.nic_v1(msg, self.acs[icao]['nic_s']) - elif self.acs[icao]['ver'] == 2: - if self.acs[icao]['nic_a'] != None and self.acs[icao]['nic_b'] != None: - self.acs[icao]['NIC'] = pms.adsb.nic_v2(msg, self.acs[icao]['nic_a'], self.acs[icao]['nic_b'], self.acs[icao]['nic_c']) if tc == 29: - if self.acs[icao]['ver'] != None: - self.acs[icao]['SIL'], self.acs[icao]['sil_s'] = pms.adsb.sil(msg, self.acs[icao]['ver']) + if ac['ver'] != None: + self.acs[icao]['SIL'], self.acs[icao]['sil_s'] = pms.adsb.sil(msg, ac['ver']) self.acs[icao]['NACp'] = pms.adsb.nac_p(msg) if tc == 31: self.acs[icao]['ver'] = pms.adsb.version(msg) @@ -149,8 +134,6 @@ class Stream(): self.acs[icao]['nic_a'], self.acs[icao]['nic_c'] = pms.adsb.nic_a_c(msg) - - # process commb message for t, msg in zip(commb_ts, commb_msgs): icao = pms.icao(msg) @@ -183,7 +166,7 @@ class Stream(): # clear up old data for icao in list(self.acs.keys()): - if self.t - self.acs[icao]['updated'] > self.cache_timeout: + if self.t - self.acs[icao]['live'] > self.cache_timeout: del self.acs[icao] continue From 205725872a753a7004760aa40635ebd8c2c3d695 Mon Sep 17 00:00:00 2001 From: Junzi Sun Date: Sat, 23 Jun 2018 15:19:29 +0200 Subject: [PATCH 07/10] update pmslive and tcpclient, add support for AVR. --- README.rst | 13 ++++-- .../extra/{beastclient.py => tcpclient.py} | 45 ++++++++++++++++--- pyModeS/streamer/pmslive | 20 +++++---- 3 files changed, 61 insertions(+), 17 deletions(-) rename pyModeS/extra/{beastclient.py => tcpclient.py} (82%) diff --git a/README.rst b/README.rst index 97a2f44..4b085c3 100644 --- a/README.rst +++ b/README.rst @@ -66,13 +66,20 @@ To install latest development version (dev-2.0) from the GitHub: -Live view traffic +Live view traffic (pmslive) ---------------------------------------------------- -Supports Mode-S Beast raw stream +Supports **Mode-S Beast** and **AVR** raw stream :: - pmslive --server URL/IP --port PORT --lat0 RECEIVER_LAT --lon0 RECEIVER_LON + pmslive --server [server_address] --port [tcp_port] --rawtype [beast_or_avr] --latlon [lat] [lon] + + Arguments: + -h, --help show this help message and exit + --server SERVER server address or IP + --port PORT raw data port + --rawtype RAWTYPE beast or avr + --latlon LAT LON receiver position diff --git a/pyModeS/extra/beastclient.py b/pyModeS/extra/tcpclient.py similarity index 82% rename from pyModeS/extra/beastclient.py rename to pyModeS/extra/tcpclient.py index 370a631..6510325 100644 --- a/pyModeS/extra/beastclient.py +++ b/pyModeS/extra/tcpclient.py @@ -2,6 +2,7 @@ Stream beast raw data from a TCP server, convert to mode-s messages ''' from __future__ import print_function, division +import os import sys import socket import time @@ -13,12 +14,15 @@ else: PY_VERSION = 2 class BaseClient(Thread): - def __init__(self, host, port): + def __init__(self, host, port, rawtype): Thread.__init__(self) self.host = host self.port = port self.buffer = [] - + self.rawtype = rawtype + if self.rawtype not in ['avr', 'beast']: + print("rawtype must be either avr or beast") + os._exit(1) def connect(self): while True: @@ -33,6 +37,33 @@ class BaseClient(Thread): print("Socket connection error: %s. reconnecting..." % err) time.sleep(3) + + def read_avr_buffer(self): + # -- testing -- + # for b in self.buffer: + # print(chr(b), b) + + # Append message with 0-9,A-F,a-f, until stop sign + + messages = [] + + msg_stop = False + for b in self.buffer: + if b == 59: + msg_stop = True + ts = time.time() + messages.append([self.current_msg, ts]) + if b == 42: + msg_stop = False + self.current_msg = '' + + if (not msg_stop) and (48<=b<=57 or 65<=b<=70 or 97<=b<=102): + self.current_msg = self.current_msg + chr(b) + + self.buffer = [] + + return messages + def read_beast_buffer(self): ''' "1" : 6 byte MLAT timestamp, 1 byte signal level, @@ -91,6 +122,8 @@ class BaseClient(Thread): # extract messages messages = [] for mm in messages_mlat: + ts = time.time() + msgtype = mm[0] # print(''.join('%02X' % i for i in mm)) @@ -108,11 +141,10 @@ class BaseClient(Thread): # incomplete message continue - ts = time.time() - messages.append([msg, ts]) return messages + def handle_messages(self, messages): """re-implement this method to handle the messages""" for msg, t in messages: @@ -136,7 +168,10 @@ class BaseClient(Thread): # continue # -- Removed!! Cause delay in low data rate scenario -- - messages = self.read_beast_buffer() + if self.rawtype == 'beast': + messages = self.read_beast_buffer() + elif self.rawtype == 'avr': + messages = self.read_avr_buffer() if not messages: continue diff --git a/pyModeS/streamer/pmslive b/pyModeS/streamer/pmslive index 7221825..463a74e 100755 --- a/pyModeS/streamer/pmslive +++ b/pyModeS/streamer/pmslive @@ -7,7 +7,7 @@ import argparse import curses from threading import Lock import pyModeS as pms -from pyModeS.extra.beastclient import BaseClient +from pyModeS.extra.tcpclient import BaseClient from pyModeS.streamer.stream import Stream from pyModeS.streamer.screen import Screen @@ -19,19 +19,21 @@ COMMB_TS = [] parser = argparse.ArgumentParser() parser.add_argument('--server', help='server address or IP', required=True) -parser.add_argument('--port', help='Raw beast port', required=True) -parser.add_argument('--lat0', help='Latitude of receiver', required=True) -parser.add_argument('--lon0', help='Longitude of receiver', required=True) +parser.add_argument('--port', help='raw data port', required=True) +parser.add_argument('--rawtype', help='beast or avr', required=True) +parser.add_argument('--latlon', help='receiver position', nargs=2, metavar=('LAT', 'LON'), required=True) args = parser.parse_args() SERVER = args.server PORT = int(args.port) -LAT0 = float(args.lat0) # 51.9899 for TU Delft -LON0 = float(args.lon0) # 4.3754 +RAWTYPE = args.rawtype +LAT0 = float(args.latlon[0]) +LON0 = float(args.latlon[1]) + class ModesClient(BaseClient): - def __init__(self, host, port): - super(ModesClient, self).__init__(host, port) + def __init__(self, host, port, rawtype): + super(ModesClient, self).__init__(host, port, rawtype) def handle_messages(self, messages): local_buffer_adsb_msg = [] @@ -66,7 +68,7 @@ class ModesClient(BaseClient): # redirect all stdout to null, avoiding messing up with the screen sys.stdout = open(os.devnull, 'w') -client = ModesClient(host=SERVER, port=PORT) +client = ModesClient(host=SERVER, port=PORT, rawtype=RAWTYPE) client.daemon = True client.start() From 24f36586739852dfd13ead69dba2ff1d3f649a94 Mon Sep 17 00:00:00 2001 From: Junzi Sun Date: Sat, 23 Jun 2018 15:54:17 +0200 Subject: [PATCH 08/10] skeleton code for DF 0/4/5/11/16 --- README.rst | 23 +++++++++++++---------- pyModeS/decoder/acas.py | 21 +++++++++++++++++++++ pyModeS/decoder/allcall.py | 21 +++++++++++++++++++++ pyModeS/decoder/surv.py | 21 +++++++++++++++++++++ 4 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 pyModeS/decoder/acas.py create mode 100644 pyModeS/decoder/allcall.py create mode 100644 pyModeS/decoder/surv.py diff --git a/README.rst b/README.rst index 4b085c3..5eb26e2 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,7 @@ The Python Mode-S Decoder (2.0-dev) Python library for Mode-S message decoding. Support Downlink Formats (DF) are: -**Automatic Dependent Surveillance - Broadcast (ADS-B) (DF17)** +**Automatic Dependent Surveillance - Broadcast (ADS-B) (DF 17/18)** - TC=1-4 / BDS 0,8: Aircraft identification and category - TC=5-8 / BDS 0,6: Surface position @@ -165,18 +165,20 @@ Common Mode-S functions pms.icao(msg) # Infer the ICAO address from the message pms.bds.infer(msg) # Infer the Modes-S BDS code - pms.bds.is10(msg) # check if BDS is 1,0 explicitly - pms.bds.is17(msg) # check if BDS is 1,7 explicitly - pms.bds.is20(msg) # check if BDS is 2,0 explicitly - pms.bds.is30(msg) # check if BDS is 3,0 explicitly - pms.bds.is40(msg) # check if BDS is 4,0 explicitly - pms.bds.is44(msg) # check if BDS is 4,4 explicitly - pms.bds.is50(msg) # check if BDS is 5,0 explicitly - pms.bds.is60(msg) # check if BDS is 6,0 explicitly - # check if BDS is 5,0 or 6,0, give reference spd, trk, alt (from ADS-B) pms.bds.is50or60(msg, spd_ref, trk_ref, alt_ref) + # check each BDS explicitly + pms.bds.bds10.is10(msg) + pms.bds.bds17.is17(msg) + pms.bds.bds20.is20(msg) + pms.bds.bds30.is30(msg) + pms.bds.bds40.is40(msg) + pms.bds.bds44.is44(msg) + pms.bds.bds50.is50(msg) + pms.bds.bds60.is60(msg) + + Mode-S elementary surveillance (ELS) ************************************* @@ -224,6 +226,7 @@ Meteorological routine air report (MRAR) [Experimental] pms.commb.p44(msg, rev=False) # pressure (hPa) pms.commb.hum44(msg, rev=False) # humidity (%) + Developement ------------ To perform unit tests. First install ``tox`` through pip, Then, run the following commands: diff --git a/pyModeS/decoder/acas.py b/pyModeS/decoder/acas.py new file mode 100644 index 0000000..6666146 --- /dev/null +++ b/pyModeS/decoder/acas.py @@ -0,0 +1,21 @@ +# Copyright (C) 2018 Junzi Sun (TU Delft) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Decoding Air-Air Surveillance (ACAS) DF=0/16 +""" + +from __future__ import absolute_import, print_function, division +from pyModeS.decoder import common diff --git a/pyModeS/decoder/allcall.py b/pyModeS/decoder/allcall.py new file mode 100644 index 0000000..01f63e3 --- /dev/null +++ b/pyModeS/decoder/allcall.py @@ -0,0 +1,21 @@ +# Copyright (C) 2018 Junzi Sun (TU Delft) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Decoding all call replies DF=11 +""" + +from __future__ import absolute_import, print_function, division +from pyModeS.decoder import common diff --git a/pyModeS/decoder/surv.py b/pyModeS/decoder/surv.py new file mode 100644 index 0000000..edd4984 --- /dev/null +++ b/pyModeS/decoder/surv.py @@ -0,0 +1,21 @@ +# Copyright (C) 2018 Junzi Sun (TU Delft) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Warpper for short roll call surveillance replies DF=4/5 +""" + +from __future__ import absolute_import, print_function, division +from pyModeS.decoder import common From 6c5ae2141b8f741002d466866c59b753a64365c6 Mon Sep 17 00:00:00 2001 From: Junzi Sun Date: Sat, 23 Jun 2018 15:59:26 +0200 Subject: [PATCH 09/10] add example screenshot of pmslive --- README.rst | 2 ++ doc/pmslive-screenshot.png | Bin 0 -> 63850 bytes 2 files changed, 2 insertions(+) create mode 100644 doc/pmslive-screenshot.png diff --git a/README.rst b/README.rst index 5eb26e2..84e6f6c 100644 --- a/README.rst +++ b/README.rst @@ -82,6 +82,8 @@ Supports **Mode-S Beast** and **AVR** raw stream --latlon LAT LON receiver position +Example screen shot: +![pmslive-screenshot](doc/pmslive-screenshot.png?raw=true) Use the library --------------- diff --git a/doc/pmslive-screenshot.png b/doc/pmslive-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..843df1799b07fa55d39e5f2c8b425d1473f35b23 GIT binary patch literal 63850 zcmZs?1ymf{wl&;Xa3^SRcXxMpcXxO91a}SY?ygM-3GVI$3l6~@KF+!Ky!Zb9-Zg4; zjjHOduAa5`>NV$@6{(~ki3o=a2LJ#NrKQAF000PT007J#2K-}>=^D%Tj}?Tgh_o6E z49wcL;;)ZQ6gP1#H&sUqH&0_1bAYOqyPKQ2i)qL-EC4_RkQNhG^IARC_te8!x)Wqw zTQ*OR3sMoQ4hk}XPqVyO_Py6Gq8^Os)ncZ>McMy)52FOFlgy{+W#@O`w)zPTG<7=h=(|z5z3!aQ;D#eYKtL2N0@Eq37o)hxUVXxv37*FW zPsr_3^P1rwh;8;qN@D2|-?NlvyhYxLYekyY)HFlBCxr{1GT^jv^SuoeG6KZ`03rU( zH}|%M5gG>tAi6sRNV2UJZfD^;kNL>*MXP0xTRn$se>(~&x9&|IsPyRv21Ry`pnrzp z6bk6q!fsz(aSCTZ^VQSju(2rC)2-g}B`A@XmFgz5nn}hxc_^aIO`(=G>+H9ui~Pv# zr&FZQSVK;9%o=XcwQT(P*8H3z_Rn%AQ*qetdTKh1}n3?`unOObQsp z*Jm@W40e^Y!p)7pxQ*xL3)18XRIkXclE}P@fQRzTHZ5K=zvCLCkn$AHQ#<$=#vZUg}}i$dlsLW2fKqk*y}qL~X;JYx@KtZDQ4(E^hU? zSXD|D_3qP0gv|i4Qj^`NHjLLBF>g!24;P9Q_v)T6Dxch_9(-0ED}*O*Jyi)TY_j5V zI-O&x5H{kKN4f%X8hB5{VP||_lj?M(0RT3|rGnBE7xVzFKrWhEA<8;Ir@IGo|6^^U zC9}~T63;OFfSY0|MrS*=oqp!)uH~qN5P(o}kIyk7c|Uk6Gw1nU#n&2%b0k}RWEscP zO^}76i3_apTKjEZ{We54WCW#V^YFSckyO_0*nzG`aVloj;p1+a^w-vYjX*Z6x6Jkj z9Ute6sLj=r(u4$`nq{_%GIrZ=owP5WlCx%MMZYG(VMG$eUT9QS*Hg%9*0B8fzK{bL z2>ipn0Dy8R&=O#2zcJ6{)&3Xl~1R5V$(89RpAQ*Ij7< zrgJc|Y8tIAYkbV+Mh6Q?LurOZcR;>Ol8g?)i+9TD$Ovj4K4?DBhw&zW`UaQve*90i zJZqqI#cUFeT>8F)E}HwG<#?~Uz@j3ff2Y&X1-(Gde(3mFG9|A`1M{?UDJT%zs#i~JCnajUx$zYmVJ%ron4u|9 zi-Z4q{V<*&@a~{l;WIQX#NFx1wbyePPDZ~ertiq6o6Q{wch`K+H`f+Xu*Cl93}CgL z?drIA93oKs;QfA#+$6y>KfOlpV?OSeZBV!1&&!2sU=PC3Z)iC&p-=EM zpK({KRZk{#y0CN}Oi0LwTT*HrBF0=9oR%5iuQFu(ob_s`u3+LY7H5#(_ioFp0=qZ( zD~T=mFzR~@V67FheB|9sVi=BJ@;bbnproHkv3faLBYXU(QByfF3S8 z>JTN?EU+3%0*3SZTi!t%l7+Wl=Vn{*S3q5>s=vbfg~hfTvhP6QS2`!Dp{sXokp45# z*oO4f?W>)w6*+m$kf*(^Jb1z_pItBmmmk-LyWg;pB0SoU5yl^pXVL&tC}+qTn#Cj) zcx84=c#Pl;zbbb4wmyw!Of&$1q%31AkZ-N&=O|(kha;%|YA)3Kt7sc6Y5yQwQd(*n zvk4yjyhW5&GJ5$sbVJ8va+uF53{2LYe%R`p+f9#;5~Y}JvAiVoWNUwZ>Qc}9PE&Y{ z_;ebq1501F-tV2xryq&Uc9^hA2o-pgc+C7#u3*BBZ~z&?yw}5N(e!WdzoCYuiWco` z?fTF#U5IOF=9q-`^kagM_mzf&!_^BZ?M+1p*o_?kT z+>t3Ao9LL3*S3nHiQQGm`oz}0EM&~29|@75qvb=3kfkR80FcxaIQaH^lradUsy`Xw z#*#EAv~JnK079upIk>jBfCymX48{9r^q%MQS8zCN|AtwU>K=(B@QSQgTYsdIQ8l>i zV_7IizgaC|J>k*n0k;gDF+g_#DeA-I^Ud0w3OGQBsO>X&P>JQqRG&`Z;9>3tPm_`N zp3jB2aa@|pG-joBahnaazjcEQQJp5S_HVn9Y{u!?pyeiQ2+3g`C}=GLe$#tvBU9-O zkPddc0`eDWUG&dY%*D0Yb^~r2KB|lk{Pyc#JKUe710(|_XQ@(Sez5@zE zw|3Zbk8KQn#5Deastw7esVyvr@x8VX1rUU?U)YeAWRD4q#{F~Vk;z_C8qr;DB(pJ{3I?awoViEN3DF2Y#pUUb|r z{yNm5Z%tF!(N(u!F@~&`j7Mej-cPKailwD9a#PV^jxY=}UDYTHORLLlt@eveJJ8Q} zqO{J?crUosE>s3q690$GI6~WK=HA=B7Xu8$BbhjIs3o(xbz83-?D= zuru-gx1miTb&iB=>igUbg1%SaTs2)+<^| zC)wlbLXkuu2-JDi=QGjCYauRChH+eRYI{&*$ac6i-WTzMd37L1IZ*3PThMWSwdWh& zqJE7sY4ZuTxy^v*Gsjl#KBKt0E57H^dWDvyf&>MY4-YR8N+hZI7U{KmWP|XIU_zWX z)p4D+Qf_iZ5;KQ~=NGcC@dsFHj7{sgm7K;a_08&CPY87A%bb}H`wyEQWcBZ2MFUOO zA+@N{g8`^CmwKNcw~4f6mb14MKOd)(Y7ac?HXlnV zuF{W3dj1+IW|7{)A>~h5Hz4MZW2C(Auc)GuNb2qHkQ*XkEZ8FPr))Y>+4rAeYeB0iKXz#*GeG5-^DEiKfhBd)Yb*N6RPe89kxv8(ZWiZ496F`Xv?(6o zGxfFu;{owkMK|~yx3o%+hOM+uO&!wf(EH zj1M;63%lYtro7mcEkEQdct=?ZJUbYa>Ixz()X3FaT~3n(^wiC%e!ADXK{|dU z;OeNoeQ7Hr9`x0*e*-nQA;;ewp7W%tl#tW!Ooiz63q3kR*U2Q22MPUN~ zI^O=4d-VnIuRaW?4_m`II4P*MABo#qPi-idxHplz4p-g5wntx>&Ik^Dax$-Hyv{Ph z@XxXHwx)_3Kx%15{P{N>ZX<1-2Z`B5cJ$M=Mr@=LO;h^ee z*-LbZmYNrOzQk+EwSLRg^=GR-;rJGKk<3VO6YwCPM$40RtF?qbCB5fzPiF(+G0Y#A zaY&HkU)_RcY0#d>J~^TfBT@6{0Y%JHm-K8%&#TH2uzD1HE6$j!4@^Tw2*TdyS@rAn zg}+T!ybve9cyb>+uf`%5ySSC@ak#x2#qPWrJb$&lBcn)1X^quoM8Ffs&*flnv0UDk zRE)Yo8Z3aj&F%gqkU zXJE^cJTdXDPJ^|IqU{cld4%apX4y&UYZ~snULfbbacS$(as}Q{d*;4^M2cXdAVLXq zbaKYdz#P2hPlL(yhdZSFsj{1mo1>IZ;O?sn^$SCa#VNr9mHYXZf>VD|T+>1q=VW5p zlH-Zoa2p?z^N8l*Pqwn2-b5B?eRY<}%G=)VHp4Ykdop$ zdXER+s$ZL}-&xUn|2qdLk&;X$66Mcvu_ST#+~!PF$Rxi;*muHR+q#2uXZL!E);j6s<;2<4z_=a4ts{ObduxN2F;!0fLnES20|>Zc|(X{rz3< zPrc8oV>){i#T8RBULYHa3%DU0AQiUXUmbUK*WV9*9zH@3vQjEq*iO%gX?z0sU*3|H zk%Q{Ey9S>}6s}RL^)$9K=5C5(qZz+wcldUeg^0N1icfhWu#=0hv(kp)>;sp%|1Qrj zYdH+4SW-&t^xPFVZ8lp>3$E=6w-~47&H4(7Y-c$lWO#B*ApjB?K7Vq%9W9{~87$Y5 z%2{iBp-mU{Us~lC)9^uMC$?_*L%{jUF{ZZX8EJ3!b5pf|)f|2e+1~*p8xN?M1OQhfl%)fX@QD)%#o;W1Kz0=&Sv~WSN8?h2q}wv~J1^$dDn& zS1aESK5t)iUa`np^#F)<1uQx2+jd*Jgn;(dN%%TF!k8c31c1^0l@O=TNB8q}zfj#`5&3+2)CMlWtbx;z$Fa{EwgUn5N<_CY(uy{RUfjvfUH zqB8c|K&b)(&oh0|w*ol=jKA>TT67^;tXR+(Z1EJ&&gedmf`_{~FjP_-qb6`*uZKN7 zU+5t+kxO%)FGJpVe72f%#2>$U@QQMTfTg~-;aw=>y0a+A-Ed!TX}h{uXTHB`sLm9K zFJ`2rHyz->G_zltw=@K0sc&)q=q*sSjeagujlxc2S8mM`M*$?vdc0;XClk3`;v7G{ z?46;l!ql~Bbr`FqCuPwJ>Z4qe!fibf$ILWWsLxp*^XsKnU~ny@YrwJ;$-mK3 z_}yqD~;pMRjPGhYs{iP)%tXp0>289bO z?Rgko;TuAV&4DCYi$jE>-Q7tIz@M=tvEz&<0;rf+YWK)T*qe!22jD<)WHY*u)A8#D zVlFwer!1tO)LEn*t?8&8i}81%J*n!%|CtKB zZNMnc^W$cQ13;!}u=v|J;6Si|-&CHe^o?q~Ab}DRA&bgvn-BMse=uE$5^5|Iw&BI$ zzG{mrx^ih7S<|b6s0smOLRC@lh5;p#YE#esC7XmSoj0I+g-x#;pM#2=*jF1Xb^euc zwqPQMH?|HUwY4Dzk8~qSV;{fEsH00vj1EGBf$rfc%yFrO>b=*ff7i-UxsdEiZ4E25kW0oCMq# zJ@I9@#+O>tBZ46@8c_og}qO3vk&Y?eS4LNVgwkO-%R3i7b_P?vPx@Nz^1*I7!9fN!GyHxv^|HahYwRk_*4DQ z{BEK@y=H26H<1hNDQ!_)7{5eEB@olov>7@uDx33-6>Z)YYD|1UjN}ohu{8HZA+5db z)b7%2ts6Jjc*LhRLv1j41 zQmWXrLa-YinWBX`fYG*qce($1>7oCqlsp|R?olHbm8O*wmnZMuZ(}Sw4 z=CktB+a!&t?%WR#*AGou({szOxHS4gKUSvg0^jH-uzC91J*RzF0DqBK$sFJF!RWbK zG-fUf*sS~h)CqM<+HA^uQA_}Yq&<~mI0xS(a5VSp#N(@4fq`~mL3(u$9VWEj&B#4} z_3?dHdxO*as`pqLwX|-X2C=b#NqjyOKdI)F)_oIjd~j}K=`VH8CAZaXy6Ml^`-(b6 z{WW%nk>8&#UkoI)sY&=w2|CM#4=>rrY*yX2h7MF?2>l2sB+0!yp$NR~raAJ=V zCL?TPoG8NAT{KG?S0k+L^(~k|_#!)Z9ri9vjOZ*RjLz}?Z0DA9HLR`f-N3I(Z}ra{ zqae(yh3KC;7F|tAO=%q8zQ(jpOkmcb0LYrvTIAT4HdwtW6Gwz$g<6v86ncx`)+q)27VNI(k(+zX3= zg3rWBQLGAdnLtiOh~iY)_!0Djwv-?S_-!62fKtU82c*3(_lo0H31SbN0A3y))XHGO z?=nQ?lq@ap296Ka;Qf;-HmCV)3F_Lxr?qW71!g9vK9g?P%=!9}z$i^Ry?MQ_IhPcM z{55uMPM~kW1yfzVg2Z_y*!rU@Rn!au^fgZSLqC4NMh6=+zy!~vS+$TqNU+De4>3-S zN~Q}SZ=}h>V>r+YzBLv>_hdmsKv2?s3X9BmuG~NaC)SAg3FoI3aryp$IN-Y6oZMee z$n@9OW5Y`K?{{IEays?do?*Uak^V*3K0;#S-x}m!I%NBO-@h5#4f?pZRzt1|T3-(`6>&uAnPXqwd2U@O7?El$Dmgie4TTFMxs~)_IXC8H4Og4d$GTpy#bLYQY0aqV$a(gX`P&5mpo)z?#=nZ+ zD29Tj;En_U=COM8nD0A#R2r{`y(KN!6P(ntMc`&FUp@m(H2Z39RJ7Yx8lr(_)z_$) zM+5{oTnB!WRh9!AOAKqv5?`D3^YWUF_j__8$wksJn?#2cZWEI-vw?#6%N`8-*3_m8 z(~iFnNGX~VuFmYKH3D(LQIWv(y`FE9E+%uQ=HcwtKTY9wm8Tbd{V7|1wEh|io+&FO zOcfY{8p`(?se?Y@P1;+Y6o>S@*KO2yFr%7{L)NQUKQt!`A_z+Gc(d6K6M_d2Nd>o?=x-D?#kXkejKkkI4RUvAMtokr%MGwH5Vd}^ z&d$FwjvMlX5f-ssdQU()dl&oSp0J2 zmN$MCnb)sad}O;uwcq5WEgq&gwL>K@!JX6}znr&=L^SP-&D+p-50*`0$!GK#Z{ofJ z>GuG4JB#;~1ImsQ2eEkAXq(NeBIZddM&dHpJ4#$C2KVN9BFGt-z}ALed{ny!iYYfj z{87Y%5~^9i-E`)p?y?Zkhesv>RZoOd?E_u29Ma1AAa9j0WUEccU34xo zAk;9OM2bII@3b`)rCeP%f<8k`6|uVNC;jxBm+vvb#ypctxSwF@l$sH4>~^Z_8&ul4 zsKrLt)yRvyecq9F*?Y2*<*2Ulr-7k&n+pH*w0plD$BlI5gFgCWO=cVE_vRW5@0L)l>pkBH%vUm+7Xqf*hw+vHVZ3C!m@J%$00J693dxf)Ed9 z3m&o{;AC}lNGG9+hw5pDn5f=ODtS>a9J{@@ zQ?cu}Nq@mVM>ctQu9DJOgLp8)>-^SM%KLCJEbPZ5ZiIWpmfkAQe5}c>ml}Jgn{F?4 z;I@vu6Uk-fbvQq%=2Wis90XcJcvw}V5yID9We8VlTq~yB)im_<3frZTJqu9hFaoy8)j|kE^)FNP@7T?n4@aT3!CT;)Y1qf-303!kbzJ~-d7&O8}SkYPds?;r5 zNhfNhdLIwSAGE&F59DhcAExKf>3dyd#VWp7`mbib=h1LwjS%&3o6oQf>$W~Eoht*3 zIL#ijk>GB4NyglW9U&@RVX3VsM)NQZgfpK#C6wN7glniPBZ>89Ro$s~>UMgg) z5Z~5LAbndd{}dtG_^1oNsD%AK_YR45Ul=i>S(As!r0jiwQV35`pyIM%=IzzucNE$8 zzOaf&F^*X_cTx;erX=qZ%a%;T5UEz;Vt2aWB_Z;3H8bnXt+#pGFGO0+!3%w=(N6ir z^FyCRHA1REOpFJKaAQ`K!tKwqQL*jJPcm}(VLzV7)=QV0KQ^J1IJ`&xb;AQvy+ z+pKBzJryg_q0aH-_;*E!%4$NvsO!`xXv$ft;pnEFdoSBwWfE{uc%kn(P|$o)dpi>! zW7L+*3VhsK1#v}>fK<50E2&4=&`_;F_Yc;(<;s>SEwh7DItr=6 zEA7ygVj(FepHGMXqBqK0Ek`(L7`Sz66?dQYMBqf8RQvu$Bh9Lj;WRQTcMcd{S4P+rz z9r_aAjAXUnB{;i_0nV!W9YF=+`@7Z+nksw(hr1-@+tMQ2-1D05R5aTe64$@HPRSdl z5(Z83uaLhvzR9n?4`s;HQOgvSJZM64up-ing!T8$OoWa^^rVzo9475OpyL#VIk2GZ zlkd4DZRF_%?V+h80QMC_u89-_8>>eKQ53BAzwj{%v}XS?KJowNfGz;)+0>0A!&Y3a zSEyqs|HR;IqrSp#7#n4RadI=V$!GAQmjl+xXaooXsoNC^wgzZTA4298u+9un03Adrj!0!56T*T#Zi+)w~SU zPj~DKS^owwLjS^Z)tl-6`jmP|pYuGK%~q3$auvn!LI7mbmxr)kYF_AL7hTo0VaQcT zhg7gpzLfo64x#tJ-6xcg4ajdo3f#IMviz_`&Fp_^esFVEt4Deh0V_^=>8f((Iwb{Pe;vf!ZP*xQi?i$)vdG12Cd|E)IC-UGpeE1k){mVgDGp{a(Hn;4JGuEo zPwW*bwkJg)M0kS2Xo7YO4LDWEUn*+~R{rQWb&jRXn2?|~2GSuz_W>r?lT_0k6LE~s zpKG$7iVixti;lOq+CSLJ-@yd&VaAyd(Asp^4uJnxJ^oRU3xNym|6KixU;G`V&_y4_ zM+#8ht%djR_dxXD5DldMe`!hYuP)tHyPJ~x56@FAgzY^A9|PF8u8Z}0=Uti}7sJ<8 z@Bb*u5Bt{2()~Q|{7N;9b~(Hjm&Ji!eu_IMhhKE-GpKE^TlSUj_pir1w8DMs9Nmn_ z8bqa$Ubf?8L#LO@yVSgi(uZxEl{Me67-G0%>BTCxQK^7qLykwtb`>~S2x@;`HOE_v zK(W9z<~^3q9c>zr`i~->*Qn0^2Yw zzhZdKJ-cj4DC-z$Tb$|o2L>dobGxr%h3lc#f;p+$Nzy>Atc_yhDH6*H({G zC0i17Ovm5Y77Pfb8(WAUwwsdsCraPe$=FszSVP>?$>J>qzu|6~KANRTi2rB~<^R_b zcSZ$}xN$=$khhjF0z~Kay(9#E^jfTETz((xVS z;lQTe4s}YS{;l}$DtxrR&>Mr*#(I0&!eqLbc)WSvt_$Y2m~}+*kw~axvhZQ=DM#`w z9A99zaPQAzg)#Hnnq>2~02QmxLAipCfZh&W1a$|SOelX8y-QQc zPal{CaZuis)@~F##5R3&R?5Ht|D~2qf2U&=hLH+Z+Sxh5GE+_ZZh8v+R%K}JI3T|( zm7366G;W4U=48EP>~$c4xL>-)r=>2!B}&I|ANF=>czN6^91I6;PvN4#1C)>`K7Gp%r^hPx_mN~Hiy&$kiyglMZToQ(RM5OcopvUgw*s59@m6lGM(f=z zIJjPQ1nz9TPoIg=cV2G!E)c0^MZgh%YMw{gJV$v(_3B*MUl{!VI}>0+=iDXnr;8o) z2Rt;i3=GQa#X~0`)e)r*SWkvDF6?*PF?K!k_tLCEZS_hA-mDBgs0;v1strxc=zMwf zw0}Z)_JrJd>(Uplo>7oX&bhY*h5c^L@Cxwg!4PV3(<75oaf~e+I+k}yNHQ5ZJqAVX zRsloikxFx{JJ^WA6lQj0z3!CFga_Up#UeAvh~kqXV+Qw@t*AKuMGCXG4e4vegsdX5 z{oyVu|5F$w!mG!2@3=~77+W>5F*DO{yhccf3SUStnlrNPE&XR9M?vR2IT}Ue z;wI|9E6_P})}Go>LUk&NxR7yOtAnE`6hATqnf3H|{Bp~#T$~}Hx6Sy9t$A}-l!*=5 zcUT;eB0Wjhx6WA9xx{MGOzA(A-y?hHWGyD{SCtcgXRugE*%u4qw8*I699kVcY$3_D z`8n-?4Br}B_CplAzrw6#WPS|&T_B*bSPgVzK-7{F(yFAt?+)dE*8RWAe4zwB9^CoQ zpR_nU?{|g!>2#$NAU~J0Ej(~S|E9+SGdO^dnV_kx*Gt8pYgE(jsdrSL%HfE6*}JOK zj&-)l%B0{kD^_lJ7O2W9HPZrmfk2|4J(_ESXd0{?Ym;wehWk|=aTu}#%?g%V3_H`a z>R=+Nm~?w2#0SEp1`CNZdqd>sAuv_H4H=H}H#(Thw=%_HYa!OjS-we4C@{^jKpvT$ z(?i`KNv#s$o4|!+bP`=%Cp}&P7uCofOJcZH@Xi$fTd5t}oWuXu?LXP9V(Fk^0Aj6n zJ?miV1>_eRZtW>AO=4kD5c*b6#XQx@PbK6}dzH{||1OZoO3p-ZAnocLh64A^tk$hq26EPpU z@H6ABE-U)JG0sxkdXg|FpWhvPB8G&BoA52t8v_tj`pY1=II&9Yqr7}Z1=zBAJB?rq z7mIeiq4M_cnradn{L0X@TUb?pkVuvLiBg>0;rV&JtjJ1LD~RR#Ctiit!LK87*L~i2 z8`7Or77OW*At3HCIcX9?Mx*KfNNCdxl=+z7-5?;=)2uK2{fOwqb0~Xd$gO)bbXc4p_101L5b!FwN+Nh!e>kylDXNSsHE8k zkU48ZjeijpgakcW=u%hw(?I(2AwNDk{}U^H`znKo!a)_^sQcGG*Ydh!CAB`**x$XE z%d#eU#IZlup*x!JapWr!^(tG4xDWHTWaTFrX(wd`|MUk}F$niMn50oYvJ50dz36Gq zxgm|1YZIE6;@g^g*?s`iwAYomP^!Zmi>_U*V#P2yDJDDzmRMBp?D~Q$J)W-ko`;%%5sN{Z5VIM1B{cWOQ<( zY?P*3oTrsTEpl9REe<8FXI3x4{o%PsPyhlD6v1$i|FbN*D8&BZl-zEcsTtbOHmWeN_Q`=kJ1Jxbh-|Inwa#LhzSjfot^281$!6K+rRhAod73L4MvA z7l*B+3>dwK3bpsi%x-!NLvyULQGA?2(~R11~}tJ zs2B4R>P*4%PKU$Lws*HDnuTp{=e(}7%@QKVI3^R{JR-jS%mE9_5YF-lq>PG+T9kin zcrmd*)&ED9ofExw+c*u~Kf^q4`$y?LMj~U%9F1;a{%40AvV`u61FUeC=7&NKc(_IA zefuA85bTcW5(MDC$hiEp4cB|*yOeFt9w?zK{Lat!zN8N6vjCydwOAZXp4qkraG6>3 z0G%FJONpPzE~DMh1P?vY0Yhz~ZI&n1y01NfdP0vX!BR0CgkSmUGKCH1#f_s5=PP}*+^ zI!$66^SerpFARNZ6)awRnVK_-{nrF+1y5#sa+Pk`E|=;&-*eR@AYe}Ye>36>Olk_7 zZ#nlF6?1`e(UHhRD5pz3mQ2}UYK3Uwk?@L8izn=V-D zcd?x#^7l-5DCBi<@%a0^+(OgbY2>weD=)&c}f0ExwRl7 zy%y-OiuCBKZ;+zu&cYjyu}4z~$>6a%Nq5A*03<44yUZ7HyU zbGu_YaJ=}o>eE(U`^iTGMQ#1~PewY8T)w-+TQR$;hw;kNwPPh%P{Hj!RATdD||t@bNH zysGybVn}J)kdwItt2dK7$fWPot$&=*%O9@6@sYw#l1tFO%7CYwz@%KE-78|=+c}-4 zGbA1rwu*Yv6Z*cN$9FYa8;qtq-@O!l2YZcAN0*;9`;l8`S4e2Jvg2SU`OIU@ZY4~n z`SHgsMulQAnD_yB_TpC<=GQO)ISl&)^8J%=Ay<5MT=9gf*sp|9L(SebNKe*BF#9fo z{Z2h@#wn~nV#3Gi2rot>vsb@w`*i+DrwmGc7&Yj86JfEkhR74T#^|+7$yA{#e@9^fYdoTHM>Ig4V-c2eg zXo=rk3i^hm2dI(*SKZz&o~N#JE#H9`E{)?0&-?oIY$bD66<2|S?I&lSnuA3fuuRLBRiJ7^qcKm`#>UP#yK`0F@%${J~IY{ zE~W=f_K3866rSSGT*)P*tS- zQc|db96PaZ;?4vc&W$srKEJ$H9dc_Dn=)R4TJiUJdU6jke!iq|7KS)$=C-i>p=s&9 z&RJVQ{D2gX)NY2iA7$?VXr`~p0CILY%UC%3xx|p`uK<6>|FX|mxk;@SFn@@}(tfj) zV;!-WjIwxrdr>?Ly1TipQ$lk=R+KcCFbS4sKuUDiX+FH#{w%qO410%y_rB}1Fac>) zX;!baS)8kNI-L)uouIbE_S7E$w2w|QP}{Uq^`GvW*+^y}dzKgUH+elfoLWM(HnPqn zd|{nWK1nvwNUiKbdM#!#f0kDN6^P3d@s*)Q=h2EVN1;rvfRAzty-sS*$&QlCl}p|_ zs(ndF2SS(+c?S_{!HnYK)XSeoMQLJo3xFDReHb6zI9nR_BcqjNoKWD2 zr zjGOb-#gF6Cl)5ih0iIgg!RCE*eFZlO8+gyuD-$^AF~@D)4wl|ui@;GJf|#5hqXI|c z8Qi$D1RuB4F($=@>pFX0?{T!by^WU4`^wb~;dA9T0>|2ShmI|5SP2JMO)1ZA_=7wtW_40^g6($(m5(&cAkv>C#uP0Ktybc@Jv`a zwG*E`CvgnZoEloya-37nbZXu~a3JQAfj245_nFZwHscdG+yLugUWw&GPpIJ~N(o(+ zq~weMldUr!R!Vsntiv&xF(Ers<$YuAXao4nb^Sm-kPpq#Rol4M-jc0y`cYR>r<~r} zSZ4Kd%({gid*xBZ7Kl&(p;3EbYop!y$3V*~W$oGfRs=rIU^8_L&811-#9nvv9yBip zK_|og`HArHESf5elUODUi5vqdx47m8KKJDI|6Bgf}bRy3En z4->cxT&gM(lh~Oa6&KqtqfA+aV)uQ`)SVq2n`<&|(`Tmh1hA?Z+w}FEqy%DF;sB(w z2N|+>n7r@9*HjIkv&oC-X~sus$0z?x&XX$*@FXpt;(UI*E!xlppo*oD_^N$e3rY!Wr- zLbS%kETJulJ~CGWPX>HKWKeaz)GEv}_|Tk(YG_vmMNshBhd20zkI`S1YrL{M$Q6oJ z<1bWlTJQa46Fkj-U;?Xv>E!`cN1}dydV6rTLzAYTZj|>7d@CW`pDt#mbp5&9#9Y69 z`_@Gm+G{bVEZeeR5sI_~o?wvfEmRq&O; zvV4#Q<7QJx3POsKPstM_>zS4q2Z?I)g;t?v_=al}muOQCrIEpnAES_8aEi3)doArg zR5tDR#8FbTw2NSwv+E72KQf}5sbIP{wM=4GPH95z9)#WIwKds6ji*Dt8fVd?^AJCF z%Oezrw+*XxTL+SqODSL4Unri_Y&l=k7v{i%Xry^O$;fDAFXqi!vEnY{l>DRepe*7ky1WWm_HN5f!PpMr{us6}+&W4OUUh*S=1cp!n?n`C= zj}BSDo1oK0VAT)-buk!B!FWe5P)TOqs&u%({@sw$Yc*;0=Hvj24wRw*CUhC;= zcK70b6|!Jgj}m0%0oL6q%zIf(*;Ndy=M;pLUf6Am^b`N(!c=j=?&Q1ss1OQo`a}0I zb|$smujt{pXFbM8G#=tIh|xT<)e*gCX|e-Uwc=}(X5&-Tzox$T=C<|(+oeDV(w9+p zHypyLliPCk6B+N-L|Z6U1#hvwEB-Jh(H>WNaCa#tSF$D0RAHwP%)hNO|HmsL7aqm? zjZKkujsAZG~nauGf#Z2F3xRdSgLjZgRoe<1C7fr@53b@G@9oQ=Qw-aHq#*>zdPjcleMz7gtJD)<{1#v z>~+>bH%}DUtvmeOh2~I_KH51Er+-TR;d3Xl>12Kr1F-Ag{~7+lec)=qPj=qpu`U<^w+8k~CBD(me^Je1_(n(vBc`&QoZ~L!+ zd$wxx3-Wq|FMQRzU!Wy31a|Fi)>CH$5_fq=Zx!$Ca`5c&(0bUGbEH8z4+-8ap!0|Q z;2K0_y^fG!hbQE`Df{8Q&Bz?!L%iWD=A@_cZyA4)#n>D7{PWW&1Z|h{M?%xp5rB}< zn(tGAH)6?tnKO8>Gke_s^_6^A_nxnFn*K?-birpMc={(1lRUgl0r5ju79ddy47|;x z-8s6hy;J6kjLXKs+y&W-*;YI)GNRfNKGm(VQBTX{q!pwm{y1Ew6XE_U*j^pkSRRY; zb+Puf?CI{^_F!G@kt|``iiPO67Rcqvir{CAjt*CI1~&ah$aQ`Umi_e1 z9^){PlN@Z6(FisfWR06ML=0wpkTlz9$vpwx(fL??&3!}Yg z_RhoVt~ZxJits@5pY{=mFSII=WK>C}A}JV7*XHI%7kdnZ zf}%fzonjK>_uapKAIp@A20${L@giKTmlwR;3Xy)And5#=F1Z(xr<8IQAGnQwkJ4;I zgi}M2I$DSJp8bR)uL@I1z(v;U1t+44y8pp|W6SHaZ|Bnl=$p>hS0*HgoNlh~+o=2! z>Le-YXgZFOs%Iw>e@5I0?NQK}+f?SYkcV=oNXlB0LwK%Ye+_-k{ODg34Aj+kGycjA zYdHgcSe`A@zqOp9ulirI+dnJl<4e&9o+(t=FYtaFjmOLR*K{#9f*5CH?hfg?h*u74LlpIJ0hxF z7N#cLw_*1_qAo`C=PQ)lN(d_0V&MC(tyA(^ZlWxYRPpM~P{yuJw*z-~l>MSD8nax= z`afd3cxTu(kv;gTT0Dgc`DU;virkp465_~hXL90fFC+qU(0_PR;-fTMj&qlKh&#sN zIgWl0ng(o@K;AXE+-3V^8geMHf3Ouu&T-1~iJ)~xx} ze|@?ath0Ncs%Jm-sofa0QiYr!961s*+2`_#Mc!{?ae^OSkE{UA#L_>a7_FJzdhkB7 zKxn;YuG2LZ63|LCa^S3p(nZL7u4Ckw)*e8d&oQ0OA1Rl}JczQ8fM_bF2Bc)pakD42 zQG|}$Y$FeO^rxjo?CNAAU7e}IIM55y{Aq8wx$Y(^c(y+{=-e)7Nd;~5vt?MP;z;j= zBUU@)rnXX#O6-oMA!r~QUVwn9|M`XBXJN!)#2BT~$TZ&vPdUAro_&*0vZ^y~s;AgV zBQQI%!tG-0ZPX$uygxJ47@d+As!{v?8m3W&JX3OpC|M$d?t7R;=6Hv0@6Y^F$RKJu z%|S89e>394&}Bq_?OaUJ@fi!lc770V!O)deCZ;oVfy>q~7`Gy(+xC&{`>7DV8i=G`EA$RW!DPu!!GHSsJXc$f z)@pWE&Eyo8dkuMddA`}x7q=6ZiORu=dC^H`CsO`#1~-V>?)DN*lDpsn-<^Uf_Q;0p zom_W2q!ft;)%(9}`)4SCilZH97Q8eV#z)uIa#mlZpKBDB&d~|;?B03U@#C>Ojzu1~ z?|q@$j^I$JoXY+ZldqpG|JPoDwSQi!ZcETL;ZQa^%!_DAuYf&Vz|tjZkb1}VNoPqS zgqR&8k%*q~pL-KDE#SQg!g~vr$;b0U9x;YkpCl|E@?) zlpeUNAg;2{=AGrYRk`VSCbM*rx~eagn6Wt6bib@V-Z5GLC!fwajdRAu<84Hi-`AVE ztlS7@{N7dSdW#|yrN_|U9B<^OZ#$#p-_i4@ls(pck>)!k`5n)sqI@CH{L~N97WU=T;Lv+a-%0-$mY4*^WG&ann@Zl`GFqWXLny}w%cx? z1aO;PCTz?L4tZYTg3KPoor(3z~zLWDw7>h7@th*_ssleIh%c4G# zes&aq;7;{SXW@P2v*BKevhdm2vyvvx9o@ZH?@1pjfKd6H+j~8Md9gSrpZ_F6vSKJ( zECUJlEFfAiP?(cxy8{d;)m&}))%sQE*Zu`B0awJ)MzyZL&?gP=U3EL& zBRFD0`u(f#uLU_wtfb5ApC_gj+IjxG!vX+=9XcXwd$n*tc>8+84ri`Dkld~-Z)pC#rX${ znJda_iN-A*VI7|1shQhHDIE2)l{8IaXA34C=MzF%o5Z;g5UkG;DdRJ>p*J;}R(JPx;MwqvbNjZgi z*TjfZ~j`$k!SYt1LAGYp3Xl|<0% z(c&nmc0$2%VJ!?F4P<Pr~Sk3Uvm$h$3_2=qs-ce83zATVEJL%6f^I`CeaQtx5`W$x$1PG4tR7rD839%8tXE2dU^PGW)8I>KW-97ecP^3 z5eZfeNfY(40Dd@EOkoS@U9PF#U~f+k?WN@f1UUO@;>nq`ge0i<-$nW#o2ByCp*&$M zKIRfw`4fVj7*X1!uzz$g>yv!N(ftaJ!%uEycd)gLKyB9{`Sr0!xH=BNqI(d72 zA}x_Vq~+d$0bc&Ou!d010(mWZQe;-4fBx>CAr?MkyYHc2OXuO_=5)hB-Ocw%SIv=t z2WO7A*F*O)R)_C(5FNA@u%|aB)cZKR*Y~hdrNi=@*Sx@CuV95dsu-5kGvu zt|R8$-Hfkuqyi^LdyBx5boNhYB+sZOs)-F^^*wzz<^&?_@jzDn<%{i)ZbFjtz;8M? z!+91|?v5M;IznSNKCe<|r8qwe|3fZ+a3lKbzV+oWkvrrzkD0ay^WDH`$vc)Z_AkZe z1?^B!JLOQ#@iKwJ`vkA2JCzhJpV!;t15s}NhERrkqO|j=J)G<6dhB3-8>O(dz8w?j zWGfB=>;9V-nJ{BvW-3Z=N8-=%cP zf6ruAnLMTaDY#7XWEiI?=;J%cKl$ylH_D^d>bzaI6k6V^*kL%Jcy5b8yr6Vpys4wQ zo;76TVp8R{F{ub8I1mEM7rTE3Vv3ip#QGNg+`INN{H=_?+=OkWbvRMHY(iJzcdkWT zBklge*(=7aR@N#g4}^#Q?yq2=$bCZYux!I88UNxTL0gi`Te56ln5%V%j2Puse^1;C zAFKj(-$4TD$RQzKa15<1LuHOIr-r6Ziy9x(Bhh#h_a5(s|1-ET29uOjUO}Q^caKm6o&72>Vet7hx=Arp?eU132pWRu8o0aCKQh%* zx)kIdxI&dE{|h+kn(67{n@b@4SK9l(r!`dSC`pV*M0La`{d3afL=dh)e$cw9nHgEE zah~YWOI*0ALYe4L_cEe0i04Uu>IZ%p5dCOKq3vYDP>5BC^INV*RMh75N@J0DK)bq4lX5l8o>eP& z>T63p&}%VsK!rs^t4$&kbH{Y;`=QF+;P%Ns_<8r|okK?M{ThQ(sZ2tpE?>HQym~YY z8`Zk+rcgr+eT$pXo652Lx+9+@_s{>7gMC!h{B(EH5d*N^U!vN#lbyxa>=?{C6*K>v zMA^?vqClno2`#1nZ&Y;p4!4^#ry>SXKFOGIlwwwj*vYU)EKdT_D(vG_|G!zA!3A~=!y=|@8~J1 z>Gj##BO>-^+dfL9wH~ZruYH!Ee@l=hBU09%4s`On8q!b!>asbH`Szb(D%$2#Th&1@m z3a3mF7(v#C~RUiB4;w+7Z4_Ff`ukJeCP*8ZcW9-c~JVgYWkN|-9ukykv zDf%0Dr}Mic#wBHpc^%d4m-G;S4gc4l6Qvur@vj(vUl6($ny#v5|N9k$|Lc@NE1AWj zW`^J2qYGqSwMOjgL`wMAFz457Z#8LpejKiJeet=^f|%{_pTUgE_;f@I0Vud?8Pimzsa_H6We22HIJqn&aXjLtVz!P;e3waCDVGf2kEyR%uee1|Ex z`5i06@%kgdF0SQ~m&eTT#ekvds;4SN9uvavH(QZgcJTF+80%g%gp5$%K}^4mG?$LK zay{~o+Hb$>05J?phX@_7j1JF3CvDX8`EnO08Uymc+$2D1B!i9DajP!YC+2f{rycTf zxU%eZ%{but9oI4Y9>2X|?`YCo<{V%Z8TCB^3)5!r&+>(8p;6+;xmS|?e01I(M*0MB{cbFhAUA{LP&_B`nf8j z81G(B`QXA#;y&vDn9scN#AN}}k@DvuzgN$&^QyJ+N$@;LH<{`<8AHI*&;m5<$Ht<~ z>mg}1n^-o<$eqz$_qfJ}wDJgyJGx{^Q1txi`(#ZUR^C)&d5h&S|64!Q+?Mn-wSC)Q@UGgt5q@4% zs|YblmxF$A#@S&aW-;HB#}^B2OV!8uwE_x1@BZ{$x!P_e>JzL8Kf#Jzu+}?3QqB2F zBu5Aj1b{4l+F*u=_$%ESHmAe4W0y99)_O6#5CUFwwR@^wE7$=B8oW8F!8<68@NF5~ z6lW{GzUM?MIsPn)2Oj?|dPrAjy#RDCl@Q;4F-USru~c$GlqMbF^9;G*xii5MAohAg zp!&{!?xk&UyavX;YK@|k)qq>@Plg!m6|FqgX6J~LPp342B`0_kdvDsj&WbviZn1^J zBJH|I{0i3NJZ%ikDzi)TVCJA12;HM9Cn+Km1@F$hYRG^Uv7H8}q6_Gp0^Y60X3_Bq*`PzBwq58$2j<4vG zQy>4C0a=~3I3!!2~C;XB<_aF+7{M+e8h6>bsB{QY(|^9u_y zrl_DGA>x;NInO^lTW5jJ>fGnJCjZK8wwuG54HZM`@pjEU{H5Y4u9%;PrVYK(Tt6;G zCl@D`&gnr!oe|IggNT{=bC)c>p^0V?Mu9Dlci2&EBG-LX0nfpN*(6?;#Y?7ixH!(T zaEe2_vSO%)Xq=c8lFDAaa8F(b--!CYDQ2-ZBuQC-TAmB(FhlE+3SnC>_+N zxo*+76pZDFz_*&=R|?1J+^L}-5cEqHEy6FD5E3$Az%3#KiJHVnpG#5C5j5bW;kE2O z-1S9QZEV_BKKg9WI$B=C^;B?fcjBWNml?jh&rr%r3%&N_xIOol=HsB&>le_J)T zOBAAF`IGUcEx`P8^~#-(O*M~#LApXo&?UHg^ljj+Y+O4oSFO;%a$-CiJ|MKn2^r+o zkbXIrrU}5gji738`+6N6$Hfp@++{wAL+YrV;`=gqzI;ZcYhF~$%3O99RWGKj)_Y^2 z)2`#u{hE}yL91)+3#(|yYZjBH zHuX>20GCvKbFHYM-Lp3BAYterW4x$}zy9&H%TYqA2Y~w$+Qd=!r_0V9GQKHF)y18R2I2ru;-RV*_(Ek@>1xj7>idb-I>w(aIaRx z>KI;*b6H+K($UQQZTZM-^%vlywh)VWo`mS{brTv96K*KZ;yt!h!W>n9?|_y^aAvrr z*IX=x4a-P4Mi+^QP6wv{aqsUWh3QImDtXD6R*dZL?;^-G4T=*CwrPK@Ox6y8dgsv( z-2*yhgnA?VcG&eW$ijSw;piY!|44z(`DP7o43(sghZnpZ1fmY1@d4kVSvjlxVg`{b z3~jnhqMt{V(0~TsV{un`pV136mptIPxbdiDR(v8fzun!vx{MIsqD|19=yYF=76cNz z*(W#pRSoC{jca;%L~2SGZ$Go%svzASfM0pj%W%|9)%>ai+f%@rosGbzz*Z#C#?p6` z`rdj@+L0-5@J0a>>rjS1g>t!GUDXk;ylqUl)#2?sEvZ8C67qU zuPqr3$LY_9VtTpjSEVXa1iF+_oH)~TH+7K$1Z7LA6KK3!T@ijg>+p5dLIKL+f~Zw= zVSav_)#vR|jT63?IBTV$*_9$$D?Lbg&c~7J0me4W!=pH%0L_gl~rZCg-*! z+Vwa~L>5+t`(f^x$!P~26Ha(2Iqu88U$vdroaU$A+FtbzN2F%zYjUXt?hL1axnbS- z{bh9|jk`8pmcHEm>2m$kFRQQFC!LEWZe9`~J1*(EsW3w9(R=z0YR{5OUq86tp@otv%EU1z8+W^ge zYeqincKVc&U36wjsv&M;6I3uc;FLIF{CtbGP4^8awD{NL9g66WeCKO#dcKS^g(x{~ zoGu_ZftVO+oh!47o|EkE2u>CIk zX^g%DPO)bTHzbZhmF;*PVGF4o^$uB8bmD=si zwF~zof$pYGKzQw6bak_jSgq%REdd6lm1kP4yn`!qb8~Bz3f7iluhjh~z~+m^8YNVaLUT(?%ab!s=h1^M+QE&$ z20qsM8I^{H#zG;t0E@4u2ieF^UUE3RG7w3F3JJ|x?uT&OZfh9@^ys7m$1whb)?ldR z=H?a;X#Uot15eXUMf`f*vMV-@4j*>|PdD)2IrojyPtjgjp3x=~3WS_WH8ha+S>tWi zq50!~%&$?{yyORMwJ84}T+L?xC4kLtl+~H$3S$?Ou0RG$$$%_-sbCj%v~_;A%-Aw1 zN^;@FwW zObklOvR(14vg_-+?p$LR4%?cwBH8y+T4nM$%Z-^W9|=i^wK0%&_r##*GkQd0mWySY za8*?T2sR?Q#s{Ekv8Ym69~=re3;#+%6g*mL%ts%+XUH11y&U~Pc3Ld6;d9Z$lW5xb zra%4e<``~&Sh(G7w}@Zw=2al^?)1xpSL=FlrR%Teq(+o?rpAhQs^>#9r3hnyoC&@w zIq7%_o)1$Dj!wtnzlGgbiLx3yQn{?2v&X-ZgdWG@dzlT>e6`R=+*V(oc6Au;l=Z!Q zIhmMvj*Wh9oOt}eUY=D)d9rO5$ksehE!KR2Z%uTPi1Hm>BU_)(<`Kes1MBU5DF~Rz zN3-wxbkMvf1V1imTSGXTy*lmwn98&Nm|iFO|Yfa51>|UMUw!K`L65ZinaSzfk z^;Gd>&GYM)%|v@jS#6CO_`IuAIBJcvyQu*L zfPceT`UAv>e3mBqCV08pRWDtfCOJQ)vd8amdXwPm{^e8B7g#BfaYW!h{0|FoPD-bJ zr85x!Gwqq4^-J^4?A*D+)I32QaHf0#y~KM&sM5xR;$+LGyk}(KEhX#Cq1jN z0BW;I&FQ;yI`TOuIE4ks-k#>2ETuBYiPzHY(Lt*j1@cd{Uv3-#0Mznhd=E%<>5P1u zRNo2c`7WQU@~>F;i5YccXUAxUbZYy))_Cu4aQS1^vy2iW7 zD4f-aX7{9nbA=e1$9a@$3{(_WqJ1&IYr94;IMr&599noO+$V9h>7e{(ngP1xF=w0L z@z1R(YuU}R^pw+eBp*CCz<=oL1I=#T-h<})4fRu+y)Pr34#qQi z?)TgjQW#~mLT=NA>pQQ90Oh5n?8Y}ML(t^l)^{%D3zju1<7=>Lk_7r^X66lt8Qm3U z%k)eEg-~-TI_M;l25=1rJ5ynEkQ6}) zRnOw3uF^#v4#k6m<-DP3a;kc{_67dUpzCZmnkye3Z-ks}adiu}0_9KVLPQ73sVTEt zGkI%BX6v8VYwv^J*3Vk^uOBn5p+#n(e}a1RuVL`7U&iVL5MQ_~>63ADy_|$aQ3YAX zUly(@4w7Fi;g)bvc(nxY^&F)}I$inq z=8*1W31|lyGx)?t-12}pt)0`k$X;cIw!`HMFQd@Z+dVcw-J@@IrTalEuQr6D?q!Z# zOa9fWDZ85cf$u18Y4FPXJf3=F_bov#z-Q`Paal4@;LF=pjHlH8lG97-WK56#N}a9V z2hLm5lZr2SbiV9bC`SsiEf+iK;^@~LGei;+|AebH7bpf8jv0xx6 zJNi9#4-sBhlJCV%B8c`@()t_6SAcG7t#)84miz5$k7IuY%B`qY$P&KlFH(vItaZTj|{YO&(A^#KMG9gB8wr2{ zM6iE6ZrAbvw@jHDQ$|~3u3`fi&1PsCAaauc$_upeP;)MN$AikV35KBjYGzNv7fPxd z9ZBi^=dIZ1;J`nt0^Oya-_GH)%agYD9skAjYnW4DcFgBZ4%Ggm{UzpfKpD(00krL$C$&bkWYB)Pp+}L&sx$GC=lU zB<>={qIg7jm?8c=Nn_Gx1=pN{#=CT&ck;?WGlM`@p&ec~>~%nlfY0ov0Z6@c+zqV+ zU75b|Q@}i^e|cEDU3+`InCL+Bl=OWjvz;$JX!liUg1}eF<*qwURKDW{b6QzH<{frF zWD1P(vX+P~>G>^hc;=T-0u<{awHk_i&5XysdilpafY7#>RRyQ%}7^K2q?4y}a; zngUwPy>5K`0PJ#8WU%Wr5OqHoeT%98Xx#Nn1-#0y-Y%KgvK|!oQ7@Eu`|hcyabkHmkXEUy-~huP~?-LIvNYMsp7-EyD;S-z~yi&BOA! zf*0<~omZSPwUBB;%LU%<8%m7F(6OzHpY=fTLu&Ja=lAm!4;-(7+x|fR`(xWCLe}p; zNZZSPHKK3svP7arJf<&AqmHfTYtzTj&M0oJl)9_dgkIf+5Ir9^a{L&ac%4zyz~K{l zYqu=0%%0m!=(>jqr-Y=7(kNr8SW41oi?n0R$y#{5BA&n&KHGa21C4#n!2T++_B}Yp z(UK-vmuao}D`d`(I5o!$AG zlDdq02tl{Yt_4g8Ku)mpa6+o<;6#P-vh7!Ts?fx7pr&HO(d|pfTfd9THbdB$1k4|w zuB~+Zxp!Ub*|K=xLI?85WlYXRwqQA}El)9RYv6OJJOTPt`ZQS-g}zh!Q@onit2Laj~Bk17_7EE1Cha;TE>&oLP^fZ z5x@a+^1(;PSubiXLHBA#Oi+#0!I|d=tGwOuT042WuY66rSW035QuBVWsIu$$+_#XO zdLRdB2?ya%;@{sE){d#Bo~pE=i6f~%1o`)970K4rV1PS` zg#m9l_w+9df*K?R{~88vtc$ksom?ML{u-TU{}{Xr zxP6n#<4RU<{YHU)B_P^v^5s(^`QzYXdO*8QhxePgZG8A04x_2nm(=Haf{=FS7B~QU zg;4u5?T-b9-*UN~0f>(9+wW)EPo3s%hP@5M|FVRi0_+52SACdyDkEGh(0HHcs#RJj zo9FA)^=SHF#)`6JFPrlmGL*+L(pAf$4rsF&>2mZ?N?V-`ZUWGONg9a-R((Fp_-ttH z9~(2N+AnsdLxGFc!fsUtq$jA;|7^T~t%?V+GR4(HhzFQ!H=T^KM&)i^(Hip9f z?j5^T7~lVyQ(araPO*E+ewH>@&V1ih*t*|tx_sSDNMK#XM=v>8h(&3#(THh>=mrUp zCeDY@G&1?g%;>t-M5r*+$H!xsa(4P+A{zw1;Z8KXgF)mWUZbI2&Mz{d{cwyj7X1sa zRs$DtiJx6ot#NDd(V?IfN6ofTMOudCG9q}HzWs5%WcTH(lS;u8_5@j9+2UIBu?$mv zJ2dT$DA0bJ1`oES09OTAJe+szhIjF~-Oe{9!h6NEjPvu=upl zs&j?Tmyhbb+`_VXhYxLefv0g($VZe2WQgAdT}jm}4`>6OWe&4sm+`C4d(ARrok?3| zGGl1qL^bH-2{>)X1o)jDaHu;IeB`*etqsBVe)5!-?Q6y?T_xcu#@4f;Y)PYYFhZu_ zWnpD^o^6fv9jvr$b4xI<`$^}FwLpV4E+iuanY@r+p@qhEHFmg2aCd->jbKLOR2w4T1h z`%5I#R+6wCM`nV^bl1;QjQglS-0m(kxeH#Mv|64|FflhOPBLz?!(sNBoD5t+oa-jh zrSyF2d(cI_o%FA4zeD^4d#SM5i=v{u_XORC9tkFNFD(wJpqyOP*koGSZ2gDl51maG zkX7YCv<}~$_Tvvyyqe38bGf3A{J!M*9l-~t87Uf)Ne<$xeq!=oRzLr)XD?uKz#)T~ zk-g4O{?e_)uHxa|vyVgMq_@w#+Mmop7>MvFSOiCBx+*G-Pp`qSxp*^$yvD7`&Ra!`R z8Kzwbhh>n7kRQIYL2IW3xqPj!y@FKzsF(u!_*F#E;NFQLV%}W4;R=tNz3yTLkH_kw?1cX)YV%tf2zLikMrIM4LwCtFC}%Z9Ht!hLn0Xo8Ty3-x$Aeinwx z)AqGn6hqJh3>U#0nF>dFasj{1E?u2Aw}|jhiV*-nwl5RmUd{h>Zdw9q9szQr;N>za zLDB@>s^ZzfuKTR9f#4$hqVI;C-KQy*`b{zt`w+N`#YFpcfe~OpL*L0v!dF&b#{2q@ zUyoVXi4)JX^ZN5-mEAbDMG1bs}gVEWLY;A`i*U>!vYz z_iYHZH^1V8;x&yG;A3fvC`ZaMXYxVebe_t-5Ym(cCU?X1M-<+W_LR%JM+Tof&)bV@ zH8p)n-fwl&#~bB)o4Y(VNfM*qf1PLHv(+H&>W?;^tl5Z`x{~ma+Q^OKGF8?xU$5S~ zS3p6|ek1`@nI=}V3b?&dJTo*L;cX}(YYh_8hjEPF*CM~h!oy$PG-x$Z(YRj885mTGb!mM(iw?DGLiU0ui zS9e*Ke7r-xE!b>4hUNycBfw>tW>Muih6%TY=eeh_mC+9gs**;kXNi!}>|G{xA0-H{ z^njAT`{huviHhP6l_6^^wq@USTqsGAK}p*@UslFuE=t1#PASGcVjq3htnA9ma%Ve) z7|IV}lp7D59MVcVBO^)}d8|Umv$$(B5t$*PIP|b;-TJV;2{eNx_{L1r*jZD+RgpH79Ie&BOB=C(UT0_hJAZE{S&;W;Tj6;dj8QSx%adQ@kk_~Hw5l-)2VX{|e& z*^V^L1dFC_jW6l71~||$Ra;sq;LS*=INs$yK$1?Nib*imZFGIO+drcHfEy051(ySRo?V3g zw}uDoZ_D6pJlSf92amRntTu(SKwc`K04vW3m>BXmw*Y+hr#Q!7VzfTko)rK90ChKD z*C903G*u*5ksL-7*Ua^3568y&u^jf0}kNS|xj=uf&c{CFwF{T+gI(6zhqh+YnNf$lK?k#Cqd zVCSI-fQG=!)6}y+Wn$yR0X+dkR?-9X6`gA8{)X+_V!(s6f)R1Wwqx5~qYe}PeiB6N8>C~HSpne9lE zlghsu7LIF7y#fE-d~Z17C+=kDIqeKue7x*5_8+FL8yy?JT@%nR{0dGr6#2uNH@o8BIl*ws%NY{lx-N zI*t^NX(i|98gJ`VG;!QFArSRXT&@5)=ayQ~f6z7HJ~dPg>&ZAwG2Hf|%o;%#E!FUs zZOMd5_r8pVP@@pfunrL9S6B#R$R_wkUK4nS7 za-jERmuV*J!jtZYvoDj0A2l#F<+lBiSU?ApMIu*iF&?myVdY|5Sha3y1#4B`7Lpc)-8P?52Wg7wq&( z0+~`EK32ug3ouIVw7%s%l$qN<=c^6V{?R3quVLl;v^!6bXUkmjziIh@D-+;=&p~WH z3;%+{#GsPJyyc)oGsfU{lXJIIdk#oFc4B{Cz5!*vj6#wOxD zFALzcqk`Mhb@t;jgd(Ev66Ies(%VMH@iB>hbB3jy$TnPcEu-HG+`hm>6q^zpt~8o= z4$A2ylyGH7J1;Yt;#gPx;aNS*SCe>nKsK+M>cdq3yH>)gnQwmfe+YL?z)t&%S3jMg z%Ej&Xo&o7EP}+xfyJ{>AY$c;hneb2&PsPo!Y5%R?@wwKfUG6vs4w68Uq4o-mEy`=I zPbhN)6i#PYa&F_<6FEG*Gv+j-|L`w;Z{@iTmE?bLF@X>y@)MqMsGr%t;4WpzjQ~S( zGnPJb?A!ll5it^xXm5Q8eW6yT>|-}tiXgFyEF%e`ct@U6PZ;DjPXs{@Uz9dRNpP8v zMPmK!J6qrd&{b6ru5Zz~j-D@9GrV2qRZKX3^Lz{yyXL$1&e)vV9PR18vG+OCYxVY6 z)iSKP`^eJytEa6FfH4IZ#CX+|1POQoLKBQ%arF-=XX^1M&9Vh@PcfN_JmukitmrMs(l<+i{*|>-nTCkuMZm*I5OMcL2FXp9$r6WNiL-jZ&$|96Dh=#9 z`>k$<9b|7Tx5HX3l_MW22A5L-wqjTNl_&=V83@2GK7+MxqN|sjw6LroxP{Wd2<14N zJHKHu-($gb5@hBTGZr}m|bkLx&YHC@z}7S_7U}vVP_b7`JGO zPKvja%SGMXMAOpN?8v{>6UU z{7Pk_0!N_WKQl3Y^bVCdpMl?Z;6XUdF7EnLpV?(b+X7odM|$gM|I}pZTsC*M?#8i{ z!28>Y?9l4+yXfe}H^X6F5rGP};Da_%>O4&r1jh;Wa#Bo|2-!F1c>j)F$M9TTE1h$p z)U$%qmVJJYc+4@b!6dV_!SSJ3ET7yn-#^O(5-_KeZqyQd+@&G=Z82fIhbT5n@0|Nz z3WbsVXHUsn=+=#8pDy3um@_Lyr`Oh0mr=3eVc` zg>`?>-L)?MH-rl$;*JJ%rWSw+^110FfR^)L2dUkYw^_ggC z%M_2j!JxafEw~DJrv`p%2fDM+6d{XJ+MR|`X7=k)r$fU)svaTz5I zcm2WUlyoKj--W@zBc0TSZgEj@@gVo}v44ik1Xk2g5l5=!8xu3TCD%wgR_&0gE6w#L zftsxitK%8lW0#kJ)!PuxQr}!M_?v!wnbazb3G_BeDmX17Tw7lQHr_ss{>j@M;J>^M zCiy-EOIhhZw-;<-HxHTBym}yAes}x;hILl^-}HTusq3=^ZRMYD6CI z!_(ZNWtaAxHV&Ha-cRz5$uTk5I~rS{rZYpb$F(I!ilF@F2}5RlCRh(cV4M2VsrMWrLqe&q}huHn(I3LhUq&3Eg*>T;Gz=kEf8683s36DkvykU)q2SHJ@+^M zCjqWg&N5!<(fSMuq8IORU}(u^yuW=1h_ixx606KUEC)mw$z`!7{fo#4r2LFD zWL^jPEM8O^FVj1v#1_4AA^eSnb6k7}$#2I``xW~~^ScCM^b{uj!vY|0i0qgJr`$!P)Xm4!C&Q;JVHV{4XBNyuTBMLMld1(q^D5#Q!k%x%Av6ZhLuS-2w;3L>G3EejtmI#T1G3z~Jp7R9rN; z)|As^(8EQ$D~0 zCGK)*8%ZAopq2AZz=zCC)z7BOk-$12!(~ulL!O1)F#spOP+j*v%~+n3xgh(xI-k2| z=}}*oXD9A=CZjhQIoj1f_H(T4Q#6AQDPx?DthdEE)StB*6h8}?o$4+Y+(GM0Q|J2& zd3dwzNi3XmZ!&pofapIxH;idIFz#MguO+ehdi|)nvm6|zQ|ZyQbhJx@c60rel0%caK&*f~r}Dlfk%k4CIzwsx>~KMt47nH_2&_~PX@ z$JZI%MFbM941ju~5SI&q{#$2%D}SAOs415HVri?1P!>&&Z&d7ScsR50o8fkhl#COl z)NwSKRlt|bs8Ehz&ZYijIgH&hiQJe^DR50$;I>^P{XD)4Ov1c&nHfcC9@P*b#_XYK z^Tpe%)*B0lC>$$TOCY;i>*6+0bo^+pxJuQm| zfD!b$_kgZ<0K#M{bFDxM;s|B&U*mi39pAAw$|mw*?Y?zV!Nq)y%y5A#=R-Di^(!s(0vz zMg+<`%~`bwnRj0ldaskPcXa#i0YQPB>2|icSY{|#2B`+fEb+sFQsyn{-}!emwdL+p z?UBg7Bggl0UAp4E)$GGXKF~~b27Gl^^s5a=p+F|C-C=mY?k3`31z(1E7t^x8;<>bx zeJFAF2gc5SXPv)}Skc~pd!LjF@5rFu9D6d`dqDQ5j<@;o2WW0_HuV#1mkaEvEtuPy z%B*tZ%6~J}SFcunp0HpV$+p>}9gS|8WwRodOOhy@Tw*}N*^;=}=niCbu52`#?T{^@TMju>#*j;Zpa!Kd>2#O-yI##*mX{Du{S zILj;m_|#d$cLGz{I||0WEReAgWf|$JuiBU}o0ZR_5Cf`s8*@`e3(pQx*E|s&k^}89l zt!sxIOjv@Egc7L(NpjDIc$0f!lH;o@aVZ(N-gcU4_)mFF3@R#0Of5Jlta!%ALIt9D z>GJ`8J|5kn*M-l7m&2d`Q^xOOXI7jn`f@Hth;1jERRRQOibLMFP-e>F`$;h9orni+)FoMvAC_YMFUIFf1{CbWZ+wiTc4d zEUSRG_j)vtgBIH@P@=#IF=9m;yGga?hSwwiJq~(Nz^A?+@(su^N&%Ba8Ad26aKCS@ zM_a#bf7>%f$#S^+z_?{vL*`qMdZr6K#=-m)YKtre3jeMuwvRcK;E)BNPUn@58TJ`R%M7c&@N^t zx@Yh1wb%MxSI1|%kePeho^I?^feLl_)}zCFk*L2H!CbV!5V?n}pJ(Iq^S0vE>tSx| z(rI}Sq2SwAmN===OTSoB3n|NFkdWiKIX_T#}g51JFvV+44czb6-p!Usu zA-kOvEa>`JvAt~Gf=E$N0bXz(_^qJ^e=+Crp=YF7vheV{@#I4H0jBs4!@Mo4_GLTz zaP#-jgGY?7|9+`je@E4cMxJKsHXp9=NE`q z4VYDn@FF^bi&gaKvcoE;_hrc@rK~T+B{FwcPL^h5fnirRoMimtZz&U}k zCam9M6h#|Vm~lUY{Jh%g#KuDx!ozhZUuRH3x~7T`o~k5l%9!>83a~~Qfy6rd2;4R3 zROW36nfooO$w8g3s zfc;M0$bAkuQ^3ufkCgA7+F}Xw*Kez(M#59MTS8bB{^en5KPRm+{^st9J>?>#ix5$l zDN$c3tVE2u86!-qW@`@3@gu*t(?kQ&LqovfyJ`}!*GX@F0@12y`>Z9#x`S6Vxq{*& zsIenYIB2yP$xI|H&V7;>a#jA8UR_#KUaX76V(|^sP|xRKmPC)WUb!xi*YI|i3&wjj z(JyaUN?9Q<9ptnyAd9Hl{`Vq*PQ#(<3I$yBP6gL zDyx7A1JB~>$)^i@%Nvf~8B?hbC)8b8AGC}l{Y>nfeP#dQVgmLre%I@mwoCl|#)NO& zzx1{Y9_Jj@-*`SrrO^N7^L3e9H0v^@@RP_xJn4N(AsYsO+mEon3?s$~qn8t%>n8H8 z3AruKLaiWW5m;U`y&+zKYvw{ZTRp_rJqcPK`trc1<%p)`aK9 z?XYpP2!d~=GPy;#dsjLQ*NeOJT~Na)k{2|RCyFtCdksTb1{n6>{$tQfTz1qt*d9~u zDP%y?m63my6UH)`w(D*2OA7e<3pMgK`I5Sq?2YBnf=*MEoO@o~g=L;IwQXFV&La8S zj@}!JJz793;|E=>+WR*GHoq#w*tx#CkLWwGm_979fL<%9jIgr0-P#JR)e!q=pNe;^ zn&2~B`qlBTag`FB9pT5Omvqt0nbg_zYWK8*TCb8C5_q7Zs82am)>_4Tsp*QFQ$1$R zj;TlAMa*F?Bf>RH66%XhgqqouCJZB^B+gZRAf33F8(PNacKVwsnS}ihQ&Rmd`3_?* zPS)mT`+PgIS5q%7dpE}Gf$oK({+)B2#G~AOf-xEtgBe-p*wmz|#=N`#N=U553QBrEM7lyZyv$do5fo04ytHouyI-YcqUw5~|^XFIpca0_D(+6B35lhKe zecw0gUbE$X5BCi+1^S(&q>2Tpz2^UBQPeum%o1zNI}J*`J;z^=panO(A}3 zFUI<^@m_~LHNvH9l|S4?zgrFKdF%UBnj=O0OJML0Nz~Ie@wjlv<`dkbFz?@Q5PAdWV3T8 znQ1A4(@W5~0oY^#0i0rGnSAE#o`HMHYe6V#a~z?6$w#npErDUtE?wv1d^WTxWg134 zbYemS8YMQ2ZO(tW;`tfRdL3`CzN3|Y#J^O$66(&JY+QE8Grl8!Nr6o@fFAKNGq^;6 zFMtj}lsm?Ad$DNUr(t1CtI$6HG5cK!?iel*Yn(A_UQI0Z!nNtA4g7bifN$KxJQo8O za&0SfZj(?iyn4iP(>~9BTTX26BZ8cN);*oky*SE>QCH`B_RqsGElc!JQ%QI6{T4?Q zt;RutgBUvQ$$|V#X{#Jvw*8a*c8{;TMQj?kgw%F0tE4DX6k}<6^Gme`#WGvttwq&n z({}jzgQl!^HT;4J6G#b4DudR98T9R0WO*zvddST3#U6D=vo9a+!b*oS5pS}bJtb5Lk;6)k3)jn0xp%f?dp2d_8 z+BJvY_P`*7^Ew4jFh~~25eO5|5mLwSHQwe@`GAVSx~r-)WX>rIM z@0-zZ*%TS={!?S9<(KFDiJEm`Vhcrk@!~#2SWn4$y)cDbSUDu{Urr|HpOP#6sdZLW z8QvW~MaZ8`&(g@!XVQTnEz_ z#5pYjooH)?%~s9jjj{UM5-#-XGx!n3vIpVY;PZnrrU_O2i49~tFw=U;j3}SJY!w>~ zEvPqrilney_If*4OmL(hQ~NU+at=b*RLS`{`1g?3GYFfi%o>z>c95U1=Idg!%;Da*MGe~dITj-_WXQYX9RA|?$gk-NuTCcgM~ z8x_VkPqCEba(9ReM_h{VCDg52k!cbXB$-*gD!_a#*d>wikB9lT&81H!!>w((eTtW) zvDkQwp{^28Cb!$%eiq{;wLOXP^U68=@9pP4nVrH+{?7fIV$wVF8Lm0Fe;8k=zaifT z#=qYQ-VMn&GO_#5=MQc*HPyFv8mW0rfPw&w`=GUoN}n8%GR5EPopg0<-vS1 z=2*{O@0giMcTNsI(QzviC7rIv&6r2@Y0%h)_Y9eZTGvbEx##k*?0aQ9$-kOPRLdGC za0{mMx$;Jv!IO`+9Z^dfaHVxGxUM4VW4i`a zQbE7%ZK_D*Yz3T_Mi>*Ls(>D8vA%vDH9>{psV(BSu+4MUEBzAchYkwH$seMP+Z^16c?&s1D*=#8u) zAxKP01bV1r!t3$|M8M`7v1y45Pp}9d;kymc$vZ9Gw~kd)-|-0Dj*%=C>pE2X@ce^k z`kcyG9+Y22^M$9-RlLK4@u!~iR`^vgC#w{+I*k8TyLk+G{8#Y2Z>X&3(ik}b!iV*x ztbsJRHOSA%OWj%DyzvN029#adbT<3`Ft;pi_lrHSU*ZgUyYisqr5U)=>{AIvJ*7wt zo0^U;Nxusz?yj(<9cKt-iFkPSX?#}dFZXS^QqNC5gv^Ke!T>ee9^yx%htu0K6$c3w zO#Om6N@wHxUnJAyNe{;6gZ($^aywAI4usNL?l;vXQ8)cAPi ziHx}AMy70V5SO68)LkG(kq5zP-Y4|Uv0oswxK0o~*UTh11(MWB7ST7mXIr0Je5uZ0 zi{)?M9=6lNW;{(^1h7Erv`V&+FQN^kd-(c2Pt*R}bYk5xnx>7uN{X!~Vcl#oMbS(m ziI>Q}^IMQ^NP+j3F{)eUf|Oy9&AG~#fMu^l@*phDdkj6bKE93#5ue*> zRi!WFFq5xIi4{mH)1rA#-fx7DUj6j2C!p8UT&k6QhQzWrftQbdbqz+g^JI%7E85o4h;(GA;QIL&% z$SLYW1~Oi3v4{gv9S*@h6UA1ML|lH=yD_ZIDUM~rRD(&w{x~&hm~wJLx}(7~!A{}B z9NfO%m@JOp$^v#J{%~cs=;z=EeG6Oem+v9KVj!d0r|~}~9x+byaDxjGy#vf@UzC^G*c59~q6bc+z|@~RO00=JgP_k_ps znBiE>LX=mM0yw{xJ}S#iKwz9g1j$QPi)Ba{nlwq!Oz8{yAC~1$OK>o##V5IMe`|@3 zm5or4s_<=rRmo2lhmynn)IbKo`8_u?NJfhGo8b`tr@xnqQ~CLFmHv~DbWWXkNzEIp z!&e^Yv59cp}85`*EayKv0+}L7e`!mJ}~68Fxv;2 zhjB+zWK946Xegxuf6By(DS-u9;$|H{cx3)khRsBX1SjuBYBiv>hX5de>ud88|7(Az zTBH2iN2nCK^txJR{JBo{_Zyai(*Lp}cxU1u8NBwqy$Vo}cv0YZR zz1lG}8<}}w>2WF6lodhkz>@p*?`O0NRYan(Jos`y6BjNo&?n+#`+FYBbrQD=FZZ#Y z+fIGN5&0?~9ly?cVk&!~)J%=urWynwxKk#rUW1^6Nw>QkXS~PAhjh>uD)u4`&z)K<|*gJwz7SXJNGPFUww!gc6 z%4?LMT5{+}DRVN?4)e{$RMPAje4Iu_soZs8-Brq&@|r2wtqu>|K+_@jIca_4;o+YF zI+jeD-}%5ZCBKG%`-aQlRmTE$D!UCIVqn>Bh3$uq*>RAP$EeR}&0dJe5$qEPTsQb1 zC~r!e`SgfAMmt~BR^zW!?8s)~{vQYr&hOag$JdLnQH9jsHvGw$)y5us<;=aFq~_OH zH-2KT_lN2P_h7791;cd85j4aIQ2FF>&U~R^vJFS675 z13IQ$mulgAnEa>*1ykjNz-~_gGRt7LkEQ>P7}b#e7$HtW*aReFutK=0jsNWdSFn`LV7x5=MNjL7SXTgB(aM}G!e z5qI}hHEGB3zwi)WGd%3s(vfA4gkF06p?Q70y{zG*qA%xn^yuEZ9JzAa*8c<$_9SAH z5zdAWleT==M1!i+{f7nkW$-7nHNF^Yc%8%mKzS}OvD~6gb=tZdc(UP7x4wq z6{)U%9g}b#z(~YfTo&2fua=Xu#uRhU%m1~qH~|*jB8hNx$8;sDEC1rV+$c7<5+;@j zbiM5JzSrvDhIbsez4w^CpFO&LSdT#H3ZaG_?f*Ke7I`Yzj$Oi=)^fquMzd>O1`#^Jk&w9WN4wXmDd;OOg<4M;%Rq0eO86NM)Gh`@>x3v^ zh0?};&h24B3#MH+<%B&LBsXig7&t~L4JScl{P-H9ChwBufv>nLL4ajAx@$U<)uF@t z(llcW?ig{}EzBU5sPVzLt_WP;0sSBNOrz}m=im&6Q?KgwdTqqt9rolk z)L)stjo#eEX{REz<-V|#GA}&MT|+-(w56;`ZlK(#a25ru$4<+O-xbQiM|t?U=Ab6o z$~r)u?6lC9!}2bC80T_!ZasJAk3B>s4L>Ja!2C{~m6*DSi>teFVsz6qm{d8}D|=ix zJB5q>UC=hYw#|iCi`(f6^7GO$fExPaBe5QxSCPD*9;;>AC>a!#&P@zh(;e2{e6`HO zE|i!TupoRIUq1QNmI2;3*>>`RT@O5pl){fxow)DV32#Mw(wwbr1OQ+x>+a*C8e?lp z?v@Tlz}0j6%k9AHeWtUOsxEmpOvl+Y-jn}@&fA#Mqe#MfiT3a?!2s=ZqQ{) zP=kOde4J+P16rRAb-Ar(Km<-{(x2qN?X!i4VA-R}8oWMW>uD}Gp7=vs^@6n(T8KcD zmX(zK;PP$pNd34&J*OJQfAF%pMZydPDuF&R?jZ#v26NZWyAlGAA$X*JA=q5zcQPPe#@j%&_C-m}s*%-(ezP zBGqQ45uMyLd&$QZ5*@idsyni9RJSA*_5NNtcDSiNLedPk>vQK^#U5X*2WS z+CC3CPh5-*byGrLQx;6wV3CIZ@Cge&X}z_l++gwY?0P)9kit3{q^+m#-X!+3vlC6p z(9D6nT2U8e-~y(e^Q+W>I8G-$^zVea{0+Obo$m8$X1Su>yWm`XXso(>p0)dWeSOBX z)jp`2m+k0k_uS~`uv{&y4m7H~6}sq#>n~Sg(rvY~dHjKui-5=OHx{AEp@W!0>wci_ zYqgg;gnkZcn9f2M1)QZ{xt~W6A#U2DwI8`E4F;aiLFt3+4b<|FWFQS!B(^Y(*CbSa z!6adSjkv_!%x{U*MW2KLBu3Owk|#h$0{7FiT|aAPq`xD5-uhakw|sh6tfJ0o@H8Od zg6(=eLG05BdvIuqZMMJfMboKSmYng1GXTm5NNu<84UlUwM+^2uEX<|&VN}W41TBIJ zBZ4aKEr0iRnQ_H|6c9`T2=+v&J2SKczyIe)cy{#*eId1If4v4-)vR`n6a4k61%6$_ zlOW7(d5Fou3v|({rG6d2Hi8mn4;O2#s@=6n@7sg%h`8Ctw_i3dIua!P9JNvnLb z3q$StS(jR&i1@w5ecE7-w$fu-%++;FZ-wn>vxVbhaMW9Y8h$Jh2&}3e|C_xE4LgBU z6~k|LVTzjkHod2Y@2XaLQqC>5#9r%lRqT^~8YXf4#_hWETKV!*4X?Pv(OpOf0)^cucn4Xfa z66(VwP(?8zk=DK4wWP|ojqkrEHLpi1de{B1Xd4?XhHuTNp&ScC?W$y@%#xVU|MCNh zAtGtj*Tv7fbDt8|rJG@I+Pr*qKJP}2?Mi&DuzUZmRWfVg;j`%(9Bd~Vd;xUi1c$|~ zsJT0#%k4mMQ$=8UIWe&9{y6_w)Lx8ry#XNsp5ZOx+=6|kyw0_%_Dp#{JPk=C5er14 zDOX}~9=bv59Q}azS%Th|AIO&r0&MyS5^6LG)9Ka1!R*SU;TSMQWN4ItkNUK9dtN{& zOC^mUE0ZDU&9#rh-af{AS6j`r>HeT<`{fmGA(J)6^w00`k*n~6CSjTt_}D7WkKq)- zEjO}vi4Vk9t-OX%?Z0oPLj;FcD4@B)%>CG^v@fTlzWRF7vpAV!?Y6yOgit7F)t=TV z;h88v8p^`j*wyZry!b*ePyNyy8&G0kYFa8^jtF{}xnYo`e%L5D$A!)E8$8V4%n7hkH=r$-8|~C+R4wV3u%b7=S5&&zjYDBO`3R+XYXEu} zoC!urL%i*a{sQq;|ty0QlCSVET z@AXcXfKvk(^R2$nGv*k%$4x_E>Q+^_g*CoueBE>lS{kv^&RU%&60`1rs=FV<2%Ezp zKPjNC@JEN!=M_JTfr+zUT?U8q8NNn#?P%amA3(MnVs7qm*a9wWZSJ- zHtcLh=r7cV6hEP~xa8Z!WD3V)bHWL+kz;9(0n_i_Vn`U3)5#ovyG~TvH!qr+zZ*&m zu84WcKAA5n`ik=wi@v59LTZs~agfH9PF2UO4o#H7Y&lD}Dic+7fk_Q(3~(dAoMg4R zCr~AjugB7uP_-xY8G}LiND3j*^BVnpHTI1n&XpaT5`Zt5y`^VXdF%f4`0Lg0F2_3<8vxdIU zmSk4%OVliC7vv=7sn{u##U$4|3VUk-Pu|1`Cu^r*!ySnoeqX=NA}$4d?gYP<4^r9w zeQ9YC2VvidUs&97PHIg8eEe_VTeEzcs+jB;ak6=*B2*at1Sd3ZA2Ug7#f?*cy_Gw9OUaVy zS{q_cXKVSvhi3QHnxHAY(2yUEwSHFt{H6zEj%^CQMEkq#E0-%RqvF`A#|KHiFuZcLXGCm#^DpkOp@*`*(x zSTBdp?QsR;6Rw->+Am*d4{D$mZZSwlZ@UQ&8_d$5Z4>Z$|6}GJd?4xjBm-Ivx8v-O zu#k}q>_fm5JcMGp$GaG+?~&JSYf&rhCKV-T@Gn4>4;RUtL{CkK-9y`NEVja-J`%No zxOGgK#zv`fn30huhr&6(pJ+bSTVd0GlYo(Z-XjH~_IN0eIEJE%mP(}hRMcla>Q)vD zS(@SWeQ?j2FrI^)N*3(HKvp|zOLixSNI9Eqi5XiDvJ0nrs>6^!VM`~kjS0Su#iSij z0pBlxoa?V~8StpyO=931ran6?`8S}BMz})5yh>i|GrA}@LKi7|k!5iC!vm_tP60wj zcqz1Y0hw^PYjWygQvrGSjg4B@jee>B%K6CUXeub3tzEmhF&W&^sCwNYW(LHo@LS!* zaJ^t%zR*T$dYmqg%3XzZf{e6oS;M({*qUC>p|CCKzCA9~DaUP@Z_K=Qh4J=kiwjxb zHtW>0zDek4lRJGC+&O2Z$^Ew9`Xz?w?vuuH=3z*Y{dVFk-Z3W8ve$NoQvEL@XQCYh z(jINc>%qnJ=c|yaUr=LMv>{^MjXG8qa?u&BbbJ>Jj1v9IZ{+|M^{FEttD{kyXKm}W z#XVpIMdE;!u6q(dn{(K)Z^_T=8K+W`p@240ZU5%DbBT{1b8^1tUS3j%#o`A)cqj)> znHh4In!$6nz2tabE=M-qYnM?~&-Qgr=8wCxwAKca0Lgn1RH29HdlASk^_}A{K5+3s zQbOmy#ql71zPyGiHP}1TqrWNAr~kP?U!Jj^wQg>he=HLJ3l#xVVZzN>*o$!iSG+G% zU#U_ZQ5yd?O&A_y9=!Q?Wh5WAf;-OP-K~t2Cf~#oT~z6=y9}mPIpK){8URs0xxh=& z{}H2ln4#pMn#cJJTB)J{m3=Z|!g{Zd;Zpuf{jW&W&r)8ZfM2B?5|n0hOK|N_Sn(SR zSA(B^)Z0Y|%!;y$gcOczq5Uuqf2wC*C%C(I0AGTtL;@1t&n`P{PS3{2lne}jxoXt7 zV+|OF;Q1N|b97PpPbK}wl4H?RNCDWLmAcPd@Ic?96+>!kzhn@7(U>)}2?9l?I$v0~ zw&TBKuCpoxj)D_{>KlOeq}Z>r4~hC-M9gQqE#XuUg=BG@hE&6*Euf>frmqND`(HZT zN_3+7UrX*1J>J06GaW-+QYfS3DBuHahBB6yfX`F&U@3dY+4&*Sl;h2uQy?H zw%zED^PT<@tS6o5>S3Qdm;U^WB>d8G_wx~!9KZ^vG#V^*!GF>ydA1Aozbs<@`o>zw ziI0Hp=#E%I)>z!A7pEOiA7MNzdI#z^G?2&`l2OQsBE!y;a|UexKokS!ZJB~`1V?*DC8nK}`Fnv2Q( z8Cnn!XW#yCG5@0uI6}iR{3u4v0IW6ktc(zj57@r^cx)2-coxK0HUn-YI|=ltsJ*hFJD|q{N}$N6)L$r+CD%R;cR>&H zeLv7|88nUfPTw)%BQnPw{S%wTN_Q8Z%ji6?K%^6Ixh%nISYI}B>FO53U^@1s(DuGy zyvfdG>oLl!-qx+1114nwH~nB5Ns@Um1>c+}i__((ji@y% zZ4pZ|Bj=ND(RGT%*q8lnQ}y}xVX;x)AE25*_rE|tP85Ha!)!6BqWXgGD({bpBrK9G zlo!nyFslbv6`=2&R#WNE?_n{wyZ;#Vq*F3crItwh_YQSE*tG8>&{#oqcfta=A7sQ< zJEiHrpq*QIs===Dt-y|G=sZs{PdCS=%{{M)!ShEFrLde>Tep__AGXNSY7F5x?zal> z5K1i~rLZ$iU&S2Es8?LvOn<7GrhP-%2p98V?~u3P)@EwJDD!a%P7oCXW3wN1LjG3z z))k>{8TnTrWI_5X+VQP!sNNoJWS}R6qPeD-U)RFlT2TYs?*09-F>KI0j2^Wr>b8;w z<;4R!%qmk!#h^jDD@b7-lSJgvn^y)K-$Mx#ex|fT!{1QtDpaC{j%O}DP;h+NPFpN0 zPDs%A1{rj%fBkc06y6tm6#1sr!=PGuJhvW@qK)#V75kR2ib-;DS{|>0=dNqXpZsfB zIlslRkfS524O1)lsQcyELVixx_vTXvG6t9*E@)tgWCEHhxsVipWovw~(`vynpr4Z3t>}4f$WFJ=Be*l|Kdi_=^1eVOl!+US^A2*FU^OQQCKRBJ9P} z?YLX*vpe!vj4@3$HK!-#t8U^#d-;HzhuItV07q!xA<`eS5Vs7EitL>P*qR2*K9}3p zoV<@wnhC2fC6g!zIG$+EnNkes$HC`WV;NUpQnk4vddyZ<{eVH`peZ?fZK%71wg!Gu z>BEp9$DQ;UDmU-uJRNe8N?^7tN{=2QLHawu$_D|n_yk#H!pR%2hUJ<<9MbUcR*li5 z%lYyfMS^z=4NY3VwJsl}hL+v{)9d)|29CGMiAS=ov)>SSGiO6y#QrP-vX9ZYHMt*k zODaawdIijvr*r0hpk3IYW%CZR*%q0Ml|N8++WiUH79S`j8fi?-nB$0Qom6TjRH5lI zXVJ;FPPl$~^sV8bqV+|wZWfubt31yme6JFmi|)c8pUg|l%CD?~JAWZDH=?AF&h+je zh!`)2f$tJ@d0}rPe{(Bv-NrOP=0y5qx-(90&!9jzZTV%5bcu^T9gX(=$LC@e6srh3_ zWUG|&Ui~l_acf00-xx_268o9418bQpWIKHAkkpWqqyZ|70%XNz3(>S~EBxl)7>I{{sN zmRh_}!mG#1@N>Q{40tjueJ~hyz*cP=7@}kUpq>bP-ad5_b9_NYoU6FFEl+w-t9Yk% z_)cT&WZZ#`MRi!zJXmTdMSPYr!lz~2gO@*a#7RrO(sQDPX)?-N~d7qfFYPfc_bt#`{u`a*8!zsPQ8Nq z@KxTp!8o8`_9X<(G2?Pxr#UPiuJw1>zc!Ne7C&Ha2#Cm*^1bNuo70v2e6N$mJykrVvAajx36y8>kPOJG|!RNi43Y)e8PkBx$gOls}7<{QnvodB1OL64!g?zwXw zscp``ORrf)ANwhln(KaDsN{XOZM%<;+-2Hl*ki-s{tBsdqe;YG8$JL5V!&t?Y;?S; zD?6*u4yUr5xa6TXo@K?Yw4}c$a?YG*2+kZyLsW}hjn2S#dy(qRZivLJDH3v2_=x2B z1k)|~O$QsS)yEkpR6J10)K4^sQ@;PiJhFdc=zAtMcwWIL9& zk1p5=OKW=ocNiTm3C_*obaZt3sW0xUhIW@sf5--~lt?)@&^nh`bw|3<`A0iYhO&c| z$REZTj7WYM8<%aprl@rHf6X);vu<}JAs6vKzi~$7YoOzjg0Ez2mR#R*dAC+d=OsN( zIas|bRifp2$j@y=L}Z4dpMZ66L>OtXnC3AabqkA&?`=6M!8h;MauAlgY@YizGVSKF z=F|JOQx1jRtS3IV{*sJG>#+)x%Up}1F;Izljt$-4cGweJk{DO(sieWXZORFEr|=p8 zQ7bYC;XZ-dF*mUY%M?<8SPeFl3A6taj&)J;$je)#JQq}q&gi((X%i7i<<^-N3WNNc zlyL<^;)-%IDDSvtLm~&Y``sSGPLBN+oe;=}P?PZZThs=xbwS|hI!3r1Lm7o6`h(h- z$Gj~o*P6bU@5P!}P(nNZLiO)ql-&w}6^p%@Hfzg6es&}O&&=#YYvA>T#0KXoJ2|1Q z=MGgA=VKv7=z#AQl{`@;JB_AEvm2}3qMVgVGZ8Vw6C9+)``2A3z3PLMZYjyx7yE;& z?rDwn%o#ldAB3NNh5EYo;Wg6G(faULZ=h`VzGrt>_0XP(F@}#{jJ-#1%dQAF zp3pWQH2ceb5Kx{s_h8=Jc;^FA6q`kW>@yoPtNb^o=BwFmM)fgyLnq)eq$AA(QKSvhU8yx;%m^ zMZS@fG$gFm&)z-xQm;bhfKf&T{)H1h>67q|Mc1z|(NsQ2{f-b?b-xt%N*{ojj(*f8 z=XNIovY>#UZN%m=x#=r+NBW`>whRc8Z+koWFIw?UMhCIyuJPmfU3Yj~UV=hG(%djC z28L;8KTFmoof%XgH~j4;X@egf7MO_Qv3NRv6jdJyZ*U_df;r~S3npXn8~>H}j^->0 zt7pD!!MEjuF8LdFVp|R1|M*TJ@-*0v)A14L@Lrfrpmp5_kP)aj?BcVDP*waDZPqT7 zP)3IkJt;~$5)}b&IEzzKF+JqI@eB$6q1u3pqUja;Natzcu3t!AZ{O!=VI8>sByzF` zj5!5;LoLyIS)1?+jSKu@^G&eS#Pq0HiY@E#u8`Y1l=J3v(53de67R>`roznU*cY?3 zTQ4PB6f11`W7EUC@$VGNA$)~BRshofV-=0s`5$^zv|RT^3r4=+{vZwAYNjF$rGEj9 zXl11&HpSD%B?F)rm?WaaHDNRm^QldJ7%;hGP$i?zgJC(YT1F(IEUmEs1n?F=b&L1X z-}P*vwvodg)25rUIw~YJST4b0+g=16^d8({L97sc4^RJnFO zO@pdiN-GX(y_bXulXA_f_7!dUbzaqSUw#3>Ov(+@rHhH_G%P(@{=6xJ6?#OUMb4Aw-mt*{#fRW*n{#4X%c6OT;|=h2o$yY(l09ha%6FJUd_W#Wu$}8I zfnO19D@>TC?qK*KCsb_ycVrn^$$W|M579eH_~eZy?V-DUue(8zCVO8RaZ*^?8OSEQ z5?je28VY?NO*B(8e%WSh`6&PC;D~m4LYdO7-Au)Zp|J_3%}?Gb6%&?QF%GTxv>t8m z1t&e~%Vwc0JiCf#hdQ|TYR*6yM41L2mAY(Dd;m%+tBtGmtnOT(<1|F=1<7OWg6~`l zJd}v?_ChON1jp_1baL3+x%bB{Q@!G9XcVP}R!~I%%drc6c=l|# zFq&y{sxGvNExY)UqJLUs+e{tUH7EF;&O~SY)b7sgGU1EOxBzkT%iG@_i4I|FX?^ADDEUFiOtP}2h znA}c7$pEzNLoa%zs2&n%)>PWvs}|~9kUwAW7P38tghWCS0z?T7g=65J&b`$KT?2G{ z?j^ldhGb`-zIrSs$bP{hV zJ3RlEEV!PoPKg3)r0|-1q~QQtb_{}*N7eN)dr8_!zr+e{m11Ux4?O~&y=UhVaAD0; z1NK9Et3SR$`S?-S(|Bkt>wu|rIyQyPZ**Ik5gMpi*6^QzQ1b79&|6fPZLsq_Xg4?W zKyd9!Y|@Tx^0UOKkM~j+jsX_tc0BC?lzGv`L$wvV(Y#2or)?3KE=Q-JNSh&bhg9&E zW36o~0edJBbTBjr4DAF2&Fp!%&mwQK({V;5A?_xSJC4Z|fC6^~Gp zYh`8+@}*qel(`^fSQqtnzO)`bo^6-m?#Y&>A*)0tnDe6z9w_;btIfvG^OkYLaEsrI zTFzp5F-p!Ts4KtZFZ=LBh1Y8kt+(7LZ)`K8T$kRThuqnq+${3cRxI8AD+HtLalIJ5 z0>S1bn~uD#&QMBkbT^1yQyNvq$OUNL&Q%ZdI#Xg#Czq}~>AOx<>y>tZy7bpe{#Ow`#LB7JSJVRYiU}22-i{oeTN#bJSC#WcxqAi8*bhcY@RJhdNt9E?c<0gAu zPpM{BA0%#8$dHG*o!j8w3d_rf6N`m6^0wEFW{c!JvuZiIW(f*(kJ{7 z^_HN%)i8UfvoQ^8-}XNuz}bW%FYa-Qu>SmE*VZjwx7mZoCs)!VS41(H@*rqfFY_VZ zt3y})0A5K9S3(BBPrdNt4UjLEiiDbUxEC?7cuFT2{ImyzCnPIJ-6%EDEciK=T4$hS ziuXX-1H$oC5e_T3=C2?rDow~nQ0573=cG;O#PDHOnI+9f2p>1SN#fcuXdRH?7tGI; zEC+Fz?|1LgOF1(_VMLY3w9Hvaw5jH}hf^cwsOxOk;viUlpE_>Mkm}K~$HrWEB6sV} zvgh;8ZBZ}{1WEdrRb84RA2808qU9d3JB04`t2Nd$&UGPS|4h^) zwt3{U$*A3)>Sa?GdE%oP*#LDF^He*Tblqb2Q~EoxjE|h{gXTQB!`_29s0}oi9mLNk zlJ-WP;E|Dy(=leC)aLFUK?LBZYNm`s!{!Mbl`O?~3JLqDYK8T;CBeQx565uKPaWxQ z8KfTTxU8rv3RFTN8PaQBT2j*B zUwnUtqK191&v#wF=MgaR&{#z7+e9gDJ=f0VN=!;&xS3V}wr^BL!V7F466otNL;=fY zLtVJPoR8@|d9P45huF_9fD1%ZOerga3l~}HGIj7C@?U~4Ymi<-Pz07p(LlZ|bLF%|c z2E&Ip#9RJY#Ae}VtePg@3yDoz&b%O}04qXzOc{SPgKX8bUu$|Q>=p<+>@}5jOj}{S zE|7L-I?mNQNnX+~ zY8A*@)oVg+sUer@W0P0B#~>~XR{xG^dzy&b+RbsEy)RYhR?O|PXvp~B)tgo=hy=Vl zzAbd9@6ii5dh+;L7W!HONXV<#aY32)1#jVpgSexo_zll6bq_uh@o;sAH)5L=Z>)?( zBClTawGnwyUt&wU&AdW7k_aQG8R2;mWCYbdFLsQ3rQSX{+AL^hGBFaVD)18PP7&gg ze;+me(+0RFKoV{VPY}5IsMX_5_6C_k5$bPBcoD-nkvb%`;go7?RpZObo_(W=mUIi3 zBh)a73d#`b?;&jn3pyXX`YQS_=!rN_{K$=FKEF%mjY3efa;+2+8OH}56xX&~<+_Kn zk8cCpiMGd;mMRLaJHetoqqlxKmwfhiR3)zO7&R-W!f?py%WCFVDn^8S3bHzst=%9dIS zAyUf23R)TvzcxB7fmZAub)9bPCrJmk)JFD6ELWiNyb|`$ev*2dVB2lHOO^FvQ1^AQZ!5%haEd-b?DC0DQ5P6ut)-A8}#$T4B>x#rT1!-Q(Crvvz{C%v)@d1p)crk#2uS_4zF zcX;3FWR(J(ZyQJ(0^ng82H3zWjCm2CCd?FNBW)p#wl(3`oIr!uLN7)s^t&wx4UMS>-yTQ zNz<}LruRz2)!E=g89Y?KmCBQb(zwRQ#w_#Gi&L6pC}D4AMobeIl(kvaU~pzHi^Di#YNE-^&#&7 z>67p>Xk99}2s-%svM83yAzXq#FmOK6(^{Zi?6DU!xHg6Q;Gg8r`rJipBbSCbs23I? zxLPss=n`3GMrEGrAPD9{+D#Z3mL53Iii^4J^Y9qo>*G9hG*o#jWuTKjak@hp5RL*8UiC^ZZx+~v{r-Rk`XX2e2(r;W$65b);5ebXiB6L+7u1+WZ^K; zLGB);)+akGTtpcx6`aqv_CtjR_od66Tw~)Y9>ax32&8lPFL#Do+RHuBKl(p8aRrRs ze4gC+B#Tu6}vocTOOe%xDugH*ajO;zq*>~rsWIm1auoTxu`O+? z7;RKgS6ZzYrw|V#0Az#dbVK0tHaA*(ob37ejoMxVo=~^S^s%_p*z15z2do0N#Nn&*dXAJqXPe2|@lU`&&rS$sPIcXaVxt@mQThYJ6H9nMu#C zmAsx$v`&NJtHRQ8hGbCGKh;HxLhQ=S7&k#{HCPCLj0j<-8L9q8f6Qg86XBw7DxIga z{mF93gdD?!sug%CA;KY5YUYLgW0}fzp1raB6|PEtjH^~3z+u5@FOT!G+0es`yxaJm zyLERP$>t$inyBeKqw%GqQ^eNgVj-Q_>iCcs!2xk}EZ2jUkbl)^a*@CjLaR(OqC%QBY4)Bt}w92BT&`V z{C}l=bySpF8?S&!I)J275;8OvAsv!RDBTDs9nuWl(hbr`4G5Cb451)h1JW~u^bi6= z*BwyL_nmY2?pk-wJ_ATiI4LPAOOoGj-dUH|zjo4K*sGa=+3)V`K2)te zSKPFBCNQpckc_TpYms$hZYg9G+?*0BPU(gY#%?fKzQq?yF#z58atGGa^r>(e5^|(rqvDa)=TnW z0PasF8QsbbDv@n4hEy)?9<+AnW$vwy*K6jgiFZJ7cFa}1^-7G#+z99#H!Bx>UP0Wu zo9fkuTy|bx);dEsf=pkRH|IAR?K(f?R4X0eh9TbKv&BA2J@ym79rz$#m-+KlN_*pj zM;@n%{f?}kkcAoTo;Ep$JhnN9rFGGZr3glBqDY72=GzAf&K|wT05j_sHL;{hC<*QL zY`q+Xi1ZcH}SgfaT|xs?Yr;YPZ8x$Cu8Wm-AF_Q{Cx< z$q8ti&#$SDC$(F%@CTY34{)#SR>3-jT~?(OG(XKM%$B7|_Z>P=(l7x}?E5}pnq;Ix zR`f+E5ND&?lL2vD<>k0HRY`NfB2V0>2&bC-(9_!$1fWz-X=4XJ`?GA!iP&}V(g7U4 zfZ45E9H+R$M#RZ^<==4E0?wCu$!;?UHkwA=q;*&UJ$Ic>9@b;x1nr&THn)MtR(Y%~j%i z)kW+Er!dnN{G&K4$QsS2&%2uRwA6b|zk-A+tGFnst{~9x3-eO7`jwYR!6I_n9z&); zOVR3}7@p^AC9idQo-F+m5@~I~zmg@M_xb>*S$M~SwWePmbrn=hbYqwC^&3@_xfta< zDTzO+Sf*yr5)pcV#c_lIcoibbHP{q+T`>q6h(;XdIY_J!7^7Cic;8G?vo%`~61&1% z+yKTXSXQC7B|Z=m4wuU?Fsz7ZQss9^Z|@Y9ehqp)o=#O*O=_96|E(|Pm^?O1$}J|mO#ZwQEG-r1q3+L_(+wFO-0 z(Hyz4<4LLPO4p+KnH&A|x;#Vk?c|1~8Z-Y-^#c<4V!{ z^1XxPVeqqT_t@({{>A9Mcbv%*P0r)Z2g^*ZVB*MVVgGcXS@`gf!~j2cN#5YG(Z^QO zqHElT-BEd;!~$l5M-W|mc_3U_$!2;*K24dC7vw0`Am5l>aQ%zXtZA;xjAyGog2<6= zzXMiEdZ65AGXF;`H+td}% zeaY*}Ab-7-E2o39EgTuw(XUbOSMXT#zK@ zoj7B`ny;9MvKQgxt@1DajDF#$&&I1OBIvOTlZD0GjvR`H-f7{UVuXkZd9;oza4`E| z%V-(cV=Xq^dP&17L3cA@WXJZgBzci{9;!{iV=MU^q`rDbfXyx^EFm06R&lbn@m%L} z@1hDkW{tbH23Fhm-Uodd1Wgyoc#5Fw0?*Vq4`HR;2gGYERVUf{dCs)l>+r}YZ&jZZ zB)S9pA^@s|4DEnt_jTotzd!$I@09lPgxhSsRKe2b+zJrN~MyQU8MME zk&mxJg)j*)4HtG_#@PiD(i=Al-H)Xbq}G{#@hi!$pb{JPy^H%wtR9&vS`S-WzegH?DnA!FdQ6ZrE71<3(aekBPb{7F#xRm$T^ z+(S%OsV8VFf<8?lXUmp&WIcB0ge6Gt?i*|2nfm2j+sTO|((ZL%^!oG17gF_ylM$h) zwr?J>`WC~mo>!;!H1e)%-D?jk&$twObRK8#?{;t%Vnt`XmWpCCPg~$CSVSy;j;4b z%IJbn!w2nV#S_XAt4A41*CF!`@=2LPk}R942H`rX^_%#&k6zX!5I%}?WYRZ?ev0R^ zx)2TE*Nj^izwSzJMjc-*`g-}9)vdAXYlCtl@#6d0E3>|iK&nzn$RTcv9~a=xWM3lB z?+F{I>S20R zs5C<2?zo2K(IhMs-)dSq7RJp@I!d4_a`tLfeA=S@Z0}@)03@>Sz)aVA^kahD2tm_F zvQ7p$j658OJ~zD@IWomy8fxu#Gjx-wH?RX5^?Rw^8>g9ikUP7SF4CEb=h0|C!;(DR&ID0 z52_0h0cj6yXH%7)_(aT~fCQW7UhTknZ%J8c zye;_p{(ig^m{2JyjLF=fq>-p)mI$`QoKU_9+f}ZsI`NWqTY-a%WlDsG*J5ANkURkZ z-*jZHdl%h-I!+=kgXJJ|Us1z%CJ5!u#4r*(T!YL~k3oM+vzt^A$n?d5x5i)2UZG)@iI zp_*HFTly>`hHn}Eg>fNt3b1|40a`g;d6+}>3UzouylUM{R8T&}ZsWpzbJ6jGLIDUu zc}3D9MX;k;%4jVqp!AdG2YwJg{Qv{$x~bca*$;kFS0hGfr(wB*!NnpY$63S)B_!{E z-Bexw%EEoKZMB`F|M_%kQsa}I!pao4PKhPK z8eA<08=!w}Ys%-$wr*JvU7`Nkgr+`snd0N@T`r%|91-2rZPxoOpHXE+_aC*_sztC* zIY0-cYFDbmwR&IH1c)*k~I=Bzt7NEU-n+RmeKj^$g(0oZJ8%S zMoQA|_SvA3a00@OQ}Ypzf&}&!ZKB}%L}0&k_{Z|YRcX%T`pk+{4Q#p*JcRy+^X70L zUaWA&ZG0+O_Ue9G%?2QNsu(}i3t3r-^W`k(`xW~3Z(6p&HjP4mkS`adq^(!iu2GcB zJ{42JyF|hGP!*_NtY{a;=!rvWQ+}^d_rD;qKpQje%6B&-bTf2nK$9Z}>KsprADbSf zl9rk0;s1<*UDJuZ_Zj>wOAtAr`5v@pJwKIy+{A}zLR}-1<7^kqjM~CbLHx+id_zGI z_gZ`qu3EFvosy-r5WJAQW?xb?rNHBFhZPQFWp^?O1r=^ z9uuNMwX})^Zx!yB1Y~~~)y2-j46ciJTMpc=-v=tci1~hBgHk{Jf9TnHX#SF}D`u~g zO}dowTnf^#>4n0pv=5RPe#z7JXO6|u)$I=l=E0AQ?bGE`!0OvQSxaGUSXCZ-BU=qG z3m>|7#I1Lhfs(e6J6xQ{XJ=gLD`1Xu_YOs6m-apZ+^jZ`Tl<`P)gtT>gQwWHKpHKy ziskjIC|j}6`9wb>x&^c#>?lqgm0~|bMyF^p+ciN7zVz$a`XoR20e~{}F2d*s=oJd7 zv9m>fL-qe+O<8&zhtsQf>La@7^!%oGHummC<{Q^L(yzBEa-Oao-)#(dGgufd z(wI`dk|>O*>J8s{vF(2|!QGo~07d z`M-KCCswg6&pWM?p?4o7`)%re_u=Oxi~Zf@)8U?~0mIP;-rG?Ub8ExUdp_SBsYn>2 zLy_J_OCH|MEf=s@KG6a{DTSdjO%y&zO(m?k$ijr|hcVyMYwyh4jaSm2xD=^|;5|Xp zH)#k{s;qrA0mo=K%IJxu5>vf$ALLmw2;6I&Jz8UBZe`8K7efB{(3dL@lQ39_AK-os z)n=T54~D>t26Ds4Qa{}q)zDH5%h?5LVEw5)Kh1)3nl+K%pVTX`oFzZD(*QPA`4F_W z`X6vJHWhisK`zzrXJtDPzW!wVY)wGPK0?&^d%qu6THUybyPMeQn|+Mjs&)I+bAwB~ z%MU^bLOm2Wt1fp}wn&+fANpCkIDmJAJ;_U;f?cXivBRB7?tR1_~73}Y^VdGzzqNr91I%dj7B8SgS(Awl?ff>SyI>H z980(5+p#rojy~%_X0apFm)=t6N;Hhf9SU;Qamkk4I$aM2wi?_9p|?ZH42ep%l`{5I zC22O{UVKlv6GW`SQeP$%K1s35vOCFd3HL1d4mTz9kRk%VxRu>v_y?5x8B{IhJ?|$@Y5As=2Sg`TO<^&x3?3*ZQ?;DxC7ggpSBLJKbtt-G}dmB zRB|1o8-}(CYeDM)VbL9uK5y-)1PCxuGoK2Ij&F68msn%HJwN?d!03z0owRGCWy9%MsO$RP1>IjPN{*utVt)V{W7}LDKlu3dZaG)* zQ=NyuBXESV+Bu9upm+9MBQ@p&HcqkJ5%-EOV!-I!aBe)^(aG`t$>QWuz_XHelU@-B z?t94++yO+qCObxhlkxXDr=}BHJRes)FKUwmH z2xOZCB*hSf=0Lnw@KD|7UPN*MK;cW{{EaNO2rH&mrQ4u+&ncw|bar#8LnaS6F_~+& z+d18fhrkTNLwxV$ix24(PB@@i1o%jg0!8r{S*er0d~+;cp$Wm_QR7f)Yu1k z`L4Dgl2Jm1Xa&+dXR<#Sx@nk8;S!Wg^@cNZW5;y&Z z0b3K&9!ieOG$f;mf|)T1_xvj+pT8*_mwE>?=h5a6v;-)5OT6a8aQ*rH zBgw5uC(2&5Gi+2RH)UxO?JL)VL|MFsw>cqa7~^w zTt=;R^XOnX8?KMWTqoPT(KhE&*mR5wgUUHeBI+9`PXYg^K$$?$7;eG`Y3pzUvmO2< zDOXAd`Fb%>sLN`;YQNj8JY^?lusQoLIdZ`zrx?zq_^VGvPJ7&RDeTKB+3(88;O{JA zsFk1wF@YFVm^yL?`^0uq(Jm$r3)kX&Pv(iL+Tyj^EB$%gVyf zl`M8&B7OnLVRM&nk z&Qm!zBFQKk^JZtZfP{df604+q3@jQJX7Uw58(&?pcgVBU^{m;%4Gn21Mjkx{1E1N- zB<9#iLry~!zb`)qB=P4PxS`A47A=a*i09BLqGXs`Qs`cT+ow+=%9@*#T|ZLxX>e0d z7FyC(^*!%wFlY{U=9hwU&+(k0nfRD6f(y5@-#x5v2l{q6o~mMx7wYRc}}4wu&Ua( zp5Gd@d2V(;qSwQDl0=haGv(ijk#Gak!m?T+kHd|H!+i7y21!&RsyhYfNCnDi{@guFEP!wf+;O-q*H@=qRTnL_i0pO!K^l39!&0*>;C;yT zkZ9bG4C(p_4%Xy4q*JH}33^cb$aBBfB8<3ZPA3>+z;X7ns~|!pW{T(zS{45vU!l{c zv~tbI)Zu51#3s@~AHPQ%1>JkknR{x^bfMJ`Ppm!#(GQJr(J8byOOMFLu&L~}ca!4N zEv}wS@c7*bIO;ZT8T2?hn1ZM=IdJ;_aCakzrdfx0JEXg=4Iu55TyfaZlT)L{R~&o_ z`^FvRwnlTJzw#ee-gLjq#MB!3_5nS7uv6S&^k3FR0P&G z^1&86A8oaHi&l30Re=yeEx@UqZ?f$R8#R9Fyc^eD(pd^0$EPq(mx<)+IB*F=sTUMa zTnjsrQ3DSH=5()y;c@kTdE)B}0PnK=V!Kj=X7%+sXJ6C>fW|MZGdUG4Kj4(iE57ri zU}}=gVm1Izv#U4~=S-5P{ctZUSGnH^OEIyTKHA2!0Nn(2p8lsfk@o@g)zgOT*nn@# z{~$uym+vVl5~C`P968P(a6X7+AO;d#n}nKd>K9mjvofsgUxcQI9lc}aW5zc|mfi?L zUD20tQkA25lTW01nnb`>7U?h3iz8f)!S}`9jczIVp6X5S)VfLAx>qhb%>FknXG{Jd zrKd<-Q~BrjGFs&65mM!%1}mAadHWakwK69oUL;cN@t|r$On=aNFy&kW+W9Z+EK}DQ z=6)RI)H1K|E&aSmu3V_bNG^^|h`Jmziqqn1=#Bz)Z4i-3^jKKt&D7rC*2N#Rd7May z)0$2H=2_;xy`uE6$rlLG*00_Ys4L+Gw*P_T*`b3vlL?m1xQ93=0z6DwU);+rtl^cl ziJrX{Tb0<23fk%mtm4jS(1urK!5}FU4<|?04u_N8jTj6+!S`~+1>5ReBlzi%T zDE>86Gk2uvpfkAOM6ULueb5ot!@t~#Gj;Nr^w@$>QS5B& zhs4j)HEt_-`o;ZVZX*g#|MhL&pvvO0Y&SUow2ZN|Ei=k|9R%-mEUJhlJM}OE13J#763gH-rQcukHKSJNqyGZ!07i@wh;} z*T*B#^HEYUwY*4SShB|Xm)^gcHVl(ve|2Ll)8=5RsN4_#p^qM}en+rkC`F6$uI-i08WIAe zTKyFAMyIakavjp}p|KzIND6Mv2G`qzh7w6B0NnG6d{pSkXpS?P446}|3PMBMQQJt{ z1ZVj^8q|?M+-Ilt8=itBvy`ALo*RuhxjU+}!Gi8Ek-TCR4zd4dY$#A8^sD`(<8j2m zi5nN3lY6^1Nt0`nR%p?vws9$j+X!*))T;6aa1!anpfO|e*WfDhs-1>p^K(5va({D$ za177I+h=i-_)9jBF10c*Kc`1adsCc;BF@@pvCMC%+h^$Y!?$Ex4Pvt?X&VO7QO$>u z&(fW&zAj};sgAqvGLZM`4rp4R-lQPPAi2h$bmqZ$1<|X4?i#Er0XllY74kB;`E3wX zdrg9PS@A9vI;ZAyN39WTicT8_Z+epPra5b|GW*#_jd1Y1DeqZmGo1$i4{MYMS z*CBUq{TR{z~`?zohE0TGV$21n_}n`b5{uP7c$@A3Vd{P;vw0wQG< z*t_BKs=YQvEevouaIeSnGpy>4Mx&bFYk1Kqg+t>011yQTz1D`8#j4j6=uYc>bHn}{ zCA**CQQuCGKeay|?h_3AnXUBe?@)Rn{2p+%6| zV}ss-aUnkna;yhMI>j%$ROM=4!k76}>sxfqA2qT)GIGC+)1eVs<9_*cC+A1NT7mQV zCt`J{4TQtAw9}qStzglmWl)W&)U78AEERcE9X{#BB3@_q%&XCg5p*vQ`(kRNL{8v^ zdC;DZ9wcG)>XmTenoYI++d~3fhmp}&wsHa&9P@sYW~&|@PUaJ*2qXFx9jfMjLe20D zsGh~+`eW+$3H?5IJKpV1G|yt;55`xtJi00J@Z0U1WNO)JbKfpgEK1}nob9f72@4yu z(gs_YL!ey?8J1y;bOS2aD4bNTrmDur)dCgr?h=|27HyZ5F)c6W03 zy=Z*@cJMnXX)Ov6$<=}OQVk42+C%)4c6RRmw0FVo2ch@Sw_ZDWFl+ZGDH9;%(UpjEc+hEpUY}tp($gL4A3?t-Z<&IVatml+{5wfD zAeP*c-@mVFnJx0LPPU&|-2#zbhA@W+g?+O}n;{z;LELAl7{5+>CZ?ZZhJ%#9mZToW zKNsg;dx5VT*JUzCjrgPziih#)YuB9#-KKMjc!k~m__U9KbB_K$Pl)Dxk@H5Ct{66* ze?g756ZofsE6iJ}unbzft;{2Ill(2we;!HB@-8eI%feV9l;sM2`t0}C5p+(F8#r~X UBEeJa8tNx2sqnN|+`#w$06>}!UjP6A literal 0 HcmV?d00001 From 5fa090b95f7e15bc89d1b7e28542cceae6218beb Mon Sep 17 00:00:00 2001 From: Junzi Sun Date: Sat, 23 Jun 2018 16:05:06 +0200 Subject: [PATCH 10/10] update screenshot --- README.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 84e6f6c..63578b0 100644 --- a/README.rst +++ b/README.rst @@ -83,7 +83,9 @@ Supports **Mode-S Beast** and **AVR** raw stream Example screen shot: -![pmslive-screenshot](doc/pmslive-screenshot.png?raw=true) + +.. image:: https://github.com/junzis/pyModeS/raw/master/doc/pmslive-screenshot.png + :width: 700px Use the library ---------------