mirror of
https://github.com/DJ2LS/FreeDATA
synced 2024-05-14 08:04:33 +00:00
commit
f118a3878a
7 changed files with 216 additions and 111 deletions
87
README.md
87
README.md
|
@ -2,13 +2,6 @@
|
|||
## FreeDV- Just Another TNC Experiment
|
||||
My first attempt to learn more about FreeDV and how to create a TNC which gets data from a TCP/IP socket
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
David Rowe and the FreeDV team for developing the modem and libraries
|
||||
FreeDV Codec 2 : https://github.com/drowe67/codec2
|
||||
This software has been heavily inspired by https://github.com/xssfox/freedv-tnc/
|
||||
|
||||
## ToDo
|
||||
|
||||
- [x] ARQ: Stop-And-Wait
|
||||
|
@ -16,17 +9,15 @@ This software has been heavily inspired by https://github.com/xssfox/freedv-tnc/
|
|||
- [x] ARQ: Selective repeating of lost arq frames
|
||||
- [x] ARQ: Dynamic number of frames per burst
|
||||
- [ ] ARQ: Set frames per burst automatically by channel quality
|
||||
- [ ] SETTING: Callsign with selective receiveing
|
||||
- [ ] SETTING: Own grid locator
|
||||
- [ ] SOCKET: Set settings & commands via command socket
|
||||
- [ ] SOCKET: Receive data via data socket
|
||||
- [x] SOCKET: Run commands via TCP/IP socket
|
||||
- [ ] TRX: Control radio via hamlib
|
||||
- [ ] MODE: Beacon
|
||||
- [ ] MODE: Broadcast
|
||||
- [ ] MODE: ARQ AX25
|
||||
- [ ] MODE: Gear shifting ARQ
|
||||
- [ ] TNC: CLI GUI for basic settings
|
||||
|
||||
- [ ] TNC: Multicore support
|
||||
- [ ] MODEM: Sample rate conversion
|
||||
|
||||
## Setup
|
||||
Install FreeDV-Socket-TNC directly to home folder and compile codec2 automatically
|
||||
|
@ -39,26 +30,73 @@ chmod +x ~/install_socket_tnc.sh
|
|||
|
||||
## Usage main program
|
||||
```
|
||||
./main.py --port 3000 --tx 1 --rx 1
|
||||
./main.py --port 3000 --tx 1 --rx 1 --mode 12
|
||||
```
|
||||
|
||||
## Usage testclient
|
||||
## Usage TCP/IP socket client
|
||||
```
|
||||
./socketclient.py --port 3000 --data "BC: hello"
|
||||
python3 readfromsocket.py --port 3000 --data "GET:RX_BUFFER:0
|
||||
```
|
||||
|
||||
|
||||
## Socket Commands
|
||||
|
||||
Send a simple broadcast
|
||||
#### SOCKETTEST
|
||||
Message for testing purposes which repeats:
|
||||
```
|
||||
BC:<DATA>
|
||||
SOCKETTEST
|
||||
```
|
||||
Send an ARQ like frame which will ask the receiver for acknowledgement
|
||||
"WELL DONE! YOU ARE ABLE TO COMMUNICATE WITH THE TNC"
|
||||
|
||||
|
||||
#### TRANSMIT ARQ MESSAGE 'HELLO!'
|
||||
```
|
||||
ACK:<DATA>
|
||||
ARQ:HELLO!
|
||||
```
|
||||
|
||||
#### SET NEW CALLSIGN
|
||||
```
|
||||
SET:MYCALLSIGN:AA1AA
|
||||
```
|
||||
|
||||
#### GET CALLSIGN
|
||||
```
|
||||
GET:MYCALLSIGN
|
||||
```
|
||||
|
||||
#### GET CALLSIGN CRC8
|
||||
```
|
||||
GET:MYCALLSIGN_CRC8
|
||||
```
|
||||
|
||||
#### GET DX CALLSIGN
|
||||
```
|
||||
GET:DXCALLSIGN
|
||||
```
|
||||
|
||||
#### GET ARQ STATE
|
||||
```
|
||||
GET:ARQ_STATE
|
||||
```
|
||||
|
||||
#### GET RX BUFFER LENGTH / SIZE
|
||||
```
|
||||
GET:RX_BUFFER_LENGTH
|
||||
```
|
||||
|
||||
#### GET RX BUFFER
|
||||
```
|
||||
GET:RX_BUFFER:POSITION
|
||||
```
|
||||
Position = 0 --> Latest Data
|
||||
Position 1-N --> Buffer positions
|
||||
|
||||
#### DELETE RX BUFFER
|
||||
```
|
||||
DEL:RX_BUFFER
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## Other stuff
|
||||
|
||||
|
@ -76,3 +114,12 @@ sudo modprobe snd-aloop index=1,2 enable=1,1 pcm_substreams=1,1 id=CHAT1,CHAT2
|
|||
./main.py --port 3001 --tx 2 --rx 2
|
||||
```
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
David Rowe and the FreeDV team for developing the modem and libraries
|
||||
FreeDV Codec 2 : https://github.com/drowe67/codec2
|
||||
|
||||
|
||||
This software has been inspired by https://github.com/xssfox/freedv-tnc/
|
||||
|
||||
|
|
6
arq.py
6
arq.py
|
@ -323,7 +323,7 @@ def transmit(data_out):
|
|||
|
||||
#--------------- BREAK LOOP IF FRAME ACK HAS BEEN RECEIVED EARLIER AS EXPECTED
|
||||
elif static.ARQ_FRAME_ACK_RECEIVED == True:
|
||||
logging.info("----------------------------------------------------------")
|
||||
|
||||
logging.info("ARQ | RX | EARLY FRAME ACK RECEIVED")
|
||||
|
||||
#static.ARQ_N_SENT_FRAMES = #static.TX_BUFFER_SIZE
|
||||
|
@ -363,11 +363,13 @@ def transmit(data_out):
|
|||
# ----------- if no ACK received and out of retries.....stop frame sending
|
||||
if static.ARQ_ACK_RECEIVED == False and static.ARQ_FRAME_ACK_RECEIVED == False and static.ARQ_RX_ACK_TIMEOUT == True:
|
||||
logging.error("ARQ | TX | NO BURST OR FRAME ACK RECEIVED | DATA SHOULD BE RESEND!")
|
||||
logging.error("----------------------------------------------------------")
|
||||
break
|
||||
|
||||
#-------------------------BREAK TX BUFFER LOOP IF ALL PACKETS HAVE BEEN SENT AND WE GOT A FRAME ACK
|
||||
elif static.ARQ_N_SENT_FRAMES == static.TX_BUFFER_SIZE and static.ARQ_FRAME_ACK_RECEIVED == True:
|
||||
logging.log(25,"ARQ | RX | FRAME ACK RECEIVED - DATA TRANSMITTED! :-)")
|
||||
logging.log(25,"----------------------------------------------------------")
|
||||
break
|
||||
|
||||
else:
|
||||
|
@ -394,7 +396,7 @@ def transmit(data_out):
|
|||
# BURST MACHINE TO DEFINE N BURSTS PER FRAME ---> LATER WE CAN USE CHANNEL MESSUREMENT TO SET FRAMES PER BURST
|
||||
def get_n_frames_per_burst():
|
||||
#n_frames_per_burst = randrange(1,10)
|
||||
n_frames_per_burst = 2
|
||||
n_frames_per_burst = 5
|
||||
return n_frames_per_burst
|
||||
|
||||
|
||||
|
|
29
main.py
29
main.py
|
@ -12,9 +12,17 @@ import argparse
|
|||
import logging
|
||||
import threading
|
||||
|
||||
|
||||
import static
|
||||
import helpers
|
||||
|
||||
def client(ip, port, message):
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
sock.connect((ip, port))
|
||||
sock.sendall(bytes(message, 'ascii'))
|
||||
response = str(sock.recv(1024), 'ascii')
|
||||
print("Received: {}".format(response))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
|
@ -25,21 +33,6 @@ if __name__ == '__main__':
|
|||
helpers.list_audio_devices()
|
||||
|
||||
|
||||
|
||||
static.MYCALLSIGN = b'DJ2LS'
|
||||
static.MYCALLSIGN_CRC8 = helpers.get_crc_8(static.MYCALLSIGN)
|
||||
|
||||
static.DXCALLSIGN = b'DH3WO'
|
||||
static.DXCALLSIGN_CRC8 = helpers.get_crc_8(static.DXCALLSIGN)
|
||||
|
||||
print("MYCALLSIGN " + str(static.MYCALLSIGN))
|
||||
print("MYCALLSIGN_CRC8 " + str(static.MYCALLSIGN_CRC8))
|
||||
|
||||
print("DXCALLSIGN " + str(static.DXCALLSIGN))
|
||||
print("DXCALLSIGN_CRC8 " + str(static.DXCALLSIGN_CRC8))
|
||||
|
||||
|
||||
|
||||
#--------------------------------------------GET PARAMETER INPUTS
|
||||
parser = argparse.ArgumentParser(description='Simons TEST TNC')
|
||||
parser.add_argument('--rx', dest="audio_input_device", default=0, help="sound card for listening.", type=int)
|
||||
|
@ -49,20 +42,16 @@ if __name__ == '__main__':
|
|||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
#--------------------------------------------START CMD & DATA SERVER
|
||||
static.FREEDV_DATA_MODE = args.freedv_data_mode
|
||||
static.AUDIO_INPUT_DEVICE = args.audio_input_device
|
||||
static.AUDIO_OUTPUT_DEVICE = args.audio_output_device
|
||||
static.PORT = args.socket_port
|
||||
|
||||
#--------------------------------------------START CMD SERVER
|
||||
import sock # we need to wait until we got all parameters from argparse
|
||||
|
||||
cmd_server_thread = threading.Thread(target=sock.start_cmd_socket, name="cmd server")
|
||||
cmd_server_thread.start()
|
||||
|
||||
data_server_thread = threading.Thread(target=sock.start_data_socket, name="data server")
|
||||
data_server_thread.start()
|
||||
|
||||
|
||||
|
||||
|
|
35
readfromsocket.py
Normal file
35
readfromsocket.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Fri Dec 11 21:53:35 2020
|
||||
|
||||
@author: parallels
|
||||
"""
|
||||
|
||||
import socket
|
||||
import sys
|
||||
import argparse
|
||||
import time
|
||||
|
||||
|
||||
|
||||
|
||||
#--------------------------------------------GET PARAMETER INPUTS
|
||||
parser = argparse.ArgumentParser(description='Simons TEST TNC')
|
||||
parser.add_argument('--port', dest="socket_port", default=3000, help="Set the port, the socket is listening on.", type=int)
|
||||
parser.add_argument('--data', dest="data", default=False, help="data", type=str)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
ip, port = "localhost", args.socket_port
|
||||
message = args.data
|
||||
|
||||
|
||||
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
print(ip)
|
||||
print(port)
|
||||
sock.connect((ip, port))
|
||||
sock.sendall(bytes(message, 'utf-8'))
|
||||
response = str(sock.recv(1024), 'utf-8')
|
||||
print("Received: {}".format(response))
|
129
sock.py
129
sock.py
|
@ -9,66 +9,107 @@ Created on Fri Dec 25 21:25:14 2020
|
|||
import socketserver
|
||||
import threading
|
||||
import logging
|
||||
|
||||
import time
|
||||
|
||||
import static
|
||||
import arq
|
||||
import helpers
|
||||
|
||||
class DATATCPRequestHandler(socketserver.BaseRequestHandler):
|
||||
class CMDTCPRequestHandler(socketserver.BaseRequestHandler):
|
||||
|
||||
def handle(self):
|
||||
|
||||
self.data = bytes()
|
||||
while True:
|
||||
chunk = self.request.recv(8192)#.strip()
|
||||
self.data += chunk
|
||||
if chunk.endswith(b'\n'):
|
||||
break
|
||||
encoding = 'utf-8'
|
||||
data = str(self.request.recv(1024), 'utf-8')
|
||||
|
||||
# SEND AN ARQ FRAME -------------------------
|
||||
if self.data.startswith(b'ARQ:'):
|
||||
# SOCKETTEST
|
||||
if data == 'SOCKETTEST':
|
||||
cur_thread = threading.current_thread()
|
||||
response = bytes("WELL DONE! YOU ARE ABLE TO COMMUNICATE WITH THE TNC", encoding)
|
||||
self.request.sendall(response)
|
||||
|
||||
data = self.data.split(b'ARQ:')
|
||||
data_out = data[1]
|
||||
# TRANSMIT ARQ MESSAGE
|
||||
if data.startswith('ARQ:'):
|
||||
logging.info("CMD | NEW ARQ DATA")
|
||||
|
||||
arqdata = data.split('ARQ:')
|
||||
data_out = bytes(arqdata[1], 'utf-8')
|
||||
|
||||
TRANSMIT_ARQ = threading.Thread(target=arq.transmit, args=[data_out], name="TRANSMIT_ARQ")
|
||||
TRANSMIT_ARQ.start()
|
||||
|
||||
|
||||
|
||||
class CMDTCPRequestHandler(socketserver.BaseRequestHandler):
|
||||
|
||||
def handle(self):
|
||||
|
||||
self.data = bytes()
|
||||
while True:
|
||||
chunk = self.request.recv(8192)#.strip()
|
||||
self.data += chunk
|
||||
if chunk.endswith(b'\n'):
|
||||
break
|
||||
# SETTINGS AND STATUS
|
||||
if data.startswith('SET:MYCALLSIGN:'):
|
||||
data = data.split('SET:MYCALLSIGN:')
|
||||
if bytes(data[1], encoding) == b'':
|
||||
self.request.sendall(b'INVALID CALLSIGN')
|
||||
else:
|
||||
static.MYCALLSIGN = bytes(data[1], encoding)
|
||||
static.MYCALLSIGN_CRC8 = helpers.get_crc_8(static.MYCALLSIGN)
|
||||
self.request.sendall(static.MYCALLSIGN)
|
||||
logging.info("CMD | MYCALLSIGN: " + str(static.MYCALLSIGN))
|
||||
|
||||
|
||||
if data == 'GET:MYCALLSIGN':
|
||||
self.request.sendall(bytes(static.MYCALLSIGN, encoding))
|
||||
|
||||
# self.request is the TCP socket connected to the client
|
||||
#self.data = self.request.recv(1024).strip()
|
||||
### self.data = self.request.recv(1000000).strip()
|
||||
if data == 'GET:MYCALLSIGN_CRC8':
|
||||
self.request.sendall(bytes(static.MYCALLSIGN_CRC8, encoding))
|
||||
|
||||
# interrupt listening loop "while true" by setting MODEM_RECEIVE to False
|
||||
#if len(self.data) > 0:
|
||||
# static.MODEM_RECEIVE = False
|
||||
if data == 'GET:DXCALLSIGN':
|
||||
self.request.sendall(bytes(static.DXCALLSIGN, encoding))
|
||||
|
||||
# ARQ
|
||||
if data == 'GET:ARQ_STATE':
|
||||
self.request.sendall(bytes(static.ARQ_STATE, encoding))
|
||||
|
||||
if data == 'GET:TX_N_MAX_RETRIES':
|
||||
self.request.sendall(bytes([static.TX_N_MAX_RETRIES], encoding))
|
||||
|
||||
if data == 'GET:TX_N_RETRIES':
|
||||
self.request.sendall(bytes([static.TX_N_RETRIES], encoding))
|
||||
|
||||
if data == 'GET:ARQ_TX_N_FRAMES_PER_BURST':
|
||||
self.request.sendall(bytes([static.ARQ_TX_N_FRAMES_PER_BURST], encoding))
|
||||
|
||||
if data == 'GET:ARQ_TX_N_BURSTS':
|
||||
self.request.sendall(bytes([static.ARQ_TX_N_BURSTS], encoding))
|
||||
|
||||
if data == 'GET:ARQ_TX_N_CURRENT_ARQ_FRAME':
|
||||
self.request.sendall(bytes([static.ARQ_TX_N_CURRENT_ARQ_FRAME], encoding))
|
||||
|
||||
if data == 'GET:ARQ_TX_N_TOTAL_ARQ_FRAMES':
|
||||
self.request.sendall(bytes([static.ARQ_TX_N_TOTAL_ARQ_FRAMES], encoding))
|
||||
|
||||
if data == 'GET:ARQ_RX_FRAME_N_BURSTS':
|
||||
self.request.sendall(bytes([static.ARQ_RX_FRAME_N_BURSTS], encoding))
|
||||
|
||||
if data == 'GET:ARQ_RX_N_CURRENT_ARQ_FRAME':
|
||||
self.request.sendall(bytes([static.ARQ_RX_N_CURRENT_ARQ_FRAME], encoding))
|
||||
|
||||
if data == 'GET:ARQ_N_ARQ_FRAMES_PER_DATA_FRAME':
|
||||
self.request.sendall(bytes([static.ARQ_N_ARQ_FRAMES_PER_DATA_FRAME], encoding))
|
||||
|
||||
if data == 'GET:RX_BUFFER_LENGTH':
|
||||
self.request.sendall(bytes(str(len(static.RX_BUFFER)),encoding))
|
||||
|
||||
if data.startswith('GET:RX_BUFFER:'):
|
||||
|
||||
data = data.split('GET:RX_BUFFER:')
|
||||
bufferposition = int(data[1])-1
|
||||
if bufferposition == -1:
|
||||
if len(static.RX_BUFFER) > 0:
|
||||
self.request.sendall(static.RX_BUFFER[-1])
|
||||
|
||||
if bufferposition <= len(static.RX_BUFFER) > 0:
|
||||
self.request.sendall(bytes(static.RX_BUFFER[bufferposition]))
|
||||
|
||||
|
||||
####print("{} wrote:".format(self.client_address[0]))
|
||||
####print(self.data)
|
||||
if data == 'DEL:RX_BUFFER':
|
||||
static.RX_BUFFER = []
|
||||
|
||||
# just send back the same data, but upper-cased
|
||||
#####self.request.sendall(self.data.upper())
|
||||
|
||||
#if self.data == b'TEST':
|
||||
#logging.info("DER TEST KLAPPT! HIER KOMMT DER COMMAND PARSER HIN!")
|
||||
if self.data.startswith(b'SHOWBUFFERSIZE'):
|
||||
self.request.sendall(bytes(static.RX_BUFFER[-1]))
|
||||
print(static.RX_BUFFER_SIZE)
|
||||
|
||||
|
||||
|
||||
|
@ -83,15 +124,3 @@ def start_cmd_socket():
|
|||
|
||||
finally:
|
||||
cmdserver.server_close()
|
||||
|
||||
|
||||
def start_data_socket():
|
||||
|
||||
try:
|
||||
logging.info("SRV | STARTING TCP/IP SOCKET FOR DATA ON PORT: " + str(static.PORT + 1))
|
||||
socketserver.TCPServer.allow_reuse_address = True #https://stackoverflow.com/a/16641793
|
||||
dataserver = socketserver.TCPServer((static.HOST, static.PORT + 1), DATATCPRequestHandler)
|
||||
dataserver.serve_forever()
|
||||
|
||||
finally:
|
||||
dataserver.server_close()
|
||||
|
|
|
@ -23,6 +23,7 @@ def create_string(length):
|
|||
# Keep appending random characters using chr(x)
|
||||
random_string += (chr(random_integer))
|
||||
print("STR:" + str(random_string))
|
||||
|
||||
return random_string
|
||||
|
||||
|
||||
|
@ -42,9 +43,11 @@ args = parser.parse_args()
|
|||
|
||||
|
||||
data = create_string(args.datalength)
|
||||
data = bytes("ACK:" + "!!!--" + data + "--!!!" + "\n", "utf-8")
|
||||
data = bytes("ARQ:" + "" + data + "" + "\n", "utf-8")
|
||||
|
||||
print(data)
|
||||
|
||||
|
||||
#print(data)
|
||||
|
||||
|
||||
HOST, PORT = "localhost", args.socket_port
|
||||
|
@ -60,6 +63,6 @@ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
|||
received = str(sock.recv(1024), "utf-8")
|
||||
|
||||
print("Sent: {}".format(data))
|
||||
print("Received: {}".format(received))
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -7,11 +7,11 @@ Created on Wed Dec 23 11:13:57 2020
|
|||
"""
|
||||
|
||||
# Operator Defaults
|
||||
MYCALLSIGN = b''
|
||||
MYCALLSIGN_CRC8 = b''
|
||||
MYCALLSIGN = b'AA0AA'
|
||||
MYCALLSIGN_CRC8 = b'A'
|
||||
|
||||
DXCALLSIGN = b''
|
||||
DXCALLSIGN_CRC8 = b''
|
||||
DXCALLSIGN = b'AA0AA'
|
||||
DXCALLSIGN_CRC8 = b'A'
|
||||
|
||||
MYGRID = b''
|
||||
#---------------------------------
|
||||
|
|
Loading…
Reference in a new issue