Remove excess trailing spaces.

This commit is contained in:
Paul Kronenwetter 2022-05-08 20:41:49 -04:00
parent f2ef3f7ea2
commit 9753735c40
14 changed files with 978 additions and 945 deletions

View file

@ -9,11 +9,11 @@ atexit.register(sd._terminate)
def get_audio_devices(): def get_audio_devices():
""" """
return list of input and output audio devices in own process to avoid crashes of portaudio on raspberry pi 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 also uses a process data manager
""" """
# we need to run this on windows for multiprocessing support # 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 # If we are not doing this at this early point, not all devices will be displayed
sd._terminate() sd._terminate()
sd._initialize() sd._initialize()
with multiprocessing.Manager() as manager: with multiprocessing.Manager() as manager:
proxy_input_devices = manager.list() proxy_input_devices = manager.list()
proxy_output_devices = manager.list() proxy_output_devices = manager.list()
#print(multiprocessing.get_start_method()) #print(multiprocessing.get_start_method())
p = multiprocessing.Process(target=fetch_audio_devices, args=(proxy_input_devices, proxy_output_devices)) p = multiprocessing.Process(target=fetch_audio_devices, args=(proxy_input_devices, proxy_output_devices))
p.start() p.start()
p.join() 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): def fetch_audio_devices(input_devices, output_devices):
""" """
get audio devices from portaudio get audio devices from portaudio
Args: Args:
input_devices: proxy variable for input devices input_devices: proxy variable for input devices
output_devices: proxy variable for outout devices output_devices: proxy variable for outout devices
@ -47,7 +47,7 @@ def fetch_audio_devices(input_devices, output_devices):
Returns: Returns:
""" """
devices = sd.query_devices(device=None, kind=None) devices = sd.query_devices(device=None, kind=None)
index = 0 index = 0
for device in devices: 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 # we need to do a try exception, beacuse for windows theres no audio device range
try: try:
name = device["name"] name = device["name"]
maxOutputChannels = device["max_output_channels"] maxOutputChannels = device["max_output_channels"]
maxInputChannels = device["max_input_channels"] maxInputChannels = device["max_input_channels"]

View file

@ -30,7 +30,7 @@ def freedv_get_mode_value_by_name(mode):
""" """
get the codec2 mode by entering its string get the codec2 mode by entering its string
Args: Args:
mode: mode:
Returns: int Returns: int
@ -42,14 +42,14 @@ def freedv_get_mode_name_by_value(mode):
""" """
get the codec2 mode name as string get the codec2 mode name as string
Args: Args:
mode: mode:
Returns: string Returns: string
""" """
return FREEDV_MODE(mode).name return FREEDV_MODE(mode).name
# check if we are running in a pyinstaller environment # check if we are running in a pyinstaller environment
try: try:
app_path = sys._MEIPASS app_path = sys._MEIPASS
@ -63,12 +63,12 @@ if sys.platform == 'linux':
files.append('libcodec2.so') files.append('libcodec2.so')
elif sys.platform == 'darwin': elif sys.platform == 'darwin':
files = glob.glob('**/*libcodec2*.dylib',recursive=True) files = glob.glob('**/*libcodec2*.dylib',recursive=True)
elif sys.platform == 'win32' or sys.platform == 'win64': elif sys.platform == 'win32' or sys.platform == 'win64':
files = glob.glob('**\*libcodec2*.dll',recursive=True) files = glob.glob('**\*libcodec2*.dll',recursive=True)
else: else:
files = [] files = []
for file in files: for file in files:
try: try:
@ -79,15 +79,15 @@ for file in files:
structlog.get_logger("structlog").warning("[C2 ] Libcodec2 found but not loaded", path=file, e=e) 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(): if not 'api' in locals():
structlog.get_logger("structlog").critical("[C2 ] Libcodec2 not loaded", path=file) structlog.get_logger("structlog").critical("[C2 ] Libcodec2 not loaded", path=file)
os._exit(1) os._exit(1)
# ctypes function init # ctypes function init
#api.freedv_set_tuning_range.restype = c_int #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.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.argtype = [c_void_p, c_int]
api.freedv_set_frames_per_burst.restype = c_void_p api.freedv_set_frames_per_burst.restype = c_void_p
api.freedv_get_rx_status.argtype = [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.argtype = [c_void_p, c_void_p, c_void_p]
api.freedv_get_modem_stats.restype = c_int 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.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.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.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.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_FS_8000 = 8000
api.FREEDV_MODE_DATAC1 = 10 api.FREEDV_MODE_DATAC1 = 10
@ -152,13 +152,13 @@ api.FREEDV_MODE_FSK_LDPC = 9
class ADVANCED(ctypes.Structure): class ADVANCED(ctypes.Structure):
""" """ """ """
_fields_ = [ _fields_ = [
("interleave_frames", ctypes.c_int), ("interleave_frames", ctypes.c_int),
("M", ctypes.c_int), ("M", ctypes.c_int),
("Rs", ctypes.c_int), ("Rs", ctypes.c_int),
("Fs", ctypes.c_int), ("Fs", ctypes.c_int),
("first_tone", ctypes.c_int), ("first_tone", ctypes.c_int),
("tone_spacing", ctypes.c_int), ("tone_spacing", ctypes.c_int),
("codename", ctypes.c_char_p), ("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 ("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)), ("fft_buf", (ctypes.c_float * MODEM_STATS_NSPEC * 2)),
] ]
# Return code flags for freedv_get_rx_status() function # Return code flags for freedv_get_rx_status() function
api.FREEDV_RX_TRIAL_SYNC = 0x1 # demodulator has trial sync api.FREEDV_RX_TRIAL_SYNC = 0x1 # demodulator has trial sync
api.FREEDV_RX_SYNC = 0x2 # demodulator has sync api.FREEDV_RX_SYNC = 0x2 # demodulator has sync
@ -264,7 +264,7 @@ api.rx_sync_flags_to_text = [
class 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 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
@ -280,7 +280,7 @@ class audio_buffer:
Push new data to buffer Push new data to buffer
Args: Args:
samples: samples:
Returns: Returns:
@ -295,7 +295,7 @@ class audio_buffer:
""" """
get data from buffer in size of NIN get data from buffer in size of NIN
Args: Args:
size: size:
Returns: Returns:
@ -306,7 +306,7 @@ class audio_buffer:
self.buffer[:self.nbuffer] = self.buffer[size:size+self.nbuffer] self.buffer[:self.nbuffer] = self.buffer[size:size+self.nbuffer]
assert self.nbuffer >= 0 assert self.nbuffer >= 0
self.mutex.release() self.mutex.release()
# resampler --------------------------------------------------------- # resampler ---------------------------------------------------------
api.FDMDV_OS_48 = int(6) # oversampling rate 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") structlog.get_logger("structlog").debug("[C2 ] create 48<->8 kHz resampler")
self.filter_mem8 = np.zeros(self.MEM8, dtype=np.int16) self.filter_mem8 = np.zeros(self.MEM8, dtype=np.int16)
self.filter_mem48 = np.zeros(self.MEM48) self.filter_mem48 = np.zeros(self.MEM48)
def resample48_to_8(self,in48): def resample48_to_8(self,in48):
""" """
audio resampler integration from codec2 audio resampler integration from codec2

View file

@ -37,8 +37,8 @@ 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: Args:
sig: sig:
frame: frame:
Returns: system exit Returns: system exit
@ -51,15 +51,15 @@ signal.signal(signal.SIGINT, signal_handler)
class DAEMON(): class DAEMON():
""" """
daemon class daemon class
""" """
def __init__(self): def __init__(self):
# load crc engine # load crc engine
self.crc_algorithm = crcengine.new('crc16-ccitt-false') # load crc8 library self.crc_algorithm = crcengine.new('crc16-ccitt-false') # load crc8 library
self.daemon_queue = sock.DAEMON_QUEUE 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 = threading.Thread(target=self.update_audio_devices, name="UPDATE_AUDIO_DEVICES", daemon=True)
update_audio_devices.start() update_audio_devices.start()
@ -69,23 +69,23 @@ class DAEMON():
worker = threading.Thread(target=self.worker, name="WORKER", daemon=True) worker = threading.Thread(target=self.worker, name="WORKER", daemon=True)
worker.start() worker.start()
def update_audio_devices(self): def update_audio_devices(self):
""" """
update audio devices and set to static update audio devices and set to static
""" """
while 1: while 1:
try: try:
if not static.TNCSTARTED: if not static.TNCSTARTED:
static.AUDIO_INPUT_DEVICES, static.AUDIO_OUTPUT_DEVICES = audio.get_audio_devices() static.AUDIO_INPUT_DEVICES, static.AUDIO_OUTPUT_DEVICES = audio.get_audio_devices()
except Exception as e: except Exception as e:
print(e) print(e)
time.sleep(1) time.sleep(1)
def update_serial_devices(self): def update_serial_devices(self):
""" """
update serial devices and set to static update serial devices and set to static
@ -96,26 +96,26 @@ class DAEMON():
serial_devices = [] serial_devices = []
ports = serial.tools.list_ports.comports() ports = serial.tools.list_ports.comports()
for port, desc, hwid in ports: for port, desc, hwid in ports:
# calculate hex of hwid if we have unique names # calculate hex of hwid if we have unique names
crc_hwid = self.crc_algorithm(bytes(hwid, encoding='utf-8')) crc_hwid = self.crc_algorithm(bytes(hwid, encoding='utf-8'))
crc_hwid = crc_hwid.to_bytes(2, byteorder='big') crc_hwid = crc_hwid.to_bytes(2, byteorder='big')
crc_hwid = crc_hwid.hex() crc_hwid = crc_hwid.hex()
description = desc + ' [' + crc_hwid + ']' description = desc + ' [' + crc_hwid + ']'
serial_devices.append({"port": str(port), "description": str(description) }) serial_devices.append({"port": str(port), "description": str(description) })
static.SERIAL_DEVICES = serial_devices static.SERIAL_DEVICES = serial_devices
time.sleep(1) time.sleep(1)
except Exception as e: except Exception as e:
print(e) print(e)
def worker(self): def worker(self):
""" """
a worker for the received commands a worker for the received commands
""" """
while 1: while 1:
try: try:
data = self.daemon_queue.get() data = self.daemon_queue.get()
# data[1] mycall # data[1] mycall
@ -141,81 +141,81 @@ class DAEMON():
# data[21] enable FSK # data[21] enable FSK
# data[22] tx-audio-level # data[22] tx-audio-level
# data[23] respond_to_cq # data[23] respond_to_cq
if data[0] == 'STARTTNC': if data[0] == 'STARTTNC':
structlog.get_logger("structlog").warning("[DMN] Starting TNC", rig=data[5], port=data[6]) 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 # list of parameters, necessary for running subprocess command as a list
options = [] options = []
options.append('--port') options.append('--port')
options.append(str(static.DAEMONPORT - 1)) options.append(str(static.DAEMONPORT - 1))
options.append('--mycall') options.append('--mycall')
options.append(data[1]) options.append(data[1])
options.append('--mygrid') options.append('--mygrid')
options.append(data[2]) options.append(data[2])
options.append('--rx') options.append('--rx')
options.append(data[3]) options.append(data[3])
options.append('--tx') options.append('--tx')
options.append(data[4]) options.append(data[4])
# if radiocontrol != disabled # 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 # disabled mode
if data[13] != 'disabled': if data[13] != 'disabled':
options.append('--devicename') options.append('--devicename')
options.append(data[5]) options.append(data[5])
options.append('--deviceport') options.append('--deviceport')
options.append(data[6]) options.append(data[6])
options.append('--serialspeed') options.append('--serialspeed')
options.append(data[7]) options.append(data[7])
options.append('--pttprotocol') options.append('--pttprotocol')
options.append(data[8]) options.append(data[8])
options.append('--pttport') options.append('--pttport')
options.append(data[9]) options.append(data[9])
options.append('--data_bits') options.append('--data_bits')
options.append(data[10]) options.append(data[10])
options.append('--stop_bits') options.append('--stop_bits')
options.append(data[11]) options.append(data[11])
options.append('--handshake') options.append('--handshake')
options.append(data[12]) options.append(data[12])
options.append('--radiocontrol') options.append('--radiocontrol')
options.append(data[13]) options.append(data[13])
if data[13] != 'rigctld': if data[13] != 'rigctld':
options.append('--rigctld_ip') options.append('--rigctld_ip')
options.append(data[14]) options.append(data[14])
options.append('--rigctld_port') options.append('--rigctld_port')
options.append(data[15]) options.append(data[15])
if data[16] == 'True': if data[16] == 'True':
options.append('--scatter') options.append('--scatter')
if data[17] == 'True': if data[17] == 'True':
options.append('--fft') options.append('--fft')
if data[18] == 'True': if data[18] == 'True':
options.append('--500hz') options.append('--500hz')
options.append('--tuning_range_fmin') options.append('--tuning_range_fmin')
options.append(data[19]) options.append(data[19])
options.append('--tuning_range_fmax') options.append('--tuning_range_fmax')
options.append(data[20]) options.append(data[20])
@ -224,13 +224,13 @@ class DAEMON():
# options.append('--fsk') # options.append('--fsk')
options.append('--tx-audio-level') options.append('--tx-audio-level')
options.append(data[22]) options.append(data[22])
if data[23] == 'True': if data[23] == 'True':
options.append('--qrv') options.append('--qrv')
# try running tnc from binary, else run from source # try running tnc from binary, else run from source
# this helps running the tnc in a developer environment # this helps running the tnc in a developer environment
try: try:
@ -239,10 +239,10 @@ class DAEMON():
command.append('./freedata-tnc') command.append('./freedata-tnc')
elif sys.platform == 'win32' or sys.platform == 'win64': elif sys.platform == 'win32' or sys.platform == 'win64':
command.append('freedata-tnc.exe') command.append('freedata-tnc.exe')
command += options command += options
p = subprocess.Popen(command) p = subprocess.Popen(command)
atexit.register(p.kill) atexit.register(p.kill)
structlog.get_logger("structlog").info("[DMN] TNC started", path="binary") structlog.get_logger("structlog").info("[DMN] TNC started", path="binary")
@ -252,7 +252,7 @@ class DAEMON():
command.append('python3') command.append('python3')
elif sys.platform == 'win32' or sys.platform == 'win64': elif sys.platform == 'win32' or sys.platform == 'win64':
command.append('python') command.append('python')
command.append('main.py') command.append('main.py')
command += options command += options
p = subprocess.Popen(command) p = subprocess.Popen(command)
@ -269,7 +269,7 @@ class DAEMON():
structlog.get_logger("structlog").warning("[DMN] Stopping TNC") structlog.get_logger("structlog").warning("[DMN] Stopping TNC")
#os.kill(static.TNCPROCESS, signal.SIGKILL) #os.kill(static.TNCPROCESS, signal.SIGKILL)
static.TNCSTARTED = False static.TNCSTARTED = False
''' '''
# data[1] devicename # data[1] devicename
# data[2] deviceport # data[2] deviceport
# data[3] serialspeed # data[3] serialspeed
@ -306,15 +306,15 @@ class DAEMON():
import rigctld as rig import rigctld as rig
else: else:
import rigdummy as rig import rigdummy as rig
hamlib = rig.radio() 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 hamlib_version = rig.hamlib_version
hamlib.set_ptt(True) hamlib.set_ptt(True)
pttstate = hamlib.get_ptt() pttstate = hamlib.get_ptt()
if pttstate: 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'} response = {'command': 'test_hamlib', 'result': 'SUCCESS'}
@ -324,13 +324,13 @@ class DAEMON():
else: 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'} response = {'command': 'test_hamlib', 'result': 'FAILED'}
hamlib.set_ptt(False) hamlib.set_ptt(False)
hamlib.close_rig() hamlib.close_rig()
jsondata = json.dumps(response) jsondata = json.dumps(response)
sock.SOCKET_QUEUE.put(jsondata) sock.SOCKET_QUEUE.put(jsondata)
except Exception as e: except Exception as e:
print(e) print(e)
@ -343,27 +343,27 @@ if __name__ == '__main__':
# --------------------------------------------GET PARAMETER INPUTS # --------------------------------------------GET PARAMETER INPUTS
PARSER = argparse.ArgumentParser(description='FreeDATA Daemon') 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() ARGS = PARSER.parse_args()
static.DAEMONPORT = ARGS.socket_port static.DAEMONPORT = ARGS.socket_port
try: try:
if sys.platform == 'linux': if sys.platform == 'linux':
logging_path = os.getenv("HOME") + '/.config/' + 'FreeDATA/' + 'daemon' logging_path = os.getenv("HOME") + '/.config/' + 'FreeDATA/' + 'daemon'
if sys.platform == 'darwin': 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': 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): if not os.path.exists(logging_path):
os.makedirs(logging_path) os.makedirs(logging_path)
log_handler.setup_logging(logging_path) log_handler.setup_logging(logging_path)
except: except:
structlog.get_logger("structlog").error("[DMN] logger init error") structlog.get_logger("structlog").error("[DMN] logger init error")
try: try:
structlog.get_logger("structlog").info("[DMN] Starting TCP/IP socket", port=static.DAEMONPORT) structlog.get_logger("structlog").info("[DMN] Starting TCP/IP socket", port=static.DAEMONPORT)
@ -379,7 +379,7 @@ if __name__ == '__main__':
os._exit(1) os._exit(1)
daemon = DAEMON() daemon = DAEMON()
structlog.get_logger("structlog").info("[DMN] Starting FreeDATA Daemon", author="DJ2LS", year="2022", version=static.VERSION) structlog.get_logger("structlog").info("[DMN] Starting FreeDATA Daemon", author="DJ2LS", year="2022", version=static.VERSION)
while True: while True:
time.sleep(1) time.sleep(1)

File diff suppressed because it is too large Load diff

View file

@ -16,7 +16,7 @@ def wait(seconds):
""" """
Args: Args:
seconds: seconds:
Returns: Returns:
@ -26,18 +26,18 @@ def wait(seconds):
while time.time() < timeout: while time.time() < timeout:
time.sleep(0.01) time.sleep(0.01)
return True return True
def get_crc_8(data): def get_crc_8(data):
"""Author: DJ2LS """Author: DJ2LS
Get the CRC8 of a byte string Get the CRC8 of a byte string
param: data = bytes() param: data = bytes()
Args: Args:
data: data:
Returns: Returns:
@ -50,13 +50,13 @@ def get_crc_8(data):
def get_crc_16(data): def get_crc_16(data):
"""Author: DJ2LS """Author: DJ2LS
Get the CRC16 of a byte string Get the CRC16 of a byte string
param: data = bytes() param: data = bytes()
Args: Args:
data: data:
Returns: Returns:
@ -68,35 +68,35 @@ def get_crc_16(data):
def get_crc_24(data): def get_crc_24(data):
"""Author: DJ2LS """Author: DJ2LS
Get the CRC24-OPENPGP of a byte string Get the CRC24-OPENPGP of a byte string
https://github.com/GardenTools/CrcEngine#examples https://github.com/GardenTools/CrcEngine#examples
param: data = bytes() param: data = bytes()
Args: Args:
data: data:
Returns: Returns:
""" """
crc_algorithm = crcengine.create(0x864cfb, 24, 0xb704ce, ref_in=False, crc_algorithm = crcengine.create(0x864cfb, 24, 0xb704ce, ref_in=False,
ref_out=False, xor_out=0, ref_out=False, xor_out=0,
name='crc-24-openpgp') name='crc-24-openpgp')
crc_data = crc_algorithm(data) crc_data = crc_algorithm(data)
crc_data = crc_data.to_bytes(3, byteorder='big') crc_data = crc_data.to_bytes(3, byteorder='big')
return crc_data return crc_data
def get_crc_32(data): def get_crc_32(data):
"""Author: DJ2LS """Author: DJ2LS
Get the CRC32 of a byte string Get the CRC32 of a byte string
param: data = bytes() param: data = bytes()
Args: Args:
data: data:
Returns: Returns:
@ -111,12 +111,12 @@ def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency):
""" """
Args: Args:
dxcallsign: dxcallsign:
dxgrid: dxgrid:
datatype: datatype:
snr: snr:
offset: offset:
frequency: frequency:
Returns: Returns:
@ -149,7 +149,7 @@ def callsign_to_bytes(callsign):
""" """
Args: Args:
callsign: callsign:
Returns: Returns:
@ -171,21 +171,21 @@ def callsign_to_bytes(callsign):
#-13 Weather stations #-13 Weather stations
#-14 Truckers or generally full time drivers #-14 Truckers or generally full time drivers
#-15 generic additional station, digi, mobile, wx, etc #-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: try:
callsign = bytes(callsign, 'utf-8') callsign = bytes(callsign, 'utf-8')
except: except:
pass 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'-') callsign = callsign.split(b'-')
try: try:
ssid = int(callsign[1]) ssid = int(callsign[1])
except: except:
ssid = 0 ssid = 0
#callsign = callsign[0] #callsign = callsign[0]
#bytestring = bytearray(8) #bytestring = bytearray(8)
#bytestring[:len(callsign)] = callsign #bytestring[:len(callsign)] = callsign
#bytestring[7:8] = bytes([ssid]) #bytestring[7:8] = bytes([ssid])
@ -196,14 +196,14 @@ def callsign_to_bytes(callsign):
return encode_call(callsign + ssid) return encode_call(callsign + ssid)
#return bytes(bytestring) #return bytes(bytestring)
def bytes_to_callsign(bytestring): def bytes_to_callsign(bytestring):
""" """
Convert our callsign, received by a frame to a callsign in a human readable format Convert our callsign, received by a frame to a callsign in a human readable format
Args: Args:
bytestring: bytestring:
Returns: Returns:
bytes bytes
@ -227,8 +227,8 @@ def bytes_to_callsign(bytestring):
#-13 Weather stations #-13 Weather stations
#-14 Truckers or generally full time drivers #-14 Truckers or generally full time drivers
#-15 generic additional station, digi, mobile, wx, etc #-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 = bytes(bytestring[:7])
callsign = callsign.rstrip(b'\x00') callsign = callsign.rstrip(b'\x00')
@ -238,8 +238,8 @@ def bytes_to_callsign(bytestring):
callsign = callsign.decode('utf-8') callsign = callsign.decode('utf-8')
callsign = callsign + str(ssid) callsign = callsign + str(ssid)
callsign = callsign.encode('utf-8') callsign = callsign.encode('utf-8')
return bytes(callsign) return bytes(callsign)
''' '''
decoded = decode_call(bytestring) decoded = decode_call(bytestring)
callsign = decoded[:-1] callsign = decoded[:-1]
@ -254,23 +254,23 @@ def check_callsign(callsign:bytes, crc_to_check:bytes):
Args: Args:
callsign: Callsign which we want to check 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: Returns:
[True, Callsign + SSID] [True, Callsign + SSID]
False False
""" """
print(callsign) print(callsign)
try: try:
callsign = callsign.split(b'-') callsign = callsign.split(b'-')
callsign = callsign[0] # we want the callsign without SSID callsign = callsign[0] # we want the callsign without SSID
except: except:
callsign = callsign callsign = callsign
for ssid in static.SSID_LIST: 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('-'.encode('utf-8'))
call_with_ssid.extend(str(ssid).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: if callsign_crc == crc_to_check:
print(call_with_ssid) print(call_with_ssid)
return [True, bytes(call_with_ssid)] return [True, bytes(call_with_ssid)]
return [False, ""] 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) 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 code_word = code_word >> 5
grid = chr((code_word & 0b11111) + 65) + grid grid = chr((code_word & 0b11111) + 65) + grid
@ -359,7 +359,7 @@ def encode_call(call):
out_code_word = int(0) out_code_word = int(0)
call = call.upper() # upper case to be save call = call.upper() # upper case to be save
for x in call: for x in call:
int_val = ord(x)-48 # -48 reduce bits, begin with first number utf8 table 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 << 6 # shift left 6 bit, making space for a new char

View file

@ -3,7 +3,7 @@ def setup_logging(filename):
""" """
Args: Args:
filename: filename:
Returns: Returns:

View file

@ -33,7 +33,7 @@ def signal_handler(sig, frame):
a signal handler, which closes the network/socket when closing the application a signal handler, which closes the network/socket when closing the application
Args: Args:
sig: signal sig: signal
frame: frame:
Returns: system exit Returns: system exit
@ -41,7 +41,7 @@ def signal_handler(sig, frame):
print('Closing TNC...') print('Closing TNC...')
sock.CLOSE_SIGNAL = True sock.CLOSE_SIGNAL = True
sys.exit(0) sys.exit(0)
signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGINT, signal_handler)
if __name__ == '__main__': if __name__ == '__main__':
@ -51,31 +51,31 @@ if __name__ == '__main__':
PARSER = argparse.ArgumentParser(description='FreeDATA TNC') PARSER = argparse.ArgumentParser(description='FreeDATA TNC')
PARSER.add_argument('--mycall', dest="mycall", default="AA0AA", help="My callsign", type=str) 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('--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('--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('--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('--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('--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('--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('--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('--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('--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('--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('--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('--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('--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_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('--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('--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('--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('--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('--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('--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_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('--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() ARGS = PARSER.parse_args()
# additional step for beeing sure our callsign is correctly # additional step for beeing sure our callsign is correctly
@ -84,10 +84,10 @@ if __name__ == '__main__':
mycallsign = bytes(ARGS.mycall.upper(), 'utf-8') mycallsign = bytes(ARGS.mycall.upper(), 'utf-8')
mycallsign = helpers.callsign_to_bytes(mycallsign) mycallsign = helpers.callsign_to_bytes(mycallsign)
static.MYCALLSIGN = helpers.bytes_to_callsign(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.SSID_LIST = ARGS.ssid_list
static.MYGRID = bytes(ARGS.mygrid, 'utf-8') static.MYGRID = bytes(ARGS.mygrid, 'utf-8')
static.AUDIO_INPUT_DEVICE = ARGS.audio_input_device static.AUDIO_INPUT_DEVICE = ARGS.audio_input_device
static.AUDIO_OUTPUT_DEVICE = ARGS.audio_output_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.HAMLIB_RGICTLD_PORT = str(ARGS.rigctld_port)
static.ENABLE_SCATTER = ARGS.send_scatter static.ENABLE_SCATTER = ARGS.send_scatter
static.ENABLE_FFT = ARGS.send_fft static.ENABLE_FFT = ARGS.send_fft
static.ENABLE_FSK = ARGS.enable_fsk static.ENABLE_FSK = ARGS.enable_fsk
static.LOW_BANDWITH_MODE = ARGS.low_bandwith_mode static.LOW_BANDWITH_MODE = ARGS.low_bandwith_mode
static.TUNING_RANGE_FMIN = ARGS.tuning_range_fmin static.TUNING_RANGE_FMIN = ARGS.tuning_range_fmin
static.TUNING_RANGE_FMAX = ARGS.tuning_range_fmax static.TUNING_RANGE_FMAX = ARGS.tuning_range_fmax
static.TX_AUDIO_LEVEL = ARGS.tx_audio_level static.TX_AUDIO_LEVEL = ARGS.tx_audio_level
static.RESPOND_TO_CQ = ARGS.enable_respond_to_cq 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 # we need to wait until we got all parameters from argparse first before we can load the other modules
import sock import sock
# config logging # config logging
try: try:
if sys.platform == 'linux': if sys.platform == 'linux':
logging_path = os.getenv("HOME") + '/.config/' + 'FreeDATA/' + 'tnc' logging_path = os.getenv("HOME") + '/.config/' + 'FreeDATA/' + 'tnc'
if sys.platform == 'darwin': 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': 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): if not os.path.exists(logging_path):
os.makedirs(logging_path) os.makedirs(logging_path)
log_handler.setup_logging(logging_path) log_handler.setup_logging(logging_path)
except: 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) structlog.get_logger("structlog").info("[TNC] Starting FreeDATA", author="DJ2LS", year="2022", version=static.VERSION)
# start data handler # start data handler
data_handler.DATA() data_handler.DATA()
# start modem # start modem
modem = modem.RF() modem = modem.RF()

View file

@ -45,10 +45,10 @@ class RF():
""" """ """ """
def __init__(self): def __init__(self):
self.sampler_avg = 0 self.sampler_avg = 0
self.buffer_avg = 0 self.buffer_avg = 0
self.AUDIO_SAMPLE_RATE_RX = 48000 self.AUDIO_SAMPLE_RATE_RX = 48000
self.AUDIO_SAMPLE_RATE_TX = 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_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_CHUNKS = 48 #8 * (self.AUDIO_SAMPLE_RATE_RX/self.MODEM_SAMPLE_RATE) #48
self.AUDIO_CHANNELS = 1 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/127
# https://github.com/DJ2LS/FreeDATA/issues/99 # https://github.com/DJ2LS/FreeDATA/issues/99
self.mod_out_locked = True 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 assert (self.AUDIO_SAMPLE_RATE_RX / self.MODEM_SAMPLE_RATE) == codec2.api.FDMDV_OS_48
# small hack for initializing codec2 via codec2.py module # small hack for initializing codec2 via codec2.py module
# TODO: we need to change the entire modem module to integrate codec2 module # TODO: we need to change the entire modem module to integrate codec2 module
self.c_lib = codec2.api self.c_lib = codec2.api
@ -76,11 +76,11 @@ class RF():
# init FIFO queue to store modulation out in # init FIFO queue to store modulation out in
self.modoutqueue = deque() self.modoutqueue = deque()
# define fft_data buffer # define fft_data buffer
self.fft_data = bytes() 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.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.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_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) #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) self.fsk_ldpc_buffer_1 = codec2.audio_buffer(self.AUDIO_FRAMES_PER_BUFFER_RX)
# initial nin values # initial nin values
self.datac0_nin = codec2.api.freedv_nin(self.datac0_freedv) self.datac0_nin = codec2.api.freedv_nin(self.datac0_freedv)
self.datac1_nin = codec2.api.freedv_nin(self.datac1_freedv) self.datac1_nin = codec2.api.freedv_nin(self.datac1_freedv)
self.datac3_nin = codec2.api.freedv_nin(self.datac3_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_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_1 = codec2.api.freedv_nin(self.fsk_ldpc_freedv_1)
# --------------------------------------------CREATE PYAUDIO INSTANCE # --------------------------------------------CREATE PYAUDIO INSTANCE
if not TESTMODE:
''' try:
try:
# we need to "try" this, because sometimes libasound.so isn't in the default place 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)
# try to supress error messages atexit.register(self.stream.stop)
with audio.noalsaerr(): # https://github.com/DJ2LS/FreeDATA/issues/22 structlog.get_logger("structlog").info("opened audio devices")
self.p = audio.pyaudio.PyAudio()
# else do it the default way except Exception as e:
except: structlog.get_logger("structlog").error("can't open audio device. Exit", e=e)
self.p = audio.pyaudio.PyAudio() os._exit(1)
atexit.register(self.p.terminate)
try:
# --------------------------------------------OPEN RX AUDIO CHANNEL structlog.get_logger("structlog").debug("[TNC] starting pyaudio callback")
# optional auto selection of loopback device if using in testmode #self.audio_stream.start_stream()
if static.AUDIO_INPUT_DEVICE == -2: self.stream.start()
loopback_list = []
for dev in range(0,self.p.get_device_count()): except Exception as e:
if 'Loopback: PCM' in self.p.get_device_info_by_index(dev)["name"]: structlog.get_logger("structlog").error("[TNC] starting pyaudio callback failed", e=e)
loopback_list.append(dev)
if len(loopback_list) >= 2: else:
static.AUDIO_INPUT_DEVICE = loopback_list[0] #0 = RX
static.AUDIO_OUTPUT_DEVICE = loopback_list[1] #1 = TX # create a stream object for simulating audio stream
print(f"loopback_list rx: {loopback_list}", file=sys.stderr) class Object(object):
pass
''' self.stream = Object()
try: self.stream.active = True
'''
self.audio_stream = self.p.open(format=audio.pyaudio.paInt16, # create mkfifo buffer
channels=self.AUDIO_CHANNELS, try:
rate=self.AUDIO_SAMPLE_RATE_RX,
frames_per_buffer=self.AUDIO_FRAMES_PER_BUFFER_RX, os.mkfifo(RXCHANNEL)
input=True, os.mkfifo(TXCHANNEL)
output=True, except:
input_device_index=static.AUDIO_INPUT_DEVICE, pass
output_device_index=static.AUDIO_OUTPUT_DEVICE,
stream_callback=self.audio_callback 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)
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) mkfifo_read_callback_thread.start()
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)
# --------------------------------------------INIT AND OPEN HAMLIB # --------------------------------------------INIT AND OPEN HAMLIB
# check how we want to control the radio # check how we want to control the radio
@ -196,9 +180,9 @@ class RF():
elif static.HAMLIB_RADIOCONTROL == 'disabled': elif static.HAMLIB_RADIOCONTROL == 'disabled':
import rigdummy as rig import rigdummy as rig
else: else:
import rigdummy as rig import rigdummy as rig
self.hamlib = rig.radio() 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) 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: 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() 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_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_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() audio_thread_datac3.start()
if static.ENABLE_FSK: 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_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() 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() 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_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() 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 audio_callback(self, data_in48k, frame_count, time_info, status):
def callback(self, data_in48k, outdata, frames, time, status): def callback(self, data_in48k, outdata, frames, time, status):
""" """
Args: Args:
data_in48k: data_in48k:
frame_count: frame_count:
time_info: time_info:
status: status:
Returns: Returns:
""" """
x = np.frombuffer(data_in48k, dtype=np.int16) 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) length_x = len(x)
# avoid decoding when transmitting to reduce CPU # avoid decoding when transmitting to reduce CPU
if not static.TRANSMITTING: if not static.TRANSMITTING:
# avoid buffer overflow by filling only if buffer not full # avoid buffer overflow by filling only if buffer not full
@ -259,14 +292,14 @@ class RF():
self.datac0_buffer.push(x) self.datac0_buffer.push(x)
else: else:
static.BUFFER_OVERFLOW_COUNTER[0] += 1 static.BUFFER_OVERFLOW_COUNTER[0] += 1
# avoid buffer overflow by filling only if buffer not full and selected datachannel mode # 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 not self.datac1_buffer.nbuffer+length_x > self.datac1_buffer.size:
if RECEIVE_DATAC1: if RECEIVE_DATAC1:
self.datac1_buffer.push(x) self.datac1_buffer.push(x)
else: else:
static.BUFFER_OVERFLOW_COUNTER[1] += 1 static.BUFFER_OVERFLOW_COUNTER[1] += 1
# avoid buffer overflow by filling only if buffer not full and selected datachannel mode # 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 not self.datac3_buffer.nbuffer+length_x > self.datac3_buffer.size:
if RECEIVE_DATAC3: if RECEIVE_DATAC3:
@ -276,29 +309,29 @@ class RF():
# avoid buffer overflow by filling only if buffer not full and selected datachannel mode # 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 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) self.fsk_ldpc_buffer_0.push(x)
else: else:
static.BUFFER_OVERFLOW_COUNTER[3] += 1 static.BUFFER_OVERFLOW_COUNTER[3] += 1
# avoid buffer overflow by filling only if buffer not full and selected datachannel mode # 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 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) self.fsk_ldpc_buffer_1.push(x)
else: else:
static.BUFFER_OVERFLOW_COUNTER[4] += 1 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: #if not self.modoutqueue or self.mod_out_locked:
data_out48k = np.zeros(frames, dtype=np.int16) data_out48k = np.zeros(frames, dtype=np.int16)
self.fft_data = x self.fft_data = x
else: else:
data_out48k = self.modoutqueue.popleft() data_out48k = self.modoutqueue.popleft()
self.fft_data = data_out48k self.fft_data = data_out48k
try: try:
outdata[:] = data_out48k[:frames] outdata[:] = data_out48k[:frames]
except Exception as e: except Exception as e:
print(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: Args:
mode: mode:
repeats: repeats:
repeat_delay: repeat_delay:
frames: frames:
Returns: Returns:
@ -324,19 +357,19 @@ class RF():
jsondata = {"ptt":"True"} jsondata = {"ptt":"True"}
data_out = json.dumps(jsondata) data_out = json.dumps(jsondata)
sock.SOCKET_QUEUE.put(data_out) sock.SOCKET_QUEUE.put(data_out)
# open codec2 instance # open codec2 instance
self.MODE = mode self.MODE = mode
if self.MODE == 'FSK_LDPC_0' or self.MODE == 200: 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) 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: 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: else:
freedv = cast(codec2.api.freedv_open(self.MODE), c_void_p) freedv = cast(codec2.api.freedv_open(self.MODE), c_void_p)
# get number of bytes per frame for mode # get number of bytes per frame for mode
bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(freedv)/8) bytes_per_frame = int(codec2.api.freedv_get_bits_per_modem_frame(freedv)/8)
payload_bytes_per_frame = bytes_per_frame -2 payload_bytes_per_frame = bytes_per_frame -2
@ -356,12 +389,12 @@ class RF():
data_delay_mseconds = 0 #miliseconds data_delay_mseconds = 0 #miliseconds
data_delay = int(self.MODEM_SAMPLE_RATE*(data_delay_mseconds/1000)) data_delay = int(self.MODEM_SAMPLE_RATE*(data_delay_mseconds/1000))
mod_out_silence = create_string_buffer(data_delay*2) 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) structlog.get_logger("structlog").debug("TRANSMIT", mode=self.MODE, payload=payload_bytes_per_frame)
for i in range(0,repeats): 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: 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 # write preamble to txbuffer
codec2.api.freedv_rawdatapreambletx(freedv, mod_out_preamble) 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 = 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 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 algorithm incompatibilities
crc = ctypes.c_ushort(codec2.api.freedv_gen_crc16(bytes(buffer), payload_bytes_per_frame)) # generate CRC16 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 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) 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) 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: 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 # write preamble to txbuffer
codec2.api.freedv_rawdatapostambletx(freedv, mod_out_postamble) codec2.api.freedv_rawdatapostambletx(freedv, mod_out_postamble)
txbuffer += bytes(mod_out_postamble) txbuffer += bytes(mod_out_postamble)
# append postamble to txbuffer # append postamble to txbuffer
# add delay to end of frames # add delay to end of frames
samples_delay = int(self.MODEM_SAMPLE_RATE*(repeat_delay/1000)) samples_delay = int(self.MODEM_SAMPLE_RATE*(repeat_delay/1000))
mod_out_silence = create_string_buffer(samples_delay*2) mod_out_silence = create_string_buffer(samples_delay*2)
txbuffer += bytes(mod_out_silence) txbuffer += bytes(mod_out_silence)
# resample up to 48k (resampler works on np.int16) # resample up to 48k (resampler works on np.int16)
x = np.frombuffer(txbuffer, dtype=np.int16) x = np.frombuffer(txbuffer, dtype=np.int16)
x = set_audio_volume(x, static.TX_AUDIO_LEVEL) 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 # explicitly lock our usage of mod_out_queue if needed
# deaktivated for testing purposes # deaktivated for testing purposes
self.mod_out_locked = False self.mod_out_locked = False
# ------------------------------- # -------------------------------
chunk_length = self.AUDIO_FRAMES_PER_BUFFER_TX #4800 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) #structlog.get_logger("structlog").debug("[TNC] mod out shorter than audio buffer", delta=delta)
self.modoutqueue.append(c) 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 self.mod_out_locked = False
while self.modoutqueue: while self.modoutqueue:
time.sleep(0.01) time.sleep(0.01)
static.PTT_STATE = self.hamlib.set_ptt(False) static.PTT_STATE = self.hamlib.set_ptt(False)
# push ptt state to socket stream # push ptt state to socket stream
jsondata = {"ptt":"False"} jsondata = {"ptt":"False"}
data_out = json.dumps(jsondata) data_out = json.dumps(jsondata)
sock.SOCKET_QUEUE.put(data_out) 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 we want to set the locking state back to true to be prepared for next transmission
self.mod_out_locked = True self.mod_out_locked = True
self.c_lib.freedv_close(freedv) self.c_lib.freedv_close(freedv)
self.modem_transmit_queue.task_done() self.modem_transmit_queue.task_done()
static.TRANSMITTING = False static.TRANSMITTING = False
threading.Event().set() threading.Event().set()
def audio_datac0(self): def audio_datac0(self):
""" """ """ """
nbytes_datac0 = 0 nbytes_datac0 = 0
while self.stream.active: while self.stream.active:
threading.Event().wait(0.01) threading.Event().wait(0.01)
while self.datac0_buffer.nbuffer >= self.datac0_nin: while self.datac0_buffer.nbuffer >= self.datac0_nin:
# demodulate audio # demodulate audio
nbytes_datac0 = codec2.api.freedv_rawdatarx(self.datac0_freedv, self.datac0_bytes_out, self.datac0_buffer.buffer.ctypes) 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) 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.modem_received_queue.put([self.datac1_bytes_out, self.datac1_freedv ,self.datac1_bytes_per_frame])
#self.get_scatter(self.datac1_freedv) #self.get_scatter(self.datac1_freedv)
self.calculate_snr(self.datac1_freedv) self.calculate_snr(self.datac1_freedv)
def audio_datac3(self): def audio_datac3(self):
""" """ """ """
nbytes_datac3 = 0 nbytes_datac3 = 0
while self.stream.active: while self.stream.active:
threading.Event().wait(0.01) threading.Event().wait(0.01)
while self.datac3_buffer.nbuffer >= self.datac3_nin: 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) 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_buffer.pop(self.datac3_nin)
self.datac3_nin = codec2.api.freedv_nin(self.datac3_freedv) self.datac3_nin = codec2.api.freedv_nin(self.datac3_freedv)
if nbytes_datac3 == self.datac3_bytes_per_frame: 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.get_scatter(self.datac3_freedv)
self.calculate_snr(self.datac3_freedv) self.calculate_snr(self.datac3_freedv)
def audio_fsk_ldpc_0(self): def audio_fsk_ldpc_0(self):
""" """ """ """
nbytes_fsk_ldpc_0 = 0 nbytes_fsk_ldpc_0 = 0
while self.stream.active and static.ENABLE_FSK: while self.stream.active and static.ENABLE_FSK:
threading.Event().wait(0.01) threading.Event().wait(0.01)
while self.fsk_ldpc_buffer_0.nbuffer >= self.fsk_ldpc_nin_0: 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) 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_buffer_0.pop(self.fsk_ldpc_nin_0)
self.fsk_ldpc_nin_0 = codec2.api.freedv_nin(self.fsk_ldpc_freedv_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: 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.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 nbytes_fsk_ldpc_1 = 0
while self.stream.active and static.ENABLE_FSK: while self.stream.active and static.ENABLE_FSK:
threading.Event().wait(0.01) threading.Event().wait(0.01)
while self.fsk_ldpc_buffer_1.nbuffer >= self.fsk_ldpc_nin_1: 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) 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_buffer_1.pop(self.fsk_ldpc_nin_1)
self.fsk_ldpc_nin_1 = codec2.api.freedv_nin(self.fsk_ldpc_freedv_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: 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.get_scatter(self.fsk_ldpc_freedv_1)
self.calculate_snr(self.fsk_ldpc_freedv_1) self.calculate_snr(self.fsk_ldpc_freedv_1)
# worker for FIFO queue for processing received frames # worker for FIFO queue for processing received frames
def worker_transmit(self): def worker_transmit(self):
""" """ """ """
while True: while True:
data = self.modem_transmit_queue.get() 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()
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
# worker for FIFO queue for processing received frames
def worker_received(self): def worker_received(self):
""" """ """ """
while True: while True:
@ -543,13 +576,13 @@ class RF():
# data[2] = bytes_per_frame # data[2] = bytes_per_frame
data_handler.DATA_QUEUE_RECEIVED.put([data[0], data[1], data[2]]) data_handler.DATA_QUEUE_RECEIVED.put([data[0], data[1], data[2]])
self.modem_received_queue.task_done() self.modem_received_queue.task_done()
def get_frequency_offset(self, freedv): def get_frequency_offset(self, freedv):
""" """
Args: Args:
freedv: freedv:
Returns: Returns:
@ -560,13 +593,13 @@ class RF():
offset = round(modemStats.foff) * (-1) offset = round(modemStats.foff) * (-1)
static.FREQ_OFFSET = offset static.FREQ_OFFSET = offset
return offset return offset
def get_scatter(self, freedv): def get_scatter(self, freedv):
""" """
Args: Args:
freedv: freedv:
Returns: Returns:
@ -596,12 +629,12 @@ class RF():
scatterdata_small = scatterdata[::10] scatterdata_small = scatterdata[::10]
static.SCATTER = scatterdata_small static.SCATTER = scatterdata_small
def calculate_snr(self, freedv): def calculate_snr(self, freedv):
""" """
Args: Args:
freedv: freedv:
Returns: Returns:
@ -633,19 +666,19 @@ class RF():
static.HAMLIB_MODE = self.hamlib.get_mode() static.HAMLIB_MODE = self.hamlib.get_mode()
static.HAMLIB_BANDWITH = self.hamlib.get_bandwith() static.HAMLIB_BANDWITH = self.hamlib.get_bandwith()
def calculate_fft(self): def calculate_fft(self):
""" """ """ """
# channel_busy_delay counter # channel_busy_delay counter
channel_busy_delay = 0 channel_busy_delay = 0
while True: while True:
#time.sleep(0.01) #time.sleep(0.01)
threading.Event().wait(0.01) threading.Event().wait(0.01)
# WE NEED TO OPTIMIZE THIS! # WE NEED TO OPTIMIZE THIS!
if len(self.fft_data) >= 128: if len(self.fft_data) >= 128:
# https://gist.github.com/ZWMiller/53232427efc5088007cab6feee7c6e4c # https://gist.github.com/ZWMiller/53232427efc5088007cab6feee7c6e4c
# Fast Fourier Transform, 10*log10(abs) is to scale it to dB # Fast Fourier Transform, 10*log10(abs) is to scale it to dB
# and make sure it's not imaginary # and make sure it's not imaginary
@ -656,7 +689,7 @@ class RF():
# set value 0 to 1 to avoid division by zero # set value 0 to 1 to avoid division by zero
fftarray[fftarray == 0] = 1 fftarray[fftarray == 0] = 1
dfft = 10.*np.log10(abs(fftarray)) dfft = 10.*np.log10(abs(fftarray))
# get average of dfft # get average of dfft
avg = np.mean(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 # 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 # have to do this when we are not transmittig so our own sending data will not affect this too much
if not static.TRANSMITTING: if not static.TRANSMITTING:
dfft[dfft>avg+10] = 100 dfft[dfft>avg+10] = 100
# calculate audio max value # 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" # 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 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: if np.sum(dfft[dfft>avg+10]) >= 300 and not static.TRANSMITTING:
static.CHANNEL_BUSY = True static.CHANNEL_BUSY = True
channel_busy_delay += 5 channel_busy_delay += 5
@ -681,56 +714,56 @@ class RF():
if channel_busy_delay > 50: if channel_busy_delay > 50:
channel_busy_delay = 50 channel_busy_delay = 50
else: 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 channel_busy_delay -= 1
if channel_busy_delay < 0: if channel_busy_delay < 0:
channel_busy_delay = 0 channel_busy_delay = 0
# if our channel busy counter reached 0, we toggle state to False # if our channel busy counter reached 0, we toggle state to False
if channel_busy_delay == 0: if channel_busy_delay == 0:
static.CHANNEL_BUSY = False static.CHANNEL_BUSY = False
# round data to decrease size # round data to decrease size
dfft = np.around(dfft, 0) dfft = np.around(dfft, 0)
dfftlist = dfft.tolist() dfftlist = dfft.tolist()
static.FFT = dfftlist[0:320] #320 --> bandwith 3000 static.FFT = dfftlist[0:320] #320 --> bandwith 3000
except: except:
structlog.get_logger("structlog").debug("[TNC] Setting fft=0") structlog.get_logger("structlog").debug("[TNC] Setting fft=0")
# else 0 # else 0
static.FFT = [0] static.FFT = [0]
else: else:
pass pass
def set_frames_per_burst(self, n_frames_per_burst): def set_frames_per_burst(self, n_frames_per_burst):
""" """
Args: Args:
n_frames_per_burst: n_frames_per_burst:
Returns: Returns:
""" """
codec2.api.freedv_set_frames_per_burst(self.datac1_freedv,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.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.fsk_ldpc_freedv_0,n_frames_per_burst)
def get_bytes_per_frame(mode): def get_bytes_per_frame(mode):
""" """
provide bytes per frame information for accessing from data handler provide bytes per frame information for accessing from data handler
Args: Args:
mode: mode:
Returns: Returns:
""" """
if mode == 200: 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: 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) 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: else:
@ -738,10 +771,10 @@ def get_bytes_per_frame(mode):
# get number of bytes per frame for 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): def set_audio_volume(datalist, volume):
data = np.fromstring(datalist, np.int16) * (volume / 100.) data = np.fromstring(datalist, np.int16) * (volume / 100.)
return data.astype(np.int16) return data.astype(np.int16)

View file

@ -20,17 +20,17 @@ except:
sys.path.append(app_path) sys.path.append(app_path)
# try importing hamlib # try importing hamlib
try: try:
# get python version # get python version
python_version = str(sys.version_info[0]) + "." + str(sys.version_info[1]) python_version = str(sys.version_info[0]) + "." + str(sys.version_info[1])
# installation path for Ubuntu 20.04 LTS python modules # installation path for Ubuntu 20.04 LTS python modules
#sys.path.append('/usr/local/lib/python'+ python_version +'/site-packages') #sys.path.append('/usr/local/lib/python'+ python_version +'/site-packages')
# installation path for Ubuntu 20.10 + # installation path for Ubuntu 20.10 +
sys.path.append('/usr/local/lib/') sys.path.append('/usr/local/lib/')
# installation path for Suse # installation path for Suse
sys.path.append('/usr/local/lib64/python'+ python_version +'/site-packages') 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.8/site-packages')
sys.path.append('/usr/local/lib/python3.9/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('/usr/local/lib/python3.10/site-packages')
sys.path.append('lib/hamlib/linux/python3.8/site-packages') sys.path.append('lib/hamlib/linux/python3.8/site-packages')
import Hamlib import Hamlib
# https://stackoverflow.com/a/4703409 # 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]) hamlib_version = float(hamlib_version[0])
min_hamlib_version = 4.1 min_hamlib_version = 4.1
if hamlib_version > min_hamlib_version: if hamlib_version > min_hamlib_version:
structlog.get_logger("structlog").info("[RIG] Hamlib found", version=hamlib_version) structlog.get_logger("structlog").info("[RIG] Hamlib found", version=hamlib_version)
@ -68,15 +68,15 @@ except Exception as e:
else: else:
raise Exception raise Exception
except Exception as e: 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 hamlib_version = 0
sys.exit() sys.exit()
class radio: class radio:
""" """ """ """
def __init__(self): def __init__(self):
self.devicename = '' self.devicename = ''
self.devicenumber = '' self.devicenumber = ''
self.deviceport = '' self.deviceport = ''
@ -87,26 +87,26 @@ class radio:
self.data_bits = '' self.data_bits = ''
self.stop_bits = '' self.stop_bits = ''
self.handshake = '' 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: Args:
devicename: devicename:
deviceport: deviceport:
hamlib_ptt_type: hamlib_ptt_type:
serialspeed: serialspeed:
pttport: pttport:
data_bits: data_bits:
stop_bits: stop_bits:
handshake: handshake:
rigctld_port: rigctld_port:
rigctld_ip: rigctld_ip:
Returns: Returns:
""" """
self.devicename = devicename self.devicename = devicename
self.deviceport = str(deviceport) self.deviceport = str(deviceport)
self.serialspeed = str(serialspeed) # we need to ensure this is a str, otherwise set_conf functions are crashing 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.data_bits = str(data_bits)
self.stop_bits = str(stop_bits) self.stop_bits = str(stop_bits)
self.handshake = str(handshake) self.handshake = str(handshake)
# try to init hamlib # try to init hamlib
try: try:
Hamlib.rig_set_debug(Hamlib.RIG_DEBUG_NONE) Hamlib.rig_set_debug(Hamlib.RIG_DEBUG_NONE)
@ -127,8 +127,8 @@ class radio:
except: except:
structlog.get_logger("structlog").error("[RIG] Hamlib: rig not supported...") structlog.get_logger("structlog").error("[RIG] Hamlib: rig not supported...")
self.devicenumber = 0 self.devicenumber = 0
self.my_rig = Hamlib.Rig(self.devicenumber) self.my_rig = Hamlib.Rig(self.devicenumber)
self.my_rig.set_conf("rig_pathname", self.deviceport) self.my_rig.set_conf("rig_pathname", self.deviceport)
self.my_rig.set_conf("retry", "5") 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("stop_bits", self.stop_bits)
self.my_rig.set_conf("data_bits", self.data_bits) self.my_rig.set_conf("data_bits", self.data_bits)
self.my_rig.set_conf("ptt_pathname", self.pttport) self.my_rig.set_conf("ptt_pathname", self.pttport)
if self.hamlib_ptt_type == 'RIG': if self.hamlib_ptt_type == 'RIG':
self.hamlib_ptt_type = Hamlib.RIG_PTT_RIG self.hamlib_ptt_type = Hamlib.RIG_PTT_RIG
self.my_rig.set_conf("ptt_type", 'RIG') self.my_rig.set_conf("ptt_type", 'RIG')
@ -176,7 +176,7 @@ class radio:
elif self.hamlib_ptt_type == 'RIG_PTT_NONE': elif self.hamlib_ptt_type == 'RIG_PTT_NONE':
self.hamlib_ptt_type = Hamlib.RIG_PTT_NONE self.hamlib_ptt_type = Hamlib.RIG_PTT_NONE
else: #self.hamlib_ptt_type == 'RIG_PTT_NONE': else: #self.hamlib_ptt_type == 'RIG_PTT_NONE':
self.hamlib_ptt_type = Hamlib.RIG_PTT_NONE self.hamlib_ptt_type = Hamlib.RIG_PTT_NONE
@ -185,13 +185,13 @@ class radio:
self.my_rig.open() self.my_rig.open()
atexit.register(self.my_rig.close) atexit.register(self.my_rig.close)
try: try:
# lets determine the error message when opening rig # lets determine the error message when opening rig
error = str(Hamlib.rigerror(my_rig.error_status)).splitlines() error = str(Hamlib.rigerror(my_rig.error_status)).splitlines()
error = error[1].split('err=') error = error[1].split('err=')
error = error[1] error = error[1]
if error == 'Permission denied': if error == 'Permission denied':
structlog.get_logger("structlog").error("[RIG] Hamlib has no permissions", e = error) 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' help_url = 'https://github.com/DJ2LS/FreeDATA/wiki/UBUNTU-Manual-installation#1-permissions'
@ -212,16 +212,16 @@ class radio:
except Exception as e: except Exception as e:
structlog.get_logger("structlog").error("[RIG] Hamlib - can't open rig", error=e, e=sys.exc_info()[0]) structlog.get_logger("structlog").error("[RIG] Hamlib - can't open rig", error=e, e=sys.exc_info()[0])
return False return False
def get_frequency(self): def get_frequency(self):
""" """ """ """
return int(self.my_rig.get_freq()) return int(self.my_rig.get_freq())
def get_mode(self): def get_mode(self):
""" """ """ """
(hamlib_mode, bandwith) = self.my_rig.get_mode() (hamlib_mode, bandwith) = self.my_rig.get_mode()
return Hamlib.rig_strrmode(hamlib_mode) return Hamlib.rig_strrmode(hamlib_mode)
def get_bandwith(self): def get_bandwith(self):
""" """ """ """
(hamlib_mode, bandwith) = self.my_rig.get_mode() (hamlib_mode, bandwith) = self.my_rig.get_mode()
@ -230,16 +230,16 @@ class radio:
# not needed yet beacuse of some possible problems # not needed yet beacuse of some possible problems
#def set_mode(self, mode): #def set_mode(self, mode):
# return 0 # return 0
def get_ptt(self): def get_ptt(self):
""" """ """ """
return self.my_rig.get_ptt() return self.my_rig.get_ptt()
def set_ptt(self, state): def set_ptt(self, state):
""" """
Args: Args:
state: state:
Returns: Returns:
@ -249,7 +249,7 @@ class radio:
else: else:
self.my_rig.set_ptt(Hamlib.RIG_VFO_CURR, 0) self.my_rig.set_ptt(Hamlib.RIG_VFO_CURR, 0)
return state return state
def close_rig(self): def close_rig(self):
""" """ """ """
self.my_rig.close() self.my_rig.close()

View file

@ -17,11 +17,11 @@ import os
# set global hamlib version # set global hamlib version
hamlib_version = 0 hamlib_version = 0
class radio: class radio:
""" """ """ """
def __init__(self): def __init__(self):
self.devicename = '' self.devicename = ''
self.devicenumber = '' self.devicenumber = ''
self.deviceport = '' self.deviceport = ''
@ -32,26 +32,26 @@ class radio:
self.data_bits = '' self.data_bits = ''
self.stop_bits = '' self.stop_bits = ''
self.handshake = '' 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: Args:
devicename: devicename:
deviceport: deviceport:
hamlib_ptt_type: hamlib_ptt_type:
serialspeed: serialspeed:
pttport: pttport:
data_bits: data_bits:
stop_bits: stop_bits:
handshake: handshake:
rigctld_ip: rigctld_ip:
rigctld_port: rigctld_port:
Returns: Returns:
""" """
self.devicename = devicename self.devicename = devicename
self.deviceport = deviceport self.deviceport = deviceport
self.serialspeed = str(serialspeed) # we need to ensure this is a str, otherwise set_conf functions are crashing 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.data_bits = data_bits
self.stop_bits = stop_bits self.stop_bits = stop_bits
self.handshake = handshake self.handshake = handshake
# check if we are running in a pyinstaller environment # check if we are running in a pyinstaller environment
try: try:
app_path = sys._MEIPASS app_path = sys._MEIPASS
except: except:
app_path = os.path.abspath(".") app_path = os.path.abspath(".")
sys.path.append(app_path) sys.path.append(app_path)
# get devicenumber by looking for deviceobject in Hamlib module # get devicenumber by looking for deviceobject in Hamlib module
try: try:
import Hamlib import Hamlib
@ -78,12 +78,12 @@ class radio:
self.devicenumber = int(self.devicename) self.devicenumber = int(self.devicename)
else: else:
self.devicenumber = 6 #dummy 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 # set deviceport to dummy port, if we selected dummy model
if self.devicenumber == 1 or self.devicenumber == 6: if self.devicenumber == 1 or self.devicenumber == 6:
self.deviceport = '/dev/ttyUSB0' self.deviceport = '/dev/ttyUSB0'
print(self.devicenumber, self.deviceport, self.serialspeed) 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 # 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 == '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)) self.cmd = app_path + 'lib\\hamlib\\'+sys.platform+'\\rigctl -m %d -r %s -s %d ' % (int(self.devicenumber), self.deviceport, int(self.serialspeed))
else: else:
self.cmd = 'rigctl -m %d -r %s -s %d ' % (int(self.devicenumber), self.deviceport, int(self.serialspeed)) 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 # 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 # set ptt to false if ptt is stuck for some reason
self.set_ptt(False) self.set_ptt(False)
return True return True
def get_frequency(self): def get_frequency(self):
""" """ """ """
cmd = self.cmd + ' f' cmd = self.cmd + ' f'
@ -116,7 +116,7 @@ class radio:
except: except:
return False return False
def get_mode(self): def get_mode(self):
""" """ """ """
#(hamlib_mode, bandwith) = self.my_rig.get_mode() #(hamlib_mode, bandwith) = self.my_rig.get_mode()
@ -126,7 +126,7 @@ class radio:
except: except:
return False return False
def get_bandwith(self): def get_bandwith(self):
""" """ """ """
#(hamlib_mode, bandwith) = self.my_rig.get_mode() #(hamlib_mode, bandwith) = self.my_rig.get_mode()
@ -141,18 +141,18 @@ class radio:
""" """
Args: Args:
mode: mode:
Returns: Returns:
""" """
# non usata # non usata
return 0 return 0
def get_ptt(self): def get_ptt(self):
""" """ """ """
cmd = self.cmd + ' t' 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) time.sleep(0.5)
status = sw_proc.communicate()[0] status = sw_proc.communicate()[0]
@ -160,12 +160,12 @@ class radio:
return status return status
except: except:
return False return False
def set_ptt(self, state): def set_ptt(self, state):
""" """
Args: Args:
state: state:
Returns: Returns:
@ -178,12 +178,12 @@ class radio:
cmd = cmd + '0' cmd = cmd + '0'
print('set_ptt', cmd) print('set_ptt', cmd)
sw_proc = subprocess.Popen(cmd, shell=True, text=True) sw_proc = subprocess.Popen(cmd, shell=True, text=True)
try: try:
return state return state
except: except:
return False return False
def close_rig(self): def close_rig(self):
""" """ """ """
#self.my_rig.close() #self.my_rig.close()

View file

@ -15,7 +15,7 @@ hamlib_version = 0
class radio(): class radio():
"""rotctld (hamlib) communication class""" """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): def __init__(self, hostname="localhost", port=4532, poll_rate=5, timeout=5):
""" Open a connection to rotctld, and test it for validity """ """ Open a connection to rotctld, and test it for validity """
@ -31,31 +31,31 @@ class radio():
""" """
Args: Args:
devicename: devicename:
deviceport: deviceport:
hamlib_ptt_type: hamlib_ptt_type:
serialspeed: serialspeed:
pttport: pttport:
data_bits: data_bits:
stop_bits: stop_bits:
handshake: handshake:
rigctld_ip: rigctld_ip:
rigctld_port: rigctld_port:
Returns: Returns:
""" """
self.hostname = rigctld_ip self.hostname = rigctld_ip
self.port = int(rigctld_port) self.port = int(rigctld_port)
if self.connect(): if self.connect():
logging.debug(f"Rigctl intialized") logging.debug(f"Rigctl intialized")
return True return True
else: else:
structlog.get_logger("structlog").error("[RIGCTLD] Can't connect to rigctld!", ip=self.hostname, port=self.port) structlog.get_logger("structlog").error("[RIGCTLD] Can't connect to rigctld!", ip=self.hostname, port=self.port)
return False return False
def connect(self): def connect(self):
"""Connect to rigctld instance""" """Connect to rigctld instance"""
if not self.connected: if not self.connected:
@ -69,7 +69,7 @@ class radio():
self.close_rig() self.close_rig()
structlog.get_logger("structlog").warning("[RIGCTLD] Connection to rigctld refused! Reconnect...", ip=self.hostname, port=self.port, e=e) structlog.get_logger("structlog").warning("[RIGCTLD] Connection to rigctld refused! Reconnect...", ip=self.hostname, port=self.port, e=e)
return False return False
def close_rig(self): def close_rig(self):
""" """ """ """
self.sock.close() self.sock.close()
@ -81,7 +81,7 @@ class radio():
and return the return value. and return the return value.
Args: Args:
command: command:
Returns: Returns:
@ -99,7 +99,7 @@ class radio():
structlog.get_logger("structlog").warning("[RIGCTLD] No command response!", command=command, ip=self.hostname, port=self.port) structlog.get_logger("structlog").warning("[RIGCTLD] No command response!", command=command, ip=self.hostname, port=self.port)
self.connected = False self.connected = False
else: else:
# reconnecting.... # reconnecting....
time.sleep(0.5) time.sleep(0.5)
self.connect() self.connect()
@ -111,7 +111,7 @@ class radio():
data = self.send_command(b"m") data = self.send_command(b"m")
data = data.split(b'\n') data = data.split(b'\n')
mode = data[0] mode = data[0]
return mode.decode("utf-8") return mode.decode("utf-8")
except: except:
0 0
def get_bandwith(self): def get_bandwith(self):
@ -123,7 +123,7 @@ class radio():
return bandwith.decode("utf-8") return bandwith.decode("utf-8")
except: except:
return 0 return 0
def get_frequency(self): def get_frequency(self):
""" """ """ """
try: try:
@ -131,19 +131,19 @@ class radio():
return frequency.decode("utf-8") return frequency.decode("utf-8")
except: except:
return 0 return 0
def get_ptt(self): def get_ptt(self):
""" """ """ """
try: try:
return self.send_command(b"t") return self.send_command(b"t")
except: except:
return False return False
def set_ptt(self, state): def set_ptt(self, state):
""" """
Args: Args:
state: state:
Returns: Returns:
@ -153,6 +153,6 @@ class radio():
self.send_command(b"T 1") self.send_command(b"T 1")
else: else:
self.send_command(b"T 0") self.send_command(b"T 0")
return state return state
except: except:
return False return False

View file

@ -3,32 +3,32 @@
import structlog import structlog
hamlib_version = 0 hamlib_version = 0
class radio: class radio:
""" """ """ """
def __init__(self): def __init__(self):
pass pass
def open_rig(self, **kwargs): def open_rig(self, **kwargs):
""" """
Args: Args:
**kwargs: **kwargs:
Returns: Returns:
""" """
return True return True
def get_frequency(self): def get_frequency(self):
""" """ """ """
return None return None
def get_mode(self): def get_mode(self):
""" """ """ """
return None return None
def get_bandwith(self): def get_bandwith(self):
""" """ """ """
return None return None
@ -37,28 +37,28 @@ class radio:
""" """
Args: Args:
mode: mode:
Returns: Returns:
""" """
return None return None
def get_ptt(self): def get_ptt(self):
""" """ """ """
return None return None
def set_ptt(self, state): def set_ptt(self, state):
""" """
Args: Args:
state: state:
Returns: Returns:
""" """
return state return state
def close_rig(self): def close_rig(self):
""" """ """ """
return return

View file

@ -7,11 +7,11 @@ Created on Fri Dec 25 21:25:14 2020
# GET COMMANDS # GET COMMANDS
# "command" : "..." # "command" : "..."
# SET COMMANDS # SET COMMANDS
# "command" : "..." # "command" : "..."
# "parameter" : " ..." # "parameter" : " ..."
# DATA COMMANDS # DATA COMMANDS
# "command" : "..." # "command" : "..."
# "type" : "..." # "type" : "..."
@ -51,25 +51,25 @@ class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
the socket handler base class the socket handler base class
""" """
pass pass
class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
""" """ """ """
def send_to_client(self): def send_to_client(self):
""" """
function called by socket handler function called by socket handler
send data to a network client if available send data to a network client if available
""" """
tempdata = b'' tempdata = b''
while self.connection_alive and not CLOSE_SIGNAL: while self.connection_alive and not CLOSE_SIGNAL:
# send tnc state as network stream # send tnc state as network stream
# check server port against daemon port and send corresponding data # check server port against daemon port and send corresponding data
if self.server.server_address[1] == static.PORT and not static.TNCSTARTED: if self.server.server_address[1] == static.PORT and not static.TNCSTARTED:
data = send_tnc_state() data = send_tnc_state()
if data != tempdata: if data != tempdata:
@ -81,13 +81,13 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
tempdata = data tempdata = data
SOCKET_QUEUE.put(data) SOCKET_QUEUE.put(data)
time.sleep(0.5) time.sleep(0.5)
while not SOCKET_QUEUE.empty(): while not SOCKET_QUEUE.empty():
data = SOCKET_QUEUE.get() data = SOCKET_QUEUE.get()
sock_data = bytes(data, 'utf-8') sock_data = bytes(data, 'utf-8')
sock_data += b'\n' # append line limiter sock_data += b'\n' # append line limiter
# send data to all clients # send data to all clients
#try: #try:
for client in CONNECTED_CLIENTS: 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 # we want to transmit scatter data only once to reduce network traffic
static.SCATTER = [] static.SCATTER = []
# we want to display INFO messages only once # we want to display INFO messages only once
static.INFO = [] static.INFO = []
#self.request.sendall(sock_data) #self.request.sendall(sock_data)
time.sleep(0.15) time.sleep(0.15)
@ -115,18 +115,18 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
try: try:
chunk = self.request.recv(1024) chunk = self.request.recv(1024)
data += chunk data += chunk
if chunk == b'': if chunk == b'':
#print("connection broken. Closing...") #print("connection broken. Closing...")
self.connection_alive = False 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 # split data by \n if we have multiple commands in socket buffer
data = data.split(b'\n') data = data.split(b'\n')
# remove empty data # remove empty data
data.remove(b'') data.remove(b'')
# iterate thorugh data list # iterate thorugh data list
for commands in data: for commands in data:
if self.server.server_address[1] == static.PORT: if self.server.server_address[1] == static.PORT:
@ -136,19 +136,19 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
# wait some time between processing multiple commands # wait some time between processing multiple commands
# this is only a first test to avoid doubled transmission # 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 # doing some kind of selection to determin which commands need to be dropped
# and which one can be processed during a running transmission # and which one can be processed during a running transmission
time.sleep(3) time.sleep(3)
# finally delete our rx buffer to be ready for new commands # finally delete our rx buffer to be ready for new commands
data = bytes() data = bytes()
except Exception as e: except Exception as e:
structlog.get_logger("structlog").info("[SCK] Connection closed", ip=self.client_address[0], port=self.client_address[1], e=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 self.connection_alive = False
def handle(self): def handle(self):
""" """
socket handler 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]) structlog.get_logger("structlog").debug("[SCK] Client connected", ip=self.client_address[0], port=self.client_address[1])
self.connection_alive = True self.connection_alive = True
self.sendThread = threading.Thread(target=self.send_to_client, args=[],daemon=True).start() 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() self.receiveThread = threading.Thread(target=self.receive_from_client, args=[],daemon=True).start()
# keep connection alive until we close it # keep connection alive until we close it
while self.connection_alive and not CLOSE_SIGNAL: while self.connection_alive and not CLOSE_SIGNAL:
time.sleep(1) time.sleep(1)
def finish(self): 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: try:
CONNECTED_CLIENTS.remove(self.request) CONNECTED_CLIENTS.remove(self.request)
except: except:
@ -182,7 +182,7 @@ def process_tnc_commands(data):
process tnc commands process tnc commands
Args: Args:
data: data:
Returns: Returns:
@ -198,9 +198,9 @@ def process_tnc_commands(data):
try: try:
static.TX_AUDIO_LEVEL = int(received_json["value"]) static.TX_AUDIO_LEVEL = int(received_json["value"])
command_response("tx_audio_level", True) command_response("tx_audio_level", True)
except Exception as e: except Exception as e:
command_response("tx_audio_level", False) command_response("tx_audio_level", 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)
@ -209,8 +209,8 @@ def process_tnc_commands(data):
try: try:
data_handler.DATA_QUEUE_TRANSMIT.put(['SEND_TEST_FRAME']) data_handler.DATA_QUEUE_TRANSMIT.put(['SEND_TEST_FRAME'])
command_response("send_test_frame", True) command_response("send_test_frame", True)
except Exception as e: except Exception as e:
command_response("send_test_frame", False) command_response("send_test_frame", 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)
# CQ CQ CQ ----------------------------------------------------- # CQ CQ CQ -----------------------------------------------------
@ -218,9 +218,9 @@ def process_tnc_commands(data):
try: try:
data_handler.DATA_QUEUE_TRANSMIT.put(['CQ']) data_handler.DATA_QUEUE_TRANSMIT.put(['CQ'])
command_response("cqcqcq", True) command_response("cqcqcq", True)
except Exception as e: except Exception as e:
command_response("cqcqcq", False) command_response("cqcqcq", 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)
# START_BEACON ----------------------------------------------------- # START_BEACON -----------------------------------------------------
if received_json["command"] == "start_beacon": if received_json["command"] == "start_beacon":
@ -229,21 +229,21 @@ def process_tnc_commands(data):
interval = int(received_json["parameter"]) interval = int(received_json["parameter"])
data_handler.DATA_QUEUE_TRANSMIT.put(['BEACON', interval, True]) data_handler.DATA_QUEUE_TRANSMIT.put(['BEACON', interval, True])
command_response("start_beacon", True) command_response("start_beacon", True)
except Exception as e: except Exception as e:
command_response("start_beacon", False) command_response("start_beacon", 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)
# STOP_BEACON ----------------------------------------------------- # STOP_BEACON -----------------------------------------------------
if received_json["command"] == "stop_beacon": if received_json["command"] == "stop_beacon":
try: try:
structlog.get_logger("structlog").warning("[TNC] Stopping beacon!") structlog.get_logger("structlog").warning("[TNC] Stopping beacon!")
static.BEACON_STATE = False static.BEACON_STATE = False
data_handler.DATA_QUEUE_TRANSMIT.put(['BEACON', None, False]) data_handler.DATA_QUEUE_TRANSMIT.put(['BEACON', None, False])
command_response("stop_beacon", True) command_response("stop_beacon", True)
except Exception as e: except Exception as e:
command_response("stop_beacon", False) command_response("stop_beacon", 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)
# PING ---------------------------------------------------------- # PING ----------------------------------------------------------
if received_json["type"] == 'ping' and received_json["command"] == "ping": if received_json["type"] == 'ping' and received_json["command"] == "ping":
# send ping frame and wait for ACK # send ping frame and wait for ACK
@ -255,14 +255,14 @@ def process_tnc_commands(data):
# then we are forcing a station ssid = 0 # then we are forcing a station ssid = 0
dxcallsign = helpers.callsign_to_bytes(dxcallsign) dxcallsign = helpers.callsign_to_bytes(dxcallsign)
dxcallsign = helpers.bytes_to_callsign(dxcallsign) dxcallsign = helpers.bytes_to_callsign(dxcallsign)
data_handler.DATA_QUEUE_TRANSMIT.put(['PING', dxcallsign]) data_handler.DATA_QUEUE_TRANSMIT.put(['PING', dxcallsign])
command_response("ping", True) command_response("ping", True)
except Exception as e: except Exception as e:
command_response("ping", False) command_response("ping", 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)
# CONNECT ---------------------------------------------------------- # CONNECT ----------------------------------------------------------
if received_json["type"] == 'arq' and received_json["command"] == "connect": if received_json["type"] == 'arq' and received_json["command"] == "connect":
static.BEACON_PAUSE = True static.BEACON_PAUSE = True
@ -275,14 +275,14 @@ def process_tnc_commands(data):
# then we are forcing a station ssid = 0 # then we are forcing a station ssid = 0
dxcallsign = helpers.callsign_to_bytes(dxcallsign) dxcallsign = helpers.callsign_to_bytes(dxcallsign)
dxcallsign = helpers.bytes_to_callsign(dxcallsign) dxcallsign = helpers.bytes_to_callsign(dxcallsign)
static.DXCALLSIGN = dxcallsign static.DXCALLSIGN = dxcallsign
static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN) static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN)
data_handler.DATA_QUEUE_TRANSMIT.put(['CONNECT', dxcallsign]) data_handler.DATA_QUEUE_TRANSMIT.put(['CONNECT', dxcallsign])
command_response("connect", True) command_response("connect", True)
except Exception as e: except Exception as e:
command_response("connect", False) command_response("connect", 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)
# DISCONNECT ---------------------------------------------------------- # DISCONNECT ----------------------------------------------------------
@ -291,15 +291,15 @@ def process_tnc_commands(data):
try: try:
data_handler.DATA_QUEUE_TRANSMIT.put(['DISCONNECT']) data_handler.DATA_QUEUE_TRANSMIT.put(['DISCONNECT'])
command_response("disconnect", True) command_response("disconnect", True)
except Exception as e: except Exception as e:
command_response("disconnect", False) 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 ------------------------------------------- # TRANSMIT RAW DATA -------------------------------------------
if received_json["type"] == 'arq' and received_json["command"] == "send_raw": if received_json["type"] == 'arq' and received_json["command"] == "send_raw":
static.BEACON_PAUSE = True static.BEACON_PAUSE = True
try: try:
if not static.ARQ_SESSION: if not static.ARQ_SESSION:
dxcallsign = received_json["parameter"][0]["dxcallsign"] dxcallsign = received_json["parameter"][0]["dxcallsign"]
# additional step for beeing sure our callsign is correctly # additional step for beeing sure our callsign is correctly
@ -314,37 +314,37 @@ def process_tnc_commands(data):
dxcallsign = static.DXCALLSIGN dxcallsign = static.DXCALLSIGN
static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN) static.DXCALLSIGN_CRC = helpers.get_crc_24(static.DXCALLSIGN)
mode = int(received_json["parameter"][0]["mode"]) mode = int(received_json["parameter"][0]["mode"])
n_frames = int(received_json["parameter"][0]["n_frames"]) n_frames = int(received_json["parameter"][0]["n_frames"])
base64data = received_json["parameter"][0]["data"] base64data = received_json["parameter"][0]["data"]
# check if specific callsign is set with different SSID than the TNC is initialized # check if specific callsign is set with different SSID than the TNC is initialized
try: try:
mycallsign = received_json["parameter"][0]["mycallsign"] mycallsign = received_json["parameter"][0]["mycallsign"]
except: except:
mycallsign = static.MYCALLSIGN mycallsign = static.MYCALLSIGN
# check if transmission uuid provided else set no-uuid # check if transmission uuid provided else set no-uuid
try: try:
arq_uuid = received_json["uuid"] arq_uuid = received_json["uuid"]
except: except:
arq_uuid = 'no-uuid' arq_uuid = 'no-uuid'
if not len(base64data) % 4: if not len(base64data) % 4:
binarydata = base64.b64decode(base64data) binarydata = base64.b64decode(base64data)
data_handler.DATA_QUEUE_TRANSMIT.put(['ARQ_RAW', binarydata, mode, n_frames, arq_uuid, mycallsign]) data_handler.DATA_QUEUE_TRANSMIT.put(['ARQ_RAW', binarydata, mode, n_frames, arq_uuid, mycallsign])
else: else:
raise TypeError raise TypeError
except Exception as e: except Exception as e:
command_response("send_raw", False) command_response("send_raw", 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)
# STOP TRANSMISSION ----------------------------------------------------------
# STOP TRANSMISSION ----------------------------------------------------------
if received_json["type"] == 'arq' and received_json["command"] == "stop_transmission": if received_json["type"] == 'arq' and received_json["command"] == "stop_transmission":
try: try:
if static.TNC_STATE == 'BUSY' or static.ARQ_STATE: if static.TNC_STATE == 'BUSY' or static.ARQ_STATE:
@ -353,10 +353,10 @@ def process_tnc_commands(data):
static.TNC_STATE = 'IDLE' static.TNC_STATE = 'IDLE'
static.ARQ_STATE = False static.ARQ_STATE = False
command_response("stop_transmission", True) command_response("stop_transmission", True)
except Exception as e: except Exception as e:
command_response("stop_transmission", False) command_response("stop_transmission", 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)
if received_json["type"] == 'get' and received_json["command"] == 'rx_buffer': if received_json["type"] == 'get' and received_json["command"] == 'rx_buffer':
try: try:
@ -364,7 +364,7 @@ def process_tnc_commands(data):
"command": "rx_buffer", "command": "rx_buffer",
"data-array": [], "data-array": [],
} }
for i in range(0, len(static.RX_BUFFER)): for i in range(0, len(static.RX_BUFFER)):
#print(static.RX_BUFFER[i][4]) #print(static.RX_BUFFER[i][4])
#rawdata = json.loads(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)) #self.request.sendall(bytes(jsondata, encoding))
SOCKET_QUEUE.put(jsondata) SOCKET_QUEUE.put(jsondata)
command_response("rx_buffer", True) command_response("rx_buffer", True)
except Exception as e: except Exception as e:
command_response("rx_buffer", False) command_response("rx_buffer", 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)
if received_json["type"] == 'set' and received_json["command"] == 'del_rx_buffer': if received_json["type"] == 'set' and received_json["command"] == 'del_rx_buffer':
try: try:
static.RX_BUFFER = [] static.RX_BUFFER = []
command_response("del_rx_buffer", True) command_response("del_rx_buffer", True)
except Exception as e: except Exception as e:
command_response("del_rx_buffer", False) command_response("del_rx_buffer", 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)
# exception, if JSON cant be decoded # exception, if JSON cant be decoded
@ -397,7 +397,7 @@ def send_tnc_state():
""" """
send the tnc state to network send the tnc state to network
""" """
encoding = 'utf-8' encoding = 'utf-8'
output = { output = {
@ -410,7 +410,7 @@ def send_tnc_state():
"audio_rms": str(static.AUDIO_RMS), "audio_rms": str(static.AUDIO_RMS),
"snr": str(static.SNR), "snr": str(static.SNR),
"frequency": str(static.HAMLIB_FREQUENCY), "frequency": str(static.HAMLIB_FREQUENCY),
"speed_level": str(static.ARQ_SPEED_LEVEL), "speed_level": str(static.ARQ_SPEED_LEVEL),
"mode": str(static.HAMLIB_MODE), "mode": str(static.HAMLIB_MODE),
"bandwith": str(static.HAMLIB_BANDWITH), "bandwith": str(static.HAMLIB_BANDWITH),
"fft": str(static.FFT), "fft": str(static.FFT),
@ -434,7 +434,7 @@ def send_tnc_state():
# add heard stations to heard stations object # add heard stations to heard stations object
for i in range(0, len(static.HEARD_STATIONS)): 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]}) 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) jsondata = json.dumps(output)
return jsondata return jsondata
@ -444,7 +444,7 @@ def process_daemon_commands(data):
process daemon commands process daemon commands
Args: Args:
data: data:
Returns: Returns:
@ -465,11 +465,11 @@ def process_daemon_commands(data):
command_response("mycallsign", True) command_response("mycallsign", True)
structlog.get_logger("structlog").info("[DMN] SET MYCALL", call=static.MYCALLSIGN, crc=static.MYCALLSIGN_CRC) 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) command_response("mycallsign", 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)
if received_json["type"] == 'set' and received_json["command"] == 'mygrid': if received_json["type"] == 'set' and received_json["command"] == 'mygrid':
try: try:
mygrid = received_json["parameter"] mygrid = received_json["parameter"]
@ -480,13 +480,13 @@ def process_daemon_commands(data):
static.MYGRID = bytes(mygrid, 'utf-8') static.MYGRID = bytes(mygrid, 'utf-8')
structlog.get_logger("structlog").info("[SCK] SET MYGRID", grid=static.MYGRID) structlog.get_logger("structlog").info("[SCK] SET MYGRID", grid=static.MYGRID)
command_response("mygrid", True) command_response("mygrid", True)
except Exception as e: except Exception as e:
command_response("mygrid", False) command_response("mygrid", 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)
if received_json["type"] == 'set' and received_json["command"] == 'start_tnc' and not static.TNCSTARTED: if received_json["type"] == 'set' and received_json["command"] == 'start_tnc' and not static.TNCSTARTED:
try: try:
mycall = str(received_json["parameter"][0]["mycall"]) mycall = str(received_json["parameter"][0]["mycall"])
mygrid = str(received_json["parameter"][0]["mygrid"]) mygrid = str(received_json["parameter"][0]["mygrid"])
@ -543,9 +543,9 @@ def process_daemon_commands(data):
respond_to_cq \ respond_to_cq \
]) ])
command_response("start_tnc", True) command_response("start_tnc", True)
except Exception as e: except Exception as e:
command_response("start_tnc", False) command_response("start_tnc", 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)
if received_json["type"] == 'get' and received_json["command"] == 'test_hamlib': if received_json["type"] == 'get' and received_json["command"] == 'test_hamlib':
@ -577,23 +577,23 @@ def process_daemon_commands(data):
rigctld_port \ rigctld_port \
]) ])
command_response("test_hamlib", True) command_response("test_hamlib", True)
except Exception as e: except Exception as e:
command_response("test_hamlib", False) command_response("test_hamlib", 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)
if received_json["type"] == 'set' and received_json["command"] == 'stop_tnc': if received_json["type"] == 'set' and received_json["command"] == 'stop_tnc':
try: try:
static.TNCPROCESS.kill() static.TNCPROCESS.kill()
# unregister process from atexit to avoid process zombies # unregister process from atexit to avoid process zombies
atexit.unregister(static.TNCPROCESS.kill) atexit.unregister(static.TNCPROCESS.kill)
structlog.get_logger("structlog").warning("[DMN] Stopping TNC") structlog.get_logger("structlog").warning("[DMN] Stopping TNC")
static.TNCSTARTED = False static.TNCSTARTED = False
command_response("stop_tnc", True) command_response("stop_tnc", True)
except Exception as e: except Exception as e:
command_response("stop_tnc", False) command_response("stop_tnc", 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)
def send_daemon_state(): def send_daemon_state():
""" """
send the daemon state to network send the daemon state to network
@ -613,26 +613,26 @@ def send_daemon_state():
#'ram': str(psutil.virtual_memory().percent), #'ram': str(psutil.virtual_memory().percent),
'version': '0.1' 'version': '0.1'
} }
if static.TNCSTARTED: if static.TNCSTARTED:
output["daemon_state"].append({"status": "running"}) output["daemon_state"].append({"status": "running"})
else: else:
output["daemon_state"].append({"status": "stopped"}) output["daemon_state"].append({"status": "stopped"})
jsondata = json.dumps(output) jsondata = json.dumps(output)
return jsondata return jsondata
except Exception as e: except Exception as e:
structlog.get_logger("structlog").warning("[SCK] error", e=e) structlog.get_logger("structlog").warning("[SCK] error", e=e)
return None return None
def command_response(command, status): def command_response(command, status):
if status: if status:
status = "OK" status = "OK"
else: else:
status = "Failed" status = "Failed"
jsondata = {"command_response": command, "status" : status} jsondata = {"command_response": command, "status" : status}
data_out = json.dumps(jsondata) data_out = json.dumps(jsondata)
SOCKET_QUEUE.put(data_out) SOCKET_QUEUE.put(data_out)

View file

@ -5,7 +5,7 @@ Created on Wed Dec 23 11:13:57 2020
@author: DJ2LS @author: DJ2LS
Here we are saving application wide variables and stats, which have to be accessed everywhere. 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' VERSION = '0.4.0-alpha'
@ -55,7 +55,7 @@ HAMLIB_HANDSHAKE = 'None'
HAMLIB_RADIOCONTROL = 'direct' HAMLIB_RADIOCONTROL = 'direct'
HAMLIB_RIGCTLD_IP = '127.0.0.1' HAMLIB_RIGCTLD_IP = '127.0.0.1'
HAMLIB_RIGCTLD_PORT = '4532' HAMLIB_RIGCTLD_PORT = '4532'
HAMLIB_FREQUENCY = 0 HAMLIB_FREQUENCY = 0
HAMLIB_MODE = '' HAMLIB_MODE = ''
HAMLIB_BANDWITH = 0 HAMLIB_BANDWITH = 0
@ -122,4 +122,4 @@ INFO = []
# ------- CODEC2 SETTINGS # ------- CODEC2 SETTINGS
TUNING_RANGE_FMIN = -50.0 TUNING_RANGE_FMIN = -50.0
TUNING_RANGE_FMAX = 50.0 TUNING_RANGE_FMAX = 50.0