Merge pull request #940 from DJ2LS/develop

pull/978/head
DJ2LS 2025-04-16 12:36:19 +02:00 committed by GitHub
commit c481972953
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 530 additions and 1186 deletions

View File

@ -1,6 +1,6 @@
{
"name": "FreeDATA",
"version": "0.17.0-beta",
"version": "0.17.1-beta",
"description": "FreeDATA Client application for connecting to FreeDATA server",
"private": true,
"scripts": {

View File

@ -1,9 +1,22 @@
<script setup>
<script>
import { settingsStore as settings, onChange } from "../store/settingsStore.js";
import { setActivePinia } from "pinia";
import pinia from "../store/index";
// Set the active Pinia store
setActivePinia(pinia);
// Export methods and computed properties for use in the template
export default {
computed: {
settings() {
return settings;
},
},
methods: {
onChange,
},
};
</script>
<template>
@ -17,10 +30,71 @@ setActivePinia(pinia);
<div
class="alert alert-info"
class="alert alert-info d-none"
role="alert"
>
<strong><i class="bi bi-info-circle me-1" /></strong>{{ $t('settings.exp.info') }}
</div>
<div class="input-group input-group-sm mb-1">
<label class="input-group-text w-50 text-wrap">
{{ $t('settings.exp.enableringbuffer') }}
<button
type="button"
class="btn btn-link p-0 ms-2"
data-bs-toggle="tooltip"
:title="$t('settings.exp.enableringbuffer_help')"
>
<i class="bi bi-question-circle" />
</button>
</label>
<label class="input-group-text w-50">
<div class="form-check form-switch form-check-inline">
<input
id="enable_ring_bufferSwitch"
v-model="settings.remote.EXP.enable_ring_buffer"
class="form-check-input"
type="checkbox"
@change="onChange"
>
<label
class="form-check-label"
for="enable_ring_bufferSwitch"
>{{ $t('settings.enable') }}</label>
</div>
</label>
</div>
<div class="input-group input-group-sm mb-1">
<label class="input-group-text w-50 text-wrap">
{{ $t('settings.exp.enablevhf') }}
<button
type="button"
class="btn btn-link p-0 ms-2"
data-bs-toggle="tooltip"
:title="$t('settings.exp.enablevhf_help')"
>
<i class="bi bi-question-circle" />
</button>
</label>
<label class="input-group-text w-50">
<div class="form-check form-switch form-check-inline">
<input
id="enable_vhfSwitch"
v-model="settings.remote.EXP.enable_vhf"
class="form-check-input"
type="checkbox"
@change="onChange"
>
<label
class="form-check-label"
for="enable_vhfSwitch"
>{{ $t('settings.enable') }}</label>
</div>
</label>
</div>
</template>

File diff suppressed because it is too large Load Diff

View File

@ -304,7 +304,11 @@
},
"exp": {
"introduction": "Warnung: Diese Funktionen können unvollständig sein. Sie sind NUR für erfahrene Benutzer!",
"info": "Info: Noch keine Funktionen zum Testen veröffentlicht"
"info": "Info: Noch keine Funktionen zum Testen veröffentlicht",
"enableringbuffer": "Ringpuffer einschalten",
"enableringbuffer_help": "Aktivieren eines experimentellen Ringpuffers, der bei einer leistungsschwachen CPU effizienter sein kann",
"enablevhf": "UKW-Modus aktivieren -- derzeit nur CQ --",
"enablevhf_help": "Aktiviert den experimentellen UKW-Modus zum Testen"
},
"radio": {
"introduction": "Einstellungen für die Transceiversteuerung, einschließlich der Auswahl der Rig-Control Methode und der Konfiguration spezifischer Einstellungen.",

View File

@ -304,7 +304,11 @@
},
"exp": {
"introduction": "Warning: These features may be incomplete. They are for experienced users ONLY!",
"info": "Info: No features publishes for testing, yet"
"info": "Info: No features publishes for testing, yet",
"enableringbuffer": "Enable ring buffer",
"enableringbuffer_help": "Enable an experimental ring buffer which can be more efficient on low power CPU",
"enablevhf": "Enable VHF modes -- only CQ for now --",
"enablevhf_help": "Enable experimental VHF modes for testing and playing"
},
"radio": {
"introduction": "Rig Control related settings, including selecting your rig control method and configuring specific settings.",

View File

@ -93,6 +93,7 @@
"ping": "Ping",
"ping_help": "Manda una richiesta di ping a una stazione remota",
"newmessage_help": "Invia un nuovo messaggio a una stazione remota",
"startnewchat_help": "Inizia o continua una conversazione con un'altra stazione",
"heardstations": "Stazioni ricevute",
"time": "Tempo",
"freq": "Freq",
@ -110,6 +111,7 @@
"sendingping": "Invio PING...",
"callcq": "Chiamo CQ",
"cqcqcq": "CQ CQ CQ",
"cqcqcq_help": "Trasmetti un CQ a tutti",
"togglebeacon": "attiva/disattiva beacon",
"togglebeacon_help": "Attiva/disattiva la modalità beacon. Mentre invii un beacon, puoi ricevere una richiesta di ping e aprire un canale per scambio di dati. If viene aperto uno scambio di dati il beacon entra in pausa.",
"enablebeacon": "Attiva il beacon",
@ -142,6 +144,7 @@
"furtheroptions": "Altre opzioni",
"deletechat": "Cancella la chat",
"startnewchat": "Inizia una nuova chat",
"startnewchat2": "Inizia una nuova chat (prima digita il nominativo del destinatario)",
"newchatline1": "Digita il callsign di destinazione",
"newchatline2": "Digita il primo messaggio",
"newchatline3": "Click su INIZIA UNA NUOVA CHAT",
@ -150,7 +153,7 @@
"audiotuning": "Regolazione audio",
"audiotuninginfo": "Regola il livello audio. Valori in dB. Valore di default 0",
"testframe": "Test frame",
"testframetransmit": "Trasmetti ( 5s )",
"testframetransmit": "Trasmetti (5s)",
"audiotuningrxlevel": "Livello RX",
"audiotuningtxlevel": "Livello TX",
"audiotuningtransmitsine": "Trasmetti un segnale sinusoidale",
@ -167,7 +170,7 @@
"radiocontrol": "Controllo della radio",
"radiocontrolinfo": "Questo è solo una configurazione rapida! Per altre opzioni vai a configurazione/controllo radio",
"rigcontrolmethod": "Metodo di controllo della radio",
"rigcontrolmethoddisabled": "Disabilitato ( nessun controllo della radio; usalo con VOX)",
"rigcontrolmethoddisabled": "Disabilitato (nessun controllo della radio; usalo con VOX)",
"rigcontrolmethodserialptt": "PTT seriale con DTR/RTS",
"rigcontrolmethodrigctld": "Rigctld (Hamlib esterna)",
"rigcontrolmethodrigctldbundle": "Rigctld (Hamlib interna)",
@ -198,7 +201,11 @@
"entermessage_placeholder": "Messaggio - Invia con [Invio]",
"attempt": "tentativo",
"utc": "UTC",
"adif": "ADIF"
"adif": "ADIF",
"new": "nuovo",
"selectChat": "Seleziona o inizia una chat",
"noConversations": "Ancora nessuna conversazione",
"loadingMessages": "Caricamento messaggi..."
},
"settings": {
"enable": "Abilita",
@ -297,7 +304,9 @@
},
"exp": {
"introduction": "Attenzione: Queste funzioni possono essere incomplete. Sono SOLO per utenti esperti!",
"info": "Info: Non sono ancora state rese disponibili funzioni per il test"
"info": "Info: Non sono ancora state rese disponibili funzioni per il test",
"enableringbuffer": "Attiva il ring buffer",
"enableringbuffer_help": "Attiva un ring buffer sperimentale che può essere più efficiente su CPU di bassa potenza"
},
"radio": {
"introduction": "Parametri per il controllo della radio, compreso il metodo di controllo e le configurazioni specifiche del metodo.",

View File

@ -87,6 +87,10 @@ const defaultConfig = {
GUI: {
auto_run_browser: true,
},
EXP: {
enable_ring_buffer: false,
enable_vhf: false,
},
},
};

View File

@ -19,7 +19,7 @@ class CircularBuffer:
self.nbuffer = 0 # Number of samples stored.
self.lock = threading.Lock()
self.cond = threading.Condition(self.lock)
log.debug("[C2 ] Creating audio buffer", size=size)
log.debug("[BUF] Creating ring buffer", size=size)
def push(self, samples):
"""Push samples onto the buffer.

View File

@ -39,6 +39,8 @@ class FREEDV_MODE(Enum):
data_ofdm_500 = 21500
data_ofdm_1700 = 211700
data_ofdm_2438 = 2124381
data_vhf_1 = 201
#data_qam_2438 = 2124382
#qam16c2 = 22
@ -395,6 +397,17 @@ def open_instance(mode: int) -> ctypes.c_void_p:
),
ctypes.c_void_p,
)
elif mode in [FREEDV_MODE.data_vhf_1.value]:
fsk_custom = 9
custom_params = fsk_configurations[mode]
return ctypes.cast(
api.freedv_open_advanced(
fsk_custom,
ctypes.byref(custom_params),
),
ctypes.c_void_p,
)
else:
if mode not in [data_custom]:
return ctypes.cast(api.freedv_open(mode), ctypes.c_void_p)
@ -468,6 +481,17 @@ class FREEDV_ADVANCED(ctypes.Structure):
("config", ctypes.POINTER(OFDM_CONFIG))
]
class FREEDV_ADVANCED_FSK(ctypes.Structure):
"""Advanced structure for fsk and ofdm modes"""
_fields_ = [
("interleave_frames", ctypes.c_int),
("M", ctypes.c_int),
("Rs", ctypes.c_int),
("Fs", ctypes.c_int),
("first_tone", ctypes.c_int),
("tone_spacing", ctypes.c_int),
("codename", ctypes.c_char_p),
]
api.freedv_open_advanced.argtypes = [ctypes.c_int, ctypes.POINTER(FREEDV_ADVANCED)]
api.freedv_open_advanced.restype = ctypes.c_void_p
@ -540,6 +564,47 @@ def create_tx_uw(nuwbits, uw_sequence):
tx_uw_array[i] = uw_sequence[i]
return tx_uw_array
def create_default_fsk_config():
return FREEDV_ADVANCED(
interleave_frames = 0,
M = 2,
Rs = 100,
Fs = 8000,
first_tone = 1000,
tone_spacing = 200,
codename = "H_256_512_4".encode("utf-8"),
config = None
)
def get_centered_first_tone(config, center=1500):
"""
Calculate and return the first tone frequency so that the set of tones
is centered at the given center frequency.
The tones are assumed to be spaced equally based on config.tone_spacing,
and config.M is the total number of tones.
Args:
config: A configuration object with the following attributes:
- M (int): Total number of tones.
- tone_spacing (float): Spacing between consecutive tones.
center (float): Desired center frequency (default is 1500 Hz).
Returns:
float: The computed value for the first tone.
"""
return int(center - ((config.M - 1) * config.tone_spacing) // 2)
data_vhf_1_config = create_default_fsk_config()
data_vhf_1_config.interleave_frames = 1
data_vhf_1_config.M = 4
data_vhf_1_config.Rs = 200
data_vhf_1_config.tone_spacing = 400
data_vhf_1_config.codename = "H_256_512_4".encode("utf-8")
data_vhf_1_config.first_tone = get_centered_first_tone(data_vhf_1_config)
# ---------------- OFDM 500 Hz Bandwidth ---------------#
# DATAC13 # OFDM 200
@ -693,6 +758,8 @@ ofdm_configurations = {
FREEDV_MODE.data_ofdm_500.value: data_ofdm_500_config,
FREEDV_MODE.data_ofdm_1700.value: data_ofdm_1700_config,
FREEDV_MODE.data_ofdm_2438.value: data_ofdm_2438_config,
#FREEDV_MODE.data_qam_2438.value: data_qam_2438_config
#FREEDV_MODE.data_qam_2438.value: data_qam_2438_config,
}
fsk_configurations = {
FREEDV_MODE.data_vhf_1.value: data_vhf_1_config
}

View File

@ -116,7 +116,13 @@ class TxCommand:
Returns:
FREEDV_MODE: The transmission mode.
"""
return FREEDV_MODE.signalling
if self.config['EXP'].get('enable_vhf'):
mode = FREEDV_MODE.data_vhf_1
else:
mode = FREEDV_MODE.signalling
return mode
def make_modem_queue_item(self, mode, repeat, repeat_delay, frame):
"""Creates a dictionary representing a modem queue item.

View File

@ -1,5 +1,7 @@
from command import TxCommand
import codec2
from codec2 import FREEDV_MODE
class TestCommand(TxCommand):
"""Command for transmitting test frames.
@ -28,4 +30,9 @@ class TestCommand(TxCommand):
Returns:
codec2.FREEDV_MODE: The FreeDV mode for test frames.
"""
return codec2.FREEDV_MODE.data_ofdm_500
if self.config['EXP'].get('enable_vhf'):
mode = FREEDV_MODE.data_vhf_1
else:
mode = FREEDV_MODE.data_ofdm_500
return mode

View File

@ -66,3 +66,7 @@ adif_wavelog_api_key = API-KEY
[GUI]
auto_run_browser = True
[EXP]
enable_ring_buffer = False
enable_vhf = False

View File

@ -79,6 +79,10 @@ class CONFIG:
'GUI': {
'auto_run_browser': bool,
},
'EXP': {
'enable_ring_buffer': bool,
'enable_vhf': bool,
}
}

View File

@ -1,7 +1,7 @@
# Module for saving some constants
CONFIG_ENV_VAR = 'FREEDATA_CONFIG'
DEFAULT_CONFIG_FILE = 'config.ini'
MODEM_VERSION = "0.17.0-beta"
MODEM_VERSION = "0.17.1-beta"
API_VERSION = 3
LICENSE = 'GPL3.0'
DOCUMENTATION_URL = 'https://wiki.freedata.app'

View File

@ -8,6 +8,8 @@ import itertools
from audio_buffer import CircularBuffer
from codec2 import (FREEDV_MODE)
TESTMODE = False
class Demodulator():
@ -56,8 +58,14 @@ class Demodulator():
self.init_codec2()
# enable decoding of signalling modes
self.MODE_DICT[codec2.FREEDV_MODE.signalling.value]["decode"] = True
self.MODE_DICT[codec2.FREEDV_MODE.signalling_ack.value]["decode"] = True
if self.config['EXP'].get('enable_vhf'):
self.MODE_DICT[codec2.FREEDV_MODE.data_vhf_1.value]["decode"] = True
self.MODE_DICT[codec2.FREEDV_MODE.signalling.value]["decode"] = True
self.MODE_DICT[codec2.FREEDV_MODE.signalling_ack.value]["decode"] = True
else:
self.MODE_DICT[codec2.FREEDV_MODE.signalling.value]["decode"] = True
self.MODE_DICT[codec2.FREEDV_MODE.signalling_ack.value]["decode"] = True
def init_codec2(self):
@ -72,7 +80,6 @@ class Demodulator():
"""
# create codec2 instance
#c2instance = ctypes.cast(
c2instance = codec2.open_instance(mode)
# get bytes per frame
@ -81,13 +88,18 @@ class Demodulator():
)
# create byte out buffer
bytes_out = ctypes.create_string_buffer(bytes_per_frame)
# set initial frames per burst
codec2.api.freedv_set_frames_per_burst(c2instance, 1)
# init audio buffer
#audio_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER_RX)
audio_buffer = CircularBuffer(2*self.AUDIO_FRAMES_PER_BUFFER_RX)
if self.config['EXP'].get('enable_ring_buffer'):
self.log.debug("[MDM] [buffer]", enable_ring_buffer=True)
audio_buffer = CircularBuffer(2 * self.AUDIO_FRAMES_PER_BUFFER_RX)
else:
self.log.debug("[MDM] [buffer]", enable_ring_buffer=False)
audio_buffer = codec2.audio_buffer(2 * self.AUDIO_FRAMES_PER_BUFFER_RX)
@ -326,6 +338,11 @@ class Demodulator():
# signalling is always true
self.MODE_DICT[codec2.FREEDV_MODE.signalling.value]["decode"] = True
if self.config['EXP'].get('enable_vhf'):
self.MODE_DICT[codec2.FREEDV_MODE.data_vhf_1.value]["decode"] = True
# we only need to decode signalling ack as ISS or within P2P Connection
if is_arq_irs and not is_p2p_connection:
self.MODE_DICT[codec2.FREEDV_MODE.signalling_ack.value]["decode"] = False

View File

@ -1,6 +1,8 @@
import ctypes
import codec2
import structlog
from codec2 import FREEDV_MODE
from codec2 import FREEDV_ADVANCED_FSK
class Modulator:
@ -48,6 +50,8 @@ class Modulator:
self.data_ofdm_500_tx = codec2.open_instance(codec2.FREEDV_MODE.data_ofdm_500.value)
self.data_ofdm_1700_tx = codec2.open_instance(codec2.FREEDV_MODE.data_ofdm_1700.value)
self.data_ofdm_2438_tx = codec2.open_instance(codec2.FREEDV_MODE.data_ofdm_2438.value)
self.data_vhf_1 = codec2.open_instance(codec2.FREEDV_MODE.data_vhf_1.value)
#self.freedv_qam16c2_tx = codec2.open_instance(codec2.FREEDV_MODE.qam16c2.value)
#self.data_qam_2438_tx = codec2.open_instance(codec2.FREEDV_MODE.data_qam_2438.value)
@ -207,6 +211,7 @@ class Modulator:
codec2.FREEDV_MODE.data_ofdm_2438: self.data_ofdm_2438_tx,
#codec2.FREEDV_MODE.qam16c2: self.freedv_qam16c2_tx,
#codec2.FREEDV_MODE.data_qam_2438: self.freedv_data_qam_2438_tx,
codec2.FREEDV_MODE.data_vhf_1: self.data_vhf_1
}
if mode in mode_transition:
freedv = mode_transition[mode]
@ -234,9 +239,11 @@ class Modulator:
# Create modulation for all frames in the list
for frame in frames:
txbuffer = self.transmit_add_preamble(txbuffer, freedv)
if not self.config['EXP'].get('enable_vhf'):
txbuffer = self.transmit_add_preamble(txbuffer, freedv)
txbuffer = self.transmit_create_frame(txbuffer, freedv, frame)
txbuffer = self.transmit_add_postamble(txbuffer, freedv)
if not self.config['EXP'].get('enable_vhf'):
txbuffer = self.transmit_add_postamble(txbuffer, freedv)
# Add delay to end of frames
txbuffer = self.transmit_add_silence(txbuffer, repeat_delay)

View File

@ -1,331 +0,0 @@
#!/usr/bin/env python3
import structlog
import threading
import websocket
import time
class TCICtrl:
def __init__(self, audio_rx_q, hostname='127.0.0.1', port=50001):
# websocket.enableTrace(True)
self.log = structlog.get_logger("TCI")
self.audio_received_queue = audio_rx_q
self.hostname = str(hostname)
self.port = str(port)
self.ws = ''
tci_thread = threading.Thread(
target=self.connect,
name="TCI THREAD",
daemon=True,
)
tci_thread.start()
# flag if we're receiving a tx_chrono
self.tx_chrono = False
# audio related parameters, will be updated by tx chrono
self.sample_rate = None
self.format = None
self.codec = None
self.audio_length = None
self.crc = None
self.channel = None
self.frequency = None
self.bandwidth = None
self.mode = None
self.alc = None
self.meter = None
self.level = None
self.ptt = None
def connect(self):
self.log.info(
"[TCI] Starting TCI thread!", ip=self.hostname, port=self.port
)
self.ws = websocket.WebSocketApp(
f"ws://{self.hostname}:{self.port}",
on_open=self.on_open,
on_message=self.on_message,
on_error=self.on_error,
on_close=self.on_close,
)
self.ws.run_forever(reconnect=5) # Set dispatcher to automatic reconnection, 5 second reconnect delay if con>
# rel.signal(2, rel.abort) # Keyboard Interrupt
# rel.dispatch()
def on_message(self, ws, message):
# ready message
# we need to wait until radio is ready before we can push commands
if message == "ready;":
self.ws.send('audio_samplerate:8000;')
self.ws.send('audio_stream_channels:1;')
self.ws.send('audio_stream_sample_type:int16;')
self.ws.send('audio_stream_samples:1200;')
self.ws.send('audio_start:0;')
# tx chrono frame
if len(message) in {64}:
receiver = message[:4]
sample_rate = int.from_bytes(message[4:8], "little")
format = int.from_bytes(message[8:12], "little")
codec = int.from_bytes(message[12:16], "little")
crc = int.from_bytes(message[16:20], "little")
audio_length = int.from_bytes(message[20:24], "little")
type = int.from_bytes(message[24:28], "little")
channel = int.from_bytes(message[28:32], "little")
reserved1 = int.from_bytes(message[32:36], "little")
reserved2 = int.from_bytes(message[36:40], "little")
reserved3 = int.from_bytes(message[40:44], "little")
reserved4 = int.from_bytes(message[44:48], "little")
reserved5 = int.from_bytes(message[48:52], "little")
reserved6 = int.from_bytes(message[52:56], "little")
reserved7 = int.from_bytes(message[56:60], "little")
reserved8 = int.from_bytes(message[60:64], "little")
if type == 3:
self.tx_chrono = True
self.sample_rate = sample_rate
self.format = format
self.codec = codec
self.audio_length = audio_length
self.channel = channel
self.crc = crc
# audio frame
if len(message) in {576, 2464, 4160}:
# audio received
receiver = message[:4]
sample_rate = int.from_bytes(message[4:8], "little")
format = int.from_bytes(message[8:12], "little")
codec = int.from_bytes(message[12:16], "little")
crc = int.from_bytes(message[16:20], "little")
audio_length = int.from_bytes(message[20:24], "little")
type = int.from_bytes(message[24:28], "little")
channel = int.from_bytes(message[28:32], "little")
reserved1 = int.from_bytes(message[32:36], "little")
reserved2 = int.from_bytes(message[36:40], "little")
reserved3 = int.from_bytes(message[40:44], "little")
reserved4 = int.from_bytes(message[44:48], "little")
reserved5 = int.from_bytes(message[48:52], "little")
reserved6 = int.from_bytes(message[52:56], "little")
reserved7 = int.from_bytes(message[56:60], "little")
reserved8 = int.from_bytes(message[60:64], "little")
audio_data = message[64:]
self.audio_received_queue.put(audio_data)
if len(message)< 64:
# find frequency
if bytes(message, "utf-8").startswith(b"vfo:0,0,"):
splitted_message = message.split("vfo:0,0,")
self.frequency = splitted_message[1][:-1]
# find mode
if bytes(message, "utf-8").startswith(b"modulation:0,"):
splitted_message = message.split("modulation:0,")
self.mode = splitted_message[1][:-1]
# find ptt
#if bytes(message, "utf-8").startswith(b"trx:0,"):
# splitted_message = message.split("trx:0,")
# self.ptt = splitted_message[1][:-1]
# find bandwidth
#if message.startswith("rx_filter_band:0,"):
# splitted_message = message.split("rx_filter_band:0,")
# bandwidths = splitted_message[1]
# splitted_bandwidths = bandwidths.split(",")
# lower_bandwidth = int(splitted_bandwidths[0])
# upper_bandwidth = int(splitted_bandwidths[1][:-1])
# self.bandwidth = upper_bandwidth - lower_bandwidth
def on_error(self, ws, error):
self.log.error(
"[TCI] Error FreeDATA to TCI rig!", ip=self.hostname, port=self.port, e=error
)
def on_close(self, ws, close_status_code, close_msg):
self.log.warning(
"[TCI] Closed FreeDATA to TCI connection!", ip=self.hostname, port=self.port, statu=close_status_code,
msg=close_msg
)
def on_open(self, ws):
self.ws = ws
self.log.info(
"[TCI] Connected FreeDATA to TCI rig!", ip=self.hostname, port=self.port
)
self.log.info(
"[TCI] Init...", ip=self.hostname, port=self.port
)
def push_audio(self, data_out):
#print(data_out)
"""
# audio[:4] = receiver.to_bytes(4,byteorder='little', signed=False)
audio[4:8] = sample_rate.to_bytes(4, byteorder='little', signed=False)
audio[8:12] = format.to_bytes(4, byteorder='little', signed=False)
audio[12:16] = codec.to_bytes(4, byteorder='little', signed=False)
audio[16:20] = crc.to_bytes(4, byteorder='little', signed=False)
audio[20:24] = audio_length.to_bytes(4, byteorder='little', signed=False)
audio[24:28] = int(2).to_bytes(4, byteorder='little', signed=True)
audio[28:32] = channel.to_bytes(4, byteorder='little', signed=False)
audio[32:36] = reserved1.to_bytes(4, byteorder='little', signed=False)
audio[36:40] = reserved2.to_bytes(4, byteorder='little', signed=False)
audio[40:44] = reserved3.to_bytes(4, byteorder='little', signed=False)
audio[44:48] = reserved4.to_bytes(4, byteorder='little', signed=False)
audio[48:52] = reserved5.to_bytes(4, byteorder='little', signed=False)
audio[52:56] = reserved6.to_bytes(4, byteorder='little', signed=False)
audio[56:60] = reserved7.to_bytes(4, byteorder='little', signed=False)
audio[60:64] = reserved8.to_bytes(4, byteorder='little', signed=False)
"""
while not self.tx_chrono:
time.sleep(0.01)
#print(len(data_out))
#print(self.sample_rate)
#print(self.audio_length)
#print(self.channel)
#print(self.crc)
#print(self.codec)
#print(self.tx_chrono)
if self.tx_chrono:
#print("#############")
#print(len(data_out))
#print(len(bytes(data_out)))
#print("-------------")
audio = bytearray(4096 + 64)
audio[64:64 + len(bytes(data_out))] = bytes(data_out)
audio[4:8] = self.sample_rate.to_bytes(4, byteorder='little', signed=False)
# audio[8:12] = format.to_bytes(4,byteorder='little', signed=False)
audio[12:16] = self.codec.to_bytes(4, byteorder='little', signed=False)
audio[16:20] = self.crc.to_bytes(4, byteorder='little', signed=False)
audio[20:24] = self.audio_length.to_bytes(4, byteorder='little', signed=False)
audio[24:28] = int(2).to_bytes(4, byteorder='little', signed=False)
audio[28:32] = self.channel.to_bytes(4, byteorder='little', signed=False)
# audio[32:36] = reserved1.to_bytes(4,byteorder='little', signed=False)
# audio[36:40] = reserved2.to_bytes(4,byteorder='little', signed=False)
# audio[40:44] = reserved3.to_bytes(4,byteorder='little', signed=False)
# audio[44:48] = reserved4.to_bytes(4,byteorder='little', signed=False)
# audio[48:52] = reserved5.to_bytes(4,byteorder='little', signed=False)
# audio[52:56] = reserved6.to_bytes(4,byteorder='little', signed=False)
# audio[56:60] = reserved7.to_bytes(4,byteorder='little', signed=False)
self.ws.send(audio, websocket.ABNF.OPCODE_BINARY)
def set_ptt(self, state):
if state:
self.ws.send('trx:0,true,tci;')
else:
self.ws.send('trx:0,false;')
self.tx_chrono = False
def get_frequency(self):
""" """
self.ws.send('VFO:0,0;')
return self.frequency
def get_mode(self):
""" """
self.ws.send('MODULATION:0;')
return self.mode
def get_level(self):
""" """
return self.level
def get_alc(self):
""" """
return self.alc
def get_meter(self):
""" """
return self.meter
def get_bandwidth(self):
""" """
return self.bandwidth
def get_strength(self):
""" """
return None
def set_bandwidth(self):
""" """
return None
def set_mode(self, mode):
"""
Args:
mode:
Returns:
"""
self.ws.send(f'MODULATION:0,{str(mode)};')
return None
def set_frequency(self, frequency):
"""
Args:
frequency:
Returns:
"""
self.ws.send(f'VFO:0,0,{str(frequency)};')
return None
def get_status(self):
"""
Args:
mode:
Returns:
"""
return True
def get_ptt(self):
""" """
self.ws.send('trx:0;')
return self.ptt
def close_rig(self):
""" """
return
def wait_until_transmitted(self, txbuffer_out):
duration = len(txbuffer_out) / 8000
timestamp_to_sleep = time.time() + duration
self.log.debug("[MDM] TCI calculated duration", duration=duration)
tci_timeout_reached = False
while not tci_timeout_reached:
if self.radiocontrol in ["tci"]:
if time.time() < timestamp_to_sleep:
tci_timeout_reached = False
else:
tci_timeout_reached = True
threading.Event().wait(0.01)
# if we're transmitting FreeDATA signals, reset channel busy state