From 9753735c40d731979769317fba3b79ba001b302b Mon Sep 17 00:00:00 2001 From: Paul Kronenwetter Date: Sun, 8 May 2022 20:41:49 -0400 Subject: [PATCH 1/3] Remove excess trailing spaces. --- tnc/audio.py | 18 +- tnc/codec2.py | 56 ++-- tnc/daemon.py | 134 ++++----- tnc/data_handler.py | 690 ++++++++++++++++++++++---------------------- tnc/helpers.py | 92 +++--- tnc/log_handler.py | 2 +- tnc/main.py | 92 +++--- tnc/modem.py | 413 ++++++++++++++------------ tnc/rig.py | 82 +++--- tnc/rigctl.py | 68 ++--- tnc/rigctld.py | 46 +-- tnc/rigdummy.py | 26 +- tnc/sock.py | 198 ++++++------- tnc/static.py | 6 +- 14 files changed, 978 insertions(+), 945 deletions(-) diff --git a/tnc/audio.py b/tnc/audio.py index 380f3c81..eb89d129 100644 --- a/tnc/audio.py +++ b/tnc/audio.py @@ -9,11 +9,11 @@ atexit.register(sd._terminate) def get_audio_devices(): - + """ return list of input and output audio devices in own process to avoid crashes of portaudio on raspberry pi - + also uses a process data manager """ # we need to run this on windows for multiprocessing support @@ -24,22 +24,22 @@ def get_audio_devices(): # If we are not doing this at this early point, not all devices will be displayed sd._terminate() sd._initialize() - + with multiprocessing.Manager() as manager: - + proxy_input_devices = manager.list() proxy_output_devices = manager.list() #print(multiprocessing.get_start_method()) p = multiprocessing.Process(target=fetch_audio_devices, args=(proxy_input_devices, proxy_output_devices)) p.start() p.join() - - return list(proxy_input_devices), list(proxy_output_devices) + + return list(proxy_input_devices), list(proxy_output_devices) def fetch_audio_devices(input_devices, output_devices): """ get audio devices from portaudio - + Args: input_devices: proxy variable for input devices output_devices: proxy variable for outout devices @@ -47,7 +47,7 @@ def fetch_audio_devices(input_devices, output_devices): Returns: """ - + devices = sd.query_devices(device=None, kind=None) index = 0 for device in devices: @@ -55,7 +55,7 @@ def fetch_audio_devices(input_devices, output_devices): # we need to do a try exception, beacuse for windows theres no audio device range try: name = device["name"] - + maxOutputChannels = device["max_output_channels"] maxInputChannels = device["max_input_channels"] diff --git a/tnc/codec2.py b/tnc/codec2.py index c1e84c15..9b2ecc61 100644 --- a/tnc/codec2.py +++ b/tnc/codec2.py @@ -30,7 +30,7 @@ def freedv_get_mode_value_by_name(mode): """ get the codec2 mode by entering its string Args: - mode: + mode: Returns: int @@ -42,14 +42,14 @@ def freedv_get_mode_name_by_value(mode): """ get the codec2 mode name as string Args: - mode: + mode: Returns: string """ return FREEDV_MODE(mode).name - - + + # check if we are running in a pyinstaller environment try: app_path = sys._MEIPASS @@ -63,12 +63,12 @@ if sys.platform == 'linux': files.append('libcodec2.so') elif sys.platform == 'darwin': files = glob.glob('**/*libcodec2*.dylib',recursive=True) - + elif sys.platform == 'win32' or sys.platform == 'win64': files = glob.glob('**\*libcodec2*.dll',recursive=True) else: files = [] - + for file in files: try: @@ -79,15 +79,15 @@ for file in files: structlog.get_logger("structlog").warning("[C2 ] Libcodec2 found but not loaded", path=file, e=e) -# quit module if codec2 cant be loaded +# quit module if codec2 cant be loaded if not 'api' in locals(): structlog.get_logger("structlog").critical("[C2 ] Libcodec2 not loaded", path=file) os._exit(1) - -# ctypes function init + +# ctypes function init #api.freedv_set_tuning_range.restype = c_int #api.freedv_set_tuning_range.argype = [c_void_p, c_float, c_float] @@ -121,24 +121,24 @@ api.freedv_get_n_max_modem_samples.restype = c_int api.freedv_set_frames_per_burst.argtype = [c_void_p, c_int] api.freedv_set_frames_per_burst.restype = c_void_p - + api.freedv_get_rx_status.argtype = [c_void_p] -api.freedv_get_rx_status.restype = c_int +api.freedv_get_rx_status.restype = c_int api.freedv_get_modem_stats.argtype = [c_void_p, c_void_p, c_void_p] api.freedv_get_modem_stats.restype = c_int api.freedv_get_n_tx_postamble_modem_samples.argtype = [c_void_p] -api.freedv_get_n_tx_postamble_modem_samples.restype = c_int +api.freedv_get_n_tx_postamble_modem_samples.restype = c_int api.freedv_get_n_tx_preamble_modem_samples.argtype = [c_void_p] -api.freedv_get_n_tx_preamble_modem_samples.restype = c_int +api.freedv_get_n_tx_preamble_modem_samples.restype = c_int api.freedv_get_n_tx_modem_samples.argtype = [c_void_p] -api.freedv_get_n_tx_modem_samples.restype = c_int +api.freedv_get_n_tx_modem_samples.restype = c_int api.freedv_get_n_max_modem_samples.argtype = [c_void_p] -api.freedv_get_n_max_modem_samples.restype = c_int +api.freedv_get_n_max_modem_samples.restype = c_int api.FREEDV_FS_8000 = 8000 api.FREEDV_MODE_DATAC1 = 10 @@ -152,13 +152,13 @@ api.FREEDV_MODE_FSK_LDPC = 9 class ADVANCED(ctypes.Structure): """ """ _fields_ = [ - ("interleave_frames", ctypes.c_int), + ("interleave_frames", ctypes.c_int), ("M", ctypes.c_int), ("Rs", ctypes.c_int), ("Fs", ctypes.c_int), - ("first_tone", ctypes.c_int), - ("tone_spacing", ctypes.c_int), - ("codename", ctypes.c_char_p), + ("first_tone", ctypes.c_int), + ("tone_spacing", ctypes.c_int), + ("codename", ctypes.c_char_p), ] ''' @@ -232,9 +232,9 @@ class MODEMSTATS(ctypes.Structure): ("f_est", (ctypes.c_float * MODEM_STATS_MAX_F_EST)), # How many samples in the eye diagram ("fft_buf", (ctypes.c_float * MODEM_STATS_NSPEC * 2)), ] - - - + + + # Return code flags for freedv_get_rx_status() function api.FREEDV_RX_TRIAL_SYNC = 0x1 # demodulator has trial sync api.FREEDV_RX_SYNC = 0x2 # demodulator has sync @@ -264,7 +264,7 @@ api.rx_sync_flags_to_text = [ class audio_buffer: """ thread safe audio buffer, which fits to needs of codec2 - + made by David Rowe, VK5DGR """ # a buffer of int16 samples, using a fixed length numpy array self.buffer for storage @@ -280,7 +280,7 @@ class audio_buffer: Push new data to buffer Args: - samples: + samples: Returns: @@ -295,7 +295,7 @@ class audio_buffer: """ get data from buffer in size of NIN Args: - size: + size: Returns: @@ -306,7 +306,7 @@ class audio_buffer: self.buffer[:self.nbuffer] = self.buffer[size:size+self.nbuffer] assert self.nbuffer >= 0 self.mutex.release() - + # resampler --------------------------------------------------------- api.FDMDV_OS_48 = int(6) # oversampling rate @@ -327,8 +327,8 @@ class resampler: structlog.get_logger("structlog").debug("[C2 ] create 48<->8 kHz resampler") self.filter_mem8 = np.zeros(self.MEM8, dtype=np.int16) self.filter_mem48 = np.zeros(self.MEM48) - - + + def resample48_to_8(self,in48): """ audio resampler integration from codec2 diff --git a/tnc/daemon.py b/tnc/daemon.py index 155e5740..5ed6b25b 100755 --- a/tnc/daemon.py +++ b/tnc/daemon.py @@ -37,8 +37,8 @@ def signal_handler(sig, frame): """ signal handler for closing the network socket on app exit Args: - sig: - frame: + sig: + frame: Returns: system exit @@ -51,15 +51,15 @@ signal.signal(signal.SIGINT, signal_handler) class DAEMON(): - """ + """ daemon class - + """ def __init__(self): - - # load crc engine + + # load crc engine self.crc_algorithm = crcengine.new('crc16-ccitt-false') # load crc8 library - + self.daemon_queue = sock.DAEMON_QUEUE update_audio_devices = threading.Thread(target=self.update_audio_devices, name="UPDATE_AUDIO_DEVICES", daemon=True) update_audio_devices.start() @@ -69,23 +69,23 @@ class DAEMON(): worker = threading.Thread(target=self.worker, name="WORKER", daemon=True) worker.start() - - - + + + def update_audio_devices(self): - """ + """ update audio devices and set to static """ while 1: try: if not static.TNCSTARTED: - + static.AUDIO_INPUT_DEVICES, static.AUDIO_OUTPUT_DEVICES = audio.get_audio_devices() except Exception as e: print(e) time.sleep(1) - - + + def update_serial_devices(self): """ update serial devices and set to static @@ -96,26 +96,26 @@ class DAEMON(): serial_devices = [] ports = serial.tools.list_ports.comports() for port, desc, hwid in ports: - + # calculate hex of hwid if we have unique names crc_hwid = self.crc_algorithm(bytes(hwid, encoding='utf-8')) crc_hwid = crc_hwid.to_bytes(2, byteorder='big') crc_hwid = crc_hwid.hex() description = desc + ' [' + crc_hwid + ']' serial_devices.append({"port": str(port), "description": str(description) }) - + static.SERIAL_DEVICES = serial_devices time.sleep(1) except Exception as e: print(e) - + def worker(self): """ a worker for the received commands """ while 1: try: - + data = self.daemon_queue.get() # data[1] mycall @@ -141,81 +141,81 @@ class DAEMON(): # data[21] enable FSK # data[22] tx-audio-level # data[23] respond_to_cq - + if data[0] == 'STARTTNC': structlog.get_logger("structlog").warning("[DMN] Starting TNC", rig=data[5], port=data[6]) # list of parameters, necessary for running subprocess command as a list options = [] - + options.append('--port') options.append(str(static.DAEMONPORT - 1)) - + options.append('--mycall') options.append(data[1]) - + options.append('--mygrid') options.append(data[2]) - + options.append('--rx') options.append(data[3]) - + options.append('--tx') options.append(data[4]) - + # if radiocontrol != disabled - # this should hopefully avoid a ton of problems if we are just running in + # this should hopefully avoid a ton of problems if we are just running in # disabled mode if data[13] != 'disabled': - + options.append('--devicename') options.append(data[5]) - + options.append('--deviceport') options.append(data[6]) - + options.append('--serialspeed') options.append(data[7]) - + options.append('--pttprotocol') options.append(data[8]) - + options.append('--pttport') options.append(data[9]) - + options.append('--data_bits') options.append(data[10]) - + options.append('--stop_bits') options.append(data[11]) - + options.append('--handshake') options.append(data[12]) - + options.append('--radiocontrol') options.append(data[13]) - + if data[13] != 'rigctld': options.append('--rigctld_ip') options.append(data[14]) - + options.append('--rigctld_port') options.append(data[15]) - + if data[16] == 'True': options.append('--scatter') - + if data[17] == 'True': options.append('--fft') if data[18] == 'True': options.append('--500hz') - + options.append('--tuning_range_fmin') options.append(data[19]) - + options.append('--tuning_range_fmax') options.append(data[20]) @@ -224,13 +224,13 @@ class DAEMON(): # options.append('--fsk') options.append('--tx-audio-level') - options.append(data[22]) - - if data[23] == 'True': + options.append(data[22]) + + if data[23] == 'True': options.append('--qrv') - - + + # try running tnc from binary, else run from source # this helps running the tnc in a developer environment try: @@ -239,10 +239,10 @@ class DAEMON(): command.append('./freedata-tnc') elif sys.platform == 'win32' or sys.platform == 'win64': command.append('freedata-tnc.exe') - + command += options p = subprocess.Popen(command) - + atexit.register(p.kill) structlog.get_logger("structlog").info("[DMN] TNC started", path="binary") @@ -252,7 +252,7 @@ class DAEMON(): command.append('python3') elif sys.platform == 'win32' or sys.platform == 'win64': command.append('python') - + command.append('main.py') command += options p = subprocess.Popen(command) @@ -269,7 +269,7 @@ class DAEMON(): structlog.get_logger("structlog").warning("[DMN] Stopping TNC") #os.kill(static.TNCPROCESS, signal.SIGKILL) static.TNCSTARTED = False - ''' + ''' # data[1] devicename # data[2] deviceport # data[3] serialspeed @@ -306,15 +306,15 @@ class DAEMON(): import rigctld as rig else: import rigdummy as rig - + hamlib = rig.radio() hamlib.open_rig(devicename=devicename, deviceport=deviceport, hamlib_ptt_type=pttprotocol, serialspeed=serialspeed, pttport=pttport, data_bits=data_bits, stop_bits=stop_bits, handshake=handshake, rigctld_ip=rigctld_ip, rigctld_port = rigctld_port) hamlib_version = rig.hamlib_version - hamlib.set_ptt(True) + hamlib.set_ptt(True) pttstate = hamlib.get_ptt() - + if pttstate: structlog.get_logger("structlog").info("[DMN] Hamlib PTT", status = 'SUCCESS') response = {'command': 'test_hamlib', 'result': 'SUCCESS'} @@ -324,13 +324,13 @@ class DAEMON(): else: structlog.get_logger("structlog").error("[DMN] Hamlib PTT", status = 'FAILED') response = {'command': 'test_hamlib', 'result': 'FAILED'} - - hamlib.set_ptt(False) + + hamlib.set_ptt(False) hamlib.close_rig() - + jsondata = json.dumps(response) sock.SOCKET_QUEUE.put(jsondata) - + except Exception as e: print(e) @@ -343,27 +343,27 @@ if __name__ == '__main__': # --------------------------------------------GET PARAMETER INPUTS PARSER = argparse.ArgumentParser(description='FreeDATA Daemon') - PARSER.add_argument('--port', dest="socket_port",default=3001, help="Socket port in the range of 1024-65536", type=int) + PARSER.add_argument('--port', dest="socket_port",default=3001, help="Socket port in the range of 1024-65536", type=int) ARGS = PARSER.parse_args() - + static.DAEMONPORT = ARGS.socket_port - + try: if sys.platform == 'linux': logging_path = os.getenv("HOME") + '/.config/' + 'FreeDATA/' + 'daemon' - + if sys.platform == 'darwin': - logging_path = os.getenv("HOME") + '/Library/' + 'Application Support/' + 'FreeDATA/' + 'daemon' - + logging_path = os.getenv("HOME") + '/Library/' + 'Application Support/' + 'FreeDATA/' + 'daemon' + if sys.platform == 'win32' or sys.platform == 'win64': - logging_path = os.getenv('APPDATA') + '/' + 'FreeDATA/' + 'daemon' - + logging_path = os.getenv('APPDATA') + '/' + 'FreeDATA/' + 'daemon' + if not os.path.exists(logging_path): os.makedirs(logging_path) log_handler.setup_logging(logging_path) except: - structlog.get_logger("structlog").error("[DMN] logger init error") + structlog.get_logger("structlog").error("[DMN] logger init error") try: structlog.get_logger("structlog").info("[DMN] Starting TCP/IP socket", port=static.DAEMONPORT) @@ -379,7 +379,7 @@ if __name__ == '__main__': os._exit(1) daemon = DAEMON() - + structlog.get_logger("structlog").info("[DMN] Starting FreeDATA Daemon", author="DJ2LS", year="2022", version=static.VERSION) while True: time.sleep(1) diff --git a/tnc/data_handler.py b/tnc/data_handler.py index 4ffa5f6d..ec178350 100644 --- a/tnc/data_handler.py +++ b/tnc/data_handler.py @@ -24,7 +24,7 @@ import base64 import numpy as np TESTMODE = False - + DATA_QUEUE_TRANSMIT = queue.Queue() DATA_QUEUE_RECEIVED = queue.Queue() @@ -32,7 +32,7 @@ class DATA(): """ """ def __init__(self): - + self.mycallsign = static.MYCALLSIGN # initial callsign. Will be overwritten later self.data_queue_transmit = DATA_QUEUE_TRANSMIT @@ -46,28 +46,28 @@ class DATA(): self.session_connect_max_retries = 3 self.transmission_uuid = '' - + self.received_mycall_crc = b'' # received my callsign crc if we received a crc for another ssid - self.data_channel_last_received = 0.0 # time of last "live sign" of a frame + self.data_channel_last_received = 0.0 # time of last "live sign" of a frame self.burst_ack_snr = 0 # SNR from received ack frames self.burst_ack = False # if we received an acknowledge frame for a burst self.data_frame_ack_received = False # if we received an acknowledge frame for a data frame self.rpt_request_received = False # if we received an request for repeater frames - self.rpt_request_buffer = [] # requested frames, saved in a list + self.rpt_request_buffer = [] # requested frames, saved in a list self.rx_start_of_transmission = 0 # time of transmission start self.data_frame_bof = b'BOF' # 2 bytes for the BOF End of File indicator in a data frame self.data_frame_eof = b'EOF' # 2 bytes for the EOF End of File indicator in a data frame self.rx_n_max_retries_per_burst = 50 self.n_retries_per_burst = 0 - + self.received_low_bandwith_mode = False # indicator if we recevied a low bandwith mode channel ope ner self.data_channel_max_retries = 5 - + self.mode_list_low_bw = [14,12] self.time_list_low_bw = [3,7] @@ -82,15 +82,15 @@ class DATA(): else: self.mode_list = self.mode_list_high_bw # mode list of available modes, each mode will be used 2times per speed level self.time_list = self.time_list_high_bw # list for time to wait for correspinding mode in seconds - + self.speed_level = len(self.mode_list) - 1 # speed level for selecting mode static.ARQ_SPEED_LEVEL = self.speed_level - + self.is_IRS = False self.burst_nack = False self.burst_nack_counter = 0 self.frame_received_counter = 0 - + self.rx_frame_bof_received = False self.rx_frame_eof_received = False @@ -98,22 +98,22 @@ class DATA(): worker_thread_transmit = threading.Thread(target=self.worker_transmit, name="worker thread transmit",daemon=True) worker_thread_transmit.start() - + worker_thread_receive = threading.Thread(target=self.worker_receive, name="worker thread receive",daemon=True) worker_thread_receive.start() - + # START THE THREAD FOR THE TIMEOUT WATCHDOG watchdog_thread = threading.Thread(target=self.watchdog, name="watchdog",daemon=True) watchdog_thread.start() - + arq_session_thread = threading.Thread(target=self.heartbeat, name="watchdog",daemon=True) - arq_session_thread.start() - + arq_session_thread.start() + self.beacon_interval = 0 self.beacon_thread = threading.Thread(target=self.run_beacon, name="watchdog",daemon=True) self.beacon_thread.start() - - + + def worker_transmit(self): """ """ while True: @@ -133,7 +133,7 @@ class DATA(): # [0] PING # [1] dxcallsign self.transmit_ping(data[1]) - + elif data[0] == 'BEACON': # [0] BEACON # [1] INTERVAL int @@ -144,7 +144,7 @@ class DATA(): static.BEACON_STATE = True else: static.BEACON_STATE = False - + elif data[0] == 'ARQ_RAW': # [0] ARQ_RAW # [1] DATA_OUT bytes @@ -154,10 +154,10 @@ class DATA(): # [5] mycallsign with ssid self.open_dc_and_transmit(data[1], data[2], data[3], data[4], data[5]) - + elif data[0] == 'CONNECT': # [0] DX CALLSIGN - self.arq_session_handler(data[1]) + self.arq_session_handler(data[1]) elif data[0] == 'DISCONNECT': # [0] DX CALLSIGN @@ -171,7 +171,7 @@ class DATA(): print(f"wrong command {data}") pass - + def worker_receive(self): """ """ while True: @@ -179,25 +179,25 @@ class DATA(): # [0] bytes # [1] freedv instance # [2] bytes_per_frame - self.process_data(bytes_out=data[0],freedv=data[1],bytes_per_frame=data[2]) + self.process_data(bytes_out=data[0],freedv=data[1],bytes_per_frame=data[2]) - def process_data(self, bytes_out, freedv, bytes_per_frame): + def process_data(self, bytes_out, freedv, bytes_per_frame): """ Args: - bytes_out: - freedv: - bytes_per_frame: + bytes_out: + freedv: + bytes_per_frame: Returns: """ # forward data only if broadcast or we are the receiver - # bytes_out[1:4] == callsign check for signalling frames, + # bytes_out[1:4] == callsign check for signalling frames, # bytes_out[2:5] == transmission # we could also create an own function, which returns True. - + frametype = int.from_bytes(bytes(bytes_out[:1]), "big") @@ -207,7 +207,7 @@ class DATA(): _valid1, _ = helpers.check_callsign(self.mycallsign, bytes(bytes_out[1:4])) _valid2, _ = helpers.check_callsign(self.mycallsign, bytes(bytes_out[2:5])) if _valid1 or _valid2 or frametype in [200, 201, 210, 250]: - + # CHECK IF FRAMETYPE IS BETWEEN 10 and 50 ------------------------ frame = frametype - 10 n_frames_per_burst = int.from_bytes(bytes(bytes_out[1:2]), "big") @@ -250,8 +250,8 @@ class DATA(): elif frametype == 64: structlog.get_logger("structlog").debug("BURST NACK RECEIVED....") self.burst_nack_received(bytes_out[:-2]) - - + + # CQ FRAME elif frametype == 200: structlog.get_logger("structlog").debug("CQ RECEIVED....") @@ -267,7 +267,7 @@ class DATA(): elif frametype == 210: structlog.get_logger("structlog").debug("PING RECEIVED....") self.received_ping(bytes_out[:-2]) - + # PING ACK elif frametype == 211: @@ -295,7 +295,7 @@ class DATA(): elif frametype == 225 or frametype == 227: structlog.get_logger("structlog").debug("ARQ arq_received_data_channel_opener") self.arq_received_data_channel_opener(bytes_out[:-2]) - + # ARQ CHANNEL IS OPENED elif frametype == 226 or frametype == 228: structlog.get_logger("structlog").debug("ARQ arq_received_channel_is_open") @@ -308,7 +308,7 @@ class DATA(): structlog.get_logger("structlog").debug("ARQ manual mode ") self.arq_received_data_channel_opener(bytes_out[:-2]) - + # ARQ STOP TRANSMISSION elif frametype == 249: structlog.get_logger("structlog").debug("ARQ received stop transmission") @@ -322,7 +322,7 @@ class DATA(): # TESTFRAMES elif frametype == 255: structlog.get_logger("structlog").debug("TESTFRAME RECEIVED", frame=bytes_out[:]) - + else: structlog.get_logger("structlog").warning("[TNC] ARQ - other frame type", frametype=frametype) @@ -336,29 +336,29 @@ class DATA(): """ Args: - data_in:bytes: - bytes_per_frame:int: - snr:int: - freedv: + data_in:bytes: + bytes_per_frame:int: + snr:int: + freedv: Returns: """ - data_in = bytes(data_in) - + data_in = bytes(data_in) + # get received crc for different mycall ssids - self.received_mycall_crc = data_in[2:5] - + self.received_mycall_crc = data_in[2:5] + global TESTMODE - + # check if callsign ssid override valid, mycallsign = helpers.check_callsign(self.mycallsign, self.received_mycall_crc) if not valid: # ARQ data packet not for me. if not TESTMODE: - self.arq_cleanup() + self.arq_cleanup() return - + # only process data if we are in ARQ and BUSY state else return to quit if not static.ARQ_STATE and static.TNC_STATE != 'BUSY': return @@ -371,28 +371,28 @@ class DATA(): static.ARQ_STATE = True static.INFO.append("ARQ;RECEIVING") self.data_channel_last_received = int(time.time()) - + # get some important data from the frame RX_N_FRAME_OF_BURST = int.from_bytes(bytes(data_in[:1]), "big") - 10 # get number of burst frame RX_N_FRAMES_PER_BURST = int.from_bytes(bytes(data_in[1:2]), "big") # get number of bursts from received frame - ''' - The RX burst buffer needs to have a fixed length filled with "None". We need this later for counting the "Nones" + ''' + The RX burst buffer needs to have a fixed length filled with "None". We need this later for counting the "Nones" check if burst buffer has expected length else create it ''' if len(static.RX_BURST_BUFFER) != RX_N_FRAMES_PER_BURST: - static.RX_BURST_BUFFER = [None] * RX_N_FRAMES_PER_BURST + static.RX_BURST_BUFFER = [None] * RX_N_FRAMES_PER_BURST # append data to rx burst buffer static.RX_BURST_BUFFER[RX_N_FRAME_OF_BURST] = data_in[8:] # [frame_type][n_frames_per_burst][CRC24][CRC24] - - - + + + structlog.get_logger("structlog").debug("[TNC] static.RX_BURST_BUFFER", buffer=static.RX_BURST_BUFFER) - + helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) - + ''' check if we received all frames per burst by checking if burst buffer has no more "Nones" this is the ideal case because we received all data @@ -410,11 +410,11 @@ class DATA(): if static.RX_FRAME_BUFFER.endswith(temp_burst_buffer): structlog.get_logger("structlog").info("[TNC] ARQ | RX | Frame already received - sending ACK again") static.RX_BURST_BUFFER = [] - + # here we are going to search for our data in the last received bytes # this increases chance we are not loosing the entire frame in case of signalling frame loss else: - + # static.RX_FRAME_BUFFER --> exisitng data # temp_burst_buffer --> new data @@ -435,20 +435,20 @@ class DATA(): else: static.RX_FRAME_BUFFER += temp_burst_buffer structlog.get_logger("structlog").debug("[TNC] ARQ | RX | appending data to buffer") - + # lets check if we didnt receive a BOF and EOF yet to avoid sending ack frames if we already received all data - if not self.rx_frame_bof_received and not self.rx_frame_eof_received and data_in.find(self.data_frame_eof) < 0: - - self.frame_received_counter += 1 + if not self.rx_frame_bof_received and not self.rx_frame_eof_received and data_in.find(self.data_frame_eof) < 0: + + self.frame_received_counter += 1 if self.frame_received_counter >= 2: self.frame_received_counter = 0 self.speed_level += 1 if self.speed_level >= len(self.mode_list): self.speed_level = len(self.mode_list) - 1 - static.ARQ_SPEED_LEVEL = self.speed_level + static.ARQ_SPEED_LEVEL = self.speed_level # updated modes we are listening to self.set_listening_modes(self.mode_list[self.speed_level]) @@ -470,28 +470,28 @@ class DATA(): time.sleep(0.01) # reset n retries per burst counter self.n_retries_per_burst = 0 - - # calculate statistics - self.calculate_transfer_rate_rx(self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)) - + # calculate statistics + self.calculate_transfer_rate_rx(self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)) + + # check if we received last frame of burst and we have "Nones" in our rx buffer # this is an indicator for missed frames. - # with this way of doing this, we always MUST receive the last frame of a burst otherwise the entire + # with this way of doing this, we always MUST receive the last frame of a burst otherwise the entire # burst is lost elif RX_N_FRAME_OF_BURST == RX_N_FRAMES_PER_BURST -1: # check where a None is in our burst buffer and do frame+1, beacuse lists start at 0 missing_frames = [(frame+1) for frame, element in enumerate(static.RX_BURST_BUFFER) if element == None] - + structlog.get_logger("structlog").debug("all frames per burst received", frame=RX_N_FRAME_OF_BURST, frames=RX_N_FRAMES_PER_BURST) - + # set n frames per burst to modem # this is an idea so its not getting lost.... - # we need to work on this + # we need to work on this codec2.api.freedv_set_frames_per_burst(freedv,len(missing_frames)) - - - # then create a repeat frame + + + # then create a repeat frame rpt_frame = bytearray(14) rpt_frame[:1] = bytes([62]) rpt_frame[1:4] = static.DXCALLSIGN_CRC @@ -507,22 +507,22 @@ class DATA(): while static.TRANSMITTING: time.sleep(0.01) self.calculate_transfer_rate_rx(self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)) - - + + # we should never reach this point else: structlog.get_logger("structlog").error("we shouldnt reach this point...", frame=RX_N_FRAME_OF_BURST, frames=RX_N_FRAMES_PER_BURST) - + # We have a BOF and EOF flag in our data. If we received both we received our frame. - # In case of loosing data but we received already a BOF and EOF we need to make sure, we - # received the complete last burst by checking it for Nones + # In case of loosing data but we received already a BOF and EOF we need to make sure, we + # received the complete last burst by checking it for Nones bof_position = static.RX_FRAME_BUFFER.find(self.data_frame_bof) eof_position = static.RX_FRAME_BUFFER.find(self.data_frame_eof) # get total bytes per transmission information as soon we recevied a frame with a BOF if bof_position >=0: - + payload = static.RX_FRAME_BUFFER[bof_position+len(self.data_frame_bof):eof_position] frame_length = int.from_bytes(payload[4:8], "big") #4:8 4bytes static.TOTAL_BYTES = frame_length @@ -530,13 +530,13 @@ class DATA(): compression_factor = np.clip(compression_factor, 0, 255) #limit to max value of 255 static.ARQ_COMPRESSION_FACTOR = compression_factor / 10 self.calculate_transfer_rate_rx(self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)) - + if bof_position >= 0 and eof_position > 0 and not None in static.RX_BURST_BUFFER: print(f"bof_position {bof_position} / eof_position {eof_position}") self.rx_frame_bof_received = True self.rx_frame_eof_received = True - + #now extract raw data from buffer payload = static.RX_FRAME_BUFFER[bof_position+len(self.data_frame_bof):eof_position] # get the data frame crc @@ -549,30 +549,30 @@ class DATA(): data_frame = payload[9:] data_frame_crc_received = helpers.get_crc_32(data_frame) - + # check if data_frame_crc is equal with received crc if data_frame_crc == data_frame_crc_received: structlog.get_logger("structlog").info("[TNC] ARQ | RX | DATA FRAME SUCESSFULLY RECEIVED") - - + + # decompression data_frame_decompressed = zlib.decompress(data_frame) static.ARQ_COMPRESSION_FACTOR = len(data_frame_decompressed) / len(data_frame) data_frame = data_frame_decompressed - - + + uniqueid = str(uuid.uuid4()) timestamp = int(time.time()) - - + + # check if callsign ssid override valid, mycallsign = helpers.check_callsign(self.mycallsign, self.received_mycall_crc) if not valid: # ARQ data packet not for me. if not TESTMODE: - self.arq_cleanup() + self.arq_cleanup() return - + base64_data = base64.b64encode(data_frame) base64_data = base64_data.decode("utf-8") static.RX_BUFFER.append([uniqueid, timestamp, static.DXCALLSIGN, static.DXGRID, base64_data]) @@ -581,7 +581,7 @@ class DATA(): print(jsondata) sock.SOCKET_QUEUE.put(json_data_out) static.INFO.append("ARQ;RECEIVING;SUCCESS") - + # BUILDING ACK FRAME FOR DATA FRAME ack_frame = bytearray(14) ack_frame[:1] = bytes([61]) @@ -599,7 +599,7 @@ class DATA(): time.sleep(0.01) # update our statistics AFTER the frame ACK self.calculate_transfer_rate_rx(self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)) - + structlog.get_logger("structlog").info("[TNC] | RX | DATACHANNEL [" + str(self.mycallsign, 'utf-8') + "]<< >>[" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR) else: @@ -610,10 +610,10 @@ class DATA(): nack_frame = bytearray(14) nack_frame[:1] = bytes([63]) nack_frame[1:4] = static.DXCALLSIGN_CRC - nack_frame[4:7] = static.MYCALLSIGN_CRC + nack_frame[4:7] = static.MYCALLSIGN_CRC nack_frame[7:8] = bytes([int(snr)]) nack_frame[8:9] = bytes([int(self.speed_level)]) - + # TRANSMIT NACK FRAME FOR BURST txbuffer = [nack_frame] static.TRANSMITTING = True @@ -621,43 +621,43 @@ class DATA(): # wait while transmitting while static.TRANSMITTING: time.sleep(0.01) - + # update session timeout self.arq_session_last_received = int(time.time()) # we need to update our timeout timestamp - + # And finally we do a cleanup of our buffers and states # do cleanup only when not in testmode if not TESTMODE: - self.arq_cleanup() - + self.arq_cleanup() + def arq_transmit(self, data_out:bytes, mode:int, n_frames_per_burst:int): """ Args: - data_out:bytes: - mode:int: - n_frames_per_burst:int: + data_out:bytes: + mode:int: + n_frames_per_burst:int: Returns: """ global TESTMODE - + self.arq_file_transfer = True - + self.speed_level = len(self.mode_list) - 1 # speed level for selecting mode static.ARQ_SPEED_LEVEL = self.speed_level TX_N_SENT_BYTES = 0 # already sent bytes per data frame self.tx_n_retry_of_burst = 0 # retries we already sent data TX_N_MAX_RETRIES_PER_BURST = 50 # max amount of retries we sent before frame is lost - TX_N_FRAMES_PER_BURST = n_frames_per_burst # amount of n frames per burst + TX_N_FRAMES_PER_BURST = n_frames_per_burst # amount of n frames per burst TX_BUFFER = [] # our buffer for appending new data - + # TIMEOUTS BURST_ACK_TIMEOUT_SECONDS = 3.0 # timeout for burst acknowledges DATA_FRAME_ACK_TIMEOUT_SECONDS = 3.0 # timeout for data frame acknowledges @@ -669,22 +669,22 @@ class DATA(): static.TOTAL_BYTES = len(data_out) frame_total_size = len(data_out).to_bytes(4, byteorder='big') static.INFO.append("ARQ;TRANSMITTING") - + jsondata = {"arq":"transmission", "status" :"transmitting", "uuid" : self.transmission_uuid, "percent" : static.ARQ_TRANSMISSION_PERCENT, "bytesperminute" : static.ARQ_BYTES_PER_MINUTE} json_data_out = json.dumps(jsondata) sock.SOCKET_QUEUE.put(json_data_out) - - + + structlog.get_logger("structlog").info("[TNC] | TX | DATACHANNEL", mode=mode, Bytes=static.TOTAL_BYTES) - + # compression data_frame_compressed = zlib.compress(data_out) compression_factor = len(data_out) / len(data_frame_compressed) static.ARQ_COMPRESSION_FACTOR = np.clip(compression_factor, 0, 255) compression_factor = bytes([int(static.ARQ_COMPRESSION_FACTOR * 10)]) - - data_out = data_frame_compressed + + data_out = data_frame_compressed # reset statistics tx_start_of_transmission = time.time() @@ -693,18 +693,18 @@ class DATA(): # append a crc and beginn and end of file indicators frame_payload_crc = helpers.get_crc_32(data_out) structlog.get_logger("structlog").debug("frame payload crc", crc=frame_payload_crc) - - + + # data_out = self.data_frame_bof + frame_payload_crc + data_out + self.data_frame_eof data_out = self.data_frame_bof + frame_payload_crc + frame_total_size + compression_factor + data_out + self.data_frame_eof - + #initial bufferposition is 0 bufferposition = 0 # iterate through data out buffer while bufferposition < len(data_out) and not self.data_frame_ack_received and static.ARQ_STATE: - + # we have TX_N_MAX_RETRIES_PER_BURST attempts for sending a burst for self.tx_n_retry_of_burst in range(0,TX_N_MAX_RETRIES_PER_BURST): @@ -713,7 +713,7 @@ class DATA(): # force usage of selected mode if mode != 255: data_mode = mode - + structlog.get_logger("structlog").debug("FIXED MODE", mode=data_mode) else: @@ -728,74 +728,74 @@ class DATA(): if self.speed_level < 0: self.speed_level = 0 ''' - + #if self.tx_n_retry_of_burst <= 1: # self.speed_level += 1 # if self.speed_level >= len(self.mode_list)-1: # self.speed_level = len(self.mode_list)-1 - + # if speed level is greater than our available modes, set speed level to maximum = lenght of mode list -1 - + if self.speed_level >= len(self.mode_list): self.speed_level = len(self.mode_list) - 1 static.ARQ_SPEED_LEVEL = self.speed_level data_mode = self.mode_list[self.speed_level] - - structlog.get_logger("structlog").debug("Speed-level:", level=self.speed_level, retry=self.tx_n_retry_of_burst, mode=data_mode) - - + + structlog.get_logger("structlog").debug("Speed-level:", level=self.speed_level, retry=self.tx_n_retry_of_burst, mode=data_mode) + + # payload information - payload_per_frame = modem.get_bytes_per_frame(data_mode) -2 + payload_per_frame = modem.get_bytes_per_frame(data_mode) -2 # tempbuffer list for storing our data frames tempbuffer = [] - + # append data frames with TX_N_FRAMES_PER_BURST to tempbuffer # this part ineeds to a completly rewrite! # TX_NF_RAMES_PER_BURST = 1 is working arqheader = bytearray() arqheader[:1] = bytes([10]) #bytes([10 + i]) - arqheader[1:2] = bytes([TX_N_FRAMES_PER_BURST]) + arqheader[1:2] = bytes([TX_N_FRAMES_PER_BURST]) arqheader[2:5] = static.DXCALLSIGN_CRC arqheader[5:8] = static.MYCALLSIGN_CRC - - bufferposition_end = (bufferposition + payload_per_frame - len(arqheader)) + + bufferposition_end = (bufferposition + payload_per_frame - len(arqheader)) # normal behavior if bufferposition_end <= len(data_out): - + frame = data_out[bufferposition:bufferposition_end] - frame = arqheader + frame - - # this point shouldnt reached that often + frame = arqheader + frame + + # this point shouldnt reached that often elif bufferposition > len(data_out): break - - # the last bytes of a frame + + # the last bytes of a frame else: extended_data_out = data_out[bufferposition:] extended_data_out += bytes([0]) * (payload_per_frame-len(extended_data_out)-len(arqheader)) frame = arqheader + extended_data_out - - + + # append frame to tempbuffer for transmission tempbuffer.append(frame) - + structlog.get_logger("structlog").debug("[TNC] tempbuffer", tempbuffer=tempbuffer) structlog.get_logger("structlog").info("[TNC] ARQ | TX | FRAMES", mode=data_mode, fpb=TX_N_FRAMES_PER_BURST, retry=self.tx_n_retry_of_burst) - + # we need to set our TRANSMITTING flag before we are adding an object the transmit queue # this is not that nice, we could improve this somehow static.TRANSMITTING = True modem.MODEM_TRANSMIT_QUEUE.put([data_mode,1,0,tempbuffer]) - + # wait while transmitting while static.TRANSMITTING: time.sleep(0.01) - + # after transmission finished wait for an ACK or RPT frame ''' burstacktimeout = time.time() + BURST_ACK_TIMEOUT_SECONDS + 100 @@ -805,7 +805,7 @@ class DATA(): #burstacktimeout = time.time() + BURST_ACK_TIMEOUT_SECONDS + 100 while not self.burst_ack and not self.burst_nack and not self.rpt_request_received and not self.data_frame_ack_received and static.ARQ_STATE: time.sleep(0.01) - + # once we received a burst ack, reset its state and break the RETRIES loop if self.burst_ack: self.burst_ack = False # reset ack state @@ -821,52 +821,52 @@ class DATA(): if self.data_frame_ack_received: break #break retry loop - - + + # we need this part for leaving the repeat loop # static.ARQ_STATE == 'DATA' --> when stopping transmission manually if not static.ARQ_STATE: #print("not ready for data...leaving loop....") break - - self.calculate_transfer_rate_tx(tx_start_of_transmission, bufferposition_end, len(data_out)) + + self.calculate_transfer_rate_tx(tx_start_of_transmission, bufferposition_end, len(data_out)) # NEXT ATTEMPT structlog.get_logger("structlog").debug("ATTEMPT", retry=self.tx_n_retry_of_burst, maxretries=TX_N_MAX_RETRIES_PER_BURST,overflows=static.BUFFER_OVERFLOW_COUNTER) - + # update buffer position bufferposition = bufferposition_end # update stats - self.calculate_transfer_rate_tx(tx_start_of_transmission, bufferposition_end, len(data_out)) - + self.calculate_transfer_rate_tx(tx_start_of_transmission, bufferposition_end, len(data_out)) + jsondata = {"arq":"transmission", "status" :"transmitting", "uuid" : self.transmission_uuid, "percent" : static.ARQ_TRANSMISSION_PERCENT, "bytesperminute" : static.ARQ_BYTES_PER_MINUTE} json_data_out = json.dumps(jsondata) sock.SOCKET_QUEUE.put(json_data_out) - + #GOING TO NEXT ITERATION - + if self.data_frame_ack_received: - + static.INFO.append("ARQ;TRANSMITTING;SUCCESS") jsondata = {"arq":"transmission", "status" :"success", "uuid" : self.transmission_uuid, "percent" : static.ARQ_TRANSMISSION_PERCENT, "bytesperminute" : static.ARQ_BYTES_PER_MINUTE} json_data_out = json.dumps(jsondata) sock.SOCKET_QUEUE.put(json_data_out) - + structlog.get_logger("structlog").info("ARQ | TX | DATA TRANSMITTED!", BytesPerMinute=static.ARQ_BYTES_PER_MINUTE, BitsPerSecond=static.ARQ_BITS_PER_SECOND, overflows=static.BUFFER_OVERFLOW_COUNTER) - + else: static.INFO.append("ARQ;TRANSMITTING;FAILED") jsondata = {"arq":"transmission", "status" :"failed", "uuid" : self.transmission_uuid, "percent" : static.ARQ_TRANSMISSION_PERCENT, "bytesperminute" : static.ARQ_BYTES_PER_MINUTE} json_data_out = json.dumps(jsondata) sock.SOCKET_QUEUE.put(json_data_out) - + structlog.get_logger("structlog").info("ARQ | TX | TRANSMISSION FAILED OR TIME OUT!", overflows=static.BUFFER_OVERFLOW_COUNTER) self.stop_transmission() - # and last but not least doing a state cleanup + # and last but not least doing a state cleanup # do cleanup only when not in testmode if not TESTMODE: self.arq_cleanup() @@ -883,17 +883,17 @@ class DATA(): """ Args: - data_in:bytes: + data_in:bytes: Returns: """ - + # increase speed level if we received a burst ack #self.speed_level += 1 #if self.speed_level >= len(self.mode_list)-1: # self.speed_level = len(self.mode_list)-1 - + # only process data if we are in ARQ and BUSY state if static.ARQ_STATE: helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) @@ -903,7 +903,7 @@ class DATA(): self.speed_level= int.from_bytes(bytes(data_in[6:7]), "big") static.ARQ_SPEED_LEVEL = self.speed_level print(self.speed_level) - # reset burst nack counter + # reset burst nack counter self.burst_nack_counter = 0 # reset n retries per burst counter self.n_retries_per_burst = 0 @@ -912,17 +912,17 @@ class DATA(): """ Args: - data_in:bytes: + data_in:bytes: Returns: """ - + # increase speed level if we received a burst ack #self.speed_level += 1 #if self.speed_level >= len(self.mode_list)-1: # self.speed_level = len(self.mode_list)-1 - + # only process data if we are in ARQ and BUSY state if static.ARQ_STATE: helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) @@ -938,7 +938,7 @@ class DATA(): def frame_ack_received(self): """ """ # only process data if we are in ARQ and BUSY state - if static.ARQ_STATE: + if static.ARQ_STATE: helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) self.data_frame_ack_received = True # Force data loops of TNC to stop and continue with next frame self.data_channel_last_received = int(time.time()) # we need to update our timeout timestamp @@ -948,7 +948,7 @@ class DATA(): """ Args: - data_in:bytes: + data_in:bytes: Returns: @@ -957,9 +957,9 @@ class DATA(): static.INFO.append("ARQ;TRANSMITTING;FAILED") jsondata = {"arq":"transmission", "status" : "failed", "uuid" : self.transmission_uuid, "percent" : static.ARQ_TRANSMISSION_PERCENT, "bytesperminute" : static.ARQ_BYTES_PER_MINUTE} json_data_out = json.dumps(jsondata) - sock.SOCKET_QUEUE.put(json_data_out) + sock.SOCKET_QUEUE.put(json_data_out) self.arq_session_last_received = int(time.time()) # we need to update our timeout timestamp - + if not TESTMODE: self.arq_cleanup() @@ -969,7 +969,7 @@ class DATA(): """ Args: - data_in:bytes: + data_in:bytes: Returns: @@ -978,7 +978,7 @@ class DATA(): # only process data if we are in ARQ and BUSY state if static.ARQ_STATE and static.TNC_STATE == 'BUSY': helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) - + self.rpt_request_received = True self.data_channel_last_received = int(time.time()) # we need to update our timeout timestamp self.rpt_request_buffer = [] @@ -1000,7 +1000,7 @@ class DATA(): """ Args: - callsign: + callsign: Returns: @@ -1008,7 +1008,7 @@ class DATA(): # das hier müssen wir checken. Sollte vielleicht in INIT!!! self.datachannel_timeout = False structlog.get_logger("structlog").info("SESSION [" + str(self.mycallsign, 'utf-8') + "]>> <<[" + str(static.DXCALLSIGN, 'utf-8') + "]", state=static.ARQ_SESSION_STATE) - + self.open_session(callsign) # wait until data channel is open @@ -1028,7 +1028,7 @@ class DATA(): """ Args: - callsign: + callsign: Returns: @@ -1037,31 +1037,31 @@ class DATA(): static.ARQ_SESSION_STATE = 'connecting' frametype = bytes([221]) - + connection_frame = bytearray(14) connection_frame[:1] = frametype connection_frame[1:4] = static.DXCALLSIGN_CRC connection_frame[4:7] = static.MYCALLSIGN_CRC connection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign) - - + + while not static.ARQ_SESSION: time.sleep(0.01) for attempt in range(1,self.session_connect_max_retries+1): - txbuffer = [connection_frame] + txbuffer = [connection_frame] static.TRANSMITTING = True - + structlog.get_logger("structlog").info("SESSION [" + str(self.mycallsign, 'utf-8') + "]>>?<<[" + str(static.DXCALLSIGN, 'utf-8') + "]", a=attempt, state=static.ARQ_SESSION_STATE) - - modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) + + modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) # wait while transmitting while static.TRANSMITTING: time.sleep(0.01) - - timeout = time.time() + 3 - while time.time() < timeout: + + timeout = time.time() + 3 + while time.time() < timeout: time.sleep(0.01) - # break if data channel is opened + # break if data channel is opened if static.ARQ_SESSION: # eventuell einfach nur return true um die nächste break ebene zu vermeiden? return True @@ -1076,13 +1076,13 @@ class DATA(): # self.arq_cleanup() self.close_session() return False - - + + def received_session_opener(self, data_in:bytes): """ Args: - data_in:bytes: + data_in:bytes: Returns: @@ -1094,7 +1094,7 @@ class DATA(): static.DXCALLSIGN_CRC = bytes(data_in[4:7]) static.DXCALLSIGN = helpers.bytes_to_callsign(bytes(data_in[7:13])) - + helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) structlog.get_logger("structlog").info("SESSION [" + str(self.mycallsign, 'utf-8') + "]>>|<<[" + str(static.DXCALLSIGN, 'utf-8') + "]", state=static.ARQ_SESSION_STATE) static.ARQ_SESSION = True @@ -1114,20 +1114,20 @@ class DATA(): self.arq_cleanup() frametype = bytes([223]) - + disconnection_frame = bytearray(14) disconnection_frame[:1] = frametype disconnection_frame[1:4] = static.DXCALLSIGN_CRC disconnection_frame[4:7] = static.MYCALLSIGN_CRC disconnection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign) - - txbuffer = [disconnection_frame] + + txbuffer = [disconnection_frame] static.TRANSMITTING = True - - modem.MODEM_TRANSMIT_QUEUE.put([14,5,250,txbuffer]) + + modem.MODEM_TRANSMIT_QUEUE.put([14,5,250,txbuffer]) # wait while transmitting while static.TRANSMITTING: - time.sleep(0.01) + time.sleep(0.01) def received_session_close(self, data_in:bytes): """ @@ -1135,7 +1135,7 @@ class DATA(): the DXCALLSIGN_CRC matches the remote station participating in the session. Args: - data_in:bytes: + data_in:bytes: Returns: """ @@ -1159,20 +1159,20 @@ class DATA(): # static.ARQ_SESSION_STATE = 'connected' frametype = bytes([222]) - + connection_frame = bytearray(14) connection_frame[:1] = frametype connection_frame[1:4] = static.DXCALLSIGN_CRC connection_frame[4:7] = static.MYCALLSIGN_CRC - - txbuffer = [connection_frame] + + txbuffer = [connection_frame] static.TRANSMITTING = True - - modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) + + modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) # wait while transmitting while static.TRANSMITTING: - time.sleep(0.01) + time.sleep(0.01) @@ -1181,12 +1181,12 @@ class DATA(): """ Args: - data_in:bytes: + data_in:bytes: Returns: """ - + # Accept session data if the DXCALLSIGN_CRC matches the station in static. _valid_crc, _ = helpers.check_callsign(static.DXCALLSIGN, bytes(data_in[4:7])) if _valid_crc: @@ -1194,7 +1194,7 @@ class DATA(): helpers.add_to_heard_stations(static.DXCALLSIGN, static.DXGRID, 'SESSION-HB', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) self.arq_session_last_received = int(time.time()) # we need to update our timeout timestamp - + static.ARQ_SESSION = True static.ARQ_SESSION_STATE = 'connected' static.TNC_STATE = 'BUSY' @@ -1210,55 +1210,55 @@ class DATA(): """ Args: - data_out:bytes: - mode:int: - n_frames_per_burst:int: + data_out:bytes: + mode:int: + n_frames_per_burst:int: Returns: """ # overwrite mycallsign in case of different SSID self.mycallsign = mycallsign - + static.TNC_STATE = 'BUSY' self.arq_file_transfer = True - + self.transmission_uuid = transmission_uuid - + # wait a moment for the case, an heartbeat is already on the way back to us if static.ARQ_SESSION: time.sleep(0.5) - - + + self.datachannel_timeout = False - + # we need to compress data for gettin a compression factor. # so we are compressing twice. This is not that nice and maybe theres another way # for calculating transmission statistics static.ARQ_COMPRESSION_FACTOR = len(data_out) / len(zlib.compress(data_out)) - + self.arq_open_data_channel(mode, n_frames_per_burst, mycallsign) - + # wait until data channel is open while not static.ARQ_STATE and not self.datachannel_timeout: time.sleep(0.01) - + if static.ARQ_STATE: self.arq_transmit(data_out, mode, n_frames_per_burst) else: return False - - def arq_open_data_channel(self, mode:int, n_frames_per_burst:int, mycallsign): + + def arq_open_data_channel(self, mode:int, n_frames_per_burst:int, mycallsign): """ Args: - mode:int: - n_frames_per_burst:int: + mode:int: + n_frames_per_burst:int: Returns: """ - self.is_IRS = False + self.is_IRS = False self.data_channel_last_received = int(time.time()) if static.LOW_BANDWITH_MODE and mode == 255: @@ -1269,34 +1269,34 @@ class DATA(): frametype = bytes([225]) structlog.get_logger("structlog").debug("requesting high bandwith mode") - + if 230 <= mode <= 240: structlog.get_logger("structlog").debug("requesting manual mode --> not yet implemented ") frametype = bytes([mode]) - + connection_frame = bytearray(14) connection_frame[:1] = frametype connection_frame[1:4] = static.DXCALLSIGN_CRC connection_frame[4:7] = static.MYCALLSIGN_CRC connection_frame[7:13] = helpers.callsign_to_bytes(mycallsign) - connection_frame[13:14] = bytes([n_frames_per_burst]) - + connection_frame[13:14] = bytes([n_frames_per_burst]) + while not static.ARQ_STATE: time.sleep(0.01) for attempt in range(1,self.data_channel_max_retries+1): static.INFO.append("DATACHANNEL;OPENING") - structlog.get_logger("structlog").info("[TNC] ARQ | DATA | TX | [" + str(mycallsign, 'utf-8') + "]>> <<[" + str(static.DXCALLSIGN, 'utf-8') + "]", attempt=str(attempt) + "/" + str(self.data_channel_max_retries)) - txbuffer = [connection_frame] + structlog.get_logger("structlog").info("[TNC] ARQ | DATA | TX | [" + str(mycallsign, 'utf-8') + "]>> <<[" + str(static.DXCALLSIGN, 'utf-8') + "]", attempt=str(attempt) + "/" + str(self.data_channel_max_retries)) + txbuffer = [connection_frame] static.TRANSMITTING = True - modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) + modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) # wait while transmitting while static.TRANSMITTING: time.sleep(0.01) - - timeout = time.time() + 3 - while time.time() < timeout: + + timeout = time.time() + 3 + while time.time() < timeout: time.sleep(0.01) - # break if data channel is opened + # break if data channel is opened if static.ARQ_STATE: break if static.ARQ_STATE: @@ -1308,7 +1308,7 @@ class DATA(): jsondata = {"arq":"transmission", "status" :"failed", "uuid" : self.transmission_uuid, "percent" : static.ARQ_TRANSMISSION_PERCENT, "bytesperminute" : static.ARQ_BYTES_PER_MINUTE} json_data_out = json.dumps(jsondata) sock.SOCKET_QUEUE.put(json_data_out) - + structlog.get_logger("structlog").warning("[TNC] ARQ | TX | DATA [" + str(mycallsign, 'utf-8') + "]>>X<<[" + str(static.DXCALLSIGN, 'utf-8') + "]") self.datachannel_timeout = True if not TESTMODE: @@ -1321,57 +1321,57 @@ class DATA(): #sys.exit() # close thread and so connection attempts - + def arq_received_data_channel_opener(self, data_in:bytes): """ Args: - data_in:bytes: + data_in:bytes: Returns: """ self.arq_file_transfer = True - self.is_IRS = True + self.is_IRS = True static.INFO.append("DATACHANNEL;RECEIVEDOPENER") static.DXCALLSIGN_CRC = bytes(data_in[4:7]) static.DXCALLSIGN = helpers.bytes_to_callsign(bytes(data_in[7:13])) - n_frames_per_burst = int.from_bytes(bytes(data_in[13:14]), "big") + n_frames_per_burst = int.from_bytes(bytes(data_in[13:14]), "big") frametype = int.from_bytes(bytes(data_in[:1]), "big") #check if we received low bandwith mode if frametype == 225: self.received_low_bandwith_mode = False - self.mode_list = self.mode_list_high_bw + self.mode_list = self.mode_list_high_bw self.time_list = self.time_list_high_bw - self.speed_level = len(self.mode_list) - 1 + self.speed_level = len(self.mode_list) - 1 else: self.received_low_bandwith_mode = True - self.mode_list = self.mode_list_low_bw + self.mode_list = self.mode_list_low_bw self.time_list = self.time_list_low_bw - self.speed_level = len(self.mode_list) - 1 - - + self.speed_level = len(self.mode_list) - 1 + + if 230 <= frametype <= 240: print("manual mode request") - + # updated modes we are listening to self.set_listening_modes(self.mode_list[self.speed_level]) helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) - + # check if callsign ssid override valid, mycallsign = helpers.check_callsign(self.mycallsign, data_in[1:4]) if not valid: # ARQ connect packet not for me. if not TESTMODE: - self.arq_cleanup() + self.arq_cleanup() return - + structlog.get_logger("structlog").info("[TNC] ARQ | DATA | RX | [" + str(mycallsign, 'utf-8') + "]>> <<[" + str(static.DXCALLSIGN, 'utf-8') + "]", bandwith="wide") - + static.ARQ_STATE = True static.TNC_STATE = 'BUSY' @@ -1386,22 +1386,22 @@ class DATA(): else: frametype = bytes([226]) structlog.get_logger("structlog").debug("responding with high bandwith mode") - + connection_frame = bytearray(14) connection_frame[:1] = frametype connection_frame[1:4] = static.DXCALLSIGN_CRC connection_frame[4:7] = static.MYCALLSIGN_CRC - connection_frame[13:14] = bytes([static.ARQ_PROTOCOL_VERSION]) #crc8 of version for checking protocol version + connection_frame[13:14] = bytes([static.ARQ_PROTOCOL_VERSION]) #crc8 of version for checking protocol version txbuffer = [connection_frame] - + static.TRANSMITTING = True modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) # wait while transmitting while static.TRANSMITTING: time.sleep(0.01) structlog.get_logger("structlog").info("[TNC] ARQ | DATA | RX | [" + str(mycallsign, 'utf-8') + "]>>|<<[" + str(static.DXCALLSIGN, 'utf-8') + "]", bandwith="wide", snr=static.SNR) - + # set start of transmission for our statistics self.rx_start_of_transmission = time.time() @@ -1411,7 +1411,7 @@ class DATA(): """ Called if we received a data channel opener Args: - data_in:bytes: + data_in:bytes: Returns: @@ -1419,26 +1419,26 @@ class DATA(): protocol_version = int.from_bytes(bytes(data_in[13:14]), "big") if protocol_version == static.ARQ_PROTOCOL_VERSION: static.INFO.append("DATACHANNEL;OPEN") - frametype = int.from_bytes(bytes(data_in[:1]), "big") - + frametype = int.from_bytes(bytes(data_in[:1]), "big") + if frametype == 228: self.received_low_bandwith_mode = True - self.mode_list = self.mode_list_low_bw + self.mode_list = self.mode_list_low_bw self.time_list = self.time_list_low_bw - self.speed_level = len(self.mode_list) - 1 + self.speed_level = len(self.mode_list) - 1 structlog.get_logger("structlog").debug("low bandwith mode", modes=self.mode_list) else: self.received_low_bandwith_mode = False - self.mode_list = self.mode_list_high_bw + self.mode_list = self.mode_list_high_bw self.time_list = self.time_list_high_bw - self.speed_level = len(self.mode_list) - 1 + self.speed_level = len(self.mode_list) - 1 structlog.get_logger("structlog").debug("high bandwith mode", modes=self.mode_list) - + helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) - + structlog.get_logger("structlog").info("[TNC] ARQ | DATA | TX | [" + str(self.mycallsign, 'utf-8') + "]>>|<<[" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR) - # as soon as we set ARQ_STATE to DATA, transmission starts + # as soon as we set ARQ_STATE to DATA, transmission starts static.ARQ_STATE = True self.data_channel_last_received = int(time.time()) else: @@ -1454,14 +1454,14 @@ class DATA(): """ Funktion for controlling pings Args: - dxcallsign:bytes: + dxcallsign:bytes: Returns: """ static.DXCALLSIGN = dxcallsign static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN) - + static.INFO.append("PING;SENDING") structlog.get_logger("structlog").info("[TNC] PING REQ [" + str(self.mycallsign, 'utf-8') + "] >>> [" + str(static.DXCALLSIGN, 'utf-8') + "]" ) @@ -1481,13 +1481,13 @@ class DATA(): # wait while transmitting while static.TRANSMITTING: time.sleep(0.01) - + def received_ping(self, data_in:bytes): """ Called if we received a ping Args: - data_in:bytes: + data_in:bytes: Returns: @@ -1496,7 +1496,7 @@ class DATA(): static.DXCALLSIGN_CRC = bytes(data_in[4:7]) static.DXCALLSIGN = helpers.bytes_to_callsign(bytes(data_in[7:13])) helpers.add_to_heard_stations(static.DXCALLSIGN, static.DXGRID, 'PING', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) - + static.INFO.append("PING;RECEIVING") # check if callsign ssid override @@ -1505,7 +1505,7 @@ class DATA(): # PING packet not for me. print("ping not for me...") return - + structlog.get_logger("structlog").info("[TNC] PING REQ [" + str(mycallsign, 'utf-8') + "] <<< [" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR ) ping_frame = bytearray(14) @@ -1524,12 +1524,12 @@ class DATA(): # wait while transmitting while static.TRANSMITTING: time.sleep(0.01) - + def received_ping_ack(self, data_in:bytes): """ Called if a PING ack has been received Args: - data_in:bytes: + data_in:bytes: Returns: @@ -1537,18 +1537,18 @@ class DATA(): static.DXCALLSIGN_CRC = bytes(data_in[4:7]) static.DXGRID = bytes(data_in[7:13]).rstrip(b'\x00') - + jsondata = {"type" : "ping", "status" : "ack", "uuid" : str(uuid.uuid4()), "timestamp": int(time.time()), "mycallsign" : str(self.mycallsign, 'utf-8'), "dxcallsign": str(static.DXCALLSIGN, 'utf-8'), "dxgrid": str(static.DXGRID, 'utf-8'), "snr": str(static.SNR)} json_data_out = json.dumps(jsondata) - sock.SOCKET_QUEUE.put(json_data_out) - + sock.SOCKET_QUEUE.put(json_data_out) + helpers.add_to_heard_stations(static.DXCALLSIGN, static.DXGRID, 'PING-ACK', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) - + static.INFO.append("PING;RECEIVEDACK") structlog.get_logger("structlog").info("[TNC] PING ACK [" + str(self.mycallsign, 'utf-8') + "] >|< [" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR ) static.TNC_STATE = 'IDLE' - + def stop_transmission(self): """ @@ -1566,7 +1566,7 @@ class DATA(): modem.MODEM_TRANSMIT_QUEUE.put([14,2,250,txbuffer]) while static.TRANSMITTING: time.sleep(0.01) - + static.TNC_STATE = 'IDLE' static.ARQ_STATE = False static.INFO.append("TRANSMISSION;STOPPED") @@ -1581,28 +1581,28 @@ class DATA(): static.ARQ_STATE = False static.INFO.append("TRANSMISSION;STOPPED") self.arq_cleanup() - + # ----------- BROADCASTS - + def run_beacon(self): """ Controlling funktion for running a beacon Args: - interval:int: + interval:int: Returns: """ try: - + while 1: time.sleep(0.5) while static.BEACON_STATE: if static.BEACON_STATE and not static.ARQ_SESSION and not self.arq_file_transfer and not static.BEACON_PAUSE: static.INFO.append("BEACON;SENDING") - structlog.get_logger("structlog").info("[TNC] Sending beacon!", interval=self.beacon_interval) - + structlog.get_logger("structlog").info("[TNC] Sending beacon!", interval=self.beacon_interval) + beacon_frame = bytearray(14) beacon_frame[:1] = bytes([250]) beacon_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign) @@ -1615,12 +1615,12 @@ class DATA(): modem.MODEM_TRANSMIT_QUEUE.put(['FSK_LDPC_0',1,0,txbuffer]) else: modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) - - + + # wait while transmitting while static.TRANSMITTING: time.sleep(0.01) - + interval_timer = time.time() + self.beacon_interval while time.time() < interval_timer and static.BEACON_STATE and not static.BEACON_PAUSE: time.sleep(0.01) @@ -1632,7 +1632,7 @@ class DATA(): """ Called if we received a beacon Args: - data_in:bytes: + data_in:bytes: Returns: @@ -1643,7 +1643,7 @@ class DATA(): jsondata = {"type" : "beacon", "status" : "received", "uuid" : str(uuid.uuid4()), "timestamp": int(time.time()), "mycallsign" : str(self.mycallsign, 'utf-8'), "dxcallsign": str(dxcallsign, 'utf-8'), "dxgrid": str(dxgrid, 'utf-8'), "snr": str(static.SNR)} json_data_out = json.dumps(jsondata) - sock.SOCKET_QUEUE.put(json_data_out) + sock.SOCKET_QUEUE.put(json_data_out) static.INFO.append("BEACON;RECEIVING") structlog.get_logger("structlog").info("[TNC] BEACON RCVD [" + str(dxcallsign, 'utf-8') + "]["+ str(dxgrid, 'utf-8') +"] ", snr=static.SNR) @@ -1656,12 +1656,12 @@ class DATA(): """ structlog.get_logger("structlog").info("CQ CQ CQ") static.INFO.append("CQ;SENDING") - + cq_frame = bytearray(14) cq_frame[:1] = bytes([200]) cq_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign) cq_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("utf-8")) - + txbuffer = [cq_frame] print(txbuffer) static.TRANSMITTING = True @@ -1680,7 +1680,7 @@ class DATA(): """ Called if we received a CQ Args: - data_in:bytes: + data_in:bytes: Returns: @@ -1759,29 +1759,29 @@ class DATA(): """ Calculate Transferrate for receiving data Args: - rx_start_of_transmission:float: - receivedbytes:int: + rx_start_of_transmission:float: + receivedbytes:int: Returns: """ - - try: + + try: if static.TOTAL_BYTES == 0: static.TOTAL_BYTES = 1 static.ARQ_TRANSMISSION_PERCENT = int((receivedbytes*static.ARQ_COMPRESSION_FACTOR / (static.TOTAL_BYTES)) * 100) if static.ARQ_TRANSMISSION_PERCENT > 100: static.ARQ_TRANSMISSION_PERCENT = 100 - + transmissiontime = time.time() - self.rx_start_of_transmission - + if receivedbytes > 0: static.ARQ_BITS_PER_SECOND = int((receivedbytes*8) / transmissiontime) static.ARQ_BYTES_PER_MINUTE = int((receivedbytes) / (transmissiontime/60)) - + else: static.ARQ_BITS_PER_SECOND = 0 - static.ARQ_BYTES_PER_MINUTE = 0 + static.ARQ_BYTES_PER_MINUTE = 0 except: static.ARQ_TRANSMISSION_PERCENT = 0.0 static.ARQ_BITS_PER_SECOND = 0 @@ -1804,36 +1804,36 @@ class DATA(): static.ARQ_BITS_PER_SECOND = 0 static.ARQ_TRANSMISSION_PERCENT = 0 static.TOTAL_BYTES = 0 - + def calculate_transfer_rate_tx(self, tx_start_of_transmission:float, sentbytes:int, tx_buffer_length:int) -> list: """ Calcualte Transferrate for transmission Args: - tx_start_of_transmission:float: - sentbytes:int: - tx_buffer_length:int: + tx_start_of_transmission:float: + sentbytes:int: + tx_buffer_length:int: Returns: """ - + try: static.ARQ_TRANSMISSION_PERCENT = int((sentbytes / tx_buffer_length) * 100) - + if static.ARQ_TRANSMISSION_PERCENT > 100: static.ARQ_TRANSMISSION_PERCENT = 100 - + transmissiontime = time.time() - tx_start_of_transmission if sentbytes > 0: - + static.ARQ_BITS_PER_SECOND = int((sentbytes*8) / transmissiontime) # Bits per Second static.ARQ_BYTES_PER_MINUTE = int((sentbytes) / (transmissiontime/60)) #Bytes per Minute else: static.ARQ_BITS_PER_SECOND = 0 - static.ARQ_BYTES_PER_MINUTE = 0 - + static.ARQ_BYTES_PER_MINUTE = 0 + except: static.ARQ_TRANSMISSION_PERCENT = 0.0 static.ARQ_BITS_PER_SECOND = 0 @@ -1852,16 +1852,16 @@ class DATA(): structlog.get_logger("structlog").debug("cleanup") - + self.received_mycall_crc = b'' - + self.rx_frame_bof_received = False self.rx_frame_eof_received = False self.burst_ack = False self.rpt_request_received = False self.data_frame_ack_received = False static.RX_BURST_BUFFER = [] - static.RX_FRAME_BUFFER = b'' + static.RX_FRAME_BUFFER = b'' self.burst_ack_snr= 255 # reset modem receiving state to reduce cpu load @@ -1869,7 +1869,7 @@ class DATA(): modem.RECEIVE_DATAC3 = False #modem.RECEIVE_FSK_LDPC_0 = False modem.RECEIVE_FSK_LDPC_1 = False - + # reset buffer overflow counter static.BUFFER_OVERFLOW_COUNTER = [0,0,0,0,0] @@ -1879,30 +1879,30 @@ class DATA(): self.frame_received_counter = 0 self.speed_level = len(self.mode_list) - 1 static.ARQ_SPEED_LEVEL = self.speed_level - + # low bandwith mode indicator self.received_low_bandwith_mode = False - + # reset retry counter for rx channel / burst self.n_retries_per_burst = 0 - + if not static.ARQ_SESSION: static.TNC_STATE = 'IDLE' - + static.ARQ_STATE = False self.arq_file_transfer = False - + static.BEACON_PAUSE = False - - - - - + + + + + def arq_reset_ack(self,state:bool): """ Funktion for resetting acknowledge states Args: - state:bool: + state:bool: Returns: @@ -1918,13 +1918,13 @@ class DATA(): Function for setting the data modes we are listening to for saving cpu power Args: - mode: + mode: Returns: """ # set modes we want listening to - + mode_name = codec2.freedv_get_mode_name_by_value(mode) if mode_name == 'datac1': modem.RECEIVE_DATAC1 = True @@ -1934,18 +1934,18 @@ class DATA(): structlog.get_logger("structlog").debug("changing listening data mode", mode="datac3") elif mode_name == 'fsk_ldpc_1': modem.RECEIVE_FSK_LDPC_1 = True - structlog.get_logger("structlog").debug("changing listening data mode", mode="fsk_ldpc_1") + structlog.get_logger("structlog").debug("changing listening data mode", mode="fsk_ldpc_1") elif mode_name == 'allmodes': modem.RECEIVE_DATAC1 = True modem.RECEIVE_DATAC3 = True modem.RECEIVE_FSK_LDPC_1 = True structlog.get_logger("structlog").debug("changing listening data mode", mode="datac1/datac3/fsk_ldpc") - + # ------------------------- WATCHDOG FUNCTIONS FOR TIMER def watchdog(self): """Author: DJ2LS - + watchdog master function. Frome here we call the watchdogs Args: @@ -1965,8 +1965,8 @@ class DATA(): watchdog which checks if we are running into a connection timeout DATA BURST """ - - # IRS SIDE + + # IRS SIDE if static.ARQ_STATE and static.ARQ_SESSION_STATE == 'connected' and static.TNC_STATE == 'BUSY' and self.is_IRS: if self.data_channel_last_received + self.time_list[self.speed_level] > time.time(): #print((self.data_channel_last_received + self.time_list[self.speed_level])-time.time()) @@ -1984,18 +1984,18 @@ class DATA(): if self.speed_level <= 0: self.speed_level = 0 static.ARQ_SPEED_LEVEL = self.speed_level - + # updated modes we are listening to self.set_listening_modes(self.mode_list[self.speed_level]) - + # BUILDING NACK FRAME FOR DATA FRAME burst_nack_frame = bytearray(14) burst_nack_frame[:1] = bytes([64]) burst_nack_frame[1:4] = static.DXCALLSIGN_CRC - burst_nack_frame[4:7] = static.MYCALLSIGN_CRC + burst_nack_frame[4:7] = static.MYCALLSIGN_CRC burst_nack_frame[7:8] = bytes([0]) burst_nack_frame[8:9] = bytes([int(self.speed_level)]) - + # TRANSMIT NACK FRAME FOR BURST txbuffer = [burst_nack_frame] static.TRANSMITTING = True @@ -2006,18 +2006,18 @@ class DATA(): # self.data_channel_last_received = time.time() self.data_channel_last_received = time.time() self.n_retries_per_burst += 1 - + if self.n_retries_per_burst >= self.rx_n_max_retries_per_burst: self.stop_transmission() self.arq_cleanup() - + def data_channel_keep_alive_watchdog(self): """ watchdog which checks if we are running into a connection timeout - DATA CHANNEL + DATA CHANNEL """ - + # and not static.ARQ_SEND_KEEP_ALIVE: if static.ARQ_STATE and static.TNC_STATE == 'BUSY': time.sleep(0.01) @@ -2031,7 +2031,7 @@ class DATA(): static.INFO.append("ARQ;RECEIVING;FAILED") if not TESTMODE: self.arq_cleanup() - + def arq_session_keep_alive_watchdog(self): """ watchdog which checks if we are running into a connection timeout @@ -2044,9 +2044,9 @@ class DATA(): structlog.get_logger("structlog").info("SESSION [" + str(self.mycallsign, 'utf-8') + "]<>[" + str(static.DXCALLSIGN, 'utf-8') + "]") static.INFO.append("ARQ;SESSION;TIMEOUT") self.close_session() - - - + + + def heartbeat(self): """ heartbeat thread which auto resumes the heartbeat signal within a arq session @@ -2057,8 +2057,8 @@ class DATA(): time.sleep(1) self.transmit_session_heartbeat() time.sleep(2) - - - + + + def send_test_frame(self): - modem.MODEM_TRANSMIT_QUEUE.put([12,1,0,[bytearray(126)]]) + modem.MODEM_TRANSMIT_QUEUE.put([12,1,0,[bytearray(126)]]) diff --git a/tnc/helpers.py b/tnc/helpers.py index bc104c76..f5782607 100644 --- a/tnc/helpers.py +++ b/tnc/helpers.py @@ -16,7 +16,7 @@ def wait(seconds): """ Args: - seconds: + seconds: Returns: @@ -26,18 +26,18 @@ def wait(seconds): while time.time() < timeout: time.sleep(0.01) return True - - + + def get_crc_8(data): """Author: DJ2LS - + Get the CRC8 of a byte string - + param: data = bytes() Args: - data: + data: Returns: @@ -50,13 +50,13 @@ def get_crc_8(data): def get_crc_16(data): """Author: DJ2LS - + Get the CRC16 of a byte string - + param: data = bytes() Args: - data: + data: Returns: @@ -68,35 +68,35 @@ def get_crc_16(data): def get_crc_24(data): """Author: DJ2LS - + Get the CRC24-OPENPGP of a byte string https://github.com/GardenTools/CrcEngine#examples - + param: data = bytes() Args: - data: + data: Returns: """ crc_algorithm = crcengine.create(0x864cfb, 24, 0xb704ce, ref_in=False, ref_out=False, xor_out=0, - name='crc-24-openpgp') + name='crc-24-openpgp') crc_data = crc_algorithm(data) crc_data = crc_data.to_bytes(3, byteorder='big') return crc_data - - + + def get_crc_32(data): """Author: DJ2LS - + Get the CRC32 of a byte string - + param: data = bytes() Args: - data: + data: Returns: @@ -111,12 +111,12 @@ def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency): """ Args: - dxcallsign: - dxgrid: - datatype: - snr: - offset: - frequency: + dxcallsign: + dxgrid: + datatype: + snr: + offset: + frequency: Returns: @@ -149,7 +149,7 @@ def callsign_to_bytes(callsign): """ Args: - callsign: + callsign: Returns: @@ -171,21 +171,21 @@ def callsign_to_bytes(callsign): #-13 Weather stations #-14 Truckers or generally full time drivers #-15 generic additional station, digi, mobile, wx, etc - + # try converting to bytestring if possible type string - try: - callsign = bytes(callsign, 'utf-8') + try: + callsign = bytes(callsign, 'utf-8') except: pass - # we need to do this step to reduce the needed paypload by the callsign ( stripping "-" out of the callsign ) + # we need to do this step to reduce the needed paypload by the callsign ( stripping "-" out of the callsign ) callsign = callsign.split(b'-') try: ssid = int(callsign[1]) except: ssid = 0 - - #callsign = callsign[0] + + #callsign = callsign[0] #bytestring = bytearray(8) #bytestring[:len(callsign)] = callsign #bytestring[7:8] = bytes([ssid]) @@ -196,14 +196,14 @@ def callsign_to_bytes(callsign): return encode_call(callsign + ssid) - #return bytes(bytestring) - + #return bytes(bytestring) + def bytes_to_callsign(bytestring): """ Convert our callsign, received by a frame to a callsign in a human readable format Args: - bytestring: + bytestring: Returns: bytes @@ -227,8 +227,8 @@ def bytes_to_callsign(bytestring): #-13 Weather stations #-14 Truckers or generally full time drivers #-15 generic additional station, digi, mobile, wx, etc - - # we need to do this step to reduce the needed paypload by the callsign ( stripping "-" out of the callsign ) + + # we need to do this step to reduce the needed paypload by the callsign ( stripping "-" out of the callsign ) ''' callsign = bytes(bytestring[:7]) callsign = callsign.rstrip(b'\x00') @@ -238,8 +238,8 @@ def bytes_to_callsign(bytestring): callsign = callsign.decode('utf-8') callsign = callsign + str(ssid) callsign = callsign.encode('utf-8') - - return bytes(callsign) + + return bytes(callsign) ''' decoded = decode_call(bytestring) callsign = decoded[:-1] @@ -254,23 +254,23 @@ def check_callsign(callsign:bytes, crc_to_check:bytes): Args: callsign: Callsign which we want to check - crc_to_check: The CRC which we want the callsign to check against + crc_to_check: The CRC which we want the callsign to check against Returns: [True, Callsign + SSID] False """ - + print(callsign) try: callsign = callsign.split(b'-') callsign = callsign[0] # we want the callsign without SSID - + except: callsign = callsign - + for ssid in static.SSID_LIST: - call_with_ssid = bytearray(callsign) + call_with_ssid = bytearray(callsign) call_with_ssid.extend('-'.encode('utf-8')) call_with_ssid.extend(str(ssid).encode('utf-8')) @@ -279,7 +279,7 @@ def check_callsign(callsign:bytes, crc_to_check:bytes): if callsign_crc == crc_to_check: print(call_with_ssid) return [True, bytes(call_with_ssid)] - + return [False, ""] @@ -327,7 +327,7 @@ def decode_grid(b_code_word:bytes): """ code_word = int.from_bytes(b_code_word, byteorder='big', signed=False) - grid = chr((code_word & 0b11111) + 65) + grid = chr((code_word & 0b11111) + 65) code_word = code_word >> 5 grid = chr((code_word & 0b11111) + 65) + grid @@ -359,7 +359,7 @@ def encode_call(call): out_code_word = int(0) call = call.upper() # upper case to be save - + for x in call: int_val = ord(x)-48 # -48 reduce bits, begin with first number utf8 table out_code_word = out_code_word << 6 # shift left 6 bit, making space for a new char diff --git a/tnc/log_handler.py b/tnc/log_handler.py index dfe22d85..a14a4159 100644 --- a/tnc/log_handler.py +++ b/tnc/log_handler.py @@ -3,7 +3,7 @@ def setup_logging(filename): """ Args: - filename: + filename: Returns: diff --git a/tnc/main.py b/tnc/main.py index 444a786b..e45a54bf 100755 --- a/tnc/main.py +++ b/tnc/main.py @@ -33,7 +33,7 @@ def signal_handler(sig, frame): a signal handler, which closes the network/socket when closing the application Args: sig: signal - frame: + frame: Returns: system exit @@ -41,7 +41,7 @@ def signal_handler(sig, frame): print('Closing TNC...') sock.CLOSE_SIGNAL = True sys.exit(0) - + signal.signal(signal.SIGINT, signal_handler) if __name__ == '__main__': @@ -51,31 +51,31 @@ if __name__ == '__main__': PARSER = argparse.ArgumentParser(description='FreeDATA TNC') PARSER.add_argument('--mycall', dest="mycall", default="AA0AA", help="My callsign", type=str) PARSER.add_argument('--ssid', dest="ssid_list", nargs='*', default=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], help="SSID list we are responding to", type=str) - PARSER.add_argument('--mygrid', dest="mygrid", default="JN12AA", help="My gridsquare", type=str) + PARSER.add_argument('--mygrid', dest="mygrid", default="JN12AA", help="My gridsquare", type=str) PARSER.add_argument('--rx', dest="audio_input_device", default=0, help="listening sound card", type=int) PARSER.add_argument('--tx', dest="audio_output_device", default=0, help="transmitting sound card", type=int) PARSER.add_argument('--port', dest="socket_port", default=3000, help="Socket port in the range of 1024-65536", type=int) PARSER.add_argument('--deviceport', dest="hamlib_device_port", default="/dev/ttyUSB0", help="Hamlib device port", type=str) - PARSER.add_argument('--devicename', dest="hamlib_device_name", default="2028", help="Hamlib device name", type=str) - PARSER.add_argument('--serialspeed', dest="hamlib_serialspeed", choices=[1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200], default=9600, help="Serialspeed", type=int) - PARSER.add_argument('--pttprotocol', dest="hamlib_ptt_type", choices=['USB', 'RIG', 'RTS', 'DTR', 'CM108', 'MICDATA', 'PARALLEL', 'DTR-H', 'DTR-L', 'NONE'], default='USB', help="PTT Type", type=str) - PARSER.add_argument('--pttport', dest="hamlib_ptt_port", default="/dev/ttyUSB0", help="PTT Port", type=str) - PARSER.add_argument('--data_bits', dest="hamlib_data_bits", choices=[7, 8], default=8, help="Hamlib data bits", type=int) - PARSER.add_argument('--stop_bits', dest="hamlib_stop_bits", choices=[1, 2], default=1, help="Hamlib stop bits", type=int) - PARSER.add_argument('--handshake', dest="hamlib_handshake", default="None", help="Hamlib handshake", type=str) - PARSER.add_argument('--radiocontrol', dest="hamlib_radiocontrol", choices=['disabled', 'direct', 'rigctl', 'rigctld'], default="disabled", help="Set how you want to control your radio") - PARSER.add_argument('--rigctld_port', dest="rigctld_port", default=4532, type=int, help="Set rigctld port") - PARSER.add_argument('--rigctld_ip', dest="rigctld_ip", default="localhost", help="Set rigctld ip") - PARSER.add_argument('--scatter', dest="send_scatter", action="store_true", help="Send scatter information via network") - PARSER.add_argument('--fft', dest="send_fft", action="store_true", help="Send fft information via network") - PARSER.add_argument('--500hz', dest="low_bandwith_mode", action="store_true", help="Enable low bandwith mode ( 500 Hz only )") - PARSER.add_argument('--fsk', dest="enable_fsk", action="store_true", help="Enable FSK mode for ping, beacon and CQ") - PARSER.add_argument('--qrv', dest="enable_respond_to_cq", action="store_true", help="Enable sending a QRV frame if CQ received") - PARSER.add_argument('--tuning_range_fmin', dest="tuning_range_fmin", choices=[-50.0, -100.0, -150.0, -200.0, -250.0], default=-50.0, help="Tuning range fmin", type=float) + PARSER.add_argument('--devicename', dest="hamlib_device_name", default="2028", help="Hamlib device name", type=str) + PARSER.add_argument('--serialspeed', dest="hamlib_serialspeed", choices=[1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200], default=9600, help="Serialspeed", type=int) + PARSER.add_argument('--pttprotocol', dest="hamlib_ptt_type", choices=['USB', 'RIG', 'RTS', 'DTR', 'CM108', 'MICDATA', 'PARALLEL', 'DTR-H', 'DTR-L', 'NONE'], default='USB', help="PTT Type", type=str) + PARSER.add_argument('--pttport', dest="hamlib_ptt_port", default="/dev/ttyUSB0", help="PTT Port", type=str) + PARSER.add_argument('--data_bits', dest="hamlib_data_bits", choices=[7, 8], default=8, help="Hamlib data bits", type=int) + PARSER.add_argument('--stop_bits', dest="hamlib_stop_bits", choices=[1, 2], default=1, help="Hamlib stop bits", type=int) + PARSER.add_argument('--handshake', dest="hamlib_handshake", default="None", help="Hamlib handshake", type=str) + PARSER.add_argument('--radiocontrol', dest="hamlib_radiocontrol", choices=['disabled', 'direct', 'rigctl', 'rigctld'], default="disabled", help="Set how you want to control your radio") + PARSER.add_argument('--rigctld_port', dest="rigctld_port", default=4532, type=int, help="Set rigctld port") + PARSER.add_argument('--rigctld_ip', dest="rigctld_ip", default="localhost", help="Set rigctld ip") + PARSER.add_argument('--scatter', dest="send_scatter", action="store_true", help="Send scatter information via network") + PARSER.add_argument('--fft', dest="send_fft", action="store_true", help="Send fft information via network") + PARSER.add_argument('--500hz', dest="low_bandwith_mode", action="store_true", help="Enable low bandwith mode ( 500 Hz only )") + PARSER.add_argument('--fsk', dest="enable_fsk", action="store_true", help="Enable FSK mode for ping, beacon and CQ") + PARSER.add_argument('--qrv', dest="enable_respond_to_cq", action="store_true", help="Enable sending a QRV frame if CQ received") + PARSER.add_argument('--tuning_range_fmin', dest="tuning_range_fmin", choices=[-50.0, -100.0, -150.0, -200.0, -250.0], default=-50.0, help="Tuning range fmin", type=float) PARSER.add_argument('--tuning_range_fmax', dest="tuning_range_fmax", choices=[50.0, 100.0, 150.0, 200.0, 250.0], default=50.0, help="Tuning range fmax", type=float) - PARSER.add_argument('--tx-audio-level', dest="tx_audio_level", default=50, help="Set the tx audio level at an early stage", type=int) - - + PARSER.add_argument('--tx-audio-level', dest="tx_audio_level", default=50, help="Set the tx audio level at an early stage", type=int) + + ARGS = PARSER.parse_args() # additional step for beeing sure our callsign is correctly @@ -84,10 +84,10 @@ if __name__ == '__main__': mycallsign = bytes(ARGS.mycall.upper(), 'utf-8') mycallsign = helpers.callsign_to_bytes(mycallsign) static.MYCALLSIGN = helpers.bytes_to_callsign(mycallsign) - static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN) - + static.MYCALLSIGN_CRC = helpers.get_crc_24(static.MYCALLSIGN) + static.SSID_LIST = ARGS.ssid_list - + static.MYGRID = bytes(ARGS.mygrid, 'utf-8') static.AUDIO_INPUT_DEVICE = ARGS.audio_input_device static.AUDIO_OUTPUT_DEVICE = ARGS.audio_output_device @@ -105,40 +105,40 @@ if __name__ == '__main__': static.HAMLIB_RGICTLD_PORT = str(ARGS.rigctld_port) static.ENABLE_SCATTER = ARGS.send_scatter static.ENABLE_FFT = ARGS.send_fft - static.ENABLE_FSK = ARGS.enable_fsk - static.LOW_BANDWITH_MODE = ARGS.low_bandwith_mode - static.TUNING_RANGE_FMIN = ARGS.tuning_range_fmin - static.TUNING_RANGE_FMAX = ARGS.tuning_range_fmax - static.TX_AUDIO_LEVEL = ARGS.tx_audio_level - static.RESPOND_TO_CQ = ARGS.enable_respond_to_cq - + static.ENABLE_FSK = ARGS.enable_fsk + static.LOW_BANDWITH_MODE = ARGS.low_bandwith_mode + static.TUNING_RANGE_FMIN = ARGS.tuning_range_fmin + static.TUNING_RANGE_FMAX = ARGS.tuning_range_fmax + static.TX_AUDIO_LEVEL = ARGS.tx_audio_level + static.RESPOND_TO_CQ = ARGS.enable_respond_to_cq + # we need to wait until we got all parameters from argparse first before we can load the other modules - import sock - - # config logging - try: + import sock + + # config logging + try: if sys.platform == 'linux': logging_path = os.getenv("HOME") + '/.config/' + 'FreeDATA/' + 'tnc' - + if sys.platform == 'darwin': - logging_path = os.getenv("HOME") + '/Library/' + 'Application Support/' + 'FreeDATA/' + 'tnc' - + logging_path = os.getenv("HOME") + '/Library/' + 'Application Support/' + 'FreeDATA/' + 'tnc' + if sys.platform == 'win32' or sys.platform == 'win64': - logging_path = os.getenv('APPDATA') + '/' + 'FreeDATA/' + 'tnc' - + logging_path = os.getenv('APPDATA') + '/' + 'FreeDATA/' + 'tnc' + if not os.path.exists(logging_path): os.makedirs(logging_path) log_handler.setup_logging(logging_path) except: - structlog.get_logger("structlog").error("[DMN] logger init error") + structlog.get_logger("structlog").error("[DMN] logger init error") + + - - structlog.get_logger("structlog").info("[TNC] Starting FreeDATA", author="DJ2LS", year="2022", version=static.VERSION) - + # start data handler data_handler.DATA() - + # start modem modem = modem.RF() diff --git a/tnc/modem.py b/tnc/modem.py index 919823be..a6a46864 100644 --- a/tnc/modem.py +++ b/tnc/modem.py @@ -45,10 +45,10 @@ class RF(): """ """ def __init__(self): - + self.sampler_avg = 0 self.buffer_avg = 0 - + self.AUDIO_SAMPLE_RATE_RX = 48000 self.AUDIO_SAMPLE_RATE_TX = 48000 @@ -57,15 +57,15 @@ class RF(): self.AUDIO_FRAMES_PER_BUFFER_TX = 2400*2 #8192 Lets to some tests with very small chunks for TX self.AUDIO_CHUNKS = 48 #8 * (self.AUDIO_SAMPLE_RATE_RX/self.MODEM_SAMPLE_RATE) #48 self.AUDIO_CHANNELS = 1 - + # locking state for mod out so buffer will be filled before we can use it # https://github.com/DJ2LS/FreeDATA/issues/127 # https://github.com/DJ2LS/FreeDATA/issues/99 self.mod_out_locked = True - + # make sure our resampler will work assert (self.AUDIO_SAMPLE_RATE_RX / self.MODEM_SAMPLE_RATE) == codec2.api.FDMDV_OS_48 - + # small hack for initializing codec2 via codec2.py module # TODO: we need to change the entire modem module to integrate codec2 module self.c_lib = codec2.api @@ -76,11 +76,11 @@ class RF(): # init FIFO queue to store modulation out in self.modoutqueue = deque() - + # define fft_data buffer self.fft_data = bytes() - - # open codec2 instance + + # open codec2 instance self.datac0_freedv = cast(codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC0), c_void_p) self.c_lib.freedv_set_tuning_range(self.datac0_freedv, c_float(static.TUNING_RANGE_FMIN), c_float(static.TUNING_RANGE_FMAX)) self.datac0_bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(self.datac0_freedv)/8) @@ -120,70 +120,54 @@ class RF(): #codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0,1) self.fsk_ldpc_buffer_1 = codec2.audio_buffer(self.AUDIO_FRAMES_PER_BUFFER_RX) - # initial nin values - self.datac0_nin = codec2.api.freedv_nin(self.datac0_freedv) - self.datac1_nin = codec2.api.freedv_nin(self.datac1_freedv) + # initial nin values + self.datac0_nin = codec2.api.freedv_nin(self.datac0_freedv) + self.datac1_nin = codec2.api.freedv_nin(self.datac1_freedv) self.datac3_nin = codec2.api.freedv_nin(self.datac3_freedv) - self.fsk_ldpc_nin_0 = codec2.api.freedv_nin(self.fsk_ldpc_freedv_0) - self.fsk_ldpc_nin_1 = codec2.api.freedv_nin(self.fsk_ldpc_freedv_1) + self.fsk_ldpc_nin_0 = codec2.api.freedv_nin(self.fsk_ldpc_freedv_0) + self.fsk_ldpc_nin_1 = codec2.api.freedv_nin(self.fsk_ldpc_freedv_1) # --------------------------------------------CREATE PYAUDIO INSTANCE - - ''' - try: - # we need to "try" this, because sometimes libasound.so isn't in the default place - # try to supress error messages - with audio.noalsaerr(): # https://github.com/DJ2LS/FreeDATA/issues/22 - self.p = audio.pyaudio.PyAudio() - # else do it the default way - except: - self.p = audio.pyaudio.PyAudio() - atexit.register(self.p.terminate) - - # --------------------------------------------OPEN RX AUDIO CHANNEL - # optional auto selection of loopback device if using in testmode - if static.AUDIO_INPUT_DEVICE == -2: - loopback_list = [] - for dev in range(0,self.p.get_device_count()): - if 'Loopback: PCM' in self.p.get_device_info_by_index(dev)["name"]: - loopback_list.append(dev) - if len(loopback_list) >= 2: - static.AUDIO_INPUT_DEVICE = loopback_list[0] #0 = RX - static.AUDIO_OUTPUT_DEVICE = loopback_list[1] #1 = TX - print(f"loopback_list rx: {loopback_list}", file=sys.stderr) - - ''' - try: - ''' - self.audio_stream = self.p.open(format=audio.pyaudio.paInt16, - channels=self.AUDIO_CHANNELS, - rate=self.AUDIO_SAMPLE_RATE_RX, - frames_per_buffer=self.AUDIO_FRAMES_PER_BUFFER_RX, - input=True, - output=True, - input_device_index=static.AUDIO_INPUT_DEVICE, - output_device_index=static.AUDIO_OUTPUT_DEVICE, - stream_callback=self.audio_callback - ) - ''' - - self.stream = sd.RawStream(channels=1, dtype='int16', callback=self.callback, device=(static.AUDIO_INPUT_DEVICE, static.AUDIO_OUTPUT_DEVICE), samplerate = self.AUDIO_SAMPLE_RATE_RX, blocksize=4800) - - - atexit.register(self.stream.stop) - - structlog.get_logger("structlog").info("opened audio devices") - - except Exception as e: - structlog.get_logger("structlog").error("can't open audio device. Exit", e=e) - os._exit(1) - - try: - structlog.get_logger("structlog").debug("[TNC] starting pyaudio callback") - #self.audio_stream.start_stream() - self.stream.start() + if not TESTMODE: + try: + + self.stream = sd.RawStream(channels=1, dtype='int16', callback=self.callback, device=(static.AUDIO_INPUT_DEVICE, static.AUDIO_OUTPUT_DEVICE), samplerate = self.AUDIO_SAMPLE_RATE_RX, blocksize=4800) + atexit.register(self.stream.stop) + structlog.get_logger("structlog").info("opened audio devices") + + except Exception as e: + structlog.get_logger("structlog").error("can't open audio device. Exit", e=e) + os._exit(1) + + try: + structlog.get_logger("structlog").debug("[TNC] starting pyaudio callback") + #self.audio_stream.start_stream() + self.stream.start() + + except Exception as e: + structlog.get_logger("structlog").error("[TNC] starting pyaudio callback failed", e=e) + + else: + + # create a stream object for simulating audio stream + class Object(object): + pass + self.stream = Object() + self.stream.active = True + + # create mkfifo buffer + try: + + os.mkfifo(RXCHANNEL) + os.mkfifo(TXCHANNEL) + except: + pass + + mkfifo_write_callback_thread = threading.Thread(target=self.mkfifo_write_callback, name="MKFIFO WRITE CALLBACK THREAD",daemon=True) + mkfifo_write_callback_thread.start() + + mkfifo_read_callback_thread = threading.Thread(target=self.mkfifo_read_callback, name="MKFIFO READ CALLBACK THREAD",daemon=True) + mkfifo_read_callback_thread.start() - except Exception as e: - structlog.get_logger("structlog").error("[TNC] starting pyaudio callback failed", e=e) # --------------------------------------------INIT AND OPEN HAMLIB # check how we want to control the radio @@ -196,9 +180,9 @@ class RF(): elif static.HAMLIB_RADIOCONTROL == 'disabled': import rigdummy as rig else: - import rigdummy as rig + import rigdummy as rig + - self.hamlib = rig.radio() self.hamlib.open_rig(devicename=static.HAMLIB_DEVICE_NAME, deviceport=static.HAMLIB_DEVICE_PORT, hamlib_ptt_type=static.HAMLIB_PTT_TYPE, serialspeed=static.HAMLIB_SERIAL_SPEED, pttport=static.HAMLIB_PTT_PORT, data_bits=static.HAMLIB_DATA_BITS, stop_bits=static.HAMLIB_STOP_BITS, handshake=static.HAMLIB_HANDSHAKE, rigctld_ip = static.HAMLIB_RGICTLD_IP, rigctld_port = static.HAMLIB_RGICTLD_PORT) @@ -206,52 +190,101 @@ class RF(): if static.ENABLE_FFT: fft_thread = threading.Thread(target=self.calculate_fft, name="FFT_THREAD" ,daemon=True) fft_thread.start() - + audio_thread_datac0 = threading.Thread(target=self.audio_datac0, name="AUDIO_THREAD DATAC0",daemon=True) audio_thread_datac0.start() audio_thread_datac1 = threading.Thread(target=self.audio_datac1, name="AUDIO_THREAD DATAC1",daemon=True) audio_thread_datac1.start() - + audio_thread_datac3 = threading.Thread(target=self.audio_datac3, name="AUDIO_THREAD DATAC3",daemon=True) audio_thread_datac3.start() - + if static.ENABLE_FSK: audio_thread_fsk_ldpc0 = threading.Thread(target=self.audio_fsk_ldpc_0, name="AUDIO_THREAD FSK LDPC0",daemon=True) audio_thread_fsk_ldpc0.start() audio_thread_fsk_ldpc1 = threading.Thread(target=self.audio_fsk_ldpc_1, name="AUDIO_THREAD FSK LDPC1",daemon=True) audio_thread_fsk_ldpc1.start() - + hamlib_thread = threading.Thread(target=self.update_rig_data, name="HAMLIB_THREAD",daemon=True) hamlib_thread.start() - + worker_received = threading.Thread(target=self.worker_received, name="WORKER_THREAD",daemon=True) worker_received.start() - + worker_transmit = threading.Thread(target=self.worker_transmit, name="WORKER_THREAD",daemon=True) worker_transmit.start() - + + # -------------------------------------------------------------------------------------------------------- + def mkfifo_read_callback(self): + while 1: + + time.sleep(0.01) + # -----read + data_in48k = bytes() + with open(RXCHANNEL, 'rb') as fifo: + for line in fifo: + + data_in48k += line + + while len(data_in48k) >= 48: + x = np.frombuffer(data_in48k[:48], dtype=np.int16) + x = self.resampler.resample48_to_8(x) + data_in48k = data_in48k[48:] + + length_x = len(x) + if not self.datac0_buffer.nbuffer+length_x > self.datac0_buffer.size: + self.datac0_buffer.push(x) + + if not self.datac1_buffer.nbuffer+length_x > self.datac1_buffer.size: + if RECEIVE_DATAC1: + self.datac1_buffer.push(x) + + if not self.datac3_buffer.nbuffer+length_x > self.datac3_buffer.size: + if RECEIVE_DATAC3: + self.datac3_buffer.push(x) + + + + def mkfifo_write_callback(self): + while 1: + time.sleep(0.01) + + # -----write + if len(self.modoutqueue) <= 0 or self.mod_out_locked: + #data_out48k = np.zeros(self.AUDIO_FRAMES_PER_BUFFER_RX, dtype=np.int16) + pass + + else: + data_out48k = self.modoutqueue.popleft() + #print(len(data_out48k)) + + fifo_write = open(TXCHANNEL, 'wb') + fifo_write.write(data_out48k) + fifo_write.flush() + + # -------------------------------------------------------------------------------------------------------- #def audio_callback(self, data_in48k, frame_count, time_info, status): def callback(self, data_in48k, outdata, frames, time, status): """ Args: - data_in48k: - frame_count: - time_info: - status: + data_in48k: + frame_count: + time_info: + status: Returns: - """ - + """ + x = np.frombuffer(data_in48k, dtype=np.int16) - x = self.resampler.resample48_to_8(x) + x = self.resampler.resample48_to_8(x) length_x = len(x) - + # avoid decoding when transmitting to reduce CPU if not static.TRANSMITTING: # avoid buffer overflow by filling only if buffer not full @@ -259,14 +292,14 @@ class RF(): self.datac0_buffer.push(x) else: static.BUFFER_OVERFLOW_COUNTER[0] += 1 - + # avoid buffer overflow by filling only if buffer not full and selected datachannel mode if not self.datac1_buffer.nbuffer+length_x > self.datac1_buffer.size: if RECEIVE_DATAC1: self.datac1_buffer.push(x) else: static.BUFFER_OVERFLOW_COUNTER[1] += 1 - + # avoid buffer overflow by filling only if buffer not full and selected datachannel mode if not self.datac3_buffer.nbuffer+length_x > self.datac3_buffer.size: if RECEIVE_DATAC3: @@ -276,29 +309,29 @@ class RF(): # avoid buffer overflow by filling only if buffer not full and selected datachannel mode if not self.fsk_ldpc_buffer_0.nbuffer+length_x > self.fsk_ldpc_buffer_0.size: - if static.ENABLE_FSK: + if static.ENABLE_FSK: self.fsk_ldpc_buffer_0.push(x) else: static.BUFFER_OVERFLOW_COUNTER[3] += 1 # avoid buffer overflow by filling only if buffer not full and selected datachannel mode if not self.fsk_ldpc_buffer_1.nbuffer+length_x > self.fsk_ldpc_buffer_1.size: - if RECEIVE_FSK_LDPC_1 and static.ENABLE_FSK: + if RECEIVE_FSK_LDPC_1 and static.ENABLE_FSK: self.fsk_ldpc_buffer_1.push(x) else: static.BUFFER_OVERFLOW_COUNTER[4] += 1 - - if len(self.modoutqueue) <= 0 or self.mod_out_locked: + + if len(self.modoutqueue) <= 0 or self.mod_out_locked: #if not self.modoutqueue or self.mod_out_locked: data_out48k = np.zeros(frames, dtype=np.int16) self.fft_data = x - + else: data_out48k = self.modoutqueue.popleft() self.fft_data = data_out48k - + try: - outdata[:] = data_out48k[:frames] + outdata[:] = data_out48k[:frames] except Exception as e: print(e) @@ -306,14 +339,14 @@ class RF(): # -------------------------------------------------------------------------------------------------------- - def transmit(self, mode, repeats, repeat_delay, frames): + def transmit(self, mode, repeats, repeat_delay, frames): """ Args: - mode: - repeats: - repeat_delay: - frames: + mode: + repeats: + repeat_delay: + frames: Returns: @@ -324,19 +357,19 @@ class RF(): jsondata = {"ptt":"True"} data_out = json.dumps(jsondata) sock.SOCKET_QUEUE.put(data_out) - - - # open codec2 instance + + + # open codec2 instance self.MODE = mode if self.MODE == 'FSK_LDPC_0' or self.MODE == 200: freedv = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV)), c_void_p) elif self.MODE == 'FSK_LDPC_1' or self.MODE == 201: - freedv = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV)), c_void_p) - + freedv = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV)), c_void_p) + else: freedv = cast(codec2.api.freedv_open(self.MODE), c_void_p) - + # get number of bytes per frame for mode bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(freedv)/8) payload_bytes_per_frame = bytes_per_frame -2 @@ -356,12 +389,12 @@ class RF(): data_delay_mseconds = 0 #miliseconds data_delay = int(self.MODEM_SAMPLE_RATE*(data_delay_mseconds/1000)) mod_out_silence = create_string_buffer(data_delay*2) - txbuffer = bytes(mod_out_silence) - + txbuffer = bytes(mod_out_silence) + structlog.get_logger("structlog").debug("TRANSMIT", mode=self.MODE, payload=payload_bytes_per_frame) - + for i in range(0,repeats): - # codec2 fsk preamble may be broken - at least it sounds like that so we are disabling it for testing + # codec2 fsk preamble may be broken - at least it sounds like that so we are disabling it for testing if not self.MODE == 'FSK_LDPC_0' or self.MODE == 200 or self.MODE == 'FSK_LDPC_1' or self.MODE == 201: # write preamble to txbuffer codec2.api.freedv_rawdatapreambletx(freedv, mod_out_preamble) @@ -372,28 +405,28 @@ class RF(): buffer = bytearray(payload_bytes_per_frame) # use this if CRC16 checksum is required ( DATA1-3) buffer[:len(frames[n])] = frames[n] # set buffersize to length of data which will be send - # create crc for data frame - we are using the crc function shipped with codec2 to avoid + # create crc for data frame - we are using the crc function shipped with codec2 to avoid # crc algorithm incompatibilities crc = ctypes.c_ushort(codec2.api.freedv_gen_crc16(bytes(buffer), payload_bytes_per_frame)) # generate CRC16 crc = crc.value.to_bytes(2, byteorder='big') # convert crc to 2 byte hex string - buffer += crc # append crc16 to buffer - + buffer += crc # append crc16 to buffer + data = (ctypes.c_ubyte * bytes_per_frame).from_buffer_copy(buffer) - codec2.api.freedv_rawdatatx(freedv,mod_out,data) # modulate DATA and save it into mod_out pointer + codec2.api.freedv_rawdatatx(freedv,mod_out,data) # modulate DATA and save it into mod_out pointer txbuffer += bytes(mod_out) - - - # codec2 fsk preamble may be broken - at least it sounds like that so we are disabling it for testing + + + # codec2 fsk preamble may be broken - at least it sounds like that so we are disabling it for testing if not self.MODE == 'FSK_LDPC_0' or self.MODE == 200 or self.MODE == 'FSK_LDPC_1' or self.MODE == 201: # write preamble to txbuffer codec2.api.freedv_rawdatapostambletx(freedv, mod_out_postamble) txbuffer += bytes(mod_out_postamble) - # append postamble to txbuffer + # append postamble to txbuffer # add delay to end of frames samples_delay = int(self.MODEM_SAMPLE_RATE*(repeat_delay/1000)) mod_out_silence = create_string_buffer(samples_delay*2) txbuffer += bytes(mod_out_silence) - + # resample up to 48k (resampler works on np.int16) x = np.frombuffer(txbuffer, dtype=np.int16) x = set_audio_volume(x, static.TX_AUDIO_LEVEL) @@ -403,11 +436,11 @@ class RF(): # explicitly lock our usage of mod_out_queue if needed # deaktivated for testing purposes self.mod_out_locked = False - - + + # ------------------------------- - - + + chunk_length = self.AUDIO_FRAMES_PER_BUFFER_TX #4800 @@ -422,35 +455,35 @@ class RF(): #structlog.get_logger("structlog").debug("[TNC] mod out shorter than audio buffer", delta=delta) self.modoutqueue.append(c) - - # Release our mod_out_lock so we can use the queue + + # Release our mod_out_lock so we can use the queue self.mod_out_locked = False while self.modoutqueue: time.sleep(0.01) - + static.PTT_STATE = self.hamlib.set_ptt(False) - + # push ptt state to socket stream jsondata = {"ptt":"False"} data_out = json.dumps(jsondata) sock.SOCKET_QUEUE.put(data_out) - + # after processing we want to set the locking state back to true to be prepared for next transmission self.mod_out_locked = True - + self.c_lib.freedv_close(freedv) self.modem_transmit_queue.task_done() static.TRANSMITTING = False threading.Event().set() - def audio_datac0(self): + def audio_datac0(self): """ """ - nbytes_datac0 = 0 + nbytes_datac0 = 0 while self.stream.active: threading.Event().wait(0.01) - while self.datac0_buffer.nbuffer >= self.datac0_nin: + while self.datac0_buffer.nbuffer >= self.datac0_nin: # demodulate audio nbytes_datac0 = codec2.api.freedv_rawdatarx(self.datac0_freedv, self.datac0_bytes_out, self.datac0_buffer.buffer.ctypes) self.datac0_buffer.pop(self.datac0_nin) @@ -474,66 +507,66 @@ class RF(): self.modem_received_queue.put([self.datac1_bytes_out, self.datac1_freedv ,self.datac1_bytes_per_frame]) #self.get_scatter(self.datac1_freedv) self.calculate_snr(self.datac1_freedv) - - def audio_datac3(self): + + def audio_datac3(self): """ """ nbytes_datac3 = 0 while self.stream.active: threading.Event().wait(0.01) while self.datac3_buffer.nbuffer >= self.datac3_nin: - # demodulate audio + # demodulate audio nbytes_datac3 = codec2.api.freedv_rawdatarx(self.datac3_freedv, self.datac3_bytes_out, self.datac3_buffer.buffer.ctypes) self.datac3_buffer.pop(self.datac3_nin) self.datac3_nin = codec2.api.freedv_nin(self.datac3_freedv) if nbytes_datac3 == self.datac3_bytes_per_frame: self.modem_received_queue.put([self.datac3_bytes_out, self.datac3_freedv ,self.datac3_bytes_per_frame]) #self.get_scatter(self.datac3_freedv) - self.calculate_snr(self.datac3_freedv) - - def audio_fsk_ldpc_0(self): + self.calculate_snr(self.datac3_freedv) + + def audio_fsk_ldpc_0(self): """ """ nbytes_fsk_ldpc_0 = 0 while self.stream.active and static.ENABLE_FSK: threading.Event().wait(0.01) while self.fsk_ldpc_buffer_0.nbuffer >= self.fsk_ldpc_nin_0: - # demodulate audio + # demodulate audio nbytes_fsk_ldpc_0 = codec2.api.freedv_rawdatarx(self.fsk_ldpc_freedv_0, self.fsk_ldpc_bytes_out_0, self.fsk_ldpc_buffer_0.buffer.ctypes) self.fsk_ldpc_buffer_0.pop(self.fsk_ldpc_nin_0) self.fsk_ldpc_nin_0 = codec2.api.freedv_nin(self.fsk_ldpc_freedv_0) if nbytes_fsk_ldpc_0 == self.fsk_ldpc_bytes_per_frame_0: self.modem_received_queue.put([self.fsk_ldpc_bytes_out_0, self.fsk_ldpc_freedv_0 ,self.fsk_ldpc_bytes_per_frame_0]) #self.get_scatter(self.fsk_ldpc_freedv_0) - self.calculate_snr(self.fsk_ldpc_freedv_0) + self.calculate_snr(self.fsk_ldpc_freedv_0) - def audio_fsk_ldpc_1(self): + def audio_fsk_ldpc_1(self): """ """ nbytes_fsk_ldpc_1 = 0 while self.stream.active and static.ENABLE_FSK: threading.Event().wait(0.01) while self.fsk_ldpc_buffer_1.nbuffer >= self.fsk_ldpc_nin_1: - # demodulate audio + # demodulate audio nbytes_fsk_ldpc_1 = codec2.api.freedv_rawdatarx(self.fsk_ldpc_freedv_1, self.fsk_ldpc_bytes_out_1, self.fsk_ldpc_buffer_1.buffer.ctypes) self.fsk_ldpc_buffer_1.pop(self.fsk_ldpc_nin_1) self.fsk_ldpc_nin_1 = codec2.api.freedv_nin(self.fsk_ldpc_freedv_1) if nbytes_fsk_ldpc_1 == self.fsk_ldpc_bytes_per_frame_1: self.modem_received_queue.put([self.fsk_ldpc_bytes_out_1, self.fsk_ldpc_freedv_1 ,self.fsk_ldpc_bytes_per_frame_1]) #self.get_scatter(self.fsk_ldpc_freedv_1) - self.calculate_snr(self.fsk_ldpc_freedv_1) - - - - # worker for FIFO queue for processing received frames + self.calculate_snr(self.fsk_ldpc_freedv_1) + + + + # worker for FIFO queue for processing received frames def worker_transmit(self): """ """ while True: data = self.modem_transmit_queue.get() - - self.transmit(mode=data[0], repeats=data[1], repeat_delay=data[2], frames=data[3]) - #self.modem_transmit_queue.task_done() - - - # worker for FIFO queue for processing received frames + self.transmit(mode=data[0], repeats=data[1], repeat_delay=data[2], frames=data[3]) + #self.modem_transmit_queue.task_done() + + + + # worker for FIFO queue for processing received frames def worker_received(self): """ """ while True: @@ -543,13 +576,13 @@ class RF(): # data[2] = bytes_per_frame data_handler.DATA_QUEUE_RECEIVED.put([data[0], data[1], data[2]]) self.modem_received_queue.task_done() - + def get_frequency_offset(self, freedv): """ Args: - freedv: + freedv: Returns: @@ -560,13 +593,13 @@ class RF(): offset = round(modemStats.foff) * (-1) static.FREQ_OFFSET = offset return offset - - + + def get_scatter(self, freedv): """ Args: - freedv: + freedv: Returns: @@ -596,12 +629,12 @@ class RF(): scatterdata_small = scatterdata[::10] static.SCATTER = scatterdata_small - + def calculate_snr(self, freedv): """ Args: - freedv: + freedv: Returns: @@ -633,19 +666,19 @@ class RF(): static.HAMLIB_MODE = self.hamlib.get_mode() static.HAMLIB_BANDWITH = self.hamlib.get_bandwith() - + def calculate_fft(self): """ """ # channel_busy_delay counter channel_busy_delay = 0 - + while True: #time.sleep(0.01) threading.Event().wait(0.01) - # WE NEED TO OPTIMIZE THIS! - + # WE NEED TO OPTIMIZE THIS! + if len(self.fft_data) >= 128: - + # https://gist.github.com/ZWMiller/53232427efc5088007cab6feee7c6e4c # Fast Fourier Transform, 10*log10(abs) is to scale it to dB # and make sure it's not imaginary @@ -656,7 +689,7 @@ class RF(): # set value 0 to 1 to avoid division by zero fftarray[fftarray == 0] = 1 dfft = 10.*np.log10(abs(fftarray)) - + # get average of dfft avg = np.mean(dfft) @@ -665,15 +698,15 @@ class RF(): # data higher than the average must be a signal. Therefore we are setting it to 100 so it will be highlighted # have to do this when we are not transmittig so our own sending data will not affect this too much if not static.TRANSMITTING: - dfft[dfft>avg+10] = 100 - + dfft[dfft>avg+10] = 100 + # calculate audio max value - # static.AUDIO_RMS = np.amax(self.fft_data) - - + # static.AUDIO_RMS = np.amax(self.fft_data) + + # check for signals higher than average by checking for "100" # if we have a signal, increment our channel_busy delay counter so we have a smoother state toggle - + if np.sum(dfft[dfft>avg+10]) >= 300 and not static.TRANSMITTING: static.CHANNEL_BUSY = True channel_busy_delay += 5 @@ -681,56 +714,56 @@ class RF(): if channel_busy_delay > 50: channel_busy_delay = 50 else: - # decrement channel busy counter if no signal has been detected. + # decrement channel busy counter if no signal has been detected. channel_busy_delay -= 1 if channel_busy_delay < 0: channel_busy_delay = 0 # if our channel busy counter reached 0, we toggle state to False if channel_busy_delay == 0: static.CHANNEL_BUSY = False - + # round data to decrease size dfft = np.around(dfft, 0) dfftlist = dfft.tolist() - + static.FFT = dfftlist[0:320] #320 --> bandwith 3000 except: - + structlog.get_logger("structlog").debug("[TNC] Setting fft=0") # else 0 static.FFT = [0] else: pass - + def set_frames_per_burst(self, n_frames_per_burst): """ Args: - n_frames_per_burst: + n_frames_per_burst: Returns: """ codec2.api.freedv_set_frames_per_burst(self.datac1_freedv,n_frames_per_burst) - codec2.api.freedv_set_frames_per_burst(self.datac3_freedv,n_frames_per_burst) - codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0,n_frames_per_burst) - - - + codec2.api.freedv_set_frames_per_burst(self.datac3_freedv,n_frames_per_burst) + codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0,n_frames_per_burst) + + + def get_bytes_per_frame(mode): """ provide bytes per frame information for accessing from data handler - + Args: - mode: + mode: Returns: """ if mode == 200: - freedv = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV)), c_void_p) + freedv = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV)), c_void_p) elif mode == 201: freedv = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV)), c_void_p) else: @@ -738,10 +771,10 @@ def get_bytes_per_frame(mode): # get number of bytes per frame for mode return int(codec2.api.freedv_get_bits_per_modem_frame(freedv)/8) - - + + def set_audio_volume(datalist, volume): data = np.fromstring(datalist, np.int16) * (volume / 100.) return data.astype(np.int16) - - + + diff --git a/tnc/rig.py b/tnc/rig.py index 05e62548..8e64784b 100644 --- a/tnc/rig.py +++ b/tnc/rig.py @@ -20,17 +20,17 @@ except: sys.path.append(app_path) -# try importing hamlib +# try importing hamlib try: # get python version python_version = str(sys.version_info[0]) + "." + str(sys.version_info[1]) # installation path for Ubuntu 20.04 LTS python modules #sys.path.append('/usr/local/lib/python'+ python_version +'/site-packages') - + # installation path for Ubuntu 20.10 + sys.path.append('/usr/local/lib/') - + # installation path for Suse sys.path.append('/usr/local/lib64/python'+ python_version +'/site-packages') @@ -41,14 +41,14 @@ try: sys.path.append('/usr/local/lib/python3.8/site-packages') sys.path.append('/usr/local/lib/python3.9/site-packages') sys.path.append('/usr/local/lib/python3.10/site-packages') - + sys.path.append('lib/hamlib/linux/python3.8/site-packages') import Hamlib - + # https://stackoverflow.com/a/4703409 - hamlib_version = re.findall(r"[-+]?\d*\.?\d+|\d+", Hamlib.cvar.hamlib_version) + hamlib_version = re.findall(r"[-+]?\d*\.?\d+|\d+", Hamlib.cvar.hamlib_version) hamlib_version = float(hamlib_version[0]) - + min_hamlib_version = 4.1 if hamlib_version > min_hamlib_version: structlog.get_logger("structlog").info("[RIG] Hamlib found", version=hamlib_version) @@ -68,15 +68,15 @@ except Exception as e: else: raise Exception except Exception as e: - structlog.get_logger("structlog").critical("[RIG] HAMLIB NOT INSTALLED", error=e) + structlog.get_logger("structlog").critical("[RIG] HAMLIB NOT INSTALLED", error=e) hamlib_version = 0 sys.exit() - - + + class radio: """ """ def __init__(self): - + self.devicename = '' self.devicenumber = '' self.deviceport = '' @@ -87,26 +87,26 @@ class radio: self.data_bits = '' self.stop_bits = '' self.handshake = '' - - def open_rig(self, devicename, deviceport, hamlib_ptt_type, serialspeed, pttport, data_bits, stop_bits, handshake, rigctld_port, rigctld_ip): + + def open_rig(self, devicename, deviceport, hamlib_ptt_type, serialspeed, pttport, data_bits, stop_bits, handshake, rigctld_port, rigctld_ip): """ Args: - devicename: - deviceport: - hamlib_ptt_type: - serialspeed: - pttport: - data_bits: - stop_bits: - handshake: - rigctld_port: - rigctld_ip: + devicename: + deviceport: + hamlib_ptt_type: + serialspeed: + pttport: + data_bits: + stop_bits: + handshake: + rigctld_port: + rigctld_ip: Returns: """ - + self.devicename = devicename self.deviceport = str(deviceport) self.serialspeed = str(serialspeed) # we need to ensure this is a str, otherwise set_conf functions are crashing @@ -115,8 +115,8 @@ class radio: self.data_bits = str(data_bits) self.stop_bits = str(stop_bits) self.handshake = str(handshake) - - + + # try to init hamlib try: Hamlib.rig_set_debug(Hamlib.RIG_DEBUG_NONE) @@ -127,8 +127,8 @@ class radio: except: structlog.get_logger("structlog").error("[RIG] Hamlib: rig not supported...") self.devicenumber = 0 - - + + self.my_rig = Hamlib.Rig(self.devicenumber) self.my_rig.set_conf("rig_pathname", self.deviceport) self.my_rig.set_conf("retry", "5") @@ -137,10 +137,10 @@ class radio: self.my_rig.set_conf("stop_bits", self.stop_bits) self.my_rig.set_conf("data_bits", self.data_bits) self.my_rig.set_conf("ptt_pathname", self.pttport) - - - + + + if self.hamlib_ptt_type == 'RIG': self.hamlib_ptt_type = Hamlib.RIG_PTT_RIG self.my_rig.set_conf("ptt_type", 'RIG') @@ -176,7 +176,7 @@ class radio: elif self.hamlib_ptt_type == 'RIG_PTT_NONE': self.hamlib_ptt_type = Hamlib.RIG_PTT_NONE - + else: #self.hamlib_ptt_type == 'RIG_PTT_NONE': self.hamlib_ptt_type = Hamlib.RIG_PTT_NONE @@ -185,13 +185,13 @@ class radio: self.my_rig.open() atexit.register(self.my_rig.close) - + try: # lets determine the error message when opening rig error = str(Hamlib.rigerror(my_rig.error_status)).splitlines() error = error[1].split('err=') error = error[1] - + if error == 'Permission denied': structlog.get_logger("structlog").error("[RIG] Hamlib has no permissions", e = error) help_url = 'https://github.com/DJ2LS/FreeDATA/wiki/UBUNTU-Manual-installation#1-permissions' @@ -212,16 +212,16 @@ class radio: except Exception as e: structlog.get_logger("structlog").error("[RIG] Hamlib - can't open rig", error=e, e=sys.exc_info()[0]) return False - + def get_frequency(self): """ """ return int(self.my_rig.get_freq()) - + def get_mode(self): """ """ (hamlib_mode, bandwith) = self.my_rig.get_mode() return Hamlib.rig_strrmode(hamlib_mode) - + def get_bandwith(self): """ """ (hamlib_mode, bandwith) = self.my_rig.get_mode() @@ -230,16 +230,16 @@ class radio: # not needed yet beacuse of some possible problems #def set_mode(self, mode): # return 0 - + def get_ptt(self): """ """ return self.my_rig.get_ptt() - + def set_ptt(self, state): """ Args: - state: + state: Returns: @@ -249,7 +249,7 @@ class radio: else: self.my_rig.set_ptt(Hamlib.RIG_VFO_CURR, 0) return state - + def close_rig(self): """ """ self.my_rig.close() diff --git a/tnc/rigctl.py b/tnc/rigctl.py index 8a9c1f65..229176c9 100644 --- a/tnc/rigctl.py +++ b/tnc/rigctl.py @@ -17,11 +17,11 @@ import os # set global hamlib version hamlib_version = 0 - + class radio: """ """ def __init__(self): - + self.devicename = '' self.devicenumber = '' self.deviceport = '' @@ -32,26 +32,26 @@ class radio: self.data_bits = '' self.stop_bits = '' self.handshake = '' - - def open_rig(self, devicename, deviceport, hamlib_ptt_type, serialspeed, pttport, data_bits, stop_bits, handshake, rigctld_ip, rigctld_port): + + def open_rig(self, devicename, deviceport, hamlib_ptt_type, serialspeed, pttport, data_bits, stop_bits, handshake, rigctld_ip, rigctld_port): """ Args: - devicename: - deviceport: - hamlib_ptt_type: - serialspeed: - pttport: - data_bits: - stop_bits: - handshake: - rigctld_ip: - rigctld_port: + devicename: + deviceport: + hamlib_ptt_type: + serialspeed: + pttport: + data_bits: + stop_bits: + handshake: + rigctld_ip: + rigctld_port: Returns: """ - + self.devicename = devicename self.deviceport = deviceport self.serialspeed = str(serialspeed) # we need to ensure this is a str, otherwise set_conf functions are crashing @@ -60,15 +60,15 @@ class radio: self.data_bits = data_bits self.stop_bits = stop_bits self.handshake = handshake - - + + # check if we are running in a pyinstaller environment try: app_path = sys._MEIPASS except: app_path = os.path.abspath(".") - sys.path.append(app_path) - + sys.path.append(app_path) + # get devicenumber by looking for deviceobject in Hamlib module try: import Hamlib @@ -78,12 +78,12 @@ class radio: self.devicenumber = int(self.devicename) else: self.devicenumber = 6 #dummy - structlog.get_logger("structlog").warning("[RIGCTL] RADIO NOT FOUND USING DUMMY!", error=e) - + structlog.get_logger("structlog").warning("[RIGCTL] RADIO NOT FOUND USING DUMMY!", error=e) + # set deviceport to dummy port, if we selected dummy model if self.devicenumber == 1 or self.devicenumber == 6: self.deviceport = '/dev/ttyUSB0' - + print(self.devicenumber, self.deviceport, self.serialspeed) @@ -93,17 +93,17 @@ class radio: # this is really a hack...somewhen we need a native hamlib integration for windows if sys.platform == 'win32' or sys.platform == 'win64': self.cmd = app_path + 'lib\\hamlib\\'+sys.platform+'\\rigctl -m %d -r %s -s %d ' % (int(self.devicenumber), self.deviceport, int(self.serialspeed)) - + else: self.cmd = 'rigctl -m %d -r %s -s %d ' % (int(self.devicenumber), self.deviceport, int(self.serialspeed)) # eseguo semplicemente rigctl con il solo comando T 1 o T 0 per - # il set e t per il get + # il set e t per il get # set ptt to false if ptt is stuck for some reason self.set_ptt(False) return True - + def get_frequency(self): """ """ cmd = self.cmd + ' f' @@ -116,7 +116,7 @@ class radio: except: return False - + def get_mode(self): """ """ #(hamlib_mode, bandwith) = self.my_rig.get_mode() @@ -126,7 +126,7 @@ class radio: except: return False - + def get_bandwith(self): """ """ #(hamlib_mode, bandwith) = self.my_rig.get_mode() @@ -141,18 +141,18 @@ class radio: """ Args: - mode: + mode: Returns: """ # non usata return 0 - + def get_ptt(self): """ """ cmd = self.cmd + ' t' - sw_proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True) + sw_proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True) time.sleep(0.5) status = sw_proc.communicate()[0] @@ -160,12 +160,12 @@ class radio: return status except: return False - + def set_ptt(self, state): """ Args: - state: + state: Returns: @@ -178,12 +178,12 @@ class radio: cmd = cmd + '0' print('set_ptt', cmd) - sw_proc = subprocess.Popen(cmd, shell=True, text=True) + sw_proc = subprocess.Popen(cmd, shell=True, text=True) try: return state except: return False - + def close_rig(self): """ """ #self.my_rig.close() diff --git a/tnc/rigctld.py b/tnc/rigctld.py index 6d3f5660..ea7452a8 100644 --- a/tnc/rigctld.py +++ b/tnc/rigctld.py @@ -15,7 +15,7 @@ hamlib_version = 0 class radio(): """rotctld (hamlib) communication class""" - # Note: This is a massive hack. + # Note: This is a massive hack. def __init__(self, hostname="localhost", port=4532, poll_rate=5, timeout=5): """ Open a connection to rotctld, and test it for validity """ @@ -31,31 +31,31 @@ class radio(): """ Args: - devicename: - deviceport: - hamlib_ptt_type: - serialspeed: - pttport: - data_bits: - stop_bits: - handshake: - rigctld_ip: - rigctld_port: + devicename: + deviceport: + hamlib_ptt_type: + serialspeed: + pttport: + data_bits: + stop_bits: + handshake: + rigctld_ip: + rigctld_port: Returns: """ self.hostname = rigctld_ip self.port = int(rigctld_port) - - + + if self.connect(): logging.debug(f"Rigctl intialized") return True else: structlog.get_logger("structlog").error("[RIGCTLD] Can't connect to rigctld!", ip=self.hostname, port=self.port) return False - + def connect(self): """Connect to rigctld instance""" if not self.connected: @@ -69,7 +69,7 @@ class radio(): self.close_rig() structlog.get_logger("structlog").warning("[RIGCTLD] Connection to rigctld refused! Reconnect...", ip=self.hostname, port=self.port, e=e) return False - + def close_rig(self): """ """ self.sock.close() @@ -81,7 +81,7 @@ class radio(): and return the return value. Args: - command: + command: Returns: @@ -99,7 +99,7 @@ class radio(): structlog.get_logger("structlog").warning("[RIGCTLD] No command response!", command=command, ip=self.hostname, port=self.port) self.connected = False else: - + # reconnecting.... time.sleep(0.5) self.connect() @@ -111,7 +111,7 @@ class radio(): data = self.send_command(b"m") data = data.split(b'\n') mode = data[0] - return mode.decode("utf-8") + return mode.decode("utf-8") except: 0 def get_bandwith(self): @@ -123,7 +123,7 @@ class radio(): return bandwith.decode("utf-8") except: return 0 - + def get_frequency(self): """ """ try: @@ -131,19 +131,19 @@ class radio(): return frequency.decode("utf-8") except: return 0 - + def get_ptt(self): """ """ try: return self.send_command(b"t") except: return False - + def set_ptt(self, state): """ Args: - state: + state: Returns: @@ -153,6 +153,6 @@ class radio(): self.send_command(b"T 1") else: self.send_command(b"T 0") - return state + return state except: return False diff --git a/tnc/rigdummy.py b/tnc/rigdummy.py index cff2ce47..06bcc38f 100644 --- a/tnc/rigdummy.py +++ b/tnc/rigdummy.py @@ -3,32 +3,32 @@ import structlog hamlib_version = 0 - + class radio: """ """ def __init__(self): pass - - def open_rig(self, **kwargs): + + def open_rig(self, **kwargs): """ Args: - **kwargs: + **kwargs: Returns: """ return True - + def get_frequency(self): """ """ return None - + def get_mode(self): """ """ return None - + def get_bandwith(self): """ """ return None @@ -37,28 +37,28 @@ class radio: """ Args: - mode: + mode: Returns: """ return None - + def get_ptt(self): """ """ return None - - def set_ptt(self, state): + + def set_ptt(self, state): """ Args: - state: + state: Returns: """ return state - + def close_rig(self): """ """ return diff --git a/tnc/sock.py b/tnc/sock.py index b8669e9a..2c5b9af2 100644 --- a/tnc/sock.py +++ b/tnc/sock.py @@ -7,11 +7,11 @@ Created on Fri Dec 25 21:25:14 2020 # GET COMMANDS # "command" : "..." - + # SET COMMANDS # "command" : "..." # "parameter" : " ..." - + # DATA COMMANDS # "command" : "..." # "type" : "..." @@ -51,25 +51,25 @@ class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): the socket handler base class """ pass - - - + + + class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): """ """ - + def send_to_client(self): """ function called by socket handler send data to a network client if available - + """ tempdata = b'' while self.connection_alive and not CLOSE_SIGNAL: # send tnc state as network stream # check server port against daemon port and send corresponding data - + if self.server.server_address[1] == static.PORT and not static.TNCSTARTED: data = send_tnc_state() if data != tempdata: @@ -81,13 +81,13 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): tempdata = data SOCKET_QUEUE.put(data) time.sleep(0.5) - + while not SOCKET_QUEUE.empty(): data = SOCKET_QUEUE.get() sock_data = bytes(data, 'utf-8') sock_data += b'\n' # append line limiter - + # send data to all clients #try: for client in CONNECTED_CLIENTS: @@ -101,7 +101,7 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): # we want to transmit scatter data only once to reduce network traffic static.SCATTER = [] # we want to display INFO messages only once - static.INFO = [] + static.INFO = [] #self.request.sendall(sock_data) time.sleep(0.15) @@ -115,18 +115,18 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): try: chunk = self.request.recv(1024) data += chunk - + if chunk == b'': #print("connection broken. Closing...") self.connection_alive = False - - if data.startswith(b'{') and data.endswith(b'}\n'): + + if data.startswith(b'{') and data.endswith(b'}\n'): # split data by \n if we have multiple commands in socket buffer data = data.split(b'\n') # remove empty data data.remove(b'') - + # iterate thorugh data list for commands in data: if self.server.server_address[1] == static.PORT: @@ -136,19 +136,19 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): # wait some time between processing multiple commands # this is only a first test to avoid doubled transmission - # we might improve this by only processing one command or + # we might improve this by only processing one command or # doing some kind of selection to determin which commands need to be dropped # and which one can be processed during a running transmission time.sleep(3) - - + + # finally delete our rx buffer to be ready for new commands data = bytes() except Exception as e: structlog.get_logger("structlog").info("[SCK] Connection closed", ip=self.client_address[0], port=self.client_address[1], e=e) self.connection_alive = False - - + + def handle(self): """ socket handler @@ -158,19 +158,19 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): structlog.get_logger("structlog").debug("[SCK] Client connected", ip=self.client_address[0], port=self.client_address[1]) self.connection_alive = True - + self.sendThread = threading.Thread(target=self.send_to_client, args=[],daemon=True).start() self.receiveThread = threading.Thread(target=self.receive_from_client, args=[],daemon=True).start() - + # keep connection alive until we close it while self.connection_alive and not CLOSE_SIGNAL: time.sleep(1) - + def finish(self): """ """ - structlog.get_logger("structlog").warning("[SCK] Closing client socket", ip=self.client_address[0], port=self.client_address[1]) + structlog.get_logger("structlog").warning("[SCK] Closing client socket", ip=self.client_address[0], port=self.client_address[1]) try: CONNECTED_CLIENTS.remove(self.request) except: @@ -182,7 +182,7 @@ def process_tnc_commands(data): process tnc commands Args: - data: + data: Returns: @@ -198,9 +198,9 @@ def process_tnc_commands(data): try: static.TX_AUDIO_LEVEL = int(received_json["value"]) command_response("tx_audio_level", True) - - except Exception as e: - command_response("tx_audio_level", False) + + except Exception as e: + command_response("tx_audio_level", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) @@ -209,8 +209,8 @@ def process_tnc_commands(data): try: data_handler.DATA_QUEUE_TRANSMIT.put(['SEND_TEST_FRAME']) command_response("send_test_frame", True) - except Exception as e: - command_response("send_test_frame", False) + except Exception as e: + command_response("send_test_frame", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) # CQ CQ CQ ----------------------------------------------------- @@ -218,9 +218,9 @@ def process_tnc_commands(data): try: data_handler.DATA_QUEUE_TRANSMIT.put(['CQ']) command_response("cqcqcq", True) - - except Exception as e: - command_response("cqcqcq", False) + + except Exception as e: + command_response("cqcqcq", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) # START_BEACON ----------------------------------------------------- if received_json["command"] == "start_beacon": @@ -229,21 +229,21 @@ def process_tnc_commands(data): interval = int(received_json["parameter"]) data_handler.DATA_QUEUE_TRANSMIT.put(['BEACON', interval, True]) command_response("start_beacon", True) - except Exception as e: - command_response("start_beacon", False) + except Exception as e: + command_response("start_beacon", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - + # STOP_BEACON ----------------------------------------------------- if received_json["command"] == "stop_beacon": - try: + try: structlog.get_logger("structlog").warning("[TNC] Stopping beacon!") static.BEACON_STATE = False data_handler.DATA_QUEUE_TRANSMIT.put(['BEACON', None, False]) command_response("stop_beacon", True) - except Exception as e: - command_response("stop_beacon", False) + except Exception as e: + command_response("stop_beacon", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - + # PING ---------------------------------------------------------- if received_json["type"] == 'ping' and received_json["command"] == "ping": # send ping frame and wait for ACK @@ -255,14 +255,14 @@ def process_tnc_commands(data): # then we are forcing a station ssid = 0 dxcallsign = helpers.callsign_to_bytes(dxcallsign) dxcallsign = helpers.bytes_to_callsign(dxcallsign) - + data_handler.DATA_QUEUE_TRANSMIT.put(['PING', dxcallsign]) command_response("ping", True) - except Exception as e: + except Exception as e: command_response("ping", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - - + + # CONNECT ---------------------------------------------------------- if received_json["type"] == 'arq' and received_json["command"] == "connect": static.BEACON_PAUSE = True @@ -275,14 +275,14 @@ def process_tnc_commands(data): # then we are forcing a station ssid = 0 dxcallsign = helpers.callsign_to_bytes(dxcallsign) dxcallsign = helpers.bytes_to_callsign(dxcallsign) - + static.DXCALLSIGN = dxcallsign static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN) - + data_handler.DATA_QUEUE_TRANSMIT.put(['CONNECT', dxcallsign]) command_response("connect", True) - except Exception as e: - command_response("connect", False) + except Exception as e: + command_response("connect", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) # DISCONNECT ---------------------------------------------------------- @@ -291,15 +291,15 @@ def process_tnc_commands(data): try: data_handler.DATA_QUEUE_TRANSMIT.put(['DISCONNECT']) command_response("disconnect", True) - except Exception as e: + except Exception as e: command_response("disconnect", False) - structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - + structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + # TRANSMIT RAW DATA ------------------------------------------- if received_json["type"] == 'arq' and received_json["command"] == "send_raw": static.BEACON_PAUSE = True - try: + try: if not static.ARQ_SESSION: dxcallsign = received_json["parameter"][0]["dxcallsign"] # additional step for beeing sure our callsign is correctly @@ -314,37 +314,37 @@ def process_tnc_commands(data): dxcallsign = static.DXCALLSIGN static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN) - + mode = int(received_json["parameter"][0]["mode"]) n_frames = int(received_json["parameter"][0]["n_frames"]) base64data = received_json["parameter"][0]["data"] - + # check if specific callsign is set with different SSID than the TNC is initialized try: mycallsign = received_json["parameter"][0]["mycallsign"] except: mycallsign = static.MYCALLSIGN - + # check if transmission uuid provided else set no-uuid try: arq_uuid = received_json["uuid"] except: arq_uuid = 'no-uuid' - - if not len(base64data) % 4: + + if not len(base64data) % 4: binarydata = base64.b64decode(base64data) data_handler.DATA_QUEUE_TRANSMIT.put(['ARQ_RAW', binarydata, mode, n_frames, arq_uuid, mycallsign]) else: raise TypeError - except Exception as e: - command_response("send_raw", False) + except Exception as e: + command_response("send_raw", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - - - # STOP TRANSMISSION ---------------------------------------------------------- + + + # STOP TRANSMISSION ---------------------------------------------------------- if received_json["type"] == 'arq' and received_json["command"] == "stop_transmission": try: if static.TNC_STATE == 'BUSY' or static.ARQ_STATE: @@ -353,10 +353,10 @@ def process_tnc_commands(data): static.TNC_STATE = 'IDLE' static.ARQ_STATE = False command_response("stop_transmission", True) - except Exception as e: - command_response("stop_transmission", False) + except Exception as e: + command_response("stop_transmission", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - + if received_json["type"] == 'get' and received_json["command"] == 'rx_buffer': try: @@ -364,7 +364,7 @@ def process_tnc_commands(data): "command": "rx_buffer", "data-array": [], } - + for i in range(0, len(static.RX_BUFFER)): #print(static.RX_BUFFER[i][4]) #rawdata = json.loads(static.RX_BUFFER[i][4]) @@ -375,18 +375,18 @@ def process_tnc_commands(data): #self.request.sendall(bytes(jsondata, encoding)) SOCKET_QUEUE.put(jsondata) command_response("rx_buffer", True) - - except Exception as e: - command_response("rx_buffer", False) + + except Exception as e: + command_response("rx_buffer", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - - + + if received_json["type"] == 'set' and received_json["command"] == 'del_rx_buffer': try: static.RX_BUFFER = [] command_response("del_rx_buffer", True) - except Exception as e: - command_response("del_rx_buffer", False) + except Exception as e: + command_response("del_rx_buffer", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) # exception, if JSON cant be decoded @@ -397,7 +397,7 @@ def send_tnc_state(): """ send the tnc state to network """ - + encoding = 'utf-8' output = { @@ -410,7 +410,7 @@ def send_tnc_state(): "audio_rms": str(static.AUDIO_RMS), "snr": str(static.SNR), "frequency": str(static.HAMLIB_FREQUENCY), - "speed_level": str(static.ARQ_SPEED_LEVEL), + "speed_level": str(static.ARQ_SPEED_LEVEL), "mode": str(static.HAMLIB_MODE), "bandwith": str(static.HAMLIB_BANDWITH), "fft": str(static.FFT), @@ -434,7 +434,7 @@ def send_tnc_state(): # add heard stations to heard stations object for i in range(0, len(static.HEARD_STATIONS)): output["stations"].append({"dxcallsign": str(static.HEARD_STATIONS[i][0], 'utf-8'), "dxgrid": str(static.HEARD_STATIONS[i][1], 'utf-8'),"timestamp": static.HEARD_STATIONS[i][2], "datatype": static.HEARD_STATIONS[i][3], "snr": static.HEARD_STATIONS[i][4], "offset": static.HEARD_STATIONS[i][5], "frequency": static.HEARD_STATIONS[i][6]}) - + jsondata = json.dumps(output) return jsondata @@ -444,7 +444,7 @@ def process_daemon_commands(data): process daemon commands Args: - data: + data: Returns: @@ -465,11 +465,11 @@ def process_daemon_commands(data): command_response("mycallsign", True) structlog.get_logger("structlog").info("[DMN] SET MYCALL", call=static.MYCALLSIGN, crc=static.MYCALLSIGN_CRC) - except Exception as e: + except Exception as e: command_response("mycallsign", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - - + + if received_json["type"] == 'set' and received_json["command"] == 'mygrid': try: mygrid = received_json["parameter"] @@ -480,13 +480,13 @@ def process_daemon_commands(data): static.MYGRID = bytes(mygrid, 'utf-8') structlog.get_logger("structlog").info("[SCK] SET MYGRID", grid=static.MYGRID) command_response("mygrid", True) - except Exception as e: + except Exception as e: command_response("mygrid", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - - + + if received_json["type"] == 'set' and received_json["command"] == 'start_tnc' and not static.TNCSTARTED: - + try: mycall = str(received_json["parameter"][0]["mycall"]) mygrid = str(received_json["parameter"][0]["mygrid"]) @@ -543,9 +543,9 @@ def process_daemon_commands(data): respond_to_cq \ ]) command_response("start_tnc", True) - - except Exception as e: - command_response("start_tnc", False) + + except Exception as e: + command_response("start_tnc", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) if received_json["type"] == 'get' and received_json["command"] == 'test_hamlib': @@ -577,23 +577,23 @@ def process_daemon_commands(data): rigctld_port \ ]) command_response("test_hamlib", True) - except Exception as e: - command_response("test_hamlib", False) + except Exception as e: + command_response("test_hamlib", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - + if received_json["type"] == 'set' and received_json["command"] == 'stop_tnc': try: static.TNCPROCESS.kill() # unregister process from atexit to avoid process zombies atexit.unregister(static.TNCPROCESS.kill) - + structlog.get_logger("structlog").warning("[DMN] Stopping TNC") static.TNCSTARTED = False command_response("stop_tnc", True) - except Exception as e: - command_response("stop_tnc", False) + except Exception as e: + command_response("stop_tnc", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - + def send_daemon_state(): """ send the daemon state to network @@ -613,26 +613,26 @@ def send_daemon_state(): #'ram': str(psutil.virtual_memory().percent), 'version': '0.1' } - + if static.TNCSTARTED: output["daemon_state"].append({"status": "running"}) else: output["daemon_state"].append({"status": "stopped"}) - - + + jsondata = json.dumps(output) return jsondata except Exception as e: structlog.get_logger("structlog").warning("[SCK] error", e=e) return None - + def command_response(command, status): if status: status = "OK" else: status = "Failed" - + jsondata = {"command_response": command, "status" : status} data_out = json.dumps(jsondata) SOCKET_QUEUE.put(data_out) diff --git a/tnc/static.py b/tnc/static.py index ae206f2d..4bf08ad1 100644 --- a/tnc/static.py +++ b/tnc/static.py @@ -5,7 +5,7 @@ Created on Wed Dec 23 11:13:57 2020 @author: DJ2LS Here we are saving application wide variables and stats, which have to be accessed everywhere. -Not nice, suggestions are appreciated :-) +Not nice, suggestions are appreciated :-) """ VERSION = '0.4.0-alpha' @@ -55,7 +55,7 @@ HAMLIB_HANDSHAKE = 'None' HAMLIB_RADIOCONTROL = 'direct' HAMLIB_RIGCTLD_IP = '127.0.0.1' HAMLIB_RIGCTLD_PORT = '4532' - + HAMLIB_FREQUENCY = 0 HAMLIB_MODE = '' HAMLIB_BANDWITH = 0 @@ -122,4 +122,4 @@ INFO = [] # ------- CODEC2 SETTINGS TUNING_RANGE_FMIN = -50.0 -TUNING_RANGE_FMAX = 50.0 +TUNING_RANGE_FMAX = 50.0 From 3a70b87e2155852a22a66830534092e68985fdaa Mon Sep 17 00:00:00 2001 From: Paul Kronenwetter Date: Sun, 8 May 2022 21:27:24 -0400 Subject: [PATCH 2/3] Remove excess newlines. --- tnc/audio.py | 5 -- tnc/codec2.py | 12 ++--- tnc/daemon.py | 19 +------ tnc/data_handler.py | 126 +------------------------------------------- tnc/helpers.py | 21 -------- tnc/log_handler.py | 1 - tnc/main.py | 9 ---- tnc/modem.py | 40 -------------- tnc/rig.py | 12 ----- tnc/rigctl.py | 11 +--- tnc/rigctld.py | 6 +-- tnc/rigdummy.py | 3 +- tnc/sock.py | 35 +----------- tnc/static.py | 3 -- 14 files changed, 14 insertions(+), 289 deletions(-) diff --git a/tnc/audio.py b/tnc/audio.py index eb89d129..203205b3 100644 --- a/tnc/audio.py +++ b/tnc/audio.py @@ -8,9 +8,6 @@ import atexit atexit.register(sd._terminate) def get_audio_devices(): - - - """ return list of input and output audio devices in own process to avoid crashes of portaudio on raspberry pi @@ -47,7 +44,6 @@ def fetch_audio_devices(input_devices, output_devices): Returns: """ - devices = sd.query_devices(device=None, kind=None) index = 0 for device in devices: @@ -70,4 +66,3 @@ def fetch_audio_devices(input_devices, output_devices): if maxOutputChannels > 0: output_devices.append({"id": index, "name": str(name)}) index += 1 - diff --git a/tnc/codec2.py b/tnc/codec2.py index 9b2ecc61..f45951f3 100644 --- a/tnc/codec2.py +++ b/tnc/codec2.py @@ -63,13 +63,11 @@ if sys.platform == 'linux': files.append('libcodec2.so') elif sys.platform == 'darwin': files = glob.glob('**/*libcodec2*.dylib',recursive=True) - elif sys.platform == 'win32' or sys.platform == 'win64': files = glob.glob('**\*libcodec2*.dll',recursive=True) else: files = [] - for file in files: try: api = ctypes.CDLL(file) @@ -78,15 +76,11 @@ for file in files: except Exception as e: structlog.get_logger("structlog").warning("[C2 ] Libcodec2 found but not loaded", path=file, e=e) - # quit module if codec2 cant be loaded if not 'api' in locals(): structlog.get_logger("structlog").critical("[C2 ] Libcodec2 not loaded", path=file) os._exit(1) - - - # ctypes function init #api.freedv_set_tuning_range.restype = c_int @@ -211,6 +205,7 @@ MODEM_STATS_EYE_IND_MAX = 160 MODEM_STATS_NSPEC = 512 MODEM_STATS_MAX_F_HZ = 4000 MODEM_STATS_MAX_F_EST = 4 + # modem stats structure class MODEMSTATS(ctypes.Structure): """ """ @@ -233,8 +228,6 @@ class MODEMSTATS(ctypes.Structure): ("fft_buf", (ctypes.c_float * MODEM_STATS_NSPEC * 2)), ] - - # Return code flags for freedv_get_rx_status() function api.FREEDV_RX_TRIAL_SYNC = 0x1 # demodulator has trial sync api.FREEDV_RX_SYNC = 0x2 # demodulator has sync @@ -275,6 +268,7 @@ class audio_buffer: self.buffer = np.zeros(size, dtype=np.int16) self.nbuffer = 0 self.mutex = Lock() + def push(self,samples): """ Push new data to buffer @@ -291,6 +285,7 @@ class audio_buffer: self.buffer[self.nbuffer:self.nbuffer+len(samples)] = samples self.nbuffer += len(samples) self.mutex.release() + def pop(self,size): """ get data from buffer in size of NIN @@ -328,7 +323,6 @@ class resampler: self.filter_mem8 = np.zeros(self.MEM8, dtype=np.int16) self.filter_mem48 = np.zeros(self.MEM48) - def resample48_to_8(self,in48): """ audio resampler integration from codec2 diff --git a/tnc/daemon.py b/tnc/daemon.py index 5ed6b25b..573a5101 100755 --- a/tnc/daemon.py +++ b/tnc/daemon.py @@ -8,7 +8,6 @@ Author: DJ2LS, January 2022 daemon for providing basic information for the tnc like audio or serial devices """ - import argparse import threading import socketserver @@ -49,7 +48,6 @@ def signal_handler(sig, frame): signal.signal(signal.SIGINT, signal_handler) - class DAEMON(): """ daemon class @@ -70,8 +68,6 @@ class DAEMON(): worker = threading.Thread(target=self.worker, name="WORKER", daemon=True) worker.start() - - def update_audio_devices(self): """ update audio devices and set to static @@ -85,7 +81,6 @@ class DAEMON(): print(e) time.sleep(1) - def update_serial_devices(self): """ update serial devices and set to static @@ -115,7 +110,6 @@ class DAEMON(): """ while 1: try: - data = self.daemon_queue.get() # data[1] mycall @@ -168,7 +162,6 @@ class DAEMON(): # disabled mode if data[13] != 'disabled': - options.append('--devicename') options.append(data[5]) @@ -212,7 +205,6 @@ class DAEMON(): if data[18] == 'True': options.append('--500hz') - options.append('--tuning_range_fmin') options.append(data[19]) @@ -229,8 +221,6 @@ class DAEMON(): if data[23] == 'True': options.append('--qrv') - - # try running tnc from binary, else run from source # this helps running the tnc in a developer environment try: @@ -282,7 +272,6 @@ class DAEMON(): # data[10] rigctld_ip # data[11] rigctld_port if data[0] == 'TEST_HAMLIB': - devicename = data[1] deviceport = data[2] serialspeed = data[3] @@ -295,8 +284,6 @@ class DAEMON(): rigctld_ip = data[10] rigctld_port = data[11] - - # check how we want to control the radio if radiocontrol == 'direct': import rig @@ -334,13 +321,10 @@ class DAEMON(): except Exception as e: print(e) - - if __name__ == '__main__': # we need to run this on windows for multiprocessing support multiprocessing.freeze_support() - # --------------------------------------------GET PARAMETER INPUTS PARSER = argparse.ArgumentParser(description='FreeDATA Daemon') PARSER.add_argument('--port', dest="socket_port",default=3001, help="Socket port in the range of 1024-65536", type=int) @@ -348,7 +332,6 @@ if __name__ == '__main__': static.DAEMONPORT = ARGS.socket_port - try: if sys.platform == 'linux': logging_path = os.getenv("HOME") + '/.config/' + 'FreeDATA/' + 'daemon' @@ -377,8 +360,8 @@ if __name__ == '__main__': except Exception as e: structlog.get_logger("structlog").error("[DMN] Starting TCP/IP socket failed", port=static.DAEMONPORT, e=e) os._exit(1) - daemon = DAEMON() + daemon = DAEMON() structlog.get_logger("structlog").info("[DMN] Starting FreeDATA Daemon", author="DJ2LS", year="2022", version=static.VERSION) while True: diff --git a/tnc/data_handler.py b/tnc/data_handler.py index ec178350..b8200366 100644 --- a/tnc/data_handler.py +++ b/tnc/data_handler.py @@ -28,11 +28,11 @@ TESTMODE = False DATA_QUEUE_TRANSMIT = queue.Queue() DATA_QUEUE_RECEIVED = queue.Queue() + class DATA(): """ """ def __init__(self): - self.mycallsign = static.MYCALLSIGN # initial callsign. Will be overwritten later self.data_queue_transmit = DATA_QUEUE_TRANSMIT @@ -49,8 +49,6 @@ class DATA(): self.received_mycall_crc = b'' # received my callsign crc if we received a crc for another ssid - - self.data_channel_last_received = 0.0 # time of last "live sign" of a frame self.burst_ack_snr = 0 # SNR from received ack frames self.burst_ack = False # if we received an acknowledge frame for a burst @@ -113,11 +111,9 @@ class DATA(): self.beacon_thread = threading.Thread(target=self.run_beacon, name="watchdog",daemon=True) self.beacon_thread.start() - def worker_transmit(self): """ """ while True: - data = self.data_queue_transmit.get() # [0] Command @@ -154,7 +150,6 @@ class DATA(): # [5] mycallsign with ssid self.open_dc_and_transmit(data[1], data[2], data[3], data[4], data[5]) - elif data[0] == 'CONNECT': # [0] DX CALLSIGN self.arq_session_handler(data[1]) @@ -171,7 +166,6 @@ class DATA(): print(f"wrong command {data}") pass - def worker_receive(self): """ """ while True: @@ -181,7 +175,6 @@ class DATA(): # [2] bytes_per_frame self.process_data(bytes_out=data[0],freedv=data[1],bytes_per_frame=data[2]) - def process_data(self, bytes_out, freedv, bytes_per_frame): """ @@ -197,11 +190,8 @@ class DATA(): # bytes_out[1:4] == callsign check for signalling frames, # bytes_out[2:5] == transmission # we could also create an own function, which returns True. - frametype = int.from_bytes(bytes(bytes_out[:1]), "big") - - print(f"self.n_retries_per_burst = {self.n_retries_per_burst}") _valid1, _ = helpers.check_callsign(self.mycallsign, bytes(bytes_out[1:4])) @@ -251,7 +241,6 @@ class DATA(): structlog.get_logger("structlog").debug("BURST NACK RECEIVED....") self.burst_nack_received(bytes_out[:-2]) - # CQ FRAME elif frametype == 200: structlog.get_logger("structlog").debug("CQ RECEIVED....") @@ -262,20 +251,16 @@ class DATA(): structlog.get_logger("structlog").debug("QRV RECEIVED!") self.received_qrv(bytes_out[:-2]) - # PING FRAME elif frametype == 210: structlog.get_logger("structlog").debug("PING RECEIVED....") self.received_ping(bytes_out[:-2]) - # PING ACK elif frametype == 211: structlog.get_logger("structlog").debug("PING ACK RECEIVED....") self.received_ping_ack(bytes_out[:-2]) - - # SESSION OPENER elif frametype == 221: structlog.get_logger("structlog").debug("OPEN SESSION RECEIVED....") @@ -301,14 +286,11 @@ class DATA(): structlog.get_logger("structlog").debug("ARQ arq_received_channel_is_open") self.arq_received_channel_is_open(bytes_out[:-2]) - - # ARQ MANUAL MODE TRANSMISSION elif 230 <= frametype <= 240 : structlog.get_logger("structlog").debug("ARQ manual mode ") self.arq_received_data_channel_opener(bytes_out[:-2]) - # ARQ STOP TRANSMISSION elif frametype == 249: structlog.get_logger("structlog").debug("ARQ received stop transmission") @@ -330,8 +312,6 @@ class DATA(): # for debugging purposes to receive all data structlog.get_logger("structlog").debug("[TNC] Unknown frame received", frame=bytes_out[:-2]) - - def arq_data_received(self, data_in:bytes, bytes_per_frame:int, snr:int, freedv): """ @@ -376,7 +356,6 @@ class DATA(): RX_N_FRAME_OF_BURST = int.from_bytes(bytes(data_in[:1]), "big") - 10 # get number of burst frame RX_N_FRAMES_PER_BURST = int.from_bytes(bytes(data_in[1:2]), "big") # get number of bursts from received frame - ''' The RX burst buffer needs to have a fixed length filled with "None". We need this later for counting the "Nones" check if burst buffer has expected length else create it @@ -387,8 +366,6 @@ class DATA(): # append data to rx burst buffer static.RX_BURST_BUFFER[RX_N_FRAME_OF_BURST] = data_in[8:] # [frame_type][n_frames_per_burst][CRC24][CRC24] - - structlog.get_logger("structlog").debug("[TNC] static.RX_BURST_BUFFER", buffer=static.RX_BURST_BUFFER) helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) @@ -414,14 +391,11 @@ class DATA(): # here we are going to search for our data in the last received bytes # this increases chance we are not loosing the entire frame in case of signalling frame loss else: - - # static.RX_FRAME_BUFFER --> exisitng data # temp_burst_buffer --> new data # search_area --> area where we want to search search_area = 510 - search_position = len(static.RX_FRAME_BUFFER)-search_area # find position of data. returns -1 if nothing found in area else >= 0 # we are beginning from the end, so if data exists twice or more, only the last one should be replaced @@ -436,9 +410,6 @@ class DATA(): static.RX_FRAME_BUFFER += temp_burst_buffer structlog.get_logger("structlog").debug("[TNC] ARQ | RX | appending data to buffer") - - - # lets check if we didnt receive a BOF and EOF yet to avoid sending ack frames if we already received all data if not self.rx_frame_bof_received and not self.rx_frame_eof_received and data_in.find(self.data_frame_eof) < 0: @@ -474,7 +445,6 @@ class DATA(): # calculate statistics self.calculate_transfer_rate_rx(self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)) - # check if we received last frame of burst and we have "Nones" in our rx buffer # this is an indicator for missed frames. # with this way of doing this, we always MUST receive the last frame of a burst otherwise the entire @@ -490,7 +460,6 @@ class DATA(): # we need to work on this codec2.api.freedv_set_frames_per_burst(freedv,len(missing_frames)) - # then create a repeat frame rpt_frame = bytearray(14) rpt_frame[:1] = bytes([62]) @@ -508,12 +477,10 @@ class DATA(): time.sleep(0.01) self.calculate_transfer_rate_rx(self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)) - # we should never reach this point else: structlog.get_logger("structlog").error("we shouldnt reach this point...", frame=RX_N_FRAME_OF_BURST, frames=RX_N_FRAMES_PER_BURST) - # We have a BOF and EOF flag in our data. If we received both we received our frame. # In case of loosing data but we received already a BOF and EOF we need to make sure, we # received the complete last burst by checking it for Nones @@ -531,7 +498,6 @@ class DATA(): static.ARQ_COMPRESSION_FACTOR = compression_factor / 10 self.calculate_transfer_rate_rx(self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)) - if bof_position >= 0 and eof_position > 0 and not None in static.RX_BURST_BUFFER: print(f"bof_position {bof_position} / eof_position {eof_position}") self.rx_frame_bof_received = True @@ -545,7 +511,6 @@ class DATA(): static.TOTAL_BYTES = frame_length # 8:9 = compression factor - data_frame = payload[9:] data_frame_crc_received = helpers.get_crc_32(data_frame) @@ -554,17 +519,14 @@ class DATA(): if data_frame_crc == data_frame_crc_received: structlog.get_logger("structlog").info("[TNC] ARQ | RX | DATA FRAME SUCESSFULLY RECEIVED") - # decompression data_frame_decompressed = zlib.decompress(data_frame) static.ARQ_COMPRESSION_FACTOR = len(data_frame_decompressed) / len(data_frame) data_frame = data_frame_decompressed - uniqueid = str(uuid.uuid4()) timestamp = int(time.time()) - # check if callsign ssid override valid, mycallsign = helpers.check_callsign(self.mycallsign, self.received_mycall_crc) if not valid: @@ -622,7 +584,6 @@ class DATA(): while static.TRANSMITTING: time.sleep(0.01) - # update session timeout self.arq_session_last_received = int(time.time()) # we need to update our timeout timestamp @@ -631,8 +592,6 @@ class DATA(): if not TESTMODE: self.arq_cleanup() - - def arq_transmit(self, data_out:bytes, mode:int, n_frames_per_burst:int): """ @@ -644,7 +603,6 @@ class DATA(): Returns: """ - global TESTMODE self.arq_file_transfer = True @@ -663,7 +621,6 @@ class DATA(): DATA_FRAME_ACK_TIMEOUT_SECONDS = 3.0 # timeout for data frame acknowledges RPT_ACK_TIMEOUT_SECONDS = 3.0 # timeout for rpt frame acknowledges - # save len of data_out to TOTAL_BYTES for our statistics --> kBytes #static.TOTAL_BYTES = round(len(data_out) / 1024, 2) static.TOTAL_BYTES = len(data_out) @@ -674,10 +631,8 @@ class DATA(): json_data_out = json.dumps(jsondata) sock.SOCKET_QUEUE.put(json_data_out) - structlog.get_logger("structlog").info("[TNC] | TX | DATACHANNEL", mode=mode, Bytes=static.TOTAL_BYTES) - # compression data_frame_compressed = zlib.compress(data_out) compression_factor = len(data_out) / len(data_frame_compressed) @@ -694,7 +649,6 @@ class DATA(): frame_payload_crc = helpers.get_crc_32(data_out) structlog.get_logger("structlog").debug("frame payload crc", crc=frame_payload_crc) - # data_out = self.data_frame_bof + frame_payload_crc + data_out + self.data_frame_eof data_out = self.data_frame_bof + frame_payload_crc + frame_total_size + compression_factor + data_out + self.data_frame_eof @@ -704,10 +658,8 @@ class DATA(): # iterate through data out buffer while bufferposition < len(data_out) and not self.data_frame_ack_received and static.ARQ_STATE: - # we have TX_N_MAX_RETRIES_PER_BURST attempts for sending a burst for self.tx_n_retry_of_burst in range(0,TX_N_MAX_RETRIES_PER_BURST): - # AUTO MODE SELECTION # mode 255 == AUTO MODE # force usage of selected mode @@ -715,7 +667,6 @@ class DATA(): data_mode = mode structlog.get_logger("structlog").debug("FIXED MODE", mode=data_mode) - else: # we are doing a modulo check of transmission retries of the actual burst # every 2nd retry which failes, decreases speedlevel by 1. @@ -735,8 +686,6 @@ class DATA(): # self.speed_level = len(self.mode_list)-1 # if speed level is greater than our available modes, set speed level to maximum = lenght of mode list -1 - - if self.speed_level >= len(self.mode_list): self.speed_level = len(self.mode_list) - 1 static.ARQ_SPEED_LEVEL = self.speed_level @@ -744,8 +693,6 @@ class DATA(): structlog.get_logger("structlog").debug("Speed-level:", level=self.speed_level, retry=self.tx_n_retry_of_burst, mode=data_mode) - - # payload information payload_per_frame = modem.get_bytes_per_frame(data_mode) -2 @@ -755,7 +702,6 @@ class DATA(): # append data frames with TX_N_FRAMES_PER_BURST to tempbuffer # this part ineeds to a completly rewrite! # TX_NF_RAMES_PER_BURST = 1 is working - arqheader = bytearray() arqheader[:1] = bytes([10]) #bytes([10 + i]) arqheader[1:2] = bytes([TX_N_FRAMES_PER_BURST]) @@ -766,7 +712,6 @@ class DATA(): # normal behavior if bufferposition_end <= len(data_out): - frame = data_out[bufferposition:bufferposition_end] frame = arqheader + frame @@ -780,7 +725,6 @@ class DATA(): extended_data_out += bytes([0]) * (payload_per_frame-len(extended_data_out)-len(arqheader)) frame = arqheader + extended_data_out - # append frame to tempbuffer for transmission tempbuffer.append(frame) @@ -822,7 +766,6 @@ class DATA(): if self.data_frame_ack_received: break #break retry loop - # we need this part for leaving the repeat loop # static.ARQ_STATE == 'DATA' --> when stopping transmission manually if not static.ARQ_STATE: @@ -845,9 +788,7 @@ class DATA(): #GOING TO NEXT ITERATION - if self.data_frame_ack_received: - static.INFO.append("ARQ;TRANSMITTING;SUCCESS") jsondata = {"arq":"transmission", "status" :"success", "uuid" : self.transmission_uuid, "percent" : static.ARQ_TRANSMISSION_PERCENT, "bytesperminute" : static.ARQ_BYTES_PER_MINUTE} json_data_out = json.dumps(jsondata) @@ -855,8 +796,6 @@ class DATA(): structlog.get_logger("structlog").info("ARQ | TX | DATA TRANSMITTED!", BytesPerMinute=static.ARQ_BYTES_PER_MINUTE, BitsPerSecond=static.ARQ_BITS_PER_SECOND, overflows=static.BUFFER_OVERFLOW_COUNTER) - - else: static.INFO.append("ARQ;TRANSMITTING;FAILED") jsondata = {"arq":"transmission", "status" :"failed", "uuid" : self.transmission_uuid, "percent" : static.ARQ_TRANSMISSION_PERCENT, "bytesperminute" : static.ARQ_BYTES_PER_MINUTE} @@ -876,8 +815,6 @@ class DATA(): import os os._exit(0) - - # signalling frames received def burst_ack_received(self, data_in:bytes): """ @@ -888,7 +825,6 @@ class DATA(): Returns: """ - # increase speed level if we received a burst ack #self.speed_level += 1 #if self.speed_level >= len(self.mode_list)-1: @@ -907,6 +843,7 @@ class DATA(): self.burst_nack_counter = 0 # reset n retries per burst counter self.n_retries_per_burst = 0 + # signalling frames received def burst_nack_received(self, data_in:bytes): """ @@ -917,7 +854,6 @@ class DATA(): Returns: """ - # increase speed level if we received a burst ack #self.speed_level += 1 #if self.speed_level >= len(self.mode_list)-1: @@ -934,7 +870,6 @@ class DATA(): self.burst_nack_counter += 1 print(self.speed_level) - def frame_ack_received(self): """ """ # only process data if we are in ARQ and BUSY state @@ -963,8 +898,6 @@ class DATA(): if not TESTMODE: self.arq_cleanup() - - def burst_rpt_received(self, data_in:bytes): """ @@ -974,7 +907,6 @@ class DATA(): Returns: """ - # only process data if we are in ARQ and BUSY state if static.ARQ_STATE and static.TNC_STATE == 'BUSY': helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) @@ -990,8 +922,6 @@ class DATA(): missing = missing_area[i:i + 2] self.rpt_request_buffer.insert(0, missing) - - # ############################################################################################################ # ARQ SESSION HANDLER # ############################################################################################################ @@ -1023,7 +953,6 @@ class DATA(): static.ARQ_SESSION_STATE = 'failed' return False - def open_session(self, callsign): """ @@ -1044,7 +973,6 @@ class DATA(): connection_frame[4:7] = static.MYCALLSIGN_CRC connection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign) - while not static.ARQ_SESSION: time.sleep(0.01) for attempt in range(1,self.session_connect_max_retries+1): @@ -1077,7 +1005,6 @@ class DATA(): self.close_session() return False - def received_session_opener(self, data_in:bytes): """ @@ -1102,7 +1029,6 @@ class DATA(): self.transmit_session_heartbeat() - def close_session(self): """ """ static.ARQ_SESSION_STATE = 'disconnecting' @@ -1153,7 +1079,6 @@ class DATA(): def transmit_session_heartbeat(self): """ """ - # static.ARQ_SESSION = True # static.TNC_STATE = 'BUSY' # static.ARQ_SESSION_STATE = 'connected' @@ -1165,7 +1090,6 @@ class DATA(): connection_frame[1:4] = static.DXCALLSIGN_CRC connection_frame[4:7] = static.MYCALLSIGN_CRC - txbuffer = [connection_frame] static.TRANSMITTING = True @@ -1174,9 +1098,6 @@ class DATA(): while static.TRANSMITTING: time.sleep(0.01) - - - def received_session_heartbeat(self, data_in:bytes): """ @@ -1186,7 +1107,6 @@ class DATA(): Returns: """ - # Accept session data if the DXCALLSIGN_CRC matches the station in static. _valid_crc, _ = helpers.check_callsign(static.DXCALLSIGN, bytes(data_in[4:7])) if _valid_crc: @@ -1229,7 +1149,6 @@ class DATA(): if static.ARQ_SESSION: time.sleep(0.5) - self.datachannel_timeout = False # we need to compress data for gettin a compression factor. @@ -1269,7 +1188,6 @@ class DATA(): frametype = bytes([225]) structlog.get_logger("structlog").debug("requesting high bandwith mode") - if 230 <= mode <= 240: structlog.get_logger("structlog").debug("requesting manual mode --> not yet implemented ") frametype = bytes([mode]) @@ -1320,10 +1238,6 @@ class DATA(): return False #sys.exit() # close thread and so connection attempts - - - - def arq_received_data_channel_opener(self, data_in:bytes): """ @@ -1353,7 +1267,6 @@ class DATA(): self.time_list = self.time_list_low_bw self.speed_level = len(self.mode_list) - 1 - if 230 <= frametype <= 240: print("manual mode request") @@ -1375,7 +1288,6 @@ class DATA(): static.ARQ_STATE = True static.TNC_STATE = 'BUSY' - self.reset_statistics() self.data_channel_last_received = int(time.time()) @@ -1405,8 +1317,6 @@ class DATA(): # set start of transmission for our statistics self.rx_start_of_transmission = time.time() - - def arq_received_channel_is_open(self, data_in:bytes): """ Called if we received a data channel opener @@ -1448,7 +1358,6 @@ class DATA(): structlog.get_logger("structlog").warning("protocol version missmatch", received=protocol_version, own=static.ARQ_PROTOCOL_VERSION) self.arq_cleanup() - # ---------- PING def transmit_ping(self, dxcallsign:bytes): """ @@ -1492,7 +1401,6 @@ class DATA(): Returns: """ - static.DXCALLSIGN_CRC = bytes(data_in[4:7]) static.DXCALLSIGN = helpers.bytes_to_callsign(bytes(data_in[7:13])) helpers.add_to_heard_stations(static.DXCALLSIGN, static.DXGRID, 'PING', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) @@ -1534,7 +1442,6 @@ class DATA(): Returns: """ - static.DXCALLSIGN_CRC = bytes(data_in[4:7]) static.DXGRID = bytes(data_in[7:13]).rstrip(b'\x00') @@ -1549,7 +1456,6 @@ class DATA(): structlog.get_logger("structlog").info("[TNC] PING ACK [" + str(self.mycallsign, 'utf-8') + "] >|< [" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR ) static.TNC_STATE = 'IDLE' - def stop_transmission(self): """ Force a stop of the running transmission @@ -1594,7 +1500,6 @@ class DATA(): """ try: - while 1: time.sleep(0.5) while static.BEACON_STATE: @@ -1616,7 +1521,6 @@ class DATA(): else: modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) - # wait while transmitting while static.TRANSMITTING: time.sleep(0.01) @@ -1649,7 +1553,6 @@ class DATA(): structlog.get_logger("structlog").info("[TNC] BEACON RCVD [" + str(dxcallsign, 'utf-8') + "]["+ str(dxgrid, 'utf-8') +"] ", snr=static.SNR) helpers.add_to_heard_stations(dxcallsign,dxgrid, 'BEACON', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) - def transmit_cq(self): """ Transmit a CQ @@ -1675,7 +1578,6 @@ class DATA(): time.sleep(0.01) return - def received_cq(self, data_in:bytes): """ Called if we received a CQ @@ -1696,7 +1598,6 @@ class DATA(): if static.RESPOND_TO_CQ: self.transmit_qrv() - def transmit_qrv(self): """ Called if we send a QRV frame @@ -1731,7 +1632,6 @@ class DATA(): while static.TRANSMITTING: time.sleep(0.01) - def received_qrv(self, data_in:bytes): """ Called if we receive a QRV frame @@ -1753,7 +1653,6 @@ class DATA(): structlog.get_logger("structlog").info("[TNC] QRV RCVD [" + str(dxcallsign, 'utf-8') + "]["+ str(dxgrid, 'utf-8') +"] ", snr=static.SNR) helpers.add_to_heard_stations(dxcallsign,dxgrid, 'QRV', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) - # ------------ CALUCLATE TRANSFER RATES def calculate_transfer_rate_rx(self, rx_start_of_transmission:float, receivedbytes:int) -> list: """ @@ -1765,7 +1664,6 @@ class DATA(): Returns: """ - try: if static.TOTAL_BYTES == 0: static.TOTAL_BYTES = 1 @@ -1791,8 +1689,6 @@ class DATA(): static.ARQ_BYTES_PER_MINUTE, \ static.ARQ_TRANSMISSION_PERCENT] - - def reset_statistics(self): """ Reset statistics @@ -1816,7 +1712,6 @@ class DATA(): Returns: """ - try: static.ARQ_TRANSMISSION_PERCENT = int((sentbytes / tx_buffer_length) * 100) @@ -1843,14 +1738,12 @@ class DATA(): static.ARQ_BYTES_PER_MINUTE, \ static.ARQ_TRANSMISSION_PERCENT] - # ----------------------CLEANUP AND RESET FUNCTIONS def arq_cleanup(self): """ Cleanup funktion which clears all ARQ states """ - structlog.get_logger("structlog").debug("cleanup") self.received_mycall_crc = b'' @@ -1894,10 +1787,6 @@ class DATA(): static.BEACON_PAUSE = False - - - - def arq_reset_ack(self,state:bool): """ Funktion for resetting acknowledge states @@ -1907,12 +1796,10 @@ class DATA(): Returns: """ - self.burst_ack = state self.rpt_request_received = state self.data_frame_ack_received = state - def set_listening_modes(self, mode): """ Function for setting the data modes we are listening to for saving cpu power @@ -1941,7 +1828,6 @@ class DATA(): modem.RECEIVE_FSK_LDPC_1 = True structlog.get_logger("structlog").debug("changing listening data mode", mode="datac1/datac3/fsk_ldpc") - # ------------------------- WATCHDOG FUNCTIONS FOR TIMER def watchdog(self): """Author: DJ2LS @@ -1959,13 +1845,11 @@ class DATA(): self.burst_watchdog() self.arq_session_keep_alive_watchdog() - def burst_watchdog(self): """ watchdog which checks if we are running into a connection timeout DATA BURST """ - # IRS SIDE if static.ARQ_STATE and static.ARQ_SESSION_STATE == 'connected' and static.TNC_STATE == 'BUSY' and self.is_IRS: if self.data_channel_last_received + self.time_list[self.speed_level] > time.time(): @@ -2011,13 +1895,11 @@ class DATA(): self.stop_transmission() self.arq_cleanup() - def data_channel_keep_alive_watchdog(self): """ watchdog which checks if we are running into a connection timeout DATA CHANNEL """ - # and not static.ARQ_SEND_KEEP_ALIVE: if static.ARQ_STATE and static.TNC_STATE == 'BUSY': time.sleep(0.01) @@ -2045,8 +1927,6 @@ class DATA(): static.INFO.append("ARQ;SESSION;TIMEOUT") self.close_session() - - def heartbeat(self): """ heartbeat thread which auto resumes the heartbeat signal within a arq session @@ -2058,7 +1938,5 @@ class DATA(): self.transmit_session_heartbeat() time.sleep(2) - - def send_test_frame(self): modem.MODEM_TRANSMIT_QUEUE.put([12,1,0,[bytearray(126)]]) diff --git a/tnc/helpers.py b/tnc/helpers.py index f5782607..7a922c3d 100644 --- a/tnc/helpers.py +++ b/tnc/helpers.py @@ -5,13 +5,10 @@ Created on Fri Dec 25 21:25:14 2020 @author: DJ2LS """ - import time import crcengine import static - - def wait(seconds): """ @@ -27,8 +24,6 @@ def wait(seconds): time.sleep(0.01) return True - - def get_crc_8(data): """Author: DJ2LS @@ -47,7 +42,6 @@ def get_crc_8(data): crc_data = crc_data.to_bytes(1, byteorder='big') return crc_data - def get_crc_16(data): """Author: DJ2LS @@ -87,7 +81,6 @@ def get_crc_24(data): crc_data = crc_data.to_bytes(3, byteorder='big') return crc_data - def get_crc_32(data): """Author: DJ2LS @@ -106,7 +99,6 @@ def get_crc_32(data): crc_data = crc_data.to_bytes(4, byteorder='big') return crc_data - def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency): """ @@ -137,14 +129,11 @@ def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency): static.HEARD_STATIONS.append([dxcallsign, dxgrid, int(time.time()), datatype, snr, offset, frequency]) break - # for idx, item in enumerate(static.HEARD_STATIONS): # if dxcallsign in item: # item = [dxcallsign, int(time.time())] # static.HEARD_STATIONS[idx] = item - - def callsign_to_bytes(callsign): """ @@ -195,7 +184,6 @@ def callsign_to_bytes(callsign): ssid = bytes([ssid]).decode("utf-8") return encode_call(callsign + ssid) - #return bytes(bytestring) def bytes_to_callsign(bytestring): @@ -209,7 +197,6 @@ def bytes_to_callsign(bytestring): bytes """ - # http://www.aprs.org/aprs11/SSIDs.txt #-0 Your primary station usually fixed and message capable #-1 generic additional station, digi, mobile, wx, etc @@ -246,8 +233,6 @@ def bytes_to_callsign(bytestring): ssid = ord(bytes(decoded[-1], "utf-8")) return bytes(callsign + "-" + str(ssid), "utf-8") - - def check_callsign(callsign:bytes, crc_to_check:bytes): """ Funktion to check a crc against a callsign to calculate the ssid by generating crc until we got it @@ -260,7 +245,6 @@ def check_callsign(callsign:bytes, crc_to_check:bytes): [True, Callsign + SSID] False """ - print(callsign) try: callsign = callsign.split(b'-') @@ -282,8 +266,6 @@ def check_callsign(callsign:bytes, crc_to_check:bytes): return [False, ""] - - def encode_grid(grid): """ @auther: DB1UJ @@ -345,8 +327,6 @@ def decode_grid(b_code_word:bytes): return grid - - def encode_call(call): """ @auther: DB1UJ @@ -390,4 +370,3 @@ def decode_call(b_code_word:bytes): call = call[0:-1] + ssid # remove the last char from call and replace with SSID return call - diff --git a/tnc/log_handler.py b/tnc/log_handler.py index a14a4159..6d489cca 100644 --- a/tnc/log_handler.py +++ b/tnc/log_handler.py @@ -8,7 +8,6 @@ def setup_logging(filename): Returns: """ - import logging.config import structlog diff --git a/tnc/main.py b/tnc/main.py index e45a54bf..a3a2fe4d 100755 --- a/tnc/main.py +++ b/tnc/main.py @@ -6,11 +6,7 @@ Created on Tue Dec 22 16:58:45 2020 @author: DJ2LS main module for running the tnc - - """ - - import argparse import threading import static @@ -26,7 +22,6 @@ import signal import time import multiprocessing - # signal handler for closing aplication def signal_handler(sig, frame): """ @@ -75,7 +70,6 @@ if __name__ == '__main__': PARSER.add_argument('--tuning_range_fmax', dest="tuning_range_fmax", choices=[50.0, 100.0, 150.0, 200.0, 250.0], default=50.0, help="Tuning range fmax", type=float) PARSER.add_argument('--tx-audio-level', dest="tx_audio_level", default=50, help="Set the tx audio level at an early stage", type=int) - ARGS = PARSER.parse_args() # additional step for beeing sure our callsign is correctly @@ -132,8 +126,6 @@ if __name__ == '__main__': except: structlog.get_logger("structlog").error("[DMN] logger init error") - - structlog.get_logger("structlog").info("[TNC] Starting FreeDATA", author="DJ2LS", year="2022", version=static.VERSION) # start data handler @@ -142,7 +134,6 @@ if __name__ == '__main__': # start modem modem = modem.RF() - # --------------------------------------------START CMD SERVER try: diff --git a/tnc/modem.py b/tnc/modem.py index a6a46864..0722cbc2 100644 --- a/tnc/modem.py +++ b/tnc/modem.py @@ -40,7 +40,6 @@ RECEIVE_DATAC1 = False RECEIVE_DATAC3 = False RECEIVE_FSK_LDPC_1 = False - class RF(): """ """ @@ -49,7 +48,6 @@ class RF(): self.sampler_avg = 0 self.buffer_avg = 0 - self.AUDIO_SAMPLE_RATE_RX = 48000 self.AUDIO_SAMPLE_RATE_TX = 48000 self.MODEM_SAMPLE_RATE = codec2.api.FREEDV_FS_8000 @@ -107,7 +105,6 @@ class RF(): codec2.api.freedv_set_frames_per_burst(self.datac3_freedv,1) self.datac3_buffer = codec2.audio_buffer(2*self.AUDIO_FRAMES_PER_BUFFER_RX) - self.fsk_ldpc_freedv_0 = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV)), c_void_p) self.fsk_ldpc_bytes_per_frame_0 = int(codec2.api.freedv_get_bits_per_modem_frame(self.fsk_ldpc_freedv_0)/8) self.fsk_ldpc_bytes_out_0 = create_string_buffer(self.fsk_ldpc_bytes_per_frame_0) @@ -147,7 +144,6 @@ class RF(): structlog.get_logger("structlog").error("[TNC] starting pyaudio callback failed", e=e) else: - # create a stream object for simulating audio stream class Object(object): pass @@ -168,7 +164,6 @@ class RF(): mkfifo_read_callback_thread = threading.Thread(target=self.mkfifo_read_callback, name="MKFIFO READ CALLBACK THREAD",daemon=True) mkfifo_read_callback_thread.start() - # --------------------------------------------INIT AND OPEN HAMLIB # check how we want to control the radio if static.HAMLIB_RADIOCONTROL == 'direct': @@ -182,7 +177,6 @@ class RF(): else: import rigdummy as rig - self.hamlib = rig.radio() self.hamlib.open_rig(devicename=static.HAMLIB_DEVICE_NAME, deviceport=static.HAMLIB_DEVICE_PORT, hamlib_ptt_type=static.HAMLIB_PTT_TYPE, serialspeed=static.HAMLIB_SERIAL_SPEED, pttport=static.HAMLIB_PTT_PORT, data_bits=static.HAMLIB_DATA_BITS, stop_bits=static.HAMLIB_STOP_BITS, handshake=static.HAMLIB_HANDSHAKE, rigctld_ip = static.HAMLIB_RGICTLD_IP, rigctld_port = static.HAMLIB_RGICTLD_PORT) @@ -219,7 +213,6 @@ class RF(): # -------------------------------------------------------------------------------------------------------- def mkfifo_read_callback(self): while 1: - time.sleep(0.01) # -----read data_in48k = bytes() @@ -245,8 +238,6 @@ class RF(): if RECEIVE_DATAC3: self.datac3_buffer.push(x) - - def mkfifo_write_callback(self): while 1: time.sleep(0.01) @@ -264,7 +255,6 @@ class RF(): fifo_write.write(data_out48k) fifo_write.flush() - # -------------------------------------------------------------------------------------------------------- #def audio_callback(self, data_in48k, frame_count, time_info, status): def callback(self, data_in48k, outdata, frames, time, status): @@ -279,7 +269,6 @@ class RF(): Returns: """ - x = np.frombuffer(data_in48k, dtype=np.int16) x = self.resampler.resample48_to_8(x) @@ -358,18 +347,15 @@ class RF(): data_out = json.dumps(jsondata) sock.SOCKET_QUEUE.put(data_out) - # open codec2 instance self.MODE = mode if self.MODE == 'FSK_LDPC_0' or self.MODE == 200: freedv = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV)), c_void_p) elif self.MODE == 'FSK_LDPC_1' or self.MODE == 201: freedv = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV)), c_void_p) - else: freedv = cast(codec2.api.freedv_open(self.MODE), c_void_p) - # get number of bytes per frame for mode bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(freedv)/8) payload_bytes_per_frame = bytes_per_frame -2 @@ -415,7 +401,6 @@ class RF(): codec2.api.freedv_rawdatatx(freedv,mod_out,data) # modulate DATA and save it into mod_out pointer txbuffer += bytes(mod_out) - # codec2 fsk preamble may be broken - at least it sounds like that so we are disabling it for testing if not self.MODE == 'FSK_LDPC_0' or self.MODE == 200 or self.MODE == 'FSK_LDPC_1' or self.MODE == 201: # write preamble to txbuffer @@ -437,12 +422,8 @@ class RF(): # deaktivated for testing purposes self.mod_out_locked = False - # ------------------------------- - - - chunk_length = self.AUDIO_FRAMES_PER_BUFFER_TX #4800 chunk = [txbuffer_48k[i:i+chunk_length] for i in range(0, len(txbuffer_48k), chunk_length)] for c in chunk: @@ -455,8 +436,6 @@ class RF(): #structlog.get_logger("structlog").debug("[TNC] mod out shorter than audio buffer", delta=delta) self.modoutqueue.append(c) - - # Release our mod_out_lock so we can use the queue self.mod_out_locked = False @@ -553,8 +532,6 @@ class RF(): #self.get_scatter(self.fsk_ldpc_freedv_1) self.calculate_snr(self.fsk_ldpc_freedv_1) - - # worker for FIFO queue for processing received frames def worker_transmit(self): """ """ @@ -564,8 +541,6 @@ class RF(): self.transmit(mode=data[0], repeats=data[1], repeat_delay=data[2], frames=data[3]) #self.modem_transmit_queue.task_done() - - # worker for FIFO queue for processing received frames def worker_received(self): """ """ @@ -577,7 +552,6 @@ class RF(): data_handler.DATA_QUEUE_RECEIVED.put([data[0], data[1], data[2]]) self.modem_received_queue.task_done() - def get_frequency_offset(self, freedv): """ @@ -594,7 +568,6 @@ class RF(): static.FREQ_OFFSET = offset return offset - def get_scatter(self, freedv): """ @@ -629,7 +602,6 @@ class RF(): scatterdata_small = scatterdata[::10] static.SCATTER = scatterdata_small - def calculate_snr(self, freedv): """ @@ -639,7 +611,6 @@ class RF(): Returns: """ - try: modem_stats_snr = c_float() modem_stats_sync = c_int() @@ -666,7 +637,6 @@ class RF(): static.HAMLIB_MODE = self.hamlib.get_mode() static.HAMLIB_BANDWITH = self.hamlib.get_bandwith() - def calculate_fft(self): """ """ # channel_busy_delay counter @@ -678,7 +648,6 @@ class RF(): # WE NEED TO OPTIMIZE THIS! if len(self.fft_data) >= 128: - # https://gist.github.com/ZWMiller/53232427efc5088007cab6feee7c6e4c # Fast Fourier Transform, 10*log10(abs) is to scale it to dB # and make sure it's not imaginary @@ -693,7 +662,6 @@ class RF(): # get average of dfft avg = np.mean(dfft) - # detect signals which are higher than the average + 10 ( +10 smoothes the output ) # data higher than the average must be a signal. Therefore we are setting it to 100 so it will be highlighted # have to do this when we are not transmittig so our own sending data will not affect this too much @@ -703,7 +671,6 @@ class RF(): # calculate audio max value # static.AUDIO_RMS = np.amax(self.fft_data) - # check for signals higher than average by checking for "100" # if we have a signal, increment our channel_busy delay counter so we have a smoother state toggle @@ -728,9 +695,7 @@ class RF(): static.FFT = dfftlist[0:320] #320 --> bandwith 3000 - except: - structlog.get_logger("structlog").debug("[TNC] Setting fft=0") # else 0 static.FFT = [0] @@ -750,8 +715,6 @@ class RF(): codec2.api.freedv_set_frames_per_burst(self.datac3_freedv,n_frames_per_burst) codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0,n_frames_per_burst) - - def get_bytes_per_frame(mode): """ provide bytes per frame information for accessing from data handler @@ -772,9 +735,6 @@ def get_bytes_per_frame(mode): # get number of bytes per frame for mode return int(codec2.api.freedv_get_bits_per_modem_frame(freedv)/8) - def set_audio_volume(datalist, volume): data = np.fromstring(datalist, np.int16) * (volume / 100.) return data.astype(np.int16) - - diff --git a/tnc/rig.py b/tnc/rig.py index 8e64784b..0016b77d 100644 --- a/tnc/rig.py +++ b/tnc/rig.py @@ -7,7 +7,6 @@ import atexit import subprocess import os - # set global hamlib version hamlib_version = 0 @@ -19,7 +18,6 @@ except: app_path = os.path.abspath(".") sys.path.append(app_path) - # try importing hamlib try: # get python version @@ -76,7 +74,6 @@ except Exception as e: class radio: """ """ def __init__(self): - self.devicename = '' self.devicenumber = '' self.deviceport = '' @@ -106,7 +103,6 @@ class radio: Returns: """ - self.devicename = devicename self.deviceport = str(deviceport) self.serialspeed = str(serialspeed) # we need to ensure this is a str, otherwise set_conf functions are crashing @@ -116,7 +112,6 @@ class radio: self.stop_bits = str(stop_bits) self.handshake = str(handshake) - # try to init hamlib try: Hamlib.rig_set_debug(Hamlib.RIG_DEBUG_NONE) @@ -128,7 +123,6 @@ class radio: structlog.get_logger("structlog").error("[RIG] Hamlib: rig not supported...") self.devicenumber = 0 - self.my_rig = Hamlib.Rig(self.devicenumber) self.my_rig.set_conf("rig_pathname", self.deviceport) self.my_rig.set_conf("retry", "5") @@ -138,9 +132,6 @@ class radio: self.my_rig.set_conf("data_bits", self.data_bits) self.my_rig.set_conf("ptt_pathname", self.pttport) - - - if self.hamlib_ptt_type == 'RIG': self.hamlib_ptt_type = Hamlib.RIG_PTT_RIG self.my_rig.set_conf("ptt_type", 'RIG') @@ -149,7 +140,6 @@ class radio: self.hamlib_ptt_type = Hamlib.RIG_PORT_USB self.my_rig.set_conf("ptt_type", 'USB') - elif self.hamlib_ptt_type == 'DTR-H': self.hamlib_ptt_type = Hamlib.RIG_PTT_SERIAL_DTR self.my_rig.set_conf("dtr_state", "HIGH") @@ -182,7 +172,6 @@ class radio: structlog.get_logger("structlog").info("[RIG] Opening...", device=self.devicenumber, path=self.my_rig.get_conf("rig_pathname"), serial_speed=self.my_rig.get_conf("serial_speed"), serial_handshake=self.my_rig.get_conf("serial_handshake"), stop_bits=self.my_rig.get_conf("stop_bits"), data_bits=self.my_rig.get_conf("data_bits"), ptt_pathname=self.my_rig.get_conf("ptt_pathname")) - self.my_rig.open() atexit.register(self.my_rig.close) @@ -199,7 +188,6 @@ class radio: except: structlog.get_logger("structlog").info("[RIG] Hamlib device opened", status='SUCCESS') - # set ptt to false if ptt is stuck for some reason self.set_ptt(False) diff --git a/tnc/rigctl.py b/tnc/rigctl.py index 229176c9..3e92e093 100644 --- a/tnc/rigctl.py +++ b/tnc/rigctl.py @@ -18,10 +18,10 @@ import os # set global hamlib version hamlib_version = 0 + class radio: """ """ def __init__(self): - self.devicename = '' self.devicenumber = '' self.deviceport = '' @@ -51,7 +51,6 @@ class radio: Returns: """ - self.devicename = devicename self.deviceport = deviceport self.serialspeed = str(serialspeed) # we need to ensure this is a str, otherwise set_conf functions are crashing @@ -61,8 +60,7 @@ class radio: self.stop_bits = stop_bits self.handshake = handshake - - # check if we are running in a pyinstaller environment + # check if we are running in a pyinstaller environment try: app_path = sys._MEIPASS except: @@ -86,9 +84,6 @@ class radio: print(self.devicenumber, self.deviceport, self.serialspeed) - - - # select precompiled executable for win32/win64 rigctl # this is really a hack...somewhen we need a native hamlib integration for windows if sys.platform == 'win32' or sys.platform == 'win64': @@ -116,7 +111,6 @@ class radio: except: return False - def get_mode(self): """ """ #(hamlib_mode, bandwith) = self.my_rig.get_mode() @@ -126,7 +120,6 @@ class radio: except: return False - def get_bandwith(self): """ """ #(hamlib_mode, bandwith) = self.my_rig.get_mode() diff --git a/tnc/rigctld.py b/tnc/rigctld.py index ea7452a8..f21b1d3f 100644 --- a/tnc/rigctld.py +++ b/tnc/rigctld.py @@ -5,6 +5,7 @@ import log_handler import logging import time import static + # class taken from darsidelemm # rigctl - https://github.com/darksidelemm/rotctld-web-gui/blob/master/rotatorgui.py#L35 # @@ -13,6 +14,7 @@ import static # set global hamlib version hamlib_version = 0 + class radio(): """rotctld (hamlib) communication class""" # Note: This is a massive hack. @@ -48,7 +50,6 @@ class radio(): self.hostname = rigctld_ip self.port = int(rigctld_port) - if self.connect(): logging.debug(f"Rigctl intialized") return True @@ -75,7 +76,6 @@ class radio(): self.sock.close() self.connected = False - def send_command(self, command): """Send a command to the connected rotctld instance, and return the return value. @@ -104,7 +104,6 @@ class radio(): time.sleep(0.5) self.connect() - def get_mode(self): """ """ try: @@ -114,6 +113,7 @@ class radio(): return mode.decode("utf-8") except: 0 + def get_bandwith(self): """ """ try: diff --git a/tnc/rigdummy.py b/tnc/rigdummy.py index 06bcc38f..445cfb58 100644 --- a/tnc/rigdummy.py +++ b/tnc/rigdummy.py @@ -4,12 +4,12 @@ import structlog hamlib_version = 0 + class radio: """ """ def __init__(self): pass - def open_rig(self, **kwargs): """ @@ -62,4 +62,3 @@ class radio: def close_rig(self): """ """ return - diff --git a/tnc/sock.py b/tnc/sock.py index 2c5b9af2..c3e3bd47 100644 --- a/tnc/sock.py +++ b/tnc/sock.py @@ -42,10 +42,6 @@ CONNECTED_CLIENTS = set() CLOSE_SIGNAL = False - - - - class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): """ the socket handler base class @@ -53,11 +49,9 @@ class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): pass - class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): """ """ - def send_to_client(self): """ function called by socket handler @@ -66,10 +60,8 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): """ tempdata = b'' while self.connection_alive and not CLOSE_SIGNAL: - # send tnc state as network stream # check server port against daemon port and send corresponding data - if self.server.server_address[1] == static.PORT and not static.TNCSTARTED: data = send_tnc_state() if data != tempdata: @@ -82,7 +74,6 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): SOCKET_QUEUE.put(data) time.sleep(0.5) - while not SOCKET_QUEUE.empty(): data = SOCKET_QUEUE.get() sock_data = bytes(data, 'utf-8') @@ -120,7 +111,6 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): #print("connection broken. Closing...") self.connection_alive = False - if data.startswith(b'{') and data.endswith(b'}\n'): # split data by \n if we have multiple commands in socket buffer data = data.split(b'\n') @@ -141,19 +131,16 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): # and which one can be processed during a running transmission time.sleep(3) - # finally delete our rx buffer to be ready for new commands data = bytes() except Exception as e: structlog.get_logger("structlog").info("[SCK] Connection closed", ip=self.client_address[0], port=self.client_address[1], e=e) self.connection_alive = False - def handle(self): """ socket handler """ - CONNECTED_CLIENTS.add(self.request) structlog.get_logger("structlog").debug("[SCK] Client connected", ip=self.client_address[0], port=self.client_address[1]) @@ -166,8 +153,6 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): while self.connection_alive and not CLOSE_SIGNAL: time.sleep(1) - - def finish(self): """ """ structlog.get_logger("structlog").warning("[SCK] Closing client socket", ip=self.client_address[0], port=self.client_address[1]) @@ -189,7 +174,6 @@ def process_tnc_commands(data): """ # we need to do some error handling in case of socket timeout or decoding issue try: - # convert data to json object received_json = json.loads(data) structlog.get_logger("structlog").debug("[SCK] CMD", command=received_json) @@ -203,7 +187,6 @@ def process_tnc_commands(data): command_response("tx_audio_level", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - # TRANSMIT SINE WAVE ----------------------------------------------------- if received_json["type"] == "set" and received_json["command"] == "send_test_frame": try: @@ -222,6 +205,7 @@ def process_tnc_commands(data): except Exception as e: command_response("cqcqcq", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) + # START_BEACON ----------------------------------------------------- if received_json["command"] == "start_beacon": try: @@ -262,7 +246,6 @@ def process_tnc_commands(data): command_response("ping", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - # CONNECT ---------------------------------------------------------- if received_json["type"] == 'arq' and received_json["command"] == "connect": static.BEACON_PAUSE = True @@ -297,7 +280,6 @@ def process_tnc_commands(data): # TRANSMIT RAW DATA ------------------------------------------- if received_json["type"] == 'arq' and received_json["command"] == "send_raw": - static.BEACON_PAUSE = True try: if not static.ARQ_SESSION: @@ -314,7 +296,6 @@ def process_tnc_commands(data): dxcallsign = static.DXCALLSIGN static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN) - mode = int(received_json["parameter"][0]["mode"]) n_frames = int(received_json["parameter"][0]["n_frames"]) base64data = received_json["parameter"][0]["data"] @@ -335,15 +316,13 @@ def process_tnc_commands(data): binarydata = base64.b64decode(base64data) data_handler.DATA_QUEUE_TRANSMIT.put(['ARQ_RAW', binarydata, mode, n_frames, arq_uuid, mycallsign]) - else: raise TypeError + except Exception as e: command_response("send_raw", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - - # STOP TRANSMISSION ---------------------------------------------------------- if received_json["type"] == 'arq' and received_json["command"] == "stop_transmission": try: @@ -357,7 +336,6 @@ def process_tnc_commands(data): command_response("stop_transmission", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - if received_json["type"] == 'get' and received_json["command"] == 'rx_buffer': try: output = { @@ -380,7 +358,6 @@ def process_tnc_commands(data): command_response("rx_buffer", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - if received_json["type"] == 'set' and received_json["command"] == 'del_rx_buffer': try: static.RX_BUFFER = [] @@ -397,7 +374,6 @@ def send_tnc_state(): """ send the tnc state to network """ - encoding = 'utf-8' output = { @@ -438,7 +414,6 @@ def send_tnc_state(): jsondata = json.dumps(output) return jsondata - def process_daemon_commands(data): """ process daemon commands @@ -469,7 +444,6 @@ def process_daemon_commands(data): command_response("mycallsign", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - if received_json["type"] == 'set' and received_json["command"] == 'mygrid': try: mygrid = received_json["parameter"] @@ -484,9 +458,7 @@ def process_daemon_commands(data): command_response("mygrid", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) - if received_json["type"] == 'set' and received_json["command"] == 'start_tnc' and not static.TNCSTARTED: - try: mycall = str(received_json["parameter"][0]["mycall"]) mygrid = str(received_json["parameter"][0]["mygrid"]) @@ -512,7 +484,6 @@ def process_daemon_commands(data): tx_audio_level = str(received_json["parameter"][0]["tx_audio_level"]) respond_to_cq = str(received_json["parameter"][0]["respond_to_cq"]) - # print some debugging parameters for item in received_json["parameter"][0]: structlog.get_logger("structlog").debug("[DMN] TNC Startup Config : " + item, value=received_json["parameter"][0][item]) @@ -549,7 +520,6 @@ def process_daemon_commands(data): structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) if received_json["type"] == 'get' and received_json["command"] == 'test_hamlib': - try: devicename = str(received_json["parameter"][0]["devicename"]) deviceport = str(received_json["parameter"][0]["deviceport"]) @@ -619,7 +589,6 @@ def send_daemon_state(): else: output["daemon_state"].append({"status": "stopped"}) - jsondata = json.dumps(output) return jsondata diff --git a/tnc/static.py b/tnc/static.py index 4bf08ad1..2922520c 100644 --- a/tnc/static.py +++ b/tnc/static.py @@ -15,7 +15,6 @@ DAEMONPORT = 3001 TNCSTARTED = False TNCPROCESS = 0 - # Operator Defaults MYCALLSIGN = b'AA0AA' MYCALLSIGN_CRC = b'A' @@ -39,7 +38,6 @@ SOCKET_TIMEOUT = 1 # seconds SERIAL_DEVICES = [] # --------------------------------- - PTT_STATE = False TRANSMITTING = False @@ -96,7 +94,6 @@ ARQ_TRANSMISSION_PERCENT = 0 ARQ_SPEED_LEVEL = 0 TOTAL_BYTES = 0 - #CHANNEL_STATE = 'RECEIVING_SIGNALLING' TNC_STATE = 'IDLE' ARQ_STATE = False From 8a5e290a307c694eb1dde765632ed7af818da924 Mon Sep 17 00:00:00 2001 From: Paul Kronenwetter Date: Wed, 11 May 2022 18:10:59 -0400 Subject: [PATCH 3/3] Various refactorings. Type hints, trailing backslash, range usage, etc. --- tnc/audio.py | 18 +- tnc/codec2.py | 254 ++++++------- tnc/daemon.py | 105 +++--- tnc/data_handler.py | 852 +++++++++++++++++++++----------------------- tnc/helpers.py | 161 +++++---- tnc/main.py | 35 +- tnc/modem.py | 458 ++++++++++++------------ tnc/rig.py | 9 +- tnc/rigctl.py | 28 +- tnc/rigctld.py | 28 +- tnc/sock.py | 130 +++---- 11 files changed, 1033 insertions(+), 1045 deletions(-) diff --git a/tnc/audio.py b/tnc/audio.py index 203205b3..23c438dd 100644 --- a/tnc/audio.py +++ b/tnc/audio.py @@ -1,9 +1,10 @@ -import json -import sys -import multiprocessing -import sounddevice as sd import atexit +import json +import multiprocessing +import sys + +import sounddevice as sd atexit.register(sd._terminate) @@ -23,7 +24,6 @@ def get_audio_devices(): sd._initialize() with multiprocessing.Manager() as manager: - proxy_input_devices = manager.list() proxy_output_devices = manager.list() #print(multiprocessing.get_start_method()) @@ -45,8 +45,7 @@ def fetch_audio_devices(input_devices, output_devices): """ devices = sd.query_devices(device=None, kind=None) - index = 0 - for device in devices: + for index, device in enumerate(devices): #for i in range(0, p.get_device_count()): # we need to do a try exception, beacuse for windows theres no audio device range try: @@ -62,7 +61,6 @@ def fetch_audio_devices(input_devices, output_devices): name = '' if maxInputChannels > 0: - input_devices.append({"id": index, "name": str(name)}) + input_devices.append({"id": index, "name": name}) if maxOutputChannels > 0: - output_devices.append({"id": index, "name": str(name)}) - index += 1 + output_devices.append({"id": index, "name": name}) diff --git a/tnc/codec2.py b/tnc/codec2.py index f45951f3..adb9f025 100644 --- a/tnc/codec2.py +++ b/tnc/codec2.py @@ -1,73 +1,77 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +# pylint: disable=invalid-name, line-too-long, c-extension-no-member +# pylint: disable=import-outside-toplevel + import ctypes -from ctypes import * -import sys -import os -from enum import Enum -import numpy as np -from threading import Lock import glob +import os +import sys +from enum import Enum +from threading import Lock + +import numpy as np import structlog # Enum for codec2 modes class FREEDV_MODE(Enum): """ - enum for codec2 modes and names + Enumeration for codec2 modes and names """ fsk_ldpc_0 = 200 fsk_ldpc_1 = 201 - fsk_ldpc = 9 - datac0 = 14 - datac1 = 10 - datac3 = 12 - allmodes = 255 + fsk_ldpc = 9 + datac0 = 14 + datac1 = 10 + datac3 = 12 + allmodes = 255 -# function for returning the mode value -def freedv_get_mode_value_by_name(mode): + +# Function for returning the mode value +def freedv_get_mode_value_by_name(mode: str) -> int: """ - get the codec2 mode by entering its string + Get the codec2 mode by entering its string + Args: - mode: - - Returns: int + mode: + Returns: + int """ return FREEDV_MODE[mode].value -# function for returning the mode name -def freedv_get_mode_name_by_value(mode): +# Function for returning the mode name +def freedv_get_mode_name_by_value(mode: int) -> str: """ get the codec2 mode name as string Args: - mode: - - Returns: string + mode: + Returns: + string """ return FREEDV_MODE(mode).name - -# check if we are running in a pyinstaller environment -try: - app_path = sys._MEIPASS -except: - app_path = os.path.abspath(".") -sys.path.append(app_path) +# Check if we are running in a pyinstaller environment +if hasattr(sys, "_MEIPASS"): + sys.path.append(getattr(sys, "_MEIPASS")) +else: + sys.path.append(os.path.abspath(".")) structlog.get_logger("structlog").info("[C2 ] Searching for libcodec2...") if sys.platform == 'linux': - files = glob.glob('**/*libcodec2*',recursive=True) + files = glob.glob(r'**/*libcodec2*',recursive=True) files.append('libcodec2.so') elif sys.platform == 'darwin': - files = glob.glob('**/*libcodec2*.dylib',recursive=True) -elif sys.platform == 'win32' or sys.platform == 'win64': - files = glob.glob('**\*libcodec2*.dll',recursive=True) + files = glob.glob(r'**/*libcodec2*.dylib',recursive=True) +elif sys.platform in ['win32', 'win64']: + files = glob.glob(r'**\*libcodec2*.dll',recursive=True) else: files = [] +api = None for file in files: try: api = ctypes.CDLL(file) @@ -76,73 +80,73 @@ for file in files: except Exception as e: structlog.get_logger("structlog").warning("[C2 ] Libcodec2 found but not loaded", path=file, e=e) -# quit module if codec2 cant be loaded -if not 'api' in locals(): - structlog.get_logger("structlog").critical("[C2 ] Libcodec2 not loaded", path=file) - os._exit(1) +# Quit module if codec2 cant be loaded +if api is None or 'api' not in locals(): + structlog.get_logger("structlog").critical("[C2 ] Libcodec2 not loaded") + sys.exit(1) # ctypes function init -#api.freedv_set_tuning_range.restype = c_int -#api.freedv_set_tuning_range.argype = [c_void_p, c_float, c_float] +#api.freedv_set_tuning_range.restype = ctypes.c_int +#api.freedv_set_tuning_range.argype = [ctypes.c_void_p, ctypes.c_float, ctypes.c_float] -api.freedv_open.argype = [c_int] -api.freedv_open.restype = c_void_p +api.freedv_open.argype = [ctypes.c_int] +api.freedv_open.restype = ctypes.c_void_p -api.freedv_open_advanced.argtype = [c_int, c_void_p] -api.freedv_open_advanced.restype = c_void_p +api.freedv_open_advanced.argtype = [ctypes.c_int, ctypes.c_void_p] +api.freedv_open_advanced.restype = ctypes.c_void_p -api.freedv_get_bits_per_modem_frame.argtype = [c_void_p] -api.freedv_get_bits_per_modem_frame.restype = c_int +api.freedv_get_bits_per_modem_frame.argtype = [ctypes.c_void_p] +api.freedv_get_bits_per_modem_frame.restype = ctypes.c_int -api.freedv_nin.argtype = [c_void_p] -api.freedv_nin.restype = c_int +api.freedv_nin.argtype = [ctypes.c_void_p] +api.freedv_nin.restype = ctypes.c_int -api.freedv_rawdatarx.argtype = [c_void_p, c_char_p, c_char_p] -api.freedv_rawdatarx.restype = c_int +api.freedv_rawdatarx.argtype = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p] +api.freedv_rawdatarx.restype = ctypes.c_int -api.freedv_rawdatatx.argtype = [c_void_p, c_char_p, c_char_p] -api.freedv_rawdatatx.restype = c_int +api.freedv_rawdatatx.argtype = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p] +api.freedv_rawdatatx.restype = ctypes.c_int -api.freedv_rawdatapostambletx.argtype = [c_void_p, c_char_p, c_char_p] -api.freedv_rawdatapostambletx.restype = c_int +api.freedv_rawdatapostambletx.argtype = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p] +api.freedv_rawdatapostambletx.restype = ctypes.c_int -api.freedv_rawdatapreambletx.argtype = [c_void_p, c_char_p, c_char_p] -api.freedv_rawdatapreambletx.restype = c_int +api.freedv_rawdatapreambletx.argtype = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p] +api.freedv_rawdatapreambletx.restype = ctypes.c_int -api.freedv_get_n_max_modem_samples.argtype = [c_void_p] -api.freedv_get_n_max_modem_samples.restype = c_int +api.freedv_get_n_max_modem_samples.argtype = [ctypes.c_void_p] +api.freedv_get_n_max_modem_samples.restype = ctypes.c_int -api.freedv_set_frames_per_burst.argtype = [c_void_p, c_int] -api.freedv_set_frames_per_burst.restype = c_void_p +api.freedv_set_frames_per_burst.argtype = [ctypes.c_void_p, ctypes.c_int] +api.freedv_set_frames_per_burst.restype = ctypes.c_void_p -api.freedv_get_rx_status.argtype = [c_void_p] -api.freedv_get_rx_status.restype = c_int +api.freedv_get_rx_status.argtype = [ctypes.c_void_p] +api.freedv_get_rx_status.restype = ctypes.c_int -api.freedv_get_modem_stats.argtype = [c_void_p, c_void_p, c_void_p] -api.freedv_get_modem_stats.restype = c_int +api.freedv_get_modem_stats.argtype = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] +api.freedv_get_modem_stats.restype = ctypes.c_int -api.freedv_get_n_tx_postamble_modem_samples.argtype = [c_void_p] -api.freedv_get_n_tx_postamble_modem_samples.restype = c_int +api.freedv_get_n_tx_postamble_modem_samples.argtype = [ctypes.c_void_p] +api.freedv_get_n_tx_postamble_modem_samples.restype = ctypes.c_int -api.freedv_get_n_tx_preamble_modem_samples.argtype = [c_void_p] -api.freedv_get_n_tx_preamble_modem_samples.restype = c_int +api.freedv_get_n_tx_preamble_modem_samples.argtype = [ctypes.c_void_p] +api.freedv_get_n_tx_preamble_modem_samples.restype = ctypes.c_int -api.freedv_get_n_tx_modem_samples.argtype = [c_void_p] -api.freedv_get_n_tx_modem_samples.restype = c_int +api.freedv_get_n_tx_modem_samples.argtype = [ctypes.c_void_p] +api.freedv_get_n_tx_modem_samples.restype = ctypes.c_int -api.freedv_get_n_max_modem_samples.argtype = [c_void_p] -api.freedv_get_n_max_modem_samples.restype = c_int +api.freedv_get_n_max_modem_samples.argtype = [ctypes.c_void_p] +api.freedv_get_n_max_modem_samples.restype = ctypes.c_int -api.FREEDV_FS_8000 = 8000 -api.FREEDV_MODE_DATAC1 = 10 -api.FREEDV_MODE_DATAC3 = 12 -api.FREEDV_MODE_DATAC0 = 14 +api.FREEDV_FS_8000 = 8000 +api.FREEDV_MODE_DATAC1 = 10 +api.FREEDV_MODE_DATAC3 = 12 +api.FREEDV_MODE_DATAC0 = 14 api.FREEDV_MODE_FSK_LDPC = 9 # -------------------------------- FSK LDPC MODE SETTINGS -# advanced structure for fsk modes +# Advanced structure for fsk modes class ADVANCED(ctypes.Structure): """ """ _fields_ = [ @@ -197,16 +201,15 @@ api.FREEDV_MODE_FSK_LDPC_1_ADV.tone_spacing = 200 api.FREEDV_MODE_FSK_LDPC_1_ADV.codename = 'H_256_512_4'.encode('utf-8') # code word # ------- MODEM STATS STRUCTURES - -MODEM_STATS_NC_MAX = 50+1 -MODEM_STATS_NR_MAX = 160 -MODEM_STATS_ET_MAX = 8 +MODEM_STATS_NC_MAX = 50 + 1 +MODEM_STATS_NR_MAX = 160 +MODEM_STATS_ET_MAX = 8 MODEM_STATS_EYE_IND_MAX = 160 -MODEM_STATS_NSPEC = 512 -MODEM_STATS_MAX_F_HZ = 4000 -MODEM_STATS_MAX_F_EST = 4 +MODEM_STATS_NSPEC = 512 +MODEM_STATS_MAX_F_HZ = 4000 +MODEM_STATS_MAX_F_EST = 4 -# modem stats structure +# Modem stats structure class MODEMSTATS(ctypes.Structure): """ """ _fields_ = [ @@ -252,18 +255,17 @@ api.rx_sync_flags_to_text = [ "EBS-", "EBST"] -# audio buffer --------------------------------------------------------- - +# Audio buffer --------------------------------------------------------- class audio_buffer: """ - thread safe audio buffer, which fits to needs of codec2 + Thread safe audio buffer, which fits to needs of codec2 made by David Rowe, VK5DGR """ - # a buffer of int16 samples, using a fixed length numpy array self.buffer for storage + # A buffer of int16 samples, using a fixed length numpy array self.buffer for storage # self.nbuffer is the current number of samples in the buffer def __init__(self, size): - structlog.get_logger("structlog").debug("[C2 ] creating audio buffer", size=size) + structlog.get_logger("structlog").debug("[C2 ] Creating audio buffer", size=size) self.size = size self.buffer = np.zeros(size, dtype=np.int16) self.nbuffer = 0 @@ -274,13 +276,13 @@ class audio_buffer: Push new data to buffer Args: - samples: + samples: Returns: - + Nothing """ self.mutex.acquire() - # add samples at the end of the buffer + # Add samples at the end of the buffer assert self.nbuffer+len(samples) <= self.size self.buffer[self.nbuffer:self.nbuffer+len(samples)] = samples self.nbuffer += len(samples) @@ -293,89 +295,89 @@ class audio_buffer: size: Returns: - + Nothing """ self.mutex.acquire() - # remove samples from the start of the buffer - self.nbuffer -= size; + # Remove samples from the start of the buffer + self.nbuffer -= size self.buffer[:self.nbuffer] = self.buffer[size:size+self.nbuffer] assert self.nbuffer >= 0 self.mutex.release() -# resampler --------------------------------------------------------- +# Resampler --------------------------------------------------------- -api.FDMDV_OS_48 = int(6) # oversampling rate -api.FDMDV_OS_TAPS_48K = int(48) # number of OS filter taps at 48kHz -api.FDMDV_OS_TAPS_48_8K = int(api.FDMDV_OS_TAPS_48K/api.FDMDV_OS_48) # number of OS filter taps at 8kHz -api.fdmdv_8_to_48_short.argtype = [c_void_p, c_void_p, c_int] -api.fdmdv_48_to_8_short.argtype = [c_void_p, c_void_p, c_int] +api.FDMDV_OS_48 = int(6) # oversampling rate +api.FDMDV_OS_TAPS_48K = int(48) # number of OS filter taps at 48kHz +api.FDMDV_OS_TAPS_48_8K = int(api.FDMDV_OS_TAPS_48K/api.FDMDV_OS_48) # number of OS filter taps at 8kHz +api.fdmdv_8_to_48_short.argtype = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int] +api.fdmdv_48_to_8_short.argtype = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int] class resampler: """ - resampler class + Re-sampler class """ - # resample an array of variable length, we just store the filter memories here + # Re-sample an array of variable length, we just store the filter memories here MEM8 = api.FDMDV_OS_TAPS_48_8K MEM48 = api.FDMDV_OS_TAPS_48K def __init__(self): - structlog.get_logger("structlog").debug("[C2 ] create 48<->8 kHz resampler") + structlog.get_logger("structlog").debug("[C2 ] Create 48<->8 kHz resampler") self.filter_mem8 = np.zeros(self.MEM8, dtype=np.int16) self.filter_mem48 = np.zeros(self.MEM48) - def resample48_to_8(self,in48): + def resample48_to_8(self, in48): """ - audio resampler integration from codec2 - downsample audio from 48000Hz to 8000Hz + Audio resampler integration from codec2 + Downsample audio from 48000Hz to 8000Hz Args: - in48: input data as np.int16 - - Returns: downsampled 8000Hz data as np.int16 + in48: input data as np.int16 + Returns: + Downsampled 8000Hz data as np.int16 """ assert in48.dtype == np.int16 - # length of input vector must be an integer multiple of api.FDMDV_OS_48 - assert(len(in48) % api.FDMDV_OS_48 == 0) + # Length of input vector must be an integer multiple of api.FDMDV_OS_48 + assert len(in48) % api.FDMDV_OS_48 == 0 - # concat filter memory and input samples + # Concatenate filter memory and input samples in48_mem = np.zeros(self.MEM48+len(in48), dtype=np.int16) in48_mem[:self.MEM48] = self.filter_mem48 in48_mem[self.MEM48:] = in48 # In C: pin48=&in48_mem[MEM48] - pin48 = byref(np.ctypeslib.as_ctypes(in48_mem), 2*self.MEM48) + pin48 = ctypes.byref(np.ctypeslib.as_ctypes(in48_mem), 2 * self.MEM48) n8 = int(len(in48) / api.FDMDV_OS_48) out8 = np.zeros(n8, dtype=np.int16) - api.fdmdv_48_to_8_short(out8.ctypes, pin48, n8); + api.fdmdv_48_to_8_short(out8.ctypes, pin48, n8) - # store memory for next time + # Store memory for next time self.filter_mem48 = in48_mem[:self.MEM48] return out8 - def resample8_to_48(self,in8): + def resample8_to_48(self, in8): """ - audio resampler integration from codec2 - resample audio from 8000Hz to 48000Hz + Audio resampler integration from codec2 + Re-sample audio from 8000Hz to 48000Hz Args: - in8: input data as np.int16 - - Returns: 48000Hz audio as np.int16 + in8: input data as np.int16 + Returns: + 48000Hz audio as np.int16 """ assert in8.dtype == np.int16 - # concat filter memory and input samples + # Concatenate filter memory and input samples in8_mem = np.zeros(self.MEM8+len(in8), dtype=np.int16) in8_mem[:self.MEM8] = self.filter_mem8 in8_mem[self.MEM8:] = in8 # In C: pin8=&in8_mem[MEM8] - pin8 = byref(np.ctypeslib.as_ctypes(in8_mem), 2*self.MEM8) + pin8 = ctypes.byref(np.ctypeslib.as_ctypes(in8_mem), 2 * self.MEM8) out48 = np.zeros(api.FDMDV_OS_48*len(in8), dtype=np.int16) api.fdmdv_8_to_48_short(out48.ctypes, pin8, len(in8)); - # store memory for next time + # Store memory for next time self.filter_mem8 = in8_mem[:self.MEM8] return out48 diff --git a/tnc/daemon.py b/tnc/daemon.py index 573a5101..d1729887 100755 --- a/tnc/daemon.py +++ b/tnc/daemon.py @@ -8,53 +8,57 @@ Author: DJ2LS, January 2022 daemon for providing basic information for the tnc like audio or serial devices """ +# pylint: disable=invalid-name, line-too-long, c-extension-no-member +# pylint: disable=import-outside-toplevel + import argparse -import threading -import socketserver -import time -import sys -import subprocess -import ujson as json -import psutil -import serial.tools.list_ports -import static -import crcengine -import re -import structlog -import log_handler -import helpers +import atexit +import multiprocessing import os import queue -import audio -import sock -import atexit +import re import signal -import multiprocessing +import socketserver +import subprocess +import sys +import threading +import time + +import crcengine +import psutil +import serial.tools.list_ports +import structlog +import ujson as json + +import audio +import helpers +import log_handler +import sock +import static + # signal handler for closing aplication def signal_handler(sig, frame): """ - signal handler for closing the network socket on app exit + Signal handler for closing the network socket on app exit Args: sig: frame: Returns: system exit - """ print('Closing daemon...') sock.CLOSE_SIGNAL = True sys.exit(0) -signal.signal(signal.SIGINT, signal_handler) +signal.signal(signal.SIGINT, signal_handler) class DAEMON(): """ - daemon class + Daemon class """ def __init__(self): - # load crc engine self.crc_algorithm = crcengine.new('crc16-ccitt-false') # load crc8 library @@ -70,20 +74,20 @@ class DAEMON(): def update_audio_devices(self): """ - update audio devices and set to static + Update audio devices and set to static """ while 1: try: if not static.TNCSTARTED: - static.AUDIO_INPUT_DEVICES, static.AUDIO_OUTPUT_DEVICES = audio.get_audio_devices() except Exception as e: - print(e) + structlog.get_logger("structlog").error("[DMN] update_audio_devices: Exception gathering audio devices:", e=e) + # print(e) time.sleep(1) def update_serial_devices(self): """ - update serial devices and set to static + Update serial devices and set to static """ while 1: try: @@ -91,22 +95,22 @@ class DAEMON(): serial_devices = [] ports = serial.tools.list_ports.comports() for port, desc, hwid in ports: - # calculate hex of hwid if we have unique names crc_hwid = self.crc_algorithm(bytes(hwid, encoding='utf-8')) crc_hwid = crc_hwid.to_bytes(2, byteorder='big') crc_hwid = crc_hwid.hex() - description = desc + ' [' + crc_hwid + ']' + description = f"{desc} [{crc_hwid}]" serial_devices.append({"port": str(port), "description": str(description) }) static.SERIAL_DEVICES = serial_devices time.sleep(1) except Exception as e: - print(e) + structlog.get_logger("structlog").error("[DMN] update_serial_devices: Exception gathering serial devices:", e=e) + # print(e) def worker(self): """ - a worker for the received commands + Worker to handle the received commands """ while 1: try: @@ -221,13 +225,13 @@ class DAEMON(): if data[23] == 'True': options.append('--qrv') - # try running tnc from binary, else run from source - # this helps running the tnc in a developer environment + # Try running tnc from binary, else run from source + # This helps running the tnc in a developer environment try: command = [] - if sys.platform == 'linux' or sys.platform == 'darwin': + if sys.platform in ['linux', 'darwin']: command.append('./freedata-tnc') - elif sys.platform == 'win32' or sys.platform == 'win64': + elif sys.platform in ['win32', 'win64']: command.append('freedata-tnc.exe') command += options @@ -236,11 +240,12 @@ class DAEMON(): atexit.register(p.kill) structlog.get_logger("structlog").info("[DMN] TNC started", path="binary") - except: + except FileNotFoundError as e: + structlog.get_logger("structlog").error("[DMN] worker: Exception:", e=e) command = [] - if sys.platform == 'linux' or sys.platform == 'darwin': + if sys.platform in ['linux', 'darwin']: command.append('python3') - elif sys.platform == 'win32' or sys.platform == 'win64': + elif sys.platform in ['win32', 'win64']: command.append('python') command.append('main.py') @@ -295,7 +300,9 @@ class DAEMON(): import rigdummy as rig hamlib = rig.radio() - hamlib.open_rig(devicename=devicename, deviceport=deviceport, hamlib_ptt_type=pttprotocol, serialspeed=serialspeed, pttport=pttport, data_bits=data_bits, stop_bits=stop_bits, handshake=handshake, rigctld_ip=rigctld_ip, rigctld_port = rigctld_port) + hamlib.open_rig(devicename=devicename, deviceport=deviceport, hamlib_ptt_type=pttprotocol, + serialspeed=serialspeed, pttport=pttport, data_bits=data_bits, stop_bits=stop_bits, + handshake=handshake, rigctld_ip=rigctld_ip, rigctld_port = rigctld_port) hamlib_version = rig.hamlib_version @@ -303,13 +310,13 @@ class DAEMON(): pttstate = hamlib.get_ptt() if pttstate: - structlog.get_logger("structlog").info("[DMN] Hamlib PTT", status = 'SUCCESS') + structlog.get_logger("structlog").info("[DMN] Hamlib PTT", status='SUCCESS') response = {'command': 'test_hamlib', 'result': 'SUCCESS'} elif not pttstate: - structlog.get_logger("structlog").warning("[DMN] Hamlib PTT", status = 'NO SUCCESS') + structlog.get_logger("structlog").warning("[DMN] Hamlib PTT", status='NO SUCCESS') response = {'command': 'test_hamlib', 'result': 'NOSUCCESS'} else: - structlog.get_logger("structlog").error("[DMN] Hamlib PTT", status = 'FAILED') + structlog.get_logger("structlog").error("[DMN] Hamlib PTT", status='FAILED') response = {'command': 'test_hamlib', 'result': 'FAILED'} hamlib.set_ptt(False) @@ -319,7 +326,8 @@ class DAEMON(): sock.SOCKET_QUEUE.put(jsondata) except Exception as e: - print(e) + structlog.get_logger("structlog").error("[DMN] worker: Exception: ", e=e) + # print(e) if __name__ == '__main__': # we need to run this on windows for multiprocessing support @@ -327,7 +335,7 @@ if __name__ == '__main__': # --------------------------------------------GET PARAMETER INPUTS PARSER = argparse.ArgumentParser(description='FreeDATA Daemon') - PARSER.add_argument('--port', dest="socket_port",default=3001, help="Socket port in the range of 1024-65536", type=int) + PARSER.add_argument('--port', dest="socket_port", default=3001, help="Socket port in the range of 1024-65536", type=int) ARGS = PARSER.parse_args() static.DAEMONPORT = ARGS.socket_port @@ -339,14 +347,14 @@ if __name__ == '__main__': if sys.platform == 'darwin': logging_path = os.getenv("HOME") + '/Library/' + 'Application Support/' + 'FreeDATA/' + 'daemon' - if sys.platform == 'win32' or sys.platform == 'win64': + if sys.platform in ['win32', 'win64']: logging_path = os.getenv('APPDATA') + '/' + 'FreeDATA/' + 'daemon' if not os.path.exists(logging_path): os.makedirs(logging_path) log_handler.setup_logging(logging_path) - except: - structlog.get_logger("structlog").error("[DMN] logger init error") + except Exception as e: + structlog.get_logger("structlog").error("[DMN] logger init error", exception=e) try: structlog.get_logger("structlog").info("[DMN] Starting TCP/IP socket", port=static.DAEMONPORT) @@ -359,8 +367,7 @@ if __name__ == '__main__': except Exception as e: structlog.get_logger("structlog").error("[DMN] Starting TCP/IP socket failed", port=static.DAEMONPORT, e=e) - os._exit(1) - + sys.exit(1) daemon = DAEMON() structlog.get_logger("structlog").info("[DMN] Starting FreeDATA Daemon", author="DJ2LS", year="2022", version=static.VERSION) diff --git a/tnc/data_handler.py b/tnc/data_handler.py index b8200366..2c2c1594 100644 --- a/tnc/data_handler.py +++ b/tnc/data_handler.py @@ -5,23 +5,30 @@ Created on Sun Dec 27 20:43:40 2020 @author: DJ2LS """ +# pylint: disable=invalid-name, line-too-long, c-extension-no-member +# pylint: disable=import-outside-toplevel + +import asyncio +import base64 +import logging +import queue import sys -import logging, structlog, log_handler import threading import time -from random import randrange -import asyncio -import zlib -import ujson as json -import static -import modem -import helpers -import codec2 -import queue -import sock import uuid -import base64 +import zlib +from random import randrange + import numpy as np +import structlog +import ujson as json + +import codec2 +import helpers +import log_handler +import modem +import sock +import static TESTMODE = False @@ -30,8 +37,7 @@ DATA_QUEUE_RECEIVED = queue.Queue() class DATA(): - """ """ - + """ Terminal Node Controller for FreeDATA """ def __init__(self): self.mycallsign = static.MYCALLSIGN # initial callsign. Will be overwritten later @@ -47,29 +53,30 @@ class DATA(): self.transmission_uuid = '' - self.received_mycall_crc = b'' # received my callsign crc if we received a crc for another ssid + self.received_mycall_crc = b'' # Received my callsign crc if we received a crc for another ssid - self.data_channel_last_received = 0.0 # time of last "live sign" of a frame - self.burst_ack_snr = 0 # SNR from received ack frames - self.burst_ack = False # if we received an acknowledge frame for a burst - self.data_frame_ack_received = False # if we received an acknowledge frame for a data frame - self.rpt_request_received = False # if we received an request for repeater frames - self.rpt_request_buffer = [] # requested frames, saved in a list - self.rx_start_of_transmission = 0 # time of transmission start - self.data_frame_bof = b'BOF' # 2 bytes for the BOF End of File indicator in a data frame - self.data_frame_eof = b'EOF' # 2 bytes for the EOF End of File indicator in a data frame + self.data_channel_last_received = 0.0 # time of last "live sign" of a frame + self.burst_ack_snr = 0 # SNR from received ack frames + self.burst_ack = False # if we received an acknowledge frame for a burst + self.data_frame_ack_received = False # if we received an acknowledge frame for a data frame + self.rpt_request_received = False # if we received an request for repeater frames + self.rpt_request_buffer = [] # requested frames, saved in a list + self.rx_start_of_transmission = 0 # time of transmission start + self.data_frame_bof = b'BOF' # 2 bytes for the BOF End of File indicator in a data frame + self.data_frame_eof = b'EOF' # 2 bytes for the EOF End of File indicator in a data frame self.rx_n_max_retries_per_burst = 50 self.n_retries_per_burst = 0 - self.received_low_bandwith_mode = False # indicator if we recevied a low bandwith mode channel ope ner + self.received_low_bandwith_mode = False # indicator if we recevied a low bandwith mode channel opener self.data_channel_max_retries = 5 + self.datachannel_timeout = False - self.mode_list_low_bw = [14,12] - self.time_list_low_bw = [3,7] + self.mode_list_low_bw = [14, 12] + self.time_list_low_bw = [3, 7] - self.mode_list_high_bw = [14,12,10] #201 = FSK mode list of available modes, each mode will be used 2times per speed level + self.mode_list_high_bw = [14, 12, 10] #201 = FSK mode list of available modes, each mode will be used 2times per speed level self.time_list_high_bw = [3, 7, 8, 30] # list for time to wait for correspinding mode in seconds # mode list for selecting between low bandwith ( 500Hz ) and normal modes with higher bandwith @@ -92,7 +99,7 @@ class DATA(): self.rx_frame_bof_received = False self.rx_frame_eof_received = False - self.transmission_timeout = 360 # transmission timeout in seconds + self.transmission_timeout = 360 # transmission timeout in seconds worker_thread_transmit = threading.Thread(target=self.worker_transmit, name="worker thread transmit",daemon=True) worker_thread_transmit.start() @@ -115,26 +122,21 @@ class DATA(): """ """ while True: data = self.data_queue_transmit.get() - # [0] Command + # [0] == Command if data[0] == 'CQ': - # [0] CQ self.transmit_cq() elif data[0] == 'STOP': - # [0] STOP self.stop_transmission() elif data[0] == 'PING': - # [0] PING # [1] dxcallsign self.transmit_ping(data[1]) elif data[0] == 'BEACON': - # [0] BEACON # [1] INTERVAL int # [2] STATE bool - #self.run_beacon(data[1]) if data[2]: self.beacon_interval = data[1] static.BEACON_STATE = True @@ -142,7 +144,6 @@ class DATA(): static.BEACON_STATE = False elif data[0] == 'ARQ_RAW': - # [0] ARQ_RAW # [1] DATA_OUT bytes # [2] MODE int # [3] N_FRAMES_PER_BURST int @@ -151,20 +152,19 @@ class DATA(): self.open_dc_and_transmit(data[1], data[2], data[3], data[4], data[5]) elif data[0] == 'CONNECT': - # [0] DX CALLSIGN + # [1] DX CALLSIGN self.arq_session_handler(data[1]) elif data[0] == 'DISCONNECT': - # [0] DX CALLSIGN + # [1] DX CALLSIGN self.close_session() elif data[0] == 'SEND_TEST_FRAME': - # [0] DX CALLSIGN + # [1] DX CALLSIGN self.send_test_frame() else: - # wrong command - print(f"wrong command {data}") - pass + structlog.get_logger("structlog").error("[TNC] worker_transmit: received invalid command:", data=data) + # print(f"Invalid command: {data}") def worker_receive(self): """ """ @@ -173,9 +173,9 @@ class DATA(): # [0] bytes # [1] freedv instance # [2] bytes_per_frame - self.process_data(bytes_out=data[0],freedv=data[1],bytes_per_frame=data[2]) + self.process_data(bytes_out=data[0], freedv=data[1], bytes_per_frame=data[2]) - def process_data(self, bytes_out, freedv, bytes_per_frame): + def process_data(self, bytes_out, freedv, bytes_per_frame: int): """ Args: @@ -186,14 +186,14 @@ class DATA(): Returns: """ + structlog.get_logger("structlog").debug("[TNC] process_data:", n_retries_per_burst=self.n_retries_per_burst) + # print(f"self.n_retries_per_burst = {self.n_retries_per_burst}") + # forward data only if broadcast or we are the receiver # bytes_out[1:4] == callsign check for signalling frames, # bytes_out[2:5] == transmission # we could also create an own function, which returns True. frametype = int.from_bytes(bytes(bytes_out[:1]), "big") - - print(f"self.n_retries_per_burst = {self.n_retries_per_burst}") - _valid1, _ = helpers.check_callsign(self.mycallsign, bytes(bytes_out[1:4])) _valid2, _ = helpers.check_callsign(self.mycallsign, bytes(bytes_out[2:5])) if _valid1 or _valid2 or frametype in [200, 201, 210, 250]: @@ -205,7 +205,7 @@ class DATA(): if 50 >= frametype >= 10: # get snr of received data #snr = self.calculate_snr(freedv) - # we need to find a wy fixing this because of mooving to class system this isn't working anymore + # we need to find a way of fixing this because after moving to class system this doesn't work anymore snr = static.SNR structlog.get_logger("structlog").debug("[TNC] RX SNR", snr=snr) # send payload data to arq checker without CRC16 @@ -213,98 +213,99 @@ class DATA(): # if we received the last frame of a burst or the last remaining rpt frame, do a modem unsync #if static.RX_BURST_BUFFER.count(None) <= 1 or (frame+1) == n_frames_per_burst: - # structlog.get_logger("structlog").debug(f"LAST FRAME OF BURST --> UNSYNC {frame+1}/{n_frames_per_burst}") + # structlog.get_logger("structlog").debug(f"[TNC] LAST FRAME OF BURST --> UNSYNC {frame+1}/{n_frames_per_burst}") # self.c_lib.freedv_set_sync(freedv, 0) # BURST ACK elif frametype == 60: - structlog.get_logger("structlog").debug("ACK RECEIVED....") + structlog.get_logger("structlog").debug("[TNC] ACK RECEIVED....") self.burst_ack_received(bytes_out[:-2]) # FRAME ACK elif frametype == 61: - structlog.get_logger("structlog").debug("FRAME ACK RECEIVED....") + structlog.get_logger("structlog").debug("[TNC] FRAME ACK RECEIVED....") self.frame_ack_received() # FRAME RPT elif frametype == 62: - structlog.get_logger("structlog").debug("REPEAT REQUEST RECEIVED....") + structlog.get_logger("structlog").debug("[TNC] REPEAT REQUEST RECEIVED....") self.burst_rpt_received(bytes_out[:-2]) # FRAME NACK elif frametype == 63: - structlog.get_logger("structlog").debug("FRAME NACK RECEIVED....") + structlog.get_logger("structlog").debug("[TNC] FRAME NACK RECEIVED....") self.frame_nack_received(bytes_out[:-2]) # BURST NACK elif frametype == 64: - structlog.get_logger("structlog").debug("BURST NACK RECEIVED....") + structlog.get_logger("structlog").debug("[TNC] BURST NACK RECEIVED....") self.burst_nack_received(bytes_out[:-2]) # CQ FRAME elif frametype == 200: - structlog.get_logger("structlog").debug("CQ RECEIVED....") + structlog.get_logger("structlog").debug("[TNC] CQ RECEIVED....") self.received_cq(bytes_out[:-2]) # QRV FRAME elif frametype == 201: - structlog.get_logger("structlog").debug("QRV RECEIVED!") + structlog.get_logger("structlog").debug("[TNC] QRV RECEIVED....") self.received_qrv(bytes_out[:-2]) # PING FRAME elif frametype == 210: - structlog.get_logger("structlog").debug("PING RECEIVED....") + structlog.get_logger("structlog").debug("[TNC] PING RECEIVED....") self.received_ping(bytes_out[:-2]) # PING ACK elif frametype == 211: - structlog.get_logger("structlog").debug("PING ACK RECEIVED....") + structlog.get_logger("structlog").debug("[TNC] PING ACK RECEIVED....") self.received_ping_ack(bytes_out[:-2]) # SESSION OPENER elif frametype == 221: - structlog.get_logger("structlog").debug("OPEN SESSION RECEIVED....") + structlog.get_logger("structlog").debug("[TNC] OPEN SESSION RECEIVED....") self.received_session_opener(bytes_out[:-2]) # SESSION HEARTBEAT elif frametype == 222: - structlog.get_logger("structlog").debug("SESSION HEARTBEAT RECEIVED....") + structlog.get_logger("structlog").debug("[TNC] SESSION HEARTBEAT RECEIVED....") self.received_session_heartbeat(bytes_out[:-2]) # SESSION CLOSE elif frametype == 223: - structlog.get_logger("structlog").debug("CLOSE ARQ SESSION RECEIVED....") + structlog.get_logger("structlog").debug("[TNC] CLOSE ARQ SESSION RECEIVED....") self.received_session_close(bytes_out[:-2]) # ARQ FILE TRANSFER RECEIVED! - elif frametype == 225 or frametype == 227: - structlog.get_logger("structlog").debug("ARQ arq_received_data_channel_opener") + elif frametype in [225, 227]: + structlog.get_logger("structlog").debug("[TNC] ARQ arq_received_data_channel_opener") self.arq_received_data_channel_opener(bytes_out[:-2]) # ARQ CHANNEL IS OPENED - elif frametype == 226 or frametype == 228: - structlog.get_logger("structlog").debug("ARQ arq_received_channel_is_open") + elif frametype in [226, 228]: + structlog.get_logger("structlog").debug("[TNC] ARQ arq_received_channel_is_open") self.arq_received_channel_is_open(bytes_out[:-2]) # ARQ MANUAL MODE TRANSMISSION elif 230 <= frametype <= 240 : - structlog.get_logger("structlog").debug("ARQ manual mode ") + structlog.get_logger("structlog").debug("[TNC] ARQ manual mode") self.arq_received_data_channel_opener(bytes_out[:-2]) # ARQ STOP TRANSMISSION elif frametype == 249: - structlog.get_logger("structlog").debug("ARQ received stop transmission") + structlog.get_logger("structlog").debug("[TNC] ARQ received stop transmission") self.received_stop_transmission() # this is outdated and we may remove it elif frametype == 250: - structlog.get_logger("structlog").debug("BEACON RECEIVED") + structlog.get_logger("structlog").debug("[TNC] BEACON RECEIVED") self.received_beacon(bytes_out[:-2]) # TESTFRAMES elif frametype == 255: - structlog.get_logger("structlog").debug("TESTFRAME RECEIVED", frame=bytes_out[:]) + structlog.get_logger("structlog").debug("[TNC] TESTFRAME RECEIVED", frame=bytes_out[:]) + # Unknown frame type else: structlog.get_logger("structlog").warning("[TNC] ARQ - other frame type", frametype=frametype) @@ -312,9 +313,112 @@ class DATA(): # for debugging purposes to receive all data structlog.get_logger("structlog").debug("[TNC] Unknown frame received", frame=bytes_out[:-2]) - def arq_data_received(self, data_in:bytes, bytes_per_frame:int, snr:int, freedv): + def enqueue_frame_for_tx(self, frame_to_tx: bytearray, c2_mode=14, copies=1, repeat_delay=0): """ + Send (transmit) supplied frame to TNC + :param frame_to_tx: Frame data to send + :type frame_to_tx: bytearray + :param c2_mode: Codec2 mode to use, defaults to "datac0" (14) + :type c2_mode: str, optional + :param copies: Number of frame copies to send, defaults to 1 + :type copies: int, optional + :param repeat_delay: Delay time before sending repeat frame, defaults to 0 + :type repeat_delay: int, optional + """ + structlog.get_logger("structlog").debug("[TNC] enqueue_frame_for_tx", c2_mode=c2_mode) + if isinstance(c2_mode, str): + _mode = codec2.freedv_get_mode_value_by_name(c2_mode.lower()) + else: + _mode = int(c2_mode) + static.TRANSMITTING = True + modem.MODEM_TRANSMIT_QUEUE.put([_mode, copies, repeat_delay, [frame_to_tx]]) + # Wait while transmitting + while static.TRANSMITTING: + time.sleep(0.01) + + def send_burst_ack_frame(self, snr): + """ Build and send ACK frame for burst DATA frame """ + ack_frame = bytearray(14) + ack_frame[:1] = bytes([60]) + ack_frame[1:4] = static.DXCALLSIGN_CRC + ack_frame[4:7] = static.MYCALLSIGN_CRC + ack_frame[7:8] = bytes([int(snr)]) + ack_frame[8:9] = bytes([int(self.speed_level)]) + + # Transmit frame + self.enqueue_frame_for_tx(ack_frame) + + def send_data_ack_frame(self, snr): + """ Build and send ACK frame for received DATA frame """ + ack_frame = bytearray(14) + ack_frame[:1] = bytes([61]) + ack_frame[1:4] = static.DXCALLSIGN_CRC + ack_frame[4:7] = static.MYCALLSIGN_CRC + ack_frame[7:8] = bytes([int(snr)]) + ack_frame[8:9] = bytes([int(self.speed_level)]) + + # Transmit frame + self.enqueue_frame_for_tx(ack_frame, copies=3, repeat_delay=100) + + def send_retransmit_request_frame(self, freedv): + # check where a None is in our burst buffer and do frame+1, beacuse lists start at 0 + missing_frames = [frame + 1 for frame, element in enumerate(static.RX_BURST_BUFFER) if element is None] + + # set n frames per burst to modem + # this is an idea so its not getting lost.... + # we need to work on this + codec2.api.freedv_set_frames_per_burst(freedv,len(missing_frames)) + + # TODO: Trim `missing_frames` bytesarray to [7:13] (6) frames, if it's larger. + + # then create a repeat frame + rpt_frame = bytearray(14) + rpt_frame[:1] = bytes([62]) + rpt_frame[1:4] = static.DXCALLSIGN_CRC + rpt_frame[4:7] = static.MYCALLSIGN_CRC + rpt_frame[7:13] = missing_frames + + structlog.get_logger("structlog").info("[TNC] ARQ | RX | Requesting", frames=missing_frames) + # Transmit frame + self.enqueue_frame_for_tx(rpt_frame) + + def send_burst_nack_frame(self, snr=0): + """ Build and send NACK frame for received DATA frame """ + nack_frame = bytearray(14) + nack_frame[:1] = bytes([63]) + nack_frame[1:4] = static.DXCALLSIGN_CRC + nack_frame[4:7] = static.MYCALLSIGN_CRC + nack_frame[7:8] = bytes([int(snr)]) + nack_frame[8:9] = bytes([int(self.speed_level)]) + + # TRANSMIT NACK FRAME FOR BURST + self.enqueue_frame_for_tx(nack_frame) + + def send_burst_nack_frame_watchdog(self, snr=0): + """ Build and send NACK frame for watchdog timeout """ + nack_frame = bytearray(14) + nack_frame[:1] = bytes([64]) + nack_frame[1:4] = static.DXCALLSIGN_CRC + nack_frame[4:7] = static.MYCALLSIGN_CRC + nack_frame[7:8] = bytes([int(snr)]) + nack_frame[8:9] = bytes([int(self.speed_level)]) + + # TRANSMIT NACK FRAME FOR BURST + self.enqueue_frame_for_tx(nack_frame) + + def send_disconnect_frame(self): + """ Build and send a disconnect frame """ + disconnection_frame = bytearray(14) + disconnection_frame[:1] = bytes([223]) + disconnection_frame[1:4] = static.DXCALLSIGN_CRC + disconnection_frame[4:7] = static.MYCALLSIGN_CRC + disconnection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign) + + self.enqueue_frame_for_tx(disconnection_frame, copies=5, repeat_delay=250) + + def arq_data_received(self, data_in: bytes, bytes_per_frame: int, snr: int, freedv): + """ Args: data_in:bytes: bytes_per_frame:int: @@ -322,15 +426,12 @@ class DATA(): freedv: Returns: - """ data_in = bytes(data_in) # get received crc for different mycall ssids self.received_mycall_crc = data_in[2:5] - global TESTMODE - # check if callsign ssid override valid, mycallsign = helpers.check_callsign(self.mycallsign, self.received_mycall_crc) if not valid: @@ -353,34 +454,31 @@ class DATA(): self.data_channel_last_received = int(time.time()) # get some important data from the frame - RX_N_FRAME_OF_BURST = int.from_bytes(bytes(data_in[:1]), "big") - 10 # get number of burst frame - RX_N_FRAMES_PER_BURST = int.from_bytes(bytes(data_in[1:2]), "big") # get number of bursts from received frame + RX_N_FRAME_OF_BURST = int.from_bytes(bytes(data_in[:1]), "big") - 10 # get number of burst frame + RX_N_FRAMES_PER_BURST = int.from_bytes(bytes(data_in[1:2]), "big") # get number of bursts from received frame - ''' - The RX burst buffer needs to have a fixed length filled with "None". We need this later for counting the "Nones" - check if burst buffer has expected length else create it - ''' + # The RX burst buffer needs to have a fixed length filled with "None". + # We need this later for counting the "Nones" to detect missing data. + # Check if burst buffer has expected length else create it if len(static.RX_BURST_BUFFER) != RX_N_FRAMES_PER_BURST: static.RX_BURST_BUFFER = [None] * RX_N_FRAMES_PER_BURST - # append data to rx burst buffer + # Append data to rx burst buffer static.RX_BURST_BUFFER[RX_N_FRAME_OF_BURST] = data_in[8:] # [frame_type][n_frames_per_burst][CRC24][CRC24] structlog.get_logger("structlog").debug("[TNC] static.RX_BURST_BUFFER", buffer=static.RX_BURST_BUFFER) - helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) + helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', snr, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) - ''' - check if we received all frames per burst by checking if burst buffer has no more "Nones" - this is the ideal case because we received all data - ''' - if not None in static.RX_BURST_BUFFER: + # Check if we received all frames in the burst by checking if burst buffer has no more "Nones" + # This is the ideal case because we received all data + if None not in static.RX_BURST_BUFFER: # then iterate through burst buffer and stick the burst together # the temp burst buffer is needed for checking, if we already recevied data temp_burst_buffer = b'' - for i in range(0,len(static.RX_BURST_BUFFER)): + for value in static.RX_BURST_BUFFER: #static.RX_FRAME_BUFFER += static.RX_BURST_BUFFER[i] - temp_burst_buffer += static.RX_BURST_BUFFER[i] + temp_burst_buffer += bytes(value) # if frame buffer ends not with the current frame, we are going to append new data # if data already exists, we received the frame correctly, but the ACK frame didnt receive its destination (ISS) @@ -388,9 +486,10 @@ class DATA(): structlog.get_logger("structlog").info("[TNC] ARQ | RX | Frame already received - sending ACK again") static.RX_BURST_BUFFER = [] - # here we are going to search for our data in the last received bytes - # this increases chance we are not loosing the entire frame in case of signalling frame loss else: + # Here we are going to search for our data in the last received bytes. + # This reduces the chance we will lose the entire frame in the case of signalling frame loss + # static.RX_FRAME_BUFFER --> exisitng data # temp_burst_buffer --> new data # search_area --> area where we want to search @@ -411,7 +510,9 @@ class DATA(): structlog.get_logger("structlog").debug("[TNC] ARQ | RX | appending data to buffer") # lets check if we didnt receive a BOF and EOF yet to avoid sending ack frames if we already received all data - if not self.rx_frame_bof_received and not self.rx_frame_eof_received and data_in.find(self.data_frame_eof) < 0: + if (not self.rx_frame_bof_received and + not self.rx_frame_eof_received and + data_in.find(self.data_frame_eof) < 0): self.frame_received_counter += 1 if self.frame_received_counter >= 2: @@ -421,65 +522,31 @@ class DATA(): self.speed_level = len(self.mode_list) - 1 static.ARQ_SPEED_LEVEL = self.speed_level - # updated modes we are listening to + # Update modes we are listening to self.set_listening_modes(self.mode_list[self.speed_level]) - # create an ack frame - ack_frame = bytearray(14) - ack_frame[:1] = bytes([60]) - ack_frame[1:4] = static.DXCALLSIGN_CRC - ack_frame[4:7] = static.MYCALLSIGN_CRC - ack_frame[7:8] = bytes([int(static.SNR)]) - ack_frame[8:9] = bytes([int(self.speed_level)]) - # and transmit it - txbuffer = [ack_frame] + # Create and send ACK frame structlog.get_logger("structlog").info("[TNC] ARQ | RX | SENDING ACK") - static.TRANSMITTING = True - modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) - # wait while transmitting - while static.TRANSMITTING: - time.sleep(0.01) - # reset n retries per burst counter + self.send_burst_ack_frame(snr) + + # Reset n retries per burst counter self.n_retries_per_burst = 0 # calculate statistics self.calculate_transfer_rate_rx(self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)) - # check if we received last frame of burst and we have "Nones" in our rx buffer - # this is an indicator for missed frames. - # with this way of doing this, we always MUST receive the last frame of a burst otherwise the entire - # burst is lost elif RX_N_FRAME_OF_BURST == RX_N_FRAMES_PER_BURST -1: - # check where a None is in our burst buffer and do frame+1, beacuse lists start at 0 - missing_frames = [(frame+1) for frame, element in enumerate(static.RX_BURST_BUFFER) if element == None] - - structlog.get_logger("structlog").debug("all frames per burst received", frame=RX_N_FRAME_OF_BURST, frames=RX_N_FRAMES_PER_BURST) - - # set n frames per burst to modem - # this is an idea so its not getting lost.... - # we need to work on this - codec2.api.freedv_set_frames_per_burst(freedv,len(missing_frames)) - - # then create a repeat frame - rpt_frame = bytearray(14) - rpt_frame[:1] = bytes([62]) - rpt_frame[1:4] = static.DXCALLSIGN_CRC - rpt_frame[4:7] = static.MYCALLSIGN_CRC - rpt_frame[7:13] = missing_frames - - # and transmit it - txbuffer = [rpt_frame] - structlog.get_logger("structlog").info("[TNC] ARQ | RX | Requesting", frames=missing_frames) - static.TRANSMITTING = True - modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) - # wait while transmitting - while static.TRANSMITTING: - time.sleep(0.01) + # We have "Nones" in our rx buffer, + # Check if we received last frame of burst - this is an indicator for missed frames. + # With this way of doing this, we always MUST receive the last frame of a burst otherwise the entire + # burst is lost + structlog.get_logger("structlog").debug("[TNC] all frames in burst received:", frame=RX_N_FRAME_OF_BURST, frames=RX_N_FRAMES_PER_BURST) + self.send_retransmit_request_frame(freedv) self.calculate_transfer_rate_rx(self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)) - # we should never reach this point + # Should never reach this point else: - structlog.get_logger("structlog").error("we shouldnt reach this point...", frame=RX_N_FRAME_OF_BURST, frames=RX_N_FRAMES_PER_BURST) + structlog.get_logger("structlog").error("[TNC] data_handler: Should not reach this point...", frame=RX_N_FRAME_OF_BURST, frames=RX_N_FRAMES_PER_BURST) # We have a BOF and EOF flag in our data. If we received both we received our frame. # In case of loosing data but we received already a BOF and EOF we need to make sure, we @@ -498,28 +565,29 @@ class DATA(): static.ARQ_COMPRESSION_FACTOR = compression_factor / 10 self.calculate_transfer_rate_rx(self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)) - if bof_position >= 0 and eof_position > 0 and not None in static.RX_BURST_BUFFER: - print(f"bof_position {bof_position} / eof_position {eof_position}") + if bof_position >= 0 and eof_position > 0 and None not in static.RX_BURST_BUFFER: + structlog.get_logger("structlog").debug("[TNC] arq_data_received:", bof_position=bof_position, eof_position=eof_position) + # print(f"bof_position {bof_position} / eof_position {eof_position}") self.rx_frame_bof_received = True self.rx_frame_eof_received = True - #now extract raw data from buffer + # Extract raw data from buffer payload = static.RX_FRAME_BUFFER[bof_position+len(self.data_frame_bof):eof_position] - # get the data frame crc + # Get the data frame crc data_frame_crc = payload[:4] #0:4 4bytes + # Get the data frame length frame_length = int.from_bytes(payload[4:8], "big") #4:8 4bytes static.TOTAL_BYTES = frame_length # 8:9 = compression factor data_frame = payload[9:] - data_frame_crc_received = helpers.get_crc_32(data_frame) - # check if data_frame_crc is equal with received crc + # Check if data_frame_crc is equal with received crc if data_frame_crc == data_frame_crc_received: structlog.get_logger("structlog").info("[TNC] ARQ | RX | DATA FRAME SUCESSFULLY RECEIVED") - # decompression + # Decompress the data frame data_frame_decompressed = zlib.decompress(data_frame) static.ARQ_COMPRESSION_FACTOR = len(data_frame_decompressed) / len(data_frame) data_frame = data_frame_decompressed @@ -535,54 +603,30 @@ class DATA(): self.arq_cleanup() return - base64_data = base64.b64encode(data_frame) - base64_data = base64_data.decode("utf-8") + # Re-code data_frame in base64, UTF-8 for JSON UI communication. + base64_data = base64.b64encode(data_frame).decode("utf-8") static.RX_BUFFER.append([uniqueid, timestamp, static.DXCALLSIGN, static.DXGRID, base64_data]) jsondata = {"arq":"received", "uuid" : uniqueid, "timestamp": timestamp, "mycallsign" : str(mycallsign, 'utf-8'), "dxcallsign": str(static.DXCALLSIGN, 'utf-8'), "dxgrid": str(static.DXGRID, 'utf-8'), "data": base64_data} json_data_out = json.dumps(jsondata) - print(jsondata) + structlog.get_logger("structlog").debug("[TNC] arq_data_received:", jsondata=jsondata) + # print(jsondata) sock.SOCKET_QUEUE.put(json_data_out) static.INFO.append("ARQ;RECEIVING;SUCCESS") - # BUILDING ACK FRAME FOR DATA FRAME - ack_frame = bytearray(14) - ack_frame[:1] = bytes([61]) - ack_frame[1:4] = static.DXCALLSIGN_CRC - ack_frame[4:7] = static.MYCALLSIGN_CRC - ack_frame[7:8] = bytes([int(snr)]) - ack_frame[8:9] = bytes([int(self.speed_level)]) - # TRANSMIT ACK FRAME FOR BURST - structlog.get_logger("structlog").info("[TNC] ARQ | RX | SENDING DATA FRAME ACK", snr=static.SNR, crc=data_frame_crc.hex()) - txbuffer = [ack_frame] - static.TRANSMITTING = True - modem.MODEM_TRANSMIT_QUEUE.put([14,3,100,txbuffer]) - # wait while transmitting - while static.TRANSMITTING: - time.sleep(0.01) + structlog.get_logger("structlog").info("[TNC] ARQ | RX | SENDING DATA FRAME ACK", snr=snr, crc=data_frame_crc.hex()) + self.send_data_ack_frame(snr) # update our statistics AFTER the frame ACK self.calculate_transfer_rate_rx(self.rx_start_of_transmission, len(static.RX_FRAME_BUFFER)) - structlog.get_logger("structlog").info("[TNC] | RX | DATACHANNEL [" + str(self.mycallsign, 'utf-8') + "]<< >>[" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR) + structlog.get_logger("structlog").info("[TNC] | RX | DATACHANNEL [" + + str(self.mycallsign, 'utf-8') + "]<< >>[" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=snr) else: static.INFO.append("ARQ;RECEIVING;FAILED") structlog.get_logger("structlog").warning("[TNC] ARQ | RX | DATA FRAME NOT SUCESSFULLY RECEIVED!", e="wrong crc", expected=data_frame_crc, received=data_frame_crc_received, overflows=static.BUFFER_OVERFLOW_COUNTER) - # BUILDING NACK FRAME FOR DATA FRAME - nack_frame = bytearray(14) - nack_frame[:1] = bytes([63]) - nack_frame[1:4] = static.DXCALLSIGN_CRC - nack_frame[4:7] = static.MYCALLSIGN_CRC - nack_frame[7:8] = bytes([int(snr)]) - nack_frame[8:9] = bytes([int(self.speed_level)]) - - # TRANSMIT NACK FRAME FOR BURST - txbuffer = [nack_frame] - static.TRANSMITTING = True - modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) - # wait while transmitting - while static.TRANSMITTING: - time.sleep(0.01) + structlog.get_logger("structlog").info("[TNC] ARQ | RX | Sending NACK") + self.send_burst_nack_frame(snr) # update session timeout self.arq_session_last_received = int(time.time()) # we need to update our timeout timestamp @@ -603,8 +647,6 @@ class DATA(): Returns: """ - global TESTMODE - self.arq_file_transfer = True self.speed_level = len(self.mode_list) - 1 # speed level for selecting mode @@ -633,7 +675,7 @@ class DATA(): structlog.get_logger("structlog").info("[TNC] | TX | DATACHANNEL", mode=mode, Bytes=static.TOTAL_BYTES) - # compression + # Compress data frame data_frame_compressed = zlib.compress(data_out) compression_factor = len(data_out) / len(data_frame_compressed) static.ARQ_COMPRESSION_FACTOR = np.clip(compression_factor, 0, 255) @@ -641,32 +683,31 @@ class DATA(): data_out = data_frame_compressed - # reset statistics + # Reset data transfer statistics tx_start_of_transmission = time.time() self.calculate_transfer_rate_tx(tx_start_of_transmission, 0, len(data_out)) - # append a crc and beginn and end of file indicators + # Append a crc and the begin and end of file indicators frame_payload_crc = helpers.get_crc_32(data_out) - structlog.get_logger("structlog").debug("frame payload crc", crc=frame_payload_crc) + structlog.get_logger("structlog").debug("[TNC] frame payload CRC:", crc=frame_payload_crc) # data_out = self.data_frame_bof + frame_payload_crc + data_out + self.data_frame_eof data_out = self.data_frame_bof + frame_payload_crc + frame_total_size + compression_factor + data_out + self.data_frame_eof #initial bufferposition is 0 - bufferposition = 0 + bufferposition = bufferposition_end = 0 # iterate through data out buffer while bufferposition < len(data_out) and not self.data_frame_ack_received and static.ARQ_STATE: # we have TX_N_MAX_RETRIES_PER_BURST attempts for sending a burst - for self.tx_n_retry_of_burst in range(0,TX_N_MAX_RETRIES_PER_BURST): + for self.tx_n_retry_of_burst in range(TX_N_MAX_RETRIES_PER_BURST): # AUTO MODE SELECTION # mode 255 == AUTO MODE # force usage of selected mode if mode != 255: data_mode = mode - - structlog.get_logger("structlog").debug("FIXED MODE", mode=data_mode) + structlog.get_logger("structlog").debug("[TNC] FIXED MODE:", mode=data_mode) else: # we are doing a modulo check of transmission retries of the actual burst # every 2nd retry which failes, decreases speedlevel by 1. @@ -685,13 +726,15 @@ class DATA(): # if self.speed_level >= len(self.mode_list)-1: # self.speed_level = len(self.mode_list)-1 + # if speed level is greater than our available modes, set speed level to maximum = lenght of mode list -1 + # if speed level is greater than our available modes, set speed level to maximum = lenght of mode list -1 if self.speed_level >= len(self.mode_list): self.speed_level = len(self.mode_list) - 1 static.ARQ_SPEED_LEVEL = self.speed_level data_mode = self.mode_list[self.speed_level] - structlog.get_logger("structlog").debug("Speed-level:", level=self.speed_level, retry=self.tx_n_retry_of_burst, mode=data_mode) + structlog.get_logger("structlog").debug("[TNC] Speed-level:", level=self.speed_level, retry=self.tx_n_retry_of_burst, mode=data_mode) # payload information payload_per_frame = modem.get_bytes_per_frame(data_mode) -2 @@ -700,10 +743,11 @@ class DATA(): tempbuffer = [] # append data frames with TX_N_FRAMES_PER_BURST to tempbuffer - # this part ineeds to a completly rewrite! - # TX_NF_RAMES_PER_BURST = 1 is working - arqheader = bytearray() - arqheader[:1] = bytes([10]) #bytes([10 + i]) + # TODO: this part needs a complete rewrite! + # TX_N_FRAMES_PER_BURST = 1 is working + + arqheader = bytearray() + arqheader[:1] = bytes([10]) #bytes([10 + i]) arqheader[1:2] = bytes([TX_N_FRAMES_PER_BURST]) arqheader[2:5] = static.DXCALLSIGN_CRC arqheader[5:8] = static.MYCALLSIGN_CRC @@ -728,7 +772,7 @@ class DATA(): # append frame to tempbuffer for transmission tempbuffer.append(frame) - structlog.get_logger("structlog").debug("[TNC] tempbuffer", tempbuffer=tempbuffer) + structlog.get_logger("structlog").debug("[TNC] tempbuffer:", tempbuffer=tempbuffer) structlog.get_logger("structlog").info("[TNC] ARQ | TX | FRAMES", mode=data_mode, fpb=TX_N_FRAMES_PER_BURST, retry=self.tx_n_retry_of_burst) # we need to set our TRANSMITTING flag before we are adding an object the transmit queue @@ -747,7 +791,9 @@ class DATA(): time.sleep(0.01) ''' #burstacktimeout = time.time() + BURST_ACK_TIMEOUT_SECONDS + 100 - while not self.burst_ack and not self.burst_nack and not self.rpt_request_received and not self.data_frame_ack_received and static.ARQ_STATE: + while (static.ARQ_STATE and not + (self.burst_ack or self.burst_nack or + self.rpt_request_received or self.data_frame_ack_received)): time.sleep(0.01) # once we received a burst ack, reset its state and break the RETRIES loop @@ -774,7 +820,7 @@ class DATA(): self.calculate_transfer_rate_tx(tx_start_of_transmission, bufferposition_end, len(data_out)) # NEXT ATTEMPT - structlog.get_logger("structlog").debug("ATTEMPT", retry=self.tx_n_retry_of_burst, maxretries=TX_N_MAX_RETRIES_PER_BURST,overflows=static.BUFFER_OVERFLOW_COUNTER) + structlog.get_logger("structlog").debug("[TNC] ATTEMPT:", retry=self.tx_n_retry_of_burst, maxretries=TX_N_MAX_RETRIES_PER_BURST, overflows=static.BUFFER_OVERFLOW_COUNTER) # update buffer position bufferposition = bufferposition_end @@ -782,7 +828,7 @@ class DATA(): # update stats self.calculate_transfer_rate_tx(tx_start_of_transmission, bufferposition_end, len(data_out)) - jsondata = {"arq":"transmission", "status" :"transmitting", "uuid" : self.transmission_uuid, "percent" : static.ARQ_TRANSMISSION_PERCENT, "bytesperminute" : static.ARQ_BYTES_PER_MINUTE} + jsondata = {"arq":"transmission", "status" :"transmitting", "uuid" : self.transmission_uuid, "percent" : static.ARQ_TRANSMISSION_PERCENT, "bytesperminute" : static.ARQ_BYTES_PER_MINUTE} json_data_out = json.dumps(jsondata) sock.SOCKET_QUEUE.put(json_data_out) @@ -794,7 +840,7 @@ class DATA(): json_data_out = json.dumps(jsondata) sock.SOCKET_QUEUE.put(json_data_out) - structlog.get_logger("structlog").info("ARQ | TX | DATA TRANSMITTED!", BytesPerMinute=static.ARQ_BYTES_PER_MINUTE, BitsPerSecond=static.ARQ_BITS_PER_SECOND, overflows=static.BUFFER_OVERFLOW_COUNTER) + structlog.get_logger("structlog").info("[TNC] ARQ | TX | DATA TRANSMITTED!", BytesPerMinute=static.ARQ_BYTES_PER_MINUTE, BitsPerSecond=static.ARQ_BITS_PER_SECOND, overflows=static.BUFFER_OVERFLOW_COUNTER) else: static.INFO.append("ARQ;TRANSMITTING;FAILED") @@ -802,18 +848,16 @@ class DATA(): json_data_out = json.dumps(jsondata) sock.SOCKET_QUEUE.put(json_data_out) - structlog.get_logger("structlog").info("ARQ | TX | TRANSMISSION FAILED OR TIME OUT!", overflows=static.BUFFER_OVERFLOW_COUNTER) + structlog.get_logger("structlog").info("[TNC] ARQ | TX | TRANSMISSION FAILED OR TIME OUT!", overflows=static.BUFFER_OVERFLOW_COUNTER) self.stop_transmission() # and last but not least doing a state cleanup # do cleanup only when not in testmode if not TESTMODE: self.arq_cleanup() - - # quit after transmission - if TESTMODE: - import os - os._exit(0) + else: + # quit after transmission + sys.exit(0) # signalling frames received def burst_ack_received(self, data_in:bytes): @@ -838,7 +882,8 @@ class DATA(): self.burst_ack_snr= int.from_bytes(bytes(data_in[5:6]), "big") self.speed_level= int.from_bytes(bytes(data_in[6:7]), "big") static.ARQ_SPEED_LEVEL = self.speed_level - print(self.speed_level) + structlog.get_logger("structlog").debug("[TNC] burst_ack_received:", speed_level=self.speed_level) + # print(self.speed_level) # reset burst nack counter self.burst_nack_counter = 0 # reset n retries per burst counter @@ -868,7 +913,8 @@ class DATA(): self.speed_level= int.from_bytes(bytes(data_in[6:7]), "big") static.ARQ_SPEED_LEVEL = self.speed_level self.burst_nack_counter += 1 - print(self.speed_level) + structlog.get_logger("structlog").debug("[TNC] burst_nack_received:", speed_level=self.speed_level) + # print(self.speed_level) def frame_ack_received(self): """ """ @@ -879,7 +925,7 @@ class DATA(): self.data_channel_last_received = int(time.time()) # we need to update our timeout timestamp self.arq_session_last_received = int(time.time()) # we need to update our timeout timestamp - def frame_nack_received(self, data_in:bytes): + def frame_nack_received(self, data_in:bytes): # pylint: disable=unused-argument """ Args: @@ -925,7 +971,6 @@ class DATA(): # ############################################################################################################ # ARQ SESSION HANDLER # ############################################################################################################ - def arq_session_handler(self, callsign): """ @@ -937,7 +982,7 @@ class DATA(): """ # das hier müssen wir checken. Sollte vielleicht in INIT!!! self.datachannel_timeout = False - structlog.get_logger("structlog").info("SESSION [" + str(self.mycallsign, 'utf-8') + "]>> <<[" + str(static.DXCALLSIGN, 'utf-8') + "]", state=static.ARQ_SESSION_STATE) + structlog.get_logger("structlog").info("[TNC] SESSION [" + str(self.mycallsign, 'utf-8') + "]>> <<[" + str(static.DXCALLSIGN, 'utf-8') + "]", state=static.ARQ_SESSION_STATE) self.open_session(callsign) @@ -949,9 +994,9 @@ class DATA(): if static.ARQ_SESSION and static.ARQ_SESSION_STATE == 'connected': # static.ARQ_SESSION_STATE = 'connected' return True - else: - static.ARQ_SESSION_STATE = 'failed' - return False + + static.ARQ_SESSION_STATE = 'failed' + return False def open_session(self, callsign): """ @@ -965,26 +1010,18 @@ class DATA(): self.IS_ARQ_SESSION_MASTER = True static.ARQ_SESSION_STATE = 'connecting' - frametype = bytes([221]) - - connection_frame = bytearray(14) - connection_frame[:1] = frametype - connection_frame[1:4] = static.DXCALLSIGN_CRC - connection_frame[4:7] = static.MYCALLSIGN_CRC - connection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign) + connection_frame = bytearray(14) + connection_frame[:1] = bytes([221]) + connection_frame[1:4] = static.DXCALLSIGN_CRC + connection_frame[4:7] = static.MYCALLSIGN_CRC + connection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign) while not static.ARQ_SESSION: time.sleep(0.01) for attempt in range(1,self.session_connect_max_retries+1): - txbuffer = [connection_frame] - static.TRANSMITTING = True + structlog.get_logger("structlog").info("[TNC] SESSION [" + str(self.mycallsign, 'utf-8') + "]>>?<<[" + str(static.DXCALLSIGN, 'utf-8') + "]", a=attempt, state=static.ARQ_SESSION_STATE) - structlog.get_logger("structlog").info("SESSION [" + str(self.mycallsign, 'utf-8') + "]>>?<<[" + str(static.DXCALLSIGN, 'utf-8') + "]", a=attempt, state=static.ARQ_SESSION_STATE) - - modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) - # wait while transmitting - while static.TRANSMITTING: - time.sleep(0.01) + self.enqueue_frame_for_tx(connection_frame) timeout = time.time() + 3 while time.time() < timeout: @@ -1000,8 +1037,6 @@ class DATA(): # attempt to cleanup the far-side, if it received the # open_session frame and can still hear us. if not static.ARQ_SESSION: - # if not TESTMODE: - # self.arq_cleanup() self.close_session() return False @@ -1023,37 +1058,24 @@ class DATA(): static.DXCALLSIGN = helpers.bytes_to_callsign(bytes(data_in[7:13])) helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) - structlog.get_logger("structlog").info("SESSION [" + str(self.mycallsign, 'utf-8') + "]>>|<<[" + str(static.DXCALLSIGN, 'utf-8') + "]", state=static.ARQ_SESSION_STATE) + structlog.get_logger("structlog").info("[TNC] SESSION [" + str(self.mycallsign, 'utf-8') + "]>>|<<[" + str(static.DXCALLSIGN, 'utf-8') + "]", state=static.ARQ_SESSION_STATE) static.ARQ_SESSION = True static.TNC_STATE = 'BUSY' self.transmit_session_heartbeat() def close_session(self): - """ """ + """ Close the ARQ session """ static.ARQ_SESSION_STATE = 'disconnecting' helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) - structlog.get_logger("structlog").info("SESSION [" + str(self.mycallsign, 'utf-8') + "]<>[" + str(static.DXCALLSIGN, 'utf-8') + "]", state=static.ARQ_SESSION_STATE) + structlog.get_logger("structlog").info("[TNC] SESSION [" + str(self.mycallsign, 'utf-8') + "]<>[" + str(static.DXCALLSIGN, 'utf-8') + "]", state=static.ARQ_SESSION_STATE) static.INFO.append("ARQ;SESSION;CLOSE") self.IS_ARQ_SESSION_MASTER = False static.ARQ_SESSION = False - self.arq_cleanup() + if not TESTMODE: + self.arq_cleanup() - frametype = bytes([223]) - - disconnection_frame = bytearray(14) - disconnection_frame[:1] = frametype - disconnection_frame[1:4] = static.DXCALLSIGN_CRC - disconnection_frame[4:7] = static.MYCALLSIGN_CRC - disconnection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign) - - txbuffer = [disconnection_frame] - static.TRANSMITTING = True - - modem.MODEM_TRANSMIT_QUEUE.put([14,5,250,txbuffer]) - # wait while transmitting - while static.TRANSMITTING: - time.sleep(0.01) + self.send_disconnect_frame() def received_session_close(self, data_in:bytes): """ @@ -1070,7 +1092,7 @@ class DATA(): if _valid_crc: static.ARQ_SESSION_STATE = 'disconnected' helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) - structlog.get_logger("structlog").info("SESSION [" + str(self.mycallsign, 'utf-8') + "]<>[" + str(static.DXCALLSIGN, 'utf-8') + "]", state=static.ARQ_SESSION_STATE) + structlog.get_logger("structlog").info("[TNC] SESSION [" + str(self.mycallsign, 'utf-8') + "]<>[" + str(static.DXCALLSIGN, 'utf-8') + "]", state=static.ARQ_SESSION_STATE) static.INFO.append("ARQ;SESSION;CLOSE") self.IS_ARQ_SESSION_MASTER = False @@ -1083,20 +1105,12 @@ class DATA(): # static.TNC_STATE = 'BUSY' # static.ARQ_SESSION_STATE = 'connected' - frametype = bytes([222]) - - connection_frame = bytearray(14) - connection_frame[:1] = frametype + connection_frame = bytearray(14) + connection_frame[:1] = bytes([222]) connection_frame[1:4] = static.DXCALLSIGN_CRC connection_frame[4:7] = static.MYCALLSIGN_CRC - txbuffer = [connection_frame] - static.TRANSMITTING = True - - modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) - # wait while transmitting - while static.TRANSMITTING: - time.sleep(0.01) + self.enqueue_frame_for_tx(connection_frame) def received_session_heartbeat(self, data_in:bytes): """ @@ -1110,7 +1124,7 @@ class DATA(): # Accept session data if the DXCALLSIGN_CRC matches the station in static. _valid_crc, _ = helpers.check_callsign(static.DXCALLSIGN, bytes(data_in[4:7])) if _valid_crc: - structlog.get_logger("structlog").debug("received session heartbeat") + structlog.get_logger("structlog").debug("[TNC] Received session heartbeat") helpers.add_to_heard_stations(static.DXCALLSIGN, static.DXGRID, 'SESSION-HB', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) self.arq_session_last_received = int(time.time()) # we need to update our timeout timestamp @@ -1125,7 +1139,6 @@ class DATA(): # ############################################################################################################ # ARQ DATA CHANNEL HANDLER # ############################################################################################################ - def open_dc_and_transmit(self, data_out:bytes, mode:int, n_frames_per_burst:int, transmission_uuid:str, mycallsign): """ @@ -1182,34 +1195,30 @@ class DATA(): if static.LOW_BANDWITH_MODE and mode == 255: frametype = bytes([227]) - structlog.get_logger("structlog").debug("requesting low bandwith mode") + structlog.get_logger("structlog").debug("[TNC] Requesting low bandwidth mode") else: frametype = bytes([225]) - structlog.get_logger("structlog").debug("requesting high bandwith mode") + structlog.get_logger("structlog").debug("[TNC] Requesting high bandwidth mode") if 230 <= mode <= 240: - structlog.get_logger("structlog").debug("requesting manual mode --> not yet implemented ") + structlog.get_logger("structlog").debug("[TNC] Requesting manual mode --> not yet implemented ") frametype = bytes([mode]) connection_frame = bytearray(14) connection_frame[:1] = frametype - connection_frame[1:4] = static.DXCALLSIGN_CRC - connection_frame[4:7] = static.MYCALLSIGN_CRC - connection_frame[7:13] = helpers.callsign_to_bytes(mycallsign) + connection_frame[1:4] = static.DXCALLSIGN_CRC + connection_frame[4:7] = static.MYCALLSIGN_CRC + connection_frame[7:13] = helpers.callsign_to_bytes(mycallsign) connection_frame[13:14] = bytes([n_frames_per_burst]) while not static.ARQ_STATE: time.sleep(0.01) for attempt in range(1,self.data_channel_max_retries+1): static.INFO.append("DATACHANNEL;OPENING") - structlog.get_logger("structlog").info("[TNC] ARQ | DATA | TX | [" + str(mycallsign, 'utf-8') + "]>> <<[" + str(static.DXCALLSIGN, 'utf-8') + "]", attempt=str(attempt) + "/" + str(self.data_channel_max_retries)) - txbuffer = [connection_frame] - static.TRANSMITTING = True - modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) - # wait while transmitting - while static.TRANSMITTING: - time.sleep(0.01) + structlog.get_logger("structlog").info("[TNC] ARQ | DATA | TX | [" + str(mycallsign, 'utf-8') + "]>> <<[" + str(static.DXCALLSIGN, 'utf-8') + "]", attempt=f"{str(attempt)}/{str(self.data_channel_max_retries)}") + + self.enqueue_frame_for_tx(connection_frame) timeout = time.time() + 3 while time.time() < timeout: @@ -1217,12 +1226,14 @@ class DATA(): # break if data channel is opened if static.ARQ_STATE: break + if static.ARQ_STATE: break - if not static.ARQ_STATE and attempt == self.data_channel_max_retries: + if attempt == self.data_channel_max_retries: static.INFO.append("DATACHANNEL;FAILED") - print(self.transmission_uuid) + structlog.get_logger("structlog").debug("[TNC] arq_open_data_channel:", transmission_uuid=self.transmission_uuid) + # print(self.transmission_uuid) jsondata = {"arq":"transmission", "status" :"failed", "uuid" : self.transmission_uuid, "percent" : static.ARQ_TRANSMISSION_PERCENT, "bytesperminute" : static.ARQ_BYTES_PER_MINUTE} json_data_out = json.dumps(jsondata) sock.SOCKET_QUEUE.put(json_data_out) @@ -1260,15 +1271,15 @@ class DATA(): self.received_low_bandwith_mode = False self.mode_list = self.mode_list_high_bw self.time_list = self.time_list_high_bw - self.speed_level = len(self.mode_list) - 1 else: self.received_low_bandwith_mode = True self.mode_list = self.mode_list_low_bw self.time_list = self.time_list_low_bw - self.speed_level = len(self.mode_list) - 1 + self.speed_level = len(self.mode_list) - 1 if 230 <= frametype <= 240: - print("manual mode request") + structlog.get_logger("structlog").debug("[TNC] arq_received_data_channel_opener: manual mode request") + # print("manual mode request") # updated modes we are listening to self.set_listening_modes(self.mode_list[self.speed_level]) @@ -1294,24 +1305,19 @@ class DATA(): # check if we are in low bandwith mode if static.LOW_BANDWITH_MODE or self.received_low_bandwith_mode: frametype = bytes([228]) - structlog.get_logger("structlog").debug("responding with low bandwith mode") + structlog.get_logger("structlog").debug("[TNC] Responding with low bandwidth mode") else: frametype = bytes([226]) - structlog.get_logger("structlog").debug("responding with high bandwith mode") + structlog.get_logger("structlog").debug("[TNC] Responding with high bandwidth mode") - connection_frame = bytearray(14) - connection_frame[:1] = frametype - connection_frame[1:4] = static.DXCALLSIGN_CRC - connection_frame[4:7] = static.MYCALLSIGN_CRC + connection_frame = bytearray(14) + connection_frame[:1] = frametype + connection_frame[1:4] = static.DXCALLSIGN_CRC + connection_frame[4:7] = static.MYCALLSIGN_CRC connection_frame[13:14] = bytes([static.ARQ_PROTOCOL_VERSION]) #crc8 of version for checking protocol version - txbuffer = [connection_frame] + self.enqueue_frame_for_tx(connection_frame) - static.TRANSMITTING = True - modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) - # wait while transmitting - while static.TRANSMITTING: - time.sleep(0.01) structlog.get_logger("structlog").info("[TNC] ARQ | DATA | RX | [" + str(mycallsign, 'utf-8') + "]>>|<<[" + str(static.DXCALLSIGN, 'utf-8') + "]", bandwith="wide", snr=static.SNR) # set start of transmission for our statistics @@ -1336,15 +1342,15 @@ class DATA(): self.mode_list = self.mode_list_low_bw self.time_list = self.time_list_low_bw self.speed_level = len(self.mode_list) - 1 - structlog.get_logger("structlog").debug("low bandwith mode", modes=self.mode_list) + structlog.get_logger("structlog").debug("[TNC] low bandwidth mode", modes=self.mode_list) else: self.received_low_bandwith_mode = False self.mode_list = self.mode_list_high_bw self.time_list = self.time_list_high_bw self.speed_level = len(self.mode_list) - 1 - structlog.get_logger("structlog").debug("high bandwith mode", modes=self.mode_list) + structlog.get_logger("structlog").debug("[TNC] high bandwidth mode", modes=self.mode_list) - helpers.add_to_heard_stations(static.DXCALLSIGN,static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) + helpers.add_to_heard_stations(static.DXCALLSIGN, static.DXGRID, 'DATA-CHANNEL', static.SNR, static.FREQ_OFFSET, static.HAMLIB_FREQUENCY) structlog.get_logger("structlog").info("[TNC] ARQ | DATA | TX | [" + str(self.mycallsign, 'utf-8') + "]>>|<<[" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR) @@ -1354,8 +1360,8 @@ class DATA(): else: static.TNC_STATE = 'IDLE' static.ARQ_STATE = False - static.INFO.append("PROTOCOL;VERSION_MISSMATCH") - structlog.get_logger("structlog").warning("protocol version missmatch", received=protocol_version, own=static.ARQ_PROTOCOL_VERSION) + static.INFO.append("PROTOCOL;VERSION_MISMATCH") + structlog.get_logger("structlog").warning("[TNC] protocol version mismatch:", received=protocol_version, own=static.ARQ_PROTOCOL_VERSION) self.arq_cleanup() # ---------- PING @@ -1374,22 +1380,17 @@ class DATA(): static.INFO.append("PING;SENDING") structlog.get_logger("structlog").info("[TNC] PING REQ [" + str(self.mycallsign, 'utf-8') + "] >>> [" + str(static.DXCALLSIGN, 'utf-8') + "]" ) - ping_frame = bytearray(14) - ping_frame[:1] = bytes([210]) - ping_frame[1:4] = static.DXCALLSIGN_CRC - ping_frame[4:7] = static.MYCALLSIGN_CRC + ping_frame = bytearray(14) + ping_frame[:1] = bytes([210]) + ping_frame[1:4] = static.DXCALLSIGN_CRC + ping_frame[4:7] = static.MYCALLSIGN_CRC ping_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign) - txbuffer = [ping_frame] - static.TRANSMITTING = True - structlog.get_logger("structlog").info("ENABLE FSK", state=static.ENABLE_FSK) + structlog.get_logger("structlog").info("[TNC] ENABLE FSK", state=static.ENABLE_FSK) if static.ENABLE_FSK: - modem.MODEM_TRANSMIT_QUEUE.put(['FSK_LDPC_0',1,0,txbuffer]) + self.enqueue_frame_for_tx(ping_frame, c2_mode='FSK_LDPC_0') else: - modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) - # wait while transmitting - while static.TRANSMITTING: - time.sleep(0.01) + self.enqueue_frame_for_tx(ping_frame) def received_ping(self, data_in:bytes): """ @@ -1411,7 +1412,8 @@ class DATA(): valid, mycallsign = helpers.check_callsign(self.mycallsign, data_in[1:4]) if not valid: # PING packet not for me. - print("ping not for me...") + structlog.get_logger("structlog").debug("[TNC] received_ping: ping not for this station.") + # print("ping not for me...") return structlog.get_logger("structlog").info("[TNC] PING REQ [" + str(mycallsign, 'utf-8') + "] <<< [" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR ) @@ -1422,16 +1424,11 @@ class DATA(): ping_frame[4:7] = static.MYCALLSIGN_CRC ping_frame[7:13] = static.MYGRID - txbuffer = [ping_frame] - static.TRANSMITTING = True - structlog.get_logger("structlog").info("ENABLE FSK", state=static.ENABLE_FSK) + structlog.get_logger("structlog").info("[TNC] ENABLE FSK", state=static.ENABLE_FSK) if static.ENABLE_FSK: - modem.MODEM_TRANSMIT_QUEUE.put(['FSK_LDPC_0',1,0,txbuffer]) + self.enqueue_frame_for_tx(ping_frame, c2_mode='FSK_LDPC_0') else: - modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) - # wait while transmitting - while static.TRANSMITTING: - time.sleep(0.01) + self.enqueue_frame_for_tx(ping_frame) def received_ping_ack(self, data_in:bytes): """ @@ -1467,11 +1464,7 @@ class DATA(): stop_frame[4:7] = static.MYCALLSIGN_CRC stop_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign) - txbuffer = [stop_frame] - static.TRANSMITTING = True - modem.MODEM_TRANSMIT_QUEUE.put([14,2,250,txbuffer]) - while static.TRANSMITTING: - time.sleep(0.01) + self.enqueue_frame_for_tx(stop_frame, copies=2, repeat_delay=250) static.TNC_STATE = 'IDLE' static.ARQ_STATE = False @@ -1489,7 +1482,6 @@ class DATA(): self.arq_cleanup() # ----------- BROADCASTS - def run_beacon(self): """ Controlling funktion for running a beacon @@ -1503,8 +1495,7 @@ class DATA(): while 1: time.sleep(0.5) while static.BEACON_STATE: - - if static.BEACON_STATE and not static.ARQ_SESSION and not self.arq_file_transfer and not static.BEACON_PAUSE: + if not static.ARQ_SESSION and not self.arq_file_transfer and not static.BEACON_PAUSE: static.INFO.append("BEACON;SENDING") structlog.get_logger("structlog").info("[TNC] Sending beacon!", interval=self.beacon_interval) @@ -1512,25 +1503,20 @@ class DATA(): beacon_frame[:1] = bytes([250]) beacon_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign) beacon_frame[9:13] = static.MYGRID[:4] - txbuffer = [beacon_frame] + structlog.get_logger("structlog").info("[TNC] ENABLE FSK", state=static.ENABLE_FSK) - static.TRANSMITTING = True - structlog.get_logger("structlog").info("ENABLE FSK", state=static.ENABLE_FSK) if static.ENABLE_FSK: - modem.MODEM_TRANSMIT_QUEUE.put(['FSK_LDPC_0',1,0,txbuffer]) + self.enqueue_frame_for_tx(beacon_frame, c2_mode='FSK_LDPC_0') else: - modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) - - # wait while transmitting - while static.TRANSMITTING: - time.sleep(0.01) + self.enqueue_frame_for_tx(beacon_frame) interval_timer = time.time() + self.beacon_interval while time.time() < interval_timer and static.BEACON_STATE and not static.BEACON_PAUSE: time.sleep(0.01) except Exception as e: - print(e) + structlog.get_logger("structlog").debug("[TNC] run_beacon: ", exception=e) + # print(e) def received_beacon(self, data_in:bytes): """ @@ -1556,8 +1542,13 @@ class DATA(): def transmit_cq(self): """ Transmit a CQ + Args: + Nothing + + Returns: + Nothing """ - structlog.get_logger("structlog").info("CQ CQ CQ") + structlog.get_logger("structlog").info("[TNC] CQ CQ CQ") static.INFO.append("CQ;SENDING") cq_frame = bytearray(14) @@ -1565,31 +1556,27 @@ class DATA(): cq_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign) cq_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("utf-8")) - txbuffer = [cq_frame] - print(txbuffer) - static.TRANSMITTING = True - structlog.get_logger("structlog").info("ENABLE FSK", state=static.ENABLE_FSK) + structlog.get_logger("structlog").info("[TNC] ENABLE FSK", state=static.ENABLE_FSK) + structlog.get_logger("structlog").debug("[TNC] CQ Frame:", data=[cq_frame]) + if static.ENABLE_FSK: - modem.MODEM_TRANSMIT_QUEUE.put(['FSK_LDPC_0',1,0,txbuffer]) + self.enqueue_frame_for_tx(cq_frame, c2_mode='FSK_LDPC_0') else: - modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) - # wait while transmitting - while static.TRANSMITTING: - time.sleep(0.01) - return + self.enqueue_frame_for_tx(cq_frame) def received_cq(self, data_in:bytes): """ - Called if we received a CQ + Called when we receive a CQ frame Args: data_in:bytes: Returns: - + Nothing """ # here we add the received station to the heard stations buffer dxcallsign = helpers.bytes_to_callsign(bytes(data_in[1:7])) - print(dxcallsign) + structlog.get_logger("structlog").debug("[TNC] received_cq:", dxcallsign=dxcallsign) + # print(dxcallsign) dxgrid = bytes(helpers.decode_grid(data_in[7:11]), "utf-8") static.INFO.append("CQ;RECEIVING") structlog.get_logger("structlog").info("[TNC] CQ RCVD [" + str(dxcallsign, 'utf-8') + "]["+ str(dxgrid, 'utf-8') +"] ", snr=static.SNR) @@ -1600,7 +1587,7 @@ class DATA(): def transmit_qrv(self): """ - Called if we send a QRV frame + Called when we send a QRV frame Args: data_in:bytes: @@ -1619,27 +1606,22 @@ class DATA(): qrv_frame[:1] = bytes([201]) qrv_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign) qrv_frame[7:11] = helpers.encode_grid(static.MYGRID.decode("utf-8")) - txbuffer = [qrv_frame] - static.TRANSMITTING = True - structlog.get_logger("structlog").info("ENABLE FSK", state=static.ENABLE_FSK) + structlog.get_logger("structlog").info("[TNC] ENABLE FSK", state=static.ENABLE_FSK) + if static.ENABLE_FSK: - modem.MODEM_TRANSMIT_QUEUE.put(['FSK_LDPC_0',1,0,txbuffer]) + self.enqueue_frame_for_tx(qrv_frame, c2_mode='FSK_LDPC_0') else: - modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) - - # wait while transmitting - while static.TRANSMITTING: - time.sleep(0.01) + self.enqueue_frame_for_tx(qrv_frame) def received_qrv(self, data_in:bytes): """ - Called if we receive a QRV frame + Called when we receive a QRV frame Args: data_in:bytes: Returns: - + Nothing """ # here we add the received station to the heard stations buffer dxcallsign = helpers.bytes_to_callsign(bytes(data_in[1:7])) @@ -1656,38 +1638,39 @@ class DATA(): # ------------ CALUCLATE TRANSFER RATES def calculate_transfer_rate_rx(self, rx_start_of_transmission:float, receivedbytes:int) -> list: """ - Calculate Transferrate for receiving data + Calculate transfer rate for received data Args: rx_start_of_transmission:float: receivedbytes:int: - Returns: - + Returns: List of: + bits_per_second: float, + bytes_per_minute: float, + transmission_percent: float """ try: if static.TOTAL_BYTES == 0: static.TOTAL_BYTES = 1 - static.ARQ_TRANSMISSION_PERCENT = int((receivedbytes*static.ARQ_COMPRESSION_FACTOR / (static.TOTAL_BYTES)) * 100) - if static.ARQ_TRANSMISSION_PERCENT > 100: - static.ARQ_TRANSMISSION_PERCENT = 100 + static.ARQ_TRANSMISSION_PERCENT = min(int((receivedbytes*static.ARQ_COMPRESSION_FACTOR / (static.TOTAL_BYTES)) * 100), 100) transmissiontime = time.time() - self.rx_start_of_transmission if receivedbytes > 0: - static.ARQ_BITS_PER_SECOND = int((receivedbytes*8) / transmissiontime) - static.ARQ_BYTES_PER_MINUTE = int((receivedbytes) / (transmissiontime/60)) + static.ARQ_BITS_PER_SECOND = int((receivedbytes * 8) / transmissiontime) + static.ARQ_BYTES_PER_MINUTE = int((receivedbytes) / (transmissiontime / 60)) else: static.ARQ_BITS_PER_SECOND = 0 static.ARQ_BYTES_PER_MINUTE = 0 - except: + except Exception as e: + structlog.get_logger("structlog").error(f"[TNC] calculate_transfer_rate_rx: Exception: {e}") static.ARQ_TRANSMISSION_PERCENT = 0.0 static.ARQ_BITS_PER_SECOND = 0 static.ARQ_BYTES_PER_MINUTE = 0 - return [static.ARQ_BITS_PER_SECOND, \ - static.ARQ_BYTES_PER_MINUTE, \ - static.ARQ_TRANSMISSION_PERCENT] + return [static.ARQ_BITS_PER_SECOND, + static.ARQ_BYTES_PER_MINUTE, + static.ARQ_TRANSMISSION_PERCENT] def reset_statistics(self): """ @@ -1695,11 +1678,11 @@ class DATA(): """ # reset ARQ statistics static.ARQ_BYTES_PER_MINUTE_BURST = 0 - static.ARQ_BYTES_PER_MINUTE = 0 - static.ARQ_BITS_PER_SECOND_BURST = 0 - static.ARQ_BITS_PER_SECOND = 0 - static.ARQ_TRANSMISSION_PERCENT = 0 - static.TOTAL_BYTES = 0 + static.ARQ_BYTES_PER_MINUTE = 0 + static.ARQ_BITS_PER_SECOND_BURST = 0 + static.ARQ_BITS_PER_SECOND = 0 + static.ARQ_TRANSMISSION_PERCENT = 0 + static.TOTAL_BYTES = 0 def calculate_transfer_rate_tx(self, tx_start_of_transmission:float, sentbytes:int, tx_buffer_length:int) -> list: """ @@ -1713,38 +1696,34 @@ class DATA(): """ try: - static.ARQ_TRANSMISSION_PERCENT = int((sentbytes / tx_buffer_length) * 100) - - if static.ARQ_TRANSMISSION_PERCENT > 100: - static.ARQ_TRANSMISSION_PERCENT = 100 + static.ARQ_TRANSMISSION_PERCENT = min(int((sentbytes / tx_buffer_length) * 100), 100) transmissiontime = time.time() - tx_start_of_transmission if sentbytes > 0: - - static.ARQ_BITS_PER_SECOND = int((sentbytes*8) / transmissiontime) # Bits per Second - static.ARQ_BYTES_PER_MINUTE = int((sentbytes) / (transmissiontime/60)) #Bytes per Minute + static.ARQ_BITS_PER_SECOND = int((sentbytes * 8) / transmissiontime) # Bits per Second + static.ARQ_BYTES_PER_MINUTE = int((sentbytes) / (transmissiontime / 60)) # Bytes per Minute else: static.ARQ_BITS_PER_SECOND = 0 static.ARQ_BYTES_PER_MINUTE = 0 - except: + except Exception as e: + structlog.get_logger("structlog").error(f"[TNC] calculate_transfer_rate_tx: Exception: {e}") static.ARQ_TRANSMISSION_PERCENT = 0.0 static.ARQ_BITS_PER_SECOND = 0 static.ARQ_BYTES_PER_MINUTE = 0 - return [static.ARQ_BITS_PER_SECOND, \ - static.ARQ_BYTES_PER_MINUTE, \ - static.ARQ_TRANSMISSION_PERCENT] + return [static.ARQ_BITS_PER_SECOND, + static.ARQ_BYTES_PER_MINUTE, + static.ARQ_TRANSMISSION_PERCENT] # ----------------------CLEANUP AND RESET FUNCTIONS def arq_cleanup(self): """ Cleanup funktion which clears all ARQ states """ - - structlog.get_logger("structlog").debug("cleanup") + structlog.get_logger("structlog").debug("[TNC] arq_cleanup") self.received_mycall_crc = b'' @@ -1764,7 +1743,7 @@ class DATA(): modem.RECEIVE_FSK_LDPC_1 = False # reset buffer overflow counter - static.BUFFER_OVERFLOW_COUNTER = [0,0,0,0,0] + static.BUFFER_OVERFLOW_COUNTER = [0, 0, 0, 0, 0] self.is_IRS = False self.burst_nack = False @@ -1810,29 +1789,29 @@ class DATA(): Returns: """ - # set modes we want listening to - + # set modes we want to listen to mode_name = codec2.freedv_get_mode_name_by_value(mode) + if mode_name == 'datac1': modem.RECEIVE_DATAC1 = True - structlog.get_logger("structlog").debug("changing listening data mode", mode="datac1") + structlog.get_logger("structlog").debug("[TNC] Changing listening data mode", mode="datac1") elif mode_name == 'datac3': modem.RECEIVE_DATAC3 = True - structlog.get_logger("structlog").debug("changing listening data mode", mode="datac3") + structlog.get_logger("structlog").debug("[TNC] Changing listening data mode", mode="datac3") elif mode_name == 'fsk_ldpc_1': modem.RECEIVE_FSK_LDPC_1 = True - structlog.get_logger("structlog").debug("changing listening data mode", mode="fsk_ldpc_1") + structlog.get_logger("structlog").debug("[TNC] Changing listening data mode", mode="fsk_ldpc_1") elif mode_name == 'allmodes': modem.RECEIVE_DATAC1 = True modem.RECEIVE_DATAC3 = True modem.RECEIVE_FSK_LDPC_1 = True - structlog.get_logger("structlog").debug("changing listening data mode", mode="datac1/datac3/fsk_ldpc") + structlog.get_logger("structlog").debug("[TNC] Changing listening data mode", mode="datac1/datac3/fsk_ldpc") # ------------------------- WATCHDOG FUNCTIONS FOR TIMER def watchdog(self): """Author: DJ2LS - watchdog master function. Frome here we call the watchdogs + Watchdog master function. From here, call the watchdogs Args: @@ -1851,49 +1830,38 @@ class DATA(): DATA BURST """ # IRS SIDE - if static.ARQ_STATE and static.ARQ_SESSION_STATE == 'connected' and static.TNC_STATE == 'BUSY' and self.is_IRS: - if self.data_channel_last_received + self.time_list[self.speed_level] > time.time(): - #print((self.data_channel_last_received + self.time_list[self.speed_level])-time.time()) - pass - else: - structlog.get_logger("structlog").warning("packet timeout", attempt=self.n_retries_per_burst, max_attempts=self.rx_n_max_retries_per_burst, speed_level=self.speed_level) - self.frame_received_counter = 0 - self.burst_nack_counter += 1 - if self.burst_nack_counter >= 2: - self.speed_level -= 1 - #print(self.burst_nack_counter) - #print(self.speed_level) - static.ARQ_SPEED_LEVEL = self.speed_level - self.burst_nack_counter = 0 - if self.speed_level <= 0: - self.speed_level = 0 - static.ARQ_SPEED_LEVEL = self.speed_level + if not static.ARQ_STATE or static.ARQ_SESSION_STATE != 'connected' or static.TNC_STATE != 'BUSY' or not self.is_IRS: + return - # updated modes we are listening to - self.set_listening_modes(self.mode_list[self.speed_level]) + if self.data_channel_last_received + self.time_list[self.speed_level] > time.time(): + # print((self.data_channel_last_received + self.time_list[self.speed_level])-time.time()) + pass + else: + structlog.get_logger("structlog").warning("[TNC] Frame timeout", attempt=self.n_retries_per_burst, max_attempts=self.rx_n_max_retries_per_burst, speed_level=self.speed_level) + self.frame_received_counter = 0 + self.burst_nack_counter += 1 + if self.burst_nack_counter >= 2: + self.speed_level -= 1 + #print(self.burst_nack_counter) + #print(self.speed_level) + static.ARQ_SPEED_LEVEL = self.speed_level + self.burst_nack_counter = 0 + if self.speed_level <= 0: + self.speed_level = 0 + static.ARQ_SPEED_LEVEL = self.speed_level - # BUILDING NACK FRAME FOR DATA FRAME - burst_nack_frame = bytearray(14) - burst_nack_frame[:1] = bytes([64]) - burst_nack_frame[1:4] = static.DXCALLSIGN_CRC - burst_nack_frame[4:7] = static.MYCALLSIGN_CRC - burst_nack_frame[7:8] = bytes([0]) - burst_nack_frame[8:9] = bytes([int(self.speed_level)]) + # updated modes we are listening to + self.set_listening_modes(self.mode_list[self.speed_level]) - # TRANSMIT NACK FRAME FOR BURST - txbuffer = [burst_nack_frame] - static.TRANSMITTING = True - modem.MODEM_TRANSMIT_QUEUE.put([14,1,0,txbuffer]) - # wait while transmitting - #while static.TRANSMITTING: - # #time.sleep(0.01) - # self.data_channel_last_received = time.time() - self.data_channel_last_received = time.time() - self.n_retries_per_burst += 1 + self.send_burst_nack_frame_watchdog(0) # Why not pass `snr`? + + self.data_channel_last_received = time.time() + self.n_retries_per_burst += 1 + + if self.n_retries_per_burst >= self.rx_n_max_retries_per_burst: + self.stop_transmission() + self.arq_cleanup() - if self.n_retries_per_burst >= self.rx_n_max_retries_per_burst: - self.stop_transmission() - self.arq_cleanup() def data_channel_keep_alive_watchdog(self): """ @@ -1909,7 +1877,7 @@ class DATA(): #pass else: self.data_channel_last_received = 0 - structlog.get_logger("structlog").info("DATA [" + str(self.mycallsign, 'utf-8') + "]<>[" + str(static.DXCALLSIGN, 'utf-8') + "]") + structlog.get_logger("structlog").info("[TNC] DATA [" + str(self.mycallsign, 'utf-8') + "]<>[" + str(static.DXCALLSIGN, 'utf-8') + "]") static.INFO.append("ARQ;RECEIVING;FAILED") if not TESTMODE: self.arq_cleanup() @@ -1923,7 +1891,7 @@ class DATA(): if self.arq_session_last_received + self.arq_session_timeout > time.time(): time.sleep(0.01) else: - structlog.get_logger("structlog").info("SESSION [" + str(self.mycallsign, 'utf-8') + "]<>[" + str(static.DXCALLSIGN, 'utf-8') + "]") + structlog.get_logger("structlog").info("[TNC] SESSION [" + str(self.mycallsign, 'utf-8') + "]<>[" + str(static.DXCALLSIGN, 'utf-8') + "]") static.INFO.append("ARQ;SESSION;TIMEOUT") self.close_session() diff --git a/tnc/helpers.py b/tnc/helpers.py index 7a922c3d..00378f7d 100644 --- a/tnc/helpers.py +++ b/tnc/helpers.py @@ -6,17 +6,20 @@ Created on Fri Dec 25 21:25:14 2020 @author: DJ2LS """ import time + import crcengine +import structlog + import static -def wait(seconds): + +def wait(seconds: float) -> bool: """ Args: - seconds: + seconds: Returns: - """ timeout = time.time() + seconds @@ -24,7 +27,7 @@ def wait(seconds): time.sleep(0.01) return True -def get_crc_8(data): +def get_crc_8(data) -> bytes: """Author: DJ2LS Get the CRC8 of a byte string @@ -32,17 +35,17 @@ def get_crc_8(data): param: data = bytes() Args: - data: + data: Returns: - + CRC-8 (CCITT) of the provided data as bytes """ crc_algorithm = crcengine.new('crc8-ccitt') # load crc8 library crc_data = crc_algorithm(data) crc_data = crc_data.to_bytes(1, byteorder='big') return crc_data -def get_crc_16(data): +def get_crc_16(data) -> bytes: """Author: DJ2LS Get the CRC16 of a byte string @@ -50,17 +53,17 @@ def get_crc_16(data): param: data = bytes() Args: - data: + data: Returns: - + CRC-16 (CCITT) of the provided data as bytes """ crc_algorithm = crcengine.new('crc16-ccitt-false') # load crc16 library crc_data = crc_algorithm(data) crc_data = crc_data.to_bytes(2, byteorder='big') return crc_data -def get_crc_24(data): +def get_crc_24(data) -> bytes: """Author: DJ2LS Get the CRC24-OPENPGP of a byte string @@ -69,10 +72,10 @@ def get_crc_24(data): param: data = bytes() Args: - data: + data: Returns: - + CRC-24 (OpenPGP) of the provided data as bytes """ crc_algorithm = crcengine.create(0x864cfb, 24, 0xb704ce, ref_in=False, ref_out=False, xor_out=0, @@ -81,7 +84,7 @@ def get_crc_24(data): crc_data = crc_data.to_bytes(3, byteorder='big') return crc_data -def get_crc_32(data): +def get_crc_32(data: bytes) -> bytes: """Author: DJ2LS Get the CRC32 of a byte string @@ -92,9 +95,9 @@ def get_crc_32(data): data: Returns: - + CRC-32 of the provided data as bytes """ - crc_algorithm = crcengine.new('crc32') # load crc16 library + crc_algorithm = crcengine.new('crc32') # load crc32 library crc_data = crc_algorithm(data) crc_data = crc_data.to_bytes(4, byteorder='big') return crc_data @@ -103,28 +106,27 @@ def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency): """ Args: - dxcallsign: - dxgrid: - datatype: - snr: - offset: - frequency: + dxcallsign: + dxgrid: + datatype: + snr: + offset: + frequency: Returns: - + Nothing """ - # check if buffer empty if len(static.HEARD_STATIONS) == 0: static.HEARD_STATIONS.append([dxcallsign, dxgrid, int(time.time()), datatype, snr, offset, frequency]) # if not, we search and update else: - for i in range(0, len(static.HEARD_STATIONS)): - # update callsign with new timestamp + for i in range(len(static.HEARD_STATIONS)): + # Update callsign with new timestamp if static.HEARD_STATIONS[i].count(dxcallsign) > 0: static.HEARD_STATIONS[i] = [dxcallsign, dxgrid, int(time.time()), datatype, snr, offset, frequency] break - # insert if nothing found + # Insert if nothing found if i == len(static.HEARD_STATIONS) - 1: static.HEARD_STATIONS.append([dxcallsign, dxgrid, int(time.time()), datatype, snr, offset, frequency]) break @@ -134,11 +136,11 @@ def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency): # item = [dxcallsign, int(time.time())] # static.HEARD_STATIONS[idx] = item -def callsign_to_bytes(callsign): +def callsign_to_bytes(callsign) -> bytes: """ Args: - callsign: + callsign: Returns: @@ -161,18 +163,20 @@ def callsign_to_bytes(callsign): #-14 Truckers or generally full time drivers #-15 generic additional station, digi, mobile, wx, etc - # try converting to bytestring if possible type string + # Try converting to bytestring if possible type string try: callsign = bytes(callsign, 'utf-8') - except: + except TypeError as e: + structlog.get_logger("structlog").debug("[HLP] callsign_to_bytes: Exception converting callsign to bytes:", e=e) pass - # we need to do this step to reduce the needed paypload by the callsign ( stripping "-" out of the callsign ) + # Need this step to reduce the needed payload by the callsign (stripping "-" out of the callsign) callsign = callsign.split(b'-') + ssid = 0 try: ssid = int(callsign[1]) - except: - ssid = 0 + except IndexError as e: + structlog.get_logger("structlog").debug("[HLP] callsign_to_bytes: Error callsign SSID to integer:", e=e) #callsign = callsign[0] #bytestring = bytearray(8) @@ -183,19 +187,17 @@ def callsign_to_bytes(callsign): callsign = callsign[0].decode("utf-8") ssid = bytes([ssid]).decode("utf-8") return encode_call(callsign + ssid) - #return bytes(bytestring) -def bytes_to_callsign(bytestring): +def bytes_to_callsign(bytestring: bytes) -> bytes: """ Convert our callsign, received by a frame to a callsign in a human readable format Args: - bytestring: + bytestring: Returns: bytes - """ # http://www.aprs.org/aprs11/SSIDs.txt #-0 Your primary station usually fixed and message capable @@ -231,27 +233,29 @@ def bytes_to_callsign(bytestring): decoded = decode_call(bytestring) callsign = decoded[:-1] ssid = ord(bytes(decoded[-1], "utf-8")) - return bytes(callsign + "-" + str(ssid), "utf-8") + return bytes(f"{callsign}-{ssid}", "utf-8") def check_callsign(callsign:bytes, crc_to_check:bytes): """ Funktion to check a crc against a callsign to calculate the ssid by generating crc until we got it Args: - callsign: Callsign which we want to check - crc_to_check: The CRC which we want the callsign to check against + callsign: Callsign which we want to check + crc_to_check: The CRC which we want the callsign to check against Returns: [True, Callsign + SSID] False """ - print(callsign) - try: - callsign = callsign.split(b'-') - callsign = callsign[0] # we want the callsign without SSID - except: - callsign = callsign + # print(callsign) + structlog.get_logger("structlog").debug("[HLP] check_callsign: Checking:", callsign=callsign) + try: + # We want the callsign without SSID + callsign = callsign.split(b'-')[0] + + except Exception as e: + structlog.get_logger("structlog").debug("[HLP] check_callsign: Error callsign SSIG to integer:", e=e) for ssid in static.SSID_LIST: call_with_ssid = bytearray(callsign) @@ -270,32 +274,32 @@ def encode_grid(grid): """ @auther: DB1UJ Args: - grid:string: maidenhead QTH locater [a-r][a-r][0-9][0-9][a-x][a-x] + grid:string: maidenhead QTH locater [a-r][a-r][0-9][0-9][a-x][a-x] Returns: - 4 bytes contains 26 bit valid data with encoded grid locator + 4 bytes contains 26 bit valid data with encoded grid locator """ - out_code_word = int(0) + out_code_word = 0 grid = grid.upper() # upper case to be save - int_first = ord(grid[0])-65 # -65 offset for 'A' become zero, utf8 table - int_sec = ord(grid[1])-65 # -65 offset for 'A' become zero, utf8 table + int_first = ord(grid[0]) - 65 # -65 offset for 'A' become zero, utf8 table + int_sec = ord(grid[1]) - 65 # -65 offset for 'A' become zero, utf8 table int_val = (int_first * 18) + int_sec # encode for modulo devision, 2 numbers in 1 out_code_word = (int_val & 0b111111111) # only 9 bit LSB A - R * A - R is needed - out_code_word = out_code_word << 9 # shift 9 bit left having space next bits, letter A-R * A-R + out_code_word <<= 9 # shift 9 bit left having space next bits, letter A-R * A-R int_val = int(grid[2:4]) # number string to number int, highest value 99 - out_code_word = out_code_word | (int_val & 0b1111111) # using bit OR to add new value - out_code_word = out_code_word << 7 # shift 7 bit left having space next bits, letter A-X + out_code_word |= (int_val & 0b1111111) # using bit OR to add new value + out_code_word <<= 7 # shift 7 bit left having space next bits, letter A-X - int_val = ord(grid[4])-65 # -65 offset for 'A' become zero, utf8 table - out_code_word = out_code_word | (int_val & 0b11111) # using bit OR to add new value - out_code_word = out_code_word << 5 # shift 5 bit left having space next bits, letter A-X + int_val = ord(grid[4]) - 65 # -65 offset for 'A' become zero, utf8 table + out_code_word |= (int_val & 0b11111) # using bit OR to add new value + out_code_word <<= 5 # shift 5 bit left having space next bits, letter A-X - int_val = ord(grid[5])-65 # -65 offset for 'A' become zero, utf8 table - out_code_word = out_code_word | (int_val & 0b11111) # using bit OR to add new value + int_val = ord(grid[5]) - 65 # -65 offset for 'A' become zero, utf8 table + out_code_word |= (int_val & 0b11111) # using bit OR to add new value return out_code_word.to_bytes(length=4, byteorder='big') @@ -303,27 +307,28 @@ def decode_grid(b_code_word:bytes): """ @auther: DB1UJ Args: - b_code_word:bytes: 4 bytes with 26 bit valid data LSB + b_code_word:bytes: 4 bytes with 26 bit valid data LSB Returns: - grid:str: upper case maidenhead QTH locater [A-R][A-R][0-9][0-9][A-X][A-X] + grid:str: upper case maidenhead QTH locater [A-R][A-R][0-9][0-9][A-X][A-X] """ code_word = int.from_bytes(b_code_word, byteorder='big', signed=False) grid = chr((code_word & 0b11111) + 65) - code_word = code_word >> 5 + code_word >>= 5 grid = chr((code_word & 0b11111) + 65) + grid - code_word = code_word >> 7 + code_word >>= 7 grid = str(int(code_word & 0b1111111)) + grid if (code_word & 0b1111111) < 10: - grid = '0' + grid - code_word = code_word >> 9 + grid = f'0{grid}' + code_word >>= 9 int_val = int(code_word & 0b111111111) - int_first = int_val // 18 - int_sec = int_val % 18 - grid = chr(int(int_first)+65) + chr(int(int_sec)+65) + grid + int_first, int_sec = divmod(int_val, 18) + # int_first = int_val // 18 + # int_sec = int_val % 18 + grid = chr(int(int_first) + 65) + chr(int(int_sec) + 65) + grid return grid @@ -336,17 +341,17 @@ def encode_call(call): Returns: 6 bytes contains 6 bits/sign encoded 8 char call sign with binary SSID (only upper letters + numbers, SSID) """ - out_code_word = int(0) + out_code_word = 0 call = call.upper() # upper case to be save for x in call: - int_val = ord(x)-48 # -48 reduce bits, begin with first number utf8 table - out_code_word = out_code_word << 6 # shift left 6 bit, making space for a new char - out_code_word = out_code_word | (int_val & 0b111111) # bit OR adds the new char, masked with AND 0b111111 - out_code_word = out_code_word >> 6 # clean last char - out_code_word = out_code_word << 6 # make clean space - out_code_word = out_code_word | (ord(call[-1]) & 0b111111) # add the SSID uncoded only 0 - 63 + int_val = ord(x) - 48 # -48 reduce bits, begin with first number utf8 table + out_code_word <<= 6 # shift left 6 bit, making space for a new char + out_code_word |= (int_val & 0b111111) # bit OR adds the new char, masked with AND 0b111111 + out_code_word >>= 6 # clean last char + out_code_word <<= 6 # make clean space + out_code_word |= (ord(call[-1]) & 0b111111) # add the SSID uncoded only 0 - 63 return out_code_word.to_bytes(length=6, byteorder='big') @@ -364,9 +369,9 @@ def decode_call(b_code_word:bytes): call = str() while code_word != 0: - call = chr((code_word & 0b111111)+48) + call - code_word = code_word >> 6 + call = chr((code_word & 0b111111)+48) + call + code_word >>= 6 - call = call[0:-1] + ssid # remove the last char from call and replace with SSID + call = call[:-1] + ssid # remove the last char from call and replace with SSID return call diff --git a/tnc/main.py b/tnc/main.py index a3a2fe4d..6e08dfbd 100755 --- a/tnc/main.py +++ b/tnc/main.py @@ -8,19 +8,21 @@ Created on Tue Dec 22 16:58:45 2020 main module for running the tnc """ import argparse -import threading -import static -import socketserver -import helpers -import data_handler -import structlog -import log_handler -import modem -import sys +import multiprocessing import os import signal +import socketserver +import sys +import threading import time -import multiprocessing + +import structlog + +import data_handler +import helpers +import log_handler +import modem +import static # signal handler for closing aplication def signal_handler(sig, frame): @@ -95,8 +97,8 @@ if __name__ == '__main__': static.HAMLIB_STOP_BITS = str(ARGS.hamlib_stop_bits) static.HAMLIB_HANDSHAKE = ARGS.hamlib_handshake static.HAMLIB_RADIOCONTROL = ARGS.hamlib_radiocontrol - static.HAMLIB_RGICTLD_IP = ARGS.rigctld_ip - static.HAMLIB_RGICTLD_PORT = str(ARGS.rigctld_port) + static.HAMLIB_RIGCTLD_IP = ARGS.rigctld_ip + static.HAMLIB_RIGCTLD_PORT = str(ARGS.rigctld_port) static.ENABLE_SCATTER = ARGS.send_scatter static.ENABLE_FFT = ARGS.send_fft static.ENABLE_FSK = ARGS.enable_fsk @@ -117,14 +119,14 @@ if __name__ == '__main__': if sys.platform == 'darwin': logging_path = os.getenv("HOME") + '/Library/' + 'Application Support/' + 'FreeDATA/' + 'tnc' - if sys.platform == 'win32' or sys.platform == 'win64': + if sys.platform in ['win32', 'win64']: logging_path = os.getenv('APPDATA') + '/' + 'FreeDATA/' + 'tnc' if not os.path.exists(logging_path): os.makedirs(logging_path) log_handler.setup_logging(logging_path) - except: - structlog.get_logger("structlog").error("[DMN] logger init error") + except Exception as e: + structlog.get_logger("structlog").error("[DMN] logger init error", exception=e) structlog.get_logger("structlog").info("[TNC] Starting FreeDATA", author="DJ2LS", year="2022", version=static.VERSION) @@ -135,7 +137,6 @@ if __name__ == '__main__': modem = modem.RF() # --------------------------------------------START CMD SERVER - try: structlog.get_logger("structlog").info("[TNC] Starting TCP/IP socket", port=static.PORT) # https://stackoverflow.com/a/16641793 @@ -148,6 +149,6 @@ if __name__ == '__main__': except Exception as e: structlog.get_logger("structlog").error("[TNC] Starting TCP/IP socket failed", port=static.PORT, e=e) - os._exit(1) + sys.exit(1) while 1: time.sleep(1) diff --git a/tnc/modem.py b/tnc/modem.py index 0722cbc2..411c26e5 100644 --- a/tnc/modem.py +++ b/tnc/modem.py @@ -5,44 +5,50 @@ Created on Wed Dec 23 07:04:24 2020 @author: DJ2LS """ -import sys -import os -import ctypes -from ctypes import * -import pathlib -import logging, structlog, log_handler -import time -import threading +# pylint: disable=invalid-name, line-too-long, c-extension-no-member +# pylint: disable=import-outside-toplevel + import atexit -import numpy as np -import helpers -import static -import data_handler -import ujson as json -import sock -import re +import ctypes +import logging +import os +import pathlib import queue -import codec2 -import audio - -import sounddevice as sd - +import re +import sys +import threading +import time from collections import deque +import numpy as np +import sounddevice as sd +import structlog +import ujson as json -# init FIFO queue to store received frames in +import audio +import codec2 +import data_handler +import helpers +import log_handler +import sock +import static + +TESTMODE = False +RXCHANNEL = '' +TXCHANNEL = '' + +# Initialize FIFO queue to store received frames MODEM_RECEIVED_QUEUE = queue.Queue() MODEM_TRANSMIT_QUEUE = queue.Queue() static.TRANSMITTING = False -# receive only specific modes to reduce cpu load +# Receive only specific modes to reduce CPU load RECEIVE_DATAC1 = False RECEIVE_DATAC3 = False RECEIVE_FSK_LDPC_1 = False class RF(): """ """ - def __init__(self): self.sampler_avg = 0 @@ -51,70 +57,70 @@ class RF(): self.AUDIO_SAMPLE_RATE_RX = 48000 self.AUDIO_SAMPLE_RATE_TX = 48000 self.MODEM_SAMPLE_RATE = codec2.api.FREEDV_FS_8000 - self.AUDIO_FRAMES_PER_BUFFER_RX = 2400*2 #8192 - self.AUDIO_FRAMES_PER_BUFFER_TX = 2400*2 #8192 Lets to some tests with very small chunks for TX - self.AUDIO_CHUNKS = 48 #8 * (self.AUDIO_SAMPLE_RATE_RX/self.MODEM_SAMPLE_RATE) #48 + self.AUDIO_FRAMES_PER_BUFFER_RX = 2400 * 2 # 8192 + self.AUDIO_FRAMES_PER_BUFFER_TX = 2400 * 2 # 8192 Lets to some tests with very small chunks for TX + self.AUDIO_CHUNKS = 48 # 8 * (self.AUDIO_SAMPLE_RATE_RX/self.MODEM_SAMPLE_RATE) #48 self.AUDIO_CHANNELS = 1 - # locking state for mod out so buffer will be filled before we can use it + # Locking state for mod out so buffer will be filled before we can use it # https://github.com/DJ2LS/FreeDATA/issues/127 # https://github.com/DJ2LS/FreeDATA/issues/99 self.mod_out_locked = True - # make sure our resampler will work + # Make sure our resampler will work assert (self.AUDIO_SAMPLE_RATE_RX / self.MODEM_SAMPLE_RATE) == codec2.api.FDMDV_OS_48 - # small hack for initializing codec2 via codec2.py module - # TODO: we need to change the entire modem module to integrate codec2 module + # Small hack for initializing codec2 via codec2.py module + # TODO: Need to change the entire modem module to integrate codec2 module self.c_lib = codec2.api self.resampler = codec2.resampler() self.modem_transmit_queue = MODEM_TRANSMIT_QUEUE self.modem_received_queue = MODEM_RECEIVED_QUEUE - # init FIFO queue to store modulation out in + # Init FIFO queue to store modulation out in self.modoutqueue = deque() - # define fft_data buffer + # Define fft_data buffer self.fft_data = bytes() - # open codec2 instance - self.datac0_freedv = cast(codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC0), c_void_p) - self.c_lib.freedv_set_tuning_range(self.datac0_freedv, c_float(static.TUNING_RANGE_FMIN), c_float(static.TUNING_RANGE_FMAX)) - self.datac0_bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(self.datac0_freedv)/8) - self.datac0_payload_per_frame = self.datac0_bytes_per_frame -2 + # Open codec2 instances + self.datac0_freedv = ctypes.cast(codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC0), ctypes.c_void_p) + self.c_lib.freedv_set_tuning_range(self.datac0_freedv, ctypes.c_float(static.TUNING_RANGE_FMIN), ctypes.c_float(static.TUNING_RANGE_FMAX)) + self.datac0_bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(self.datac0_freedv) / 8) + self.datac0_payload_per_frame = self.datac0_bytes_per_frame - 2 self.datac0_n_nom_modem_samples = self.c_lib.freedv_get_n_nom_modem_samples(self.datac0_freedv) self.datac0_n_tx_modem_samples = self.c_lib.freedv_get_n_tx_modem_samples(self.datac0_freedv) self.datac0_n_tx_preamble_modem_samples = self.c_lib.freedv_get_n_tx_preamble_modem_samples(self.datac0_freedv) self.datac0_n_tx_postamble_modem_samples = self.c_lib.freedv_get_n_tx_postamble_modem_samples(self.datac0_freedv) - self.datac0_bytes_out = create_string_buffer(self.datac0_bytes_per_frame) - codec2.api.freedv_set_frames_per_burst(self.datac0_freedv,1) + self.datac0_bytes_out = ctypes.create_string_buffer(self.datac0_bytes_per_frame) + codec2.api.freedv_set_frames_per_burst(self.datac0_freedv, 1) self.datac0_buffer = codec2.audio_buffer(2*self.AUDIO_FRAMES_PER_BUFFER_RX) - self.datac1_freedv = cast(codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC1), c_void_p) - self.c_lib.freedv_set_tuning_range(self.datac1_freedv, c_float(static.TUNING_RANGE_FMIN), c_float(static.TUNING_RANGE_FMAX)) - self.datac1_bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(self.datac1_freedv)/8) - self.datac1_bytes_out = create_string_buffer(self.datac1_bytes_per_frame) - codec2.api.freedv_set_frames_per_burst(self.datac1_freedv,1) + self.datac1_freedv = ctypes.cast(codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC1), ctypes.c_void_p) + self.c_lib.freedv_set_tuning_range(self.datac1_freedv, ctypes.c_float(static.TUNING_RANGE_FMIN), ctypes.c_float(static.TUNING_RANGE_FMAX)) + self.datac1_bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(self.datac1_freedv) / 8) + self.datac1_bytes_out = ctypes.create_string_buffer(self.datac1_bytes_per_frame) + codec2.api.freedv_set_frames_per_burst(self.datac1_freedv, 1) self.datac1_buffer = codec2.audio_buffer(2*self.AUDIO_FRAMES_PER_BUFFER_RX) - self.datac3_freedv = cast(codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC3), c_void_p) - self.c_lib.freedv_set_tuning_range(self.datac3_freedv, c_float(static.TUNING_RANGE_FMIN), c_float(static.TUNING_RANGE_FMAX)) - self.datac3_bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(self.datac3_freedv)/8) - self.datac3_bytes_out = create_string_buffer(self.datac3_bytes_per_frame) - codec2.api.freedv_set_frames_per_burst(self.datac3_freedv,1) + self.datac3_freedv = ctypes.cast(codec2.api.freedv_open(codec2.api.FREEDV_MODE_DATAC3), ctypes.c_void_p) + self.c_lib.freedv_set_tuning_range(self.datac3_freedv, ctypes.c_float(static.TUNING_RANGE_FMIN), ctypes.c_float(static.TUNING_RANGE_FMAX)) + self.datac3_bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(self.datac3_freedv) / 8) + self.datac3_bytes_out = ctypes.create_string_buffer(self.datac3_bytes_per_frame) + codec2.api.freedv_set_frames_per_burst(self.datac3_freedv, 1) self.datac3_buffer = codec2.audio_buffer(2*self.AUDIO_FRAMES_PER_BUFFER_RX) - self.fsk_ldpc_freedv_0 = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV)), c_void_p) - self.fsk_ldpc_bytes_per_frame_0 = int(codec2.api.freedv_get_bits_per_modem_frame(self.fsk_ldpc_freedv_0)/8) - self.fsk_ldpc_bytes_out_0 = create_string_buffer(self.fsk_ldpc_bytes_per_frame_0) - #codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0,1) + self.fsk_ldpc_freedv_0 = ctypes.cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV)), ctypes.c_void_p) + self.fsk_ldpc_bytes_per_frame_0 = int(codec2.api.freedv_get_bits_per_modem_frame(self.fsk_ldpc_freedv_0) / 8) + self.fsk_ldpc_bytes_out_0 = ctypes.create_string_buffer(self.fsk_ldpc_bytes_per_frame_0) + #codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0, 1) self.fsk_ldpc_buffer_0 = codec2.audio_buffer(self.AUDIO_FRAMES_PER_BUFFER_RX) - self.fsk_ldpc_freedv_1 = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV)), c_void_p) - self.fsk_ldpc_bytes_per_frame_1 = int(codec2.api.freedv_get_bits_per_modem_frame(self.fsk_ldpc_freedv_1)/8) - self.fsk_ldpc_bytes_out_1 = create_string_buffer(self.fsk_ldpc_bytes_per_frame_1) - #codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0,1) + self.fsk_ldpc_freedv_1 = ctypes.cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV)), ctypes.c_void_p) + self.fsk_ldpc_bytes_per_frame_1 = int(codec2.api.freedv_get_bits_per_modem_frame(self.fsk_ldpc_freedv_1) / 8) + self.fsk_ldpc_bytes_out_1 = ctypes.create_string_buffer(self.fsk_ldpc_bytes_per_frame_1) + #codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0, 1) self.fsk_ldpc_buffer_1 = codec2.audio_buffer(self.AUDIO_FRAMES_PER_BUFFER_RX) # initial nin values @@ -126,22 +132,21 @@ class RF(): # --------------------------------------------CREATE PYAUDIO INSTANCE if not TESTMODE: try: - self.stream = sd.RawStream(channels=1, dtype='int16', callback=self.callback, device=(static.AUDIO_INPUT_DEVICE, static.AUDIO_OUTPUT_DEVICE), samplerate = self.AUDIO_SAMPLE_RATE_RX, blocksize=4800) atexit.register(self.stream.stop) - structlog.get_logger("structlog").info("opened audio devices") + structlog.get_logger("structlog").info("[MDM] init: opened audio devices") except Exception as e: - structlog.get_logger("structlog").error("can't open audio device. Exit", e=e) - os._exit(1) + structlog.get_logger("structlog").error("[MDM] init: can't open audio device. Exit", e=e) + sys.exit(1) try: - structlog.get_logger("structlog").debug("[TNC] starting pyaudio callback") + structlog.get_logger("structlog").debug("[MDM] init: starting pyaudio callback") #self.audio_stream.start_stream() self.stream.start() except Exception as e: - structlog.get_logger("structlog").error("[TNC] starting pyaudio callback failed", e=e) + structlog.get_logger("structlog").error("[MDM] init: starting pyaudio callback failed", e=e) else: # create a stream object for simulating audio stream @@ -152,10 +157,10 @@ class RF(): # create mkfifo buffer try: - os.mkfifo(RXCHANNEL) os.mkfifo(TXCHANNEL) - except: + except Exception as e: + structlog.get_logger("structlog").error(f"[MDM] init:mkfifo: Exception: {e}") pass mkfifo_write_callback_thread = threading.Thread(target=self.mkfifo_write_callback, name="MKFIFO WRITE CALLBACK THREAD",daemon=True) @@ -165,7 +170,7 @@ class RF(): mkfifo_read_callback_thread.start() # --------------------------------------------INIT AND OPEN HAMLIB - # check how we want to control the radio + # Check how we want to control the radio if static.HAMLIB_RADIOCONTROL == 'direct': import rig elif static.HAMLIB_RADIOCONTROL == 'rigctl': @@ -182,32 +187,32 @@ class RF(): # --------------------------------------------START DECODER THREAD if static.ENABLE_FFT: - fft_thread = threading.Thread(target=self.calculate_fft, name="FFT_THREAD" ,daemon=True) + fft_thread = threading.Thread(target=self.calculate_fft, name="FFT_THREAD", daemon=True) fft_thread.start() - audio_thread_datac0 = threading.Thread(target=self.audio_datac0, name="AUDIO_THREAD DATAC0",daemon=True) + audio_thread_datac0 = threading.Thread(target=self.audio_datac0, name="AUDIO_THREAD DATAC0", daemon=True) audio_thread_datac0.start() - audio_thread_datac1 = threading.Thread(target=self.audio_datac1, name="AUDIO_THREAD DATAC1",daemon=True) + audio_thread_datac1 = threading.Thread(target=self.audio_datac1, name="AUDIO_THREAD DATAC1", daemon=True) audio_thread_datac1.start() - audio_thread_datac3 = threading.Thread(target=self.audio_datac3, name="AUDIO_THREAD DATAC3",daemon=True) + audio_thread_datac3 = threading.Thread(target=self.audio_datac3, name="AUDIO_THREAD DATAC3", daemon=True) audio_thread_datac3.start() if static.ENABLE_FSK: - audio_thread_fsk_ldpc0 = threading.Thread(target=self.audio_fsk_ldpc_0, name="AUDIO_THREAD FSK LDPC0",daemon=True) + audio_thread_fsk_ldpc0 = threading.Thread(target=self.audio_fsk_ldpc_0, name="AUDIO_THREAD FSK LDPC0", daemon=True) audio_thread_fsk_ldpc0.start() - audio_thread_fsk_ldpc1 = threading.Thread(target=self.audio_fsk_ldpc_1, name="AUDIO_THREAD FSK LDPC1",daemon=True) + audio_thread_fsk_ldpc1 = threading.Thread(target=self.audio_fsk_ldpc_1, name="AUDIO_THREAD FSK LDPC1", daemon=True) audio_thread_fsk_ldpc1.start() - hamlib_thread = threading.Thread(target=self.update_rig_data, name="HAMLIB_THREAD",daemon=True) + hamlib_thread = threading.Thread(target=self.update_rig_data, name="HAMLIB_THREAD", daemon=True) hamlib_thread.start() - worker_received = threading.Thread(target=self.worker_received, name="WORKER_THREAD",daemon=True) + worker_received = threading.Thread(target=self.worker_received, name="WORKER_THREAD", daemon=True) worker_received.start() - worker_transmit = threading.Thread(target=self.worker_transmit, name="WORKER_THREAD",daemon=True) + worker_transmit = threading.Thread(target=self.worker_transmit, name="WORKER_THREAD", daemon=True) worker_transmit.start() # -------------------------------------------------------------------------------------------------------- @@ -218,7 +223,6 @@ class RF(): data_in48k = bytes() with open(RXCHANNEL, 'rb') as fifo: for line in fifo: - data_in48k += line while len(data_in48k) >= 48: @@ -227,15 +231,13 @@ class RF(): data_in48k = data_in48k[48:] length_x = len(x) - if not self.datac0_buffer.nbuffer+length_x > self.datac0_buffer.size: + if not self.datac0_buffer.nbuffer + length_x > self.datac0_buffer.size: self.datac0_buffer.push(x) - if not self.datac1_buffer.nbuffer+length_x > self.datac1_buffer.size: - if RECEIVE_DATAC1: + if not self.datac1_buffer.nbuffer + length_x > self.datac1_buffer.size and RECEIVE_DATAC1: self.datac1_buffer.push(x) - if not self.datac3_buffer.nbuffer+length_x > self.datac3_buffer.size: - if RECEIVE_DATAC3: + if not self.datac3_buffer.nbuffer + length_x > self.datac3_buffer.size and RECEIVE_DATAC3: self.datac3_buffer.push(x) def mkfifo_write_callback(self): @@ -255,80 +257,77 @@ class RF(): fifo_write.write(data_out48k) fifo_write.flush() - # -------------------------------------------------------------------------------------------------------- - #def audio_callback(self, data_in48k, frame_count, time_info, status): + # -------------------------------------------------------------------- def callback(self, data_in48k, outdata, frames, time, status): """ Args: - data_in48k: - frame_count: - time_info: + data_in48k: Incoming data received + outdata: Container for the data returned + frames: Number of frames + time: status: Returns: - + Nothing """ x = np.frombuffer(data_in48k, dtype=np.int16) x = self.resampler.resample48_to_8(x) - length_x = len(x) - - # avoid decoding when transmitting to reduce CPU + # Avoid decoding when transmitting to reduce CPU if not static.TRANSMITTING: - # avoid buffer overflow by filling only if buffer not full - if not self.datac0_buffer.nbuffer+length_x > self.datac0_buffer.size: + length_x = len(x) + # Avoid buffer overflow by filling only if buffer not full + if not self.datac0_buffer.nbuffer + length_x > self.datac0_buffer.size: self.datac0_buffer.push(x) else: static.BUFFER_OVERFLOW_COUNTER[0] += 1 - # avoid buffer overflow by filling only if buffer not full and selected datachannel mode - if not self.datac1_buffer.nbuffer+length_x > self.datac1_buffer.size: + # Avoid buffer overflow by filling only if buffer not full and selected datachannel mode + if not self.datac1_buffer.nbuffer + length_x > self.datac1_buffer.size: if RECEIVE_DATAC1: self.datac1_buffer.push(x) else: static.BUFFER_OVERFLOW_COUNTER[1] += 1 - # avoid buffer overflow by filling only if buffer not full and selected datachannel mode - if not self.datac3_buffer.nbuffer+length_x > self.datac3_buffer.size: + # Avoid buffer overflow by filling only if buffer not full and selected datachannel mode + if not self.datac3_buffer.nbuffer + length_x > self.datac3_buffer.size: if RECEIVE_DATAC3: self.datac3_buffer.push(x) else: static.BUFFER_OVERFLOW_COUNTER[2] += 1 - # avoid buffer overflow by filling only if buffer not full and selected datachannel mode - if not self.fsk_ldpc_buffer_0.nbuffer+length_x > self.fsk_ldpc_buffer_0.size: + # Avoid buffer overflow by filling only if buffer not full and selected datachannel mode + if not self.fsk_ldpc_buffer_0.nbuffer + length_x > self.fsk_ldpc_buffer_0.size: if static.ENABLE_FSK: self.fsk_ldpc_buffer_0.push(x) else: static.BUFFER_OVERFLOW_COUNTER[3] += 1 - # avoid buffer overflow by filling only if buffer not full and selected datachannel mode - if not self.fsk_ldpc_buffer_1.nbuffer+length_x > self.fsk_ldpc_buffer_1.size: + # Avoid buffer overflow by filling only if buffer not full and selected datachannel mode + if not self.fsk_ldpc_buffer_1.nbuffer + length_x > self.fsk_ldpc_buffer_1.size: if RECEIVE_FSK_LDPC_1 and static.ENABLE_FSK: self.fsk_ldpc_buffer_1.push(x) else: static.BUFFER_OVERFLOW_COUNTER[4] += 1 if len(self.modoutqueue) <= 0 or self.mod_out_locked: - #if not self.modoutqueue or self.mod_out_locked: + # if not self.modoutqueue or self.mod_out_locked: data_out48k = np.zeros(frames, dtype=np.int16) self.fft_data = x - else: data_out48k = self.modoutqueue.popleft() self.fft_data = data_out48k try: outdata[:] = data_out48k[:frames] - except Exception as e: - print(e) + except IndexError as e: + structlog.get_logger("structlog").debug(f"[MDM] callback: IndexError: {e}") - #return (data_out48k, audio.pyaudio.paContinue) + # return (data_out48k, audio.pyaudio.paContinue) - # -------------------------------------------------------------------------------------------------------- - - def transmit(self, mode, repeats, repeat_delay, frames): + # -------------------------------------------------------------------- + def transmit(self, mode, repeats: int, repeat_delay: int, frames: bytearray): """ Args: @@ -340,100 +339,97 @@ class RF(): Returns: """ + structlog.get_logger("structlog").debug("[MDM] transmit", mode=mode) static.TRANSMITTING = True - # toggle ptt early to save some time and send ptt state via socket + # Toggle ptt early to save some time and send ptt state via socket static.PTT_STATE = self.hamlib.set_ptt(True) jsondata = {"ptt":"True"} data_out = json.dumps(jsondata) sock.SOCKET_QUEUE.put(data_out) - # open codec2 instance + # Open codec2 instance self.MODE = mode - if self.MODE == 'FSK_LDPC_0' or self.MODE == 200: - freedv = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV)), c_void_p) - elif self.MODE == 'FSK_LDPC_1' or self.MODE == 201: - freedv = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV)), c_void_p) - else: - freedv = cast(codec2.api.freedv_open(self.MODE), c_void_p) + freedv = open_codec2_instance(self.MODE) - # get number of bytes per frame for mode - bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(freedv)/8) - payload_bytes_per_frame = bytes_per_frame -2 - # init buffer for data + # Get number of bytes per frame for mode + bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(freedv) / 8) + payload_bytes_per_frame = bytes_per_frame - 2 + + # Init buffer for data n_tx_modem_samples = codec2.api.freedv_get_n_tx_modem_samples(freedv) - mod_out = create_string_buffer(n_tx_modem_samples * 2) + mod_out = ctypes.create_string_buffer(n_tx_modem_samples * 2) - # init buffer for preample + # Init buffer for preample n_tx_preamble_modem_samples = codec2.api.freedv_get_n_tx_preamble_modem_samples(freedv) - mod_out_preamble = create_string_buffer(n_tx_preamble_modem_samples * 2) + mod_out_preamble = ctypes.create_string_buffer(n_tx_preamble_modem_samples * 2) - # init buffer for postamble + # Init buffer for postamble n_tx_postamble_modem_samples = codec2.api.freedv_get_n_tx_postamble_modem_samples(freedv) - mod_out_postamble = create_string_buffer(n_tx_postamble_modem_samples * 2) + mod_out_postamble = ctypes.create_string_buffer(n_tx_postamble_modem_samples * 2) - # add empty data to handle ptt toggle time - data_delay_mseconds = 0 #miliseconds - data_delay = int(self.MODEM_SAMPLE_RATE*(data_delay_mseconds/1000)) - mod_out_silence = create_string_buffer(data_delay*2) + # Add empty data to handle ptt toggle time + data_delay_mseconds = 0 # milliseconds + data_delay = int(self.MODEM_SAMPLE_RATE * (data_delay_mseconds / 1000)) + mod_out_silence = ctypes.create_string_buffer(data_delay * 2) txbuffer = bytes(mod_out_silence) - structlog.get_logger("structlog").debug("TRANSMIT", mode=self.MODE, payload=payload_bytes_per_frame) + structlog.get_logger("structlog").debug("[MDM] TRANSMIT", mode=self.MODE, payload=payload_bytes_per_frame) - for i in range(0,repeats): + for _ in range(repeats): # codec2 fsk preamble may be broken - at least it sounds like that so we are disabling it for testing - if not self.MODE == 'FSK_LDPC_0' or self.MODE == 200 or self.MODE == 'FSK_LDPC_1' or self.MODE == 201: - # write preamble to txbuffer + if self.MODE not in ['FSK_LDPC_0', 'FSK_LDPC_1', 200, 201]: + # Write preamble to txbuffer codec2.api.freedv_rawdatapreambletx(freedv, mod_out_preamble) txbuffer += bytes(mod_out_preamble) - # create modulaton for n frames in list - for n in range(0,len(frames)): - # create buffer for data - buffer = bytearray(payload_bytes_per_frame) # use this if CRC16 checksum is required ( DATA1-3) - buffer[:len(frames[n])] = frames[n] # set buffersize to length of data which will be send - # create crc for data frame - we are using the crc function shipped with codec2 to avoid - # crc algorithm incompatibilities - crc = ctypes.c_ushort(codec2.api.freedv_gen_crc16(bytes(buffer), payload_bytes_per_frame)) # generate CRC16 - crc = crc.value.to_bytes(2, byteorder='big') # convert crc to 2 byte hex string - buffer += crc # append crc16 to buffer + # Create modulaton for n frames in list + for n in range(len(frames)): + # Create buffer for data + buffer = bytearray(payload_bytes_per_frame) # Use this if CRC16 checksum is required ( DATA1-3) + buffer[:len(frames[n])] = frames[n] # Set buffersize to length of data which will be send + + # Create crc for data frame - we are using the crc function shipped with codec2 to avoid + # CRC algorithm incompatibilities + crc = ctypes.c_ushort(codec2.api.freedv_gen_crc16(bytes(buffer), payload_bytes_per_frame)) # Generate CRC16 + crc = crc.value.to_bytes(2, byteorder='big') # Convert crc to 2 byte hex string + buffer += crc # Append crc16 to buffer data = (ctypes.c_ubyte * bytes_per_frame).from_buffer_copy(buffer) codec2.api.freedv_rawdatatx(freedv,mod_out,data) # modulate DATA and save it into mod_out pointer txbuffer += bytes(mod_out) - # codec2 fsk preamble may be broken - at least it sounds like that so we are disabling it for testing - if not self.MODE == 'FSK_LDPC_0' or self.MODE == 200 or self.MODE == 'FSK_LDPC_1' or self.MODE == 201: - # write preamble to txbuffer + # codec2 fsk postamble may be broken - at least it sounds like that so we are disabling it for testing + if self.MODE not in ['FSK_LDPC_0', 'FSK_LDPC_1', 200, 201]: + # Write postamble to txbuffer codec2.api.freedv_rawdatapostambletx(freedv, mod_out_postamble) + # Append postamble to txbuffer txbuffer += bytes(mod_out_postamble) - # append postamble to txbuffer - # add delay to end of frames - samples_delay = int(self.MODEM_SAMPLE_RATE*(repeat_delay/1000)) - mod_out_silence = create_string_buffer(samples_delay*2) + + # Add delay to end of frames + samples_delay = int(self.MODEM_SAMPLE_RATE * (repeat_delay / 1000)) + mod_out_silence = ctypes.create_string_buffer(samples_delay * 2) txbuffer += bytes(mod_out_silence) - # resample up to 48k (resampler works on np.int16) + # Re-sample back up to 48k (resampler works on np.int16) x = np.frombuffer(txbuffer, dtype=np.int16) x = set_audio_volume(x, static.TX_AUDIO_LEVEL) txbuffer_48k = self.resampler.resample8_to_48(x) - # explicitly lock our usage of mod_out_queue if needed - # deaktivated for testing purposes + # Explicitly lock our usage of mod_out_queue if needed + # Deaktivated for testing purposes self.mod_out_locked = False # ------------------------------- - chunk_length = self.AUDIO_FRAMES_PER_BUFFER_TX #4800 chunk = [txbuffer_48k[i:i+chunk_length] for i in range(0, len(txbuffer_48k), chunk_length)] for c in chunk: - if len(c) < chunk_length: delta = chunk_length - len(c) delta_zeros = np.zeros(delta, dtype=np.int16) c = np.append(c, delta_zeros) + #structlog.get_logger("structlog").debug("[MDM] mod out shorter than audio buffer", delta=delta) - #structlog.get_logger("structlog").debug("[TNC] mod out shorter than audio buffer", delta=delta) self.modoutqueue.append(c) # Release our mod_out_lock so we can use the queue @@ -444,12 +440,12 @@ class RF(): static.PTT_STATE = self.hamlib.set_ptt(False) - # push ptt state to socket stream + # Push ptt state to socket stream jsondata = {"ptt":"False"} data_out = json.dumps(jsondata) sock.SOCKET_QUEUE.put(data_out) - # after processing we want to set the locking state back to true to be prepared for next transmission + # After processing, set the locking state back to true to be prepared for next transmission self.mod_out_locked = True self.c_lib.freedv_close(freedv) @@ -468,7 +464,7 @@ class RF(): self.datac0_buffer.pop(self.datac0_nin) self.datac0_nin = codec2.api.freedv_nin(self.datac0_freedv) if nbytes_datac0 == self.datac0_bytes_per_frame: - self.modem_received_queue.put([self.datac0_bytes_out, self.datac0_freedv ,self.datac0_bytes_per_frame]) + self.modem_received_queue.put([self.datac0_bytes_out, self.datac0_freedv, self.datac0_bytes_per_frame]) #self.get_scatter(self.datac0_freedv) self.calculate_snr(self.datac0_freedv) @@ -483,7 +479,7 @@ class RF(): self.datac1_buffer.pop(self.datac1_nin) self.datac1_nin = codec2.api.freedv_nin(self.datac1_freedv) if nbytes_datac1 == self.datac1_bytes_per_frame: - self.modem_received_queue.put([self.datac1_bytes_out, self.datac1_freedv ,self.datac1_bytes_per_frame]) + self.modem_received_queue.put([self.datac1_bytes_out, self.datac1_freedv, self.datac1_bytes_per_frame]) #self.get_scatter(self.datac1_freedv) self.calculate_snr(self.datac1_freedv) @@ -498,7 +494,7 @@ class RF(): self.datac3_buffer.pop(self.datac3_nin) self.datac3_nin = codec2.api.freedv_nin(self.datac3_freedv) if nbytes_datac3 == self.datac3_bytes_per_frame: - self.modem_received_queue.put([self.datac3_bytes_out, self.datac3_freedv ,self.datac3_bytes_per_frame]) + self.modem_received_queue.put([self.datac3_bytes_out, self.datac3_freedv, self.datac3_bytes_per_frame]) #self.get_scatter(self.datac3_freedv) self.calculate_snr(self.datac3_freedv) @@ -513,7 +509,7 @@ class RF(): self.fsk_ldpc_buffer_0.pop(self.fsk_ldpc_nin_0) self.fsk_ldpc_nin_0 = codec2.api.freedv_nin(self.fsk_ldpc_freedv_0) if nbytes_fsk_ldpc_0 == self.fsk_ldpc_bytes_per_frame_0: - self.modem_received_queue.put([self.fsk_ldpc_bytes_out_0, self.fsk_ldpc_freedv_0 ,self.fsk_ldpc_bytes_per_frame_0]) + self.modem_received_queue.put([self.fsk_ldpc_bytes_out_0, self.fsk_ldpc_freedv_0, self.fsk_ldpc_bytes_per_frame_0]) #self.get_scatter(self.fsk_ldpc_freedv_0) self.calculate_snr(self.fsk_ldpc_freedv_0) @@ -528,7 +524,7 @@ class RF(): self.fsk_ldpc_buffer_1.pop(self.fsk_ldpc_nin_1) self.fsk_ldpc_nin_1 = codec2.api.freedv_nin(self.fsk_ldpc_freedv_1) if nbytes_fsk_ldpc_1 == self.fsk_ldpc_bytes_per_frame_1: - self.modem_received_queue.put([self.fsk_ldpc_bytes_out_1, self.fsk_ldpc_freedv_1 ,self.fsk_ldpc_bytes_per_frame_1]) + self.modem_received_queue.put([self.fsk_ldpc_bytes_out_1, self.fsk_ldpc_freedv_1, self.fsk_ldpc_bytes_per_frame_1]) #self.get_scatter(self.fsk_ldpc_freedv_1) self.calculate_snr(self.fsk_ldpc_freedv_1) @@ -538,6 +534,7 @@ class RF(): while True: data = self.modem_transmit_queue.get() + structlog.get_logger("structlog").debug("[MDM] worker_transmit", mode=data[0]) self.transmit(mode=data[0], repeats=data[1], repeat_delay=data[2], frames=data[3]) #self.modem_transmit_queue.task_done() @@ -561,7 +558,7 @@ class RF(): Returns: """ - modemStats = MODEMSTATS() + modemStats = codec2.MODEMSTATS() self.c_lib.freedv_get_modem_extended_stats.restype = None self.c_lib.freedv_get_modem_extended_stats(freedv, ctypes.byref(modemStats)) offset = round(modemStats.foff) * (-1) @@ -577,30 +574,32 @@ class RF(): Returns: """ - if static.ENABLE_SCATTER: - modemStats = codec2.MODEMSTATS() - self.c_lib.freedv_get_modem_extended_stats.restype = None - self.c_lib.freedv_get_modem_extended_stats(freedv, ctypes.byref(modemStats)) + if not static.ENABLE_SCATTER: + return - scatterdata = [] - scatterdata_small = [] - for i in range(codec2.MODEM_STATS_NC_MAX): - for j in range(codec2.MODEM_STATS_NR_MAX): - # check if odd or not to get every 2nd item for x - if (j % 2) == 0: - xsymbols = round(modemStats.rx_symbols[i][j]/1000) - ysymbols = round(modemStats.rx_symbols[i][j+1]/1000) - # check if value 0.0 or has real data - if xsymbols != 0.0 and ysymbols != 0.0: - scatterdata.append({"x": xsymbols, "y": ysymbols}) + modemStats = codec2.MODEMSTATS() + self.c_lib.freedv_get_modem_extended_stats.restype = None + self.c_lib.freedv_get_modem_extended_stats(freedv, ctypes.byref(modemStats)) - # only append scatter data if new data arrived - if 150 > len(scatterdata) > 0: - static.SCATTER = scatterdata - else: - # only take every tenth data point - scatterdata_small = scatterdata[::10] - static.SCATTER = scatterdata_small + scatterdata = [] + scatterdata_small = [] + for i in range(codec2.MODEM_STATS_NC_MAX): + for j in range(codec2.MODEM_STATS_NR_MAX): + # check if odd or not to get every 2nd item for x + if (j % 2) == 0: + xsymbols = round(modemStats.rx_symbols[i][j] / 1000) + ysymbols = round(modemStats.rx_symbols[i][j + 1] / 1000) + # check if value 0.0 or has real data + if xsymbols != 0.0 and ysymbols != 0.0: + scatterdata.append({"x": xsymbols, "y": ysymbols}) + + # only append scatter data if new data arrived + if 150 > len(scatterdata) > 0: + static.SCATTER = scatterdata + else: + # only take every tenth data point + scatterdata_small = scatterdata[::10] + static.SCATTER = scatterdata_small def calculate_snr(self, freedv): """ @@ -612,27 +611,29 @@ class RF(): """ try: - modem_stats_snr = c_float() - modem_stats_sync = c_int() + modem_stats_snr = ctypes.c_float() + modem_stats_sync = ctypes.c_int() - self.c_lib.freedv_get_modem_stats(freedv, byref(modem_stats_sync), byref(modem_stats_snr)) + self.c_lib.freedv_get_modem_stats(freedv, ctypes.byref(modem_stats_sync), ctypes.byref(modem_stats_snr)) modem_stats_snr = modem_stats_snr.value modem_stats_sync = modem_stats_sync.value snr = round(modem_stats_snr, 1) - print(snr) - static.SNR = np.clip(snr, 0, 255) #limit to max value of 255 + structlog.get_logger("structlog").info("[MDM] calculate_snr: ", snr=snr) + # print(snr) + static.SNR = np.clip(snr, 0, 255) # limit to max value of 255 return static.SNR - except: + except Exception as e: + structlog.get_logger("structlog").error(f"[MDM] calculate_snr: Exception: {e}") static.SNR = 0 return static.SNR def update_rig_data(self): """ """ while True: - #time.sleep(1.5) + # time.sleep(1.5) threading.Event().wait(0.5) - #(static.HAMLIB_FREQUENCY, static.HAMLIB_MODE, static.HAMLIB_BANDWITH, static.PTT_STATE) = self.hamlib.get_rig_data() + # (static.HAMLIB_FREQUENCY, static.HAMLIB_MODE, static.HAMLIB_BANDWITH, static.PTT_STATE) = self.hamlib.get_rig_data() static.HAMLIB_FREQUENCY = self.hamlib.get_frequency() static.HAMLIB_MODE = self.hamlib.get_mode() static.HAMLIB_BANDWITH = self.hamlib.get_bandwith() @@ -651,7 +652,6 @@ class RF(): # https://gist.github.com/ZWMiller/53232427efc5088007cab6feee7c6e4c # Fast Fourier Transform, 10*log10(abs) is to scale it to dB # and make sure it's not imaginary - try: fftarray = np.fft.rfft(self.fft_data) @@ -662,30 +662,25 @@ class RF(): # get average of dfft avg = np.mean(dfft) - # detect signals which are higher than the average + 10 ( +10 smoothes the output ) - # data higher than the average must be a signal. Therefore we are setting it to 100 so it will be highlighted - # have to do this when we are not transmittig so our own sending data will not affect this too much + # Detect signals which are higher than the average + 10 ( +10 smoothes the output ) + # Data higher than the average must be a signal. Therefore we are setting it to 100 so it will be highlighted + # Have to do this when we are not transmitting so our own sending data will not affect this too much if not static.TRANSMITTING: dfft[dfft>avg+10] = 100 - # calculate audio max value + # Calculate audio max value # static.AUDIO_RMS = np.amax(self.fft_data) - # check for signals higher than average by checking for "100" - # if we have a signal, increment our channel_busy delay counter so we have a smoother state toggle - - if np.sum(dfft[dfft>avg+10]) >= 300 and not static.TRANSMITTING: + # Check for signals higher than average by checking for "100" + # If we have a signal, increment our channel_busy delay counter so we have a smoother state toggle + if np.sum(dfft[dfft > avg + 10]) >= 300 and not static.TRANSMITTING: static.CHANNEL_BUSY = True - channel_busy_delay += 5 - # limit delay counter to a maximun of 30. The higher this value, the linger we will wait until releasing state - if channel_busy_delay > 50: - channel_busy_delay = 50 + # Limit delay counter to a maximun of 30. The higher this value, the linger we will wait until releasing state + channel_busy_delay = min(channel_busy_delay + 5, 50) else: - # decrement channel busy counter if no signal has been detected. - channel_busy_delay -= 1 - if channel_busy_delay < 0: - channel_busy_delay = 0 - # if our channel busy counter reached 0, we toggle state to False + # Decrement channel busy counter if no signal has been detected. + channel_busy_delay = max(channel_busy_delay - 1, 0) + # If our channel busy counter reached 0, toggle state to False if channel_busy_delay == 0: static.CHANNEL_BUSY = False @@ -693,14 +688,12 @@ class RF(): dfft = np.around(dfft, 0) dfftlist = dfft.tolist() - static.FFT = dfftlist[0:320] #320 --> bandwith 3000 - - except: - structlog.get_logger("structlog").debug("[TNC] Setting fft=0") + static.FFT = dfftlist[:320] #320 --> bandwidth 3000 + except Exception as e: + structlog.get_logger("structlog").error(f"[MDM] calculate_fft: Exception: {e}") + structlog.get_logger("structlog").debug("[MDM] Setting fft=0") # else 0 static.FFT = [0] - else: - pass def set_frames_per_burst(self, n_frames_per_burst): """ @@ -711,9 +704,22 @@ class RF(): Returns: """ - codec2.api.freedv_set_frames_per_burst(self.datac1_freedv,n_frames_per_burst) - codec2.api.freedv_set_frames_per_burst(self.datac3_freedv,n_frames_per_burst) - codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0,n_frames_per_burst) + codec2.api.freedv_set_frames_per_burst(self.datac1_freedv, n_frames_per_burst) + codec2.api.freedv_set_frames_per_burst(self.datac3_freedv, n_frames_per_burst) + codec2.api.freedv_set_frames_per_burst(self.fsk_ldpc_freedv_0, n_frames_per_burst) + +def open_codec2_instance(mode): + """ Return a codec2 instance """ + if mode in ['FSK_LDPC_0', 200]: + return ctypes.cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, + ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV)), ctypes.c_void_p) + + if mode in ['FSK_LDPC_1', 201]: + return ctypes.cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, + ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV)), ctypes.c_void_p) + + return ctypes.cast(codec2.api.freedv_open(mode), ctypes.c_void_p) + def get_bytes_per_frame(mode): """ @@ -725,15 +731,11 @@ def get_bytes_per_frame(mode): Returns: """ - if mode == 200: - freedv = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_0_ADV)), c_void_p) - elif mode == 201: - freedv = cast(codec2.api.freedv_open_advanced(codec2.api.FREEDV_MODE_FSK_LDPC, ctypes.byref(codec2.api.FREEDV_MODE_FSK_LDPC_1_ADV)), c_void_p) - else: - freedv = cast(codec2.api.freedv_open(mode), c_void_p) + freedv = open_codec2_instance(mode) # get number of bytes per frame for mode - return int(codec2.api.freedv_get_bits_per_modem_frame(freedv)/8) + return int(codec2.api.freedv_get_bits_per_modem_frame(freedv) / 8) + def set_audio_volume(datalist, volume): data = np.fromstring(datalist, np.int16) * (volume / 100.) diff --git a/tnc/rig.py b/tnc/rig.py index 0016b77d..cdbf879f 100644 --- a/tnc/rig.py +++ b/tnc/rig.py @@ -12,11 +12,10 @@ hamlib_version = 0 # append local search path # check if we are running in a pyinstaller environment -try: - app_path = sys._MEIPASS -except: - app_path = os.path.abspath(".") -sys.path.append(app_path) +if hasattr(sys, "_MEIPASS"): + sys.path.append(getattr(sys, "_MEIPASS")) +else: + sys.path.append(os.path.abspath(".")) # try importing hamlib try: diff --git a/tnc/rigctl.py b/tnc/rigctl.py index 3e92e093..319d511a 100644 --- a/tnc/rigctl.py +++ b/tnc/rigctl.py @@ -2,17 +2,18 @@ # Intially created by Franco Spinelli, IW2DHW, 01/2022 # Updated by DJ2LS # -# # versione mia di rig.py per gestire Ft897D tramite rigctl e senza # fare alcun riferimento alla configurazione # # e' una pezza clamorosa ma serve per poter provare on-air il modem # -import subprocess -import structlog -import time -import sys import os +import subprocess +import sys +import time + +import structlog + # for rig_model -> rig_number only # set global hamlib version @@ -60,23 +61,22 @@ class radio: self.stop_bits = stop_bits self.handshake = handshake - # check if we are running in a pyinstaller environment - try: - app_path = sys._MEIPASS - except: - app_path = os.path.abspath(".") - sys.path.append(app_path) + # check if we are running in a pyinstaller environment + if hasattr(sys, "_MEIPASS"): + sys.path.append(getattr(sys, "_MEIPASS")) + else: + sys.path.append(os.path.abspath(".")) # get devicenumber by looking for deviceobject in Hamlib module try: import Hamlib self.devicenumber = int(getattr(Hamlib, self.devicename)) - except: + except Exception as e: if int(self.devicename): self.devicenumber = int(self.devicename) else: self.devicenumber = 6 #dummy - structlog.get_logger("structlog").warning("[RIGCTL] RADIO NOT FOUND USING DUMMY!", error=e) + structlog.get_logger("structlog").warning("[RIGCTL] Radio not found. Using DUMMY!", error=e) # set deviceport to dummy port, if we selected dummy model if self.devicenumber == 1 or self.devicenumber == 6: @@ -86,7 +86,7 @@ class radio: # select precompiled executable for win32/win64 rigctl # this is really a hack...somewhen we need a native hamlib integration for windows - if sys.platform == 'win32' or sys.platform == 'win64': + if sys.platform in ['win32', 'win64']: self.cmd = app_path + 'lib\\hamlib\\'+sys.platform+'\\rigctl -m %d -r %s -s %d ' % (int(self.devicenumber), self.deviceport, int(self.serialspeed)) else: diff --git a/tnc/rigctld.py b/tnc/rigctld.py index f21b1d3f..422a6e95 100644 --- a/tnc/rigctld.py +++ b/tnc/rigctld.py @@ -1,22 +1,24 @@ #!/usr/bin/env python3 -import socket -import structlog -import log_handler -import logging -import time -import static - # class taken from darsidelemm # rigctl - https://github.com/darksidelemm/rotctld-web-gui/blob/master/rotatorgui.py#L35 # # modified and adjusted to FreeDATA needs by DJ2LS +import logging +import socket +import time + +import structlog + +import log_handler +import static + # set global hamlib version hamlib_version = 0 class radio(): - """rotctld (hamlib) communication class""" + """rigctld (hamlib) communication class""" # Note: This is a massive hack. def __init__(self, hostname="localhost", port=4532, poll_rate=5, timeout=5): @@ -51,11 +53,11 @@ class radio(): self.port = int(rigctld_port) if self.connect(): - logging.debug(f"Rigctl intialized") + logging.debug("Rigctl intialized") return True - else: - structlog.get_logger("structlog").error("[RIGCTLD] Can't connect to rigctld!", ip=self.hostname, port=self.port) - return False + + structlog.get_logger("structlog").error("[RIGCTLD] Can't connect to rigctld!", ip=self.hostname, port=self.port) + return False def connect(self): """Connect to rigctld instance""" @@ -112,7 +114,7 @@ class radio(): mode = data[0] return mode.decode("utf-8") except: - 0 + return 0 def get_bandwith(self): """ """ diff --git a/tnc/sock.py b/tnc/sock.py index c3e3bd47..6b6179b0 100644 --- a/tnc/sock.py +++ b/tnc/sock.py @@ -19,21 +19,25 @@ Created on Fri Dec 25 21:25:14 2020 # "data" : "..." """ +import atexit +import base64 +import logging +import os +import queue import socketserver +import sys import threading -import ujson as json import time -import static + +import psutil +import structlog +import ujson as json + +import audio import data_handler import helpers -import sys -import os -import logging, structlog, log_handler -import queue -import psutil -import audio -import base64 -import atexit +import log_handler +import static SOCKET_QUEUE = queue.Queue() DAEMON_QUEUE = queue.Queue() @@ -51,12 +55,12 @@ class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): """ """ + connection_alive = False def send_to_client(self): """ function called by socket handler send data to a network client if available - """ tempdata = b'' while self.connection_alive and not CLOSE_SIGNAL: @@ -85,8 +89,8 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): try: client.send(sock_data) except Exception as e: - print("connection lost...") - print(e) + # print("connection lost...") + structlog.get_logger("structlog").info("[SCK] Connection lost", e=e) self.connection_alive = False # we want to transmit scatter data only once to reduce network traffic @@ -161,7 +165,6 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): except: structlog.get_logger("structlog").warning("[SCK] client connection already removed from client list", client=self.request) - def process_tnc_commands(data): """ process tnc commands @@ -318,7 +321,6 @@ def process_tnc_commands(data): data_handler.DATA_QUEUE_TRANSMIT.put(['ARQ_RAW', binarydata, mode, n_frames, arq_uuid, mycallsign]) else: raise TypeError - except Exception as e: command_response("send_raw", False) structlog.get_logger("structlog").warning("[SCK] command execution error", e=e, command=received_json) @@ -343,7 +345,7 @@ def process_tnc_commands(data): "data-array": [], } - for i in range(0, len(static.RX_BUFFER)): + for i in range(len(static.RX_BUFFER)): #print(static.RX_BUFFER[i][4]) #rawdata = json.loads(static.RX_BUFFER[i][4]) base64_data = static.RX_BUFFER[i][4] @@ -408,11 +410,17 @@ def send_tnc_state(): } # add heard stations to heard stations object - for i in range(0, len(static.HEARD_STATIONS)): - output["stations"].append({"dxcallsign": str(static.HEARD_STATIONS[i][0], 'utf-8'), "dxgrid": str(static.HEARD_STATIONS[i][1], 'utf-8'),"timestamp": static.HEARD_STATIONS[i][2], "datatype": static.HEARD_STATIONS[i][3], "snr": static.HEARD_STATIONS[i][4], "offset": static.HEARD_STATIONS[i][5], "frequency": static.HEARD_STATIONS[i][6]}) + for heard in static.HEARD_STATIONS: + output["stations"].append({ + "dxcallsign": str(heard[0], 'utf-8'), + "dxgrid": str(heard[1], 'utf-8'), + "timestamp": heard[2], + "datatype": heard[3], + "snr": heard[4], + "offset": heard[5], + "frequency": heard[6]}) - jsondata = json.dumps(output) - return jsondata + return json.dumps(output) def process_daemon_commands(data): """ @@ -488,30 +496,30 @@ def process_daemon_commands(data): for item in received_json["parameter"][0]: structlog.get_logger("structlog").debug("[DMN] TNC Startup Config : " + item, value=received_json["parameter"][0][item]) - DAEMON_QUEUE.put(['STARTTNC', \ - mycall, \ - mygrid, \ - rx_audio, \ - tx_audio, \ - devicename, \ - deviceport, \ - serialspeed, \ - pttprotocol, \ - pttport, \ - data_bits, \ - stop_bits, \ - handshake, \ - radiocontrol, \ - rigctld_ip, \ - rigctld_port, \ - enable_scatter, \ - enable_fft, \ - low_bandwith_mode, \ - tuning_range_fmin, \ - tuning_range_fmax, \ - enable_fsk, \ - tx_audio_level, \ - respond_to_cq \ + DAEMON_QUEUE.put(['STARTTNC', + mycall, + mygrid, + rx_audio, + tx_audio, + devicename, + deviceport, + serialspeed, + pttprotocol, + pttport, + data_bits, + stop_bits, + handshake, + radiocontrol, + rigctld_ip, + rigctld_port, + enable_scatter, + enable_fft, + low_bandwith_mode, + tuning_range_fmin, + tuning_range_fmax, + enable_fsk, + tx_audio_level, + respond_to_cq, ]) command_response("start_tnc", True) @@ -533,18 +541,18 @@ def process_daemon_commands(data): rigctld_ip = str(received_json["parameter"][0]["rigctld_ip"]) rigctld_port = str(received_json["parameter"][0]["rigctld_port"]) - DAEMON_QUEUE.put(['TEST_HAMLIB', \ - devicename, \ - deviceport, \ - serialspeed, \ - pttprotocol, \ - pttport, \ - data_bits, \ - stop_bits, \ - handshake, \ - radiocontrol, \ - rigctld_ip, \ - rigctld_port \ + DAEMON_QUEUE.put(['TEST_HAMLIB', + devicename, + deviceport, + serialspeed, + pttprotocol, + pttport, + data_bits, + stop_bits, + handshake, + radiocontrol, + rigctld_ip, + rigctld_port, ]) command_response("test_hamlib", True) except Exception as e: @@ -569,7 +577,7 @@ def send_daemon_state(): send the daemon state to network """ try: - python_version = str(sys.version_info[0]) + "." + str(sys.version_info[1]) + python_version = f"{str(sys.version_info[0])}.{str(sys.version_info[1])}" output = { 'command': 'daemon_state', @@ -597,11 +605,7 @@ def send_daemon_state(): return None def command_response(command, status): - if status: - status = "OK" - else: - status = "Failed" - - jsondata = {"command_response": command, "status" : status} + s_status = "OK" if status else "Failed" + jsondata = {"command_response": command, "status" : s_status} data_out = json.dumps(jsondata) SOCKET_QUEUE.put(data_out)