Merge pull request #25 from DJ2LS/SOCKETv1

new socket handler
This commit is contained in:
DJ2LS 2021-02-18 16:37:54 +01:00 committed by GitHub
commit f118a3878a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 216 additions and 111 deletions

View file

@ -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
View file

@ -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

37
main.py
View file

@ -12,8 +12,16 @@ 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__':
@ -23,21 +31,6 @@ if __name__ == '__main__':
# list audio devices
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
@ -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
View 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))

145
sock.py
View file

@ -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)
# TRANSMIT ARQ MESSAGE
if data.startswith('ARQ:'):
logging.info("CMD | NEW ARQ DATA")
arqdata = data.split('ARQ:')
data_out = bytes(arqdata[1], 'utf-8')
data = self.data.split(b'ARQ:')
data_out = data[1]
TRANSMIT_ARQ = threading.Thread(target=arq.transmit, args=[data_out], name="TRANSMIT_ARQ")
TRANSMIT_ARQ.start()
# 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))
if data == 'GET:MYCALLSIGN_CRC8':
self.request.sendall(bytes(static.MYCALLSIGN_CRC8, encoding))
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))
class CMDTCPRequestHandler(socketserver.BaseRequestHandler):
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]))
def handle(self):
self.data = bytes()
while True:
chunk = self.request.recv(8192)#.strip()
self.data += chunk
if chunk.endswith(b'\n'):
break
if data == 'DEL:RX_BUFFER':
static.RX_BUFFER = []
# self.request is the TCP socket connected to the client
#self.data = self.request.recv(1024).strip()
### self.data = self.request.recv(1000000).strip()
# interrupt listening loop "while true" by setting MODEM_RECEIVE to False
#if len(self.data) > 0:
# static.MODEM_RECEIVE = False
####print("{} wrote:".format(self.client_address[0]))
####print(self.data)
# 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()

View file

@ -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))

View file

@ -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''
#---------------------------------