2020-12-23 16:48:54 +00:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Dec 23 07 : 04 : 24 2020
@author : DJ2LS
"""
import ctypes
from ctypes import *
import pathlib
import pyaudio
import sys
2020-12-26 18:27:09 +00:00
import logging
2021-01-06 12:17:17 +00:00
import time
import threading
2020-12-23 16:48:54 +00:00
2021-01-06 12:17:17 +00:00
import helpers
2020-12-23 16:48:54 +00:00
import static
2020-12-27 21:38:49 +00:00
import arq
2020-12-23 16:48:54 +00:00
class RF ( ) :
2021-01-30 16:25:24 +00:00
def __init__ ( self ) :
2021-01-07 08:28:26 +00:00
#-------------------------------------------- LOAD FREEDV
2020-12-26 10:02:14 +00:00
libname = pathlib . Path ( ) . absolute ( ) / " codec2/build_linux/src/libcodec2.so "
2020-12-23 16:48:54 +00:00
self . c_lib = ctypes . CDLL ( libname )
2021-01-07 08:28:26 +00:00
#--------------------------------------------CREATE PYAUDIO INSTANCE
2021-01-05 14:03:41 +00:00
self . p = pyaudio . PyAudio ( )
2021-01-07 08:28:26 +00:00
#--------------------------------------------GET SUPPORTED SAMPLE RATES FROM SOUND DEVICE
2021-01-30 16:25:24 +00:00
#static.AUDIO_SAMPLE_RATE_RX = int(self.p.get_device_info_by_index(static.AUDIO_INPUT_DEVICE)['defaultSampleRate'])
#static.AUDIO_SAMPLE_RATE_TX = int(self.p.get_device_info_by_index(static.AUDIO_OUTPUT_DEVICE)['defaultSampleRate'])
static . AUDIO_SAMPLE_RATE_TX = 8000
static . AUDIO_SAMPLE_RATE_TX = 8000
2021-01-07 08:28:26 +00:00
#--------------------------------------------OPEN AUDIO CHANNEL RX
2021-01-05 14:03:41 +00:00
self . stream_rx = self . p . open ( format = pyaudio . paInt16 ,
channels = static . AUDIO_CHANNELS ,
2021-01-07 08:28:26 +00:00
rate = static . AUDIO_SAMPLE_RATE_RX ,
2021-01-06 12:17:17 +00:00
frames_per_buffer = static . AUDIO_FRAMES_PER_BUFFER ,
2021-01-05 14:03:41 +00:00
input = True ,
input_device_index = static . AUDIO_INPUT_DEVICE ,
)
2021-01-06 12:17:17 +00:00
#--------------------------------------------OPEN AUDIO CHANNEL TX
2021-01-06 17:01:54 +00:00
self . stream_tx = self . p . open ( format = pyaudio . paInt16 ,
channels = 1 ,
2021-01-07 08:28:26 +00:00
rate = static . AUDIO_SAMPLE_RATE_TX ,
2021-01-06 17:01:54 +00:00
frames_per_buffer = static . AUDIO_FRAMES_PER_BUFFER , #n_nom_modem_samples
output = True ,
output_device_index = static . AUDIO_OUTPUT_DEVICE , #static.AUDIO_OUTPUT_DEVICE
)
2021-01-30 16:25:24 +00:00
#--------------------------------------------START DECODER THREAD
FREEDV_DECODER_THREAD = threading . Thread ( target = self . receive , args = [ static . FREEDV_DATA_MODE , static . FREEDV_SIGNALLING_MODE ] , name = " FREEDV_DECODER_THREAD " )
FREEDV_DECODER_THREAD . start ( )
2021-01-06 12:17:17 +00:00
#--------------------------------------------------------------------------------------------------------
2020-12-23 16:48:54 +00:00
# GET DATA AND MODULATE IT
2020-12-27 21:38:49 +00:00
2021-01-06 12:17:17 +00:00
def transmit ( self , mode , data_out ) :
2021-01-05 14:03:41 +00:00
self . c_lib . freedv_open . restype = ctypes . POINTER ( ctypes . c_ubyte )
freedv = self . c_lib . freedv_open ( mode )
bytes_per_frame = int ( self . c_lib . freedv_get_bits_per_modem_frame ( freedv ) / 8 )
payload_per_frame = bytes_per_frame - 2
n_nom_modem_samples = self . c_lib . freedv_get_n_nom_modem_samples ( freedv )
n_tx_modem_samples = self . c_lib . freedv_get_n_tx_modem_samples ( freedv ) * 2 #get n_tx_modem_samples which defines the size of the modulation object
mod_out = ctypes . c_short * n_tx_modem_samples
mod_out = mod_out ( )
2021-01-20 21:51:14 +00:00
mod_out_preamble = ctypes . c_short * n_tx_modem_samples #1760 for mode 10,11,12 #4000 for mode 9
mod_out_preamble = mod_out_preamble ( )
2021-01-05 14:03:41 +00:00
data_list = [ data_out [ i : i + payload_per_frame ] for i in range ( 0 , len ( data_out ) , payload_per_frame ) ] # split incomming bytes to size of 30bytes, create a list and loop through it
2020-12-28 14:20:51 +00:00
data_list_length = len ( data_list )
for i in range ( data_list_length ) : # LOOP THROUGH DATA LIST
2021-01-20 21:51:14 +00:00
buffer = bytearray ( payload_per_frame ) # use this if CRC16 checksum is required ( DATA1-3)
buffer [ : len ( data_list [ i ] ) ] = data_list [ i ] # set buffersize to length of data which will be send
crc = ctypes . c_ushort ( self . c_lib . freedv_gen_crc16 ( bytes ( buffer ) , payload_per_frame ) ) # generate CRC16
crc = crc . value . to_bytes ( 2 , byteorder = ' big ' ) # convert crc to 2 byte hex string
buffer + = crc # append crc16 to buffer
2021-01-21 07:33:45 +00:00
2021-01-05 14:03:41 +00:00
data = ( ctypes . c_ubyte * bytes_per_frame ) . from_buffer_copy ( buffer )
2021-01-20 21:51:14 +00:00
self . c_lib . freedv_rawdatapreambletx ( freedv , mod_out_preamble )
2021-01-05 14:03:41 +00:00
self . c_lib . freedv_rawdatatx ( freedv , mod_out , data ) # modulate DATA and safe it into mod_out pointer
2021-01-16 13:28:47 +00:00
# -------------- preamble area
2021-01-20 21:51:14 +00:00
# WE NEED TO ADJUST IT FOR SINGLE TRANSMISSION
2021-01-16 13:28:47 +00:00
2021-01-20 21:51:14 +00:00
txbuffer = bytearray ( )
txbuffer + = bytes ( mod_out_preamble )
txbuffer + = bytes ( mod_out )
2021-01-21 20:00:21 +00:00
#txbuffer = txbuffer.rstrip(b'\x00')
2021-01-16 13:28:47 +00:00
# -------------- audio sample rate conversion
# -------------- transmit audio
2021-01-30 16:25:24 +00:00
self . stream_tx . write ( bytes ( txbuffer ) )
2020-12-23 16:48:54 +00:00
2021-01-16 19:43:10 +00:00
#--------------------------------------------------------------------------------------------------------
2021-01-21 07:33:45 +00:00
def transmit_arq_ack ( self , ack_buffer ) :
2021-01-21 20:00:21 +00:00
static . ARQ_STATE = ' SENDING_ACK '
2021-01-21 07:33:45 +00:00
self . c_lib . freedv_open . restype = ctypes . POINTER ( ctypes . c_ubyte )
freedv = self . c_lib . freedv_open ( static . FREEDV_SIGNALLING_MODE )
bytes_per_frame = int ( self . c_lib . freedv_get_bits_per_modem_frame ( freedv ) / 8 )
payload_per_frame = bytes_per_frame - 2
n_nom_modem_samples = self . c_lib . freedv_get_n_nom_modem_samples ( freedv )
n_tx_modem_samples = self . c_lib . freedv_get_n_tx_modem_samples ( freedv ) * 2 #get n_tx_modem_samples which defines the size of the modulation object
mod_out = ctypes . c_short * n_tx_modem_samples
mod_out = mod_out ( )
2021-01-30 16:25:24 +00:00
mod_out_preamble = ctypes . c_short * ( n_tx_modem_samples * 2 ) #1760 for mode 10,11,12 #4000 for mode 9
mod_out_preamble = mod_out_preamble ( )
2021-01-21 07:33:45 +00:00
buffer = bytearray ( payload_per_frame ) # use this if CRC16 checksum is required ( DATA1-3)
buffer [ : len ( ack_buffer ) ] = ack_buffer # set buffersize to length of data which will be send
crc = ctypes . c_ushort ( self . c_lib . freedv_gen_crc16 ( bytes ( buffer ) , payload_per_frame ) ) # generate CRC16
crc = crc . value . to_bytes ( 2 , byteorder = ' big ' ) # convert crc to 2 byte hex string
buffer + = crc # append crc16 to buffer
print ( bytes ( buffer ) )
data = ( ctypes . c_ubyte * bytes_per_frame ) . from_buffer_copy ( buffer )
2021-01-30 16:25:24 +00:00
self . c_lib . freedv_rawdatapreambletx ( freedv , mod_out_preamble )
2021-01-21 07:33:45 +00:00
self . c_lib . freedv_rawdatatx ( freedv , mod_out , data ) # modulate DATA and safe it into mod_out pointer
2021-01-30 16:25:24 +00:00
txbuffer = bytearray ( )
txbuffer + = bytes ( mod_out_preamble )
txbuffer + = bytes ( mod_out )
txbuffer = txbuffer . rstrip ( b ' \x00 ' )
2021-01-20 22:42:45 +00:00
2021-01-21 20:00:21 +00:00
# -------------- transmit audio twice
2021-01-30 16:25:24 +00:00
self . stream_tx . write ( bytes ( txbuffer ) )
2021-01-21 20:00:21 +00:00
static . ARQ_STATE = ' RECEIVING_DATA '
2021-01-21 07:33:45 +00:00
#--------------------------------------------------------------------------------------------------------
# GET ARQ BURST FRAME VOM BUFFER AND MODULATE IT
def transmit_arq_burst ( self ) :
2021-01-21 20:00:21 +00:00
static . ARQ_STATE = ' SENDING_DATA '
2021-01-16 19:43:10 +00:00
self . c_lib . freedv_open . restype = ctypes . POINTER ( ctypes . c_ubyte )
2021-01-20 21:51:14 +00:00
freedv = self . c_lib . freedv_open ( static . FREEDV_DATA_MODE )
static . FREEDV_DATA_BYTES_PER_FRAME = int ( self . c_lib . freedv_get_bits_per_modem_frame ( freedv ) / 8 )
static . FREEDV_DATA_PAYLOAD_PER_FRAME = static . FREEDV_DATA_BYTES_PER_FRAME - 2
print ( static . FREEDV_DATA_BYTES_PER_FRAME )
2021-01-16 19:43:10 +00:00
n_nom_modem_samples = self . c_lib . freedv_get_n_nom_modem_samples ( freedv )
n_tx_modem_samples = self . c_lib . freedv_get_n_tx_modem_samples ( freedv ) * 2 #get n_tx_modem_samples which defines the size of the modulation object
mod_out = ctypes . c_short * n_tx_modem_samples
mod_out = mod_out ( )
2021-01-30 16:25:24 +00:00
mod_out_preamble = ctypes . c_short * ( 1760 * 2 ) #1760 for mode 10,11,12 #4000 for mode 9
2021-01-20 21:51:14 +00:00
mod_out_preamble = mod_out_preamble ( )
self . c_lib . freedv_rawdatapreambletx ( freedv , mod_out_preamble ) ;
txbuffer = bytearray ( )
txbuffer + = bytes ( mod_out_preamble )
2021-01-21 20:00:21 +00:00
time_start = time . time ( )
2021-01-20 21:51:14 +00:00
for n in range ( 0 , static . ARQ_TX_N_FRAMES_PER_BURST ) :
#---------------------------BUILD ARQ BURST ---------------------------------------------------------------------
frame_type = 10 + static . ARQ_TX_N_FRAMES_PER_BURST
2021-01-16 19:43:10 +00:00
frame_type = bytes ( [ frame_type ] )
2021-01-21 20:00:21 +00:00
print ( frame_type )
print ( " static.ARQ_N_SENT_FRAMES: " + str ( static . ARQ_N_SENT_FRAMES ) )
print ( " static.ARQ_TX_N_FRAMES_PER_BURST: " + str ( static . ARQ_TX_N_FRAMES_PER_BURST ) )
2021-01-20 21:51:14 +00:00
payload_data = bytes ( static . TX_BUFFER [ static . ARQ_N_SENT_FRAMES + n ] )
2021-01-16 19:43:10 +00:00
2021-01-20 21:51:14 +00:00
arqframe = frame_type + static . ARQ_BURST_PAYLOAD_CRC + payload_data
2021-01-16 19:43:10 +00:00
2021-01-20 21:51:14 +00:00
buffer = bytearray ( static . FREEDV_DATA_PAYLOAD_PER_FRAME ) # create TX buffer
2021-01-16 19:43:10 +00:00
buffer [ : len ( arqframe ) ] = arqframe # set buffersize to length of data which will be send
2021-01-30 16:25:24 +00:00
2021-01-20 21:51:14 +00:00
crc = ctypes . c_ushort ( self . c_lib . freedv_gen_crc16 ( bytes ( buffer ) , static . FREEDV_DATA_PAYLOAD_PER_FRAME ) ) # generate CRC16
2021-01-16 19:43:10 +00:00
crc = crc . value . to_bytes ( 2 , byteorder = ' big ' ) # convert crc to 2 byte hex string
buffer + = crc # append crc16 to buffer
2021-01-20 21:51:14 +00:00
data = ( ctypes . c_ubyte * static . FREEDV_DATA_BYTES_PER_FRAME ) . from_buffer_copy ( buffer )
self . c_lib . freedv_rawdatatx ( freedv , mod_out , data ) # modulate DATA and safe it into mod_out pointer
txbuffer + = bytes ( mod_out )
2021-01-30 16:25:24 +00:00
2021-01-21 20:00:21 +00:00
# -------------- transmit audio
2021-01-30 16:25:24 +00:00
self . stream_tx . write ( bytes ( txbuffer ) )
2021-01-21 20:00:21 +00:00
static . ARQ_STATE = ' RECEIVING_ACK '
2021-01-21 07:33:45 +00:00
#--------------------------------------------------------------------------------------------------------
2021-01-20 21:51:14 +00:00
def receive ( self , data_mode , signalling_mode ) :
2021-01-16 19:43:10 +00:00
2021-01-20 21:51:14 +00:00
print ( " RECEIVING FOR DATA MODE: " + str ( data_mode ) )
print ( " RECEIVING FOR SIGNALLING MODE: " + str ( signalling_mode ) )
2021-01-16 19:43:10 +00:00
2021-01-05 14:03:41 +00:00
self . c_lib . freedv_open . restype = ctypes . POINTER ( ctypes . c_ubyte )
2021-01-20 21:51:14 +00:00
freedv_data = self . c_lib . freedv_open ( data_mode )
freedv_signalling = self . c_lib . freedv_open ( signalling_mode )
static . FREEDV_DATA_BYTES_PER_FRAME = int ( self . c_lib . freedv_get_bits_per_modem_frame ( freedv_data ) / 8 )
static . FREEDV_DATA_PAYLOAD_PER_FRAME = static . FREEDV_DATA_BYTES_PER_FRAME - 2
static . FREEDV_SIGNALLING_BYTES_PER_FRAME = int ( self . c_lib . freedv_get_bits_per_modem_frame ( freedv_signalling ) / 8 )
static . FREEDV_SIGNALLING_PAYLOAD_PER_FRAME = static . FREEDV_SIGNALLING_BYTES_PER_FRAME - 2
data_bytes_out = ( ctypes . c_ubyte * static . FREEDV_DATA_BYTES_PER_FRAME )
data_bytes_out = data_bytes_out ( ) #get pointer from bytes_out
signalling_bytes_out = ( ctypes . c_ubyte * static . FREEDV_SIGNALLING_BYTES_PER_FRAME )
signalling_bytes_out = signalling_bytes_out ( ) #get pointer from bytes_out
while 1 :
time . sleep ( 0.01 )
2021-01-06 12:17:17 +00:00
2021-01-20 21:51:14 +00:00
while static . ARQ_STATE == ' RECEIVING_DATA ' :
time . sleep ( 0.01 )
nin = self . c_lib . freedv_nin ( freedv_data )
nin = int ( nin * ( static . AUDIO_SAMPLE_RATE_RX / static . MODEM_SAMPLE_RATE ) )
data_in = self . stream_rx . read ( nin , exception_on_overflow = False )
data_in = audioop . ratecv ( data_in , 2 , 1 , static . AUDIO_SAMPLE_RATE_RX , static . MODEM_SAMPLE_RATE , None )
data_in = data_in [ 0 ] . rstrip ( b ' \x00 ' )
2021-01-21 20:00:21 +00:00
#print(data_in)
2021-01-20 21:51:14 +00:00
self . c_lib . freedv_rawdatarx . argtype = [ ctypes . POINTER ( ctypes . c_ubyte ) , data_bytes_out , data_in ] # check if really neccessary
nbytes = self . c_lib . freedv_rawdatarx ( freedv_data , data_bytes_out , data_in ) # demodulate audio
2021-01-21 20:00:21 +00:00
print ( self . c_lib . freedv_get_rx_status ( freedv_data ) )
2021-01-30 16:25:24 +00:00
#modem_stats_snr = c_float()
#modem_stats_sync = c_int()
2021-01-21 20:00:21 +00:00
#self.c_lib.freedv_get_modem_stats(freedv_data,byref(modem_stats_sync), byref(modem_stats_snr))
#modem_stats_snr = modem_stats_snr.value
#print(modem_stats_snr)
2021-01-20 21:51:14 +00:00
if nbytes == static . FREEDV_DATA_BYTES_PER_FRAME :
2021-01-21 20:00:21 +00:00
2021-01-20 21:51:14 +00:00
# CHECK IF FRAMETYPE IS BETWEEN 10 and 50 ------------------------
frametype = int . from_bytes ( bytes ( data_bytes_out [ : 1 ] ) , " big " )
if 50 > = frametype > = 10 and len ( data_bytes_out ) > 30 : # --> The length check filters out random strings without CRC
print ( " MODE: " + str ( data_mode ) + " DATA: " + str ( bytes ( data_bytes_out [ : - 2 ] ) ) )
arq . data_received ( bytes ( data_bytes_out [ : - 2 ] ) ) #send payload data to arq checker without CRC16
2021-01-21 20:00:21 +00:00
#self.c_lib.freedv_set_sync(freedv_data, 0) #FORCE UNSYNC
2021-01-20 21:51:14 +00:00
else :
print ( " MODE: " + str ( data_mode ) + " DATA: " + str ( bytes ( data_bytes_out ) ) )
2021-01-06 12:17:17 +00:00
2021-01-21 20:00:21 +00:00
# NEEDS TO BE OPTIMIZED
# DO UNSYNC IF LAST FRAME PER BURST SENT
if len ( static . ARQ_RX_BURST_BUFFER ) == static . ARQ_N_RX_FRAMES_PER_BURSTS and len ( static . ARQ_RX_BURST_BUFFER ) != 0 :
print ( " DOING UNSYNC " )
self . c_lib . freedv_set_sync ( freedv_data , 0 ) #FORCE UNSYNC
2020-12-27 21:38:49 +00:00
2021-01-20 21:51:14 +00:00
while static . ARQ_STATE == ' IDLE ' or static . ARQ_STATE == ' RECEIVING_ACK ' :
time . sleep ( 0.01 )
2021-01-21 20:00:21 +00:00
2021-01-20 21:51:14 +00:00
nin = self . c_lib . freedv_nin ( freedv_signalling )
nin = int ( nin * ( static . AUDIO_SAMPLE_RATE_RX / static . MODEM_SAMPLE_RATE ) )
data_in = self . stream_rx . read ( nin , exception_on_overflow = False )
data_in = audioop . ratecv ( data_in , 2 , 1 , static . AUDIO_SAMPLE_RATE_RX , static . MODEM_SAMPLE_RATE , None )
data_in = data_in [ 0 ] . rstrip ( b ' \x00 ' )
2020-12-28 14:20:51 +00:00
2021-01-20 21:51:14 +00:00
self . c_lib . freedv_rawdatarx . argtype = [ ctypes . POINTER ( ctypes . c_ubyte ) , signalling_bytes_out , data_in ] # check if really neccessary
nbytes = self . c_lib . freedv_rawdatarx ( freedv_signalling , signalling_bytes_out , data_in ) # demodulate audio
2020-12-25 21:55:56 +00:00
2020-12-23 16:48:54 +00:00
2021-01-20 21:51:14 +00:00
# CHECK IF FRAME CONTAINS ACK------------------------ --> 700D / 7
frametype = int . from_bytes ( bytes ( signalling_bytes_out [ : 1 ] ) , " big " )
2021-01-21 20:00:21 +00:00
if frametype == 60 and len ( signalling_bytes_out ) == static . FREEDV_SIGNALLING_BYTES_PER_FRAME :
arq . ack_received ( )
# print("ACK FRAME RECEIVED!!!!!!!!!!")
2021-01-20 21:51:14 +00:00
#if bytes(bytes_out[:1]) == b'<': #b'\7': < = 60
# CHECK CRC 8 OF ACK FRAME
2021-01-06 12:17:17 +00:00
2021-01-21 20:00:21 +00:00
# print(signalling_bytes_out[:1])
# print(signalling_bytes_out[3:14])
2021-01-06 12:17:17 +00:00
2021-01-21 20:00:21 +00:00
# if bytes(signalling_bytes_out[:2]) == helpers.get_crc_8(bytes(signalling_bytes_out[3:14])):
# print("MODE: " + str(signalling_mode) + " DATA: " + str(bytes(signalling_bytes_out)))
# arq.ack_received()
2021-01-20 21:51:14 +00:00