Merge branch 'develop' into qm-develop-gui

This commit is contained in:
DJ2LS 2023-11-04 14:33:09 +01:00 committed by GitHub
commit 22e92af2ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 406 additions and 309 deletions

View file

@ -17,17 +17,22 @@ import {
getNewMessagesByDXCallsign,
} from "../js/chatHandler";
import { sendTestFrame, setTxAudioLevel } from "../js/sock.js";
import { sendTestFrame, setTxAudioLevel, setRxAudioLevel } from "../js/sock.js";
function tuneAudio() {
sendTestFrame();
}
function set_audio_level() {
function set_tx_audio_level() {
saveSettingsToFile();
setTxAudioLevel(settings.tx_audio_level);
}
function set_rx_audio_level() {
saveSettingsToFile();
setRxAudioLevel(settings.rx_audio_level);
}
function deleteChat() {
//console.log(chat.selectedCallsign)
deleteChatByCallsign(chat.selectedCallsign);
@ -1194,6 +1199,21 @@ const transmissionSpeedChartDataMessageInfo = computed(() => ({
Transmit
</button>
</div>
<div class="input-group input-group-sm mb-1">
<span class="input-group-text">RX Level</span>
<span class="input-group-text">{{ settings.rx_audio_level }}</span>
<span class="input-group-text w-75">
<input
type="range"
class="form-range"
min="-30"
max="20"
step="1"
id="audioLevelRX"
@click="set_rx_audio_level()"
v-model="settings.rx_audio_level"
/></span>
</div>
<div class="input-group input-group-sm mb-1">
<span class="input-group-text">TX Level</span>
<span class="input-group-text">{{ settings.tx_audio_level }}</span>
@ -1201,11 +1221,11 @@ const transmissionSpeedChartDataMessageInfo = computed(() => ({
<input
type="range"
class="form-range"
min="0"
max="250"
min="-30"
max="20"
step="1"
id="audioLevelTX"
@click="set_audio_level()"
@click="set_tx_audio_level()"
v-model="settings.tx_audio_level"
/></span>
</div>

View file

@ -195,6 +195,7 @@ export function startModem() {
tuning_range_fmin: settings.tuning_range_fmin,
tuning_range_fmax: settings.tuning_range_fmax,
tx_audio_level: settings.tx_audio_level,
rx_audio_level: settings.rx_audio_level,
respond_to_cq: settings.respond_to_cq,
rx_buffer_size: settings.rx_buffer_size,
enable_explorer: settings.enable_explorer,

View file

@ -55,7 +55,8 @@ const configDefaultSettings =
"daemon_port": 3001,\
"rx_audio" : "",\
"tx_audio" : "",\
"tx_audio_level" : 100,\
"tx_audio_level" : 0,\
"rx_audio_level" : 0,\
"mycall": "AA0AA-0",\
"myssid": "0",\
"mygrid": "JN40aa",\

View file

@ -31,6 +31,8 @@ const split_char = "0;1;";
// global to keep track of Modem connection error emissions
var modemShowConnectStateError = 1;
var setTxAudioLevelOnce = true;
var setRxAudioLevelOnce = true;
// network connection Timeout
setTimeout(connectModem, 2000);
@ -171,7 +173,8 @@ client.on("data", function (socketdata) {
stateStore.arq_state = data["arq_state"];
stateStore.mode = data["mode"];
stateStore.bandwidth = data["bandwidth"];
stateStore.tx_audio_level = data["audio_level"];
stateStore.tx_audio_level = data["tx_audio_level"];
stateStore.rx_audio_level = data["rx_audio_level"];
// if audio level is different from config one, send new audio level to modem
//console.log(parseInt(stateStore.tx_audio_level))
//console.log(parseInt(settings.tx_audio_level))
@ -185,6 +188,16 @@ client.on("data", function (socketdata) {
setTxAudioLevel(settings.tx_audio_level);
}
if (
parseInt(stateStore.rx_audio_level) !==
parseInt(settings.rx_audio_level) &&
setRxAudioLevelOnce === true
) {
setRxAudioLevelOnce = false;
console.log(setRxAudioLevelOnce);
setRxAudioLevel(settings.rx_audio_level);
}
stateStore.dbfs_level = data["audio_dbfs"];
stateStore.ptt_state = data["ptt_state"];
stateStore.speed_level = data["speed_level"];
@ -541,6 +554,11 @@ export function setTxAudioLevel(value) {
'{"type" : "set", "command" : "tx_audio_level", "value" : "' + value + '"}';
writeTncCommand(command);
}
export function setRxAudioLevel(value) {
var command =
'{"type" : "set", "command" : "rx_audio_level", "value" : "' + value + '"}';
writeTncCommand(command);
}
// Send Message
export function sendMessage(obj) {

View file

@ -6,6 +6,7 @@ export const useSettingsStore = defineStore("settingsStore", () => {
var tx_audio = ref();
var rx_audio = ref();
var tx_audio_level = ref();
var rx_audio_level = ref();
// network
var modem_host = ref("127.0.0.1");
@ -159,6 +160,7 @@ export const useSettingsStore = defineStore("settingsStore", () => {
tx_audio: tx_audio.value,
rx_audio: rx_audio.value,
tx_audio_level: tx_audio_level.value,
rx_audio_level: rx_audio_level.value,
};
return config_export;
@ -227,5 +229,6 @@ export const useSettingsStore = defineStore("settingsStore", () => {
getSerialDevices,
serial_devices,
tx_audio_level,
rx_audio_level,
};
});

View file

@ -41,6 +41,8 @@ export const useStateStore = defineStore("stateStore", () => {
var hamlib_status = ref("");
var tx_audio_level = ref("");
var rx_audio_level = ref("");
var alc = ref("");
var is_codec2_traffic = ref("");
@ -115,6 +117,7 @@ export const useStateStore = defineStore("stateStore", () => {
audio_recording,
hamlib_status,
tx_audio_level,
rx_audio_level,
alc,
updateTncState,
arq_transmission_percent,

View file

@ -58,6 +58,7 @@ class CONFIG:
'rx': data[3],
'tx': data[4],
'txaudiolevel': data[14],
'rxaudiolevel': data[25],
'auto_tune': data[19]
}
@ -77,14 +78,15 @@ class CONFIG:
'explorer': data[17],
'stats': data[19],
'fsk': data[13],
'tx_delay': data[21]
}
'tx_delay': data[21],
'transmit_morse_identifier' : data[26]
}
self.config['TCI'] = {'#TCI settings': None,
'ip': data[22],
'port': data[23]
}
self.config['MESH'] = {'#TCI settings': None,
self.config['MESH'] = {'#Mesh settings': None,
'enable_protocol': data[24]
}

View file

@ -6,8 +6,10 @@ import numpy as np
"""
class MorseCodePlayer:
def __init__(self, wpm=150, f=1500, fs=48000):
def __init__(self, wpm=25, f=1500, fs=48000):
self.wpm = wpm
self.f0 = f
self.fs = fs
@ -39,28 +41,26 @@ class MorseCodePlayer:
signal = np.array([], dtype=np.int16)
for char in morse:
if char == '.':
duration = int(self.dot_duration * self.fs)
s = np.sin(2 * np.pi * self.f0 * np.arange(duration) / self.fs)
signal = np.concatenate((signal, s * 32767))
pause_duration = int(self.pause_duration * self.fs)
signal = np.concatenate((signal, np.zeros(pause_duration, dtype=np.int16)))
duration = self.dot_duration # Using class-defined duration
t = np.linspace(0, duration, int(self.fs * duration), endpoint=False)
s = 0.5 * np.sin(2 * np.pi * self.f0 * t)
signal = np.concatenate((signal, np.int16(s * 32767)))
pause_samples = int(self.pause_duration * self.fs)
signal = np.concatenate((signal, np.zeros(pause_samples, dtype=np.int16)))
elif char == '-':
duration = int(self.dash_duration * self.fs)
s = np.sin(2 * np.pi * self.f0 * np.arange(duration) / self.fs)
signal = np.concatenate((signal, s * 32767))
pause_duration = int(self.pause_duration * self.fs)
signal = np.concatenate((signal, np.zeros(pause_duration, dtype=np.int16)))
duration = self.dash_duration # Using class-defined duration
t = np.linspace(0, duration, int(self.fs * duration), endpoint=False)
s = 0.5 * np.sin(2 * np.pi * self.f0 * t)
signal = np.concatenate((signal, np.int16(s * 32767)))
pause_samples = int(self.pause_duration * self.fs)
signal = np.concatenate((signal, np.zeros(pause_samples, dtype=np.int16)))
elif char == ' ':
pause_duration = int(self.word_pause_duration * self.fs)
signal = np.concatenate((signal, np.zeros(pause_duration, dtype=np.int16)))
pause_duration = int(self.pause_duration * self.fs)
signal = np.concatenate((signal, np.zeros(pause_duration, dtype=np.int16)))
pause_duration = int(self.word_pause_duration * self.fs)
#signal = np.concatenate((signal, np.zeros(pause_duration, dtype=np.int16)))
# Convert the signal to mono (single-channel)
#signal = signal.reshape(-1, 1)
pause_samples = int(self.word_pause_duration * self.fs)
signal = np.concatenate((signal, np.zeros(pause_samples, dtype=np.int16)))
pause_samples = int(self.pause_duration * self.fs)
signal = np.concatenate((signal, np.zeros(pause_samples, dtype=np.int16)))
return signal

View file

@ -212,7 +212,7 @@ class DAEMON:
data[13] hamlib_rigctld_ip,
data[14] hamlib_rigctld_path,
data[15] hamlib_rigctld_server_port,
data[16] hamlib_rigctld_custom_args
data[16] hamlib_rigctld_custom_args
"""
self.start_rigctld(data)
@ -417,11 +417,8 @@ class DAEMON:
except Exception as err:
self.log.warning("[DMN] err starting rigctld: ", e=err)
def start_modem(self, data):
self.log.warning("[DMN] Starting Modem", rig=data[5], port=data[6])
# list of parameters, necessary for running subprocess command as a list
options = ["--port", str(DAEMON.port - 1)]
@ -492,10 +489,19 @@ class DAEMON:
options.append(data[21])
#Mesh
print(data[24])
if data[24] == "True":
options.append("--mesh")
options.append("--rx-audio-level")
options.append(data[25])
#Morse identifier
if data[26] == "True":
options.append("--morse")
# safe data to config file
config.write_entire_config(data)

View file

@ -56,10 +56,16 @@ class DATA:
self.length_sig0_frame = 14
self.length_sig1_frame = 14
# duration of signalling frame
self.duration_sig0_frame = 2.3
self.duration_sig1_frame = 2.3
self.longest_duration = 5.8 # datac5
# duration of frames
self.duration_datac4 = 5.17
self.duration_datac13 = 2.0
self.duration_datac1 = 4.18
self.duration_datac3 = 3.19
self.duration_sig0_frame = self.duration_datac13
self.duration_sig1_frame = self.duration_datac13
self.longest_duration = self.duration_datac4
# hold session id
self.session_id = bytes(1)
@ -110,7 +116,6 @@ class DATA:
self.received_LOW_BANDWIDTH_MODE = False
self.data_channel_max_retries = 15
self.datachannel_timeout = False
# -------------- AVAILABLE MODES START-----------
# IMPORTANT: LISTS MUST BE OF EQUAL LENGTH
@ -124,7 +129,7 @@ class DATA:
# List for minimum SNR operating level for the corresponding mode in self.mode_list
self.snr_list_low_bw = [-100]
# List for time to wait for corresponding mode in seconds
self.time_list_low_bw = [6 + self.duration_sig0_frame + 1]
self.time_list_low_bw = [self.duration_datac4]
# --------------------- HIGH BANDWIDTH
@ -140,7 +145,7 @@ class DATA:
# test with 6,7 --> caused sometimes a frame timeout if ack frame takes longer
# TODO Need to check why ACK frames needs more time
# TODO Adjust these times
self.time_list_high_bw = [6 + self.duration_sig0_frame + 1, 6 + self.duration_sig0_frame + 1, 6 + self.duration_sig0_frame + 1]
self.time_list_high_bw = [self.duration_datac4, self.duration_datac3, self.duration_datac1]
# -------------- AVAILABLE MODES END-----------
# Mode list for selecting between low bandwidth ( 500Hz ) and modes with higher bandwidth
@ -182,6 +187,8 @@ class DATA:
self.data_frame_ack_timeout_seconds = 4.5 # timeout for data frame acknowledges
self.rpt_ack_timeout_seconds = 4.5 # timeout for rpt frame acknowledges
self.transmission_timeout = 180 # transmission timeout in seconds
self.channel_busy_timeout = 3 # time how long we want to wait until channel busy state overrides
self.datachannel_opening_interval = self.duration_sig1_frame + self.channel_busy_timeout + 1 # time between attempts when opening data channel
# Dictionary of functions and log messages used in process_data
# instead of a long series of if-elif-else statements.
@ -546,10 +553,9 @@ class DATA:
ack_frame[3:4] = bytes([int(self.speed_level)])
ack_frame[4:8] = len(ARQ.rx_frame_buffer).to_bytes(4, byteorder="big")
# wait while timeout not reached and our busy state is busy
channel_busy_timeout = time.time() + 5
while ModemParam.channel_busy and time.time() < channel_busy_timeout and not self.check_if_mode_fits_to_busy_slot():
threading.Event().wait(0.01)
# wait if we have a channel busy condition
if ModemParam.channel_busy:
self.channel_busy_handler()
# Transmit frame
self.enqueue_frame_for_tx([ack_frame], c2_mode=FREEDV_MODE.sig1.value)
@ -562,16 +568,11 @@ class DATA:
ack_frame[1:2] = self.session_id
ack_frame[2:3] = helpers.snr_to_bytes(snr)
# wait while timeout not reached and our busy state is busy
channel_busy_timeout = time.time() + 5
while ModemParam.channel_busy and time.time() < channel_busy_timeout and not self.check_if_mode_fits_to_busy_slot():
threading.Event().wait(0.01)
# wait if we have a channel busy condition
if ModemParam.channel_busy:
self.channel_busy_handler()
# reset burst timeout in case we had to wait too long
self.burst_last_received = time.time() + channel_busy_timeout + 8
# Transmit frame
# TODO Do we have to send , self.send_ident_frame(False) ?
# self.enqueue_frame_for_tx([ack_frame, self.send_ident_frame(False)], c2_mode=FREEDV_MODE.sig1.value, copies=3, repeat_delay=0)
self.enqueue_frame_for_tx([ack_frame], c2_mode=FREEDV_MODE.sig1.value, copies=3, repeat_delay=0)
def send_retransmit_request_frame(self) -> None:
@ -607,22 +608,20 @@ class DATA:
# TODO Do we have to send ident frame?
# self.enqueue_frame_for_tx([ack_frame, self.send_ident_frame(False)], c2_mode=FREEDV_MODE.sig1.value, copies=3, repeat_delay=0)
# wait while timeout not reached and our busy state is busy
channel_busy_timeout = time.time() + 5
while ModemParam.channel_busy and time.time() < channel_busy_timeout and not self.check_if_mode_fits_to_busy_slot():
threading.Event().wait(0.01)
# wait if we have a channel busy condition
if ModemParam.channel_busy:
self.channel_busy_handler()
self.enqueue_frame_for_tx([nack_frame], c2_mode=FREEDV_MODE.sig1.value, copies=3, repeat_delay=0)
# reset burst timeout in case we had to wait too long
self.burst_last_received = time.time()
def send_burst_nack_frame_watchdog(self, snr: bytes, tx_n_frames_per_burst) -> None:
def send_burst_nack_frame_watchdog(self, tx_n_frames_per_burst) -> None:
"""Build and send NACK frame for watchdog timeout"""
# increment nack counter for transmission stats
self.frame_nack_counter += 1
# we need to clear our rx burst buffer
ARQ.rx_burst_buffer = []
@ -631,16 +630,14 @@ class DATA:
nack_frame = bytearray(self.length_sig1_frame)
nack_frame[:1] = bytes([FR_TYPE.BURST_NACK.value])
nack_frame[1:2] = self.session_id
nack_frame[2:3] = helpers.snr_to_bytes(snr)
nack_frame[2:3] = helpers.snr_to_bytes(0)
nack_frame[3:4] = bytes([int(self.speed_level)])
nack_frame[4:5] = bytes([int(tx_n_frames_per_burst)])
nack_frame[5:9] = len(ARQ.rx_frame_buffer).to_bytes(4, byteorder="big")
# wait while timeout not reached and our busy state is busy
channel_busy_timeout = time.time() + 5 + 5
while ModemParam.channel_busy and time.time() < channel_busy_timeout and not self.check_if_mode_fits_to_busy_slot():
threading.Event().wait(0.01)
# wait if we have a channel busy condition
if ModemParam.channel_busy:
self.channel_busy_handler()
# TRANSMIT NACK FRAME FOR BURST
self.enqueue_frame_for_tx([nack_frame], c2_mode=FREEDV_MODE.sig1.value, copies=1, repeat_delay=0)
@ -654,16 +651,10 @@ class DATA:
disconnection_frame[:1] = bytes([FR_TYPE.ARQ_SESSION_CLOSE.value])
disconnection_frame[1:2] = self.session_id
disconnection_frame[2:5] = Station.dxcallsign_crc
# TODO Needed? disconnection_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign)
# self.enqueue_frame_for_tx([disconnection_frame, self.send_ident_frame(False)], c2_mode=FREEDV_MODE.sig0.value, copies=5, repeat_delay=0)
# TODO We need to add the ident frame feature with a seperate PR after publishing latest protocol
# TODO We need to wait some time between last arq related signalling frame and ident frame
# TODO Maybe about 500ms - 1500ms to avoid confusion and too much PTT toggles
# wait while timeout not reached and our busy state is busy
channel_busy_timeout = time.time() + 5
while ModemParam.channel_busy and time.time() < channel_busy_timeout and not self.check_if_mode_fits_to_busy_slot():
threading.Event().wait(0.01)
# wait if we have a channel busy condition
if ModemParam.channel_busy:
self.channel_busy_handler()
self.enqueue_frame_for_tx([disconnection_frame], c2_mode=FREEDV_MODE.sig0.value, copies=3, repeat_delay=0)
@ -815,8 +806,12 @@ class DATA:
):
self.arq_calculate_speed_level(snr)
self.data_channel_last_received = int(time.time()) + 6 + 6
self.burst_last_received = int(time.time()) + 6 + 6
# TIMING TEST
#self.data_channel_last_received = int(time.time()) + 6 + 6
#self.burst_last_received = int(time.time()) + 6 + 6
self.data_channel_last_received = int(time.time())
self.burst_last_received = int(time.time())
# Create and send ACK frame
self.log.info("[Modem] ARQ | RX | SENDING ACK", finished=ARQ.arq_seconds_until_finish,
bytesperminute=ARQ.bytes_per_minute)
@ -1008,9 +1003,7 @@ class DATA:
mode_slots=mode_slots,
)
return False
else:
return True
return True
def arq_calculate_speed_level(self, snr):
current_speed_level = self.speed_level
@ -1041,6 +1034,13 @@ class DATA:
# Update modes we are listening to
self.set_listening_modes(False, True, self.mode_list[self.speed_level])
self.log.debug(
"[Modem] calculated speed level",
speed_level=self.speed_level,
given_snr=ModemParam.snr,
min_snr=self.snr_list[self.speed_level],
)
def arq_process_received_data_frame(self, data_frame, snr, signed):
"""
@ -1742,8 +1742,6 @@ class DATA:
Station.dxcallsign = self.dxcallsign
Station.dxcallsign_crc = helpers.get_crc_24(self.dxcallsign)
# TODO we need to check this, maybe placing it to class init
self.datachannel_timeout = False
self.log.info(
"[Modem] SESSION ["
+ str(self.mycallsign, "UTF-8")
@ -1753,35 +1751,9 @@ class DATA:
state=ARQ.arq_session_state,
)
# Let's check if we have a busy channel
# wait if we have a channel busy condition
if ModemParam.channel_busy:
self.log.warning("[Modem] Channel busy, waiting until free...")
self.send_data_to_socket_queue(
freedata="modem-message",
arq="session",
status="waiting",
mycallsign=str(self.mycallsign, 'UTF-8'),
dxcallsign=str(self.dxcallsign, 'UTF-8'),
)
# wait while timeout not reached and our busy state is busy
channel_busy_timeout = time.time() + 15
while ModemParam.channel_busy and time.time() < channel_busy_timeout and not self.check_if_mode_fits_to_busy_slot():
threading.Event().wait(0.01)
# if channel busy timeout reached stop connecting
if time.time() > channel_busy_timeout:
self.log.warning("[Modem] Channel busy, try again later...")
ARQ.arq_session_state = "failed"
self.send_data_to_socket_queue(
freedata="modem-message",
arq="session",
status="failed",
reason="busy",
mycallsign=str(self.mycallsign, 'UTF-8'),
dxcallsign=str(self.dxcallsign, 'UTF-8'),
)
return False
self.channel_busy_handler()
self.open_session()
@ -1995,6 +1967,10 @@ class DATA:
# we need to send disconnect frame before doing arq cleanup
# we would lose our session id then
self.send_disconnect_frame()
# transmit morse identifier if configured
if Modem.transmit_morse_identifier:
modem.MODEM_TRANSMIT_QUEUE.put(["morse", 1, 0, self.mycallsign])
self.arq_cleanup()
def received_session_close(self, data_in: bytes):
@ -2162,114 +2138,19 @@ class DATA:
if ARQ.arq_session:
threading.Event().wait(2.5)
self.datachannel_timeout = False
# we need to compress data for getting a compression factor.
# so we are compressing twice. This is not that nice and maybe there is another way
# for calculating transmission statistics
# ARQ.arq_compression_factor = len(data_out) / len(lzma.compress(data_out))
# init arq state event
ARQ.arq_state_event = threading.Event()
# finally start the channel opening procedure
self.arq_open_data_channel(mycallsign)
# wait until data channel is open
while not ARQ.arq_state and not self.datachannel_timeout and Modem.modem_state in ["BUSY"]:
threading.Event().wait(0.01)
if ARQ.arq_state:
# if data channel is open, return true else false
if ARQ.arq_state_event.is_set():
# start arq transmission
self.arq_transmit(data_out, hmac_salt)
return True
return False
def arq_open_data_channel(
self, mycallsign
) -> bool:
"""
Open an ARQ data channel.
Args:
mycallsign:bytes:
Returns:
True if the data channel was opened successfully
False if the data channel failed to open
"""
self.is_IRS = False
# init a new random session id if we are not in an arq session
if not ARQ.arq_session:
self.session_id = np.random.bytes(1)
# Update data_channel timestamp
self.data_channel_last_received = int(time.time())
if Modem.low_bandwidth_mode:
frametype = bytes([FR_TYPE.ARQ_DC_OPEN_N.value])
self.log.debug("[Modem] Requesting low bandwidth mode")
else:
frametype = bytes([FR_TYPE.ARQ_DC_OPEN_W.value])
self.log.debug("[Modem] Requesting high bandwidth mode")
connection_frame = bytearray(self.length_sig0_frame)
connection_frame[:1] = frametype
connection_frame[1:4] = Station.dxcallsign_crc
connection_frame[4:7] = Station.mycallsign_crc
connection_frame[7:13] = helpers.callsign_to_bytes(mycallsign)
connection_frame[13:14] = self.session_id
while not ARQ.arq_state:
threading.Event().wait(0.01)
for attempt in range(self.data_channel_max_retries):
self.send_data_to_socket_queue(
freedata="modem-message",
arq="transmission",
status="opening",
mycallsign=str(mycallsign, 'UTF-8'),
dxcallsign=str(self.dxcallsign, 'UTF-8'),
irs=helpers.bool_to_string(self.is_IRS)
)
self.log.info(
"[Modem] ARQ | DATA | TX | ["
+ str(mycallsign, "UTF-8")
+ "]>> <<["
+ str(self.dxcallsign, "UTF-8")
+ "]",
attempt=f"{str(attempt + 1)}/{str(self.data_channel_max_retries)}",
)
# Let's check if we have a busy channel and if we are not in a running arq session.
if ModemParam.channel_busy and not ARQ.arq_state:
self.log.warning("[Modem] Channel busy, waiting until free...")
self.send_data_to_socket_queue(
freedata="modem-message",
arq="transmission",
status="waiting",
mycallsign=str(self.mycallsign, 'UTF-8'),
dxcallsign=str(self.dxcallsign, 'UTF-8'),
irs=helpers.bool_to_string(self.is_IRS)
)
# wait while timeout not reached and our busy state is busy
channel_busy_timeout = time.time() + 5
while ModemParam.channel_busy and time.time() < channel_busy_timeout and not self.check_if_mode_fits_to_busy_slot():
threading.Event().wait(0.01)
self.enqueue_frame_for_tx([connection_frame], c2_mode=FREEDV_MODE.sig0.value, copies=1, repeat_delay=0)
timeout = time.time() + self.duration_sig1_frame * 3 + (ModemParam.tx_delay / 1000 * 2)
while time.time() < timeout:
threading.Event().wait(0.01)
# Stop waiting if data channel is opened
if ARQ.arq_state:
return True
if Modem.modem_state in ["IDLE"]:
return False
# `data_channel_max_retries` attempts have been sent. Aborting attempt & cleaning up
self.log.debug(
"[Modem] arq_open_data_channel:", transmission_uuid=self.transmission_uuid
)
@ -2297,16 +2178,93 @@ class DATA:
+ str(self.dxcallsign, "UTF-8")
+ "]"
)
self.datachannel_timeout = True
# Attempt to clean up the far-side, if it received the
# open_session frame and can still hear us.
self.close_session()
# otherwise return false
return False
# Shouldn't get here...
return True
def arq_open_data_channel(
self, mycallsign
) -> bool:
"""
Open an ARQ data channel.
Args:
mycallsign:bytes:
Returns:
True if the data channel was opened successfully
False if the data channel failed to open
"""
# set IRS indicator to false, because we are IRS
self.is_IRS = False
# init a new random session id if we are not in an arq session
if not ARQ.arq_session:
self.session_id = np.random.bytes(1)
# Update data_channel timestamp
self.data_channel_last_received = int(time.time())
# check if the Modem is running in low bandwidth mode
# then set the corresponding frametype and build frame
if Modem.low_bandwidth_mode:
frametype = bytes([FR_TYPE.ARQ_DC_OPEN_N.value])
self.log.debug("[Modem] Requesting low bandwidth mode")
else:
frametype = bytes([FR_TYPE.ARQ_DC_OPEN_W.value])
self.log.debug("[Modem] Requesting high bandwidth mode")
connection_frame = bytearray(self.length_sig0_frame)
connection_frame[:1] = frametype
connection_frame[1:4] = Station.dxcallsign_crc
connection_frame[4:7] = Station.mycallsign_crc
connection_frame[7:13] = helpers.callsign_to_bytes(mycallsign)
connection_frame[13:14] = self.session_id
for attempt in range(self.data_channel_max_retries):
self.send_data_to_socket_queue(
freedata="modem-message",
arq="transmission",
status="opening",
mycallsign=str(mycallsign, 'UTF-8'),
dxcallsign=str(self.dxcallsign, 'UTF-8'),
irs=helpers.bool_to_string(self.is_IRS)
)
self.log.info(
"[Modem] ARQ | DATA | TX | ["
+ str(mycallsign, "UTF-8")
+ "]>> <<["
+ str(self.dxcallsign, "UTF-8")
+ "]",
attempt=f"{str(attempt + 1)}/{str(self.data_channel_max_retries)}",
)
# Let's check if we have a busy channel and if we are not in a running arq session.
if ModemParam.channel_busy and not ARQ.arq_state_event.is_set() or ModemParam.is_codec2_traffic:
self.channel_busy_handler()
# if channel free, enqueue frame for tx
if not ARQ.arq_state_event.is_set():
self.enqueue_frame_for_tx([connection_frame], c2_mode=FREEDV_MODE.sig0.value, copies=1, repeat_delay=0)
# wait until timeout or event set
random_wait_time = randrange(int(self.duration_sig1_frame * 10), int(self.datachannel_opening_interval * 10), 1) / 10
ARQ.arq_state_event.wait(timeout=random_wait_time)
if ARQ.arq_state_event.is_set():
return True
if Modem.modem_state in ["IDLE"]:
return False
# `data_channel_max_retries` attempts have been sent. Aborting attempt & cleaning up
return False
def arq_received_data_channel_opener(self, data_in: bytes):
"""
@ -2338,7 +2296,7 @@ class DATA:
# Station B already tries connecting to Station A.
# For avoiding ignoring repeated connect request in case of packet loss
# we are only ignoring packets in case we are ISS
if ARQ.arq_state and not self.is_IRS:
if ARQ.arq_state_event.is_set() and not self.is_IRS:
return False
self.is_IRS = True
@ -2356,9 +2314,6 @@ class DATA:
irs=helpers.bool_to_string(self.is_IRS)
)
# n_frames_per_burst is currently unused
# n_frames_per_burst = int.from_bytes(bytes(data_in[13:14]), "big")
frametype = int.from_bytes(bytes(data_in[:1]), "big")
# check if we received low bandwidth mode
# possible channel constellations
@ -2406,29 +2361,14 @@ class DATA:
# initially set speed_level 0 in case of bad SNR and no matching mode
self.speed_level = 0
# TODO MOVE THIS TO arq_calculate_speed_level()
# calculate speed level in correlation to latest known SNR
# calculate initial speed level in correlation to latest known SNR
for i in range(len(self.mode_list)):
if ModemParam.snr >= self.snr_list[i]:
self.speed_level = i
# calculate if speed level fits to busy condition
mode_name = codec2.FREEDV_MODE(self.mode_list[self.speed_level]).name
mode_slots = codec2.FREEDV_MODE_USED_SLOTS[mode_name].value
if mode_slots in [ModemParam.channel_busy_slot]:
# check if speed level fits to busy condition
if not self.check_if_mode_fits_to_busy_slot():
self.speed_level = 0
self.log.warning(
"[Modem] busy slot detection",
slots=ModemParam.channel_busy_slot,
mode_slots=mode_slots,
)
self.log.debug(
"[Modem] calculated speed level",
speed_level=self.speed_level,
given_snr=ModemParam.snr,
min_snr=self.snr_list[self.speed_level],
)
# Update modes we are listening to
self.set_listening_modes(True, True, self.mode_list[self.speed_level])
@ -2457,6 +2397,7 @@ class DATA:
)
# Reset data_channel/burst timestamps
# TIMING TEST
self.data_channel_last_received = int(time.time())
self.burst_last_received = int(time.time() + 10) # we might need some more time so lets increase this
@ -2505,7 +2446,9 @@ class DATA:
# set start of transmission for our statistics
self.rx_start_of_transmission = time.time()
# TIMING TEST
# Reset data_channel/burst timestamps once again for avoiding running into timeout
# and therefore sending a NACK
self.data_channel_last_received = int(time.time())
self.burst_last_received = int(time.time() + 10) # we might need some more time so lets increase this
@ -2539,10 +2482,10 @@ class DATA:
self.time_list = self.time_list_high_bw
self.log.debug("[Modem] high bandwidth mode", modes=self.mode_list)
# set speed level from session opener frame which is selected by SNR measurement
# set speed level from session opener frame delegation
self.speed_level = int.from_bytes(bytes(data_in[8:9]), "big")
self.log.debug("[Modem] speed level selected for given SNR", speed_level=self.speed_level)
# self.speed_level = len(self.mode_list) - 1
Station.dxgrid = b'------'
helpers.add_to_heard_stations(
Station.dxcallsign,
@ -2562,13 +2505,15 @@ class DATA:
snr=ModemParam.snr,
)
# as soon as we set ARQ_STATE to DATA, transmission starts
# as soon as we set ARQ_STATE to True, transmission starts
ARQ.arq_state = True
# also set the ARQ event
ARQ.arq_state_event.set()
# Update data_channel timestamp
self.data_channel_last_received = int(time.time())
else:
Modem.modem_state = "IDLE"
ARQ.arq_state = False
self.send_data_to_socket_queue(
freedata="modem-message",
arq="transmission",
@ -2578,7 +2523,6 @@ class DATA:
dxcallsign=str(self.dxcallsign, 'UTF-8'),
irs=helpers.bool_to_string(self.is_IRS)
)
# TODO We should display a message to this effect on the UI.
self.log.warning(
"[Modem] protocol version mismatch:",
received=protocol_version,
@ -2761,8 +2705,6 @@ class DATA:
"""
self.log.warning("[Modem] Stopping transmission!")
Modem.modem_state = "IDLE"
ARQ.arq_state = False
self.send_data_to_socket_queue(
freedata="modem-message",
arq="transmission",
@ -2778,7 +2720,6 @@ class DATA:
# TODO Not sure if we really need the session id when disconnecting
# stop_frame[1:2] = self.session_id
stop_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign)
self.enqueue_frame_for_tx([stop_frame], c2_mode=FREEDV_MODE.sig1.value, copies=3, repeat_delay=0)
self.arq_cleanup()
@ -2849,6 +2790,8 @@ class DATA:
else:
self.enqueue_frame_for_tx([beacon_frame], c2_mode=FREEDV_MODE.sig0.value, copies=1,
repeat_delay=0)
if Modem.transmit_morse_identifier:
modem.MODEM_TRANSMIT_QUEUE.put(["morse", 1, 0, self.mycallsign])
self.beacon_interval_timer = time.time() + self.beacon_interval
while (
@ -2926,9 +2869,7 @@ class DATA:
self.enqueue_frame_for_tx([cq_frame], c2_mode=FREEDV_MODE.fsk_ldpc_0.value)
else:
self.enqueue_frame_for_tx([cq_frame], c2_mode=FREEDV_MODE.sig0.value, copies=1, repeat_delay=0)
# FIXME Remove or change this in later versions for full CW support
# Modem.transmitting = True
# modem.MODEM_TRANSMIT_QUEUE.put(["morse", 1, 0, "123"])
def received_cq(self, data_in: bytes) -> None:
"""
@ -3073,6 +3014,26 @@ class DATA:
+ "] ",
)
def channel_busy_handler(self):
"""
function for handling the channel busy situation
Args:
Returns:
"""
self.log.warning("[Modem] Channel busy, waiting until free...")
self.send_data_to_socket_queue(
freedata="modem-message",
channel="busy",
status="waiting",
)
# wait while timeout not reached and our busy state is busy
channel_busy_timeout = time.time() + self.channel_busy_timeout
while ModemParam.channel_busy and time.time() < channel_busy_timeout and not self.check_if_mode_fits_to_busy_slot():
threading.Event().wait(0.01)
# ------------ CALCULATE TRANSFER RATES
def calculate_transfer_rate_rx(
self, rx_start_of_transmission: float, receivedbytes: int
@ -3263,6 +3224,7 @@ class DATA:
ARQ.arq_session_state = "disconnected"
ARQ.speed_list = []
ARQ.arq_state = False
ARQ.arq_state_event = threading.Event()
self.arq_file_transfer = False
Beacon.beacon_pause = False
@ -3375,10 +3337,38 @@ class DATA:
if frames_left == 0:
frames_left = 1
timeout = self.burst_last_received + (self.time_list[self.speed_level] * frames_left)
# timeout is reached, if we didnt receive data, while we waited
# for the corresponding data frame + the transmitted signalling frame of ack/nack
# + a small offset of about 1 second
timeout = \
(
self.burst_last_received
+ (self.time_list[self.speed_level] * frames_left)
+ self.duration_sig0_frame
+ self.channel_busy_timeout
+ 1
)
# override calculation
# if we reached 2/3 of the waiting time and didnt received a signal
# then send NACK earlier
time_left = timeout - time.time()
waiting_time = (self.time_list[self.speed_level] * frames_left) + self.duration_sig0_frame + self.channel_busy_timeout + 1
timeout_percent = 100 - (time_left / waiting_time * 100)
#timeout_percent = 0
if timeout_percent >= 75 and not ModemParam.is_codec2_traffic and not Modem.transmitting:
override = True
else:
override = False
# TODO Enable this for development
# print(f"timeout expected in:{round(timeout - time.time())} | frames left: {frames_left} of {self.rx_n_frames_per_burst} | speed level: {self.speed_level}")
if timeout <= time.time() or modem_error_state:
print(f"timeout expected in:{round(timeout - time.time())} | timeout percent: {timeout_percent} | frames left: {frames_left} of {self.rx_n_frames_per_burst} | speed level: {self.speed_level}")
# if timeout is expired, but we are receivingt codec2 data,
# better wait some more time because data might be important for us
# reason for this situation can be delays on IRS and ISS, maybe because both had a busy channel condition.
# Nevertheless, we need to keep timeouts short for efficiency
if timeout <= time.time() or modem_error_state and not ModemParam.is_codec2_traffic and not Modem.transmitting or override:
self.log.warning(
"[Modem] Burst decoding error or timeout",
attempt=self.n_retries_per_burst,
@ -3389,7 +3379,7 @@ class DATA:
print(
f"frames_per_burst {self.rx_n_frame_of_burst} / {self.rx_n_frames_per_burst}, Repeats: {self.burst_rpt_counter} Nones: {ARQ.rx_burst_buffer.count(None)}")
# check if we have N frames per burst > 1
if self.rx_n_frames_per_burst > 1 and self.burst_rpt_counter < 3 and ARQ.rx_burst_buffer.count(None) > 0:
# reset self.burst_last_received
self.burst_last_received = time.time() + self.time_list[self.speed_level] * frames_left
@ -3398,8 +3388,8 @@ class DATA:
else:
# reset self.burst_last_received
self.burst_last_received = time.time() + self.time_list[self.speed_level]
# reset self.burst_last_received counter
self.burst_last_received = time.time()
# reduce speed level if nack counter increased
self.frame_received_counter = 0
@ -3420,13 +3410,14 @@ class DATA:
self.set_listening_modes(True, True, self.mode_list[self.speed_level])
# TODO Does SNR make sense for NACK if we dont have an actual SNR information?
self.send_burst_nack_frame_watchdog(0, tx_n_frames_per_burst)
self.send_burst_nack_frame_watchdog(tx_n_frames_per_burst)
# Update data_channel timestamp
# TODO Disabled this one for testing.
# self.data_channel_last_received = time.time()
self.n_retries_per_burst += 1
else:
# debugging output
# print((self.data_channel_last_received + self.time_list[self.speed_level])-time.time())
pass

View file

@ -190,10 +190,17 @@ if __name__ == "__main__":
PARSER.add_argument(
"--tx-audio-level",
dest="tx_audio_level",
default=50,
default=0,
help="Set the tx audio level at an early stage",
type=int,
)
PARSER.add_argument(
"--rx-audio-level",
dest="rx_audio_level",
default=0,
help="Set the rx audio level at an early stage",
type=int,
)
PARSER.add_argument(
"--rx-buffer-size",
dest="rx_buffer_size",
@ -261,6 +268,15 @@ if __name__ == "__main__":
help="Enable and set hmac message salt",
)
PARSER.add_argument(
"--morse",
dest="transmit_morse_identifier",
action="store_true",
default=False,
help="Enable and send a morse identifier on disconnect an beacon",
)
ARGS = PARSER.parse_args()
# set save to folder state for allowing downloading files to local file system
@ -306,6 +322,7 @@ if __name__ == "__main__":
ModemParam.tuning_range_fmin = ARGS.tuning_range_fmin
ModemParam.tuning_range_fmax = ARGS.tuning_range_fmax
AudioParam.tx_audio_level = ARGS.tx_audio_level
AudioParam.rx_audio_level = ARGS.rx_audio_level
Modem.respond_to_cq = ARGS.enable_respond_to_cq
ARQ.rx_buffer_size = ARGS.rx_buffer_size
Modem.enable_explorer = ARGS.enable_explorer
@ -316,7 +333,7 @@ if __name__ == "__main__":
ModemParam.tx_delay = ARGS.tx_delay
MeshParam.enable_protocol = ARGS.enable_mesh
Modem.enable_hmac = ARGS.enable_hmac
Modem.transmit_morse_identifier = ARGS.transmit_morse_identifier
except Exception as e:
log.error("[DMN] Error reading config file", exception=e)
@ -358,7 +375,8 @@ if __name__ == "__main__":
Modem.low_bandwidth_mode = conf.get('Modem', 'narrowband', 'False')
ModemParam.tuning_range_fmin = float(conf.get('Modem', 'fmin', '-50.0'))
ModemParam.tuning_range_fmax = float(conf.get('Modem', 'fmax', '50.0'))
AudioParam.tx_audio_level = int(conf.get('AUDIO', 'txaudiolevel', '100'))
AudioParam.tx_audio_level = int(conf.get('AUDIO', 'txaudiolevel', '0'))
AudioParam.rx_audio_level = int(conf.get('AUDIO', 'rxaudiolevel', '0'))
Modem.respond_to_cq = conf.get('Modem', 'qrv', 'True')
ARQ.rx_buffer_size = int(conf.get('Modem', 'rx_buffer_size', '16'))
Modem.enable_explorer = conf.get('Modem', 'explorer', 'False')
@ -368,6 +386,8 @@ if __name__ == "__main__":
TCIParam.port = int(conf.get('TCI', 'tci_port', '50001'))
ModemParam.tx_delay = int(conf.get('Modem', 'tx_delay', '0'))
MeshParam.enable_protocol = conf.get('MESH','mesh_enable','False')
MeshParam.transmit_morse_identifier = conf.get('Modem','transmit_morse_identifier','False')
except KeyError as e:
log.warning("[CFG] Error reading config file near", key=str(e))
except Exception as e:

View file

@ -16,20 +16,17 @@ import sys
import threading
import time
from collections import deque
import wave
import codec2
import itertools
import numpy as np
import sock
import sounddevice as sd
import static
from global_instances import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, Modem
from static import FRAME_TYPE
import structlog
import ujson as json
import tci
# FIXME used for def transmit_morse
# import cw
import cw
from queues import DATA_QUEUE_RECEIVED, MODEM_RECEIVED_QUEUE, MODEM_TRANSMIT_QUEUE, RIGCTLD_COMMAND_QUEUE, \
AUDIO_RECEIVED_QUEUE, AUDIO_TRANSMIT_QUEUE, MESH_RECEIVED_QUEUE
@ -80,6 +77,8 @@ class RF:
self.AUDIO_CHANNELS = 1
self.MODE = 0
self.is_codec2_traffic_cooldown = 20
self.is_codec2_traffic_counter = 0
# Locking state for mod out so buffer will be filled before we can use it
# https://github.com/DJ2LS/FreeDATA/issues/127
# https://github.com/DJ2LS/FreeDATA/issues/99
@ -460,6 +459,7 @@ class RF:
# self.log.debug("[MDM] callback")
x = np.frombuffer(data_in48k, dtype=np.int16)
x = self.resampler.resample48_to_8(x)
x = set_audio_volume(x, AudioParam.rx_audio_level)
# audio recording for debugging purposes
if AudioParam.audio_record:
@ -745,18 +745,7 @@ class RF:
)
start_of_transmission = time.time()
txbuffer = cw.MorseCodePlayer().text_to_signal("DJ2LS-1")
print(txbuffer)
print(type(txbuffer))
x = np.frombuffer(txbuffer, dtype=np.int16)
print(type(x))
txbuffer_out = x
print(txbuffer_out)
#if not HamlibParam.hamlib_radiocontrol in ["tci"]:
# txbuffer_out = self.resampler.resample8_to_48(x)
#else:
# txbuffer_out = x
txbuffer_out = cw.MorseCodePlayer().text_to_signal("DJ2LS-1")
self.mod_out_locked = True
self.enqueue_modulation(txbuffer_out)
@ -808,6 +797,8 @@ class RF:
self.log.debug("[MDM] ON AIR TIME", time=transmission_time)
def enqueue_modulation(self, txbuffer_out):
chunk_length = self.AUDIO_FRAMES_PER_BUFFER_TX # 4800
chunk = [
txbuffer_out[i: i + chunk_length]
@ -822,7 +813,6 @@ class RF:
# self.log.debug("[MDM] mod out shorter than audio buffer", delta=delta)
self.modoutqueue.append(c)
def demodulate_audio(
self,
audiobuffer: codec2.audio_buffer,
@ -854,6 +844,7 @@ class RF:
:return: NIN from freedv instance
:rtype: int
"""
nbytes = 0
try:
while self.stream.active:
@ -871,14 +862,15 @@ class RF:
# 10 error decoding == NACK
rx_status = codec2.api.freedv_get_rx_status(freedv)
if rx_status != 0:
if rx_status not in [0]:
# we need to disable this if in testmode as its causing problems with FIFO it seems
if not TESTMODE:
ModemParam.is_codec2_traffic = True
self.is_codec2_traffic_counter = self.is_codec2_traffic_cooldown
if not ModemParam.channel_busy:
self.log.debug("[MDM] Setting channel_busy since codec2 data detected")
ModemParam.channel_busy=True
ModemParam.channel_busy_delay+=10
ModemParam.channel_busy_delay += 10
self.log.debug(
"[MDM] [demod_audio] modem state", mode=mode_name, rx_status=rx_status,
sync_flag=codec2.api.rx_sync_flags_to_text[rx_status]
@ -886,6 +878,13 @@ class RF:
else:
ModemParam.is_codec2_traffic = False
# decrement codec traffic counter for making state smoother
if self.is_codec2_traffic_counter > 0:
self.is_codec2_traffic_counter -= 1
ModemParam.is_codec2_traffic = True
else:
ModemParam.is_codec2_traffic = False
if rx_status == 10:
state_buffer.append(rx_status)
@ -897,7 +896,6 @@ class RF:
# process commands only if Modem.listen = True
if Modem.listen:
# ignore data channel opener frames for avoiding toggle states
# use case: opener already received, but ack got lost and we are receiving
# an opener again
@ -1139,7 +1137,7 @@ class RF:
def get_scatter(self, freedv: ctypes.c_void_p) -> None:
"""
Ask codec2 for data about the received signal and calculate the scatter plot.
Side-effect: sets ModemParam.scatter
Side effect: sets ModemParam.scatter
:param freedv: codec2 instance to query
:type freedv: ctypes.c_void_p
@ -1181,7 +1179,7 @@ class RF:
"""
Ask codec2 for data about the received signal and calculate
the signal-to-noise ratio.
Side-effect: sets ModemParam.snr
Side effect: sets ModemParam.snr
:param freedv: codec2 instance to query
:type freedv: ctypes.c_void_p
@ -1227,7 +1225,7 @@ class RF:
def update_rig_data(self) -> None:
"""
Request information about the current state of the radio via hamlib
Side-effect: sets
Side effect: sets
- HamlibParam.hamlib_frequency
- HamlibParam.hamlib_mode
- HamlibParam.hamlib_bandwidth
@ -1258,6 +1256,7 @@ class RF:
e=e,
)
threading.Event().wait(1)
def calculate_fft(self) -> None:
"""
Calculate an average signal strength of the channel to assess
@ -1366,7 +1365,7 @@ class RF:
ModemParam.channel_busy_slot[slot] = False
# increment slot
slot += 1
if (addDelay):
if addDelay:
# Limit delay counter to a maximum of 200. The higher this value,
# the longer we will wait until releasing state
ModemParam.channel_busy = True
@ -1460,31 +1459,39 @@ def get_bytes_per_frame(mode: int) -> int:
# get number of bytes per frame for mode
return int(codec2.api.freedv_get_bits_per_modem_frame(freedv) / 8)
def set_audio_volume(datalist, volume: float) -> np.int16:
def set_audio_volume(datalist: np.ndarray, dB: float) -> np.ndarray:
"""
Scale values for the provided audio samples by volume,
`volume` is clipped to the range of 0-200
Scale values for the provided audio samples by dB.
:param datalist: Audio samples to scale
:type datalist: NDArray[np.int16]
:param volume: "Percentage" (0-200) to scale samples
:type volume: float
:type datalist: np.ndarray
:param dB: Decibels to scale samples, constrained to the range [-50, 50]
:type dB: float
:return: Scaled audio samples
:rtype: np.int16
:rtype: np.ndarray
"""
# make sure we have float as data type to avoid crash
try:
volume = float(volume)
except Exception as e:
print(f"[MDM] changing audio volume failed with error: {e}")
volume = 100.0
dB = float(dB)
except ValueError as e:
print(f"[MDM] Changing audio volume failed with error: {e}")
dB = 0.0 # 0 dB means no change
# Clip volume provided to acceptable values
volume = np.clip(volume, 0, 200) # limit to max value of 255
# Scale samples by the ratio of volume / 100.0
data = np.fromstring(datalist, np.int16) * (volume / 100.0) # type: ignore
return data.astype(np.int16)
# Clip dB value to the range [-50, 50]
dB = np.clip(dB, -30, 20)
# Ensure datalist is an np.ndarray
if not isinstance(datalist, np.ndarray):
print("[MDM] Invalid data type for datalist. Expected np.ndarray.")
return datalist
# Convert dB to linear scale
scale_factor = 10 ** (dB / 20)
# Scale samples
scaled_data = datalist * scale_factor
# Clip values to int16 range and convert data type
return np.clip(scaled_data, -32768, 32767).astype(np.int16)
def get_modem_error_state():

View file

@ -256,6 +256,12 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
else:
self.modem_set_tx_audio_level(received_json)
# SET RX AUDIO LEVEL
if received_json["type"] == "set" and received_json["command"] == "rx_audio_level":
if TESTMODE:
ThreadedTCPRequestHandler.modem_set_rx_audio_level(None, received_json)
else:
self.modem_set_rx_audio_level(received_json)
# TRANSMIT TEST FRAME
if received_json["type"] == "set" and received_json["command"] == "send_test_frame":
@ -493,6 +499,18 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
command=received_json,
)
def modem_set_rx_audio_level(self, received_json):
try:
AudioParam.rx_audio_level = int(received_json["value"])
command_response("rx_audio_level", True)
except Exception as err:
command_response("rx_audio_level", False)
log.warning(
"[SCK] TX audio command execution error",
e=err,
command=received_json,
)
def modem_set_send_test_frame(self, received_json):
try:
DATA_QUEUE_TRANSMIT.put(["SEND_TEST_FRAME"])
@ -1078,7 +1096,8 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
low_bandwidth_mode = str(helpers.return_key_from_object("False", startparam, "low_bandwidth_mode"))
tuning_range_fmin = str(helpers.return_key_from_object("-50", startparam, "tuning_range_fmin"))
tuning_range_fmax = str(helpers.return_key_from_object("50", startparam, "tuning_range_fmax"))
tx_audio_level = str(helpers.return_key_from_object("100", startparam, "tx_audio_level"))
tx_audio_level = str(helpers.return_key_from_object("0", startparam, "tx_audio_level"))
rx_audio_level = str(helpers.return_key_from_object("0", startparam, "rx_audio_level"))
respond_to_cq = str(helpers.return_key_from_object("False", startparam, "respond_to_cq"))
rx_buffer_size = str(helpers.return_key_from_object("16", startparam, "rx_buffer_size"))
enable_explorer = str(helpers.return_key_from_object("False", startparam, "enable_explorer"))
@ -1131,7 +1150,8 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
tx_delay,
tci_ip,
tci_port,
enable_mesh
enable_mesh,
rx_audio_level,
]
)
command_response("start_modem", True)
@ -1345,7 +1365,8 @@ def send_modem_state():
"rf_level": str(HamlibParam.hamlib_rf),
"strength": str(HamlibParam.hamlib_strength),
"alc": str(HamlibParam.alc),
"audio_level": str(AudioParam.tx_audio_level),
"tx_audio_level": str(AudioParam.tx_audio_level),
"rx_audio_level": str(AudioParam.tx_audio_level),
"audio_auto_tune": str(AudioParam.audio_auto_tune),
"speed_level": str(ARQ.arq_speed_level),
"mode": str(HamlibParam.hamlib_mode),

View file

@ -11,6 +11,7 @@ from dataclasses import dataclass, field
from typing import List
import subprocess
from enum import Enum
import threading
# CHANNEL_STATE = 'RECEIVING_SIGNALLING'
@ -33,6 +34,7 @@ class ARQ:
arq_session_state: str = "disconnected" # can be: disconnected, disconnecting, connected, connecting, failed
arq_session: bool = False
arq_state: bool = False
arq_state_event: threading.Event = field(default_factory=threading.Event)
# ARQ PROTOCOL VERSION
# v.5 - signalling frame uses datac0
# v.6 - signalling frame uses datac13
@ -48,7 +50,8 @@ class ARQ:
@dataclass
class AudioParam:
tx_audio_level: int = 50
tx_audio_level: int = 0
rx_audio_level: int = 0
audio_input_devices = []
audio_output_devices = []
audio_input_device: int = -2
@ -114,10 +117,10 @@ class ModemParam:
@dataclass
class Station:
mycallsign: bytes = b"AA0AA"
mycallsign: bytes = b"AA0AA-0"
mycallsign_crc: bytes = b"A"
dxcallsign: bytes = b"ZZ9YY"
dxcallsign_crc: bytes = b"A"
dxcallsign: bytes = b"ZZ9YY-0"
dxcallsign_crc: bytes = b"B"
mygrid: bytes = b""
dxgrid: bytes = b""
ssid_list = [] # ssid list we are responding to
@ -134,7 +137,7 @@ class TCIParam:
@dataclass
class Modem:
version = "0.11.1-alpha.3"
version = "0.11.2-alpha.4"
host: str = "0.0.0.0"
port: int = 3000
SOCKET_TIMEOUT: int = 1 # seconds
@ -149,6 +152,7 @@ class Modem:
heard_stations = []
listen: bool = True
enable_hmac: bool = True
transmit_morse_identifier: bool = False
# ------------