added rx audio level and moved from linear to dB

This commit is contained in:
DJ2LS 2023-10-31 15:29:33 +01:00
parent 3f49b63930
commit 5632310b8c
12 changed files with 118 additions and 39 deletions

View file

@ -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>

View file

@ -184,6 +184,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,

View file

@ -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",\

View file

@ -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);
@ -172,6 +174,7 @@ client.on("data", function (socketdata) {
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["audio_level"];
stateStore.rx_audio_level = data["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) {

View file

@ -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,
}; };
}); });

View file

@ -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("");
@ -159,6 +161,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,

View file

@ -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[26],
'auto_tune': data[19] 'auto_tune': data[19]
} }

View file

@ -501,6 +501,8 @@ class DAEMON:
if data[25] == "True": if data[25] == "True":
options.append("--morse") options.append("--morse")
options.append("--rx-audio-level")
options.append(data[26])
# safe data to config file # safe data to config file
config.write_entire_config(data) config.write_entire_config(data)

View file

@ -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",
@ -315,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
@ -367,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')

View file

@ -16,19 +16,16 @@ 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
@ -462,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:
@ -815,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,
@ -848,7 +845,6 @@ class RF:
:rtype: int :rtype: int
""" """
nbytes = 0 nbytes = 0
try: try:
while self.stream.active: while self.stream.active:
@ -874,7 +870,7 @@ class RF:
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]
@ -883,8 +879,6 @@ class RF:
ModemParam.is_codec2_traffic = False ModemParam.is_codec2_traffic = False
# decrement codec traffic counter for making state smoother # decrement codec traffic counter for making state smoother
print(f"{mode_name}: {self.is_codec2_traffic_counter}")
if self.is_codec2_traffic_counter > 0: if self.is_codec2_traffic_counter > 0:
self.is_codec2_traffic_counter -= 1 self.is_codec2_traffic_counter -= 1
ModemParam.is_codec2_traffic = True ModemParam.is_codec2_traffic = True
@ -902,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
@ -1144,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
@ -1186,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
@ -1232,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
@ -1263,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
@ -1465,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():

View file

@ -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"])

View file

@ -50,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