diff --git a/.github/workflows/build-project.yml b/.github/workflows/build-project.yml index 4af5577e..9928ba61 100644 --- a/.github/workflows/build-project.yml +++ b/.github/workflows/build-project.yml @@ -79,9 +79,9 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] include: - - os: ubuntu-latest + - os: ubuntu-20.04 zip_name: ubuntu_tnc generator: Unix Makefiles @@ -97,7 +97,7 @@ jobs: - name: Install Linux dependencies - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu-20.04' run: | sudo apt install portaudio19-dev libhamlib-dev libhamlib-utils build-essential cmake python3-libhamlib2 python -m pip install --upgrade pip @@ -128,7 +128,7 @@ jobs: - name: Build codec2 Linux - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu-20.04' #working-directory: tnc run: | cd ~ @@ -139,7 +139,7 @@ jobs: - name: Build Linux - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu-20.04' working-directory: tnc run: | #pyinstaller -F daemon.py -n daemon @@ -171,7 +171,7 @@ jobs: node-version: 14 - name: Copy TNC to GUI Linux - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu-20.04' run: | cp -R ./tnc/dist ./gui/tnc ls -R @@ -218,7 +218,7 @@ jobs: release: name: Upload Release needs: [build_linux_release, build_windows_release] - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: diff --git a/README.md b/README.md index 9cfd7bda..e8d7282f 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ # FreeDATA -My attempt to create a free and opensource TNC with a GUI for [codec2](https://github.com/drowe67/codec2) to send data over HF channels. +My attempt to create a free and opensource TNC with a GUI for [codec2](https://github.com/drowe67/codec2) for sending data over HF channels. ## Under development This project is still a prototype and not usable at this time. Build steps for other OS than Ubuntu are provided, but not working, yet. +## Preview +![preview](https://github.com/DJ2LS/FreeDATA/blob/main/documentation/FreeDATA_preview.gif?raw=true "Preview") + ## Credits * David Rowe and the FreeDV team for developing the modem and libraries - FreeDV Codec 2 : https://github.com/drowe67/codec2 @@ -13,9 +16,9 @@ xssfox : https://github.com/xssfox/freedv-tnc * Wolfgang, for lending me his radio so I'm able to do real hf tests ## Running the Ubuntu app bundle -Just download the latest developer release from the releases section, unpack it and just start the ".AppImage file". No more dependencies +Download the latest developer release from the releases section, unpack it and just start the ".AppImage file". No more dependencies ## Manual installation -Please check the wiki for installation instructions +Please check the [wiki](https://github.com/DJ2LS/FreeDATA/wiki) for installation instructions diff --git a/documentation/FreeDATA_preview.gif b/documentation/FreeDATA_preview.gif new file mode 100644 index 00000000..4638daa2 Binary files /dev/null and b/documentation/FreeDATA_preview.gif differ diff --git a/gui/daemon.js b/gui/daemon.js index dc1060be..feec94d2 100644 --- a/gui/daemon.js +++ b/gui/daemon.js @@ -140,14 +140,14 @@ exports.getDaemonState = function() { // START TNC // ` `== multi line string -exports.startTNC = function(rx_audio, tx_audio, deviceid, deviceport, pttprotocol, pttport, serialspeed, pttspeed, data_bits, stop_bits, handshake) { +exports.startTNC = function(rx_audio, tx_audio, devicename, deviceport, pttprotocol, pttport, serialspeed, pttspeed, data_bits, stop_bits, handshake) { var json_command = JSON.stringify({ type: 'SET', command: 'STARTTNC', parameter: [{ rx_audio: rx_audio, tx_audio: tx_audio, - deviceid: deviceid, + devicename: devicename, deviceport: deviceport, pttprotocol: pttprotocol, pttport: pttport, @@ -172,13 +172,13 @@ exports.stopTNC = function() { } // TEST HAMLIB -exports.testHamlib = function(deviceid, deviceport, serialspeed, pttprotocol, pttport, pttspeed, data_bits, stop_bits, handshake) { +exports.testHamlib = function(devicename, deviceport, serialspeed, pttprotocol, pttport, pttspeed, data_bits, stop_bits, handshake) { var json_command = JSON.stringify({ type: 'GET', command: 'TEST_HAMLIB', parameter: [{ - deviceid: deviceid, + devicename: devicename, deviceport: deviceport, pttprotocol: pttprotocol, pttport: pttport, diff --git a/gui/preload-main.js b/gui/preload-main.js index 7c4dac95..adc7d274 100644 --- a/gui/preload-main.js +++ b/gui/preload-main.js @@ -330,9 +330,6 @@ advancedHamlibSettingsModal daemon.startTNC(rx_audio, tx_audio, deviceid, deviceport, pttprotocol, pttport, serialspeed, pttspeed, data_bits, stop_bits, handshake) - - - setTimeout(function() { @@ -340,7 +337,7 @@ advancedHamlibSettingsModal }, 3000); setTimeout(function() { sock.saveMyGrid(config.mygrid); - }, 4000); + }, 3500); }) // stopTNC button clicked @@ -649,7 +646,6 @@ ipcRenderer.on('action-update-tnc-state', (event, arg) => { } // BEACON STATE - console.log(arg.beacon_state) if (arg.beacon_state == 'True') { document.getElementById("startBeacon").className = "btn btn-success spinner-grow" document.getElementById("startBeacon").disabled = true @@ -670,27 +666,6 @@ ipcRenderer.on('action-update-tnc-state', (event, arg) => { document.getElementById("rms_level").setAttribute("aria-valuenow", arg.rms_level) document.getElementById("rms_level").setAttribute("style", "width:" + arg.rms_level + "%;") - // CHANNEL STATE - if (arg.channel_state == 'RECEIVING_SIGNALLING') { - document.getElementById("signalling_state").className = "btn btn-success"; - document.getElementById("data_state").className = "btn btn-secondary"; - - } else if (arg.channel_state == 'SENDING_SIGNALLING') { - document.getElementById("signalling_state").className = "btn btn-danger"; - document.getElementById("data_state").className = "btn btn-secondary"; - - } else if (arg.channel_state == 'RECEIVING_DATA') { - document.getElementById("signalling_state").className = "btn btn-secondary"; - document.getElementById("data_state").className = "btn btn-success"; - - } else if (arg.channel_state == 'SENDING_DATA') { - document.getElementById("signalling_state").className = "btn btn-secondary"; - document.getElementById("data_state").className = "btn btn-danger"; - } else { - document.getElementById("signalling_state").className = "btn btn-secondary" - document.getElementById("busy_state").className = "btn btn-secondary" - - } // SET FREQUENCY document.getElementById("frequency").innerHTML = arg.frequency @@ -1219,7 +1194,7 @@ ipcRenderer.on('action-update-rx-buffer', (event, arg) => { var fileName = document.createElement("td"); var fileNameText = document.createElement('span'); - var fileNameString = arg.data[i]['RXDATA'][0]['filename'] + var fileNameString = arg.data[i]['RXDATA'][0]['fn'] fileNameText.innerText = fileNameString fileName.appendChild(fileNameText); @@ -1247,7 +1222,7 @@ ipcRenderer.on('action-update-rx-buffer', (event, arg) => { // write file to rxdata folder - var base64String = arg.data[i]['RXDATA'][0]['data'] + var base64String = arg.data[i]['RXDATA'][0]['d'] // remove header from base64 String // https://www.codeblocq.com/2016/04/Convert-a-base64-string-to-a-file-in-Node/ var base64Data = base64String.split(';base64,').pop() diff --git a/gui/sock.js b/gui/sock.js index 429e79ea..fdfc8307 100644 --- a/gui/sock.js +++ b/gui/sock.js @@ -46,7 +46,7 @@ client.on('error', function(data) { let Data = { busy_state: "-", arq_state: "-", - channel_state: "-", + //channel_state: "-", frequency: "-", mode: "-", bandwith: "-", @@ -133,7 +133,7 @@ client.on('data', function(data) { ptt_state: data['PTT_STATE'], busy_state: data['TNC_STATE'], arq_state: data['ARQ_STATE'], - channel_state: data['CHANNEL_STATE'], + //channel_state: data['CHANNEL_STATE'], frequency: data['FREQUENCY'], mode: data['MODE'], bandwith: data['BANDWITH'], diff --git a/gui/src/index.html b/gui/src/index.html index 4e134b9b..d2da0044 100644 --- a/gui/src/index.html +++ b/gui/src/index.html @@ -905,7 +905,6 @@
Frames
@@ -973,22 +972,7 @@ - +
diff --git a/tnc/daemon.py b/tnc/daemon.py index 01787030..3e1da96b 100755 --- a/tnc/daemon.py +++ b/tnc/daemon.py @@ -20,7 +20,7 @@ import serial.tools.list_ports import static import crcengine import re - +import rig import logging, structlog, log_handler log_handler.setup_logging("daemon") @@ -153,13 +153,13 @@ class CMDTCPRequestHandler(socketserver.BaseRequestHandler): if received_json["type"] == 'SET' and received_json["command"] == 'STARTTNC' and not static.TNCSTARTED: rx_audio = str(received_json["parameter"][0]["rx_audio"]) tx_audio = str(received_json["parameter"][0]["tx_audio"]) - deviceid = str(received_json["parameter"][0]["deviceid"]) + devicename = str(received_json["parameter"][0]["devicename"]) deviceport = str(received_json["parameter"][0]["deviceport"]) serialspeed = str(received_json["parameter"][0]["serialspeed"]) pttprotocol = str(received_json["parameter"][0]["pttprotocol"]) pttport = str(received_json["parameter"][0]["pttport"]) - structlog.get_logger("structlog").warning("[DMN] Starting TNC", rig=deviceid, port=deviceport) + structlog.get_logger("structlog").warning("[DMN] Starting TNC", rig=devicename, port=deviceport) #print(received_json["parameter"][0]) # command = "--rx "+ rx_audio +" \ @@ -178,8 +178,8 @@ class CMDTCPRequestHandler(socketserver.BaseRequestHandler): options.append(tx_audio) options.append('--deviceport') options.append(deviceport) - options.append('--deviceid') - options.append(deviceid) + options.append('--devicename') + options.append(devicename) options.append('--serialspeed') options.append(serialspeed) options.append('--pttprotocol') @@ -276,101 +276,38 @@ class CMDTCPRequestHandler(socketserver.BaseRequestHandler): if received_json["type"] == 'GET' and received_json["command"] == 'TEST_HAMLIB': - print(received_json["parameter"]) - - deviceid = str(received_json["parameter"][0]["deviceid"]) - deviceport = str(received_json["parameter"][0]["deviceport"]) - serialspeed = str(received_json["parameter"][0]["serialspeed"]) - pttprotocol = str(received_json["parameter"][0]["pttprotocol"]) - pttport = str(received_json["parameter"][0]["pttport"]) - - pttspeed = str(received_json["parameter"][0]["pttspeed"]) - data_bits = str(received_json["parameter"][0]["data_bits"]) - stop_bits = str(received_json["parameter"][0]["stop_bits"]) - handshake = str(received_json["parameter"][0]["handshake"]) - - # try to init hamlib try: + print(received_json["parameter"]) - Hamlib.rig_set_debug(Hamlib.RIG_DEBUG_NONE) + devicename = str(received_json["parameter"][0]["devicename"]) + deviceport = str(received_json["parameter"][0]["deviceport"]) + serialspeed = str(received_json["parameter"][0]["serialspeed"]) + pttprotocol = str(received_json["parameter"][0]["pttprotocol"]) + pttport = str(received_json["parameter"][0]["pttport"]) - # get devicenumber by looking for deviceobject in Hamlib module - try: - devicenumber = getattr(Hamlib, deviceid) - except: - structlog.get_logger("structlog").error("[DMN] Hamlib: rig not supported...") - devicenumber = 0 - - - my_rig = Hamlib.Rig(int(devicenumber)) - my_rig.set_conf("rig_pathname", deviceport) - my_rig.set_conf("retry", "1") - my_rig.set_conf("serial_speed", serialspeed) - my_rig.set_conf("serial_handshake", handshake) - my_rig.set_conf("stop_bits", stop_bits) - my_rig.set_conf("data_bits", data_bits) + pttspeed = str(received_json["parameter"][0]["pttspeed"]) + data_bits = str(received_json["parameter"][0]["data_bits"]) + stop_bits = str(received_json["parameter"][0]["stop_bits"]) + handshake = str(received_json["parameter"][0]["handshake"]) - if pttprotocol == 'RIG': - hamlib_ptt_type = Hamlib.RIG_PTT_RIG - - elif pttprotocol == 'DTR-H': - hamlib_ptt_type = Hamlib.RIG_PTT_SERIAL_DTR - my_rig.set_conf("dtr_state", "HIGH") - my_rig.set_conf("ptt_type", "DTR") - - elif pttprotocol == 'DTR-L': - hamlib_ptt_type = Hamlib.RIG_PTT_SERIAL_DTR - my_rig.set_conf("dtr_state", "LOW") - my_rig.set_conf("ptt_type", "DTR") - - elif pttprotocol == 'RTS': - hamlib_ptt_type = Hamlib.RIG_PTT_SERIAL_RTS - my_rig.set_conf("dtr_state", "OFF") - my_rig.set_conf("ptt_type", "RTS") - - elif pttprotocol == 'PARALLEL': - hamlib_ptt_type = Hamlib.RIG_PTT_PARALLEL - - elif pttprotocol == 'MICDATA': - hamlib_ptt_type = Hamlib.RIG_PTT_RIG_MICDATA - - elif pttprotocol == 'CM108': - hamlib_ptt_type = Hamlib.RIG_PTT_CM108 - - else: # static.HAMLIB_PTT_TYPE == 'RIG_PTT_NONE': - hamlib_ptt_type = Hamlib.RIG_PTT_NONE + hamlib = rig.radio() + hamlib.open_rig(devicename=devicename, deviceport=deviceport, hamlib_ptt_type=pttprotocol, serialspeed=serialspeed) - - my_rig.open() - - 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("[DMN] Hamlib has no permissions", e = error) - help_url = 'https://github.com/DJ2LS/FreeDATA/wiki/UBUNTU-Manual-installation#1-permissions' - structlog.get_logger("structlog").error("[DMN] HELP:", check = help_url) - except: - structlog.get_logger("structlog").info("[DMN] Hamlib device openend", status='SUCCESS') - - my_rig.set_ptt(hamlib_ptt_type, 1) - pttstate = my_rig.get_ptt() - if pttstate == 1: + hamlib.set_ptt(True) + pttstate = hamlib.get_ptt() + if pttstate: structlog.get_logger("structlog").info("[DMN] Hamlib PTT", status = 'SUCCESS') data = {'COMMAND': 'TEST_HAMLIB', 'RESULT': 'SUCCESS'} - elif pttstate == 0: + elif not pttstate: structlog.get_logger("structlog").warning("[DMN] Hamlib PTT", status = 'NO SUCCESS') data = {'COMMAND': 'TEST_HAMLIB', 'RESULT': 'NOSUCCESS'} else: structlog.get_logger("structlog").error("[DMN] Hamlib PTT", status = 'FAILED') data = {'COMMAND': 'TEST_HAMLIB', 'RESULT': 'FAILED'} - - my_rig.set_ptt(hamlib_ptt_type, 0) - my_rig.close - + + hamlib.set_ptt(False) + hamlib.close_rig() + jsondata = json.dumps(data) self.request.sendall(bytes(jsondata, encoding)) diff --git a/tnc/data_handler.py b/tnc/data_handler.py index fd64fafd..75acaf71 100644 --- a/tnc/data_handler.py +++ b/tnc/data_handler.py @@ -13,7 +13,6 @@ from random import randrange import asyncio import ujson as json - import static import modem import helpers @@ -54,6 +53,7 @@ RX_START_OF_TRANSMISSION = 0 # time of transmission start # ################################################ def arq_data_received(data_in, bytes_per_frame): + # we neeed to declare our global variables, so the thread has access to them global RX_START_OF_TRANSMISSION global DATA_CHANNEL_LAST_RECEIVED @@ -92,7 +92,7 @@ def arq_data_received(data_in, bytes_per_frame): frame_progress = str(RX_N_FRAME_OF_BURST) + "/" + str(RX_N_FRAMES_PER_BURST) total_frame_progress = str(RX_N_FRAME_OF_DATA_FRAME) + "/" + str(RX_N_FRAMES_PER_DATA_FRAME) transmission_percent = str(static.ARQ_TRANSMISSION_PERCENT).zfill(3) - structlog.get_logger("structlog").info("[TNC] ARQ RX DATA", mode=DATA_CHANNEL_MODE, frames=frame_progress, percent=transmission_percent, frames_total=total_frame_progress) + structlog.get_logger("structlog").info("[TNC] ARQ | RX | DATA FRAME", mode=DATA_CHANNEL_MODE, frames=frame_progress, percent=transmission_percent, frames_total=total_frame_progress) # allocate ARQ_static.RX_FRAME_BUFFER as a list with "None" if not already done. This should be done only once per burst! # here we will save the N frame of a data frame to N list position so we can explicit search for it @@ -102,11 +102,7 @@ def arq_data_received(data_in, bytes_per_frame): # but better doing this, to avoid problems caused by old chunks in data if RX_N_FRAME_OF_DATA_FRAME == 1: static.RX_FRAME_BUFFER = [] - # - # # we set the start of transmission - 7 seconds, which is more or less the transfer time for the first frame - # RX_START_OF_TRANSMISSION = time.time() - 7 - # calculate_transfer_rate() - + #try appending data to frame buffer try: static.RX_FRAME_BUFFER[RX_N_FRAME_OF_DATA_FRAME] = bytes(data_in) @@ -123,10 +119,6 @@ def arq_data_received(data_in, bytes_per_frame): static.RX_FRAME_BUFFER.insert(i, None) static.RX_FRAME_BUFFER[RX_N_FRAME_OF_DATA_FRAME] = bytes(data_in) - - #if RX_N_FRAME_OF_BURST == 1: - # static.ARQ_START_OF_BURST = time.time() - 6 - # try appending data to burst buffer try: @@ -147,8 +139,7 @@ def arq_data_received(data_in, bytes_per_frame): # if we received the last burst of a data frame, we can directly send a frame ack to # improve transfer rate if static.RX_BURST_BUFFER.count(None) == 1 and RX_N_FRAMES_PER_DATA_FRAME != RX_N_FRAME_OF_DATA_FRAME : # count nones - logging.info("ARQ | TX | BURST ACK") - structlog.get_logger("structlog").info("[TNC] ARQ TX BURST ACK") + structlog.get_logger("structlog").info("[TNC] ARQ | RX | SENDING BURST ACK") # BUILDING ACK FRAME FOR BURST ----------------------------------------------- ack_frame = bytearray(14) @@ -156,14 +147,9 @@ def arq_data_received(data_in, bytes_per_frame): ack_frame[1:2] = static.DXCALLSIGN_CRC8 ack_frame[2:3] = static.MYCALLSIGN_CRC8 - - # TRANSMIT ACK FRAME FOR BURST----------------------------------------------- - helpers.wait(0.3) - txbuffer = [ack_frame] - modem.transmit('datac0', 1, txbuffer) + modem.transmit(mode=14, repeats=1, repeat_delay=0, frames=txbuffer) - static.CHANNEL_STATE = 'RECEIVING_DATA' # clear burst buffer static.RX_BURST_BUFFER = [] @@ -182,7 +168,8 @@ def arq_data_received(data_in, bytes_per_frame): frame_number = frame_number.to_bytes(2, byteorder='big') missing_frames += frame_number - structlog.get_logger("structlog").warning("[TNC] ARQ RPT FRAMES", snr=static.SNR, frames=missing_frames) + structlog.get_logger("structlog").warning("[TNC] ARQ | RX | RPT FRAMES", snr=static.SNR, frames=missing_frames) + # BUILDING RPT FRAME FOR BURST ----------------------------------------------- rpt_frame = bytearray(14) rpt_frame[:1] = bytes([62]) @@ -192,17 +179,13 @@ def arq_data_received(data_in, bytes_per_frame): # TRANSMIT RPT FRAME FOR BURST----------------------------------------------- txbuffer = [rpt_frame] - modem.transmit('datac0', 1, txbuffer) - - #while not modem.transmit_signalling(rpt_frame, 1): - # time.sleep(0.01) - static.CHANNEL_STATE = 'RECEIVING_DATA' - -# ---------------------------- FRAME MACHINE + modem.transmit(mode=14, repeats=1, repeat_delay=0, frames=txbuffer) + + # ---------------------------- FRAME MACHINE # --------------- IF LIST NOT CONTAINS "None" stick everything together complete_data_frame = bytearray() if static.RX_FRAME_BUFFER.count(None) == 1: # 1 because position 0 of list will alaways be None in our case - logging.debug("DECODING FRAME!") + #logging.debug("DECODING FRAME!") for frame in range(1, len(static.RX_FRAME_BUFFER)): raw_arq_frame = static.RX_FRAME_BUFFER[frame] arq_frame_payload = raw_arq_frame[8:] @@ -214,7 +197,7 @@ def arq_data_received(data_in, bytes_per_frame): arq_frame_payload = arq_frame_payload.split(DATA_FRAME_BOF) arq_frame_payload = arq_frame_payload[1] - logging.debug("BOF") + #logging.debug("BOF") # -------- DETECT IF WE RECEIVED A FRAME FOOTER THEN SAVE DATA TO GLOBALS @@ -227,7 +210,7 @@ def arq_data_received(data_in, bytes_per_frame): else: arq_frame_payload = arq_frame_payload.split(DATA_FRAME_EOF) arq_frame_payload = arq_frame_payload[0] - logging.debug("EOF") + #logging.debug("EOF") # --------- AFTER WE SEPARATED BOF AND EOF, STICK EVERYTHING TOGETHER complete_data_frame = complete_data_frame + arq_frame_payload @@ -241,7 +224,7 @@ def arq_data_received(data_in, bytes_per_frame): # IF THE FRAME PAYLOAD CRC IS EQUAL TO THE FRAME CRC WHICH IS KNOWN FROM THE HEADER --> SUCCESS if frame_payload_crc == data_frame_crc: static.INFO.append("ARQ;RECEIVING;SUCCESS") - structlog.get_logger("structlog").info("[TNC] DATA FRAME SUCESSFULLY RECEIVED") + structlog.get_logger("structlog").info("[TNC] ARQ | RX | DATA FRAME SUCESSFULLY RECEIVED") calculate_transfer_rate_rx(RX_N_FRAMES_PER_DATA_FRAME, RX_N_FRAME_OF_DATA_FRAME, RX_START_OF_TRANSMISSION, RX_PAYLOAD_PER_ARQ_FRAME) # decode to utf-8 string @@ -250,15 +233,22 @@ def arq_data_received(data_in, bytes_per_frame): # decode json objects from data frame to inspect if we received a file or message rawdata = json.loads(complete_data_frame) - # if datatype is a file, we append to RX_BUFFER, which contains files only - if rawdata["datatype"] == "file": - logging.info("RECEIVED FILE --> MOVING DATA TO RX BUFFER") + # if datatype is a file, we append to RX_BUFFER, which contains files only + # dt = datatype + # --> f = file + # --> m = message + # fn = filename + # ft = filetype + # d = data + # crc = checksum + if rawdata["dt"] == "f": + #logging.debug("RECEIVED FILE --> MOVING DATA TO RX BUFFER") static.RX_BUFFER.append([static.DXCALLSIGN,static.DXGRID,int(time.time()), complete_data_frame]) # if datatype is a file, we append to RX_MSG_BUFFER, which contains messages only - if rawdata["datatype"] == "message": + if rawdata["dt"] == "m": static.RX_MSG_BUFFER.append([static.DXCALLSIGN,static.DXGRID,int(time.time()), complete_data_frame]) - logging.info("RECEIVED MESSAGE --> MOVING DATA TO MESSAGE BUFFER") + #logging.debug("RECEIVED MESSAGE --> MOVING DATA TO MESSAGE BUFFER") # BUILDING ACK FRAME FOR DATA FRAME ----------------------------------------------- ack_frame = bytearray(14) @@ -267,17 +257,11 @@ def arq_data_received(data_in, bytes_per_frame): ack_frame[2:3] = static.MYCALLSIGN_CRC8 # TRANSMIT ACK FRAME FOR BURST----------------------------------------------- - structlog.get_logger("structlog").info("[TNC] ARQ DATA FRAME ACK", snr=static.SNR, crc=data_frame_crc.hex()) - # since simultaneous decoding it seems, we don't have to wait anymore - # however, we will wait a little bit for easier ptt debugging - # possibly we can remove this later - helpers.wait(0.5) + structlog.get_logger("structlog").info("[TNC] ARQ | RX | SENDING DATA FRAME ACK", snr=static.SNR, crc=data_frame_crc.hex()) + txbuffer = [ack_frame] - modem.transmit('datac0', 1, txbuffer) - - #while not modem.transmit_signalling(ack_frame, 3): - # time.sleep(0.01) + modem.transmit(mode=14, repeats=2, repeat_delay=250, frames=txbuffer) calculate_transfer_rate_rx(RX_N_FRAMES_PER_DATA_FRAME, RX_N_FRAME_OF_DATA_FRAME, RX_START_OF_TRANSMISSION, RX_PAYLOAD_PER_ARQ_FRAME) @@ -288,14 +272,14 @@ def arq_data_received(data_in, bytes_per_frame): static.RX_BURST_BUFFER = [] static.RX_FRAME_BUFFER = [] - structlog.get_logger("structlog").info("[TNC] DATA [" + str(static.MYCALLSIGN, 'utf-8') + "]<< >>[" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR) + structlog.get_logger("structlog").info("[TNC] DATACHANNEL [" + str(static.MYCALLSIGN, 'utf-8') + "]<< >>[" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR) else: structlog.get_logger("structlog").debug("[TNC] ARQ: ", ARQ_FRAME_BOF_RECEIVED=RX_FRAME_BOF_RECEIVED, ARQ_FRAME_EOF_RECEIVED=RX_FRAME_EOF_RECEIVED ) calculate_transfer_rate_rx(RX_N_FRAMES_PER_DATA_FRAME, RX_N_FRAME_OF_DATA_FRAME, RX_START_OF_TRANSMISSION, RX_PAYLOAD_PER_ARQ_FRAME) static.INFO.append("ARQ;RECEIVING;FAILED") - structlog.get_logger("structlog").warning("[TNC] ARQ: DATA FRAME NOT SUCESSFULLY RECEIVED!") + structlog.get_logger("structlog").warning("[TNC] ARQ | RX | DATA FRAME NOT SUCESSFULLY RECEIVED!") # STATE CLEANUP #arq_reset_frame_machine() @@ -305,7 +289,7 @@ def arq_data_received(data_in, bytes_per_frame): static.RX_BURST_BUFFER = [] static.RX_FRAME_BUFFER = [] - structlog.get_logger("structlog").info("[TNC] DATA [" + str(static.MYCALLSIGN, 'utf-8') + "]<>[" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR) + structlog.get_logger("structlog").info("[TNC] DATACHANNEL [" + str(static.MYCALLSIGN, 'utf-8') + "]<>[" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR) def arq_transmit(data_out, mode, n_frames_per_burst): @@ -329,7 +313,7 @@ def arq_transmit(data_out, mode, n_frames_per_burst): TX_BUFFER = [] # our buffer for appending new data # TIMEOUTS - BURST_ACK_TIMEOUT_SECONDS = 3.0 # timeout for burst acknowledges + BURST_ACK_TIMEOUT_SECONDS = 10.0 # timeout for burst acknowledges DATA_FRAME_ACK_TIMEOUT_SECONDS = 10.0 # timeout for data frame acknowledges RPT_ACK_TIMEOUT_SECONDS = 10.0 # timeout for rpt frame acknowledges @@ -352,8 +336,6 @@ def arq_transmit(data_out, mode, n_frames_per_burst): TX_PAYLOAD_PER_ARQ_FRAME = payload_per_frame - 8 frame_header_length = 6 - #n_arq_frames_per_data_frame = (len(data_out) + frame_header_length) // TX_PAYLOAD_PER_ARQ_FRAME + ((len(data_out) + frame_header_length) % TX_PAYLOAD_PER_ARQ_FRAME > 0) - frame_payload_crc = helpers.get_crc_16(data_out) # This is the total frame with frame header, which will be send @@ -367,7 +349,7 @@ def arq_transmit(data_out, mode, n_frames_per_burst): TX_BUFFER_SIZE = len(TX_BUFFER) static.INFO.append("ARQ;TRANSMITTING") - structlog.get_logger("structlog").info("[TNC] ARQ TX DATA", mode=DATA_CHANNEL_MODE, bytes=len(data_out), frames=TX_BUFFER_SIZE) + structlog.get_logger("structlog").info("[TNC] DATACHANNEL", mode=DATA_CHANNEL_MODE, bytes=len(data_out), frames=TX_BUFFER_SIZE) # ----------------------- THIS IS THE MAIN LOOP----------------------------------------------------------------- @@ -401,7 +383,7 @@ def arq_transmit(data_out, mode, n_frames_per_burst): total_frame_progress = str(TX_N_SENT_FRAMES) + "/" + str(TX_BUFFER_SIZE) transmission_percent = str(static.ARQ_TRANSMISSION_PERCENT).zfill(3) transmission_attempts = str(TX_N_RETRIES_PER_BURST + 1) + "/" + str(TX_N_MAX_RETRIES_PER_BURST) - structlog.get_logger("structlog").info("[TNC] ARQ TX DATA", mode=DATA_CHANNEL_MODE, frames=frame_progress, percent=transmission_percent, frames_total=total_frame_progress, attempt=transmission_attempts) + structlog.get_logger("structlog").info("[TNC] ARQ | TX | DATA", mode=DATA_CHANNEL_MODE, frames=frame_progress, percent=transmission_percent, frames_total=total_frame_progress, attempt=transmission_attempts) # lets refresh all timers and ack states before sending a new frame arq_reset_ack(False) @@ -419,7 +401,6 @@ def arq_transmit(data_out, mode, n_frames_per_burst): n_current_arq_frame = TX_N_SENT_FRAMES + n + 1 n_current_arq_frame = n_current_arq_frame.to_bytes(2, byteorder='big') n_total_arq_frame = len(TX_BUFFER) - #static.ARQ_TX_N_TOTAL_ARQ_FRAMES = n_total_arq_frame arqframe = frame_type + \ bytes([TX_N_FRAMES_PER_BURST]) + \ @@ -430,30 +411,26 @@ def arq_transmit(data_out, mode, n_frames_per_burst): payload_data tempbuffer.append(arqframe) - - while not modem.transmit_arq_burst(DATA_CHANNEL_MODE, tempbuffer): - time.sleep(0.01) - - ## lets wait during sending. After sending is finished we will continue - #while static.CHANNEL_STATE == 'SENDING_DATA': - # time.sleep(0.01) - + + modem.transmit(mode=DATA_CHANNEL_MODE, repeats=1, repeat_delay=0, frames=tempbuffer) + + # --------------------------- START TIMER FOR WAITING FOR ACK ---> IF TIMEOUT REACHED, ACK_TIMEOUT = 1 - structlog.get_logger("structlog").debug("[TNC] ARQ | RX | WAITING FOR BURST ACK") - static.CHANNEL_STATE = 'RECEIVING_SIGNALLING' + structlog.get_logger("structlog").debug("[TNC] ARQ | TX | WAITING FOR BURST ACK") + #static.CHANNEL_STATE = 'RECEIVING_SIGNALLING' burstacktimeout = time.time() + BURST_ACK_TIMEOUT_SECONDS # --------------------------- WHILE TIMEOUT NOT REACHED AND NO ACK RECEIVED AND IN ARQ STATE--> LISTEN while not BURST_ACK_RECEIVED and not RPT_REQUEST_RECEIVED and not DATA_FRAME_ACK_RECEIVED and time.time() < burstacktimeout and static.ARQ_STATE == 'DATA': time.sleep(0.01) # lets reduce CPU load a little bit - logging.debug(static.CHANNEL_STATE) + logging.debug("WAITING FOR BURST ACK..") # HERE WE PROCESS DATA IF WE RECEIVED ACK/RPT FRAMES OR NOT WHILE WE ARE IN ARQ STATE # IF WE ARE NOT IN ARQ STATE, WE STOPPED THE TRANSMISSION if RPT_REQUEST_RECEIVED and static.ARQ_STATE == 'DATA': - structlog.get_logger("structlog").debug("[TNC] ARQ | RX | REQUEST FOR REPEATING FRAMES: ",buffer=RPT_REQUEST_BUFFER) + structlog.get_logger("structlog").debug("[TNC] ARQ | TX | REQUEST FOR REPEATING FRAMES: ",buffer=RPT_REQUEST_BUFFER) structlog.get_logger("structlog").debug("[TNC] ARQ | TX | SENDING REQUESTED FRAMES: ",buffer=RPT_REQUEST_BUFFER) # --------- BUILD RPT FRAME -------------- tempbuffer = [] @@ -484,14 +461,8 @@ def arq_transmit(data_out, mode, n_frames_per_burst): payload_data tempbuffer.append(arqframe) - - while not modem.transmit_arq_burst(DATA_CHANNEL_MODE, tempbuffer): - time.sleep(0.01) - - # lets wait during sending. After sending is finished we will continue - #while static.ARQ_STATE == 'SENDING_DATA': - # time.sleep(0.01) - #static.CHANNEL_STATE = 'RECEIVING_SIGNALLING' + + modem.transmit(mode=DATA_CHANNEL_MODE, repeats=1, repeat_delay=0, frames=tempbuffer) arq_reset_ack(False) @@ -536,25 +507,22 @@ def arq_transmit(data_out, mode, n_frames_per_burst): TX_N_RETRIES_PER_BURST = 0 calculate_transfer_rate_tx(TX_N_SENT_FRAMES, TX_PAYLOAD_PER_ARQ_FRAME, TX_START_OF_TRANSMISSION, TX_BUFFER_SIZE) - logging.info("ARQ | RX | ACK [" + str(static.ARQ_BITS_PER_SECOND) + " bit/s | " + str(static.ARQ_BYTES_PER_MINUTE) + " B/min]") - - # lets wait a little bit before we are processing the next frame - helpers.wait(0.3) - - + logging.info("ARQ | RX | ACK [" + str(static.ARQ_BITS_PER_SECOND) + " bit/s | " + str(static.ARQ_BYTES_PER_MINUTE) + " B/min]") break else: - logging.info("--->NO RULE MATCHED OR TRANSMISSION STOPPED!") - print("ARQ_ACK_RECEIVED " + str(BURST_ACK_RECEIVED)) + logging.debug("--->NO RULE MATCHED OR TRANSMISSION STOPPED!") + logging.debug("ARQ_ACK_RECEIVED " + str(BURST_ACK_RECEIVED)) + logging.debug(f"TX_N_SENT_FRAMES: {TX_N_SENT_FRAMES}") # SENT FRAMES WILL INCREMENT AFTER ACK RECEIVED! + logging.debug(f"TX_BUFFER_SIZE: {TX_BUFFER_SIZE}") + logging.debug(f"DATA_FRAME_ACK_RECEIVED: {DATA_FRAME_ACK_RECEIVED}") break # --------------------------------WAITING AREA FOR FRAME ACKs - static.CHANNEL_STATE = 'RECEIVING_SIGNALLING' - frameacktimeout = time.time() + DATA_FRAME_ACK_TIMEOUT_SECONDS # wait for frame ACK if we processed the last frame/burst + while not DATA_FRAME_ACK_RECEIVED and time.time() < frameacktimeout and TX_N_SENT_FRAMES == TX_BUFFER_SIZE: time.sleep(0.01) # lets reduce CPU load a little bit logging.debug("WAITING FOR FRAME ACK") @@ -567,10 +535,9 @@ def arq_transmit(data_out, mode, n_frames_per_burst): # -------------------------BREAK TX BUFFER LOOP IF ALL PACKETS HAVE BEEN SENT AND WE GOT A FRAME ACK elif TX_N_SENT_FRAMES == TX_BUFFER_SIZE and DATA_FRAME_ACK_RECEIVED: - print(TX_N_SENT_FRAMES) calculate_transfer_rate_tx(TX_N_SENT_FRAMES, TX_PAYLOAD_PER_ARQ_FRAME, TX_START_OF_TRANSMISSION, TX_BUFFER_SIZE) static.INFO.append("ARQ;TRANSMITTING;SUCCESS") - logging.log(25, "ARQ | RX | FRAME ACK! - DATA TRANSMITTED! [" + str(static.ARQ_BITS_PER_SECOND) + " bit/s | " + str(static.ARQ_BYTES_PER_MINUTE) + " B/min]") + logging.info("ARQ | RX | FRAME ACK! - DATA TRANSMITTED! [" + str(static.ARQ_BITS_PER_SECOND) + " bit/s | " + str(static.ARQ_BYTES_PER_MINUTE) + " B/min]") break elif not DATA_FRAME_ACK_RECEIVED and time.time() > frameacktimeout: @@ -650,20 +617,14 @@ def burst_rpt_received(data_in): def open_dc_and_transmit(data_out, mode, n_frames_per_burst): global DATA_CHANNEL_READY_FOR_DATA + + static.TNC_STATE = 'BUSY' asyncio.run(arq_open_data_channel(mode)) # wait until data channel is open while not DATA_CHANNEL_READY_FOR_DATA: time.sleep(0.01) - - # lets wait a little bit so RX station is ready for receiving - #wait_before_data_timer = time.time() + 0.8 - #while time.time() < wait_before_data_timer: - # pass - helpers.wait(0.8) - - - # transmit data + arq_transmit(data_out, mode, n_frames_per_burst) @@ -694,8 +655,7 @@ async def arq_open_data_channel(mode): txbuffer = [connection_frame] - modem.transmit('datac0', 1, txbuffer) - + modem.transmit(mode=14, repeats=1, repeat_delay=0, frames=txbuffer) timeout = time.time() + 3 while time.time() < timeout: @@ -742,14 +702,10 @@ def arq_received_data_channel_opener(data_in): connection_frame[12:13] = bytes([mode]) txbuffer = [connection_frame] - modem.transmit('datac0', 1, txbuffer) - + modem.transmit(mode=14, repeats=1, repeat_delay=0, frames=txbuffer) + structlog.get_logger("structlog").info("[TNC] DATA [" + str(static.MYCALLSIGN, 'utf-8') + "]>>|<<[" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR, mode=mode) - static.CHANNEL_STATE = 'RECEIVING_DATA' - # and now we are going to "RECEIVING_DATA" mode.... - - def arq_received_channel_is_open(data_in): global DATA_CHANNEL_LAST_RECEIVED @@ -767,19 +723,13 @@ def arq_received_channel_is_open(data_in): # we are forcing doing a transmission at the moment --> see else statement if DATA_CHANNEL_MODE == int.from_bytes(bytes(data_in[12:13]), "big"): structlog.get_logger("structlog").info("[TNC] DATA [" + str(static.MYCALLSIGN, 'utf-8') + "]>>|<<[" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR) - - # wait a little bit so other station is ready ( PTT toggle ) - print("wait.....") - print(time.time()) - helpers.wait(0.5) - print(time.time()) + # as soon as we set ARQ_STATE to DATA, transmission starts static.ARQ_STATE = 'DATA' DATA_CHANNEL_READY_FOR_DATA = True DATA_CHANNEL_LAST_RECEIVED = int(time.time()) else: structlog.get_logger("structlog").info("[TNC] DATA [" + str(static.MYCALLSIGN, 'utf-8') + "]>>|<<[" + str(static.DXCALLSIGN, 'utf-8') + "]", snr=static.SNR, info="wrong mode rcvd") - helpers.wait(0.5) # as soon as we set ARQ_STATE to DATA, transmission starts static.ARQ_STATE = 'DATA' DATA_CHANNEL_READY_FOR_DATA = True @@ -803,8 +753,7 @@ def transmit_ping(callsign): ping_frame[3:9] = static.MYCALLSIGN txbuffer = [ping_frame] - modem.transmit('datac0', 1, txbuffer) - + modem.transmit(mode=14, repeats=1, repeat_delay=0, frames=txbuffer) def received_ping(data_in, frequency_offset): @@ -824,7 +773,7 @@ def received_ping(data_in, frequency_offset): ping_frame[9:11] = frequency_offset.to_bytes(2, byteorder='big', signed=True) txbuffer = [ping_frame] - modem.transmit('datac0', 1, txbuffer) + modem.transmit(mode=14, repeats=1, repeat_delay=0, frames=txbuffer) def received_ping_ack(data_in): @@ -858,8 +807,7 @@ def run_beacon(interval): structlog.get_logger("structlog").info("[TNC] Sending beacon!", interval=interval) txbuffer = [beacon_frame] - modem.transmit('datac0', 1, txbuffer) - + modem.transmit(mode=14, repeats=1, repeat_delay=0, frames=txbuffer) time.sleep(interval) @@ -890,8 +838,8 @@ def transmit_cq(): cq_frame[8:14] = static.MYGRID txbuffer = [cq_frame] - modem.transmit('datac0', 1, txbuffer) - #while not modem.transmit('datac0', 1, txbuffer): + modem.transmit(mode=14, repeats=1, repeat_delay=1000, frames=txbuffer) + #while not modem.transmit(14, 1, txbuffer): # pass @@ -1008,7 +956,7 @@ def watchdog(): watchdog master function. Frome here we call the watchdogs """ while True: - time.sleep(0.01) + time.sleep(0.5) data_channel_keep_alive_watchdog() diff --git a/tnc/lib/codec2/linux/libcodec2.so.1.0 b/tnc/lib/codec2/linux/libcodec2.so.1.0 index 34328f4a..a61dff0d 100755 Binary files a/tnc/lib/codec2/linux/libcodec2.so.1.0 and b/tnc/lib/codec2/linux/libcodec2.so.1.0 differ diff --git a/tnc/main.py b/tnc/main.py index 411f552c..54432dd1 100644 --- a/tnc/main.py +++ b/tnc/main.py @@ -28,8 +28,8 @@ if __name__ == '__main__': 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", type=int) - PARSER.add_argument('--deviceport', dest="hamlib_device_port", default="/dev/ttyUSB0", help="Socket port", type=str) - PARSER.add_argument('--deviceid', dest="hamlib_device_id", default=2028, help="Socket port", type=str) + 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", default=9600, help="Serialspeed", type=str) PARSER.add_argument('--pttprotocol', dest="hamlib_ptt_type", default='RTS', help="PTT Type", type=str) PARSER.add_argument('--pttport', dest="hamlib_ptt_port", default="/dev/ttyUSB0", help="PTT Port", type=str) @@ -39,10 +39,10 @@ if __name__ == '__main__': static.AUDIO_INPUT_DEVICE = ARGS.audio_input_device static.AUDIO_OUTPUT_DEVICE = ARGS.audio_output_device static.PORT = ARGS.socket_port - static.HAMLIB_DEVICE_ID = ARGS.hamlib_device_id + static.HAMLIB_DEVICE_NAME = ARGS.hamlib_device_name static.HAMLIB_DEVICE_PORT = ARGS.hamlib_device_port static.HAMLIB_PTT_TYPE = ARGS.hamlib_ptt_type - HAMLIB_PTT_PORT = ARGS.hamlib_ptt_port + static.HAMLIB_PTT_PORT = ARGS.hamlib_ptt_port static.HAMLIB_SERIAL_SPEED = ARGS.hamlib_serialspeed # we need to wait until we got all parameters from argparse first before we can load the other modules @@ -59,36 +59,4 @@ if __name__ == '__main__': CMD_SERVER_THREAD = threading.Thread(target=sock.start_cmd_socket, name="cmd server") CMD_SERVER_THREAD.start() - - ''' - # Start RIGCTLD - - if static.HAMLIB_PTT_TYPE == "RTS": - dtr_state = "OFF" - else: - dtr_state = "NONE" - - if sys.platform == "linux": - command = "exec ./hamlib/linux/rigctld -r " + str(static.HAMLIB_DEVICE_PORT) + \ - " -s "+ str(static.HAMLIB_SERIAL_SPEED) + \ - " -P "+ str(static.HAMLIB_PTT_TYPE) + \ - " -m "+ str(static.HAMLIB_DEVICE_ID) + \ - " --set-conf=dtr_state=" + dtr_state - try: - p = subprocess.Popen(command, shell=True) - except: - print("hamlib not started") - sys.exit() - - elif sys.platform == "darwin": - print("platform not yet supported") - sys.exit() - - elif sys.platform == "win32": - print("platform not yet supported") - sys.exit() - - else: - print("platform not supported!") - sys.exit() - ''' + diff --git a/tnc/modem.py b/tnc/modem.py index ec60c510..157c36dc 100644 --- a/tnc/modem.py +++ b/tnc/modem.py @@ -21,6 +21,7 @@ import data_handler import re import queue import codec2 +import rig # option for testing miniaudio instead of audioop for sample rate conversion #import miniaudio @@ -54,29 +55,6 @@ def noalsaerr(): # p = pyaudio.PyAudio() ###################################################### -# 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/') - import Hamlib - - # https://stackoverflow.com/a/4703409 - 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("[TNC] Hamlib found", version=hamlib_version) - else: - structlog.get_logger("structlog").warning("[TNC] Hamlib outdated", found=hamlib_version, recommend=min_hamlib_version) -except Exception as e: - structlog.get_logger("structlog").critical("[TNC] Hamlib not found", error=e) - MODEM_STATS_NR_MAX = 320 MODEM_STATS_NC_MAX = 51 @@ -124,7 +102,9 @@ class RF(): # init FIFO queue to store modulation out in self.modoutqueue = queue.Queue() - + # 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.datac0_bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(self.datac0_freedv)/8) @@ -185,90 +165,31 @@ class RF(): output=True, input_device_index=static.AUDIO_INPUT_DEVICE, output_device_index=static.AUDIO_OUTPUT_DEVICE, - stream_callback=self.callback + stream_callback=self.audio_callback ) - # not needed anymore. - #self.streambuffer = bytes(0) + + # --------------------------------------------INIT AND OPEN HAMLIB + 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) # --------------------------------------------START DECODER THREAD + FFT_THREAD = threading.Thread(target=self.calculate_fft, name="FFT_THREAD") + FFT_THREAD.start() + AUDIO_THREAD = threading.Thread(target=self.audio, name="AUDIO_THREAD") AUDIO_THREAD.start() + HAMLIB_THREAD = threading.Thread(target=self.update_rig_data, name="HAMLIB_THREAD") + HAMLIB_THREAD.start() + WORKER_THREAD = threading.Thread(target=self.worker, name="WORKER_THREAD") WORKER_THREAD.start() - - self.fft_data = bytes() - FFT_THREAD = threading.Thread(target=self.calculate_fft, name="FFT_THREAD") - FFT_THREAD.start() - - # --------------------------------------------CONFIGURE HAMLIB - # try to init hamlib - try: - Hamlib.rig_set_debug(Hamlib.RIG_DEBUG_NONE) - - # get devicenumber by looking for deviceobject in Hamlib module - try: - devicenumber = getattr(Hamlib, static.HAMLIB_DEVICE_ID) - except: - structlog.get_logger("structlog").error("[DMN] Hamlib: rig not supported...") - devicenumber = 0 - - self.my_rig = Hamlib.Rig(int(devicenumber)) - self.my_rig.set_conf("rig_pathname", static.HAMLIB_DEVICE_PORT) - self.my_rig.set_conf("retry", "5") - self.my_rig.set_conf("serial_speed", static.HAMLIB_SERIAL_SPEED) - self.my_rig.set_conf("serial_handshake", "None") - self.my_rig.set_conf("stop_bits", "1") - self.my_rig.set_conf("data_bits", "8") - - if static.HAMLIB_PTT_TYPE == 'RIG': - self.hamlib_ptt_type = Hamlib.RIG_PTT_RIG - - elif static.HAMLIB_PTT_TYPE == 'DTR-H': - self.hamlib_ptt_type = Hamlib.RIG_PTT_SERIAL_DTR - self.my_rig.set_conf("dtr_state", "HIGH") - self.my_rig.set_conf("ptt_type", "DTR") - - elif static.HAMLIB_PTT_TYPE == 'DTR-L': - self.hamlib_ptt_type = Hamlib.RIG_PTT_SERIAL_DTR - self.my_rig.set_conf("dtr_state", "LOW") - self.my_rig.set_conf("ptt_type", "DTR") - - elif static.HAMLIB_PTT_TYPE == 'RTS': - self.hamlib_ptt_type = Hamlib.RIG_PTT_SERIAL_RTS - self.my_rig.set_conf("dtr_state", "OFF") - self.my_rig.set_conf("ptt_type", "RTS") - - elif static.HAMLIB_PTT_TYPE == 'PARALLEL': - self.hamlib_ptt_type = Hamlib.RIG_PTT_PARALLEL - - elif static.HAMLIB_PTT_TYPE == 'MICDATA': - self.hamlib_ptt_type = Hamlib.RIG_PTT_RIG_MICDATA - - elif static.HAMLIB_PTT_TYPE == 'CM108': - self.hamlib_ptt_type = Hamlib.RIG_PTT_CM108 - - else: # static.HAMLIB_PTT_TYPE == 'RIG_PTT_NONE': - self.hamlib_ptt_type = Hamlib.RIG_PTT_NONE - - self.my_rig.open() - atexit.register(self.my_rig.close) - - # set rig mode to USB - self.my_rig.set_mode(Hamlib.RIG_MODE_USB) - - # start thread for getting hamlib data - HAMLIB_THREAD = threading.Thread(target=self.get_radio_stats, name="HAMLIB_THREAD") - HAMLIB_THREAD.start() - - except: - structlog.get_logger("structlog").error("[TNC] Hamlib - can't open rig", e=sys.exc_info()[0]) - + # -------------------------------------------------------------------------------------------------------- - def callback(self, data_in48k, frame_count, time_info, status): + def audio_callback(self, data_in48k, frame_count, time_info, status): x = np.frombuffer(data_in48k, dtype=np.int16) x = self.resampler.resample48_to_8(x) @@ -276,11 +197,7 @@ class RF(): self.datac0_buffer.push(x) self.datac1_buffer.push(x) self.datac3_buffer.push(x) - - # refill fft_data buffer so we can plot a fft - if len(self.fft_data) < 1024: - self.fft_data += bytes(x) - + self.fft_data += bytes(x) if self.modoutqueue.empty(): data_out48k = bytes(self.AUDIO_FRAMES_PER_BUFFER_TX*2*2) @@ -288,42 +205,15 @@ class RF(): data_out48k = self.modoutqueue.get() return (data_out48k, pyaudio.paContinue) - - - - def ptt_and_wait(self, state): - static.PTT_STATE = state - - if state: - - self.my_rig.set_ptt(self.hamlib_ptt_type, 1) - # rigctld.ptt_enable() - ptt_toggle_timeout = time.time() + 0.5 - - while time.time() < ptt_toggle_timeout and not self.modoutqueue.empty(): - pass - - else: - - ptt_toggle_timeout = time.time() + 0.5 - while time.time() < ptt_toggle_timeout: - pass - - self.my_rig.set_ptt(self.hamlib_ptt_type, 0) - # rigctld.ptt_disable() - - return False # -------------------------------------------------------------------------------------------------------- - def transmit(self, mode, count, frames): - - state_before_transmit = static.CHANNEL_STATE - static.CHANNEL_STATE = 'SENDING_SIGNALLING' + def transmit(self, mode, repeats, repeat_delay, frames): # open codec2 instance - self.MODE = codec2.FREEDV_MODE[mode].value + #self.MODE = codec2.freedv_get_mode_value_by_name(mode) + self.MODE = mode freedv = cast(codec2.api.freedv_open(self.MODE), c_void_p) # get number of bytes per frame for mode @@ -342,18 +232,22 @@ class RF(): 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) - - - for i in range(1,count+1): + # add empty data to handle ptt toggle time + data_delay_seconds = 250 + data_delay = int(self.MODEM_SAMPLE_RATE*(data_delay_seconds/1000)) + mod_out_silence = create_string_buffer(data_delay*2) + txbuffer = bytes(mod_out_silence) + for i in range(1,repeats+1): + # write preamble to txbuffer codec2.api.freedv_rawdatapreambletx(freedv, mod_out_preamble) - txbuffer = bytes(mod_out_preamble) - + time.sleep(0.01) + 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 @@ -366,14 +260,20 @@ class RF(): 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 - + time.sleep(0.01) txbuffer += bytes(mod_out) # append postamble to txbuffer codec2.api.freedv_rawdatapostambletx(freedv, mod_out_postamble) txbuffer += bytes(mod_out_postamble) - + time.sleep(0.01) + # 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) + time.sleep(0.01) + # resample up to 48k (resampler works on np.int16) x = np.frombuffer(txbuffer, dtype=np.int16) txbuffer_48k = self.resampler.resample8_to_48(x) @@ -389,167 +289,17 @@ class RF(): if len(c) < self.AUDIO_FRAMES_PER_BUFFER_RX*2: c += bytes(self.AUDIO_FRAMES_PER_BUFFER_RX*2 - len(c)) self.modoutqueue.put(c) - print(len(c)) - while self.ptt_and_wait(True): + # maybe we need to toggle PTT before craeting modulation because of queue processing + static.PTT_STATE = self.hamlib.set_ptt(True) + while not self.modoutqueue.empty(): pass - - # set channel state - static.CHANNEL_STATE = 'SENDING_SIGNALLING' - - # set ptt back to false - self.ptt_and_wait(False) - - - # we have a problem with the receiving state - if state_before_transmit != 'RECEIVING_DATA': - static.CHANNEL_STATE = 'RECEIVING_SIGNALLING' - else: - static.CHANNEL_STATE = state_before_transmit + static.PTT_STATE = self.hamlib.set_ptt(False) + self.c_lib.freedv_close(freedv) return True - - - ''' - def transmit_signalling(self, data_out, count): - state_before_transmit = static.CHANNEL_STATE - static.CHANNEL_STATE = 'SENDING_SIGNALLING' - - mod_out = create_string_buffer(self.datac0_n_tx_modem_samples * 2) - mod_out_preamble = create_string_buffer(self.datac0_n_tx_preamble_modem_samples * 2) - mod_out_postamble = create_string_buffer(self.datac0_n_tx_postamble_modem_samples * 2) - - buffer = bytearray(self.datac0_payload_per_frame) - # set buffersize to length of data which will be send - buffer[:len(data_out)] = data_out - - crc = ctypes.c_ushort(self.c_lib.freedv_gen_crc16(bytes(buffer), self.datac0_payload_per_frame)) # generate CRC16 - # convert crc to 2 byte hex string - crc = crc.value.to_bytes(2, byteorder='big') - buffer += crc # append crc16 to buffer - data = (ctypes.c_ubyte * self.datac0_bytes_per_frame).from_buffer_copy(buffer) - - # modulate DATA and safe it into mod_out pointer - self.c_lib.freedv_rawdatapreambletx(self.datac0_freedv, mod_out_preamble) - self.c_lib.freedv_rawdatatx(self.datac0_freedv, mod_out, data) - self.c_lib.freedv_rawdatapostambletx(self.datac0_freedv, mod_out_postamble) - - self.streambuffer = bytearray() - self.streambuffer += bytes(mod_out_preamble) - self.streambuffer += bytes(mod_out) - self.streambuffer += bytes(mod_out_postamble) - - # resample up to 48k (resampler works on np.int16) - x = np.frombuffer(self.streambuffer, dtype=np.int16) - txbuffer_48k = self.resampler.resample8_to_48(x) - - - # append frame again with as much as in count defined - #for i in range(1, count): - # self.streambuffer += bytes(txbuffer_48k.tobytes()) - - while self.ptt_and_wait(True): - pass - - # set channel state - static.CHANNEL_STATE = 'SENDING_SIGNALLING' - - # start writing audio data to audio stream - #self.stream_tx.write(self.streambuffer) - self.stream_tx.write(txbuffer_48k.tobytes()) - - - # set ptt back to false - self.ptt_and_wait(False) - - - # we have a problem with the receiving state - if state_before_transmit != 'RECEIVING_DATA': - static.CHANNEL_STATE = 'RECEIVING_SIGNALLING' - else: - static.CHANNEL_STATE = state_before_transmit - - return True -# -------------------------------------------------------------------------------------------------------- - # GET ARQ BURST FRAME VOM BUFFER AND MODULATE IT - - def transmit_arq_burst(self, mode, frames): - - # we could place this timing part inside the modem... - # lets see if this is a good idea.. - # we need to update our timeout timestamp - - state_before_transmit = static.CHANNEL_STATE - static.CHANNEL_STATE = 'SENDING_DATA' - - freedv = cast(self.c_lib.freedv_open(mode), c_void_p) - self.c_lib.freedv_set_clip(freedv, 1) - self.c_lib.freedv_set_tx_bpf(freedv, 1) - - bytes_per_frame = int(self.c_lib.freedv_get_bits_per_modem_frame(freedv) / 8) - payload_per_frame = bytes_per_frame - 2 - n_nom_modem_samples = self.c_lib.freedv_get_n_nom_modem_samples(freedv) - n_tx_modem_samples = self.c_lib.freedv_get_n_tx_modem_samples(freedv) - n_tx_preamble_modem_samples = self.c_lib.freedv_get_n_tx_preamble_modem_samples(freedv) - n_tx_postamble_modem_samples = self.c_lib.freedv_get_n_tx_postamble_modem_samples(freedv) - - mod_out = create_string_buffer(n_tx_modem_samples * 2) - mod_out_preamble = create_string_buffer(n_tx_preamble_modem_samples * 2) - mod_out_postamble = create_string_buffer(n_tx_postamble_modem_samples * 2) - - self.streambuffer = bytearray() - self.c_lib.freedv_rawdatapreambletx(freedv, mod_out_preamble) - self.streambuffer += bytes(mod_out_preamble) - - - # loop through list of frames per burst - for n in range(0, len(frames)): - - # create TX buffer - buffer = bytearray(payload_per_frame) - # set buffersize to length of data which will be send - buffer[:len(frames[n])] = frames[n] - - crc = ctypes.c_ushort(self.c_lib.freedv_gen_crc16(bytes(buffer), payload_per_frame)) # generate CRC16 - # convert crc to 2 byte hex string - crc = crc.value.to_bytes(2, byteorder='big') - buffer += crc # append crc16 to buffer - data = (ctypes.c_ubyte * bytes_per_frame).from_buffer_copy(buffer) - - # modulate DATA and safe it into mod_out pointer - self.c_lib.freedv_rawdatatx(freedv, mod_out, data) - self.streambuffer += bytes(mod_out) - - self.c_lib.freedv_rawdatapostambletx(freedv, mod_out_postamble) - self.streambuffer += bytes(mod_out_postamble) - - # resample up to 48k (resampler works on np.int16) - x = np.frombuffer(self.streambuffer, dtype=np.int16) - txbuffer_48k = self.resampler.resample8_to_48(x) - - # -------------- transmit audio - - while self.ptt_and_wait(True): - pass - - # set channel state - static.CHANNEL_STATE = 'SENDING_DATA' - - # write audio to stream - self.stream_tx.write(txbuffer_48k.tobytes()) - - static.CHANNEL_STATE = 'RECEIVING_SIGNALLING' - - self.ptt_and_wait(False) - - # close codec2 instance - self.c_lib.freedv_close(freedv) - - return True -# -------------------------------------------------------------------------------------------------------- - ''' def audio(self): try: print(f"starting pyaudio callback", file=sys.stderr) @@ -565,7 +315,6 @@ class RF(): self.datac0_buffer.pop(self.datac0_nin) self.datac0_nin = codec2.api.freedv_nin(self.datac0_freedv) if nbytes == self.datac0_bytes_per_frame: - print(len(self.datac0_bytes_out)) self.dataqueue.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) @@ -597,7 +346,7 @@ class RF(): # worker for FIFO queue for processing received frames def worker(self): while True: - time.sleep(0.01) + time.sleep(0.1) data = self.dataqueue.get() self.process_data(data[0], data[1], data[2]) self.dataqueue.task_done() @@ -616,27 +365,21 @@ class RF(): frametype = int.from_bytes(bytes(bytes_out[:1]), "big") frame = frametype - 10 n_frames_per_burst = int.from_bytes(bytes(bytes_out[1:2]), "big") - self.c_lib.freedv_set_frames_per_burst(freedv, n_frames_per_burst); + #self.c_lib.freedv_set_frames_per_burst(freedv, n_frames_per_burst); #frequency_offset = self.get_frequency_offset(freedv) #print("Freq-Offset: " + str(frequency_offset)) if 50 >= frametype >= 10: - # force = True, if we don't simulate a loss of the third frame, else force = False - force = True - if frame != 3 or force: - # send payload data to arq checker without CRC16 - data_handler.arq_data_received(bytes(bytes_out[:-2]), bytes_per_frame) + # send payload data to arq checker without CRC16 + data_handler.arq_data_received(bytes(bytes_out[:-2]), bytes_per_frame) - #print("static.ARQ_RX_BURST_BUFFER.count(None) " + str(static.ARQ_RX_BURST_BUFFER.count(None))) - if static.RX_BURST_BUFFER.count(None) <= 1: - logging.debug("FULL BURST BUFFER ---> UNSYNC") - self.c_lib.freedv_set_sync(freedv, 0) + #print("static.ARQ_RX_BURST_BUFFER.count(None) " + str(static.ARQ_RX_BURST_BUFFER.count(None))) + if static.RX_BURST_BUFFER.count(None) <= 1: + logging.debug("FULL BURST BUFFER ---> UNSYNC") + self.c_lib.freedv_set_sync(freedv, 0) - else: - logging.critical("-------------SIMULATED MISSING FRAME") - force = True # BURST ACK elif frametype == 60: @@ -683,12 +426,12 @@ class RF(): # ARQ FILE TRANSFER RECEIVED! elif frametype == 225: - logging.debug("ARQ arq_received_data_channel_opener RECEIVED") + logging.debug("ARQ arq_received_data_channel_opener") data_handler.arq_received_data_channel_opener(bytes_out[:-2]) # ARQ CHANNEL IS OPENED elif frametype == 226: - logging.debug("ARQ arq_received_channel_is_open RECEIVED") + logging.debug("ARQ arq_received_channel_is_open") data_handler.arq_received_channel_is_open(bytes_out[:-2]) # ARQ CONNECT ACK / KEEP ALIVE @@ -703,11 +446,12 @@ class RF(): else: structlog.get_logger("structlog").warning("[TNC] ARQ - other frame type", frametype=frametype) + # DO UNSYNC AFTER LAST BURST by checking the frame nums against the total frames per burst if frame == n_frames_per_burst: logging.info("LAST FRAME ---> UNSYNC") self.c_lib.freedv_set_sync(freedv, 0) # FORCE UNSYNC - + else: # for debugging purposes to receive all data @@ -747,19 +491,8 @@ class RF(): # only take every tenth data point scatterdata_small = scatterdata[::10] static.SCATTER = scatterdata_small - - - def calculate_ber(self, freedv): - Tbits = self.c_lib.freedv_get_total_bits(freedv) - Terrs = self.c_lib.freedv_get_total_bit_errors(freedv) - - if Tbits != 0: - ber = (Terrs / Tbits) * 100 - static.BER = int(ber) - - self.c_lib.freedv_set_total_bit_errors(freedv, 0) - self.c_lib.freedv_set_total_bits(freedv, 0) + def calculate_snr(self, freedv): modem_stats_snr = c_float() @@ -773,13 +506,15 @@ class RF(): except: static.SNR = 0 - def get_radio_stats(self): - while True: - time.sleep(0.1) - static.HAMLIB_FREQUENCY = int(self.my_rig.get_freq()) - (hamlib_mode, static.HAMLIB_BANDWITH) = self.my_rig.get_mode() - static.HAMLIB_MODE = Hamlib.rig_strrmode(hamlib_mode) + def update_rig_data(self): + while True: + time.sleep(0.5) + #(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() + def calculate_fft(self): while True: time.sleep(0.01) @@ -802,12 +537,12 @@ class RF(): # round data to decrease size dfft = np.around(dfft, 1) dfftlist = dfft.tolist() - - static.FFT = dfftlist[10:180] #200 --> bandwith 3000 + + static.FFT = dfftlist[0:320] #200 --> bandwith 3000 except: structlog.get_logger("structlog").debug("[TNC] Setting fft=0") # else 0 - static.FFT = [0] * 400 + static.FFT = [0] * 320 else: pass diff --git a/tnc/rig.py b/tnc/rig.py new file mode 100644 index 00000000..c974bd81 --- /dev/null +++ b/tnc/rig.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 + +import sys +import re +import logging, structlog, log_handler +import atexit + + +# 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/') + import Hamlib + + # https://stackoverflow.com/a/4703409 + 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("[TNC] Hamlib found", version=hamlib_version) + else: + structlog.get_logger("structlog").warning("[TNC] Hamlib outdated", found=hamlib_version, recommend=min_hamlib_version) +except Exception as e: + structlog.get_logger("structlog").critical("[TNC] Hamlib not found", error=e) + + + +class radio: + def __init__(self): + + self.devicename = '' + self.devicenumber = '' + self.deviceport = '' + self.serialspeed = 0 + self.hamlib_ptt_type = '' + self.my_rig = '' + + + def open_rig(self, devicename, deviceport, hamlib_ptt_type, serialspeed): + + self.devicename = devicename + self.deviceport = deviceport + self.serialspeed = str(serialspeed) # we need to ensure this is a str, otherwise set_conf functions are crashing + self.hamlib_ptt_type = hamlib_ptt_type + + # try to init hamlib + try: + Hamlib.rig_set_debug(Hamlib.RIG_DEBUG_NONE) + + # get devicenumber by looking for deviceobject in Hamlib module + try: + self.devicenumber = int(getattr(Hamlib, self.devicename)) + except: + structlog.get_logger("structlog").error("[DMN] 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") + self.my_rig.set_conf("serial_speed", self.serialspeed) + self.my_rig.set_conf("serial_handshake", "None") + self.my_rig.set_conf("stop_bits", "1") + self.my_rig.set_conf("data_bits", "8") + + if self.hamlib_ptt_type == 'RIG': + self.hamlib_ptt_type = Hamlib.RIG_PTT_RIG + self.my_rig.set_conf("ptt_type", 'RIG') + + elif self.hamlib_ptt_type == 'DTR-H': + self.hamlib_ptt_type = Hamlib.RIG_PTT_SERIAL_DTR + self.my_rig.set_conf("dtr_state", "HIGH") + self.my_rig.set_conf("ptt_type", "DTR") + + elif self.hamlib_ptt_type == 'DTR-L': + self.hamlib_ptt_type = Hamlib.RIG_PTT_SERIAL_DTR + self.my_rig.set_conf("dtr_state", "LOW") + self.my_rig.set_conf("ptt_type", "DTR") + + elif self.hamlib_ptt_type == 'RTS': + self.hamlib_ptt_type = Hamlib.RIG_PTT_SERIAL_RTS + self.my_rig.set_conf("dtr_state", "OFF") + self.my_rig.set_conf("ptt_type", "RTS") + + elif self.hamlib_ptt_type == 'PARALLEL': + self.hamlib_ptt_type = Hamlib.RIG_PTT_PARALLEL + + elif self.hamlib_ptt_type == 'MICDATA': + self.hamlib_ptt_type = Hamlib.RIG_PTT_RIG_MICDATA + + elif self.hamlib_ptt_type == 'CM108': + self.hamlib_ptt_type = Hamlib.RIG_PTT_CM108 + + else: #self.hamlib_ptt_type == 'RIG_PTT_NONE': + self.hamlib_ptt_type = Hamlib.RIG_PTT_NONE + + + 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("[DMN] Hamlib has no permissions", e = error) + help_url = 'https://github.com/DJ2LS/FreeDATA/wiki/UBUNTU-Manual-installation#1-permissions' + structlog.get_logger("structlog").error("[DMN] HELP:", check = help_url) + except: + structlog.get_logger("structlog").info("[DMN] Hamlib device openend", status='SUCCESS') + + + # set ptt to false if ptt is stuck for some reason + self.set_ptt(False) + + # set rig mode to USB + self.my_rig.set_mode(Hamlib.RIG_MODE_USB) + return True + + except Exception as e: + structlog.get_logger("structlog").error("[TNC] 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() + return bandwith + + def set_mode(self, mode): + return 0 + + def get_ptt(self): + return self.my_rig.get_ptt() + + def set_ptt(self, state): + if state: + self.my_rig.set_ptt(self.hamlib_ptt_type, 1) + else: + self.my_rig.set_ptt(self.hamlib_ptt_type, 0) + return state + + def close_rig(self): + self.my_rig.close() diff --git a/tnc/rigctld.py b/tnc/rigctld.py deleted file mode 100644 index 526d0e90..00000000 --- a/tnc/rigctld.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env python3 -import socket -import logging -import static - -# rigctl - https://github.com/darksidelemm/rotctld-web-gui/blob/master/rotatorgui.py#L35 -# https://github.com/xssfox/freedv-tnc/blob/master/freedvtnc/rigctl.py - - -class Rigctld(): - """ rotctld (hamlib) communication class """ - # 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 """ - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.settimeout(timeout) - - self.hostname = hostname - self.port = port - self.connect() - logging.debug(f"Rigctl intialized") - - def get_model(self): - """ Get the rotator model from rotctld """ - model = self.send_command(b'_') - return model - - def connect(self): - """ Connect to rotctld instance """ - self.sock.connect((self.hostname,self.port)) - model = self.get_model() - if model == None: - # Timeout! - self.close() - raise Exception("Timeout!") - else: - return model - - - def close(self): - self.sock.close() - - - def send_command(self, command): - """ Send a command to the connected rotctld instance, - and return the return value. - """ - self.sock.sendall(command+b'\n') - try: - return self.sock.recv(1024) - except: - return None - - def ptt_enable(self): - logging.debug(f"PTT enabled") - self.send_command(b"T 1") - - def ptt_disable(self): - logging.debug(f"PTT disabled") - self.send_command(b"T 0") - - def get_frequency(self): - data = self.send_command(b"f") - if data is not None: - data = data.split(b'\n') - try: - freq = int(data[0])/1000 - except: - freq = static.HAMLIB_FREQUENCY - #print("freq-err: " + str(data)) - #for i in range(len(data)): - # print(data[i]) - - return freq - - def get_mode(self): - data = self.send_command(b"m") - if data is not None: - data = data.split(b'\n') - try: - mode = str(data[0], "utf-8") - bandwith = int(data[1]) - except: - #print("mode-err: " + str(data)) - mode = static.HAMLIB_MODE - bandwith = static.HAMLIB_BANDWITH - return [mode, bandwith] - diff --git a/tnc/sock.py b/tnc/sock.py index 38e8f627..fe5df197 100644 --- a/tnc/sock.py +++ b/tnc/sock.py @@ -100,14 +100,14 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): CQ_THREAD = threading.Thread(target=data_handler.transmit_cq, args=[], name="CQ") CQ_THREAD.start() - # CQ CQ CQ ----------------------------------------------------- + # START_BEACON ----------------------------------------------------- if received_json["command"] == "START_BEACON": static.BEACON_STATE = True interval = int(received_json["parameter"]) BEACON_THREAD = threading.Thread(target=data_handler.run_beacon, args=[interval], name="START BEACON") BEACON_THREAD.start() - # CQ CQ CQ ----------------------------------------------------- + # STOP_BEACON ----------------------------------------------------- if received_json["command"] == "STOP_BEACON": static.BEACON_STATE = False structlog.get_logger("structlog").warning("[TNC] Stopping beacon!") @@ -140,8 +140,15 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): static.DXCALLSIGN = bytes(dxcallsign, 'utf-8') static.DXCALLSIGN_CRC8 = helpers.get_crc_8( static.DXCALLSIGN) - - rawdata = {"datatype": "file", "filename": filename, "filetype": filetype,"data": data, "checksum": checksum} + + # dt = datatype + # --> f = file + # --> m = message + # fn = filename + # ft = filetype + # d = data + # crc = checksum + rawdata = {"dt": "f", "fn": filename, "ft": filetype,"d": data, "crc": checksum} dataframe = json.dumps(rawdata) data_out = bytes(dataframe, 'utf-8') @@ -158,15 +165,21 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): dxcallsign = received_json["dxcallsign"] mode = int(received_json["mode"]) n_frames = int(received_json["n_frames"]) - data = received_json["data"] - checksum = received_json["checksum"] + data = received_json["d"] # d = data + checksum = received_json["crc"] # crc = checksum static.DXCALLSIGN = bytes(dxcallsign, 'utf-8') - static.DXCALLSIGN_CRC8 = helpers.get_crc_8( - static.DXCALLSIGN) - - rawdata = {"datatype": "message","data": data, "checksum": checksum} + static.DXCALLSIGN_CRC8 = helpers.get_crc_8(static.DXCALLSIGN) + + # dt = datatype + # --> f = file + # --> m = message + # fn = filename + # ft = filetype + # d = data + # crc = checksum + rawdata = {"dt": "m","d": data, "crc": checksum} dataframe = json.dumps(rawdata) data_out = bytes(dataframe, 'utf-8') @@ -221,7 +234,7 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): "COMMAND": "TNC_STATE", "TIMESTAMP": received_json["timestamp"], "PTT_STATE": str(static.PTT_STATE), - "CHANNEL_STATE": str(static.CHANNEL_STATE), + #"CHANNEL_STATE": str(static.CHANNEL_STATE), "TNC_STATE": str(static.TNC_STATE), "ARQ_STATE": str(static.ARQ_STATE), "AUDIO_RMS": str(static.AUDIO_RMS), diff --git a/tnc/static.py b/tnc/static.py index 060e988f..c110f064 100644 --- a/tnc/static.py +++ b/tnc/static.py @@ -36,10 +36,10 @@ SOCKET_TIMEOUT = 3 # seconds HAMLIB_PTT_TYPE = 'RTS' PTT_STATE = False -HAMLIB_DEVICE_ID = 'RIG_MODEL_DUMMY_NOVFO' +HAMLIB_DEVICE_NAME = 'RIG_MODEL_DUMMY_NOVFO' HAMLIB_DEVICE_PORT = '/dev/ttyUSB0' HAMLIB_SERIAL_SPEED = '9600' - +HAMLIB_PTT_PORT = '/dev/ttyUSB0' HAMLIB_FREQUENCY = 0 HAMLIB_MODE = '' @@ -71,7 +71,7 @@ ARQ_BITS_PER_SECOND = 0 ARQ_TRANSMISSION_PERCENT = 0 TOTAL_BYTES = 0 -CHANNEL_STATE = 'RECEIVING_SIGNALLING' +#CHANNEL_STATE = 'RECEIVING_SIGNALLING' TNC_STATE = 'IDLE' ARQ_STATE = 'IDLE'