def detect(self, data): n, detected_mode = detect_data_format(data) if detected_mode is not None: log("Detected {mode} format input".format(mode=detected_mode)) if detected_mode == _modes.AVR: log("Input format is AVR with no timestamps. " "This format does not contain enough information for multilateration. " "Please enable mlat timestamps on your receiver.") self.close() return (0, (), False) self.reader.mode = detected_mode self.feed = self.reader.feed # synthesize a mode-change event before the real messages mode_change = (mode_change_event(self.reader), ) try: m, messages, pending_error = self.feed(data[n:]) except ValueError: # return just the mode change and keep the error pending return (n, mode_change, True) # put the mode change on the front of the message list return (n + m, mode_change + messages, pending_error) else: if len(data) > 512: raise ValueError('Unable to autodetect input message format') return (0, (), False)
def __init__(self, port, connection_factory): asyncore.dispatcher.__init__(self) self.port = port self.a_type = socket.SOCK_STREAM try: # bind to V6 so we can accept both V4 and V6 # (asyncore makes it a hassle to bind to more than # one address here) self.a_family = socket.AF_INET6 self.create_socket(self.a_family, self.a_type) except socket.error: # maybe no v6 support? self.a_family = socket.AF_INET self.create_socket(self.a_family, self.a_type) try: self.set_reuse_addr() self.bind(('', port)) self.listen(5) except: self.close() raise self.output_channels = set() self.connection_factory = connection_factory log('Listening for {0} on port {1}', connection_factory.describe(), port)
def log(self, fmt, *args, **kwargs): log('{what} with {host}:{port}: ' + fmt, *args, what=self.describe(), host=self.addr[0], port=self.addr[1], **kwargs)
def reconnect(self): if self.state != 'disconnected': self.disconnect('About to reconnect') try: self.reset_connection() if len(self.addrlist) == 0: # ran out of addresses to try, resolve it again self.addrlist = socket.getaddrinfo(host=self.host, port=self.port, family=socket.AF_UNSPEC, type=socket.SOCK_STREAM, proto=0, flags=0) # try the next available address a_family, a_type, a_proto, a_canonname, a_sockaddr = self.addrlist[ 0] del self.addrlist[0] self.create_socket(a_family, a_type) self.connect(a_sockaddr) except socket.error as e: log('Connection to {host}:{port} failed: {ex!s}', host=self.host, port=self.port, ex=e) self.close()
def reconnect(self): if self.state != 'disconnected': self.disconnect('About to reconnect') try: self.reset_connection() if len(self.addrlist) == 0: # ran out of addresses to try, resolve it again self.addrlist = socket.getaddrinfo(host=self.host, port=self.port, family=socket.AF_UNSPEC, type=socket.SOCK_STREAM, proto=0, flags=0) # try the next available address a_family, a_type, a_proto, a_canonname, a_sockaddr = self.addrlist[0] del self.addrlist[0] sock = self.create_socket(a_family, a_type) self.connect(a_sockaddr) except socket.error as e: log('Connection to {host}:{port} failed: {ex!s}', host=self.host, port=self.port, ex=e) self.close()
def periodic_stats(self, now): global_stats.log_and_reset(self) adsb_req = adsb_total = modes_req = modes_total = 0 now = monotonic_time() for ac in self.aircraft.values(): if ac.messages < 2: continue if ac.adsb_good: adsb_total += 1 if ac.requested: adsb_req += 1 else: modes_total += 1 if ac.requested: modes_req += 1 log('Aircraft: {modes_req} of {modes_total} Mode S, {adsb_req} of {adsb_total} ADS-B used', modes_req=modes_req, modes_total=modes_total, adsb_req=adsb_req, adsb_total=adsb_total) if self.recent_jumps > 0: log('Out-of-order timestamps: {recent}', recent=self.recent_jumps) self.recent_jumps = 0
def disconnect(self, reason): if self.state != 'disconnected': log('Disconnecting from {host}:{port}: {reason}', host=self.host, port=self.port, reason=reason) self.close(True)
def start_connection(self): log('Connected to multilateration server at {0}:{1}, handshaking', self.host, self.port) self.state = 'handshaking' self.last_data_received = monotonic_time() compress_methods = ['none'] if self.offer_zlib: compress_methods.append('zlib') compress_methods.append('zlib2') handshake_msg = { 'version': 3, 'client_version': mlat.client.version.CLIENT_VERSION, 'compress': compress_methods, 'selective_traffic': True, 'heartbeat': True, 'return_results': self.return_results, 'udp_transport': 2 if self.offer_udp else False, 'return_result_format': 'ecef' } handshake_msg.update(self.handshake_data) if DEBUG: log("Handshake: {0}", handshake_msg) self.writebuf += (json.dumps(handshake_msg) + '\n').encode( 'ascii') # linebuf not used yet self.consume_readbuf = self.consume_readbuf_uncompressed self.handle_server_line = self.handle_handshake_response
def received_timestamp_jump_event(self, message, now): self.recent_jumps += 1 if self.recent_jumps == 10: log("Warning: the timestamps provided by your receiver do not seem to be self-consistent. " "This can happen if you feed data from multiple receivers to a single mlat-client, which " "is not supported; use a separate mlat-client for each receiver." )
def handle_connected_request(self, request): if DEBUG: log('Receive: {0}', request) if 'start_sending' in request: self.coordinator.server_start_sending( [int(x, 16) for x in request['start_sending']]) elif 'stop_sending' in request: self.coordinator.server_stop_sending( [int(x, 16) for x in request['stop_sending']]) elif 'heartbeat' in request: pass elif 'result' in request: result = request['result'] ecef = result.get('ecef') if ecef is not None: # new format lat, lon, alt = mlat.geodesy.ecef2llh(ecef) alt = alt / 0.3038 # convert meters to feet ecef_cov = result.get('cov') if ecef_cov: var_est = ecef_cov[0] + ecef_cov[3] + ecef_cov[5] if var_est >= 0: error_est = math.sqrt(var_est) else: error_est = -1 else: error_est = -1 nstations = result['nd'] callsign = None squawk = None else: lat = result['lat'] lon = result['lon'] alt = result['alt'] error_est = result['gdop'] * 300 # make a guess nstations = result['nstations'] callsign = result['callsign'] squawk = result['squawk'] nsvel = result.get('nsvel') ewvel = result.get('ewvel') vrate = result.get('vrate') self.coordinator.server_mlat_result(timestamp=result['@'], addr=int(result['addr'], 16), lat=lat, lon=lon, alt=alt, nsvel=nsvel, ewvel=ewvel, vrate=vrate, callsign=callsign, squawk=squawk, error_est=error_est, nstations=nstations, anon=False, modeac=False) else: log('ignoring request from server: {0}', request)
def handle_error(self): t, v, tb = sys.exc_info() if isinstance(v, IOError): log("Connection to {host}:{port} lost: {ex!s}", host=self.host, port=self.port, ex=v) else: log_exc("Unexpected exception on connection to {host}:{port}", host=self.host, port=self.port) self.handle_close()
def start_connection(self): log('Input connected to {0}:{1}', self.host, self.port) self.last_data_received = monotonic_time() self.state = 'connected' self.coordinator.input_connected() # synthesize a mode change immediately if we are not autodetecting if self.reader.mode is not None: self.coordinator.input_received_messages((mode_change_event(self.reader),))
def handle_accept(self): accepted = self.accept() if not accepted: return new_socket, address = accepted log('Accepted {0} from {1}:{2}', self.connection_factory.describe(), address[0], address[1]) self.output_channels.add(self.connection_factory(self, new_socket, self.a_type, self.a_family, address))
def handle_accept(self): accepted = self.accept() if not accepted: return new_socket, address = accepted log('Accepted {0} from {1}:{2}', self.connection_factory.describe(), address[0], address[1]) self.output_channels.add(self.connection_factory(self, new_socket, address))
def handle_read(self): try: moredata = self.recv(16384) except socket.error as e: if e.errno == errno.EAGAIN: return raise if not moredata: self.close() return global_stats.receiver_rx_bytes += len(moredata) self.coordinator.copy_received_messages(moredata) if self.residual: moredata = self.residual + moredata self.last_data_received = monotonic_time() try: consumed, messages, pending_error = self.feed(moredata) except ValueError as e: log("Parsing receiver data failed: {e}", e=str(e)) self.reconnect_interval = 5.0 self.close() return if consumed < len(moredata): self.residual = moredata[consumed:] if len(self.residual) > 5120: raise RuntimeError('parser broken - buffer not being consumed') else: self.residual = None global_stats.receiver_rx_messages += self.reader.received_messages global_stats.receiver_rx_filtered += self.reader.suppressed_messages self.reader.received_messages = self.reader.suppressed_messages = 0 if messages: self.coordinator.input_received_messages(messages) if pending_error: # call it again to get the exception # now that we've handled all the messages try: if self.residual is None: self.feed(b'') else: self.feed(self.residual) except ValueError as e: log("Parsing receiver data failed: {e}", e=str(e)) self.close() return
def consume_readbuf_uncompressed(self): lines = self.readbuf.split(b'\n') self.readbuf = lines[-1] for line in lines[:-1]: try: msg = json.loads(line.decode('ascii')) except ValueError: log("json parsing problem, line: >>{line}<<", line=line) raise self.handle_server_line(msg)
def start_connection(self): log('Input connected to {0}:{1}', self.host, self.port) self.last_data_received = monotonic_time() self.state = 'connected' self.coordinator.input_connected() # synthesize a mode change immediately if we are not autodetecting if self.reader.mode is not None: self.coordinator.input_received_messages((mode_change_event(self.reader),)) self.send_settings_message()
def received_timestamp_jump_event(self, message, now): self.recent_jumps += 1 self.server.send_clock_jump() #log("clockjump") if self.recent_jumps % 9 == 8 and time.monotonic( ) > self.last_jump_message + 300.0: self.last_jump_message = time.monotonic() log("WARNING: the timestamps provided by your receiver do not seem to be self-consistent. " "This can happen if you feed data from multiple receivers to a single mlat-client, which " "is not supported; use a separate mlat-client for each receiver." )
def reconnect(self): if self.state != "disconnected": self.disconnect("About to reconnect") try: self.reset_connection() self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect((self.host, self.port)) except socket.error as e: log("Connection to {host}:{port} failed: {ex!s}", host=self.host, port=self.port, ex=e) self.close()
def handle_connected_request(self, request): if DEBUG: log('Receive: {0}', request) if 'start_sending' in request: self.coordinator.server_start_sending([int(x, 16) for x in request['start_sending']]) elif 'stop_sending' in request: self.coordinator.server_stop_sending([int(x, 16) for x in request['stop_sending']]) elif 'heartbeat' in request: pass elif 'result' in request: result = request['result'] ecef = result.get('ecef') if ecef is not None: # new format lat, lon, alt = mlat.geodesy.ecef2llh(ecef) alt = alt / 0.3038 # convert meters to feet ecef_cov = result.get('cov') if ecef_cov: var_est = ecef_cov[0] + ecef_cov[3] + ecef_cov[5] if var_est >= 0: error_est = math.sqrt(var_est) else: error_est = -1 else: error_est = -1 nstations = result['nd'] callsign = None squawk = None else: lat = result['lat'] lon = result['lon'] alt = result['alt'] error_est = result['gdop'] * 300 # make a guess nstations = result['nstations'] callsign = result['callsign'] squawk = result['squawk'] self.coordinator.server_mlat_result(timestamp=result['@'], addr=int(result['addr'], 16), lat=lat, lon=lon, alt=alt, nsvel=None, ewvel=None, vrate=None, callsign=callsign, squawk=squawk, error_est=error_est, nstations=nstations, anon=False, modeac=False) else: log('ignoring request from server: {0}', request)
def handle_read(self): try: moredata = self.recv(16384) except socket.error as e: if e.errno == errno.EAGAIN: return raise if not moredata: self.close() return global_stats.receiver_rx_bytes += len(moredata) if self.residual: moredata = self.residual + moredata self.last_data_received = monotonic_time() try: consumed, messages, pending_error = self.feed(moredata) except ValueError as e: log("Parsing receiver data failed: {e}", e=str(e)) self.close() return if consumed < len(moredata): self.residual = moredata[consumed:] if len(self.residual) > 5120: raise RuntimeError('parser broken - buffer not being consumed') else: self.residual = None global_stats.receiver_rx_messages += self.reader.received_messages global_stats.receiver_rx_filtered += self.reader.suppressed_messages self.reader.received_messages = self.reader.suppressed_messages = 0 if messages: self.coordinator.input_received_messages(messages) if pending_error: # call it again to get the exception # now that we've handled all the messages try: if self.residual is None: self.feed(b'') else: self.feed(self.residual) except ValueError as e: log("Parsing receiver data failed: {e}", e=str(e)) self.close() return
def close(self, manual_close=False): asyncore.dispatcher.close(self) if self.state != 'disconnected': if not manual_close: log('Lost connection to {host}:{port}', host=self.host, port=self.port) self.state = 'disconnected' self.reset_connection() self.lost_connection() if not manual_close: self.schedule_reconnect()
def refresh_socket(self): try: self.sock.connect(self.remote_address) except OSError: pass except socket.error: pass new_mtu = get_mtu(self.sock) if new_mtu is not None and new_mtu != self.route_mtu: util.log('Route MTU changed to {0}', new_mtu) self.route_mtu = new_mtu self.mtu = max(100, self.route_mtu - 100)
def __init__(self, port, connection_factory): asyncore.dispatcher.__init__(self) self.port = port self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() self.bind(('', port)) self.listen(0) self.output_channels = set() self.connection_factory = connection_factory log('Listening for {0} on port {1}', connection_factory.describe(), port)
def handle_error(self): t, v, tb = sys.exc_info() if isinstance(v, IOError): log('Connection to {host}:{port} lost: {ex!s}', host=self.host, port=self.port, ex=v) else: log_exc('Unexpected exception on connection to {host}:{port}', host=self.host, port=self.port) self.handle_close()
def consume_readbuf_uncompressed(self): lines = self.readbuf.split(b'\n') self.readbuf = lines[-1] for line in lines[:-1]: try: msg = json.loads(line.decode('ascii')) except ValueError: log("json parsing problem, line: >>{line}<<", line=line) raise if DEBUG: log('Receive: {0}', msg) self.handle_server_line(msg)
def received_mode_change_event(self, message, now): # decoder mode changed, clock parameters possibly changed self.freq = message.eventdata['frequency'] self.recent_jumps = 0 self.server.send_clock_reset( reason='Decoder mode changed to {mode}'.format( mode=message.eventdata['mode']), frequency=message.eventdata['frequency'], epoch=message.eventdata['epoch'], mode=message.eventdata['mode']) log("Input format changed to {mode}, {freq:.0f}MHz clock", mode=message.eventdata['mode'], freq=message.eventdata['frequency'] / 1e6)
def periodic_stats(self, now): log('Receiver status: {0}', self.receiver.state) log('Server status: {0}', self.server.state) global_stats.log_and_reset() adsb_req = adsb_total = modes_req = modes_total = 0 now = monotonic_time() for ac in self.aircraft.values(): if ac.messages < 2: continue if now - ac.last_position_time < self.position_expiry_age: adsb_total += 1 if ac.requested: adsb_req += 1 else: modes_total += 1 if ac.requested: modes_req += 1 log('Aircraft: {modes_req} of {modes_total} Mode S, {adsb_req} of {adsb_total} ADS-B used', modes_req=modes_req, modes_total=modes_total, adsb_req=adsb_req, adsb_total=adsb_total) if self.recent_jumps > 0: log('Out-of-order timestamps: {recent}', recent=self.recent_jumps) self.recent_jumps = 0
def start_connection(self): log('Input connected to {0}:{1}', self.host, self.port) self.last_data_received = monotonic_time() self.state = 'connected' self.coordinator.input_connected() # synthesize a mode change immediately if we are not autodetecting if self.reader.mode is not None: self.coordinator.input_received_messages((mode_change_event(self.reader),)) # if we are connected to something that is Beast-like (or autodetecting), send a beast settings message if self.reader.mode in (None, _modes.BEAST, _modes.RADARCAPE, _modes.RADARCAPE_EMULATED): # Binary format, no filters, CRC checks enabled, mode A/C disabled settings_message = b'\x1a1C\x1a1d\x1a1f\x1a1j' self.send(settings_message)
def schedule_reconnect(self): if self.reconnect_at is None: if len(self.addrlist) > 0: # we still have more addresses to try # nb: asyncore breaks in odd ways if you try # to reconnect immediately at this point # (pending events for the old socket go to # the new socket) so do it in 0.5s time # so the caller can clean up the old # socket and discard the events. interval = 0.5 else: interval = self.reconnect_interval log('Reconnecting in {0} seconds', interval) self.reconnect_at = monotonic_time() + interval
def close(self, manual_close=False): try: asyncore.dispatcher.close(self) except AttributeError: # blarg, try to eat asyncore bugs pass if self.state != 'disconnected': if not manual_close: log('Lost connection to {host}:{port}', host=self.host, port=self.port) self.state = 'disconnected' self.reset_connection() self.lost_connection() if not manual_close: self.schedule_reconnect()
def main(): # piaware will timestamp our log messages itself, suppress the normal logging timestamps mlat.client.util.suppress_log_timestamps = True parser = argparse.ArgumentParser(description="Client for multilateration.") options.make_inputs_group(parser) options.make_results_group(parser) parser.add_argument( '--udp-transport', help= "Provide UDP transport information. Expects an IP:port:key argument.", required=True) args = parser.parse_args() log("fa-mlat-client {version} starting up", version=mlat.client.version.CLIENT_VERSION) # udp_transport is IP:port:key # split backwards to handle IPv6 addresses in the host part, which themselves contain colons. parts = args.udp_transport.split(':') udp_key = int(parts[-1]) udp_port = int(parts[-2]) udp_host = ':'.join(parts[:-2]) udp_transport = UdpServerConnection(udp_host, udp_port, udp_key) log("Using UDP transport to {host} port {port}", host=udp_host, port=udp_port) receiver = options.build_receiver_connection(args) adept = AdeptConnection(udp_transport, allow_anon=args.allow_anon_results, allow_modeac=args.allow_modeac_results) outputs = options.build_outputs(args) coordinator = Coordinator(receiver=receiver, server=adept, outputs=outputs, freq=options.clock_frequency(args), allow_anon=args.allow_anon_results, allow_modeac=args.allow_modeac_results) adept.start(coordinator) coordinator.run_until(lambda: adept.closed)
def build_outputs(args): outputs = [] for s in args.results: try: factory = output_factory(s) except ValueError as e: log("Warning: Ignoring bad results output option '{0}': {1}", s, str(e)) continue try: output = factory() except Exception as e: log("Warning: Could not create results output '{0}': {1}", s, str(e)) continue outputs.append(output) return outputs
def altitude(s): if s.endswith('m'): alt = float(s[:-1]) elif s.endswith('ft'): alt = float(s[:-2]) * 0.3048 else: alt = float(s) # Wikipedia to the rescue! # "The lowest point on dry land is the shore of the Dead Sea [...] # 418m below sea level". Perhaps not the best spot for a receiver? # La Rinconada, Peru, pop. 30,000, is at 5100m. if s == '60440ft': log('<3>Altitude not configured, please configure and reboot') time.sleep(3600) raise SystemExit if alt < -420 or alt > 5100: raise argparse.ArgumentTypeError( 'Altitude %s must be in the range -420m to 6000m' % s) return alt
def start_connection(self): log('Connected to multilateration server at {0}:{1}, handshaking', self.host, self.port) self.state = 'handshaking' self.last_data_received = monotonic_time() compress_methods = ['none'] if self.offer_zlib: compress_methods.append('zlib') compress_methods.append('zlib2') handshake_msg = {'version': 3, 'client_version': mlat.client.version.CLIENT_VERSION, 'compress': compress_methods, 'selective_traffic': True, 'heartbeat': True, 'return_results': self.return_results, 'udp_transport': 2 if self.offer_udp else False, 'return_result_format': 'ecef'} handshake_msg.update(self.handshake_data) self.writebuf += (json.dumps(handshake_msg) + '\n').encode('ascii') # linebuf not used yet self.consume_readbuf = self.consume_readbuf_uncompressed self.handle_server_line = self.handle_handshake_response
def start_connection(self): log('Connected to multilateration server at {0}:{1}, handshaking', self.host, self.port) self.state = 'handshaking' self.last_data_received = monotonic_time() compress_methods = ['none'] if self.offer_zlib: compress_methods.append('zlib') compress_methods.append('zlib2') uuid = None for path in self.uuid_path: try: with open(path) as file: uuid = file.readline().rstrip('\n') break except Exception: pass handshake_msg = { 'version': 3, 'client_version': mlat.client.version.CLIENT_VERSION, 'compress': compress_methods, 'selective_traffic': True, 'heartbeat': True, 'return_results': self.return_results, 'udp_transport': 2 if self.offer_udp else False, 'return_result_format': 'ecef', 'uuid': uuid } handshake_msg.update(self.handshake_data) if DEBUG: log("Handshake: {0}", handshake_msg) self.writebuf += (json.dumps(handshake_msg, sort_keys=True) + 16 * ' ' + '\n').encode( 'ascii') # linebuf not used yet self.consume_readbuf = self.consume_readbuf_uncompressed self.handle_server_line = self.handle_handshake_response
def consume_readbuf_zlib(self): i = 0 while i + 2 < len(self.readbuf): hlen, = struct.unpack_from('!H', self.readbuf, i) end = i + 2 + hlen if end > len(self.readbuf): break packet = self.readbuf[i + 2:end] + b'\x00\x00\xff\xff' linebuf = self.decompressor.decompress(packet) lines = linebuf.split(b'\n') for line in lines[:-1]: try: msg = json.loads(line.decode('ascii')) except ValueError: log("json parsing problem, line: >>{line}<<", line=line) raise self.handle_server_line(msg) i = end del self.readbuf[:i]
def reconnect(self): if self.state != 'disconnected': self.disconnect('About to reconnect') try: self.reset_connection() if len(self.addrlist) == 0: # ran out of addresses to try, resolve it again if self.host == 'feed.adsbexchange.com' and self.basePort == 31090: for index, port in enumerate(self.adsbexchangePorts): if self.port == port: self.port = self.adsbexchangePorts[(index + 1) % len(self.adsbexchangePorts)] break #if self.host == 'feed.adsbexchange.com' and self.basePort != self.port: # log('Connecting to {host}:{port} (trying hard-coded alternate port for adsbexchange)', host=self.host, port=self.port) #else: # log('Connecting to {host}:{port}', host=self.host, port=self.port) self.addrlist = socket.getaddrinfo(host=self.host, port=self.port, family=socket.AF_UNSPEC, type=socket.SOCK_STREAM, proto=0, flags=0) # try the next available address a_family, a_type, a_proto, a_canonname, a_sockaddr = self.addrlist[0] del self.addrlist[0] self.create_socket(a_family, a_type) self.connect(a_sockaddr) except socket.error as e: log('Connection to {host}:{port} failed: {ex!s}', host=self.host, port=self.port, ex=e) self.close()
def log_and_reset(self): now = monotonic_time() elapsed = now - self.start log('Receiver: {0:6.1f} msg/s received {1:4.1f}kB/s from receiver', self.receiver_rx_messages / elapsed, self.receiver_rx_bytes / elapsed / 1000.0) log('Server: {0:6.1f} kB/s from server {1:4.1f}kB/s TCP to server {2:4.1f}kB/s UDP to server', self.server_rx_bytes / elapsed / 1000.0, self.server_tx_bytes / elapsed / 1000.0, self.server_udp_bytes / elapsed / 1000.0) if self.mlat_positions: log('Results: {0:3.1f} positions/minute', self.mlat_positions / elapsed * 60.0) self.reset(now)
def log_and_reset(self): now = monotonic_time() elapsed = now - self.start processed = self.receiver_rx_messages - self.receiver_rx_filtered log( 'Receiver: {0:6.1f} msg/s received {1:6.1f} msg/s processed ({2:.0f}%)', self.receiver_rx_messages / elapsed, processed / elapsed, 0 if self.receiver_rx_messages == 0 else 100.0 * processed / self.receiver_rx_messages) if self.receiver_rx_mlat: log( 'WARNING: Ignored {0:5d} messages with MLAT magic timestamp (do you have --forward-mlat on?)', self.receiver_rx_mlat) log( 'Server: {0:6.1f} kB/s from server {1:4.1f}kB/s TCP to server {2:6.1f}kB/s UDP to server', self.server_rx_bytes / elapsed / 1000.0, self.server_tx_bytes / elapsed / 1000.0, self.server_udp_bytes / elapsed / 1000.0) if self.mlat_positions: log('Results: {0:3.1f} positions/minute', self.mlat_positions / elapsed * 60.0) self.reset(now)
def log_and_reset(self): now = monotonic_time() elapsed = now - self.start processed = self.receiver_rx_messages - self.receiver_rx_filtered log('Receiver: {0:6.1f} msg/s received {1:6.1f} msg/s processed ({2:.0f}%)', self.receiver_rx_messages / elapsed, processed / elapsed, 0 if self.receiver_rx_messages == 0 else 100.0 * processed / self.receiver_rx_messages) log('Server: {0:6.1f} kB/s from server {1:4.1f}kB/s TCP to server {2:6.1f}kB/s UDP to server', self.server_rx_bytes / elapsed / 1000.0, self.server_tx_bytes / elapsed / 1000.0, self.server_udp_bytes / elapsed / 1000.0) if self.mlat_positions: log('Results: {0:3.1f} positions/minute', self.mlat_positions / elapsed * 60.0) self.reset(now)
def log_and_reset(self, coordinator): now = monotonic_time() elapsed = now - self.start #log('Receiver status: {0}', coordinator.receiver.state) #log('Server status: {0}', coordinator.server.state) processed = self.receiver_rx_messages - self.receiver_rx_filtered log( 'Receiver: {3:10s} {0:6.1f} msg/s received {1:6.1f} msg/s processed ({2:.0f}%)', self.receiver_rx_messages / elapsed, processed / elapsed, 0 if self.receiver_rx_messages == 0 else 100.0 * processed / self.receiver_rx_messages, coordinator.receiver.state) if self.receiver_rx_mlat: log( 'WARNING: Ignored {0:5d} messages with MLAT magic timestamp (do you have --forward-mlat on?)', self.receiver_rx_mlat) log( 'Server: {0:10s} {1:6.1f} kB/s from server {2:6.1f} kB/s to server', coordinator.server.state, self.server_rx_bytes / elapsed / 1000.0, (self.server_tx_bytes + self.server_udp_bytes) / elapsed / 1000.0) log('Results: {0:3.1f} positions/minute', self.mlat_positions / elapsed * 60.0) self.reset(now)
def log_and_reset(self): now = monotonic_time() elapsed = now - self.start processed = self.receiver_rx_messages - self.receiver_rx_filtered log( 'Receiver: {0:6.1f} msg/s received {1:6.1f} msg/s processed ({2:.0f}%)', self.receiver_rx_messages / elapsed, processed / elapsed, 0 if self.receiver_rx_messages == 0 else 100.0 * processed / self.receiver_rx_messages) log( 'Server: {0:6.1f} kB/s from server {1:4.1f}kB/s TCP to server {2:6.1f}kB/s UDP to server', self.server_rx_bytes / elapsed / 1000.0, self.server_tx_bytes / elapsed / 1000.0, self.server_udp_bytes / elapsed / 1000.0) if self.mlat_positions: log('Results: {0:3.1f} positions/minute', self.mlat_positions / elapsed * 60.0) self.reset(now)
def handle_read(self): try: moredata = self.recv(16384) except socket.error as e: if e.errno == errno.EAGAIN: return raise if not moredata: self.close() return global_stats.receiver_rx_bytes += len(moredata) if self.residual: moredata = self.residual + moredata self.last_data_received = monotonic_time() try: consumed, messages = self.packetize(moredata, self.last_timestamp) except _modes.ClockResetError as e: log("Problem reading receiver messages: " + str(e)) log("Ensure that only one receiver is feeding data to this client.") log("A single multilateration client cannot handle data from multiple receivers.") self.close() return if consumed < len(moredata): self.residual = moredata[consumed:] if len(self.residual) > 5120: raise RuntimeError('parser broken - buffer not being consumed') else: self.residual = None if messages: global_stats.receiver_rx_messages += len(messages) self.last_timestamp = messages[-1].timestamp self.coordinator.input_received_messages(messages)
def log(self, message): log('{0}', message)
def start_connection(self): log('Input connected to {0}:{1}', self.host, self.port) self.last_data_received = monotonic_time() self.state = 'ready' self.coordinator.input_connected()
def handle_handshake_response(self, response): if 'reconnect_in' in response: self.reconnect_interval = response['reconnect_in'] if 'deny' in response: log('Server explicitly rejected our connection, saying:') for reason in response['deny']: log(' {0}', reason) raise IOError('Server rejected our connection attempt') if 'motd' in response: log('Server says: {0}', response['motd']) compress = response.get('compress', 'none') if response['compress'] == 'none': self.fill_writebuf = self.fill_uncompressed self.consume_readbuf = self.consume_readbuf_uncompressed elif response['compress'] == 'zlib' and self.offer_zlib: self.compressor = zlib.compressobj(1) self.fill_writebuf = self.fill_zlib self.consume_readbuf = self.consume_readbuf_uncompressed elif response['compress'] == 'zlib2' and self.offer_zlib: self.compressor = zlib.compressobj(1) self.decompressor = zlib.decompressobj() self.fill_writebuf = self.fill_zlib self.consume_readbuf = self.consume_readbuf_zlib else: raise IOError('Server response asked for a compression method {0}, which we do not support'.format( response['compress'])) self.server_heartbeat_at = monotonic_time() + self.heartbeat_interval if 'udp_transport' in response: host, port, key = response['udp_transport'] if not host: host = self.host self.udp_transport = UdpServerConnection(host, port, key) self.send_mlat = self.udp_transport.send_mlat self.send_sync = self.udp_transport.send_sync self.send_split_sync = self.udp_transport.send_split_sync else: self.udp_transport = None self.send_mlat = self.send_tcp_mlat self.send_sync = self.send_tcp_sync self.send_split_sync = self.send_tcp_split_sync # turn off the sync method we don't want if response.get('split_sync', False): self.send_sync = None else: self.send_split_sync = None log('Handshake complete.') log(' Compression: {0}', compress) log(' UDP transport: {0}', self.udp_transport and str(self.udp_transport) or 'disabled') log(' Split sync: {0}', self.send_split_sync and 'enabled' or 'disabled') self.state = 'ready' self.handle_server_line = self.handle_connected_request self.coordinator.server_connected() # dummy rate report to indicate we'll be sending them self.send_rate_report({})
def _send_json(self, o): if DEBUG: log('Send: {0}', o) self.linebuf.append(json.dumps(o, separators=(',', ':')))
def log_info(self, message, type='info'): log('{0}: {1}', message, type)
def log(self, fmt, *args, **kwargs): log('{what} with {addr[0]}:{addr[1]}: ' + fmt, *args, what=self.describe(), addr=self.addr, **kwargs)
def handle_close(self): log('Lost SBS output connection from {0}:{1}', self.addr[0], self.addr[1]) self.close()