diff --git a/mlat-client b/mlat-client index 08cf6d3..4f173d4 100755 --- a/mlat-client +++ b/mlat-client @@ -23,8 +23,11 @@ import mlat.client.version from mlat.client.util import log from mlat.client.receiver import ReceiverConnection +from mlat.client.receiver_mirror import ReceiverConnectionMirror +from mlat.client.mirror import MirrorReceiverConnection from mlat.client.jsonclient import JsonServerConnection from mlat.client.coordinator import Coordinator +from mlat.client.coordinator_mirror import CoordinatorMirror from mlat.client import options @@ -65,6 +68,9 @@ location pin from the coverage maps.""", help="host:port of the multilateration server to connect to", type=options.hostport, default=('mlat.mutability.co.uk', 40147)) + server.add_argument('--mirror_server', + help="host:port for mirrored traffic", + type=options.hostport) server.add_argument('--no-udp', dest='udp', help="Don't offer to use UDP transport for sync/mlat messages", @@ -77,8 +83,6 @@ location pin from the coverage maps.""", outputs = options.build_outputs(args) - receiver = ReceiverConnection(host=args.input_connect[0], port=args.input_connect[1], - mode=options.connection_mode(args)) server = JsonServerConnection(host=args.server[0], port=args.server[1], handshake_data={'lat': args.lat, 'lon': args.lon, @@ -92,8 +96,20 @@ location pin from the coverage maps.""", offer_udp=args.udp, return_results=(len(outputs) > 0)) - coordinator = Coordinator(receiver=receiver, server=server, outputs=outputs, freq=options.clock_frequency(args), - allow_anon=args.allow_anon_results, allow_modeac=args.allow_modeac_results) + if args.mirror_server is not None: + + mirror_receiver = MirrorReceiverConnection(host=args.mirror_server[0], port=args.mirror_server[1]) + + receiver = ReceiverConnectionMirror(host=args.input_connect[0], port=args.input_connect[1], + mode=options.connection_mode(args)) + + coordinator = CoordinatorMirror(receiver=receiver, mirror_receiver=mirror_receiver, server=server, outputs=outputs, freq=options.clock_frequency(args), + allow_anon=args.allow_anon_results, allow_modeac=args.allow_modeac_results) + else: + receiver = ReceiverConnection(host=args.input_connect[0], port=args.input_connect[1], + mode=options.connection_mode(args)) + coordinator = Coordinator(receiver=receiver, server=server, outputs=outputs, freq=options.clock_frequency(args), + allow_anon=args.allow_anon_results, allow_modeac=args.allow_modeac_results) server.start() coordinator.run_forever() diff --git a/mlat/client/coordinator_mirror.py b/mlat/client/coordinator_mirror.py new file mode 100644 index 0000000..d73b37c --- /dev/null +++ b/mlat/client/coordinator_mirror.py @@ -0,0 +1,71 @@ +# -*- mode: python; indent-tabs-mode: nil -*- + +# Part of mlat-client - an ADS-B multilateration client. +# Copyright 2015, Oliver Jowett +# +# 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 . + +""" +Core of the client: track aircraft and send data to the server as needed - mirror server version. +""" + +import asyncore +import time + +import _modes +import mlat.profile +from mlat.client.util import monotonic_time, log +from mlat.client.stats import global_stats +from mlat.client.coordinator import Coordinator + +class CoordinatorMirror(Coordinator): + + def __init__(self, receiver, mirror_receiver, server, outputs, freq, allow_anon, allow_modeac): + super().__init__(receiver, server, outputs, freq, allow_anon, allow_modeac) + + self.mirror_receiver = mirror_receiver + mirror_receiver.coordinator = self + + # internals + + def run_until(self, termination_condition): + try: + super().run_until(termination_condition) + finally: + self.mirror_receiver.disconnect('Client mirror shutting down') + + def heartbeat(self, now): + self.mirror_receiver.heartbeat(now) + super().heartbeat(now) + + def periodic_stats(self, now): + super().periodic_stats(now) + log('Mirror receiver status: {0}', self.mirror_receiver.state) + + # callbacks from server connection + + def server_connected(self): + super().server_connected() + if self.mirror_receiver.state != 'ready': + self.mirror_receiver.reconnect() + + def server_disconnected(self): + super().server_disconnected() + self.mirror_receiver.disconnect('Lost connection to multilateration server, no need for input data') + + @mlat.profile.trackcpu + + def copy_received_messages(self, messages): +# self.mirror_receiver.send_to_mirror(messages) + self.mirror_receiver.send(messages) diff --git a/mlat/client/mirror.py b/mlat/client/mirror.py new file mode 100644 index 0000000..576140b --- /dev/null +++ b/mlat/client/mirror.py @@ -0,0 +1,49 @@ +# -*- mode: python; indent-tabs-mode: nil -*- + +# Part of mlat-client - an ADS-B multilateration client. +# Copyright 2015, Oliver Jowett +# +# 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 . + +""" +Copy received Mode S messages to mirror server. +""" + +import socket +import errno + +import _modes +import mlat.profile +from mlat.client.stats import global_stats +from mlat.client.net import ReconnectingConnection +from mlat.client.util import log, monotonic_time + +class MirrorReceiverConnection(ReconnectingConnection): + reconnect_interval = 15.0 + + def __init__(self, host, port): + ReconnectingConnection.__init__(self, host, port) + self.reset_connection() + + @mlat.profile.trackcpu + + def start_connection(self): + log('Mirror connected to {0}:{1}', self.host, self.port) + self.state = 'connected' + + +""" + def send_to_mirror(self,messages): + self.send(messages) +""" diff --git a/mlat/client/receiver_mirror.py b/mlat/client/receiver_mirror.py new file mode 100644 index 0000000..69c82ac --- /dev/null +++ b/mlat/client/receiver_mirror.py @@ -0,0 +1,94 @@ +# -*- mode: python; indent-tabs-mode: nil -*- + +# Part of mlat-client - an ADS-B multilateration client. +# Copyright 2015, Oliver Jowett +# +# 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 . + +""" +Handles receiving Mode S messages from receivers using various formats - mirror server version. +""" + +import socket +import errno + +import _modes +import mlat.profile +from mlat.client.stats import global_stats +from mlat.client.net import ReconnectingConnection +from mlat.client.util import log, monotonic_time +from mlat.client.receiver import ReceiverConnection + +class ReceiverConnectionMirror(ReceiverConnection): + + def __init__(self, host, port, mode): + super().__init__(host, port, mode) + self.mirror_receiver = None + + @mlat.profile.trackcpu + 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 \ No newline at end of file