Merge branch 'develop' of github.com:DJ2LS/FreeDATA into develop

This commit is contained in:
Mashintime 2023-12-09 11:36:04 -05:00
commit 41e13770b8
8 changed files with 87 additions and 75 deletions

View file

@ -51,7 +51,8 @@ const gridWidgets = [
{ x: 0, y: 0, w: 16, h: 40 }, { x: 0, y: 0, w: 16, h: 40 },
"Detailed heard stations list", "Detailed heard stations list",
true, true,
true,"Activity" true,
"Activity",
), ),
new gridWidget( new gridWidget(
active_stats, active_stats,
@ -59,7 +60,7 @@ const gridWidgets = [
"Stats (waterfall, etc)", "Stats (waterfall, etc)",
true, true,
true, true,
"Stats" "Stats",
), ),
new gridWidget( new gridWidget(
active_audio_level, active_audio_level,
@ -75,7 +76,7 @@ const gridWidgets = [
"Rig control main", "Rig control main",
true, true,
true, true,
"Rig" "Rig",
), ),
new gridWidget( new gridWidget(
active_broadcats, active_broadcats,
@ -83,7 +84,7 @@ const gridWidgets = [
"Broadcats main", "Broadcats main",
true, true,
true, true,
"Broadcasts" "Broadcasts",
), ),
new gridWidget( new gridWidget(
mini_heard_stations, mini_heard_stations,
@ -91,16 +92,23 @@ const gridWidgets = [
"Mini heard stations list", "Mini heard stations list",
false, false,
true, true,
"Activity" "Activity",
),
new gridWidget(
s_meter,
{ x: 1, y: 1, w: 4, h: 8 },
"S-Meter",
false,
true,
"Rig",
), ),
new gridWidget(s_meter, { x: 1, y: 1, w: 4, h: 8 }, "S-Meter", false, true, "Rig"),
new gridWidget( new gridWidget(
dbfs_meter, dbfs_meter,
{ x: 1, y: 1, w: 4, h: 8 }, { x: 1, y: 1, w: 4, h: 8 },
"Dbfs Meter", "Dbfs Meter",
false, false,
true, true,
"Audio" "Audio",
), ),
new gridWidget( new gridWidget(
grid_activities, grid_activities,
@ -110,7 +118,6 @@ const gridWidgets = [
true, true,
"Activity", "Activity",
), ),
]; ];
onMounted(() => { onMounted(() => {
grid = GridStack.init({ grid = GridStack.init({
@ -137,23 +144,23 @@ onMounted(() => {
grid.on("change", onChange); grid.on("change", onChange);
gridWidgets.forEach((gw) =>{ gridWidgets.forEach((gw) => {
//Dynamically add widgets to widget menu //Dynamically add widgets to widget menu
let dom = document.getElementById("otherBod"); let dom = document.getElementById("otherBod");
switch (gw.category) { switch (gw.category) {
case "Activity": case "Activity":
dom = document.getElementById("actBody"); dom = document.getElementById("actBody");
break; break;
case "Stats": case "Stats":
dom = document.getElementById("statsBody"); dom = document.getElementById("statsBody");
break; break;
case "Audio": case "Audio":
dom = document.getElementById("audioBody"); dom = document.getElementById("audioBody");
break; break;
case "Rig": case "Rig":
dom = document.getElementById("rigBody"); dom = document.getElementById("rigBody");
break; break;
case "Broadcasts": case "Broadcasts":
dom = document.getElementById("bcBody"); dom = document.getElementById("bcBody");
break; break;
default: default:
@ -161,7 +168,7 @@ onMounted(() => {
break; break;
} }
var index = gridWidgets.findIndex((w) => gw.text == w.text); var index = gridWidgets.findIndex((w) => gw.text == w.text);
dom.insertAdjacentHTML("beforeend",`<div id="gridbtn-${index}""></div>`); dom.insertAdjacentHTML("beforeend", `<div id="gridbtn-${index}""></div>`);
let dom2 = document.getElementById(`gridbtn-${index}`); let dom2 = document.getElementById(`gridbtn-${index}`);
let vueComponent = h(grid_button,{btnText: gw.text,btnID:index}); let vueComponent = h(grid_button,{btnText: gw.text,btnID:index});
render(vueComponent,dom2); render(vueComponent,dom2);
@ -274,7 +281,7 @@ function quickfill() {
<h5 class="offcanvas-title" id="offcanvasGridItemsLabel"> <h5 class="offcanvas-title" id="offcanvasGridItemsLabel">
Manage grid widgets Manage grid widgets
</h5> </h5>
<button <button
type="button" type="button"
class="btn-close" class="btn-close"
@ -283,8 +290,10 @@ function quickfill() {
></button> ></button>
</div> </div>
<div class="offcanvas-body"> <div class="offcanvas-body">
<p>Grid widgets allow you to customize the display for your own usage. Here you may add additional widgets to fit your needs. <p>
You can move and resize the individual widgets! Grid widgets allow you to customize the display for your own usage. Here
you may add additional widgets to fit your needs. You can move and
resize the individual widgets!
</p> </p>
<div> <div>
<button <button
@ -294,10 +303,8 @@ function quickfill() {
> >
Fill grid with common widgets Fill grid with common widgets
</button> </button>
</div> </div>
<hr> <hr />
<!-- Begin widget selector --> <!-- Begin widget selector -->
<div class="accordion" id="accordionExample"> <div class="accordion" id="accordionExample">
<!-- Heard Stations --> <!-- Heard Stations -->
@ -320,9 +327,7 @@ function quickfill() {
aria-labelledby="headingHeardStations" aria-labelledby="headingHeardStations"
data-bs-parent="#accordionExample" data-bs-parent="#accordionExample"
> >
<div class="accordion-body" id="actBody"> <div class="accordion-body" id="actBody"></div>
</div>
</div> </div>
</div> </div>
@ -346,9 +351,7 @@ function quickfill() {
aria-labelledby="headingActivities" aria-labelledby="headingActivities"
data-bs-parent="#accordionExample" data-bs-parent="#accordionExample"
> >
<div class="accordion-body" id="audioBody"> <div class="accordion-body" id="audioBody"></div>
</div>
</div> </div>
</div> </div>
<!-- Broadcasts --> <!-- Broadcasts -->
@ -371,9 +374,7 @@ function quickfill() {
aria-labelledby="headingBroadcasts" aria-labelledby="headingBroadcasts"
data-bs-parent="#accordionExample" data-bs-parent="#accordionExample"
> >
<div class="accordion-body" id="bcBody"> <div class="accordion-body" id="bcBody"></div>
</div>
</div> </div>
</div> </div>
<!-- Radio Control --> <!-- Radio Control -->
@ -396,9 +397,7 @@ function quickfill() {
aria-labelledby="headingRadioControl" aria-labelledby="headingRadioControl"
data-bs-parent="#accordionExample" data-bs-parent="#accordionExample"
> >
<div class="accordion-body" id="rigBody"> <div class="accordion-body" id="rigBody"></div>
</div>
</div> </div>
</div> </div>
@ -422,9 +421,7 @@ function quickfill() {
aria-labelledby="headingAudioControl" aria-labelledby="headingAudioControl"
data-bs-parent="#accordionExample" data-bs-parent="#accordionExample"
> >
<div class="accordion-body" id="statsBody"> <div class="accordion-body" id="statsBody"></div>
</div>
</div> </div>
</div> </div>
@ -448,20 +445,18 @@ function quickfill() {
aria-labelledby="headingStatistics" aria-labelledby="headingStatistics"
data-bs-parent="#accordionExample" data-bs-parent="#accordionExample"
> >
<div class="accordion-body" id="otherBod"> <div class="accordion-body" id="otherBod"></div>
</div>
</div> </div>
</div> </div>
</div> </div>
<hr> <hr />
<button <button
class="btn btn-sm btn-outline-warning" class="btn btn-sm btn-outline-warning"
type="button" type="button"
@click="clearAllItems" @click="clearAllItems"
> >
Clear grid Clear grid
</button> </button>
</div> </div>
</div> </div>
</template> </template>

View file

@ -1,12 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { defineProps } from 'vue'; import { defineProps } from "vue";
const props = defineProps(['btnText','btnID']); const props = defineProps(["btnText", "btnID"]);
function emitClick() { function emitClick() {
window.dispatchEvent(new CustomEvent("add-widget", { detail: props.btnID })); window.dispatchEvent(new CustomEvent("add-widget", { detail: props.btnID }));
} }
</script> </script>
<template> <template>
<button class="btn btn-small btn-outline-secondary" v-on:click="emitClick">{{ btnText}}</button> <button class="btn btn-small btn-outline-secondary" v-on:click="emitClick">
{{ btnText }}
</button>
</template> </template>

View file

@ -13,7 +13,7 @@ class ARQSessionIRS(arq_session.ARQSession):
RETRIES_CONNECT = 3 RETRIES_CONNECT = 3
RETRIES_TRANSFER = 3 RETRIES_TRANSFER = 3
TIMEOUT_DATA = 2 TIMEOUT_DATA = 6
def __init__(self, config: dict, tx_frame_queue: queue.Queue, dxcall: str, session_id: int): def __init__(self, config: dict, tx_frame_queue: queue.Queue, dxcall: str, session_id: int):
super().__init__(config, tx_frame_queue, dxcall) super().__init__(config, tx_frame_queue, dxcall)
@ -35,7 +35,7 @@ class ARQSessionIRS(arq_session.ARQSession):
pass pass
def set_state(self, state): def set_state(self, state):
self.log(f"ARQ Session {self.id} state {self.state}") self.log(f"ARQ Session IRS {self.id} state {self.state}")
self.state = state self.state = state
def set_modem_decode_modes(self, modes): def set_modem_decode_modes(self, modes):
@ -50,24 +50,25 @@ class ARQSessionIRS(arq_session.ARQSession):
self.transmit_frame(ack_frame) self.transmit_frame(ack_frame)
self.set_modem_decode_modes(None) self.set_modem_decode_modes(None)
self.state = self.STATE_WAITING_DATA self.state = self.STATE_WAITING_DATA
while self.state == self.STATE_WAITING_DATA: while self.state == self.STATE_WAITING_DATA:
if not self.event_data_received.wait(self.TIMEOUT_DATA): if not self.event_data_received.wait(self.TIMEOUT_DATA):
self.log("Timeout waiting for data") self.log("Timeout waiting for data")
self.state = self.STATE_FAILED self.state = self.STATE_FAILED
return return
self.log("Finished ARQ IRS session") self.log("Finished ARQ IRS session")
def run(self): def run(self):
self.thread = threading.Thread(target=self.runner, name=f"ARQ IRS Session {self.id}", daemon=True) self.thread = threading.Thread(target=self.runner, name=f"ARQ IRS Session {self.id}", daemon=True)
self.thread.run() self.thread.start()
def on_data_received(self, data_frame): def on_data_received(self, data_frame):
if self.state != self.STATE_WAITING_DATA: if self.state != self.STATE_WAITING_DATA:
raise RuntimeError(f"ARQ Session: Received data while in state {self.state}") raise RuntimeError(f"ARQ Session: Received data while in state {self.state}, expected {self.STATE_WAITING_DATA}")
self.received_data = data_frame["data"]
self.event_data_received.set() self.event_data_received.set()
@ -87,3 +88,4 @@ class ARQSessionIRS(arq_session.ARQSession):
self.event_connection_ack_received.clear() self.event_connection_ack_received.clear()
self.event_transfer_feedback.set() self.event_transfer_feedback.set()
self.event_transfer_feedback.clear() self.event_transfer_feedback.clear()
self.received_data = b''

View file

@ -35,7 +35,7 @@ class ARQSessionISS(arq_session.ARQSession):
return random.randint(1,255) return random.randint(1,255)
def set_state(self, state): def set_state(self, state):
self.logger.info(f"ARQ Session {self.id} state {self.state}") self.logger.info(f"ARQ Session ISS {self.id} state {self.state}")
self.state = state self.state = state
def runner(self): def runner(self):
@ -49,7 +49,7 @@ class ARQSessionISS(arq_session.ARQSession):
self.thread.run() self.thread.run()
def connect(self): def connect(self):
self.state = self.STATE_CONNECTING self.state = self.STATE_CONNECTING
connect_frame = self.frame_factory.build_arq_session_connect(True, self.dxcall, self.id) connect_frame = self.frame_factory.build_arq_session_connect(True, self.dxcall, self.id)
@ -69,9 +69,10 @@ class ARQSessionISS(arq_session.ARQSession):
if self.state != self.STATE_CONNECTING: if self.state != self.STATE_CONNECTING:
raise RuntimeError(f"ARQ Session: Received connection ACK while in state {self.state}") raise RuntimeError(f"ARQ Session: Received connection ACK while in state {self.state}")
self.speed_level = ack['speed_level'] self.build_arq_data_framespeed_level = ack['speed_level']
self.event_connection_ack_received.set() self.event_connection_ack_received.set()
# Sends the full payload in multiple frames # Sends the full payload in multiple frames
def send_data(self): def send_data(self):
# Todo make this n frames per burst stuff part of the protocol again # Todo make this n frames per burst stuff part of the protocol again
@ -84,6 +85,7 @@ class ARQSessionISS(arq_session.ARQSession):
max_size = self.get_payload_size(self.speed_level) max_size = self.get_payload_size(self.speed_level)
end_offset = min(len(self.data), max_size) end_offset = min(len(self.data), max_size)
frame_payload = self.data[offset:end_offset] frame_payload = self.data[offset:end_offset]
print(self.id)
data_frame = self.frame_factory.build_arq_data_frame(self.id, n_frames_per_burst, max_size, n_frame, frame_payload) data_frame = self.frame_factory.build_arq_data_frame(self.id, n_frames_per_burst, max_size, n_frame, frame_payload)
self.set_state(self.STATE_SENDING) self.set_state(self.STATE_SENDING)
if not self.send_arq(data_frame): if not self.send_arq(data_frame):

View file

@ -127,12 +127,12 @@ class DataFrameFactory:
# arq data frame # arq data frame
# register n frames # register n frames
for n_frame in range(0,50): for n_frame in range(1,5):
self.template_list[FR_TYPE.BURST_01.value + n_frame] = { self.template_list[FR_TYPE.BURST_01.value + (n_frame-1)] = {
"frame_length": "dynamic", "frame_length": "dynamic",
"n_frames_per_burst": 1, "n_frames_per_burst": 1,
"session_id": 1, "session_id": 1,
"payload": "dynamic", "data": "dynamic",
} }
# arq burst ack # arq burst ack
@ -175,18 +175,19 @@ class DataFrameFactory:
def construct(self, frametype, content, frame_length=LENGTH_SIG1_FRAME): def construct(self, frametype, content, frame_length=LENGTH_SIG1_FRAME):
# frame_length: can be set manually for data frames, whose length can be dynamic regarding corresponding mode # frame_length: can be set manually for data frames, whose length can be dynamic regarding corresponding mode
# data bursts have a frame type range of 01-50 # data bursts have a frame type range of 01-50
if frametype in range(1, 50): if frametype in range(1, 50):
frame_template = self.template_list[frametype.value] frame_template = self.template_list[frametype]
frame = bytearray(frame_length) frame = bytearray(frame_length)
# override "dynamic" value of payload length # override "dynamic" value of payload length
self.template_list[frame_template].payload = frame_length - 3 self.template_list[frametype]["data"] = frame_length - 3
frame[:1] = bytes([frametype])
else: else:
frame_template = self.template_list[frametype.value] frame_template = self.template_list[frametype.value]
frame_length = frame_template["frame_length"] frame_length = frame_template["frame_length"]
frame = bytearray(frame_length) frame = bytearray(frame_length)
frame[:1] = bytes([frametype.value])
buffer_position = 1 buffer_position = 1
for key, item_length in frame_template.items(): for key, item_length in frame_template.items():
@ -194,7 +195,6 @@ class DataFrameFactory:
frame[buffer_position: buffer_position + item_length] = content[key] frame[buffer_position: buffer_position + item_length] = content[key]
buffer_position += item_length buffer_position += item_length
frame[:1] = bytes([frametype.value])
return frame return frame
def deconstruct(self, frame): def deconstruct(self, frame):
@ -212,16 +212,24 @@ class DataFrameFactory:
for key, item_length in frame_template.items(): for key, item_length in frame_template.items():
if key != "frame_length": if key != "frame_length":
data = frame[buffer_position: buffer_position + item_length] # data is always on the last payload slots
if item_length in ["dynamic"] and key in["data"]:
data = frame[buffer_position:]
item_length = len(data)
else:
data = frame[buffer_position: buffer_position + item_length]
# Process the data based on the key # Process the data based on the key
if key in ["origin", "destination"]: if key in ["origin", "destination"]:
extracted_data[key] = helpers.bytes_to_callsign(data).decode() extracted_data[key] = helpers.bytes_to_callsign(data).decode()
if key in ["origin_crc", "destination_crc"]:
extracted_data[key] = data.hex()
elif key == "gridsquare": elif key == "gridsquare":
extracted_data[key] = helpers.decode_grid(data) extracted_data[key] = helpers.decode_grid(data)
elif key in ["session_id", "speed_level"]: elif key in ["session_id", "speed_level", "n_frames_per_burst"]:
extracted_data[key] = int.from_bytes(data, 'big') extracted_data[key] = int.from_bytes(data, 'big')
else: else:
@ -319,9 +327,7 @@ class DataFrameFactory:
return test_frame return test_frame
def build_arq_session_connect(self, isWideband, destination, session_id): def build_arq_session_connect(self, isWideband, destination, session_id):
print(isWideband)
print(destination)
print(session_id)
payload = { payload = {
"destination_crc": helpers.get_crc_24(destination), "destination_crc": helpers.get_crc_24(destination),
"origin_crc": helpers.get_crc_24(self.myfullcall), "origin_crc": helpers.get_crc_24(self.myfullcall),
@ -351,11 +357,11 @@ class DataFrameFactory:
def build_arq_data_frame(self, session_id: bytes, n_frames_per_burst: int, max_size: int, n_frame: int, frame_payload: bytes): def build_arq_data_frame(self, session_id: bytes, n_frames_per_burst: int, max_size: int, n_frame: int, frame_payload: bytes):
payload = { payload = {
"n_frames_per_burst": bytes([n_frames_per_burst]), "n_frames_per_burst": bytes([n_frames_per_burst]),
"session_id": session_id, "session_id": session_id.to_bytes(1, 'big'),
"data": frame_payload "data": frame_payload
} }
return self.construct(FR_TYPE.FR_TYPE.BURST_01.value + n_frame, payload, frame_length=max_size) return self.construct(FR_TYPE.BURST_01.value + (n_frame-1), payload, frame_length=max_size)
def build_arq_burst_ack(self, session_id: bytes, snr: int, speed_level: int, len_arq_rx_frame_buffer: int): def build_arq_burst_ack(self, session_id: bytes, snr: int, speed_level: int, len_arq_rx_frame_buffer: int):

View file

@ -35,6 +35,7 @@ class DISPATCHER():
FR_TYPE.ARQ_CONNECTION_OPEN.value: {"class": ARQFrameHandler, "name": "ARQ OPEN SESSION"}, FR_TYPE.ARQ_CONNECTION_OPEN.value: {"class": ARQFrameHandler, "name": "ARQ OPEN SESSION"},
FR_TYPE.ARQ_STOP.value: {"class": ARQFrameHandler, "name": "ARQ STOP TX"}, FR_TYPE.ARQ_STOP.value: {"class": ARQFrameHandler, "name": "ARQ STOP TX"},
FR_TYPE.BEACON.value: {"class": FrameHandler, "name": "BEACON"}, FR_TYPE.BEACON.value: {"class": FrameHandler, "name": "BEACON"},
FR_TYPE.BURST_01.value:{"class": ARQFrameHandler, "name": "BURST_01"},
FR_TYPE.BURST_ACK.value: {"class": FrameHandler, "name": "BURST ACK"}, FR_TYPE.BURST_ACK.value: {"class": FrameHandler, "name": "BURST ACK"},
FR_TYPE.BURST_NACK.value: {"class": FrameHandler, "name": "BURST NACK"}, FR_TYPE.BURST_NACK.value: {"class": FrameHandler, "name": "BURST NACK"},
FR_TYPE.CQ.value: {"class": CQFrameHandler, "name": "CQ"}, FR_TYPE.CQ.value: {"class": CQFrameHandler, "name": "CQ"},

View file

@ -23,3 +23,9 @@ class ARQFrameHandler(frame_handler.FrameHandler):
if frame['frame_type_int'] in [FR.ARQ_SESSION_OPEN_ACK_N.value, FR.ARQ_SESSION_OPEN_ACK_W.value]: if frame['frame_type_int'] in [FR.ARQ_SESSION_OPEN_ACK_N.value, FR.ARQ_SESSION_OPEN_ACK_W.value]:
iss_session:ARQSessionISS = self.states.get_arq_iss_session(frame['session_id']) iss_session:ARQSessionISS = self.states.get_arq_iss_session(frame['session_id'])
iss_session.on_connection_ack_received(frame) iss_session.on_connection_ack_received(frame)
# ARQ session data frame received
if frame['frame_type_int'] in [FR.BURST_01.value, FR.BURST_02.value, FR.BURST_03.value, FR.BURST_04.value, FR.BURST_05.value]:
print("received data frame....")
irs_session:ARQSessionIRS = self.states.get_arq_irs_session(frame['session_id'])
irs_session.on_data_received(frame)

View file

@ -11,8 +11,7 @@ class FRAME_TYPE(Enum):
BURST_02 = 2 BURST_02 = 2
BURST_03 = 3 BURST_03 = 3
BURST_04 = 4 BURST_04 = 4
# ... BURST_05 = 5
BURST_50 = 50
BURST_ACK = 60 BURST_ACK = 60
FR_ACK = 61 FR_ACK = 61
FR_REPEAT = 62 FR_REPEAT = 62