mirror of
https://github.com/DJ2LS/FreeDATA
synced 2024-05-14 08:04:33 +00:00
some more mesh additions
This commit is contained in:
parent
c174a543fd
commit
24392a62dd
|
@ -165,8 +165,7 @@ def add_to_heard_stations(dxcallsign, dxgrid, datatype, snr, offset, frequency):
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
# trigger update of routing table
|
|
||||||
mesh.MeshRouter.get_from_heard_stations()
|
|
||||||
|
|
||||||
# for idx, item in enumerate(TNC.heard_stations):
|
# for idx, item in enumerate(TNC.heard_stations):
|
||||||
# if dxcallsign in item:
|
# if dxcallsign in item:
|
||||||
|
|
|
@ -33,7 +33,7 @@ from static import ARQ, AudioParam, Beacon, Channel, Daemon, HamlibParam, ModemP
|
||||||
import structlog
|
import structlog
|
||||||
import explorer
|
import explorer
|
||||||
import json
|
import json
|
||||||
|
import mesh
|
||||||
log = structlog.get_logger("main")
|
log = structlog.get_logger("main")
|
||||||
|
|
||||||
def signal_handler(sig, frame):
|
def signal_handler(sig, frame):
|
||||||
|
@ -404,6 +404,9 @@ if __name__ == "__main__":
|
||||||
# start modem
|
# start modem
|
||||||
modem = modem.RF()
|
modem = modem.RF()
|
||||||
|
|
||||||
|
# start mesh module
|
||||||
|
mesh = mesh.MeshRouter()
|
||||||
|
|
||||||
# optionally start explorer module
|
# optionally start explorer module
|
||||||
if TNC.enable_explorer:
|
if TNC.enable_explorer:
|
||||||
log.info("[EXPLORER] Publishing to https://explorer.freedata.app", state=TNC.enable_explorer)
|
log.info("[EXPLORER] Publishing to https://explorer.freedata.app", state=TNC.enable_explorer)
|
||||||
|
|
131
tnc/mesh.py
131
tnc/mesh.py
|
@ -16,11 +16,27 @@ HF mesh networking prototype and testing module
|
||||||
# pylint: disable=invalid-name, line-too-long, c-extension-no-member
|
# pylint: disable=invalid-name, line-too-long, c-extension-no-member
|
||||||
# pylint: disable=import-outside-toplevel, attribute-defined-outside-init
|
# pylint: disable=import-outside-toplevel, attribute-defined-outside-init
|
||||||
|
|
||||||
from static import TNC, MeshParam
|
from static import TNC, MeshParam, FRAME_TYPE, Station, ModemParam
|
||||||
|
from codec2 import FREEDV_MODE
|
||||||
|
import numpy as np
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
|
import modem
|
||||||
|
import helpers
|
||||||
|
from queues import MESH_RECEIVED_QUEUE
|
||||||
|
|
||||||
class MeshRouter():
|
class MeshRouter():
|
||||||
|
def __init__(self):
|
||||||
|
self.mesh_broadcasting_thread = threading.Thread(
|
||||||
|
target=self.broadcast_routing_table, name="worker thread receive", daemon=True
|
||||||
|
)
|
||||||
|
self.mesh_broadcasting_thread.start()
|
||||||
|
|
||||||
|
self.mesh_rx_dispatcher_thread = threading.Thread(
|
||||||
|
target=self.mesh_rx_dispatcher, name="worker thread receive", daemon=True
|
||||||
|
)
|
||||||
|
self.mesh_rx_dispatcher_thread.start()
|
||||||
|
|
||||||
|
|
||||||
def get_from_heard_stations(self):
|
def get_from_heard_stations(self):
|
||||||
"""
|
"""
|
||||||
|
@ -48,9 +64,9 @@ class MeshRouter():
|
||||||
offset = 5
|
offset = 5
|
||||||
frequency = 6
|
frequency = 6
|
||||||
|
|
||||||
|
|
||||||
for item in TNC.heard_stations:
|
for item in TNC.heard_stations:
|
||||||
new_router = [item[dxcallsign], 'direct', 0, item[snr], item[snr], item[timestamp]]
|
new_router = [helpers.get_crc_24(item[dxcallsign]), helpers.get_crc_24(b'direct'), 0, int(item[snr]), int(item[snr]), item[timestamp]]
|
||||||
|
|
||||||
self.add_router_to_routing_table(new_router)
|
self.add_router_to_routing_table(new_router)
|
||||||
|
|
||||||
def add_router_to_routing_table(self, new_router):
|
def add_router_to_routing_table(self, new_router):
|
||||||
|
@ -64,3 +80,112 @@ class MeshRouter():
|
||||||
# add new routing entry if not exists
|
# add new routing entry if not exists
|
||||||
if new_router not in MeshParam.routing_table:
|
if new_router not in MeshParam.routing_table:
|
||||||
MeshParam.routing_table.append(new_router)
|
MeshParam.routing_table.append(new_router)
|
||||||
|
|
||||||
|
def broadcast_routing_table(self, interval=60):
|
||||||
|
# enable receiving for datac4 if broadcasting
|
||||||
|
modem.RECEIVE_DATAC4 = True
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# wait some time until sending routing table
|
||||||
|
threading.Event().wait(interval)
|
||||||
|
|
||||||
|
# before we are transmitting, let us update our routing table
|
||||||
|
self.get_from_heard_stations()
|
||||||
|
|
||||||
|
#[b'DJ2LS-0', 'direct', 0, 9.6, 9.6, 1684912305]
|
||||||
|
mesh_broadcast_frame_header = bytearray(4)
|
||||||
|
mesh_broadcast_frame_header[:1] = bytes([FRAME_TYPE.MESH_BROADCAST.value])
|
||||||
|
mesh_broadcast_frame_header[1:4] = helpers.get_crc_24(Station.mycallsign)
|
||||||
|
|
||||||
|
# callsign(6), router(6), hops(1), path_score(1) == 14 ==> 14 28 42 ==> 3 mesh routing entries
|
||||||
|
# callsign_crc(3), router_crc(3), hops(1), path_score(1) == 8 --> 6
|
||||||
|
# callsign_crc(3), hops(1), path_score(1) == 5 --> 10
|
||||||
|
|
||||||
|
# Create a new bytearray with a fixed length of 50
|
||||||
|
result = bytearray(50)
|
||||||
|
|
||||||
|
# Iterate over the route subarrays and add the selected entries to the result bytearray
|
||||||
|
index = 0
|
||||||
|
for route in MeshParam.routing_table:
|
||||||
|
dxcall = MeshParam.routing_table[route][0]
|
||||||
|
# router = MeshParam.routing_table[i][1]
|
||||||
|
hops = MeshParam.routing_table[route][2]
|
||||||
|
# snr = MeshParam.routing_table[i][3]
|
||||||
|
route_score = np.clip(MeshParam.routing_table[route][4], 0, 254)
|
||||||
|
# timestamp = MeshParam.routing_table[i][5]
|
||||||
|
result[index:index + 3] = dxcall + hops + route_score
|
||||||
|
index += len(route[0])
|
||||||
|
index += len(route[2])
|
||||||
|
index += len(route[4])
|
||||||
|
|
||||||
|
# Split the result bytearray into a list of fixed-length bytearrays
|
||||||
|
split_result = [result[i:i + 50] for i in range(0, len(result), 50)]
|
||||||
|
|
||||||
|
frame_list = []
|
||||||
|
for _ in split_result:
|
||||||
|
# make sure payload is always 50
|
||||||
|
_[len(_):] = bytes(50 - len(_))
|
||||||
|
#print(_)
|
||||||
|
#print(len(_))
|
||||||
|
frame_list.apppend(mesh_broadcast_frame_header + _)
|
||||||
|
|
||||||
|
print(frame_list)
|
||||||
|
TNC.transmitting = True
|
||||||
|
c2_mode = FREEDV_MODE.datac4.value
|
||||||
|
modem.MODEM_TRANSMIT_QUEUE.put([c2_mode, 1, 0, [frame_list]])
|
||||||
|
|
||||||
|
# Wait while transmitting
|
||||||
|
while TNC.transmitting:
|
||||||
|
threading.Event().wait(0.01)
|
||||||
|
|
||||||
|
def mesh_rx_dispatcher(self):
|
||||||
|
while True:
|
||||||
|
data_in = MESH_RECEIVED_QUEUE.get()
|
||||||
|
if int.from_bytes(data_in[:1], "big") in [FRAME_TYPE.MESH_BROADCAST.value]:
|
||||||
|
self.received_routing_table(data_in)
|
||||||
|
else:
|
||||||
|
print("wrong mesh data received")
|
||||||
|
print(data_in)
|
||||||
|
def received_routing_table(self, data_in):
|
||||||
|
print("data received........")
|
||||||
|
print(data_in)
|
||||||
|
|
||||||
|
router = data_in[1:4] # Extract the first 4 bytes (header)
|
||||||
|
payload = data_in[4:] # Extract the payload (excluding the header)
|
||||||
|
|
||||||
|
print("Router:", router) # Output the header bytes
|
||||||
|
|
||||||
|
for i in range(0, len(payload), 5):
|
||||||
|
callsign_checksum = payload[i:i + 3] # First 3 bytes of the information (callsign_checksum)
|
||||||
|
hops = payload[i + 3] # Fourth byte of the information (hops)
|
||||||
|
score = payload[i + 4] # Fifth byte of the information (score)
|
||||||
|
timestamp = int(time.time())
|
||||||
|
snr = int(ModemParam.snr)
|
||||||
|
print("Callsign Checksum:", callsign_checksum)
|
||||||
|
print("Hops:", hops)
|
||||||
|
print("Score:", score)
|
||||||
|
|
||||||
|
# use case 1: add new router to table only if callsign not empty
|
||||||
|
_use_case1 = callsign_checksum.startswith(b'\x00')
|
||||||
|
|
||||||
|
# use case 2: add new router to table only if not own callsign
|
||||||
|
_use_case2 = callsign_checksum not in [helpers.get_crc_24(Station.mycallsign)]
|
||||||
|
|
||||||
|
# use case 3: increment hop if not direct
|
||||||
|
if router not in [helpers.get_crc_24(b'direct')] and hops == 0:
|
||||||
|
hops += 1
|
||||||
|
|
||||||
|
# use case 4: calculate score
|
||||||
|
# TODO...
|
||||||
|
|
||||||
|
if not _use_case1 \
|
||||||
|
and _use_case2:
|
||||||
|
|
||||||
|
new_router = [callsign_checksum, router, hops, snr, score, timestamp]
|
||||||
|
print(new_router)
|
||||||
|
self.add_router_to_routing_table(new_router)
|
||||||
|
|
||||||
|
print("-------------------------")
|
||||||
|
for _ in MeshParam.routing_table:
|
||||||
|
print(MeshParam.routing_table[_])
|
||||||
|
print("-------------------------")
|
11
tnc/modem.py
11
tnc/modem.py
|
@ -29,7 +29,7 @@ import structlog
|
||||||
import ujson as json
|
import ujson as json
|
||||||
import tci
|
import tci
|
||||||
from queues import DATA_QUEUE_RECEIVED, MODEM_RECEIVED_QUEUE, MODEM_TRANSMIT_QUEUE, RIGCTLD_COMMAND_QUEUE, \
|
from queues import DATA_QUEUE_RECEIVED, MODEM_RECEIVED_QUEUE, MODEM_TRANSMIT_QUEUE, RIGCTLD_COMMAND_QUEUE, \
|
||||||
AUDIO_RECEIVED_QUEUE, AUDIO_TRANSMIT_QUEUE
|
AUDIO_RECEIVED_QUEUE, AUDIO_TRANSMIT_QUEUE, MESH_RECEIVED_QUEUE
|
||||||
|
|
||||||
TESTMODE = False
|
TESTMODE = False
|
||||||
RXCHANNEL = ""
|
RXCHANNEL = ""
|
||||||
|
@ -811,6 +811,7 @@ class RF:
|
||||||
audiobuffer.pop(nin)
|
audiobuffer.pop(nin)
|
||||||
nin = codec2.api.freedv_nin(freedv)
|
nin = codec2.api.freedv_nin(freedv)
|
||||||
if nbytes == bytes_per_frame:
|
if nbytes == bytes_per_frame:
|
||||||
|
print(bytes(bytes_out))
|
||||||
|
|
||||||
# process commands only if TNC.listen = True
|
# process commands only if TNC.listen = True
|
||||||
if TNC.listen:
|
if TNC.listen:
|
||||||
|
@ -827,6 +828,14 @@ class RF:
|
||||||
FRAME_TYPE.ARQ_DC_OPEN_ACK_N.value
|
FRAME_TYPE.ARQ_DC_OPEN_ACK_N.value
|
||||||
]:
|
]:
|
||||||
print("dropp")
|
print("dropp")
|
||||||
|
elif int.from_bytes(bytes(bytes_out[:1]), "big") in [
|
||||||
|
FRAME_TYPE.MESH_BROADCAST.value
|
||||||
|
]:
|
||||||
|
self.log.debug(
|
||||||
|
"[MDM] [demod_audio] moving data to mesh dispatcher", nbytes=nbytes
|
||||||
|
)
|
||||||
|
MESH_RECEIVED_QUEUE.put(bytes(bytes_out))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
"[MDM] [demod_audio] Pushing received data to received_queue", nbytes=nbytes
|
"[MDM] [demod_audio] Pushing received data to received_queue", nbytes=nbytes
|
||||||
|
|
|
@ -12,6 +12,9 @@ DATA_QUEUE_RECEIVED = queue.Queue()
|
||||||
MODEM_RECEIVED_QUEUE = queue.Queue()
|
MODEM_RECEIVED_QUEUE = queue.Queue()
|
||||||
MODEM_TRANSMIT_QUEUE = queue.Queue()
|
MODEM_TRANSMIT_QUEUE = queue.Queue()
|
||||||
|
|
||||||
|
# Initialize FIFO queue to store received frames
|
||||||
|
MESH_RECEIVED_QUEUE = queue.Queue()
|
||||||
|
|
||||||
# Initialize FIFO queue to store audio frames
|
# Initialize FIFO queue to store audio frames
|
||||||
AUDIO_RECEIVED_QUEUE = queue.Queue()
|
AUDIO_RECEIVED_QUEUE = queue.Queue()
|
||||||
AUDIO_TRANSMIT_QUEUE = queue.Queue()
|
AUDIO_TRANSMIT_QUEUE = queue.Queue()
|
||||||
|
|
|
@ -130,7 +130,7 @@ class TCIParam:
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TNC:
|
class TNC:
|
||||||
version = "0.9.2-alpha.5"
|
version = "0.10.0-alpha.1-mesh"
|
||||||
host: str = "0.0.0.0"
|
host: str = "0.0.0.0"
|
||||||
port: int = 3000
|
port: int = 3000
|
||||||
SOCKET_TIMEOUT: int = 1 # seconds
|
SOCKET_TIMEOUT: int = 1 # seconds
|
||||||
|
@ -162,6 +162,7 @@ class FRAME_TYPE(Enum):
|
||||||
FR_REPEAT = 62
|
FR_REPEAT = 62
|
||||||
FR_NACK = 63
|
FR_NACK = 63
|
||||||
BURST_NACK = 64
|
BURST_NACK = 64
|
||||||
|
MESH_BROADCAST = 100
|
||||||
CQ = 200
|
CQ = 200
|
||||||
QRV = 201
|
QRV = 201
|
||||||
PING = 210
|
PING = 210
|
||||||
|
|
Loading…
Reference in a new issue