mirror of
https://github.com/DJ2LS/FreeDATA
synced 2024-05-14 08:04:33 +00:00
Merge branch 'develop' into dependabot/npm_and_yarn/gui/develop/vite-plugin-electron-0.15.5
This commit is contained in:
commit
210ec62ba3
36 changed files with 432 additions and 805 deletions
|
@ -138,7 +138,7 @@ const gridWidgets = [
|
||||||
),
|
),
|
||||||
new gridWidget(
|
new gridWidget(
|
||||||
grid_ptt,
|
grid_ptt,
|
||||||
{ x: 17, y: 8, w: 2, h: 8 },
|
{ x: 17, y: 8, w: 5, h: 12 },
|
||||||
"Tx/PTT indicator",
|
"Tx/PTT indicator",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
@ -146,7 +146,7 @@ const gridWidgets = [
|
||||||
),
|
),
|
||||||
new gridWidget(
|
new gridWidget(
|
||||||
grid_mycall,
|
grid_mycall,
|
||||||
{ x: 8, y: 40, w: 5, h: 8 },
|
{ x: 8, y: 40, w: 5, h: 15 },
|
||||||
"My callsign widget",
|
"My callsign widget",
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
|
|
@ -5,12 +5,6 @@ setActivePinia(pinia);
|
||||||
|
|
||||||
import { useStateStore } from "../../store/stateStore.js";
|
import { useStateStore } from "../../store/stateStore.js";
|
||||||
const state = useStateStore(pinia);
|
const state = useStateStore(pinia);
|
||||||
|
|
||||||
import { record_audio } from "../../js/sock";
|
|
||||||
|
|
||||||
function startStopRecordAudio() {
|
|
||||||
record_audio();
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="card w-100 h-100">
|
<div class="card w-100 h-100">
|
||||||
|
@ -34,11 +28,6 @@ function startStopRecordAudio() {
|
||||||
>Tune</a
|
>Tune</a
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
<a class="dropdown-item" @click="startStopRecordAudio" href="#"
|
|
||||||
>Record</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -42,7 +42,7 @@ var dxcallPing = ref("");
|
||||||
v-model="dxcallPing"
|
v-model="dxcallPing"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm btn-outline-secondary"
|
class="btn btn-sm btn-outline-secondary w-50"
|
||||||
id="sendPing"
|
id="sendPing"
|
||||||
type="button"
|
type="button"
|
||||||
data-bs-placement="bottom"
|
data-bs-placement="bottom"
|
||||||
|
@ -52,7 +52,7 @@ var dxcallPing = ref("");
|
||||||
title="Send a ping request to a remote station"
|
title="Send a ping request to a remote station"
|
||||||
@click="transmitPing()"
|
@click="transmitPing()"
|
||||||
>
|
>
|
||||||
Ping
|
Send ping
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
@ -76,7 +76,7 @@ var dxcallPing = ref("");
|
||||||
title="Send a CQ to the world"
|
title="Send a CQ to the world"
|
||||||
@click="sendModemCQ()"
|
@click="sendModemCQ()"
|
||||||
>
|
>
|
||||||
Call CQ
|
<h3>CQ CQ CQ</h3>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,12 +13,12 @@ function updateMyCall() {
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="d-flex justify-content-center align-items-center object-fill border rounded text-bg-light w-100 h-100"
|
class="d-flex justify-content-center align-items-center object-fill border rounded w-100 h-100"
|
||||||
>
|
>
|
||||||
<strong>
|
<h1>
|
||||||
{{ settingsStore.remote.STATION.mycall }}-{{
|
{{ settingsStore.remote.STATION.mycall }}-{{
|
||||||
settingsStore.remote.STATION.myssid
|
settingsStore.remote.STATION.myssid
|
||||||
}}
|
}}
|
||||||
</strong>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -9,8 +9,8 @@ const state = useStateStore(pinia);
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="d-flex justify-content-center align-items-center object-fill border rounded w-100 h-100"
|
class="d-flex justify-content-center align-items-center object-fill border rounded w-100 h-100"
|
||||||
:class="state.ptt_state === true ? 'text-bg-warning' : 'text-bg-light'"
|
:class="state.ptt_state === true ? 'text-bg-warning' : 'text-bg-white'"
|
||||||
>
|
>
|
||||||
TX
|
<h2>ON AIR</h2>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -5,12 +5,6 @@ setActivePinia(pinia);
|
||||||
|
|
||||||
import { useStateStore } from "../store/stateStore.js";
|
import { useStateStore } from "../store/stateStore.js";
|
||||||
const state = useStateStore(pinia);
|
const state = useStateStore(pinia);
|
||||||
|
|
||||||
import { record_audio } from "../js/sock";
|
|
||||||
|
|
||||||
function startStopRecordAudio() {
|
|
||||||
record_audio();
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="card mb-1">
|
<div class="card mb-1">
|
||||||
|
@ -33,18 +27,6 @@ function startStopRecordAudio() {
|
||||||
>
|
>
|
||||||
Tune
|
Tune
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
id="startStopRecording"
|
|
||||||
class="btn btn-sm"
|
|
||||||
@click="startStopRecordAudio()"
|
|
||||||
v-bind:class="{
|
|
||||||
'btn-outline-secondary': state.audio_recording == false,
|
|
||||||
'btn-secondary': state.audio_recording == true,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
Record
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-1 text-end">
|
<div class="col-1 text-end">
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -4,10 +4,6 @@ import pinia from "../store/index";
|
||||||
setActivePinia(pinia);
|
setActivePinia(pinia);
|
||||||
|
|
||||||
import { settingsStore as settings } from "../store/settingsStore.js";
|
import { settingsStore as settings } from "../store/settingsStore.js";
|
||||||
|
|
||||||
function saveSettings() {
|
|
||||||
saveSettingsToFile();
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -59,15 +55,13 @@ function saveSettings() {
|
||||||
maxlength="8"
|
maxlength="8"
|
||||||
aria-label="Input group"
|
aria-label="Input group"
|
||||||
aria-describedby="btnGroupAddon"
|
aria-describedby="btnGroupAddon"
|
||||||
v-model="settings.mycall"
|
v-model="settings.remote.STATION.mycall"
|
||||||
@input="saveSettings"
|
|
||||||
/>
|
/>
|
||||||
<select
|
<select
|
||||||
class="form-select form-select-sm"
|
class="form-select form-select-sm"
|
||||||
aria-label=".form-select-sm"
|
aria-label=".form-select-sm"
|
||||||
id="myCallSSID"
|
id="myCallSSID"
|
||||||
v-model="settings.myssid"
|
v-model="settings.remote.STATION.myssid"
|
||||||
@change="saveSettings"
|
|
||||||
>
|
>
|
||||||
<option selected value="0">0</option>
|
<option selected value="0">0</option>
|
||||||
<option value="1">1</option>
|
<option value="1">1</option>
|
||||||
|
@ -109,8 +103,7 @@ function saveSettings() {
|
||||||
maxlength="6"
|
maxlength="6"
|
||||||
aria-label="Input group"
|
aria-label="Input group"
|
||||||
aria-describedby="btnGroupAddon"
|
aria-describedby="btnGroupAddon"
|
||||||
v-model="settings.mygrid"
|
v-model="settings.remote.STATION.mygrid"
|
||||||
@input="saveSettings"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
import { startRigctld, stopRigctld } from "../js/deprecated_daemon";
|
|
||||||
|
|
||||||
|
|
||||||
import { setActivePinia } from "pinia";
|
import { setActivePinia } from "pinia";
|
||||||
import pinia from "../store/index";
|
import pinia from "../store/index";
|
||||||
|
@ -16,15 +14,15 @@ function startStopRigctld() {
|
||||||
switch (state.rigctld_started) {
|
switch (state.rigctld_started) {
|
||||||
case "stopped":
|
case "stopped":
|
||||||
|
|
||||||
settings.hamlib_deviceport = (<HTMLInputElement>document.getElementById("hamlib_deviceport")).value;
|
settings.remote.RADIO.serial_port = (<HTMLInputElement>document.getElementById("hamlib_deviceport")).value;
|
||||||
|
|
||||||
startRigctld();
|
//startRigctld();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "running":
|
case "running":
|
||||||
stopRigctld();
|
stopRigctld();
|
||||||
// dirty hack for calling this command twice, otherwise modem won't stop rigctld from time to time
|
// dirty hack for calling this command twice, otherwise modem won't stop rigctld from time to time
|
||||||
stopRigctld();
|
//stopRigctld();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
@ -34,17 +32,17 @@ function selectRadioControl() {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
switch (event.target.id) {
|
switch (event.target.id) {
|
||||||
case "list-rig-control-none-list":
|
case "list-rig-control-none-list":
|
||||||
settings.radiocontrol = "disabled";
|
settings.remote.RADIO.control = "disabled";
|
||||||
break;
|
break;
|
||||||
case "list-rig-control-rigctld-list":
|
case "list-rig-control-rigctld-list":
|
||||||
settings.radiocontrol = "rigctld";
|
settings.remote.RADIO.control = "rigctld";
|
||||||
break;
|
break;
|
||||||
case "list-rig-control-tci-list":
|
case "list-rig-control-tci-list":
|
||||||
settings.radiocontrol = "tci";
|
settings.remote.RADIO.control = "tci";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.log("default=!==");
|
console.log("default=!==");
|
||||||
settings.radiocontrol = "disabled";
|
settings.remote.RADIO.control = "disabled";
|
||||||
}
|
}
|
||||||
saveSettingsToFile();
|
saveSettingsToFile();
|
||||||
}
|
}
|
||||||
|
@ -84,7 +82,7 @@ alert("not yet implemented")
|
||||||
href="#list-rig-control-none"
|
href="#list-rig-control-none"
|
||||||
role="tab"
|
role="tab"
|
||||||
aria-controls="list-rig-control-none"
|
aria-controls="list-rig-control-none"
|
||||||
v-bind:class="{ active: settings.radiocontrol === 'disabled' }"
|
v-bind:class="{ active: settings.remote.RADIO.control === 'disabled' }"
|
||||||
@click="selectRadioControl()"
|
@click="selectRadioControl()"
|
||||||
>None</a
|
>None</a
|
||||||
>
|
>
|
||||||
|
@ -95,7 +93,7 @@ alert("not yet implemented")
|
||||||
href="#list-rig-control-rigctld"
|
href="#list-rig-control-rigctld"
|
||||||
role="tab"
|
role="tab"
|
||||||
aria-controls="list-rig-control-rigctld"
|
aria-controls="list-rig-control-rigctld"
|
||||||
v-bind:class="{ active: settings.radiocontrol === 'rigctld' }"
|
v-bind:class="{ active: settings.remote.RADIO.control === 'rigctld' }"
|
||||||
@click="selectRadioControl()"
|
@click="selectRadioControl()"
|
||||||
>Rigctld</a
|
>Rigctld</a
|
||||||
>
|
>
|
||||||
|
@ -106,7 +104,7 @@ alert("not yet implemented")
|
||||||
href="#list-rig-control-tci"
|
href="#list-rig-control-tci"
|
||||||
role="tab"
|
role="tab"
|
||||||
aria-controls="list-rig-control-tci"
|
aria-controls="list-rig-control-tci"
|
||||||
v-bind:class="{ active: settings.radiocontrol === 'tci' }"
|
v-bind:class="{ active: settings.remote.RADIO.control === 'tci' }"
|
||||||
@click="selectRadioControl()"
|
@click="selectRadioControl()"
|
||||||
>TCI</a
|
>TCI</a
|
||||||
>
|
>
|
||||||
|
@ -131,7 +129,7 @@ alert("not yet implemented")
|
||||||
<div class="tab-content" id="rig-control-nav-tabContent">
|
<div class="tab-content" id="rig-control-nav-tabContent">
|
||||||
<div
|
<div
|
||||||
class="tab-pane fade"
|
class="tab-pane fade"
|
||||||
v-bind:class="{ 'show active': settings.radiocontrol === 'disabled' }"
|
v-bind:class="{ 'show active': settings.remote.RADIO.control === 'disabled' }"
|
||||||
id="list-rig-control-none"
|
id="list-rig-control-none"
|
||||||
role="tabpanel"
|
role="tabpanel"
|
||||||
aria-labelledby="list-rig-control-none-list"
|
aria-labelledby="list-rig-control-none-list"
|
||||||
|
@ -145,7 +143,7 @@ alert("not yet implemented")
|
||||||
<div
|
<div
|
||||||
class="tab-pane fade"
|
class="tab-pane fade"
|
||||||
id="list-rig-control-rigctld"
|
id="list-rig-control-rigctld"
|
||||||
v-bind:class="{ 'show active': settings.radiocontrol === 'rigctld' }"
|
v-bind:class="{ 'show active': settings.remote.RADIO.control === 'rigctld' }"
|
||||||
role="tabpanel"
|
role="tabpanel"
|
||||||
aria-labelledby="list-rig-control-rigctld-list"
|
aria-labelledby="list-rig-control-rigctld-list"
|
||||||
>
|
>
|
||||||
|
@ -200,7 +198,7 @@ alert("not yet implemented")
|
||||||
<div
|
<div
|
||||||
class="tab-pane fade"
|
class="tab-pane fade"
|
||||||
id="list-rig-control-tci"
|
id="list-rig-control-tci"
|
||||||
v-bind:class="{ 'show active': settings.radiocontrol === 'tci' }"
|
v-bind:class="{ 'show active': settings.remote.RADIO.control === 'tci' }"
|
||||||
role="tabpanel"
|
role="tabpanel"
|
||||||
aria-labelledby="list-rig-control-tci-list"
|
aria-labelledby="list-rig-control-tci-list"
|
||||||
>
|
>
|
||||||
|
@ -214,7 +212,7 @@ alert("not yet implemented")
|
||||||
placeholder="tci IP"
|
placeholder="tci IP"
|
||||||
id="tci_ip"
|
id="tci_ip"
|
||||||
aria-label="Device IP"
|
aria-label="Device IP"
|
||||||
v-model="settings.tci_ip"
|
v-model="settings.remote.TCI.tci_ip"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -226,7 +224,7 @@ alert("not yet implemented")
|
||||||
placeholder="tci port"
|
placeholder="tci port"
|
||||||
id="tci_port"
|
id="tci_port"
|
||||||
aria-label="Device Port"
|
aria-label="Device Port"
|
||||||
v-model="settings.tci_port"
|
v-model="settings.remote.TCI.tci_port"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -219,10 +219,11 @@ function testHamlib() {
|
||||||
</div>
|
</div>
|
||||||
<!-- Audio Input Device -->
|
<!-- Audio Input Device -->
|
||||||
<div class="input-group input-group-sm mb-1">
|
<div class="input-group input-group-sm mb-1">
|
||||||
<label class="input-group-text w-25">Input device</label>
|
<label class="input-group-text w-50"
|
||||||
|
>Audio Input device</label
|
||||||
|
>
|
||||||
<select
|
<select
|
||||||
class="form-select form-select-sm"
|
class="form-select form-select-sm"
|
||||||
id="rx_audio"
|
|
||||||
aria-label=".form-select-sm"
|
aria-label=".form-select-sm"
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
v-model="settings.remote.AUDIO.input_device"
|
v-model="settings.remote.AUDIO.input_device"
|
||||||
|
@ -238,12 +239,13 @@ function testHamlib() {
|
||||||
|
|
||||||
<!-- Audio Output Device -->
|
<!-- Audio Output Device -->
|
||||||
<div class="input-group input-group-sm mb-1">
|
<div class="input-group input-group-sm mb-1">
|
||||||
<label class="input-group-text w-25">Output device</label>
|
<label class="input-group-text w-50"
|
||||||
|
>Audio Output device</label
|
||||||
|
>
|
||||||
<select
|
<select
|
||||||
class="form-select form-select-sm"
|
class="form-select form-select-sm"
|
||||||
id="tx_audio"
|
|
||||||
aria-label=".form-select-sm"
|
aria-label=".form-select-sm"
|
||||||
@change="setConfig"
|
@change="onChange"
|
||||||
v-model="settings.remote.AUDIO.output_device"
|
v-model="settings.remote.AUDIO.output_device"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
|
|
|
@ -9,108 +9,5 @@ import { settingsStore as settings } from "../store/settingsStore.js";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="input-group input-group-sm mb-1">
|
<h5>...soon...</h5>
|
||||||
<label class="input-group-text w-50">Enable "is typing"</label>
|
|
||||||
<label class="input-group-text w-50">
|
|
||||||
<div class="form-check form-switch form-check-inline">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
id="enable_is_writing"
|
|
||||||
@change="setConfig"
|
|
||||||
v-model="settings.enable_is_writing"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
<label class="form-check-label" for="GraphicsSwitch"
|
|
||||||
>Additional broadcast burst</label
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
|
||||||
<label class="input-group-text w-50">Allow requesting "user profile"</label>
|
|
||||||
<label class="input-group-text w-50">
|
|
||||||
<div class="form-check form-switch form-check-inline">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
id="enable_request_profile"
|
|
||||||
@change="setConfig"
|
|
||||||
v-model="settings.enable_request_profile"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
|
||||||
<label class="input-group-text w-50"
|
|
||||||
>Allow requesting "shared folder"</label
|
|
||||||
>
|
|
||||||
<label class="input-group-text w-50">
|
|
||||||
<div class="form-check form-switch form-check-inline">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
id="enable_request_shared_folder"
|
|
||||||
@change="setConfig"
|
|
||||||
v-model="settings.enable_request_shared_folder"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
|
||||||
<label class="input-group-text w-50">Shared folder path</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control w-50"
|
|
||||||
id="shared_folder_path"
|
|
||||||
@change="setConfig"
|
|
||||||
v-model="settings.shared_folder_path"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
|
||||||
<label class="input-group-text w-50"
|
|
||||||
>Enable auto retry on Beacon or Ping
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label class="input-group-text w-50">
|
|
||||||
<div class="form-check form-switch form-check-inline">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
id="enable_auto_retry"
|
|
||||||
@change="setConfig"
|
|
||||||
v-model="settings.enable_auto_retry"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
|
||||||
<span class="input-group-text w-50">message retry attempts</span>
|
|
||||||
<select
|
|
||||||
class="form-select form-select-sm w-50"
|
|
||||||
id="max_retry_attempts"
|
|
||||||
@change="setConfig"
|
|
||||||
v-model="settings.max_retry_attempts"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
<option value="1">1</option>
|
|
||||||
<option value="2">2</option>
|
|
||||||
<option value="3">3</option>
|
|
||||||
<option value="4">4</option>
|
|
||||||
<option value="5">5</option>
|
|
||||||
<option value="6">6</option>
|
|
||||||
<option value="7">7</option>
|
|
||||||
<option value="8">8</option>
|
|
||||||
<option value="9">9</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -9,24 +9,6 @@ import { settingsStore as settings } from "../store/settingsStore.js";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="input-group input-group-sm mb-1">
|
|
||||||
<label class="input-group-text w-50">Enable FSK mode</label>
|
|
||||||
<label class="input-group-text w-50">
|
|
||||||
<div class="form-check form-switch form-check-inline ms-2">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
id="fskModeSwitch"
|
|
||||||
@change="setConfig"
|
|
||||||
v-model="settings.remote.MODEM.enable_fsk"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
<label class="form-check-label" for="fskModeSwitch"
|
|
||||||
>not available, yet</label
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
<div class="input-group input-group-sm mb-1">
|
||||||
<label class="input-group-text w-50">Enable MESH protocol</label>
|
<label class="input-group-text w-50">Enable MESH protocol</label>
|
||||||
<label class="input-group-text w-50">
|
<label class="input-group-text w-50">
|
||||||
|
|
|
@ -70,7 +70,6 @@ import { audioInputOptions, audioOutputOptions } from "../js/deviceFormHelper";
|
||||||
<label class="input-group-text w-50">Audio Input device</label>
|
<label class="input-group-text w-50">Audio Input device</label>
|
||||||
<select
|
<select
|
||||||
class="form-select form-select-sm"
|
class="form-select form-select-sm"
|
||||||
id="rx_audio"
|
|
||||||
aria-label=".form-select-sm"
|
aria-label=".form-select-sm"
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
v-model="settings.remote.AUDIO.input_device"
|
v-model="settings.remote.AUDIO.input_device"
|
||||||
|
@ -86,7 +85,6 @@ import { audioInputOptions, audioOutputOptions } from "../js/deviceFormHelper";
|
||||||
<label class="input-group-text w-50">Audio Output device</label>
|
<label class="input-group-text w-50">Audio Output device</label>
|
||||||
<select
|
<select
|
||||||
class="form-select form-select-sm"
|
class="form-select form-select-sm"
|
||||||
id="tx_audio"
|
|
||||||
aria-label=".form-select-sm"
|
aria-label=".form-select-sm"
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
v-model="settings.remote.AUDIO.output_device"
|
v-model="settings.remote.AUDIO.output_device"
|
||||||
|
@ -178,20 +176,7 @@ import { audioInputOptions, audioOutputOptions } from "../js/deviceFormHelper";
|
||||||
<option value="3600">60 mins</option>
|
<option value="3600">60 mins</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group input-group-sm mb-1">
|
|
||||||
<label class="input-group-text w-50">Enable waterfall data</label>
|
|
||||||
<label class="input-group-text w-50">
|
|
||||||
<div class="form-check form-switch form-check-inline">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
id="fftSwitch"
|
|
||||||
v-model="settings.local.enable_fft"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label" for="fftSwitch">Waterfall</label>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
<div class="input-group input-group-sm mb-1">
|
||||||
<label class="input-group-text w-50">Enable 250Hz bandwidth mode</label>
|
<label class="input-group-text w-50">Enable 250Hz bandwidth mode</label>
|
||||||
<label class="input-group-text w-50">
|
<label class="input-group-text w-50">
|
||||||
|
|
|
@ -18,7 +18,12 @@ import { settingsStore as settings } from "../store/settingsStore.js";
|
||||||
import { displayToast } from "./popupHandler.js";
|
import { displayToast } from "./popupHandler.js";
|
||||||
|
|
||||||
//const FD = require("./src/js/freedata.js");
|
//const FD = require("./src/js/freedata.js");
|
||||||
import { btoa_FD, sortByProperty } from "./freedata.js";
|
import {
|
||||||
|
atob_FD,
|
||||||
|
btoa_FD,
|
||||||
|
sortByProperty,
|
||||||
|
sortByPropertyDesc,
|
||||||
|
} from "./freedata.js";
|
||||||
|
|
||||||
import { sendModemARQRaw } from "../js/api.js";
|
import { sendModemARQRaw } from "../js/api.js";
|
||||||
|
|
||||||
|
@ -859,27 +864,39 @@ export function newMessageReceived(message, protocol) {
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
console.log(protocol);
|
console.log(protocol);
|
||||||
|
|
||||||
|
var encoded_data = atob_FD(message);
|
||||||
|
var splitted_data = encoded_data.split(split_char);
|
||||||
|
|
||||||
|
// new message received
|
||||||
|
if (splitted_data[0] == "m") {
|
||||||
|
console.log(splitted_data);
|
||||||
|
message = splitted_data;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let newChatObj: messageDefaultObject = {
|
let newChatObj: messageDefaultObject = {
|
||||||
command: "msg",
|
command: "msg",
|
||||||
hmac_signed: protocol["hmac_signed"],
|
hmac_signed: false,
|
||||||
percent: 100,
|
percent: 100,
|
||||||
bytesperminute: protocol["bytesperminute"],
|
bytesperminute: 0,
|
||||||
is_new: true,
|
is_new: true,
|
||||||
_id: message[3],
|
_id: message[3],
|
||||||
timestamp: message[4],
|
timestamp: message[4],
|
||||||
dxcallsign: protocol["dxcallsign"],
|
dxcallsign: protocol["dxcall"],
|
||||||
dxgrid: protocol["dxgrid"],
|
dxgrid: "",
|
||||||
msg: message[5],
|
msg: message[5],
|
||||||
checksum: message[2],
|
checksum: message[2],
|
||||||
type: protocol["status"],
|
type: "received",
|
||||||
status: protocol["status"],
|
status: "received",
|
||||||
attempt: 1,
|
attempt: 1,
|
||||||
uuid: message[3],
|
uuid: message[3],
|
||||||
duration: protocol["duration"],
|
duration: 0,
|
||||||
nacks: protocol["nacks"],
|
nacks: 0,
|
||||||
speed_list: protocol["speed_list"],
|
speed_list: "[]",
|
||||||
_attachments: {
|
_attachments: {
|
||||||
[message[6]]: {
|
[message[6]]: {
|
||||||
content_type: message[7],
|
content_type: message[7],
|
||||||
|
|
|
@ -23,14 +23,8 @@ export function connectionFailed(endpoint, event) {
|
||||||
}
|
}
|
||||||
export function stateDispatcher(data) {
|
export function stateDispatcher(data) {
|
||||||
data = JSON.parse(data);
|
data = JSON.parse(data);
|
||||||
console.log(data);
|
//console.log(data);
|
||||||
|
if (data["type"] == "state-change" || data["type"] == "state") {
|
||||||
stateStore.modem_connection = "connected";
|
|
||||||
|
|
||||||
if (
|
|
||||||
data["freedata-message"] == "state-change" ||
|
|
||||||
data["freedata-message"] == "state"
|
|
||||||
) {
|
|
||||||
stateStore.channel_busy = data["channel_busy"];
|
stateStore.channel_busy = data["channel_busy"];
|
||||||
stateStore.is_codec2_traffic = data["is_codec2_traffic"];
|
stateStore.is_codec2_traffic = data["is_codec2_traffic"];
|
||||||
stateStore.is_modem_running = data["is_modem_running"];
|
stateStore.is_modem_running = data["is_modem_running"];
|
||||||
|
@ -46,98 +40,157 @@ export function stateDispatcher(data) {
|
||||||
//Reverse entries so most recent is first
|
//Reverse entries so most recent is first
|
||||||
stateStore.activities = Object.entries(data["activities"]).reverse();
|
stateStore.activities = Object.entries(data["activities"]).reverse();
|
||||||
build_HSL();
|
build_HSL();
|
||||||
/*
|
|
||||||
self.is_arq_state = False
|
|
||||||
self.is_arq_session = False
|
|
||||||
self.arq_session_state = 'disconnected'
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
stateStore.rx_buffer_length = data["rx_buffer_length"];
|
|
||||||
stateStore.frequency = data["frequency"];
|
|
||||||
stateStore.busy_state = data["modem_state"];
|
|
||||||
stateStore.arq_state = data["arq_state"];
|
|
||||||
stateStore.mode = data["mode"];
|
|
||||||
stateStore.bandwidth = data["bandwidth"];
|
|
||||||
stateStore.tx_audio_level = data["tx_audio_level"];
|
|
||||||
stateStore.rx_audio_level = data["rx_audio_level"];
|
|
||||||
// if audio level is different from config one, send new audio level to modem
|
|
||||||
//console.log(parseInt(stateStore.tx_audio_level))
|
|
||||||
//console.log(parseInt(settings.tx_audio_level))
|
|
||||||
if (
|
|
||||||
parseInt(stateStore.tx_audio_level) !==
|
|
||||||
parseInt(settings.tx_audio_level) &&
|
|
||||||
setTxAudioLevelOnce === true
|
|
||||||
) {
|
|
||||||
setTxAudioLevelOnce = false;
|
|
||||||
console.log(setTxAudioLevelOnce);
|
|
||||||
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.ptt_state = data["ptt_state"];
|
|
||||||
stateStore.speed_level = data["speed_level"];
|
|
||||||
stateStore.fft = JSON.parse(data["fft"]);
|
|
||||||
|
|
||||||
addDataToWaterfall(JSON.parse(data["fft"]));
|
|
||||||
|
|
||||||
if (data["scatter"].length > 0) {
|
|
||||||
stateStore.scatter = data["scatter"];
|
|
||||||
}
|
|
||||||
// s meter strength
|
|
||||||
stateStore.s_meter_strength_raw = data["strength"];
|
|
||||||
if (stateStore.s_meter_strength_raw == "") {
|
|
||||||
stateStore.s_meter_strength_raw = "Unsupported";
|
|
||||||
stateStore.s_meter_strength_percent = 0;
|
|
||||||
} else {
|
|
||||||
// https://www.moellerstudios.org/converting-amplitude-representations/
|
|
||||||
stateStore.s_meter_strength_percent = Math.round(
|
|
||||||
Math.pow(10, stateStore.s_meter_strength_raw / 20) * 100,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
stateStore.dbfs_level_percent = Math.round(
|
|
||||||
Math.pow(10, stateStore.dbfs_level / 20) * 100,
|
|
||||||
);
|
|
||||||
stateStore.dbfs_level = Math.round(stateStore.dbfs_level);
|
|
||||||
|
|
||||||
stateStore.arq_total_bytes = data["total_bytes"];
|
|
||||||
stateStore.heard_stations = data["stations"].sort(
|
|
||||||
sortByPropertyDesc("timestamp"),
|
|
||||||
);
|
|
||||||
stateStore.dxcallsign = data["dxcallsign"];
|
|
||||||
|
|
||||||
stateStore.audio_recording = data["audio_recording"];
|
|
||||||
|
|
||||||
stateStore.hamlib_status = data["hamlib_status"];
|
|
||||||
stateStore.alc = data["alc"];
|
|
||||||
stateStore.rf_level = data["rf_level"];
|
|
||||||
|
|
||||||
|
|
||||||
stateStore.arq_session_state = data["arq_session"];
|
|
||||||
stateStore.arq_state = data["arq_state"];
|
|
||||||
stateStore.arq_transmission_percent = data["arq_transmission_percent"];
|
|
||||||
stateStore.arq_seconds_until_finish = data["arq_seconds_until_finish"];
|
|
||||||
stateStore.arq_seconds_until_timeout = data["arq_seconds_until_timeout"];
|
|
||||||
stateStore.arq_seconds_until_timeout_percent =
|
|
||||||
(stateStore.arq_seconds_until_timeout / 180) * 100;
|
|
||||||
|
|
||||||
if (data["speed_list"].length > 0) {
|
|
||||||
prepareStatsDataForStore(data["speed_list"]);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function eventDispatcher(data) {
|
||||||
|
data = JSON.parse(data);
|
||||||
|
//console.info(data);
|
||||||
|
|
||||||
|
if (data["scatter"] !== undefined) {
|
||||||
|
stateStore.scatter = JSON.parse(data["scatter"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (data["ptt"]) {
|
||||||
|
case true:
|
||||||
|
case false:
|
||||||
|
// get ptt state as a first test
|
||||||
|
//console.warn("PTT state true")
|
||||||
|
stateStore.ptt_state = data.ptt;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (data["modem"]) {
|
||||||
|
case "started":
|
||||||
|
displayToast("success", "bi-arrow-left-right", "Modem started", 5000);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case "stopped":
|
||||||
|
displayToast("success", "bi-arrow-left-right", "Modem stopped", 5000);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case "restarted":
|
||||||
|
displayToast("secondary", "bi-bootstrap-reboot", "Modem restarted", 5000);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case "failed":
|
||||||
|
displayToast(
|
||||||
|
"danger",
|
||||||
|
"bi-bootstrap-reboot",
|
||||||
|
"Modem startup failed | bad config?",
|
||||||
|
5000,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = "";
|
||||||
|
|
||||||
|
switch (data["type"]) {
|
||||||
|
case "hello-client":
|
||||||
|
message = "Connected to server";
|
||||||
|
displayToast("success", "bi-ethernet", message, 5000);
|
||||||
|
stateStore.modem_connection = "connected";
|
||||||
|
return;
|
||||||
|
|
||||||
|
case "arq":
|
||||||
|
if (data["arq-transfer-outbound"]) {
|
||||||
|
switch (data["arq-transfer-outbound"].state) {
|
||||||
|
case "NEW":
|
||||||
|
message = `Type: ${data.type}, Session ID: ${data["arq-transfer-outbound"].session_id}, DXCall: ${data["arq-transfer-outbound"].dxcall}, Total Bytes: ${data["arq-transfer-outbound"].total_bytes}, State: ${data["arq-transfer-outbound"].state}`;
|
||||||
|
displayToast("success", "bi-check-circle", message, 5000);
|
||||||
|
return;
|
||||||
|
case "OPEN_SENT":
|
||||||
|
console.log("state OPEN_SENT needs to be implemented");
|
||||||
|
return;
|
||||||
|
|
||||||
|
case "INFO_SENT":
|
||||||
|
console.log("state INFO_SENT needs to be implemented");
|
||||||
|
return;
|
||||||
|
|
||||||
|
case "BURST_SENT":
|
||||||
|
message = `Type: ${data.type}, Session ID: ${data["arq-transfer-outbound"].session_id}, DXCall: ${data["arq-transfer-outbound"].dxcall}, Received Bytes: ${data["arq-transfer-outbound"].received_bytes}/${data["arq-transfer-outbound"].total_bytes}, State: ${data["arq-transfer-outbound"].state}`;
|
||||||
|
displayToast("info", "bi-info-circle", message, 5000);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case "ABORTING":
|
||||||
|
console.log("state ABORTING needs to be implemented");
|
||||||
|
return;
|
||||||
|
|
||||||
|
case "ABORTED":
|
||||||
|
message = `Type: ${data.type}, Session ID: ${
|
||||||
|
data["arq-transfer-outbound"].session_id
|
||||||
|
}, DXCall: ${data["arq-transfer-outbound"].dxcall}, Total Bytes: ${
|
||||||
|
data["arq-transfer-outbound"].total_bytes
|
||||||
|
}, Success: ${
|
||||||
|
data["arq-transfer-outbound"].success ? "Yes" : "No"
|
||||||
|
}, State: ${data["arq-transfer-outbound"].state}, Data: ${
|
||||||
|
data["arq-transfer-outbound"].data ? "Available" : "Not Available"
|
||||||
|
}`;
|
||||||
|
displayToast("warning", "bi-exclamation-triangle", message, 5000);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case "FAILED":
|
||||||
|
message = `Type: ${data.type}, Session ID: ${
|
||||||
|
data["arq-transfer-outbound"].session_id
|
||||||
|
}, DXCall: ${data["arq-transfer-outbound"].dxcall}, Total Bytes: ${
|
||||||
|
data["arq-transfer-outbound"].total_bytes
|
||||||
|
}, Success: ${
|
||||||
|
data["arq-transfer-outbound"].success ? "Yes" : "No"
|
||||||
|
}, State: ${data["arq-transfer-outbound"].state}, Data: ${
|
||||||
|
data["arq-transfer-outbound"].data ? "Available" : "Not Available"
|
||||||
|
}`;
|
||||||
|
displayToast("danger", "bi-x-octagon", message, 5000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data["arq-transfer-inbound"]) {
|
||||||
|
switch (data["arq-transfer-inbound"].state) {
|
||||||
|
case "NEW":
|
||||||
|
message = `Type: ${data.type}, Session ID: ${data["arq-transfer-inbound"].session_id}, DXCall: ${data["arq-transfer-inbound"].dxcall}, State: ${data["arq-transfer-inbound"].state}`;
|
||||||
|
displayToast("info", "bi-info-circle", message, 5000);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case "OPEN_ACK_SENT":
|
||||||
|
message = `Session ID: ${data["arq-transfer-inbound"].session_id}, DXCall: ${data["arq-transfer-inbound"].dxcall}, Total Bytes: ${data["arq-transfer-inbound"].total_bytes}, State: ${data["arq-transfer-inbound"].state}`;
|
||||||
|
displayToast("info", "bi-arrow-left-right", message, 5000);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case "INFO_ACK_SENT":
|
||||||
|
message = `Type: ${data.type}, Session ID: ${data["arq-transfer-inbound"].session_id}, DXCall: ${data["arq-transfer-inbound"].dxcall}, Received Bytes: ${data["arq-transfer-inbound"].received_bytes}/${data["arq-transfer-inbound"].total_bytes}, State: ${data["arq-transfer-inbound"].state}`;
|
||||||
|
displayToast("info", "bi-info-circle", message, 5000);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case "BURST_REPLY_SENT":
|
||||||
|
message = `Type: ${data.type}, Session ID: ${data["arq-transfer-inbound"].session_id}, DXCall: ${data["arq-transfer-inbound"].dxcall}, Received Bytes: ${data["arq-transfer-inbound"].received_bytes}/${data["arq-transfer-inbound"].total_bytes}, State: ${data["arq-transfer-inbound"].state}`;
|
||||||
|
displayToast("info", "bi-info-circle", message, 5000);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case "ENDED":
|
||||||
|
message = `Type: ${data.type}, Session ID: ${data["arq-transfer-inbound"].session_id}, DXCall: ${data["arq-transfer-inbound"].dxcall}, Received Bytes: ${data["arq-transfer-inbound"].received_bytes}/${data["arq-transfer-inbound"].total_bytes}, State: ${data["arq-transfer-inbound"].state}`;
|
||||||
|
displayToast("info", "bi-info-circle", message, 5000);
|
||||||
|
// Forward data to chat module
|
||||||
|
newMessageReceived(
|
||||||
|
data["arq-transfer-inbound"].data,
|
||||||
|
data["arq-transfer-inbound"],
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case "ABORTED":
|
||||||
|
console.log("state ABORTED needs to be implemented");
|
||||||
|
return;
|
||||||
|
|
||||||
|
case "FAILED":
|
||||||
|
message = `Type: ${data.type}, Session ID: ${data["arq-transfer-outbound"].session_id}, DXCall: ${data["arq-transfer-outbound"].dxcall}, Received Bytes: ${data["arq-transfer-outbound"].received_bytes}/${data["arq-transfer-outbound"].total_bytes}, State: ${data["arq-transfer-outbound"].state}`;
|
||||||
|
displayToast("info", "bi-info-circle", message, 5000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function build_HSL() {
|
function build_HSL() {
|
||||||
//Use data from activities to build HSL list
|
//Use data from activities to build HSL list
|
||||||
for (let i = 0; i < stateStore.activities.length; i++) {
|
for (let i = 0; i < stateStore.activities.length; i++) {
|
||||||
|
@ -173,323 +226,3 @@ function build_HSL() {
|
||||||
}
|
}
|
||||||
stateStore.heard_stations.sort((a, b) => b.timestamp - a.timestamp); // b - a for reverse sort
|
stateStore.heard_stations.sort((a, b) => b.timestamp - a.timestamp); // b - a for reverse sort
|
||||||
}
|
}
|
||||||
|
|
||||||
export function eventDispatcher(data) {
|
|
||||||
data = JSON.parse(data);
|
|
||||||
|
|
||||||
// break early if we received a dummy callsign
|
|
||||||
// thats a kind of hotfix, as long as the modem isnt handling this better
|
|
||||||
if (data["dxcallsign"] == "AA0AA-0" || data["dxcallsign"] == "ZZ9YY-0") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.info(data);
|
|
||||||
if (data["scatter"] !== undefined) {
|
|
||||||
//console.warn("Got scatter data!!!!");
|
|
||||||
stateStore.scatter = JSON.parse(data["scatter"]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (data["ptt"]) {
|
|
||||||
case true:
|
|
||||||
case false:
|
|
||||||
// get ptt state as a first test
|
|
||||||
//console.warn("PTT state true")
|
|
||||||
stateStore.ptt_state = data.ptt;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (data["freedata"]) {
|
|
||||||
case "modem-message":
|
|
||||||
switch (data["received"]) {
|
|
||||||
case "BEACON":
|
|
||||||
//Beacon received
|
|
||||||
displayToast(
|
|
||||||
"info",
|
|
||||||
"bi-broadcast",
|
|
||||||
"Beacon from " + data["dxcallsign"],
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
case "QRV":
|
|
||||||
//Qrv received
|
|
||||||
displayToast(
|
|
||||||
"success",
|
|
||||||
"bi-person-raised-hand",
|
|
||||||
"QRV from " + data["dxcallsign"],
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
case "PING":
|
|
||||||
//Qrv received
|
|
||||||
displayToast(
|
|
||||||
"success",
|
|
||||||
"bi-arrow-right-short",
|
|
||||||
"Ping request from " + data["dxcallsign"],
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
case "PING_ACK":
|
|
||||||
//Qrv received
|
|
||||||
displayToast(
|
|
||||||
"success",
|
|
||||||
"bi-arrow-left-right",
|
|
||||||
"Received ping-ack from " +
|
|
||||||
"callsignisbroken" +
|
|
||||||
" | " +
|
|
||||||
data["dxgrid"],
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "modem-event":
|
|
||||||
switch (data["event"]) {
|
|
||||||
case "start":
|
|
||||||
displayToast("success", "bi-arrow-left-right", "Modem started", 5000);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case "stop":
|
|
||||||
displayToast("success", "bi-arrow-left-right", "Modem stopped", 5000);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case "restart":
|
|
||||||
displayToast(
|
|
||||||
"secondary",
|
|
||||||
"bi-bootstrap-reboot",
|
|
||||||
"Modem restarted",
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case "failed":
|
|
||||||
displayToast(
|
|
||||||
"danger",
|
|
||||||
"bi-bootstrap-reboot",
|
|
||||||
"Modem startup failed | bad config?",
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
console.warn("Unknown event message received:");
|
|
||||||
console.warn(data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
var message = "";
|
|
||||||
if (data["freedata"] == "modem-message") {
|
|
||||||
|
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
switch (data["fec"]) {
|
|
||||||
case "is_writing":
|
|
||||||
// RX'd FECiswriting
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "broadcast":
|
|
||||||
// RX'd FEC BROADCAST
|
|
||||||
var encoded_data = atob_FD(data["data"]);
|
|
||||||
var splitted_data = encoded_data.split(split_char);
|
|
||||||
var messageArray = [];
|
|
||||||
if (splitted_data[0] == "m") {
|
|
||||||
messageArray.push(data);
|
|
||||||
console.log(data);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (data["cq"]) {
|
|
||||||
case "transmitting":
|
|
||||||
// CQ TRANSMITTING
|
|
||||||
displayToast("success", "bi-arrow-left-right", "Transmitting CQ", 5000);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "received":
|
|
||||||
// CQ RECEIVED
|
|
||||||
message = "CQ from " + data["dxcallsign"];
|
|
||||||
displayToast("success", "bi-person-arms-up", message, 5000);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (data["qrv"]) {
|
|
||||||
case "transmitting":
|
|
||||||
// QRV TRANSMITTING
|
|
||||||
displayToast(
|
|
||||||
"info",
|
|
||||||
"bi-person-raised-hand",
|
|
||||||
"Transmitting QRV ",
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "received":
|
|
||||||
// QRV RECEIVED
|
|
||||||
message = "QRV from " + data["dxcallsign"] + " | " + data["dxgrid"];
|
|
||||||
displayToast("success", "bi-person-raised-hand", message, 5000);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (data["beacon"]) {
|
|
||||||
case "transmitting":
|
|
||||||
// BEACON TRANSMITTING
|
|
||||||
displayToast(
|
|
||||||
"success",
|
|
||||||
"bi-broadcast-pin",
|
|
||||||
"Transmitting beacon",
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "received":
|
|
||||||
// BEACON RECEIVED
|
|
||||||
newBeaconReceived(data);
|
|
||||||
|
|
||||||
message = "Beacon from " + data["dxcallsign"] + " | " + data["dxgrid"];
|
|
||||||
displayToast("info", "bi-broadcast", message, 5000);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (data["ping"]) {
|
|
||||||
case "transmitting":
|
|
||||||
// PING TRANSMITTING
|
|
||||||
message = "Sending ping to " + data["dxcallsign"];
|
|
||||||
displayToast("success", "bi-arrow-right", message, 5000);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "received":
|
|
||||||
// PING RECEIVED
|
|
||||||
message =
|
|
||||||
"Ping request from " + data["dxcallsign"] + " | " + data["dxgrid"];
|
|
||||||
displayToast("success", "bi-arrow-right-short", message, 5000);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "acknowledge":
|
|
||||||
// PING ACKNOWLEDGE
|
|
||||||
message =
|
|
||||||
"Received ping-ack from " +
|
|
||||||
data["dxcallsign"] +
|
|
||||||
" | " +
|
|
||||||
data["dxgrid"];
|
|
||||||
displayToast("success", "bi-arrow-left-right", message, 5000);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ARQ SESSION && freedata == modem-message
|
|
||||||
if (data["arq"] == "session") {
|
|
||||||
switch (data["status"]) {
|
|
||||||
case "connecting":
|
|
||||||
// ARQ Open
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "connected":
|
|
||||||
// ARQ Opening
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "waiting":
|
|
||||||
// ARQ Opening
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "close":
|
|
||||||
// ARQ Closing
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "failed":
|
|
||||||
// ARQ Failed
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ARQ TRANSMISSION && freedata == modem-message
|
|
||||||
if (data["arq"] == "transmission") {
|
|
||||||
switch (data["status"]) {
|
|
||||||
case "opened":
|
|
||||||
// ARQ Open
|
|
||||||
message = "ARQ session opened: " + data["dxcallsign"];
|
|
||||||
displayToast("success", "bi-arrow-left-right", message, 5000);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "opening":
|
|
||||||
// ARQ Opening IRS/ISS
|
|
||||||
if (data["irs"] == "False") {
|
|
||||||
message = "ARQ session opening: " + data["dxcallsign"];
|
|
||||||
displayToast("info", "bi-arrow-left-right", message, 5000);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
message = "ARQ sesson request from: " + data["dxcallsign"];
|
|
||||||
displayToast("success", "bi-arrow-left-right", message, 5000);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "waiting":
|
|
||||||
// ARQ waiting
|
|
||||||
message = "Channel busy | ARQ protocol is waiting";
|
|
||||||
displayToast("warning", "bi-hourglass-split", message, 5000);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "receiving":
|
|
||||||
// ARQ RX
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "failed":
|
|
||||||
// ARQ TX Failed
|
|
||||||
if (data["reason"] == "protocol version missmatch") {
|
|
||||||
message = "Protocol version mismatch!";
|
|
||||||
displayToast("danger", "bi-chevron-bar-expand", message, 5000);
|
|
||||||
setStateFailed();
|
|
||||||
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
message = "Transmission failed";
|
|
||||||
displayToast("danger", "bi-x-octagon", message, 5000);
|
|
||||||
updateTransmissionStatus(data);
|
|
||||||
setStateFailed();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch (data["irs"]) {
|
|
||||||
case "True":
|
|
||||||
updateTransmissionStatus(data);
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
updateTransmissionStatus(data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "received":
|
|
||||||
// ARQ data received
|
|
||||||
|
|
||||||
console.log(data);
|
|
||||||
// we need to encode here to do a deep check for checking if file or message
|
|
||||||
//var encoded_data = atob(data['data'])
|
|
||||||
var encoded_data = atob_FD(data["data"]);
|
|
||||||
var splitted_data = encoded_data.split(split_char);
|
|
||||||
|
|
||||||
// new message received
|
|
||||||
if (splitted_data[0] == "m") {
|
|
||||||
console.log(splitted_data);
|
|
||||||
newMessageReceived(splitted_data, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "transmitting":
|
|
||||||
// ARQ transmitting
|
|
||||||
updateTransmissionStatus(data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "transmitted":
|
|
||||||
// ARQ transmitted
|
|
||||||
message = "Data transmitted";
|
|
||||||
displayToast("success", "bi-check-sqaure", message, 5000);
|
|
||||||
updateTransmissionStatus(data);
|
|
||||||
setStateSuccess();
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ function connect(endpoint, dispatcher) {
|
||||||
// handle data
|
// handle data
|
||||||
socket.addEventListener("message", function (event) {
|
socket.addEventListener("message", function (event) {
|
||||||
dispatcher(event.data);
|
dispatcher(event.data);
|
||||||
|
return;
|
||||||
});
|
});
|
||||||
|
|
||||||
// handle errors
|
// handle errors
|
||||||
|
|
|
@ -8,9 +8,6 @@ setActivePinia(pinia);
|
||||||
|
|
||||||
import { settingsStore as settings, onChange } from "../store/settingsStore.js";
|
import { settingsStore as settings, onChange } from "../store/settingsStore.js";
|
||||||
|
|
||||||
import { useAudioStore } from "../store/audioStore.js";
|
|
||||||
const audioStore = useAudioStore(pinia);
|
|
||||||
|
|
||||||
import { useStateStore } from "../store/stateStore";
|
import { useStateStore } from "../store/stateStore";
|
||||||
const stateStore = useStateStore(pinia);
|
const stateStore = useStateStore(pinia);
|
||||||
|
|
||||||
|
@ -89,11 +86,11 @@ export function loadSettings() {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (key == "wftheme") {
|
if (key == "wftheme") {
|
||||||
setColormap(config[key]);
|
setColormap();
|
||||||
}
|
}
|
||||||
if (key == "mycall") {
|
if (key == "mycall") {
|
||||||
settings.mycall = config[key].split("-")[0];
|
settings.remote.STATION.mycall = config[key].split("-")[0];
|
||||||
settings.myssid = config[key].split("-")[1];
|
settings.remote.STATION.myssid = config[key].split("-")[1];
|
||||||
} else {
|
} else {
|
||||||
settings[key] = config[key];
|
settings[key] = config[key];
|
||||||
}
|
}
|
||||||
|
@ -127,11 +124,14 @@ export function processModemConfig(data) {
|
||||||
let mycall = data[category][setting];
|
let mycall = data[category][setting];
|
||||||
if (mycall.includes("-")) {
|
if (mycall.includes("-")) {
|
||||||
const splittedCallsign = mycall.split("-");
|
const splittedCallsign = mycall.split("-");
|
||||||
settings.mycall = splittedCallsign[0]; // The part before the hyphen
|
settings.remote.STATION.mycall = splittedCallsign[0]; // The part before the hyphen
|
||||||
settings.myssid = parseInt(splittedCallsign[1], 10); // The part after the hyphen, converted to a number
|
settings.remote.STATION.myssid = parseInt(
|
||||||
|
splittedCallsign[1],
|
||||||
|
10,
|
||||||
|
); // The part after the hyphen, converted to a number
|
||||||
} else {
|
} else {
|
||||||
settings.mycall = mycall; // Use the original mycall if no SSID is present
|
settings.remote.STATION.mycall = mycall; // Use the original mycall if no SSID is present
|
||||||
settings.myssid = 0; // Default SSID if not provided
|
settings.remote.STATION.myssid = 0; // Default SSID if not provided
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
settings[variableName] = data[category][setting];
|
settings[variableName] = data[category][setting];
|
||||||
|
|
|
@ -46,7 +46,7 @@ class ARQSession():
|
||||||
self.id = None
|
self.id = None
|
||||||
|
|
||||||
def log(self, message, isWarning = False):
|
def log(self, message, isWarning = False):
|
||||||
msg = f"[{type(self).__name__}][state={self.state}]: {message}"
|
msg = f"[{type(self).__name__}][id={self.id}][state={self.state}]: {message}"
|
||||||
logger = self.logger.warn if isWarning else self.logger.info
|
logger = self.logger.warn if isWarning else self.logger.info
|
||||||
logger(msg)
|
logger(msg)
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,10 @@ class ARQSessionIRS(arq_session.ARQSession):
|
||||||
FRAME_TYPE.ARQ_STOP.value: 'send_stop_ack'
|
FRAME_TYPE.ARQ_STOP.value: 'send_stop_ack'
|
||||||
},
|
},
|
||||||
IRS_State.ABORTED: {
|
IRS_State.ABORTED: {
|
||||||
FRAME_TYPE.ARQ_STOP.value: 'send_stop_ack'
|
FRAME_TYPE.ARQ_STOP.value: 'send_stop_ack',
|
||||||
|
FRAME_TYPE.ARQ_SESSION_OPEN.value: 'send_open_ack',
|
||||||
|
FRAME_TYPE.ARQ_SESSION_INFO.value: 'send_info_ack',
|
||||||
|
FRAME_TYPE.ARQ_BURST_FRAME.value: 'receive_data',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,14 +102,16 @@ class ARQSessionIRS(arq_session.ARQSession):
|
||||||
thread_wait.start()
|
thread_wait.start()
|
||||||
|
|
||||||
def send_open_ack(self, open_frame):
|
def send_open_ack(self, open_frame):
|
||||||
|
self.event_manager.send_arq_session_new(
|
||||||
|
False, self.id, self.dxcall, 0, self.state.name)
|
||||||
ack_frame = self.frame_factory.build_arq_session_open_ack(
|
ack_frame = self.frame_factory.build_arq_session_open_ack(
|
||||||
self.id,
|
self.id,
|
||||||
self.dxcall,
|
self.dxcall,
|
||||||
self.version,
|
self.version,
|
||||||
self.snr[0])
|
self.snr[0], flag_abort=self.abort)
|
||||||
self.launch_transmit_and_wait(ack_frame, self.TIMEOUT_CONNECT, mode=FREEDV_MODE.signalling)
|
self.launch_transmit_and_wait(ack_frame, self.TIMEOUT_CONNECT, mode=FREEDV_MODE.signalling)
|
||||||
self.set_state(IRS_State.OPEN_ACK_SENT)
|
if not self.abort:
|
||||||
|
self.set_state(IRS_State.OPEN_ACK_SENT)
|
||||||
|
|
||||||
def send_info_ack(self, info_frame):
|
def send_info_ack(self, info_frame):
|
||||||
# Get session info from ISS
|
# Get session info from ISS
|
||||||
|
@ -125,7 +130,8 @@ class ARQSessionIRS(arq_session.ARQSession):
|
||||||
self.id, self.total_crc, self.snr[0],
|
self.id, self.total_crc, self.snr[0],
|
||||||
self.speed_level, self.frames_per_burst, flag_abort=self.abort)
|
self.speed_level, self.frames_per_burst, flag_abort=self.abort)
|
||||||
self.launch_transmit_and_wait(info_ack, self.TIMEOUT_CONNECT, mode=FREEDV_MODE.signalling)
|
self.launch_transmit_and_wait(info_ack, self.TIMEOUT_CONNECT, mode=FREEDV_MODE.signalling)
|
||||||
self.set_state(IRS_State.INFO_ACK_SENT)
|
if not self.abort:
|
||||||
|
self.set_state(IRS_State.INFO_ACK_SENT)
|
||||||
|
|
||||||
|
|
||||||
def process_incoming_data(self, frame):
|
def process_incoming_data(self, frame):
|
||||||
|
@ -160,6 +166,8 @@ class ARQSessionIRS(arq_session.ARQSession):
|
||||||
self.id, self.received_bytes,
|
self.id, self.received_bytes,
|
||||||
self.speed_level, self.frames_per_burst, self.snr[0], flag_abort=self.abort)
|
self.speed_level, self.frames_per_burst, self.snr[0], flag_abort=self.abort)
|
||||||
|
|
||||||
|
self.set_decode_mode()
|
||||||
|
|
||||||
# increase ack counter
|
# increase ack counter
|
||||||
# self.transmitted_acks += 1
|
# self.transmitted_acks += 1
|
||||||
self.set_state(IRS_State.BURST_REPLY_SENT)
|
self.set_state(IRS_State.BURST_REPLY_SENT)
|
||||||
|
|
|
@ -52,8 +52,9 @@ class ARQSessionISS(arq_session.ARQSession):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, config: dict, modem, dxcall: str, data: bytearray):
|
def __init__(self, config: dict, modem, dxcall: str, data: bytearray, state_manager):
|
||||||
super().__init__(config, modem, dxcall)
|
super().__init__(config, modem, dxcall)
|
||||||
|
self.state_manager = state_manager
|
||||||
self.data = data
|
self.data = data
|
||||||
self.data_crc = ''
|
self.data_crc = ''
|
||||||
|
|
||||||
|
@ -61,11 +62,18 @@ class ARQSessionISS(arq_session.ARQSession):
|
||||||
|
|
||||||
self.state = ISS_State.NEW
|
self.state = ISS_State.NEW
|
||||||
self.id = self.generate_id()
|
self.id = self.generate_id()
|
||||||
|
|
||||||
self.frame_factory = data_frame_factory.DataFrameFactory(self.config)
|
self.frame_factory = data_frame_factory.DataFrameFactory(self.config)
|
||||||
|
|
||||||
def generate_id(self):
|
def generate_id(self):
|
||||||
return random.randint(1,255)
|
while True:
|
||||||
|
random_int = random.randint(1,255)
|
||||||
|
if random_int not in self.state_manager.arq_iss_sessions:
|
||||||
|
return random_int
|
||||||
|
if len(self.state_manager.arq_iss_sessions) >= 255:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def transmit_wait_and_retry(self, frame_or_burst, timeout, retries, mode):
|
def transmit_wait_and_retry(self, frame_or_burst, timeout, retries, mode):
|
||||||
while retries > 0:
|
while retries > 0:
|
||||||
self.event_frame_received = threading.Event()
|
self.event_frame_received = threading.Event()
|
||||||
|
@ -88,6 +96,8 @@ class ARQSessionISS(arq_session.ARQSession):
|
||||||
twr.start()
|
twr.start()
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
|
self.event_manager.send_arq_session_new(
|
||||||
|
True, self.id, self.dxcall, len(self.data), self.state.name)
|
||||||
session_open_frame = self.frame_factory.build_arq_session_open(self.dxcall, self.id)
|
session_open_frame = self.frame_factory.build_arq_session_open(self.dxcall, self.id)
|
||||||
self.launch_twr(session_open_frame, self.TIMEOUT_CONNECT_ACK, self.RETRIES_CONNECT, mode=FREEDV_MODE.signalling)
|
self.launch_twr(session_open_frame, self.TIMEOUT_CONNECT_ACK, self.RETRIES_CONNECT, mode=FREEDV_MODE.signalling)
|
||||||
self.set_state(ISS_State.OPEN_SENT)
|
self.set_state(ISS_State.OPEN_SENT)
|
||||||
|
@ -98,7 +108,12 @@ class ARQSessionISS(arq_session.ARQSession):
|
||||||
self.frames_per_burst = frame['frames_per_burst']
|
self.frames_per_burst = frame['frames_per_burst']
|
||||||
self.log(f"Frames per burst set to {self.frames_per_burst}")
|
self.log(f"Frames per burst set to {self.frames_per_burst}")
|
||||||
|
|
||||||
def send_info(self, frame):
|
def send_info(self, irs_frame):
|
||||||
|
# check if we received an abort flag
|
||||||
|
if irs_frame["flag"]["ABORT"]:
|
||||||
|
self.transmission_aborted(irs_frame)
|
||||||
|
return
|
||||||
|
|
||||||
info_frame = self.frame_factory.build_arq_session_info(self.id, len(self.data),
|
info_frame = self.frame_factory.build_arq_session_info(self.id, len(self.data),
|
||||||
helpers.get_crc_32(self.data),
|
helpers.get_crc_32(self.data),
|
||||||
self.snr[0])
|
self.snr[0])
|
||||||
|
@ -116,6 +131,7 @@ class ARQSessionISS(arq_session.ARQSession):
|
||||||
self.event_manager.send_arq_session_progress(
|
self.event_manager.send_arq_session_progress(
|
||||||
True, self.id, self.dxcall, self.confirmed_bytes, len(self.data), self.state.name)
|
True, self.id, self.dxcall, self.confirmed_bytes, len(self.data), self.state.name)
|
||||||
|
|
||||||
|
# check if we received an abort flag
|
||||||
if irs_frame["flag"]["ABORT"]:
|
if irs_frame["flag"]["ABORT"]:
|
||||||
self.transmission_aborted(irs_frame)
|
self.transmission_aborted(irs_frame)
|
||||||
return
|
return
|
||||||
|
@ -173,5 +189,8 @@ class ARQSessionISS(arq_session.ARQSession):
|
||||||
def transmission_aborted(self, irs_frame):
|
def transmission_aborted(self, irs_frame):
|
||||||
self.log("session aborted")
|
self.log("session aborted")
|
||||||
self.set_state(ISS_State.ABORTED)
|
self.set_state(ISS_State.ABORTED)
|
||||||
|
# break actual retries
|
||||||
|
self.event_frame_received.set()
|
||||||
|
|
||||||
self.event_manager.send_arq_session_finished(
|
self.event_manager.send_arq_session_finished(
|
||||||
True, self.id, self.dxcall, len(self.data), False, self.state.name)
|
True, self.id, self.dxcall, len(self.data), False, self.state.name)
|
|
@ -6,11 +6,11 @@ class Beacon:
|
||||||
|
|
||||||
BEACON_LOOP_INTERVAL = 1
|
BEACON_LOOP_INTERVAL = 1
|
||||||
|
|
||||||
def __init__(self, config, states, event_queue, logger, modem):
|
def __init__(self, config, states, event_manager, logger, modem):
|
||||||
|
|
||||||
self.modem_config = config
|
self.modem_config = config
|
||||||
self.states = states
|
self.states = states
|
||||||
self.event_queue = event_queue
|
self.event_manager = event_manager
|
||||||
self.log = logger
|
self.log = logger
|
||||||
self.modem = modem
|
self.modem = modem
|
||||||
|
|
||||||
|
@ -39,8 +39,8 @@ class Beacon:
|
||||||
True):
|
True):
|
||||||
#not self.states.channel_busy):
|
#not self.states.channel_busy):
|
||||||
|
|
||||||
cmd = command_beacon.BeaconCommand(self.modem_config, self.states, self.event_queue)
|
cmd = command_beacon.BeaconCommand(self.modem_config, self.states, self.event_manager)
|
||||||
cmd.run(self.event_queue, self.modem)
|
cmd.run(self.event_manager, self.modem)
|
||||||
self.event.wait(self.modem_config['MODEM']['beacon_interval'])
|
self.event.wait(self.modem_config['MODEM']['beacon_interval'])
|
||||||
|
|
||||||
self.event.wait(self.BEACON_LOOP_INTERVAL)
|
self.event.wait(self.BEACON_LOOP_INTERVAL)
|
||||||
|
|
|
@ -6,11 +6,11 @@ from state_manager import StateManager
|
||||||
|
|
||||||
class TxCommand():
|
class TxCommand():
|
||||||
|
|
||||||
def __init__(self, config: dict, state_manager: StateManager, modem_events: queue.Queue, apiParams:dict = {}):
|
def __init__(self, config: dict, state_manager: StateManager, event_manager, apiParams:dict = {}):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.logger = structlog.get_logger("Command")
|
self.logger = structlog.get_logger("Command")
|
||||||
self.state_manager = state_manager
|
self.state_manager = state_manager
|
||||||
self.modem_events = modem_events
|
self.event_manager = event_manager
|
||||||
self.set_params_from_api(apiParams)
|
self.set_params_from_api(apiParams)
|
||||||
self.frame_factory = DataFrameFactory(config)
|
self.frame_factory = DataFrameFactory(config)
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,9 @@ class ARQRawCommand(TxCommand):
|
||||||
self.emit_event(event_queue)
|
self.emit_event(event_queue)
|
||||||
self.logger.info(self.log_message())
|
self.logger.info(self.log_message())
|
||||||
|
|
||||||
iss = ARQSessionISS(self.config, modem, self.dxcall, self.data)
|
iss = ARQSessionISS(self.config, modem, self.dxcall, self.data, self.state_manager)
|
||||||
self.state_manager.register_arq_iss_session(iss)
|
if iss.id:
|
||||||
iss.start()
|
self.state_manager.register_arq_iss_session(iss)
|
||||||
return iss
|
iss.start()
|
||||||
|
return iss
|
||||||
|
return False
|
|
@ -101,7 +101,6 @@ class CONFIG:
|
||||||
# Validates config data
|
# Validates config data
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
for section in data:
|
for section in data:
|
||||||
print(section)
|
|
||||||
for setting in data[section]:
|
for setting in data[section]:
|
||||||
if not isinstance(data[section][setting], self.config_types[section][setting]):
|
if not isinstance(data[section][setting], self.config_types[section][setting]):
|
||||||
message = (f"{section}.{setting} must be {self.config_types[section][setting]}."
|
message = (f"{section}.{setting} must be {self.config_types[section][setting]}."
|
||||||
|
@ -112,25 +111,26 @@ class CONFIG:
|
||||||
# is_writing means data from a dict being writen to the config file
|
# is_writing means data from a dict being writen to the config file
|
||||||
# if False, it means the opposite direction
|
# if False, it means the opposite direction
|
||||||
def handle_setting(self, section, setting, value, is_writing = False):
|
def handle_setting(self, section, setting, value, is_writing = False):
|
||||||
|
try:
|
||||||
|
if self.config_types[section][setting] == list:
|
||||||
|
if (is_writing):
|
||||||
|
return json.dumps(value)
|
||||||
|
else:
|
||||||
|
return json.loads(value)
|
||||||
|
|
||||||
|
elif self.config_types[section][setting] == bool and not is_writing:
|
||||||
|
return self.parser.getboolean(section, setting)
|
||||||
|
|
||||||
|
elif self.config_types[section][setting] == int and not is_writing:
|
||||||
|
return self.parser.getint(section, setting)
|
||||||
|
|
||||||
if self.config_types[section][setting] == list:
|
|
||||||
if (is_writing):
|
|
||||||
return json.dumps(value)
|
|
||||||
else:
|
else:
|
||||||
return json.loads(value)
|
return value
|
||||||
|
except KeyError as key:
|
||||||
elif self.config_types[section][setting] == bool and not is_writing:
|
self.log.error("[CFG] key error in logfile, please check 'config.ini.example' for help", key=key)
|
||||||
return self.parser.getboolean(section, setting)
|
|
||||||
|
|
||||||
elif self.config_types[section][setting] == int and not is_writing:
|
|
||||||
return self.parser.getint(section, setting)
|
|
||||||
|
|
||||||
else:
|
|
||||||
return value
|
|
||||||
|
|
||||||
# Sets and writes config data from a dict containing data settings
|
# Sets and writes config data from a dict containing data settings
|
||||||
def write(self, data):
|
def write(self, data):
|
||||||
|
|
||||||
# Validate config data before writing
|
# Validate config data before writing
|
||||||
self.validate(data)
|
self.validate(data)
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,7 @@ class DataFrameFactory:
|
||||||
"destination_crc": 3,
|
"destination_crc": 3,
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"snr": 1,
|
"snr": 1,
|
||||||
|
"flag": 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.template_list[FR_TYPE.ARQ_SESSION_INFO.value] = {
|
self.template_list[FR_TYPE.ARQ_SESSION_INFO.value] = {
|
||||||
|
@ -227,7 +228,9 @@ class DataFrameFactory:
|
||||||
|
|
||||||
data = int.from_bytes(data, "big")
|
data = int.from_bytes(data, "big")
|
||||||
extracted_data[key] = {}
|
extracted_data[key] = {}
|
||||||
if frametype in [FR_TYPE.ARQ_BURST_ACK.value, FR_TYPE.ARQ_SESSION_INFO_ACK.value]:
|
|
||||||
|
# check for frametype for selecting the correspinding flag dictionary
|
||||||
|
if frametype in [FR_TYPE.ARQ_SESSION_OPEN_ACK.value, FR_TYPE.ARQ_SESSION_INFO_ACK.value, FR_TYPE.ARQ_BURST_ACK.value]:
|
||||||
flag_dict = self.ARQ_FLAGS
|
flag_dict = self.ARQ_FLAGS
|
||||||
for flag in flag_dict:
|
for flag in flag_dict:
|
||||||
# Update extracted_data with the status of each flag
|
# Update extracted_data with the status of each flag
|
||||||
|
@ -332,7 +335,10 @@ class DataFrameFactory:
|
||||||
}
|
}
|
||||||
return self.construct(FR_TYPE.ARQ_SESSION_OPEN, payload)
|
return self.construct(FR_TYPE.ARQ_SESSION_OPEN, payload)
|
||||||
|
|
||||||
def build_arq_session_open_ack(self, session_id, destination, version, snr):
|
def build_arq_session_open_ack(self, session_id, destination, version, snr, flag_abort=False):
|
||||||
|
flag = 0b00000000
|
||||||
|
if flag_abort:
|
||||||
|
flag = helpers.set_flag(flag, 'ABORT', True, self.ARQ_FLAGS)
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
"session_id": session_id.to_bytes(1, 'big'),
|
"session_id": session_id.to_bytes(1, 'big'),
|
||||||
|
@ -340,6 +346,7 @@ class DataFrameFactory:
|
||||||
"destination_crc": helpers.get_crc_24(destination),
|
"destination_crc": helpers.get_crc_24(destination),
|
||||||
"version": bytes([version]),
|
"version": bytes([version]),
|
||||||
"snr": helpers.snr_to_bytes(1),
|
"snr": helpers.snr_to_bytes(1),
|
||||||
|
"flag": flag.to_bytes(1, 'big'),
|
||||||
}
|
}
|
||||||
return self.construct(FR_TYPE.ARQ_SESSION_OPEN_ACK, payload)
|
return self.construct(FR_TYPE.ARQ_SESSION_OPEN_ACK, payload)
|
||||||
|
|
||||||
|
@ -420,22 +427,3 @@ class DataFrameFactory:
|
||||||
"flag": flag.to_bytes(1, 'big'),
|
"flag": flag.to_bytes(1, 'big'),
|
||||||
}
|
}
|
||||||
return self.construct(FR_TYPE.ARQ_BURST_ACK, payload)
|
return self.construct(FR_TYPE.ARQ_BURST_ACK, payload)
|
||||||
|
|
||||||
def build_arq_burst_nack(self, session_id: bytes, offset, speed_level: int,
|
|
||||||
frames_per_burst: int, snr: int):
|
|
||||||
payload = {
|
|
||||||
"session_id": session_id.to_bytes(1, 'big'),
|
|
||||||
"offset": offset.to_bytes(4, 'big'),
|
|
||||||
"speed_level": speed_level.to_bytes(1, 'big'),
|
|
||||||
"frames_per_burst": frames_per_burst.to_bytes(1, 'big'),
|
|
||||||
"snr": helpers.snr_to_bytes(snr),
|
|
||||||
}
|
|
||||||
return self.construct(FR_TYPE.ARQ_BURST_NACK, payload)
|
|
||||||
|
|
||||||
def build_arq_data_ack_nack(self, session_id: bytes, state: int, snr: int):
|
|
||||||
payload = {
|
|
||||||
"session_id": session_id.to_bytes(1, 'big'),
|
|
||||||
"state": state.to_bytes(1, 'big'),
|
|
||||||
"snr": helpers.snr_to_bytes(snr),
|
|
||||||
}
|
|
||||||
return self.construct(FR_TYPE. ARQ_DATA_ACK_NACK, payload)
|
|
||||||
|
|
|
@ -206,50 +206,57 @@ class Demodulator():
|
||||||
bytes_per_frame= self.MODE_DICT[mode]["bytes_per_frame"]
|
bytes_per_frame= self.MODE_DICT[mode]["bytes_per_frame"]
|
||||||
state_buffer = self.MODE_DICT[mode]["state_buffer"]
|
state_buffer = self.MODE_DICT[mode]["state_buffer"]
|
||||||
mode_name = self.MODE_DICT[mode]["name"]
|
mode_name = self.MODE_DICT[mode]["name"]
|
||||||
|
try:
|
||||||
while self.stream.active:
|
while self.stream.active:
|
||||||
threading.Event().wait(0.01)
|
threading.Event().wait(0.01)
|
||||||
while audiobuffer.nbuffer >= nin:
|
while audiobuffer.nbuffer >= nin:
|
||||||
# demodulate audio
|
# demodulate audio
|
||||||
nbytes = codec2.api.freedv_rawdatarx(
|
nbytes = codec2.api.freedv_rawdatarx(
|
||||||
freedv, bytes_out, audiobuffer.buffer.ctypes
|
freedv, bytes_out, audiobuffer.buffer.ctypes
|
||||||
)
|
|
||||||
# get current modem states and write to list
|
|
||||||
# 1 trial
|
|
||||||
# 2 sync
|
|
||||||
# 3 trial sync
|
|
||||||
# 6 decoded
|
|
||||||
# 10 error decoding == NACK
|
|
||||||
rx_status = codec2.api.freedv_get_rx_status(freedv)
|
|
||||||
|
|
||||||
if rx_status not in [0]:
|
|
||||||
self.is_codec2_traffic_counter = self.is_codec2_traffic_cooldown
|
|
||||||
self.log.debug(
|
|
||||||
"[MDM] [demod_audio] modem state", mode=mode_name, rx_status=rx_status,
|
|
||||||
sync_flag=codec2.api.rx_sync_flags_to_text[rx_status]
|
|
||||||
)
|
)
|
||||||
|
# get current modem states and write to list
|
||||||
|
# 1 trial
|
||||||
|
# 2 sync
|
||||||
|
# 3 trial sync
|
||||||
|
# 6 decoded
|
||||||
|
# 10 error decoding == NACK
|
||||||
|
rx_status = codec2.api.freedv_get_rx_status(freedv)
|
||||||
|
|
||||||
# decrement codec traffic counter for making state smoother
|
if rx_status not in [0]:
|
||||||
if self.is_codec2_traffic_counter > 0:
|
self.is_codec2_traffic_counter = self.is_codec2_traffic_cooldown
|
||||||
self.is_codec2_traffic_counter -= 1
|
self.log.debug(
|
||||||
self.states.set_channel_busy_condition_codec2(True)
|
"[MDM] [demod_audio] modem state", mode=mode_name, rx_status=rx_status,
|
||||||
else:
|
sync_flag=codec2.api.rx_sync_flags_to_text[rx_status]
|
||||||
self.states.set_channel_busy_condition_codec2(False)
|
)
|
||||||
if rx_status == 10:
|
|
||||||
state_buffer.append(rx_status)
|
|
||||||
|
|
||||||
audiobuffer.pop(nin)
|
# decrement codec traffic counter for making state smoother
|
||||||
nin = codec2.api.freedv_nin(freedv)
|
if self.is_codec2_traffic_counter > 0:
|
||||||
if nbytes == bytes_per_frame:
|
self.is_codec2_traffic_counter -= 1
|
||||||
self.log.debug(
|
self.states.set_channel_busy_condition_codec2(True)
|
||||||
"[MDM] [demod_audio] Pushing received data to received_queue", nbytes=nbytes
|
else:
|
||||||
)
|
self.states.set_channel_busy_condition_codec2(False)
|
||||||
snr = self.calculate_snr(freedv)
|
if rx_status == 10:
|
||||||
self.get_scatter(freedv)
|
state_buffer.append(rx_status)
|
||||||
|
|
||||||
self.modem_received_queue.put([bytes_out, freedv, bytes_per_frame, snr])
|
audiobuffer.pop(nin)
|
||||||
state_buffer = []
|
nin = codec2.api.freedv_nin(freedv)
|
||||||
|
if nbytes == bytes_per_frame:
|
||||||
|
self.log.debug(
|
||||||
|
"[MDM] [demod_audio] Pushing received data to received_queue", nbytes=nbytes
|
||||||
|
)
|
||||||
|
snr = self.calculate_snr(freedv)
|
||||||
|
self.get_scatter(freedv)
|
||||||
|
|
||||||
|
self.modem_received_queue.put([bytes_out, freedv, bytes_per_frame, snr])
|
||||||
|
state_buffer = []
|
||||||
|
except Exception as e:
|
||||||
|
error_message = str(e)
|
||||||
|
# we expect this error when shutdown
|
||||||
|
if "PortAudio not initialized" in error_message:
|
||||||
|
e = None
|
||||||
|
self.log.debug(
|
||||||
|
"[MDM] [demod_audio] demod loop ended", mode=mode_name, e=e
|
||||||
|
)
|
||||||
def tci_rx_callback(self) -> None:
|
def tci_rx_callback(self) -> None:
|
||||||
"""
|
"""
|
||||||
Callback for TCI RX
|
Callback for TCI RX
|
||||||
|
@ -384,6 +391,6 @@ class Demodulator():
|
||||||
|
|
||||||
# Enable mode based on speed_level
|
# Enable mode based on speed_level
|
||||||
self.MODE_DICT[mode.value]["decode"] = True
|
self.MODE_DICT[mode.value]["decode"] = True
|
||||||
self.log.info(f"Demodulator data mode {mode.name}")
|
self.log.info(f"[MDM] [demod_audio] set data mode: {mode.name}")
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
@ -32,7 +32,8 @@ class EventManager:
|
||||||
def send_arq_session_new(self, outbound: bool, session_id, dxcall, total_bytes, state):
|
def send_arq_session_new(self, outbound: bool, session_id, dxcall, total_bytes, state):
|
||||||
direction = 'outbound' if outbound else 'inbound'
|
direction = 'outbound' if outbound else 'inbound'
|
||||||
event = {
|
event = {
|
||||||
f"arq-transfer-{direction}": {
|
"type": "arq",
|
||||||
|
f"arq-transfer-{direction}": {
|
||||||
'session_id': session_id,
|
'session_id': session_id,
|
||||||
'dxcall': dxcall,
|
'dxcall': dxcall,
|
||||||
'total_bytes': total_bytes,
|
'total_bytes': total_bytes,
|
||||||
|
@ -44,7 +45,8 @@ class EventManager:
|
||||||
def send_arq_session_progress(self, outbound: bool, session_id, dxcall, received_bytes, total_bytes, state):
|
def send_arq_session_progress(self, outbound: bool, session_id, dxcall, received_bytes, total_bytes, state):
|
||||||
direction = 'outbound' if outbound else 'inbound'
|
direction = 'outbound' if outbound else 'inbound'
|
||||||
event = {
|
event = {
|
||||||
f"arq-transfer-{direction}": {
|
"type": "arq",
|
||||||
|
f"arq-transfer-{direction}": {
|
||||||
'session_id': session_id,
|
'session_id': session_id,
|
||||||
'dxcall': dxcall,
|
'dxcall': dxcall,
|
||||||
'received_bytes': received_bytes,
|
'received_bytes': received_bytes,
|
||||||
|
@ -54,18 +56,35 @@ class EventManager:
|
||||||
}
|
}
|
||||||
self.broadcast(event)
|
self.broadcast(event)
|
||||||
|
|
||||||
def send_arq_session_finished(self, outbound: bool, session_id, dxcall, total_bytes, success: bool, state, data=False):
|
def send_arq_session_finished(self, outbound: bool, session_id, dxcall, total_bytes, success: bool, state: bool, data=False):
|
||||||
if data:
|
if data:
|
||||||
data = base64.b64encode(data).decode("UTF-8")
|
data = base64.b64encode(data).decode("UTF-8")
|
||||||
direction = 'outbound' if outbound else 'inbound'
|
direction = 'outbound' if outbound else 'inbound'
|
||||||
event = {
|
event = {
|
||||||
f"arq-transfer-{direction}": {
|
"type" : "arq",
|
||||||
|
f"arq-transfer-{direction}": {
|
||||||
'session_id': session_id,
|
'session_id': session_id,
|
||||||
'dxcall': dxcall,
|
'dxcall': dxcall,
|
||||||
'total_bytes': total_bytes,
|
'total_bytes': total_bytes,
|
||||||
'success': success,
|
'success': bool(success),
|
||||||
'state': state,
|
'state': state,
|
||||||
'data': data
|
'data': data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.broadcast(event)
|
self.broadcast(event)
|
||||||
|
|
||||||
|
def modem_started(self):
|
||||||
|
event = {"modem": "started"}
|
||||||
|
self.broadcast(event)
|
||||||
|
|
||||||
|
def modem_restarted(self):
|
||||||
|
event = {"modem": "restarted"}
|
||||||
|
self.broadcast(event)
|
||||||
|
|
||||||
|
def modem_stopped(self):
|
||||||
|
event = {"modem": "stopped"}
|
||||||
|
self.broadcast(event)
|
||||||
|
|
||||||
|
def modem_failed(self):
|
||||||
|
event = {"modem": "failed"}
|
||||||
|
self.broadcast(event)
|
|
@ -38,26 +38,25 @@ class DISPATCHER():
|
||||||
FR_TYPE.FEC_WAKEUP.value: {"class": FrameHandler, "name": "FEC WAKEUP"},
|
FR_TYPE.FEC_WAKEUP.value: {"class": FrameHandler, "name": "FEC WAKEUP"},
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, config, event_queue, states, modem):
|
def __init__(self, config, event_manager, states, modem):
|
||||||
self.log = structlog.get_logger("frame_dispatcher")
|
self.log = structlog.get_logger("frame_dispatcher")
|
||||||
|
|
||||||
self.log.info("loading frame dispatcher.....\n")
|
self.log.info("loading frame dispatcher.....\n")
|
||||||
self.config = config
|
self.config = config
|
||||||
self.event_queue = event_queue
|
|
||||||
self.states = states
|
self.states = states
|
||||||
|
self.event_manager = event_manager
|
||||||
|
|
||||||
self._initialize_handlers(config, event_queue, states)
|
self._initialize_handlers(config, states)
|
||||||
|
|
||||||
self.modem = modem
|
self.modem = modem
|
||||||
self.data_queue_received = modem.data_queue_received
|
self.data_queue_received = modem.data_queue_received
|
||||||
|
|
||||||
self.arq_sessions = []
|
self.arq_sessions = []
|
||||||
|
|
||||||
def _initialize_handlers(self, config, event_queue, states):
|
def _initialize_handlers(self, config, states):
|
||||||
"""Initializes various data handlers."""
|
"""Initializes various data handlers."""
|
||||||
|
|
||||||
self.frame_factory = DataFrameFactory(config)
|
self.frame_factory = DataFrameFactory(config)
|
||||||
self.event_manager = event_manager.EventManager([event_queue])
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""Starts worker threads for transmit and receive operations."""
|
"""Starts worker threads for transmit and receive operations."""
|
||||||
|
|
|
@ -73,13 +73,13 @@ class FrameHandler():
|
||||||
|
|
||||||
def make_event(self):
|
def make_event(self):
|
||||||
event = {
|
event = {
|
||||||
"freedata": "modem-message",
|
"type": "frame-handler",
|
||||||
"received": self.details['frame']['frame_type'],
|
"received": self.details['frame']['frame_type'],
|
||||||
"uuid": str(uuid.uuid4()),
|
|
||||||
"timestamp": int(time.time()),
|
"timestamp": int(time.time()),
|
||||||
"mycallsign": self.config['STATION']['mycall'],
|
"mycallsign": self.config['STATION']['mycall'],
|
||||||
|
"myssid": self.config['STATION']['myssid'],
|
||||||
"snr": str(self.details['snr']),
|
"snr": str(self.details['snr']),
|
||||||
}
|
}
|
||||||
if 'origin' in self.details['frame']:
|
if 'origin' in self.details['frame']:
|
||||||
event['dxcallsign'] = self.details['frame']['origin']
|
event['dxcallsign'] = self.details['frame']['origin']
|
||||||
return event
|
return event
|
||||||
|
@ -101,7 +101,6 @@ class FrameHandler():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def log(self):
|
def log(self):
|
||||||
return
|
|
||||||
self.logger.info(f"[Frame Handler] Handling frame {self.details['frame']['frame_type']}")
|
self.logger.info(f"[Frame Handler] Handling frame {self.details['frame']['frame_type']}")
|
||||||
|
|
||||||
def handle(self, frame, snr, frequency_offset, freedv_inst, bytes_per_frame):
|
def handle(self, frame, snr, frequency_offset, freedv_inst, bytes_per_frame):
|
||||||
|
@ -114,5 +113,5 @@ class FrameHandler():
|
||||||
self.log()
|
self.log()
|
||||||
self.add_to_heard_stations()
|
self.add_to_heard_stations()
|
||||||
self.add_to_activity_list()
|
self.add_to_activity_list()
|
||||||
self.emit_event()
|
#self.emit_event()
|
||||||
self.follow_protocol()
|
self.follow_protocol()
|
||||||
|
|
|
@ -5,9 +5,9 @@ import data_frame_factory
|
||||||
class PingFrameHandler(frame_handler.FrameHandler):
|
class PingFrameHandler(frame_handler.FrameHandler):
|
||||||
|
|
||||||
def is_frame_for_me(self):
|
def is_frame_for_me(self):
|
||||||
# check if callsign ssid override
|
call_with_ssid = self.config['STATION']['mycall'] + "-" + str(self.config['STATION']['myssid'])
|
||||||
valid, mycallsign = helpers.check_callsign(
|
valid, mycallsign = helpers.check_callsign(
|
||||||
self.config['STATION']['mycall'],
|
call_with_ssid,
|
||||||
self.details["frame"]["destination_crc"],
|
self.details["frame"]["destination_crc"],
|
||||||
self.config['STATION']['ssid_list'])
|
self.config['STATION']['ssid_list'])
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ import tci
|
||||||
import cw
|
import cw
|
||||||
from queues import RIGCTLD_COMMAND_QUEUE
|
from queues import RIGCTLD_COMMAND_QUEUE
|
||||||
import audio
|
import audio
|
||||||
import event_manager
|
|
||||||
import demodulator
|
import demodulator
|
||||||
|
|
||||||
TESTMODE = False
|
TESTMODE = False
|
||||||
|
@ -32,12 +31,11 @@ class RF:
|
||||||
|
|
||||||
log = structlog.get_logger("RF")
|
log = structlog.get_logger("RF")
|
||||||
|
|
||||||
def __init__(self, config, event_queue, fft_queue, service_queue, states) -> None:
|
def __init__(self, config, event_manager, fft_queue, service_queue, states) -> None:
|
||||||
self.config = config
|
self.config = config
|
||||||
print(config)
|
|
||||||
self.service_queue = service_queue
|
self.service_queue = service_queue
|
||||||
self.states = states
|
self.states = states
|
||||||
|
self.event_manager = event_manager
|
||||||
self.sampler_avg = 0
|
self.sampler_avg = 0
|
||||||
self.buffer_avg = 0
|
self.buffer_avg = 0
|
||||||
|
|
||||||
|
@ -78,7 +76,6 @@ class RF:
|
||||||
self.modem_received_queue = queue.Queue()
|
self.modem_received_queue = queue.Queue()
|
||||||
self.audio_received_queue = queue.Queue()
|
self.audio_received_queue = queue.Queue()
|
||||||
self.data_queue_received = queue.Queue()
|
self.data_queue_received = queue.Queue()
|
||||||
self.event_manager = event_manager.EventManager([event_queue])
|
|
||||||
self.fft_queue = fft_queue
|
self.fft_queue = fft_queue
|
||||||
|
|
||||||
self.demodulator = demodulator.Demodulator(self.config,
|
self.demodulator = demodulator.Demodulator(self.config,
|
||||||
|
@ -256,16 +253,10 @@ class RF:
|
||||||
# Wait for some other thread that might be transmitting
|
# Wait for some other thread that might be transmitting
|
||||||
self.states.waitForTransmission()
|
self.states.waitForTransmission()
|
||||||
self.states.setTransmitting(True)
|
self.states.setTransmitting(True)
|
||||||
#self.states.waitForChannelBusy()
|
#self.states.channel_busy_event.wait()
|
||||||
|
|
||||||
|
|
||||||
start_of_transmission = time.time()
|
start_of_transmission = time.time()
|
||||||
# TODO Moved ptt toggle some steps before audio is ready for testing
|
|
||||||
# Toggle ptt early to save some time and send ptt state via socket
|
|
||||||
# self.radio.set_ptt(True)
|
|
||||||
# jsondata = {"ptt": "True"}
|
|
||||||
# data_out = json.dumps(jsondata)
|
|
||||||
# sock.SOCKET_QUEUE.put(data_out)
|
|
||||||
|
|
||||||
# Open codec2 instance
|
# Open codec2 instance
|
||||||
self.MODE = mode
|
self.MODE = mode
|
||||||
|
@ -430,7 +421,7 @@ class RF:
|
||||||
# we need to wait manually for tci processing
|
# we need to wait manually for tci processing
|
||||||
self.tci_module.wait_until_transmitted(audio_48k)
|
self.tci_module.wait_until_transmitted(audio_48k)
|
||||||
else:
|
else:
|
||||||
sd.play(audio_48k, blocking=True)
|
sd.play(audio_48k, blocksize=4096, blocking=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def init_rig_control(self):
|
def init_rig_control(self):
|
||||||
|
|
|
@ -17,6 +17,7 @@ import command_ping
|
||||||
import command_feq
|
import command_feq
|
||||||
import command_test
|
import command_test
|
||||||
import command_arq_raw
|
import command_arq_raw
|
||||||
|
import event_manager
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
CORS(app)
|
CORS(app)
|
||||||
|
@ -49,6 +50,7 @@ app.state_queue = queue.Queue() # queue which holds latest states
|
||||||
app.modem_events = queue.Queue() # queue which holds latest events
|
app.modem_events = queue.Queue() # queue which holds latest events
|
||||||
app.modem_fft = queue.Queue() # queue which holds latest fft data
|
app.modem_fft = queue.Queue() # queue which holds latest fft data
|
||||||
app.modem_service = queue.Queue() # start / stop modem service
|
app.modem_service = queue.Queue() # start / stop modem service
|
||||||
|
app.event_manager = event_manager.EventManager([app.modem_events]) # TODO remove the app.modem_event custom queue
|
||||||
|
|
||||||
# init state manager
|
# init state manager
|
||||||
app.state_manager = state_manager.StateManager(app.state_queue)
|
app.state_manager = state_manager.StateManager(app.state_queue)
|
||||||
|
@ -82,10 +84,11 @@ def validate(req, param, validator, isRequired = True):
|
||||||
|
|
||||||
# Takes a transmit command and puts it in the transmit command queue
|
# Takes a transmit command and puts it in the transmit command queue
|
||||||
def enqueue_tx_command(cmd_class, params = {}):
|
def enqueue_tx_command(cmd_class, params = {}):
|
||||||
command = cmd_class(app.config_manager.read(), app.state_manager, app.modem_events, params)
|
command = cmd_class(app.config_manager.read(), app.state_manager, app.event_manager, params)
|
||||||
app.logger.info(f"Command {command.get_name()} running...")
|
app.logger.info(f"Command {command.get_name()} running...")
|
||||||
command.run(app.modem_events, app.service_manager.modem)
|
if command.run(app.modem_events, app.service_manager.modem): # TODO remove the app.modem_event custom queue
|
||||||
|
return True
|
||||||
|
return False
|
||||||
## REST API
|
## REST API
|
||||||
@app.route('/', methods=['GET'])
|
@app.route('/', methods=['GET'])
|
||||||
def index():
|
def index():
|
||||||
|
@ -139,6 +142,7 @@ def post_cqcqcq():
|
||||||
def post_beacon():
|
def post_beacon():
|
||||||
if request.method not in ['POST']:
|
if request.method not in ['POST']:
|
||||||
return api_response({"info": "endpoint for controlling BEACON STATE via POST"})
|
return api_response({"info": "endpoint for controlling BEACON STATE via POST"})
|
||||||
|
|
||||||
if not isinstance(request.json['enabled'], bool):
|
if not isinstance(request.json['enabled'], bool):
|
||||||
api_abort(f"Incorrect value for 'enabled'. Shoud be bool.")
|
api_abort(f"Incorrect value for 'enabled'. Shoud be bool.")
|
||||||
if not app.state_manager.is_modem_running:
|
if not app.state_manager.is_modem_running:
|
||||||
|
@ -217,9 +221,10 @@ def post_modem_send_raw():
|
||||||
return api_response({"info": "endpoint for SENDING RAW DATA via POST"})
|
return api_response({"info": "endpoint for SENDING RAW DATA via POST"})
|
||||||
if not app.state_manager.is_modem_running:
|
if not app.state_manager.is_modem_running:
|
||||||
api_abort('Modem not running', 503)
|
api_abort('Modem not running', 503)
|
||||||
enqueue_tx_command(command_arq_raw.ARQRawCommand, request.json)
|
if enqueue_tx_command(command_arq_raw.ARQRawCommand, request.json):
|
||||||
return api_response(request.json)
|
return api_response(request.json)
|
||||||
|
else:
|
||||||
|
api_abort('Error executing command...', 500)
|
||||||
@app.route('/modem/stop_transmission', methods=['POST'])
|
@app.route('/modem/stop_transmission', methods=['POST'])
|
||||||
def post_modem_send_raw_stop():
|
def post_modem_send_raw_stop():
|
||||||
|
|
||||||
|
@ -257,7 +262,7 @@ def post_modem_send_raw_stop():
|
||||||
# Event websocket
|
# Event websocket
|
||||||
@sock.route('/events')
|
@sock.route('/events')
|
||||||
def sock_events(sock):
|
def sock_events(sock):
|
||||||
wsm.handle_connection(sock, wsm.events_client_list, app.modem_events)
|
wsm.handle_connection(sock, wsm.events_client_list, app.modem_events) # TODO remove the app.modem_event custom queue
|
||||||
|
|
||||||
@sock.route('/fft')
|
@sock.route('/fft')
|
||||||
def sock_fft(sock):
|
def sock_fft(sock):
|
||||||
|
|
|
@ -14,22 +14,20 @@ class SM:
|
||||||
|
|
||||||
self.modem = False
|
self.modem = False
|
||||||
self.beacon = False
|
self.beacon = False
|
||||||
self.data_handler = False
|
|
||||||
self.app = app
|
self.app = app
|
||||||
self.config = self.app.config_manager.read()
|
self.config = self.app.config_manager.read()
|
||||||
self.modem_events = app.modem_events
|
|
||||||
self.modem_fft = app.modem_fft
|
self.modem_fft = app.modem_fft
|
||||||
self.modem_service = app.modem_service
|
self.modem_service = app.modem_service
|
||||||
self.states = app.state_manager
|
self.states = app.state_manager
|
||||||
|
self.event_manager = app.event_manager
|
||||||
|
|
||||||
|
|
||||||
runner_thread = threading.Thread(
|
runner_thread = threading.Thread(
|
||||||
target=self.runner, name="runner thread", daemon=True
|
target=self.runner, name="runner thread", daemon=True
|
||||||
)
|
)
|
||||||
runner_thread.start()
|
runner_thread.start()
|
||||||
|
|
||||||
# optionally start explorer module
|
self.start_explorer_publishing()
|
||||||
if self.config['STATION']['enable_explorer']:
|
|
||||||
explorer.explorer(self.app, self.config, self.states)
|
|
||||||
|
|
||||||
def runner(self):
|
def runner(self):
|
||||||
while True:
|
while True:
|
||||||
|
@ -49,7 +47,7 @@ class SM:
|
||||||
# we need to wait a bit for avoiding a portaudio crash
|
# we need to wait a bit for avoiding a portaudio crash
|
||||||
threading.Event().wait(0.5)
|
threading.Event().wait(0.5)
|
||||||
if self.start_modem():
|
if self.start_modem():
|
||||||
self.modem_events.put(json.dumps({"freedata": "modem-event", "event": "restart"}))
|
self.event_manager.modem_restarted()
|
||||||
elif cmd in ['start_beacon']:
|
elif cmd in ['start_beacon']:
|
||||||
self.start_beacon()
|
self.start_beacon()
|
||||||
|
|
||||||
|
@ -76,18 +74,19 @@ class SM:
|
||||||
if False in audio_test or None in audio_test or self.states.is_modem_running:
|
if False in audio_test or None in audio_test or self.states.is_modem_running:
|
||||||
self.log.warning("starting modem failed", input_test=audio_test[0], output_test=audio_test[1])
|
self.log.warning("starting modem failed", input_test=audio_test[0], output_test=audio_test[1])
|
||||||
self.states.set("is_modem_running", False)
|
self.states.set("is_modem_running", False)
|
||||||
self.modem_events.put({"freedata": "modem-event", "event": "failed"})
|
self.event_manager.modem_failed()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.log.info("starting modem....")
|
self.log.info("starting modem....")
|
||||||
self.modem = modem.RF(self.config, self.modem_events, self.modem_fft, self.modem_service, self.states)
|
self.modem = modem.RF(self.config, self.event_manager, self.modem_fft, self.modem_service, self.states)
|
||||||
|
|
||||||
self.frame_dispatcher = frame_dispatcher.DISPATCHER(self.config,
|
self.frame_dispatcher = frame_dispatcher.DISPATCHER(self.config,
|
||||||
self.modem_events,
|
self.event_manager,
|
||||||
self.states,
|
self.states,
|
||||||
self.modem)
|
self.modem)
|
||||||
self.frame_dispatcher.start()
|
self.frame_dispatcher.start()
|
||||||
|
|
||||||
|
self.event_manager.modem_started()
|
||||||
self.states.set("is_modem_running", True)
|
self.states.set("is_modem_running", True)
|
||||||
self.modem.start_modem()
|
self.modem.start_modem()
|
||||||
|
|
||||||
|
@ -96,22 +95,32 @@ class SM:
|
||||||
def stop_modem(self):
|
def stop_modem(self):
|
||||||
self.log.info("stopping modem....")
|
self.log.info("stopping modem....")
|
||||||
del self.modem
|
del self.modem
|
||||||
#del self.data_handler
|
|
||||||
self.modem = False
|
self.modem = False
|
||||||
#self.data_handler = False
|
|
||||||
self.states.set("is_modem_running", False)
|
self.states.set("is_modem_running", False)
|
||||||
|
self.event_manager.modem_stopped()
|
||||||
|
|
||||||
def test_audio(self):
|
def test_audio(self):
|
||||||
audio_test = audio.test_audio_devices(self.config['AUDIO']['input_device'],
|
try:
|
||||||
self.config['AUDIO']['output_device'])
|
audio_test = audio.test_audio_devices(self.config['AUDIO']['input_device'],
|
||||||
self.log.info("tested audio devices", result=audio_test)
|
self.config['AUDIO']['output_device'])
|
||||||
|
self.log.info("tested audio devices", result=audio_test)
|
||||||
return audio_test
|
|
||||||
|
|
||||||
|
return audio_test
|
||||||
|
except Exception as e:
|
||||||
|
self.log.error("Error testing audio devices", e=e)
|
||||||
|
return [False, False]
|
||||||
|
|
||||||
def start_beacon(self):
|
def start_beacon(self):
|
||||||
self.beacon = beacon.Beacon(self.config, self.states, self.modem_events, self.log, self.modem)
|
self.beacon = beacon.Beacon(self.config, self.states, self.event_manager, self.log, self.modem)
|
||||||
self.beacon.start()
|
self.beacon.start()
|
||||||
|
|
||||||
def stop_beacon(self):
|
def stop_beacon(self):
|
||||||
del self.beacon
|
del self.beacon
|
||||||
|
|
||||||
|
def start_explorer_publishing(self):
|
||||||
|
try:
|
||||||
|
# optionally start explorer module
|
||||||
|
if self.config['STATION']['enable_explorer']:
|
||||||
|
explorer.explorer(self.app, self.config, self.states)
|
||||||
|
except Exception as e:
|
||||||
|
self.log.warning("[EXPLORER] Publishin not started because of error", e=e)
|
|
@ -79,7 +79,7 @@ class StateManager:
|
||||||
msgtype = "state"
|
msgtype = "state"
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"freedata-message": msgtype,
|
"type": msgtype,
|
||||||
"is_modem_running": self.is_modem_running,
|
"is_modem_running": self.is_modem_running,
|
||||||
"is_beacon_running": self.is_beacon_running,
|
"is_beacon_running": self.is_beacon_running,
|
||||||
"radio_status": self.radio_status,
|
"radio_status": self.radio_status,
|
||||||
|
@ -109,13 +109,15 @@ class StateManager:
|
||||||
|
|
||||||
def register_arq_iss_session(self, session):
|
def register_arq_iss_session(self, session):
|
||||||
if session.id in self.arq_iss_sessions:
|
if session.id in self.arq_iss_sessions:
|
||||||
raise RuntimeError(f"ARQ ISS Session '{session.id}' already exists!")
|
return False
|
||||||
self.arq_iss_sessions[session.id] = session
|
self.arq_iss_sessions[session.id] = session
|
||||||
|
return True
|
||||||
|
|
||||||
def register_arq_irs_session(self, session):
|
def register_arq_irs_session(self, session):
|
||||||
if session.id in self.arq_irs_sessions:
|
if session.id in self.arq_irs_sessions:
|
||||||
raise RuntimeError(f"ARQ IRS Session '{session.id}' already exists!")
|
return False
|
||||||
self.arq_irs_sessions[session.id] = session
|
self.arq_irs_sessions[session.id] = session
|
||||||
|
return True
|
||||||
|
|
||||||
def get_arq_iss_session(self, id):
|
def get_arq_iss_session(self, id):
|
||||||
if id not in self.arq_iss_sessions:
|
if id not in self.arq_iss_sessions:
|
||||||
|
|
|
@ -8,7 +8,7 @@ fft_client_list = set()
|
||||||
states_client_list = set()
|
states_client_list = set()
|
||||||
|
|
||||||
def handle_connection(sock, client_list, event_queue):
|
def handle_connection(sock, client_list, event_queue):
|
||||||
event_queue.put({"freedata-message": "hello-client"})
|
event_queue.put({"type": "hello-client"})
|
||||||
|
|
||||||
client_list.add(sock)
|
client_list.add(sock)
|
||||||
while True:
|
while True:
|
||||||
|
@ -26,13 +26,11 @@ def handle_connection(sock, client_list, event_queue):
|
||||||
def transmit_sock_data_worker(client_list, event_queue):
|
def transmit_sock_data_worker(client_list, event_queue):
|
||||||
while True:
|
while True:
|
||||||
event = event_queue.get()
|
event = event_queue.get()
|
||||||
|
|
||||||
if isinstance(event, str):
|
if isinstance(event, str):
|
||||||
print(f"WARNING: Queue event:\n'{event}'\n still in string format")
|
print(f"WARNING: Queue event:\n'{event}'\n still in string format")
|
||||||
json_event = event
|
json_event = event
|
||||||
else:
|
else:
|
||||||
json_event = json.dumps(event)
|
json_event = json.dumps(event)
|
||||||
|
|
||||||
clients = client_list.copy()
|
clients = client_list.copy()
|
||||||
for client in clients:
|
for client in clients:
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -58,19 +58,21 @@ class TestARQSession(unittest.TestCase):
|
||||||
|
|
||||||
# ISS
|
# ISS
|
||||||
cls.iss_state_manager = StateManager(queue.Queue())
|
cls.iss_state_manager = StateManager(queue.Queue())
|
||||||
|
cls.iss_event_manager = EventManager([queue.Queue()])
|
||||||
cls.iss_event_queue = queue.Queue()
|
cls.iss_event_queue = queue.Queue()
|
||||||
cls.iss_modem = TestModem(cls.iss_event_queue)
|
cls.iss_modem = TestModem(cls.iss_event_queue)
|
||||||
cls.iss_frame_dispatcher = DISPATCHER(cls.config,
|
cls.iss_frame_dispatcher = DISPATCHER(cls.config,
|
||||||
cls.iss_event_queue,
|
cls.iss_event_manager,
|
||||||
cls.iss_state_manager,
|
cls.iss_state_manager,
|
||||||
cls.iss_modem)
|
cls.iss_modem)
|
||||||
|
|
||||||
# IRS
|
# IRS
|
||||||
cls.irs_state_manager = StateManager(queue.Queue())
|
cls.irs_state_manager = StateManager(queue.Queue())
|
||||||
|
cls.irs_event_manager = EventManager([queue.Queue()])
|
||||||
cls.irs_event_queue = queue.Queue()
|
cls.irs_event_queue = queue.Queue()
|
||||||
cls.irs_modem = TestModem(cls.irs_event_queue)
|
cls.irs_modem = TestModem(cls.irs_event_queue)
|
||||||
cls.irs_frame_dispatcher = DISPATCHER(cls.config,
|
cls.irs_frame_dispatcher = DISPATCHER(cls.config,
|
||||||
cls.irs_event_queue,
|
cls.irs_event_manager,
|
||||||
cls.irs_state_manager,
|
cls.irs_state_manager,
|
||||||
cls.irs_modem)
|
cls.irs_modem)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue