mirror of
https://github.com/DJ2LS/FreeDATA
synced 2024-05-14 08:04:33 +00:00
Merge branch 'develop' into qm-develop-gui
This commit is contained in:
commit
22e92af2ac
14 changed files with 406 additions and 309 deletions
|
@ -17,17 +17,22 @@ import {
|
||||||
getNewMessagesByDXCallsign,
|
getNewMessagesByDXCallsign,
|
||||||
} from "../js/chatHandler";
|
} from "../js/chatHandler";
|
||||||
|
|
||||||
import { sendTestFrame, setTxAudioLevel } from "../js/sock.js";
|
import { sendTestFrame, setTxAudioLevel, setRxAudioLevel } from "../js/sock.js";
|
||||||
|
|
||||||
function tuneAudio() {
|
function tuneAudio() {
|
||||||
sendTestFrame();
|
sendTestFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_audio_level() {
|
function set_tx_audio_level() {
|
||||||
saveSettingsToFile();
|
saveSettingsToFile();
|
||||||
setTxAudioLevel(settings.tx_audio_level);
|
setTxAudioLevel(settings.tx_audio_level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function set_rx_audio_level() {
|
||||||
|
saveSettingsToFile();
|
||||||
|
setRxAudioLevel(settings.rx_audio_level);
|
||||||
|
}
|
||||||
|
|
||||||
function deleteChat() {
|
function deleteChat() {
|
||||||
//console.log(chat.selectedCallsign)
|
//console.log(chat.selectedCallsign)
|
||||||
deleteChatByCallsign(chat.selectedCallsign);
|
deleteChatByCallsign(chat.selectedCallsign);
|
||||||
|
@ -1194,6 +1199,21 @@ const transmissionSpeedChartDataMessageInfo = computed(() => ({
|
||||||
Transmit
|
Transmit
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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">
|
<div class="input-group input-group-sm mb-1">
|
||||||
<span class="input-group-text">TX Level</span>
|
<span class="input-group-text">TX Level</span>
|
||||||
<span class="input-group-text">{{ settings.tx_audio_level }}</span>
|
<span class="input-group-text">{{ settings.tx_audio_level }}</span>
|
||||||
|
@ -1201,11 +1221,11 @@ const transmissionSpeedChartDataMessageInfo = computed(() => ({
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
class="form-range"
|
class="form-range"
|
||||||
min="0"
|
min="-30"
|
||||||
max="250"
|
max="20"
|
||||||
step="1"
|
step="1"
|
||||||
id="audioLevelTX"
|
id="audioLevelTX"
|
||||||
@click="set_audio_level()"
|
@click="set_tx_audio_level()"
|
||||||
v-model="settings.tx_audio_level"
|
v-model="settings.tx_audio_level"
|
||||||
/></span>
|
/></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -195,6 +195,7 @@ export function startModem() {
|
||||||
tuning_range_fmin: settings.tuning_range_fmin,
|
tuning_range_fmin: settings.tuning_range_fmin,
|
||||||
tuning_range_fmax: settings.tuning_range_fmax,
|
tuning_range_fmax: settings.tuning_range_fmax,
|
||||||
tx_audio_level: settings.tx_audio_level,
|
tx_audio_level: settings.tx_audio_level,
|
||||||
|
rx_audio_level: settings.rx_audio_level,
|
||||||
respond_to_cq: settings.respond_to_cq,
|
respond_to_cq: settings.respond_to_cq,
|
||||||
rx_buffer_size: settings.rx_buffer_size,
|
rx_buffer_size: settings.rx_buffer_size,
|
||||||
enable_explorer: settings.enable_explorer,
|
enable_explorer: settings.enable_explorer,
|
||||||
|
|
|
@ -55,7 +55,8 @@ const configDefaultSettings =
|
||||||
"daemon_port": 3001,\
|
"daemon_port": 3001,\
|
||||||
"rx_audio" : "",\
|
"rx_audio" : "",\
|
||||||
"tx_audio" : "",\
|
"tx_audio" : "",\
|
||||||
"tx_audio_level" : 100,\
|
"tx_audio_level" : 0,\
|
||||||
|
"rx_audio_level" : 0,\
|
||||||
"mycall": "AA0AA-0",\
|
"mycall": "AA0AA-0",\
|
||||||
"myssid": "0",\
|
"myssid": "0",\
|
||||||
"mygrid": "JN40aa",\
|
"mygrid": "JN40aa",\
|
||||||
|
|
|
@ -31,6 +31,8 @@ const split_char = "0;1;";
|
||||||
// global to keep track of Modem connection error emissions
|
// global to keep track of Modem connection error emissions
|
||||||
var modemShowConnectStateError = 1;
|
var modemShowConnectStateError = 1;
|
||||||
var setTxAudioLevelOnce = true;
|
var setTxAudioLevelOnce = true;
|
||||||
|
var setRxAudioLevelOnce = true;
|
||||||
|
|
||||||
// network connection Timeout
|
// network connection Timeout
|
||||||
setTimeout(connectModem, 2000);
|
setTimeout(connectModem, 2000);
|
||||||
|
|
||||||
|
@ -171,7 +173,8 @@ client.on("data", function (socketdata) {
|
||||||
stateStore.arq_state = data["arq_state"];
|
stateStore.arq_state = data["arq_state"];
|
||||||
stateStore.mode = data["mode"];
|
stateStore.mode = data["mode"];
|
||||||
stateStore.bandwidth = data["bandwidth"];
|
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
|
// if audio level is different from config one, send new audio level to modem
|
||||||
//console.log(parseInt(stateStore.tx_audio_level))
|
//console.log(parseInt(stateStore.tx_audio_level))
|
||||||
//console.log(parseInt(settings.tx_audio_level))
|
//console.log(parseInt(settings.tx_audio_level))
|
||||||
|
@ -185,6 +188,16 @@ client.on("data", function (socketdata) {
|
||||||
setTxAudioLevel(settings.tx_audio_level);
|
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.dbfs_level = data["audio_dbfs"];
|
||||||
stateStore.ptt_state = data["ptt_state"];
|
stateStore.ptt_state = data["ptt_state"];
|
||||||
stateStore.speed_level = data["speed_level"];
|
stateStore.speed_level = data["speed_level"];
|
||||||
|
@ -541,6 +554,11 @@ export function setTxAudioLevel(value) {
|
||||||
'{"type" : "set", "command" : "tx_audio_level", "value" : "' + value + '"}';
|
'{"type" : "set", "command" : "tx_audio_level", "value" : "' + value + '"}';
|
||||||
writeTncCommand(command);
|
writeTncCommand(command);
|
||||||
}
|
}
|
||||||
|
export function setRxAudioLevel(value) {
|
||||||
|
var command =
|
||||||
|
'{"type" : "set", "command" : "rx_audio_level", "value" : "' + value + '"}';
|
||||||
|
writeTncCommand(command);
|
||||||
|
}
|
||||||
|
|
||||||
// Send Message
|
// Send Message
|
||||||
export function sendMessage(obj) {
|
export function sendMessage(obj) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ export const useSettingsStore = defineStore("settingsStore", () => {
|
||||||
var tx_audio = ref();
|
var tx_audio = ref();
|
||||||
var rx_audio = ref();
|
var rx_audio = ref();
|
||||||
var tx_audio_level = ref();
|
var tx_audio_level = ref();
|
||||||
|
var rx_audio_level = ref();
|
||||||
|
|
||||||
// network
|
// network
|
||||||
var modem_host = ref("127.0.0.1");
|
var modem_host = ref("127.0.0.1");
|
||||||
|
@ -159,6 +160,7 @@ export const useSettingsStore = defineStore("settingsStore", () => {
|
||||||
tx_audio: tx_audio.value,
|
tx_audio: tx_audio.value,
|
||||||
rx_audio: rx_audio.value,
|
rx_audio: rx_audio.value,
|
||||||
tx_audio_level: tx_audio_level.value,
|
tx_audio_level: tx_audio_level.value,
|
||||||
|
rx_audio_level: rx_audio_level.value,
|
||||||
};
|
};
|
||||||
|
|
||||||
return config_export;
|
return config_export;
|
||||||
|
@ -227,5 +229,6 @@ export const useSettingsStore = defineStore("settingsStore", () => {
|
||||||
getSerialDevices,
|
getSerialDevices,
|
||||||
serial_devices,
|
serial_devices,
|
||||||
tx_audio_level,
|
tx_audio_level,
|
||||||
|
rx_audio_level,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -41,6 +41,8 @@ export const useStateStore = defineStore("stateStore", () => {
|
||||||
|
|
||||||
var hamlib_status = ref("");
|
var hamlib_status = ref("");
|
||||||
var tx_audio_level = ref("");
|
var tx_audio_level = ref("");
|
||||||
|
var rx_audio_level = ref("");
|
||||||
|
|
||||||
var alc = ref("");
|
var alc = ref("");
|
||||||
|
|
||||||
var is_codec2_traffic = ref("");
|
var is_codec2_traffic = ref("");
|
||||||
|
@ -115,6 +117,7 @@ export const useStateStore = defineStore("stateStore", () => {
|
||||||
audio_recording,
|
audio_recording,
|
||||||
hamlib_status,
|
hamlib_status,
|
||||||
tx_audio_level,
|
tx_audio_level,
|
||||||
|
rx_audio_level,
|
||||||
alc,
|
alc,
|
||||||
updateTncState,
|
updateTncState,
|
||||||
arq_transmission_percent,
|
arq_transmission_percent,
|
||||||
|
|
|
@ -58,6 +58,7 @@ class CONFIG:
|
||||||
'rx': data[3],
|
'rx': data[3],
|
||||||
'tx': data[4],
|
'tx': data[4],
|
||||||
'txaudiolevel': data[14],
|
'txaudiolevel': data[14],
|
||||||
|
'rxaudiolevel': data[25],
|
||||||
'auto_tune': data[19]
|
'auto_tune': data[19]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -77,14 +78,15 @@ class CONFIG:
|
||||||
'explorer': data[17],
|
'explorer': data[17],
|
||||||
'stats': data[19],
|
'stats': data[19],
|
||||||
'fsk': data[13],
|
'fsk': data[13],
|
||||||
'tx_delay': data[21]
|
'tx_delay': data[21],
|
||||||
}
|
'transmit_morse_identifier' : data[26]
|
||||||
|
}
|
||||||
self.config['TCI'] = {'#TCI settings': None,
|
self.config['TCI'] = {'#TCI settings': None,
|
||||||
'ip': data[22],
|
'ip': data[22],
|
||||||
'port': data[23]
|
'port': data[23]
|
||||||
}
|
}
|
||||||
|
|
||||||
self.config['MESH'] = {'#TCI settings': None,
|
self.config['MESH'] = {'#Mesh settings': None,
|
||||||
'enable_protocol': data[24]
|
'enable_protocol': data[24]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
42
modem/cw.py
42
modem/cw.py
|
@ -6,8 +6,10 @@ import numpy as np
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class MorseCodePlayer:
|
class MorseCodePlayer:
|
||||||
def __init__(self, wpm=150, f=1500, fs=48000):
|
def __init__(self, wpm=25, f=1500, fs=48000):
|
||||||
self.wpm = wpm
|
self.wpm = wpm
|
||||||
self.f0 = f
|
self.f0 = f
|
||||||
self.fs = fs
|
self.fs = fs
|
||||||
|
@ -39,28 +41,26 @@ class MorseCodePlayer:
|
||||||
signal = np.array([], dtype=np.int16)
|
signal = np.array([], dtype=np.int16)
|
||||||
for char in morse:
|
for char in morse:
|
||||||
if char == '.':
|
if char == '.':
|
||||||
duration = int(self.dot_duration * self.fs)
|
duration = self.dot_duration # Using class-defined duration
|
||||||
s = np.sin(2 * np.pi * self.f0 * np.arange(duration) / self.fs)
|
t = np.linspace(0, duration, int(self.fs * duration), endpoint=False)
|
||||||
signal = np.concatenate((signal, s * 32767))
|
s = 0.5 * np.sin(2 * np.pi * self.f0 * t)
|
||||||
pause_duration = int(self.pause_duration * self.fs)
|
signal = np.concatenate((signal, np.int16(s * 32767)))
|
||||||
signal = np.concatenate((signal, np.zeros(pause_duration, dtype=np.int16)))
|
pause_samples = int(self.pause_duration * self.fs)
|
||||||
|
signal = np.concatenate((signal, np.zeros(pause_samples, dtype=np.int16)))
|
||||||
|
|
||||||
elif char == '-':
|
elif char == '-':
|
||||||
duration = int(self.dash_duration * self.fs)
|
duration = self.dash_duration # Using class-defined duration
|
||||||
s = np.sin(2 * np.pi * self.f0 * np.arange(duration) / self.fs)
|
t = np.linspace(0, duration, int(self.fs * duration), endpoint=False)
|
||||||
signal = np.concatenate((signal, s * 32767))
|
s = 0.5 * np.sin(2 * np.pi * self.f0 * t)
|
||||||
pause_duration = int(self.pause_duration * self.fs)
|
signal = np.concatenate((signal, np.int16(s * 32767)))
|
||||||
signal = np.concatenate((signal, np.zeros(pause_duration, dtype=np.int16)))
|
pause_samples = int(self.pause_duration * self.fs)
|
||||||
|
signal = np.concatenate((signal, np.zeros(pause_samples, dtype=np.int16)))
|
||||||
|
|
||||||
elif char == ' ':
|
elif char == ' ':
|
||||||
pause_duration = int(self.word_pause_duration * self.fs)
|
pause_samples = int(self.word_pause_duration * self.fs)
|
||||||
signal = np.concatenate((signal, np.zeros(pause_duration, dtype=np.int16)))
|
signal = np.concatenate((signal, np.zeros(pause_samples, dtype=np.int16)))
|
||||||
pause_duration = int(self.pause_duration * self.fs)
|
pause_samples = int(self.pause_duration * self.fs)
|
||||||
signal = np.concatenate((signal, np.zeros(pause_duration, dtype=np.int16)))
|
signal = np.concatenate((signal, np.zeros(pause_samples, 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)
|
|
||||||
|
|
||||||
return signal
|
return signal
|
||||||
|
|
||||||
|
|
|
@ -417,11 +417,8 @@ class DAEMON:
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
self.log.warning("[DMN] err starting rigctld: ", e=err)
|
self.log.warning("[DMN] err starting rigctld: ", e=err)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def start_modem(self, data):
|
def start_modem(self, data):
|
||||||
self.log.warning("[DMN] Starting Modem", rig=data[5], port=data[6])
|
self.log.warning("[DMN] Starting Modem", 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 = ["--port", str(DAEMON.port - 1)]
|
options = ["--port", str(DAEMON.port - 1)]
|
||||||
|
|
||||||
|
@ -492,10 +489,19 @@ class DAEMON:
|
||||||
options.append(data[21])
|
options.append(data[21])
|
||||||
|
|
||||||
#Mesh
|
#Mesh
|
||||||
print(data[24])
|
|
||||||
if data[24] == "True":
|
if data[24] == "True":
|
||||||
options.append("--mesh")
|
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
|
# safe data to config file
|
||||||
config.write_entire_config(data)
|
config.write_entire_config(data)
|
||||||
|
|
||||||
|
|
|
@ -56,10 +56,16 @@ class DATA:
|
||||||
self.length_sig0_frame = 14
|
self.length_sig0_frame = 14
|
||||||
self.length_sig1_frame = 14
|
self.length_sig1_frame = 14
|
||||||
|
|
||||||
# duration of signalling frame
|
# duration of frames
|
||||||
self.duration_sig0_frame = 2.3
|
self.duration_datac4 = 5.17
|
||||||
self.duration_sig1_frame = 2.3
|
self.duration_datac13 = 2.0
|
||||||
self.longest_duration = 5.8 # datac5
|
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
|
# hold session id
|
||||||
self.session_id = bytes(1)
|
self.session_id = bytes(1)
|
||||||
|
@ -110,7 +116,6 @@ class DATA:
|
||||||
self.received_LOW_BANDWIDTH_MODE = False
|
self.received_LOW_BANDWIDTH_MODE = False
|
||||||
|
|
||||||
self.data_channel_max_retries = 15
|
self.data_channel_max_retries = 15
|
||||||
self.datachannel_timeout = False
|
|
||||||
|
|
||||||
# -------------- AVAILABLE MODES START-----------
|
# -------------- AVAILABLE MODES START-----------
|
||||||
# IMPORTANT: LISTS MUST BE OF EQUAL LENGTH
|
# 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
|
# List for minimum SNR operating level for the corresponding mode in self.mode_list
|
||||||
self.snr_list_low_bw = [-100]
|
self.snr_list_low_bw = [-100]
|
||||||
# List for time to wait for corresponding mode in seconds
|
# 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
|
# --------------------- HIGH BANDWIDTH
|
||||||
|
|
||||||
|
@ -140,7 +145,7 @@ class DATA:
|
||||||
# test with 6,7 --> caused sometimes a frame timeout if ack frame takes longer
|
# 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 Need to check why ACK frames needs more time
|
||||||
# TODO Adjust these times
|
# 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-----------
|
# -------------- AVAILABLE MODES END-----------
|
||||||
|
|
||||||
# Mode list for selecting between low bandwidth ( 500Hz ) and modes with higher bandwidth
|
# 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.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.rpt_ack_timeout_seconds = 4.5 # timeout for rpt frame acknowledges
|
||||||
self.transmission_timeout = 180 # transmission timeout in seconds
|
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
|
# Dictionary of functions and log messages used in process_data
|
||||||
# instead of a long series of if-elif-else statements.
|
# 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[3:4] = bytes([int(self.speed_level)])
|
||||||
ack_frame[4:8] = len(ARQ.rx_frame_buffer).to_bytes(4, byteorder="big")
|
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
|
# wait if we have a channel busy condition
|
||||||
channel_busy_timeout = time.time() + 5
|
if ModemParam.channel_busy:
|
||||||
while ModemParam.channel_busy and time.time() < channel_busy_timeout and not self.check_if_mode_fits_to_busy_slot():
|
self.channel_busy_handler()
|
||||||
threading.Event().wait(0.01)
|
|
||||||
|
|
||||||
# Transmit frame
|
# Transmit frame
|
||||||
self.enqueue_frame_for_tx([ack_frame], c2_mode=FREEDV_MODE.sig1.value)
|
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[1:2] = self.session_id
|
||||||
ack_frame[2:3] = helpers.snr_to_bytes(snr)
|
ack_frame[2:3] = helpers.snr_to_bytes(snr)
|
||||||
|
|
||||||
# wait while timeout not reached and our busy state is busy
|
# wait if we have a channel busy condition
|
||||||
channel_busy_timeout = time.time() + 5
|
if ModemParam.channel_busy:
|
||||||
while ModemParam.channel_busy and time.time() < channel_busy_timeout and not self.check_if_mode_fits_to_busy_slot():
|
self.channel_busy_handler()
|
||||||
threading.Event().wait(0.01)
|
|
||||||
|
|
||||||
# reset burst timeout in case we had to wait too long
|
|
||||||
self.burst_last_received = time.time() + channel_busy_timeout + 8
|
|
||||||
# Transmit frame
|
# 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)
|
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:
|
def send_retransmit_request_frame(self) -> None:
|
||||||
|
@ -607,22 +608,20 @@ class DATA:
|
||||||
# TODO Do we have to send ident frame?
|
# 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)
|
# 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
|
# wait if we have a channel busy condition
|
||||||
channel_busy_timeout = time.time() + 5
|
if ModemParam.channel_busy:
|
||||||
while ModemParam.channel_busy and time.time() < channel_busy_timeout and not self.check_if_mode_fits_to_busy_slot():
|
self.channel_busy_handler()
|
||||||
threading.Event().wait(0.01)
|
|
||||||
|
|
||||||
self.enqueue_frame_for_tx([nack_frame], c2_mode=FREEDV_MODE.sig1.value, copies=3, repeat_delay=0)
|
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
|
# reset burst timeout in case we had to wait too long
|
||||||
self.burst_last_received = time.time()
|
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"""
|
"""Build and send NACK frame for watchdog timeout"""
|
||||||
|
|
||||||
# increment nack counter for transmission stats
|
# increment nack counter for transmission stats
|
||||||
self.frame_nack_counter += 1
|
self.frame_nack_counter += 1
|
||||||
|
|
||||||
|
|
||||||
# we need to clear our rx burst buffer
|
# we need to clear our rx burst buffer
|
||||||
ARQ.rx_burst_buffer = []
|
ARQ.rx_burst_buffer = []
|
||||||
|
|
||||||
|
@ -631,16 +630,14 @@ class DATA:
|
||||||
nack_frame = bytearray(self.length_sig1_frame)
|
nack_frame = bytearray(self.length_sig1_frame)
|
||||||
nack_frame[:1] = bytes([FR_TYPE.BURST_NACK.value])
|
nack_frame[:1] = bytes([FR_TYPE.BURST_NACK.value])
|
||||||
nack_frame[1:2] = self.session_id
|
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[3:4] = bytes([int(self.speed_level)])
|
||||||
nack_frame[4:5] = bytes([int(tx_n_frames_per_burst)])
|
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")
|
nack_frame[5:9] = len(ARQ.rx_frame_buffer).to_bytes(4, byteorder="big")
|
||||||
|
|
||||||
|
# wait if we have a channel busy condition
|
||||||
# wait while timeout not reached and our busy state is busy
|
if ModemParam.channel_busy:
|
||||||
channel_busy_timeout = time.time() + 5 + 5
|
self.channel_busy_handler()
|
||||||
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)
|
|
||||||
|
|
||||||
# TRANSMIT NACK FRAME FOR BURST
|
# TRANSMIT NACK FRAME FOR BURST
|
||||||
self.enqueue_frame_for_tx([nack_frame], c2_mode=FREEDV_MODE.sig1.value, copies=1, repeat_delay=0)
|
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] = bytes([FR_TYPE.ARQ_SESSION_CLOSE.value])
|
||||||
disconnection_frame[1:2] = self.session_id
|
disconnection_frame[1:2] = self.session_id
|
||||||
disconnection_frame[2:5] = Station.dxcallsign_crc
|
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
|
# wait if we have a channel busy condition
|
||||||
channel_busy_timeout = time.time() + 5
|
if ModemParam.channel_busy:
|
||||||
while ModemParam.channel_busy and time.time() < channel_busy_timeout and not self.check_if_mode_fits_to_busy_slot():
|
self.channel_busy_handler()
|
||||||
threading.Event().wait(0.01)
|
|
||||||
|
|
||||||
self.enqueue_frame_for_tx([disconnection_frame], c2_mode=FREEDV_MODE.sig0.value, copies=3, repeat_delay=0)
|
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.arq_calculate_speed_level(snr)
|
||||||
|
|
||||||
self.data_channel_last_received = int(time.time()) + 6 + 6
|
# TIMING TEST
|
||||||
self.burst_last_received = int(time.time()) + 6 + 6
|
#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
|
# Create and send ACK frame
|
||||||
self.log.info("[Modem] ARQ | RX | SENDING ACK", finished=ARQ.arq_seconds_until_finish,
|
self.log.info("[Modem] ARQ | RX | SENDING ACK", finished=ARQ.arq_seconds_until_finish,
|
||||||
bytesperminute=ARQ.bytes_per_minute)
|
bytesperminute=ARQ.bytes_per_minute)
|
||||||
|
@ -1008,9 +1003,7 @@ class DATA:
|
||||||
mode_slots=mode_slots,
|
mode_slots=mode_slots,
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
return True
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def arq_calculate_speed_level(self, snr):
|
def arq_calculate_speed_level(self, snr):
|
||||||
current_speed_level = self.speed_level
|
current_speed_level = self.speed_level
|
||||||
|
@ -1041,6 +1034,13 @@ class DATA:
|
||||||
# Update modes we are listening to
|
# Update modes we are listening to
|
||||||
self.set_listening_modes(False, True, self.mode_list[self.speed_level])
|
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):
|
def arq_process_received_data_frame(self, data_frame, snr, signed):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -1742,8 +1742,6 @@ class DATA:
|
||||||
Station.dxcallsign = self.dxcallsign
|
Station.dxcallsign = self.dxcallsign
|
||||||
Station.dxcallsign_crc = helpers.get_crc_24(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(
|
self.log.info(
|
||||||
"[Modem] SESSION ["
|
"[Modem] SESSION ["
|
||||||
+ str(self.mycallsign, "UTF-8")
|
+ str(self.mycallsign, "UTF-8")
|
||||||
|
@ -1753,35 +1751,9 @@ class DATA:
|
||||||
state=ARQ.arq_session_state,
|
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:
|
if ModemParam.channel_busy:
|
||||||
self.log.warning("[Modem] Channel busy, waiting until free...")
|
self.channel_busy_handler()
|
||||||
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.open_session()
|
self.open_session()
|
||||||
|
|
||||||
|
@ -1995,6 +1967,10 @@ class DATA:
|
||||||
# we need to send disconnect frame before doing arq cleanup
|
# we need to send disconnect frame before doing arq cleanup
|
||||||
# we would lose our session id then
|
# we would lose our session id then
|
||||||
self.send_disconnect_frame()
|
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()
|
self.arq_cleanup()
|
||||||
|
|
||||||
def received_session_close(self, data_in: bytes):
|
def received_session_close(self, data_in: bytes):
|
||||||
|
@ -2162,114 +2138,19 @@ class DATA:
|
||||||
if ARQ.arq_session:
|
if ARQ.arq_session:
|
||||||
threading.Event().wait(2.5)
|
threading.Event().wait(2.5)
|
||||||
|
|
||||||
self.datachannel_timeout = False
|
# init arq state event
|
||||||
|
ARQ.arq_state_event = threading.Event()
|
||||||
# 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))
|
|
||||||
|
|
||||||
|
# finally start the channel opening procedure
|
||||||
self.arq_open_data_channel(mycallsign)
|
self.arq_open_data_channel(mycallsign)
|
||||||
|
|
||||||
# wait until data channel is open
|
# if data channel is open, return true else false
|
||||||
while not ARQ.arq_state and not self.datachannel_timeout and Modem.modem_state in ["BUSY"]:
|
if ARQ.arq_state_event.is_set():
|
||||||
threading.Event().wait(0.01)
|
# start arq transmission
|
||||||
|
|
||||||
if ARQ.arq_state:
|
|
||||||
self.arq_transmit(data_out, hmac_salt)
|
self.arq_transmit(data_out, hmac_salt)
|
||||||
return True
|
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:
|
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(
|
self.log.debug(
|
||||||
"[Modem] arq_open_data_channel:", transmission_uuid=self.transmission_uuid
|
"[Modem] arq_open_data_channel:", transmission_uuid=self.transmission_uuid
|
||||||
)
|
)
|
||||||
|
@ -2297,16 +2178,93 @@ class DATA:
|
||||||
+ str(self.dxcallsign, "UTF-8")
|
+ str(self.dxcallsign, "UTF-8")
|
||||||
+ "]"
|
+ "]"
|
||||||
)
|
)
|
||||||
self.datachannel_timeout = True
|
|
||||||
|
|
||||||
# Attempt to clean up the far-side, if it received the
|
# Attempt to clean up the far-side, if it received the
|
||||||
# open_session frame and can still hear us.
|
# open_session frame and can still hear us.
|
||||||
self.close_session()
|
self.close_session()
|
||||||
|
|
||||||
|
# otherwise return false
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Shouldn't get here...
|
def arq_open_data_channel(
|
||||||
return True
|
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):
|
def arq_received_data_channel_opener(self, data_in: bytes):
|
||||||
"""
|
"""
|
||||||
|
@ -2338,7 +2296,7 @@ class DATA:
|
||||||
# Station B already tries connecting to Station A.
|
# Station B already tries connecting to Station A.
|
||||||
# For avoiding ignoring repeated connect request in case of packet loss
|
# For avoiding ignoring repeated connect request in case of packet loss
|
||||||
# we are only ignoring packets in case we are ISS
|
# 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
|
return False
|
||||||
|
|
||||||
self.is_IRS = True
|
self.is_IRS = True
|
||||||
|
@ -2356,9 +2314,6 @@ class DATA:
|
||||||
irs=helpers.bool_to_string(self.is_IRS)
|
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")
|
frametype = int.from_bytes(bytes(data_in[:1]), "big")
|
||||||
# check if we received low bandwidth mode
|
# check if we received low bandwidth mode
|
||||||
# possible channel constellations
|
# possible channel constellations
|
||||||
|
@ -2406,29 +2361,14 @@ class DATA:
|
||||||
# initially set speed_level 0 in case of bad SNR and no matching mode
|
# initially set speed_level 0 in case of bad SNR and no matching mode
|
||||||
self.speed_level = 0
|
self.speed_level = 0
|
||||||
|
|
||||||
# TODO MOVE THIS TO arq_calculate_speed_level()
|
# calculate initial speed level in correlation to latest known SNR
|
||||||
# calculate speed level in correlation to latest known SNR
|
|
||||||
for i in range(len(self.mode_list)):
|
for i in range(len(self.mode_list)):
|
||||||
if ModemParam.snr >= self.snr_list[i]:
|
if ModemParam.snr >= self.snr_list[i]:
|
||||||
self.speed_level = i
|
self.speed_level = i
|
||||||
|
|
||||||
# calculate if speed level fits to busy condition
|
# check if speed level fits to busy condition
|
||||||
mode_name = codec2.FREEDV_MODE(self.mode_list[self.speed_level]).name
|
if not self.check_if_mode_fits_to_busy_slot():
|
||||||
mode_slots = codec2.FREEDV_MODE_USED_SLOTS[mode_name].value
|
|
||||||
if mode_slots in [ModemParam.channel_busy_slot]:
|
|
||||||
self.speed_level = 0
|
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
|
# Update modes we are listening to
|
||||||
self.set_listening_modes(True, True, self.mode_list[self.speed_level])
|
self.set_listening_modes(True, True, self.mode_list[self.speed_level])
|
||||||
|
@ -2457,6 +2397,7 @@ class DATA:
|
||||||
)
|
)
|
||||||
|
|
||||||
# Reset data_channel/burst timestamps
|
# Reset data_channel/burst timestamps
|
||||||
|
# TIMING TEST
|
||||||
self.data_channel_last_received = int(time.time())
|
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
|
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
|
# set start of transmission for our statistics
|
||||||
self.rx_start_of_transmission = time.time()
|
self.rx_start_of_transmission = time.time()
|
||||||
|
|
||||||
|
# TIMING TEST
|
||||||
# Reset data_channel/burst timestamps once again for avoiding running into timeout
|
# 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.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
|
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.time_list = self.time_list_high_bw
|
||||||
self.log.debug("[Modem] high bandwidth mode", modes=self.mode_list)
|
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.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.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'------'
|
Station.dxgrid = b'------'
|
||||||
helpers.add_to_heard_stations(
|
helpers.add_to_heard_stations(
|
||||||
Station.dxcallsign,
|
Station.dxcallsign,
|
||||||
|
@ -2562,13 +2505,15 @@ class DATA:
|
||||||
snr=ModemParam.snr,
|
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
|
ARQ.arq_state = True
|
||||||
|
# also set the ARQ event
|
||||||
|
ARQ.arq_state_event.set()
|
||||||
|
|
||||||
# Update data_channel timestamp
|
# Update data_channel timestamp
|
||||||
self.data_channel_last_received = int(time.time())
|
self.data_channel_last_received = int(time.time())
|
||||||
|
|
||||||
else:
|
else:
|
||||||
Modem.modem_state = "IDLE"
|
|
||||||
ARQ.arq_state = False
|
|
||||||
self.send_data_to_socket_queue(
|
self.send_data_to_socket_queue(
|
||||||
freedata="modem-message",
|
freedata="modem-message",
|
||||||
arq="transmission",
|
arq="transmission",
|
||||||
|
@ -2578,7 +2523,6 @@ class DATA:
|
||||||
dxcallsign=str(self.dxcallsign, 'UTF-8'),
|
dxcallsign=str(self.dxcallsign, 'UTF-8'),
|
||||||
irs=helpers.bool_to_string(self.is_IRS)
|
irs=helpers.bool_to_string(self.is_IRS)
|
||||||
)
|
)
|
||||||
# TODO We should display a message to this effect on the UI.
|
|
||||||
self.log.warning(
|
self.log.warning(
|
||||||
"[Modem] protocol version mismatch:",
|
"[Modem] protocol version mismatch:",
|
||||||
received=protocol_version,
|
received=protocol_version,
|
||||||
|
@ -2761,8 +2705,6 @@ class DATA:
|
||||||
"""
|
"""
|
||||||
self.log.warning("[Modem] Stopping transmission!")
|
self.log.warning("[Modem] Stopping transmission!")
|
||||||
|
|
||||||
Modem.modem_state = "IDLE"
|
|
||||||
ARQ.arq_state = False
|
|
||||||
self.send_data_to_socket_queue(
|
self.send_data_to_socket_queue(
|
||||||
freedata="modem-message",
|
freedata="modem-message",
|
||||||
arq="transmission",
|
arq="transmission",
|
||||||
|
@ -2778,7 +2720,6 @@ class DATA:
|
||||||
# TODO Not sure if we really need the session id when disconnecting
|
# TODO Not sure if we really need the session id when disconnecting
|
||||||
# stop_frame[1:2] = self.session_id
|
# stop_frame[1:2] = self.session_id
|
||||||
stop_frame[7:13] = helpers.callsign_to_bytes(self.mycallsign)
|
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.enqueue_frame_for_tx([stop_frame], c2_mode=FREEDV_MODE.sig1.value, copies=3, repeat_delay=0)
|
||||||
|
|
||||||
self.arq_cleanup()
|
self.arq_cleanup()
|
||||||
|
@ -2849,6 +2790,8 @@ class DATA:
|
||||||
else:
|
else:
|
||||||
self.enqueue_frame_for_tx([beacon_frame], c2_mode=FREEDV_MODE.sig0.value, copies=1,
|
self.enqueue_frame_for_tx([beacon_frame], c2_mode=FREEDV_MODE.sig0.value, copies=1,
|
||||||
repeat_delay=0)
|
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
|
self.beacon_interval_timer = time.time() + self.beacon_interval
|
||||||
while (
|
while (
|
||||||
|
@ -2926,9 +2869,7 @@ class DATA:
|
||||||
self.enqueue_frame_for_tx([cq_frame], c2_mode=FREEDV_MODE.fsk_ldpc_0.value)
|
self.enqueue_frame_for_tx([cq_frame], c2_mode=FREEDV_MODE.fsk_ldpc_0.value)
|
||||||
else:
|
else:
|
||||||
self.enqueue_frame_for_tx([cq_frame], c2_mode=FREEDV_MODE.sig0.value, copies=1, repeat_delay=0)
|
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:
|
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
|
# ------------ CALCULATE TRANSFER RATES
|
||||||
def calculate_transfer_rate_rx(
|
def calculate_transfer_rate_rx(
|
||||||
self, rx_start_of_transmission: float, receivedbytes: int
|
self, rx_start_of_transmission: float, receivedbytes: int
|
||||||
|
@ -3263,6 +3224,7 @@ class DATA:
|
||||||
ARQ.arq_session_state = "disconnected"
|
ARQ.arq_session_state = "disconnected"
|
||||||
ARQ.speed_list = []
|
ARQ.speed_list = []
|
||||||
ARQ.arq_state = False
|
ARQ.arq_state = False
|
||||||
|
ARQ.arq_state_event = threading.Event()
|
||||||
self.arq_file_transfer = False
|
self.arq_file_transfer = False
|
||||||
|
|
||||||
Beacon.beacon_pause = False
|
Beacon.beacon_pause = False
|
||||||
|
@ -3375,10 +3337,38 @@ class DATA:
|
||||||
if frames_left == 0:
|
if frames_left == 0:
|
||||||
frames_left = 1
|
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
|
# 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}")
|
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 <= time.time() or modem_error_state:
|
# 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(
|
self.log.warning(
|
||||||
"[Modem] Burst decoding error or timeout",
|
"[Modem] Burst decoding error or timeout",
|
||||||
attempt=self.n_retries_per_burst,
|
attempt=self.n_retries_per_burst,
|
||||||
|
@ -3389,7 +3379,7 @@ class DATA:
|
||||||
|
|
||||||
print(
|
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)}")
|
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:
|
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
|
# reset self.burst_last_received
|
||||||
self.burst_last_received = time.time() + self.time_list[self.speed_level] * frames_left
|
self.burst_last_received = time.time() + self.time_list[self.speed_level] * frames_left
|
||||||
|
@ -3398,8 +3388,8 @@ class DATA:
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
# reset self.burst_last_received
|
# reset self.burst_last_received counter
|
||||||
self.burst_last_received = time.time() + self.time_list[self.speed_level]
|
self.burst_last_received = time.time()
|
||||||
|
|
||||||
# reduce speed level if nack counter increased
|
# reduce speed level if nack counter increased
|
||||||
self.frame_received_counter = 0
|
self.frame_received_counter = 0
|
||||||
|
@ -3420,13 +3410,14 @@ class DATA:
|
||||||
self.set_listening_modes(True, True, self.mode_list[self.speed_level])
|
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?
|
# 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
|
# Update data_channel timestamp
|
||||||
# TODO Disabled this one for testing.
|
# TODO Disabled this one for testing.
|
||||||
# self.data_channel_last_received = time.time()
|
# self.data_channel_last_received = time.time()
|
||||||
self.n_retries_per_burst += 1
|
self.n_retries_per_burst += 1
|
||||||
else:
|
else:
|
||||||
|
# debugging output
|
||||||
# print((self.data_channel_last_received + self.time_list[self.speed_level])-time.time())
|
# print((self.data_channel_last_received + self.time_list[self.speed_level])-time.time())
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -190,10 +190,17 @@ if __name__ == "__main__":
|
||||||
PARSER.add_argument(
|
PARSER.add_argument(
|
||||||
"--tx-audio-level",
|
"--tx-audio-level",
|
||||||
dest="tx_audio_level",
|
dest="tx_audio_level",
|
||||||
default=50,
|
default=0,
|
||||||
help="Set the tx audio level at an early stage",
|
help="Set the tx audio level at an early stage",
|
||||||
type=int,
|
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(
|
PARSER.add_argument(
|
||||||
"--rx-buffer-size",
|
"--rx-buffer-size",
|
||||||
dest="rx_buffer_size",
|
dest="rx_buffer_size",
|
||||||
|
@ -261,6 +268,15 @@ if __name__ == "__main__":
|
||||||
help="Enable and set hmac message salt",
|
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()
|
ARGS = PARSER.parse_args()
|
||||||
|
|
||||||
# set save to folder state for allowing downloading files to local file system
|
# 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_fmin = ARGS.tuning_range_fmin
|
||||||
ModemParam.tuning_range_fmax = ARGS.tuning_range_fmax
|
ModemParam.tuning_range_fmax = ARGS.tuning_range_fmax
|
||||||
AudioParam.tx_audio_level = ARGS.tx_audio_level
|
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
|
Modem.respond_to_cq = ARGS.enable_respond_to_cq
|
||||||
ARQ.rx_buffer_size = ARGS.rx_buffer_size
|
ARQ.rx_buffer_size = ARGS.rx_buffer_size
|
||||||
Modem.enable_explorer = ARGS.enable_explorer
|
Modem.enable_explorer = ARGS.enable_explorer
|
||||||
|
@ -316,7 +333,7 @@ if __name__ == "__main__":
|
||||||
ModemParam.tx_delay = ARGS.tx_delay
|
ModemParam.tx_delay = ARGS.tx_delay
|
||||||
MeshParam.enable_protocol = ARGS.enable_mesh
|
MeshParam.enable_protocol = ARGS.enable_mesh
|
||||||
Modem.enable_hmac = ARGS.enable_hmac
|
Modem.enable_hmac = ARGS.enable_hmac
|
||||||
|
Modem.transmit_morse_identifier = ARGS.transmit_morse_identifier
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error("[DMN] Error reading config file", exception=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')
|
Modem.low_bandwidth_mode = conf.get('Modem', 'narrowband', 'False')
|
||||||
ModemParam.tuning_range_fmin = float(conf.get('Modem', 'fmin', '-50.0'))
|
ModemParam.tuning_range_fmin = float(conf.get('Modem', 'fmin', '-50.0'))
|
||||||
ModemParam.tuning_range_fmax = float(conf.get('Modem', 'fmax', '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')
|
Modem.respond_to_cq = conf.get('Modem', 'qrv', 'True')
|
||||||
ARQ.rx_buffer_size = int(conf.get('Modem', 'rx_buffer_size', '16'))
|
ARQ.rx_buffer_size = int(conf.get('Modem', 'rx_buffer_size', '16'))
|
||||||
Modem.enable_explorer = conf.get('Modem', 'explorer', 'False')
|
Modem.enable_explorer = conf.get('Modem', 'explorer', 'False')
|
||||||
|
@ -368,6 +386,8 @@ if __name__ == "__main__":
|
||||||
TCIParam.port = int(conf.get('TCI', 'tci_port', '50001'))
|
TCIParam.port = int(conf.get('TCI', 'tci_port', '50001'))
|
||||||
ModemParam.tx_delay = int(conf.get('Modem', 'tx_delay', '0'))
|
ModemParam.tx_delay = int(conf.get('Modem', 'tx_delay', '0'))
|
||||||
MeshParam.enable_protocol = conf.get('MESH','mesh_enable','False')
|
MeshParam.enable_protocol = conf.get('MESH','mesh_enable','False')
|
||||||
|
MeshParam.transmit_morse_identifier = conf.get('Modem','transmit_morse_identifier','False')
|
||||||
|
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
log.warning("[CFG] Error reading config file near", key=str(e))
|
log.warning("[CFG] Error reading config file near", key=str(e))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
@ -16,20 +16,17 @@ import sys
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from collections import deque
|
from collections import deque
|
||||||
import wave
|
|
||||||
import codec2
|
import codec2
|
||||||
import itertools
|
import itertools
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import sock
|
import sock
|
||||||
import sounddevice as sd
|
import sounddevice as sd
|
||||||
import static
|
|
||||||
from global_instances import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, Modem
|
from global_instances import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemParam, Station, Statistics, TCIParam, Modem
|
||||||
from static import FRAME_TYPE
|
from static import FRAME_TYPE
|
||||||
import structlog
|
import structlog
|
||||||
import ujson as json
|
import ujson as json
|
||||||
import tci
|
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, \
|
from queues import DATA_QUEUE_RECEIVED, MODEM_RECEIVED_QUEUE, MODEM_TRANSMIT_QUEUE, RIGCTLD_COMMAND_QUEUE, \
|
||||||
AUDIO_RECEIVED_QUEUE, AUDIO_TRANSMIT_QUEUE, MESH_RECEIVED_QUEUE
|
AUDIO_RECEIVED_QUEUE, AUDIO_TRANSMIT_QUEUE, MESH_RECEIVED_QUEUE
|
||||||
|
|
||||||
|
@ -80,6 +77,8 @@ class RF:
|
||||||
self.AUDIO_CHANNELS = 1
|
self.AUDIO_CHANNELS = 1
|
||||||
self.MODE = 0
|
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
|
# 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
|
||||||
|
@ -460,6 +459,7 @@ class RF:
|
||||||
# self.log.debug("[MDM] callback")
|
# self.log.debug("[MDM] callback")
|
||||||
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)
|
||||||
|
x = set_audio_volume(x, AudioParam.rx_audio_level)
|
||||||
|
|
||||||
# audio recording for debugging purposes
|
# audio recording for debugging purposes
|
||||||
if AudioParam.audio_record:
|
if AudioParam.audio_record:
|
||||||
|
@ -745,18 +745,7 @@ class RF:
|
||||||
)
|
)
|
||||||
start_of_transmission = time.time()
|
start_of_transmission = time.time()
|
||||||
|
|
||||||
txbuffer = cw.MorseCodePlayer().text_to_signal("DJ2LS-1")
|
txbuffer_out = 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
|
|
||||||
|
|
||||||
self.mod_out_locked = True
|
self.mod_out_locked = True
|
||||||
self.enqueue_modulation(txbuffer_out)
|
self.enqueue_modulation(txbuffer_out)
|
||||||
|
@ -808,6 +797,8 @@ class RF:
|
||||||
self.log.debug("[MDM] ON AIR TIME", time=transmission_time)
|
self.log.debug("[MDM] ON AIR TIME", time=transmission_time)
|
||||||
|
|
||||||
def enqueue_modulation(self, txbuffer_out):
|
def enqueue_modulation(self, txbuffer_out):
|
||||||
|
|
||||||
|
|
||||||
chunk_length = self.AUDIO_FRAMES_PER_BUFFER_TX # 4800
|
chunk_length = self.AUDIO_FRAMES_PER_BUFFER_TX # 4800
|
||||||
chunk = [
|
chunk = [
|
||||||
txbuffer_out[i: i + chunk_length]
|
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.log.debug("[MDM] mod out shorter than audio buffer", delta=delta)
|
||||||
self.modoutqueue.append(c)
|
self.modoutqueue.append(c)
|
||||||
|
|
||||||
|
|
||||||
def demodulate_audio(
|
def demodulate_audio(
|
||||||
self,
|
self,
|
||||||
audiobuffer: codec2.audio_buffer,
|
audiobuffer: codec2.audio_buffer,
|
||||||
|
@ -854,6 +844,7 @@ class RF:
|
||||||
:return: NIN from freedv instance
|
:return: NIN from freedv instance
|
||||||
:rtype: int
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
|
|
||||||
nbytes = 0
|
nbytes = 0
|
||||||
try:
|
try:
|
||||||
while self.stream.active:
|
while self.stream.active:
|
||||||
|
@ -871,14 +862,15 @@ class RF:
|
||||||
# 10 error decoding == NACK
|
# 10 error decoding == NACK
|
||||||
rx_status = codec2.api.freedv_get_rx_status(freedv)
|
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
|
# we need to disable this if in testmode as its causing problems with FIFO it seems
|
||||||
if not TESTMODE:
|
if not TESTMODE:
|
||||||
ModemParam.is_codec2_traffic = True
|
ModemParam.is_codec2_traffic = True
|
||||||
|
self.is_codec2_traffic_counter = self.is_codec2_traffic_cooldown
|
||||||
if not ModemParam.channel_busy:
|
if not ModemParam.channel_busy:
|
||||||
self.log.debug("[MDM] Setting channel_busy since codec2 data detected")
|
self.log.debug("[MDM] Setting channel_busy since codec2 data detected")
|
||||||
ModemParam.channel_busy=True
|
ModemParam.channel_busy=True
|
||||||
ModemParam.channel_busy_delay+=10
|
ModemParam.channel_busy_delay += 10
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
"[MDM] [demod_audio] modem state", mode=mode_name, rx_status=rx_status,
|
"[MDM] [demod_audio] modem state", mode=mode_name, rx_status=rx_status,
|
||||||
sync_flag=codec2.api.rx_sync_flags_to_text[rx_status]
|
sync_flag=codec2.api.rx_sync_flags_to_text[rx_status]
|
||||||
|
@ -886,6 +878,13 @@ class RF:
|
||||||
else:
|
else:
|
||||||
ModemParam.is_codec2_traffic = False
|
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:
|
if rx_status == 10:
|
||||||
state_buffer.append(rx_status)
|
state_buffer.append(rx_status)
|
||||||
|
|
||||||
|
@ -897,7 +896,6 @@ class RF:
|
||||||
# process commands only if Modem.listen = True
|
# process commands only if Modem.listen = True
|
||||||
if Modem.listen:
|
if Modem.listen:
|
||||||
|
|
||||||
|
|
||||||
# ignore data channel opener frames for avoiding toggle states
|
# ignore data channel opener frames for avoiding toggle states
|
||||||
# use case: opener already received, but ack got lost and we are receiving
|
# use case: opener already received, but ack got lost and we are receiving
|
||||||
# an opener again
|
# an opener again
|
||||||
|
@ -1139,7 +1137,7 @@ class RF:
|
||||||
def get_scatter(self, freedv: ctypes.c_void_p) -> None:
|
def get_scatter(self, freedv: ctypes.c_void_p) -> None:
|
||||||
"""
|
"""
|
||||||
Ask codec2 for data about the received signal and calculate the scatter plot.
|
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
|
:param freedv: codec2 instance to query
|
||||||
:type freedv: ctypes.c_void_p
|
:type freedv: ctypes.c_void_p
|
||||||
|
@ -1181,7 +1179,7 @@ class RF:
|
||||||
"""
|
"""
|
||||||
Ask codec2 for data about the received signal and calculate
|
Ask codec2 for data about the received signal and calculate
|
||||||
the signal-to-noise ratio.
|
the signal-to-noise ratio.
|
||||||
Side-effect: sets ModemParam.snr
|
Side effect: sets ModemParam.snr
|
||||||
|
|
||||||
:param freedv: codec2 instance to query
|
:param freedv: codec2 instance to query
|
||||||
:type freedv: ctypes.c_void_p
|
:type freedv: ctypes.c_void_p
|
||||||
|
@ -1227,7 +1225,7 @@ class RF:
|
||||||
def update_rig_data(self) -> None:
|
def update_rig_data(self) -> None:
|
||||||
"""
|
"""
|
||||||
Request information about the current state of the radio via hamlib
|
Request information about the current state of the radio via hamlib
|
||||||
Side-effect: sets
|
Side effect: sets
|
||||||
- HamlibParam.hamlib_frequency
|
- HamlibParam.hamlib_frequency
|
||||||
- HamlibParam.hamlib_mode
|
- HamlibParam.hamlib_mode
|
||||||
- HamlibParam.hamlib_bandwidth
|
- HamlibParam.hamlib_bandwidth
|
||||||
|
@ -1258,6 +1256,7 @@ class RF:
|
||||||
e=e,
|
e=e,
|
||||||
)
|
)
|
||||||
threading.Event().wait(1)
|
threading.Event().wait(1)
|
||||||
|
|
||||||
def calculate_fft(self) -> None:
|
def calculate_fft(self) -> None:
|
||||||
"""
|
"""
|
||||||
Calculate an average signal strength of the channel to assess
|
Calculate an average signal strength of the channel to assess
|
||||||
|
@ -1366,7 +1365,7 @@ class RF:
|
||||||
ModemParam.channel_busy_slot[slot] = False
|
ModemParam.channel_busy_slot[slot] = False
|
||||||
# increment slot
|
# increment slot
|
||||||
slot += 1
|
slot += 1
|
||||||
if (addDelay):
|
if addDelay:
|
||||||
# Limit delay counter to a maximum of 200. The higher this value,
|
# Limit delay counter to a maximum of 200. The higher this value,
|
||||||
# the longer we will wait until releasing state
|
# the longer we will wait until releasing state
|
||||||
ModemParam.channel_busy = True
|
ModemParam.channel_busy = True
|
||||||
|
@ -1460,31 +1459,39 @@ def get_bytes_per_frame(mode: int) -> int:
|
||||||
# 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: np.ndarray, dB: float) -> np.ndarray:
|
||||||
def set_audio_volume(datalist, volume: float) -> np.int16:
|
|
||||||
"""
|
"""
|
||||||
Scale values for the provided audio samples by volume,
|
Scale values for the provided audio samples by dB.
|
||||||
`volume` is clipped to the range of 0-200
|
|
||||||
|
|
||||||
:param datalist: Audio samples to scale
|
:param datalist: Audio samples to scale
|
||||||
:type datalist: NDArray[np.int16]
|
:type datalist: np.ndarray
|
||||||
:param volume: "Percentage" (0-200) to scale samples
|
:param dB: Decibels to scale samples, constrained to the range [-50, 50]
|
||||||
:type volume: float
|
:type dB: float
|
||||||
:return: Scaled audio samples
|
:return: Scaled audio samples
|
||||||
:rtype: np.int16
|
:rtype: np.ndarray
|
||||||
"""
|
"""
|
||||||
# make sure we have float as data type to avoid crash
|
|
||||||
try:
|
try:
|
||||||
volume = float(volume)
|
dB = float(dB)
|
||||||
except Exception as e:
|
except ValueError as e:
|
||||||
print(f"[MDM] changing audio volume failed with error: {e}")
|
print(f"[MDM] Changing audio volume failed with error: {e}")
|
||||||
volume = 100.0
|
dB = 0.0 # 0 dB means no change
|
||||||
|
|
||||||
# Clip volume provided to acceptable values
|
# Clip dB value to the range [-50, 50]
|
||||||
volume = np.clip(volume, 0, 200) # limit to max value of 255
|
dB = np.clip(dB, -30, 20)
|
||||||
# Scale samples by the ratio of volume / 100.0
|
|
||||||
data = np.fromstring(datalist, np.int16) * (volume / 100.0) # type: ignore
|
# Ensure datalist is an np.ndarray
|
||||||
return data.astype(np.int16)
|
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():
|
def get_modem_error_state():
|
||||||
|
|
|
@ -256,6 +256,12 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
|
||||||
else:
|
else:
|
||||||
self.modem_set_tx_audio_level(received_json)
|
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
|
# TRANSMIT TEST FRAME
|
||||||
if received_json["type"] == "set" and received_json["command"] == "send_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,
|
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):
|
def modem_set_send_test_frame(self, received_json):
|
||||||
try:
|
try:
|
||||||
DATA_QUEUE_TRANSMIT.put(["SEND_TEST_FRAME"])
|
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"))
|
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_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"))
|
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"))
|
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"))
|
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"))
|
enable_explorer = str(helpers.return_key_from_object("False", startparam, "enable_explorer"))
|
||||||
|
@ -1131,7 +1150,8 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
|
||||||
tx_delay,
|
tx_delay,
|
||||||
tci_ip,
|
tci_ip,
|
||||||
tci_port,
|
tci_port,
|
||||||
enable_mesh
|
enable_mesh,
|
||||||
|
rx_audio_level,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
command_response("start_modem", True)
|
command_response("start_modem", True)
|
||||||
|
@ -1345,7 +1365,8 @@ def send_modem_state():
|
||||||
"rf_level": str(HamlibParam.hamlib_rf),
|
"rf_level": str(HamlibParam.hamlib_rf),
|
||||||
"strength": str(HamlibParam.hamlib_strength),
|
"strength": str(HamlibParam.hamlib_strength),
|
||||||
"alc": str(HamlibParam.alc),
|
"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),
|
"audio_auto_tune": str(AudioParam.audio_auto_tune),
|
||||||
"speed_level": str(ARQ.arq_speed_level),
|
"speed_level": str(ARQ.arq_speed_level),
|
||||||
"mode": str(HamlibParam.hamlib_mode),
|
"mode": str(HamlibParam.hamlib_mode),
|
||||||
|
|
|
@ -11,6 +11,7 @@ from dataclasses import dataclass, field
|
||||||
from typing import List
|
from typing import List
|
||||||
import subprocess
|
import subprocess
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
import threading
|
||||||
|
|
||||||
|
|
||||||
# CHANNEL_STATE = 'RECEIVING_SIGNALLING'
|
# CHANNEL_STATE = 'RECEIVING_SIGNALLING'
|
||||||
|
@ -33,6 +34,7 @@ class ARQ:
|
||||||
arq_session_state: str = "disconnected" # can be: disconnected, disconnecting, connected, connecting, failed
|
arq_session_state: str = "disconnected" # can be: disconnected, disconnecting, connected, connecting, failed
|
||||||
arq_session: bool = False
|
arq_session: bool = False
|
||||||
arq_state: bool = False
|
arq_state: bool = False
|
||||||
|
arq_state_event: threading.Event = field(default_factory=threading.Event)
|
||||||
# ARQ PROTOCOL VERSION
|
# ARQ PROTOCOL VERSION
|
||||||
# v.5 - signalling frame uses datac0
|
# v.5 - signalling frame uses datac0
|
||||||
# v.6 - signalling frame uses datac13
|
# v.6 - signalling frame uses datac13
|
||||||
|
@ -48,7 +50,8 @@ class ARQ:
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class AudioParam:
|
class AudioParam:
|
||||||
tx_audio_level: int = 50
|
tx_audio_level: int = 0
|
||||||
|
rx_audio_level: int = 0
|
||||||
audio_input_devices = []
|
audio_input_devices = []
|
||||||
audio_output_devices = []
|
audio_output_devices = []
|
||||||
audio_input_device: int = -2
|
audio_input_device: int = -2
|
||||||
|
@ -114,10 +117,10 @@ class ModemParam:
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Station:
|
class Station:
|
||||||
mycallsign: bytes = b"AA0AA"
|
mycallsign: bytes = b"AA0AA-0"
|
||||||
mycallsign_crc: bytes = b"A"
|
mycallsign_crc: bytes = b"A"
|
||||||
dxcallsign: bytes = b"ZZ9YY"
|
dxcallsign: bytes = b"ZZ9YY-0"
|
||||||
dxcallsign_crc: bytes = b"A"
|
dxcallsign_crc: bytes = b"B"
|
||||||
mygrid: bytes = b""
|
mygrid: bytes = b""
|
||||||
dxgrid: bytes = b""
|
dxgrid: bytes = b""
|
||||||
ssid_list = [] # ssid list we are responding to
|
ssid_list = [] # ssid list we are responding to
|
||||||
|
@ -134,7 +137,7 @@ class TCIParam:
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Modem:
|
class Modem:
|
||||||
version = "0.11.1-alpha.3"
|
version = "0.11.2-alpha.4"
|
||||||
host: str = "0.0.0.0"
|
host: str = "0.0.0.0"
|
||||||
port: int = 3000
|
port: int = 3000
|
||||||
SOCKET_TIMEOUT: int = 1 # seconds
|
SOCKET_TIMEOUT: int = 1 # seconds
|
||||||
|
@ -149,6 +152,7 @@ class Modem:
|
||||||
heard_stations = []
|
heard_stations = []
|
||||||
listen: bool = True
|
listen: bool = True
|
||||||
enable_hmac: bool = True
|
enable_hmac: bool = True
|
||||||
|
transmit_morse_identifier: bool = False
|
||||||
|
|
||||||
# ------------
|
# ------------
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue