mirror of https://github.com/DJ2LS/FreeDATA
Merge pull request #386 from DJ2LS/tci
This commit is contained in:
commit
30073e7077
117
tnc/modem.py
117
tnc/modem.py
|
@ -26,7 +26,8 @@ import static
|
||||||
import structlog
|
import structlog
|
||||||
import ujson as json
|
import ujson as json
|
||||||
import tci
|
import tci
|
||||||
from queues import DATA_QUEUE_RECEIVED, MODEM_RECEIVED_QUEUE, MODEM_TRANSMIT_QUEUE, RIGCTLD_COMMAND_QUEUE, AUDIO_RECEIVED_QUEUE, AUDIO_TRANSMIT_QUEUE
|
from queues import DATA_QUEUE_RECEIVED, MODEM_RECEIVED_QUEUE, MODEM_TRANSMIT_QUEUE, RIGCTLD_COMMAND_QUEUE, \
|
||||||
|
AUDIO_RECEIVED_QUEUE, AUDIO_TRANSMIT_QUEUE
|
||||||
|
|
||||||
TESTMODE = False
|
TESTMODE = False
|
||||||
RXCHANNEL = ""
|
RXCHANNEL = ""
|
||||||
|
@ -40,7 +41,6 @@ RECEIVE_SIG1 = False
|
||||||
RECEIVE_DATAC1 = False
|
RECEIVE_DATAC1 = False
|
||||||
RECEIVE_DATAC3 = False
|
RECEIVE_DATAC3 = False
|
||||||
|
|
||||||
|
|
||||||
# state buffer
|
# state buffer
|
||||||
SIG0_DATAC0_STATE = []
|
SIG0_DATAC0_STATE = []
|
||||||
SIG1_DATAC0_STATE = []
|
SIG1_DATAC0_STATE = []
|
||||||
|
@ -49,6 +49,7 @@ DAT0_DATAC3_STATE = []
|
||||||
FSK_LDPC0_STATE = []
|
FSK_LDPC0_STATE = []
|
||||||
FSK_LDPC1_STATE = []
|
FSK_LDPC1_STATE = []
|
||||||
|
|
||||||
|
|
||||||
class RF:
|
class RF:
|
||||||
"""Class to encapsulate interactions between the audio device and codec2"""
|
"""Class to encapsulate interactions between the audio device and codec2"""
|
||||||
|
|
||||||
|
@ -62,10 +63,10 @@ class RF:
|
||||||
self.AUDIO_SAMPLE_RATE_RX = 48000
|
self.AUDIO_SAMPLE_RATE_RX = 48000
|
||||||
self.AUDIO_SAMPLE_RATE_TX = 48000
|
self.AUDIO_SAMPLE_RATE_TX = 48000
|
||||||
self.MODEM_SAMPLE_RATE = codec2.api.FREEDV_FS_8000
|
self.MODEM_SAMPLE_RATE = codec2.api.FREEDV_FS_8000
|
||||||
|
|
||||||
self.AUDIO_FRAMES_PER_BUFFER_RX = 2400 * 2 # 8192
|
self.AUDIO_FRAMES_PER_BUFFER_RX = 2400 * 2 # 8192
|
||||||
# 8192 Let's do some tests with very small chunks for TX
|
# 8192 Let's do some tests with very small chunks for TX
|
||||||
self.AUDIO_FRAMES_PER_BUFFER_TX = 2400 * 2
|
self.AUDIO_FRAMES_PER_BUFFER_TX = 1200 if static.AUDIO_ENABLE_TCI else 2400 * 2
|
||||||
|
|
||||||
# 8 * (self.AUDIO_SAMPLE_RATE_RX/self.MODEM_SAMPLE_RATE) == 48
|
# 8 * (self.AUDIO_SAMPLE_RATE_RX/self.MODEM_SAMPLE_RATE) == 48
|
||||||
self.AUDIO_CHANNELS = 1
|
self.AUDIO_CHANNELS = 1
|
||||||
self.MODE = 0
|
self.MODE = 0
|
||||||
|
@ -89,7 +90,6 @@ class RF:
|
||||||
self.audio_received_queue = AUDIO_RECEIVED_QUEUE
|
self.audio_received_queue = AUDIO_RECEIVED_QUEUE
|
||||||
self.audio_transmit_queue = AUDIO_TRANSMIT_QUEUE
|
self.audio_transmit_queue = AUDIO_TRANSMIT_QUEUE
|
||||||
|
|
||||||
|
|
||||||
# Init FIFO queue to store modulation out in
|
# Init FIFO queue to store modulation out in
|
||||||
self.modoutqueue = deque()
|
self.modoutqueue = deque()
|
||||||
|
|
||||||
|
@ -188,10 +188,12 @@ class RF:
|
||||||
# placeholder area for processing audio via TCI
|
# placeholder area for processing audio via TCI
|
||||||
# https://github.com/maksimus1210/TCI
|
# https://github.com/maksimus1210/TCI
|
||||||
self.log.warning("[MDM] [TCI] Not yet fully implemented", ip=static.TCI_IP, port=static.TCI_PORT)
|
self.log.warning("[MDM] [TCI] Not yet fully implemented", ip=static.TCI_IP, port=static.TCI_PORT)
|
||||||
|
|
||||||
# we are trying this by simulating an audio stream Object like with mkfifo
|
# we are trying this by simulating an audio stream Object like with mkfifo
|
||||||
class Object:
|
class Object:
|
||||||
"""An object for simulating audio stream"""
|
"""An object for simulating audio stream"""
|
||||||
active = True
|
active = True
|
||||||
|
|
||||||
self.stream = Object()
|
self.stream = Object()
|
||||||
|
|
||||||
# lets init TCI module
|
# lets init TCI module
|
||||||
|
@ -254,14 +256,17 @@ class RF:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
elif static.HAMLIB_RADIOCONTROL == "rigctld":
|
elif static.HAMLIB_RADIOCONTROL == "rigctld":
|
||||||
import rigctld as rig
|
import rigctld as rig
|
||||||
|
elif static.AUDIO_ENABLE_TCI:
|
||||||
|
self.radio = self.tci_module
|
||||||
else:
|
else:
|
||||||
import rigdummy as rig
|
import rigdummy as rig
|
||||||
|
|
||||||
self.hamlib = rig.radio()
|
if not static.AUDIO_ENABLE_TCI:
|
||||||
self.hamlib.open_rig(
|
self.radio = rig.radio()
|
||||||
rigctld_ip=static.HAMLIB_RIGCTLD_IP,
|
self.radio.open_rig(
|
||||||
rigctld_port=static.HAMLIB_RIGCTLD_PORT,
|
rigctld_ip=static.HAMLIB_RIGCTLD_IP,
|
||||||
)
|
rigctld_port=static.HAMLIB_RIGCTLD_PORT,
|
||||||
|
)
|
||||||
|
|
||||||
# --------------------------------------------START DECODER THREAD
|
# --------------------------------------------START DECODER THREAD
|
||||||
if static.ENABLE_FFT:
|
if static.ENABLE_FFT:
|
||||||
|
@ -302,7 +307,6 @@ class RF:
|
||||||
)
|
)
|
||||||
audio_thread_dat0_datac3.start()
|
audio_thread_dat0_datac3.start()
|
||||||
|
|
||||||
|
|
||||||
hamlib_thread = threading.Thread(
|
hamlib_thread = threading.Thread(
|
||||||
target=self.update_rig_data, name="HAMLIB_THREAD", daemon=True
|
target=self.update_rig_data, name="HAMLIB_THREAD", daemon=True
|
||||||
)
|
)
|
||||||
|
@ -332,11 +336,14 @@ class RF:
|
||||||
while True:
|
while True:
|
||||||
threading.Event().wait(0.01)
|
threading.Event().wait(0.01)
|
||||||
|
|
||||||
|
|
||||||
# -----write
|
|
||||||
if len(self.modoutqueue) > 0 and not self.mod_out_locked:
|
if len(self.modoutqueue) > 0 and not self.mod_out_locked:
|
||||||
data_out48k = self.modoutqueue.popleft()
|
static.PTT_STATE = self.radio.set_ptt(True)
|
||||||
self.tci_module.push_audio(data_out48k)
|
jsondata = {"ptt": "True"}
|
||||||
|
data_out = json.dumps(jsondata)
|
||||||
|
sock.SOCKET_QUEUE.put(data_out)
|
||||||
|
|
||||||
|
data_out = self.modoutqueue.popleft()
|
||||||
|
self.tci_module.push_audio(data_out)
|
||||||
|
|
||||||
def tci_rx_callback(self) -> None:
|
def tci_rx_callback(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -351,7 +358,7 @@ class RF:
|
||||||
|
|
||||||
x = self.audio_received_queue.get()
|
x = self.audio_received_queue.get()
|
||||||
x = np.frombuffer(x, dtype=np.int16)
|
x = np.frombuffer(x, dtype=np.int16)
|
||||||
#x = self.resampler.resample48_to_8(x)
|
# x = self.resampler.resample48_to_8(x)
|
||||||
|
|
||||||
self.fft_data = x
|
self.fft_data = x
|
||||||
|
|
||||||
|
@ -370,8 +377,6 @@ class RF:
|
||||||
):
|
):
|
||||||
data_buffer.push(x)
|
data_buffer.push(x)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def mkfifo_read_callback(self) -> None:
|
def mkfifo_read_callback(self) -> None:
|
||||||
"""
|
"""
|
||||||
Support testing by reading the audio data from a pipe and
|
Support testing by reading the audio data from a pipe and
|
||||||
|
@ -469,7 +474,7 @@ class RF:
|
||||||
if not static.PTT_STATE:
|
if not static.PTT_STATE:
|
||||||
# TODO: Moved to this place for testing
|
# TODO: Moved to this place for testing
|
||||||
# Maybe we can avoid moments of silence before transmitting
|
# Maybe we can avoid moments of silence before transmitting
|
||||||
static.PTT_STATE = self.hamlib.set_ptt(True)
|
static.PTT_STATE = self.radio.set_ptt(True)
|
||||||
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)
|
||||||
|
@ -528,7 +533,7 @@ class RF:
|
||||||
start_of_transmission = time.time()
|
start_of_transmission = time.time()
|
||||||
# TODO: Moved ptt toggle some steps before audio is ready for testing
|
# TODO: Moved ptt toggle some steps before audio is ready for testing
|
||||||
# Toggle ptt early to save some time and send ptt state via socket
|
# Toggle ptt early to save some time and send ptt state via socket
|
||||||
# static.PTT_STATE = self.hamlib.set_ptt(True)
|
# static.PTT_STATE = self.radio.set_ptt(True)
|
||||||
# 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)
|
||||||
|
@ -632,24 +637,32 @@ class RF:
|
||||||
elif 0.0 < static.HAMLIB_ALC <= 0.1:
|
elif 0.0 < static.HAMLIB_ALC <= 0.1:
|
||||||
print("0.0 < static.HAMLIB_ALC <= 0.1")
|
print("0.0 < static.HAMLIB_ALC <= 0.1")
|
||||||
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL + 2
|
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL + 2
|
||||||
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL), alc_level=str(static.HAMLIB_ALC))
|
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL),
|
||||||
|
alc_level=str(static.HAMLIB_ALC))
|
||||||
elif 0.1 < static.HAMLIB_ALC < 0.2:
|
elif 0.1 < static.HAMLIB_ALC < 0.2:
|
||||||
print("0.1 < static.HAMLIB_ALC < 0.2")
|
print("0.1 < static.HAMLIB_ALC < 0.2")
|
||||||
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL
|
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL
|
||||||
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL), alc_level=str(static.HAMLIB_ALC))
|
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL),
|
||||||
|
alc_level=str(static.HAMLIB_ALC))
|
||||||
elif 0.2 < static.HAMLIB_ALC < 0.99:
|
elif 0.2 < static.HAMLIB_ALC < 0.99:
|
||||||
print("0.2 < static.HAMLIB_ALC < 0.99")
|
print("0.2 < static.HAMLIB_ALC < 0.99")
|
||||||
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL - 20
|
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL - 20
|
||||||
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL), alc_level=str(static.HAMLIB_ALC))
|
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL),
|
||||||
elif 1.0 >=static.HAMLIB_ALC:
|
alc_level=str(static.HAMLIB_ALC))
|
||||||
|
elif 1.0 >= static.HAMLIB_ALC:
|
||||||
print("1.0 >= static.HAMLIB_ALC")
|
print("1.0 >= static.HAMLIB_ALC")
|
||||||
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL - 40
|
static.TX_AUDIO_LEVEL = static.TX_AUDIO_LEVEL - 40
|
||||||
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL), alc_level=str(static.HAMLIB_ALC))
|
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL),
|
||||||
|
alc_level=str(static.HAMLIB_ALC))
|
||||||
else:
|
else:
|
||||||
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL), alc_level=str(static.HAMLIB_ALC))
|
self.log.debug("[MDM] AUDIO TUNE", audio_level=str(static.TX_AUDIO_LEVEL),
|
||||||
|
alc_level=str(static.HAMLIB_ALC))
|
||||||
x = set_audio_volume(x, static.TX_AUDIO_LEVEL)
|
x = set_audio_volume(x, static.TX_AUDIO_LEVEL)
|
||||||
|
|
||||||
txbuffer_48k = self.resampler.resample8_to_48(x)
|
if not static.AUDIO_ENABLE_TCI:
|
||||||
|
txbuffer_out = self.resampler.resample8_to_48(x)
|
||||||
|
else:
|
||||||
|
txbuffer_out = x
|
||||||
|
|
||||||
# Explicitly lock our usage of mod_out_queue if needed
|
# Explicitly lock our usage of mod_out_queue if needed
|
||||||
# This could avoid audio problems on slower CPU
|
# This could avoid audio problems on slower CPU
|
||||||
|
@ -660,8 +673,8 @@ class RF:
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
chunk_length = self.AUDIO_FRAMES_PER_BUFFER_TX # 4800
|
chunk_length = self.AUDIO_FRAMES_PER_BUFFER_TX # 4800
|
||||||
chunk = [
|
chunk = [
|
||||||
txbuffer_48k[i: i + chunk_length]
|
txbuffer_out[i: i + chunk_length]
|
||||||
for i in range(0, len(txbuffer_48k), chunk_length)
|
for i in range(0, len(txbuffer_out), chunk_length)
|
||||||
]
|
]
|
||||||
for c in chunk:
|
for c in chunk:
|
||||||
# Pad the chunk, if needed
|
# Pad the chunk, if needed
|
||||||
|
@ -670,18 +683,36 @@ class RF:
|
||||||
delta_zeros = np.zeros(delta, dtype=np.int16)
|
delta_zeros = np.zeros(delta, dtype=np.int16)
|
||||||
c = np.append(c, delta_zeros)
|
c = np.append(c, delta_zeros)
|
||||||
# self.log.debug("[MDM] mod out shorter than audio buffer", delta=delta)
|
# self.log.debug("[MDM] 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:
|
# we need to wait manually for tci processing
|
||||||
|
if static.AUDIO_ENABLE_TCI:
|
||||||
|
duration = len(txbuffer_out) / 8000
|
||||||
|
timestamp_to_sleep = time.time() + duration
|
||||||
|
self.log.debug("[MDM] TCI calculated duration", duration=duration)
|
||||||
|
tci_timeout_reached = False
|
||||||
|
#while time.time() < timestamp_to_sleep:
|
||||||
|
# threading.Event().wait(0.01)
|
||||||
|
else:
|
||||||
|
timestamp_to_sleep = time.time()
|
||||||
|
# set tci timeout reached to True for overriding if not used
|
||||||
|
tci_timeout_reached = True
|
||||||
|
|
||||||
|
while self.modoutqueue or not tci_timeout_reached:
|
||||||
|
if static.AUDIO_ENABLE_TCI:
|
||||||
|
if time.time() < timestamp_to_sleep:
|
||||||
|
tci_timeout_reached = False
|
||||||
|
else:
|
||||||
|
tci_timeout_reached = True
|
||||||
|
|
||||||
threading.Event().wait(0.01)
|
threading.Event().wait(0.01)
|
||||||
# if we're transmitting FreeDATA signals, reset channel busy state
|
# if we're transmitting FreeDATA signals, reset channel busy state
|
||||||
static.CHANNEL_BUSY = False
|
static.CHANNEL_BUSY = False
|
||||||
|
|
||||||
static.PTT_STATE = self.hamlib.set_ptt(False)
|
static.PTT_STATE = self.radio.set_ptt(False)
|
||||||
|
|
||||||
# Push ptt state to socket stream
|
# Push ptt state to socket stream
|
||||||
jsondata = {"ptt": "False"}
|
jsondata = {"ptt": "False"}
|
||||||
|
@ -1051,10 +1082,10 @@ class RF:
|
||||||
cmd = RIGCTLD_COMMAND_QUEUE.get()
|
cmd = RIGCTLD_COMMAND_QUEUE.get()
|
||||||
if cmd[0] == "set_frequency":
|
if cmd[0] == "set_frequency":
|
||||||
# [1] = Frequency
|
# [1] = Frequency
|
||||||
self.hamlib.set_frequency(cmd[1])
|
self.radio.set_frequency(cmd[1])
|
||||||
if cmd[0] == "set_mode":
|
if cmd[0] == "set_mode":
|
||||||
# [1] = Mode
|
# [1] = Mode
|
||||||
self.hamlib.set_mode(cmd[1])
|
self.radio.set_mode(cmd[1])
|
||||||
|
|
||||||
def update_rig_data(self) -> None:
|
def update_rig_data(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -1067,22 +1098,22 @@ class RF:
|
||||||
while True:
|
while True:
|
||||||
# this looks weird, but is necessary for avoiding rigctld packet colission sock
|
# this looks weird, but is necessary for avoiding rigctld packet colission sock
|
||||||
threading.Event().wait(0.25)
|
threading.Event().wait(0.25)
|
||||||
static.HAMLIB_FREQUENCY = self.hamlib.get_frequency()
|
static.HAMLIB_FREQUENCY = self.radio.get_frequency()
|
||||||
threading.Event().wait(0.1)
|
threading.Event().wait(0.1)
|
||||||
static.HAMLIB_MODE = self.hamlib.get_mode()
|
static.HAMLIB_MODE = self.radio.get_mode()
|
||||||
threading.Event().wait(0.1)
|
threading.Event().wait(0.1)
|
||||||
static.HAMLIB_BANDWIDTH = self.hamlib.get_bandwidth()
|
static.HAMLIB_BANDWIDTH = self.radio.get_bandwidth()
|
||||||
threading.Event().wait(0.1)
|
threading.Event().wait(0.1)
|
||||||
static.HAMLIB_STATUS = self.hamlib.get_status()
|
static.HAMLIB_STATUS = self.radio.get_status()
|
||||||
threading.Event().wait(0.1)
|
threading.Event().wait(0.1)
|
||||||
if static.TRANSMITTING:
|
if static.TRANSMITTING:
|
||||||
static.HAMLIB_ALC = self.hamlib.get_alc()
|
static.HAMLIB_ALC = self.radio.get_alc()
|
||||||
threading.Event().wait(0.1)
|
threading.Event().wait(0.1)
|
||||||
#static.HAMLIB_RF = self.hamlib.get_level()
|
# static.HAMLIB_RF = self.radio.get_level()
|
||||||
#threading.Event().wait(0.1)
|
# threading.Event().wait(0.1)
|
||||||
static.HAMLIB_STRENGTH = self.hamlib.get_strength()
|
static.HAMLIB_STRENGTH = self.radio.get_strength()
|
||||||
|
|
||||||
#print(f"ALC: {static.HAMLIB_ALC}, RF: {static.HAMLIB_RF}, STRENGTH: {static.HAMLIB_STRENGTH}")
|
# print(f"ALC: {static.HAMLIB_ALC}, RF: {static.HAMLIB_RF}, STRENGTH: {static.HAMLIB_STRENGTH}")
|
||||||
|
|
||||||
def calculate_fft(self) -> None:
|
def calculate_fft(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -11,7 +11,7 @@ Not nice, suggestions are appreciated :-)
|
||||||
import subprocess
|
import subprocess
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
VERSION = "0.8.0-alpha.4"
|
VERSION = "0.8.0-alpha.4-TCI-exp"
|
||||||
|
|
||||||
ENABLE_EXPLORER = False
|
ENABLE_EXPLORER = False
|
||||||
ENABLE_STATS = False
|
ENABLE_STATS = False
|
||||||
|
|
246
tnc/tci.py
246
tnc/tci.py
|
@ -4,14 +4,10 @@
|
||||||
import structlog
|
import structlog
|
||||||
import threading
|
import threading
|
||||||
import websocket
|
import websocket
|
||||||
|
import numpy as np
|
||||||
|
import time
|
||||||
from queues import AUDIO_TRANSMIT_QUEUE, AUDIO_RECEIVED_QUEUE
|
from queues import AUDIO_TRANSMIT_QUEUE, AUDIO_RECEIVED_QUEUE
|
||||||
|
|
||||||
"""
|
|
||||||
trx:0,true;
|
|
||||||
trx:0,false;
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
class TCI:
|
class TCI:
|
||||||
def __init__(self, hostname='127.0.0.1', port=50001):
|
def __init__(self, hostname='127.0.0.1', port=50001):
|
||||||
# websocket.enableTrace(True)
|
# websocket.enableTrace(True)
|
||||||
|
@ -32,6 +28,25 @@ class TCI:
|
||||||
)
|
)
|
||||||
tci_thread.start()
|
tci_thread.start()
|
||||||
|
|
||||||
|
# flag if we're receiving a tx_chrono
|
||||||
|
self.tx_chrono = False
|
||||||
|
|
||||||
|
# audio related parameters, will be updated by tx chrono
|
||||||
|
self.sample_rate = None
|
||||||
|
self.format = None
|
||||||
|
self.codec = None
|
||||||
|
self.audio_length = None
|
||||||
|
self.crc = None
|
||||||
|
self.channel = None
|
||||||
|
|
||||||
|
self.frequency = None
|
||||||
|
self.bandwidth = None
|
||||||
|
self.mode = None
|
||||||
|
self.alc = None
|
||||||
|
self.meter = None
|
||||||
|
self.level = None
|
||||||
|
self.ptt = None
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
self.log.info(
|
self.log.info(
|
||||||
"[TCI] Starting TCI thread!", ip=self.hostname, port=self.port
|
"[TCI] Starting TCI thread!", ip=self.hostname, port=self.port
|
||||||
|
@ -45,17 +60,49 @@ class TCI:
|
||||||
)
|
)
|
||||||
|
|
||||||
self.ws.run_forever(reconnect=5) # Set dispatcher to automatic reconnection, 5 second reconnect delay if con>
|
self.ws.run_forever(reconnect=5) # Set dispatcher to automatic reconnection, 5 second reconnect delay if con>
|
||||||
#rel.signal(2, rel.abort) # Keyboard Interrupt
|
# rel.signal(2, rel.abort) # Keyboard Interrupt
|
||||||
#rel.dispatch()
|
# rel.dispatch()
|
||||||
|
|
||||||
def on_message(self, ws, message):
|
def on_message(self, ws, message):
|
||||||
|
|
||||||
|
# ready message
|
||||||
|
# we need to wait until radio is ready before we can push commands
|
||||||
if message == "ready;":
|
if message == "ready;":
|
||||||
self.ws.send('audio_samplerate:8000;')
|
self.ws.send('audio_samplerate:8000;')
|
||||||
self.ws.send('audio_stream_channels:1;')
|
self.ws.send('audio_stream_channels:1;')
|
||||||
self.ws.send('AUDIO_STREAM_SAMPLE_TYPE:int16;')
|
self.ws.send('audio_stream_sample_type:int16;')
|
||||||
self.ws.send('AUDIO_STREAM_SAMPLES:1200;')
|
self.ws.send('audio_stream_samples:1200;')
|
||||||
self.ws.send('audio_start:0;')
|
self.ws.send('audio_start:0;')
|
||||||
|
|
||||||
|
# tx chrono frame
|
||||||
|
if len(message) in {64}:
|
||||||
|
receiver = message[:4]
|
||||||
|
sample_rate = int.from_bytes(message[4:8], "little")
|
||||||
|
format = int.from_bytes(message[8:12], "little")
|
||||||
|
codec = int.from_bytes(message[12:16], "little")
|
||||||
|
crc = int.from_bytes(message[16:20], "little")
|
||||||
|
audio_length = int.from_bytes(message[20:24], "little")
|
||||||
|
type = int.from_bytes(message[24:28], "little")
|
||||||
|
channel = int.from_bytes(message[28:32], "little")
|
||||||
|
reserved1 = int.from_bytes(message[32:36], "little")
|
||||||
|
reserved2 = int.from_bytes(message[36:40], "little")
|
||||||
|
reserved3 = int.from_bytes(message[40:44], "little")
|
||||||
|
reserved4 = int.from_bytes(message[44:48], "little")
|
||||||
|
reserved5 = int.from_bytes(message[48:52], "little")
|
||||||
|
reserved6 = int.from_bytes(message[52:56], "little")
|
||||||
|
reserved7 = int.from_bytes(message[56:60], "little")
|
||||||
|
reserved8 = int.from_bytes(message[60:64], "little")
|
||||||
|
if type == 3:
|
||||||
|
self.tx_chrono = True
|
||||||
|
|
||||||
|
self.sample_rate = sample_rate
|
||||||
|
self.format = format
|
||||||
|
self.codec = codec
|
||||||
|
self.audio_length = audio_length
|
||||||
|
self.channel = channel
|
||||||
|
self.crc = crc
|
||||||
|
|
||||||
|
# audio frame
|
||||||
if len(message) in {576, 2464, 4160}:
|
if len(message) in {576, 2464, 4160}:
|
||||||
# audio received
|
# audio received
|
||||||
receiver = message[:4]
|
receiver = message[:4]
|
||||||
|
@ -77,6 +124,36 @@ class TCI:
|
||||||
audio_data = message[64:]
|
audio_data = message[64:]
|
||||||
self.audio_received_queue.put(audio_data)
|
self.audio_received_queue.put(audio_data)
|
||||||
|
|
||||||
|
|
||||||
|
if len(message)< 64:
|
||||||
|
# find frequency
|
||||||
|
if bytes(message, "utf-8").startswith(b"vfo:0,0,"):
|
||||||
|
splitted_message = message.split("vfo:0,0,")
|
||||||
|
self.frequency = splitted_message[1][:-1]
|
||||||
|
|
||||||
|
# find mode
|
||||||
|
if bytes(message, "utf-8").startswith(b"modulation:0,"):
|
||||||
|
splitted_message = message.split("modulation:0,")
|
||||||
|
self.mode = splitted_message[1][:-1]
|
||||||
|
|
||||||
|
# find ptt
|
||||||
|
#if bytes(message, "utf-8").startswith(b"trx:0,"):
|
||||||
|
# splitted_message = message.split("trx:0,")
|
||||||
|
# self.ptt = splitted_message[1][:-1]
|
||||||
|
|
||||||
|
# find bandwidth
|
||||||
|
#if message.startswith("rx_filter_band:0,"):
|
||||||
|
# splitted_message = message.split("rx_filter_band:0,")
|
||||||
|
# bandwidths = splitted_message[1]
|
||||||
|
# splitted_bandwidths = bandwidths.split(",")
|
||||||
|
# lower_bandwidth = int(splitted_bandwidths[0])
|
||||||
|
# upper_bandwidth = int(splitted_bandwidths[1][:-1])
|
||||||
|
# self.bandwidth = upper_bandwidth - lower_bandwidth
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def on_error(self, error):
|
def on_error(self, error):
|
||||||
self.log.error(
|
self.log.error(
|
||||||
"[TCI] Error FreeDATA to TCI rig!", ip=self.hostname, port=self.port, e=error
|
"[TCI] Error FreeDATA to TCI rig!", ip=self.hostname, port=self.port, e=error
|
||||||
|
@ -84,15 +161,160 @@ class TCI:
|
||||||
|
|
||||||
def on_close(self, ws, close_status_code, close_msg):
|
def on_close(self, ws, close_status_code, close_msg):
|
||||||
self.log.warning(
|
self.log.warning(
|
||||||
"[TCI] Closed FreeDATA to TCI connection!", ip=self.hostname, port=self.port, statu=close_status_code, msg=close_msg
|
"[TCI] Closed FreeDATA to TCI connection!", ip=self.hostname, port=self.port, statu=close_status_code,
|
||||||
|
msg=close_msg
|
||||||
)
|
)
|
||||||
|
|
||||||
def on_open(self, ws):
|
def on_open(self, ws):
|
||||||
|
self.ws = ws
|
||||||
self.log.info(
|
self.log.info(
|
||||||
"[TCI] Connected FreeDATA to TCI rig!", ip=self.hostname, port=self.port
|
"[TCI] Connected FreeDATA to TCI rig!", ip=self.hostname, port=self.port
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
self.log.info(
|
self.log.info(
|
||||||
"[TCI] Init...", ip=self.hostname, port=self.port
|
"[TCI] Init...", ip=self.hostname, port=self.port
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def push_audio(self, data_out):
|
||||||
|
#print(data_out)
|
||||||
|
|
||||||
|
"""
|
||||||
|
# audio[:4] = receiver.to_bytes(4,byteorder='little', signed=False)
|
||||||
|
audio[4:8] = sample_rate.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
audio[8:12] = format.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
audio[12:16] = codec.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
audio[16:20] = crc.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
audio[20:24] = audio_length.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
audio[24:28] = int(2).to_bytes(4, byteorder='little', signed=True)
|
||||||
|
audio[28:32] = channel.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
audio[32:36] = reserved1.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
audio[36:40] = reserved2.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
audio[40:44] = reserved3.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
audio[44:48] = reserved4.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
audio[48:52] = reserved5.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
audio[52:56] = reserved6.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
audio[56:60] = reserved7.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
audio[60:64] = reserved8.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
"""
|
||||||
|
|
||||||
|
while not self.tx_chrono:
|
||||||
|
time.sleep(0.01)
|
||||||
|
|
||||||
|
#print(len(data_out))
|
||||||
|
#print(self.sample_rate)
|
||||||
|
#print(self.audio_length)
|
||||||
|
#print(self.channel)
|
||||||
|
#print(self.crc)
|
||||||
|
#print(self.codec)
|
||||||
|
#print(self.tx_chrono)
|
||||||
|
|
||||||
|
if self.tx_chrono:
|
||||||
|
#print("#############")
|
||||||
|
#print(len(data_out))
|
||||||
|
#print(len(bytes(data_out)))
|
||||||
|
#print("-------------")
|
||||||
|
audio = bytearray(4096 + 64)
|
||||||
|
|
||||||
|
audio[64:64 + len(bytes(data_out))] = bytes(data_out)
|
||||||
|
audio[4:8] = self.sample_rate.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
# audio[8:12] = format.to_bytes(4,byteorder='little', signed=False)
|
||||||
|
audio[12:16] = self.codec.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
audio[16:20] = self.crc.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
audio[20:24] = self.audio_length.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
audio[24:28] = int(2).to_bytes(4, byteorder='little', signed=False)
|
||||||
|
audio[28:32] = self.channel.to_bytes(4, byteorder='little', signed=False)
|
||||||
|
# audio[32:36] = reserved1.to_bytes(4,byteorder='little', signed=False)
|
||||||
|
# audio[36:40] = reserved2.to_bytes(4,byteorder='little', signed=False)
|
||||||
|
# audio[40:44] = reserved3.to_bytes(4,byteorder='little', signed=False)
|
||||||
|
# audio[44:48] = reserved4.to_bytes(4,byteorder='little', signed=False)
|
||||||
|
# audio[48:52] = reserved5.to_bytes(4,byteorder='little', signed=False)
|
||||||
|
# audio[52:56] = reserved6.to_bytes(4,byteorder='little', signed=False)
|
||||||
|
# audio[56:60] = reserved7.to_bytes(4,byteorder='little', signed=False)
|
||||||
|
|
||||||
|
self.ws.send(audio, websocket.ABNF.OPCODE_BINARY)
|
||||||
|
|
||||||
|
def set_ptt(self, state):
|
||||||
|
if state:
|
||||||
|
self.ws.send('trx:0,true,tci;')
|
||||||
|
else:
|
||||||
|
|
||||||
|
self.ws.send('trx:0,false;')
|
||||||
|
self.tx_chrono = False
|
||||||
|
|
||||||
|
def get_frequency(self):
|
||||||
|
""" """
|
||||||
|
self.ws.send('VFO:0,0;')
|
||||||
|
return self.frequency
|
||||||
|
|
||||||
|
def get_mode(self):
|
||||||
|
""" """
|
||||||
|
self.ws.send('MODULATION:0;')
|
||||||
|
return self.mode
|
||||||
|
|
||||||
|
def get_level(self):
|
||||||
|
""" """
|
||||||
|
return self.level
|
||||||
|
|
||||||
|
def get_alc(self):
|
||||||
|
""" """
|
||||||
|
return self.alc
|
||||||
|
|
||||||
|
def get_meter(self):
|
||||||
|
""" """
|
||||||
|
return self.meter
|
||||||
|
|
||||||
|
def get_bandwidth(self):
|
||||||
|
""" """
|
||||||
|
return self.bandwidth
|
||||||
|
|
||||||
|
def get_strength(self):
|
||||||
|
""" """
|
||||||
|
return None
|
||||||
|
|
||||||
|
def set_bandwidth(self):
|
||||||
|
""" """
|
||||||
|
return None
|
||||||
|
|
||||||
|
def set_mode(self, mode):
|
||||||
|
"""
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mode:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.ws.send(f'MODULATION:0,{str(mode)};')
|
||||||
|
return None
|
||||||
|
|
||||||
|
def set_frequency(self, frequency):
|
||||||
|
"""
|
||||||
|
|
||||||
|
Args:
|
||||||
|
frequency:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.ws.send(f'VFO:0,0,{str(frequency)};')
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_status(self):
|
||||||
|
"""
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mode:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
"""
|
||||||
|
return "connected"
|
||||||
|
|
||||||
|
def get_ptt(self):
|
||||||
|
""" """
|
||||||
|
self.ws.send('trx:0;')
|
||||||
|
return self.ptt
|
||||||
|
|
||||||
|
def close_rig(self):
|
||||||
|
""" """
|
||||||
|
return
|
||||||
|
|
Loading…
Reference in New Issue