diff --git a/README.md b/README.md index f6c710fd..f5e928c2 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ ## 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 diff --git a/arq.py b/arq.py index 430d9246..3e820c19 100644 --- a/arq.py +++ b/arq.py @@ -5,309 +5,356 @@ Created on Sun Dec 27 20:43:40 2020 @author: DJ2LS """ -# CRC aller payloads via XOR scrambeln und dann eine CRC8 mitsenden - import logging -import crcengine import threading import time +from random import randrange import static import modem -import other - +import helpers +import main modem = modem.RF() -crc_algorithm = crcengine.new('crc16-ccitt-false') #load crc16 library - -static.ARQ_PAYLOAD_PER_FRAME = static.FREEDV_PAYLOAD_PER_FRAME - 6 - - +static.ARQ_PAYLOAD_PER_FRAME = static.FREEDV_DATA_PAYLOAD_PER_FRAME - 3 #6?! +static.ARQ_ACK_PAYLOAD_PER_FRAME = 14 - 2# +def arq_ack_timeout(): + static.ARQ_ACK_TIMEOUT = 1 + def data_received(data_in): - - ARQ_N_RX_BURSTS = int.from_bytes(bytes(data_in[:1]), "big") - 10 - static.ARQ_RX_BURST_BUFFER.append(data_in) #append data to RX BUFFER - - print(ARQ_N_RX_BURSTS) - - - #while static.ACK_RX_TIMEOUT == 0: #define timeout where data has to be received untl error occurs + +# arqframe = frame_type + \ # 1 [:1] # frame type and current number of arq frame of (current) burst +# bytes([static.ARQ_TX_N_FRAMES_PER_BURST]) + \ # 1 [1:2] # total number of arq frames per (current) burst +# static.ARQ_N_CURRENT_ARQ_FRAME + \ # 2 [2:4] # current arq frame number +# static.ARQ_N_TOTAL_ARQ_FRAMES + \ # 2 [4:6] # total number arq frames +# static.ARQ_BURST_PAYLOAD_CRC + \ # 2 [6:8] # arq crc +# payload_data # N [8:N] # payload data + + - if len(static.ARQ_RX_BURST_BUFFER) == ARQ_N_RX_BURSTS: #if received bursts are equal to burst number in frame + static.ARQ_N_FRAME = int.from_bytes(bytes(data_in[:1]), "big") - 10 #get number of burst frame + static.ARQ_N_RX_FRAMES_PER_BURSTS = int.from_bytes(bytes(data_in[1:2]), "big") #get number of bursts from received frame + static.ARQ_RX_N_CURRENT_ARQ_FRAME = int.from_bytes(bytes(data_in[2:4]), "big") #get current number of total frames + static.ARQ_N_ARQ_FRAMES_PER_DATA_FRAME = int.from_bytes(bytes(data_in[4:6]), "big") # get get total number of frames + static.ARQ_BURST_PAYLOAD_CRC = data_in[6:8] - burst_total_payload = bytearray() - for n_raw_frame in range(0,len(static.ARQ_RX_BURST_BUFFER)): + + logging.debug("----------------------------------------------------------------") + logging.debug("ARQ_N_FRAME: " + str(static.ARQ_N_FRAME)) + logging.debug("ARQ_N_RX_FRAMES_PER_BURSTS: " + str(static.ARQ_N_RX_FRAMES_PER_BURSTS)) + logging.debug("ARQ_RX_N_CURRENT_ARQ_FRAME: " + str(static.ARQ_RX_N_CURRENT_ARQ_FRAME)) + logging.debug("ARQ_N_ARQ_FRAMES_PER_DATA_FRAME: " + str(static.ARQ_N_ARQ_FRAMES_PER_DATA_FRAME)) + logging.debug("static.ARQ_BURST_PAYLOAD_CRC: " + str(static.ARQ_BURST_PAYLOAD_CRC)) + logging.debug("----------------------------------------------------------------") + + + arq_percent_burst = int((static.ARQ_N_FRAME / static.ARQ_N_RX_FRAMES_PER_BURSTS)*100) + arq_percent_frame = int(((static.ARQ_RX_N_CURRENT_ARQ_FRAME)/static.ARQ_N_ARQ_FRAMES_PER_DATA_FRAME)*100) + + logging.info("ARQ | RX | ARQ FRAME [" + str(static.ARQ_N_FRAME) + "/" + str(static.ARQ_N_RX_FRAMES_PER_BURSTS) + "] [" + str(arq_percent_burst).zfill(3) + "%] --- TOTAL [" + str(static.ARQ_RX_N_CURRENT_ARQ_FRAME) + "/" + str(static.ARQ_N_ARQ_FRAMES_PER_DATA_FRAME) + "] [" + str(arq_percent_frame).zfill(3) + "%]" ) + + + + #allocate ARQ_RX_FRAME_BUFFER as a list with "None" if not already done. This should be done only once per burst! + # here we will save the N frame of a data frame to N list position so we can explicit search for it + if static.ARQ_N_ARQ_FRAMES_PER_DATA_FRAME != len(static.ARQ_RX_FRAME_BUFFER) and static.ARQ_RX_N_CURRENT_ARQ_FRAME == 1: + for i in range(0,static.ARQ_N_ARQ_FRAMES_PER_DATA_FRAME+1): + static.ARQ_RX_FRAME_BUFFER.insert(i,None) + + + #allocate ARQ_RX_BURST_BUFFER as a list with "None" if not already done. This should be done only once per burst! + # here we will save the N frame of a burst to N list position so we can explicit search for it + if static.ARQ_N_RX_FRAMES_PER_BURSTS != len(static.ARQ_RX_BURST_BUFFER) and static.ARQ_N_FRAME == 1: + for i in range(0,static.ARQ_N_RX_FRAMES_PER_BURSTS+1): + static.ARQ_RX_BURST_BUFFER.insert(i,None) - burst_frame = static.ARQ_RX_BURST_BUFFER[n_raw_frame] #get burst frame - burst_payload = burst_frame[3:] #remove frame type and burst CRC - burst_total_payload = burst_total_payload + burst_payload #stick bursts together + + + + # now we add the incoming data to the specified position in our lists + static.ARQ_RX_BURST_BUFFER[static.ARQ_N_FRAME] = bytes(data_in) + static.ARQ_RX_FRAME_BUFFER[static.ARQ_RX_N_CURRENT_ARQ_FRAME] = bytes(data_in) + + +# -------------------------- ARQ BURST CHECKER + + # run only if we recieved all ARQ FRAMES per ARQ BURST + burst_total_payload = bytearray() + if static.ARQ_N_FRAME == static.ARQ_N_RX_FRAMES_PER_BURSTS: #if received bursts are equal to burst number in frame - print(burst_total_payload) - burst_payload_crc = crc_algorithm(burst_total_payload) - burst_payload_crc = burst_payload_crc.to_bytes(2, byteorder='big') - print(burst_payload_crc) - - - if burst_payload_crc == data_in[1:3]: #IF burst payload crc and input crc are equal - - print(data_in[1:3]) - print("CRC EQUAL") - logging.info("TX | SENDING ACK [" + str(data_in[1:3]) +"]") - static.ARQ_RX_FRAME_BUFFER.append(burst_total_payload) # IF CRC TRUE APPEND burst_total_payload TO ARQ_RX_FRAME_BUFFER - print(data_in[7:9]) - - - # -------- DETECT IF WE HAVE A FRAME HEADER - - if data_in[7:9].startswith(b'\xAA\xAA'): - print("DAS IST DER ERSTE BURST MIT BOF!!!") - print("FRAME CRC = " + str(data_in[5:7])) - print("FRAME BURSTS = " + str(data_in[3:5])) - static.FRAME_CRC = data_in[5:7] - - - if data_in.rstrip(b'\x00').endswith(b'\xFF\xFF'): - print("DAS IST DER LETZTE BURST MIT EOF!!!") - - # WENN DAS HIER ERFÜLLT IST, DANN KÖNNEN WIR MAL SCHAUEN WAS WIR AUSGEBEN KÖNNEN - print(len(static.ARQ_RX_FRAME_BUFFER)) - - total_frame = bytearray() - for b in range(len(static.ARQ_RX_FRAME_BUFFER)): - - total_frame = total_frame + static.ARQ_RX_FRAME_BUFFER[b] - #print(total_frame) - - payload = total_frame.split(b'\xAA\xAA') - payload = payload[1] - payload = payload.split(b'\xFF\xFF') - payload = payload[0] - - frame_payload_crc = crc_algorithm(payload) - frame_payload_crc = frame_payload_crc.to_bytes(2, byteorder='big') - - - if static.FRAME_CRC == frame_payload_crc: - print("FRAME CRC PASST") - print(payload) - else: - print("FRAME CRC PASST NICHT") - print(static.FRAME_CRC) - print(frame_payload_crc) - print(payload) - - - - - - - - - - - - - #BUILDING ACK FRAME ----------------------------------------------- - - ack_frame = b'\7' + bytes(burst_payload_crc) - ack_buffer = bytearray(static.ARQ_PAYLOAD_PER_FRAME) + #here we get the total payload for the frame to calculate the crc + for n_raw_frame in range(1,len(static.ARQ_RX_BURST_BUFFER)): + # we need to check if we have a None or received data in list + if static.ARQ_RX_BURST_BUFFER[n_raw_frame] != None: + burst_frame = static.ARQ_RX_BURST_BUFFER[n_raw_frame] #get burst frame + burst_payload = burst_frame[8:] #remove frame type and burst CRC #4 + burst_total_payload = burst_total_payload + burst_payload #stick bursts together + + # ------------------ calculate CRC of BURST + burst_payload_crc = helpers.get_crc_16(burst_total_payload) + # IF BURST CRC IS CORRECT, APPEND BURST TO BUFFER AND SEND ACK FRAME + if burst_payload_crc == data_in[6:8]: + + #BUILDING ACK FRAME FOR BURST ----------------------------------------------- + #ack_payload = bytes(burst_payload_crc) + ack_frame = b'<'+ bytes(burst_payload_crc) # < = 60 + ack_buffer = bytearray(static.ARQ_ACK_PAYLOAD_PER_FRAME) ack_buffer[:len(ack_frame)] = ack_frame # set buffersize to length of data which will be send - #TRANSMIT ACK FRAME ----------------------------------------------- - time.sleep(2) - modem.Transmit(ack_buffer) - static.ARQ_RX_BURST_BUFFER = [] + #TRANSMIT ACK FRAME FOR BURST----------------------------------------------- + logging.info("ARQ | TX | ARQ BURST ACK [" + str(data_in[6:8].hex()) +"]") + modem.transmit_arq_ack(ack_buffer) - + # ---------------------------------------------------------------- + static.ARQ_RX_BURST_BUFFER = [] # CLEAR RX BURST BUFFER AFTER SENDING DATA + else: #IF burst payload crc and input crc are NOT equal - print("CRC NOT EQUAL!!!!!") - print(data_in[1:3]) - static.ARQ_RX_BURST_BUFFER = [] + logging.info("ARQ BURST CRC NOT EQUAL! [" + str(data_in[6:8]) + "]") + static.ARQ_RX_BURST_BUFFER = [] #erase ARQ RX Burst buffer + + #if nframes are unequal to expected frames per burst + else: + #just a placeholder at this time + pass + +# ---------------------------- FRAME MACHINE + + # --------------- CHECK IF WE ARE MISSING FRAMES ------------------------------------------- + #for frame in range(1,len(static.ARQ_RX_FRAME_BUFFER)): + # if static.ARQ_RX_FRAME_BUFFER[frame] == None: + # print("Missing frames:" + str(frame)) + + # --------------- IF LIST NOT CONTAINS "None" stick everything together + complete_data_frame = bytearray() + if static.ARQ_RX_FRAME_BUFFER.count(None) == 1: ## 1 because position 0 of list will alaways be None in our case + + for frame in range(1,len(static.ARQ_RX_FRAME_BUFFER)): + raw_arq_frame = static.ARQ_RX_FRAME_BUFFER[frame] + arq_frame_payload = raw_arq_frame[8:] + + # -------- DETECT IF WE RECEIVED A FRAME HEADER THEN SAVE DATA TO GLOBALS + if arq_frame_payload[2:4].startswith(static.FRAME_BOF): + static.FRAME_CRC = arq_frame_payload[:2] + static.ARQ_FRAME_BOF_RECEIVED = True + + arq_frame_payload = arq_frame_payload.split(static.FRAME_BOF) + arq_frame_payload = arq_frame_payload[1] + + # -------- DETECT IF WE RECEIVED A FRAME FOOTER THEN SAVE DATA TO GLOBALS + if arq_frame_payload.rstrip(b'\x00').endswith(static.FRAME_EOF): + static.ARQ_FRAME_EOF_RECEIVED = True + + arq_frame_payload = arq_frame_payload.split(static.FRAME_EOF) + arq_frame_payload = arq_frame_payload[0] + + + # --------- AFTER WE SEPARATED BOF AND EOF, STICK EVERYTHING TOGETHER + + complete_data_frame = complete_data_frame + arq_frame_payload + + + #check if Begin of Frame BOF and End of Frame EOF are received, then start calculating CRC and sticking everything together + if static.ARQ_FRAME_BOF_RECEIVED == True and static.ARQ_FRAME_EOF_RECEIVED == True: + + frame_payload_crc = helpers.get_crc_16(complete_data_frame) + + #IF THE FRAME PAYLOAD CRC IS EQUAL TO THE FRAME CRC WHICH IS KNOWN FROM THE HEADER --> SUCCESS + if frame_payload_crc == static.FRAME_CRC: + logging.info("ARQ | RX | DATA FRAME SUCESSFULLY RECEIVED! - TIME TO PARTY") + + static.RX_BUFFER.append(complete_data_frame) + + + #BUILDING ACK FRAME FOR DATA FRAME ----------------------------------------------- + + ack_frame = b'='+ bytes(static.FRAME_CRC) # < = 61 + ack_buffer = bytearray(static.ARQ_ACK_PAYLOAD_PER_FRAME) + ack_buffer[:len(ack_frame)] = ack_frame # set buffersize to length of data which will be send + + #TRANSMIT ACK FRAME FOR BURST----------------------------------------------- + logging.info("ARQ | TX | ARQ DATA FRAME ACK [" + str(static.FRAME_CRC.hex()) +"]") + modem.transmit_arq_ack(ack_buffer) + + # clearing buffers and resetting counters + static.ARQ_RX_BURST_BUFFER = [] + static.ARQ_RX_FRAME_BUFFER = [] + static.ARQ_FRAME_BOF_RECEIVED = False + static.ARQ_FRAME_EOF_RECEIVED = False + static.ARQ_N_ARQ_FRAMES_PER_DATA_FRAME = 0 + + #print("----------------------------------------------------------------") + #print(static.RX_BUFFER[-1]) + #print("----------------------------------------------------------------") + + else: + logging.info("ARQ | RX | DATA FRAME NOT SUCESSFULLY RECEIVED!") - - - - - - -def ack_received(): +def burst_ack_received(): - logging.info("RX | ACK RCVD!") - static.ACK_TIMEOUT = 1 #Force timer to stop waiting - static.ACK_RECEIVED = 1 #Force data loops of TNC to stop and continue with next frame - # static.ARQ_ACK_WAITING_FOR_ID - - - - - - - + logging.debug("ARQ | RX | BURST ACK RCVD!") + static.ARQ_ACK_TIMEOUT = 1 #Force timer to stop waiting + static.ARQ_ACK_RECEIVED = 1 #Force data loops of TNC to stop and continue with next frame +def frame_ack_received(): + logging.debug("ARQ | RX | FRAME ACK RCVD!") + static.ARQ_ACK_TIMEOUT = 1 #Force timer to stop waiting + static.ARQ_FRAME_ACK_RECEIVED = 1 #Force data loops of TNC to stop and continue with next frame + + def transmit(data_out): - - static.ARQ_PAYLOAD_PER_FRAME = static.FREEDV_PAYLOAD_PER_FRAME - 3 - - - #----------------------- BUILD A FRAME WITH CRC AND N BURSTS - - frame_BOF = b'\xAA\xAA' - frame_EOF = b'\xFF\xFF' - - - frame_header_length = 8 - - n_bursts_prediction = (len(data_out)+frame_header_length) // static.ARQ_PAYLOAD_PER_FRAME + ((len(data_out)+frame_header_length) % static.ARQ_PAYLOAD_PER_FRAME > 0) # aufrunden 3.2 = 4 - n_bursts_prediction = n_bursts_prediction.to_bytes(2, byteorder='big') #65535 - - frame_payload_crc = crc_algorithm(data_out) - frame_payload_crc = frame_payload_crc.to_bytes(2, byteorder='big') - - data_out = n_bursts_prediction + frame_payload_crc + frame_BOF + data_out + frame_EOF - # 2 2 2 N 2 - print(data_out) - # --------------------------------------------- START OF MAIN DATA LOOP - - static.TX_BUFFER = [data_out[i:i+static.ARQ_PAYLOAD_PER_FRAME] for i in range(0, len(data_out), static.ARQ_PAYLOAD_PER_FRAME)] # split incomming bytes to size of 30bytes - arq payload - static.TX_BUFFER_SIZE = len(static.TX_BUFFER) - - static.ARQ_TX_N_FRAMES = n_frames_per_burst(len(data_out)) # DEFINE NUMBER OF FRAMES PER BURSTS - - logging.info("TX | TOTAL PAYLOAD BYTES/FRAMES TO SEND: " + str(len(data_out)) + " / " + str(static.TX_BUFFER_SIZE)) - - #print(static.TX_BUFFER[2]) - - for n_raw_frame in range(0, static.TX_BUFFER_SIZE, static.ARQ_TX_N_FRAMES): # LOOP THROUGH DATA LIST with steps = ARQ_TX_N_FRAMES - - # ----------- GENERATE PAYLOAD CRC FOR ARQ_TX_N_FRAMES - burst_total_payload = bytearray() - - #---------------------------------------------------------------------------------------------------------- - try: # DETECT IF LAST BURST - for i in range(static.ARQ_TX_N_FRAMES): #bytearray(b'111111111111111111111111222222222222222222222222') - - # we need to make sure, payload data is always as long as static.ARQ_PAYLOAD_PER_FRAME beacuse of CRC! - - burst_raw_payload = static.TX_BUFFER[n_raw_frame + i] - burst_payload = bytearray(static.ARQ_PAYLOAD_PER_FRAME) - burst_payload[:len(burst_raw_payload)] = burst_raw_payload # set buffersize to length of data which will be send - burst_total_payload = burst_total_payload + burst_payload - - except IndexError: # IF LAST BURST DETECTED BUILD CRC WITH LESS FRAMES AND SET static.ARQ_TX_N_FRAMES TO VALUE OF REST! - print("LAST BURST!!!") - burst_total_payload = bytearray() # reset burst_total_payload because of possible input remaining of detecting loop one step above - n_last_burst = (static.TX_BUFFER_SIZE % n_raw_frame) - print(n_last_burst) - static.ARQ_TX_N_FRAMES = n_last_burst - - for i in range(n_last_burst): #bytearray(b'111111111111111111111111222222222222222222222222') - - burst_raw_payload = static.TX_BUFFER[n_raw_frame + i] - burst_payload = bytearray(static.ARQ_PAYLOAD_PER_FRAME) - burst_payload[:len(burst_raw_payload)] = burst_raw_payload # set buffersize to length of data which will be send - burst_total_payload = burst_total_payload + burst_payload - - #---------------------------------------------------------------------------------------------------------- - - print(burst_total_payload) - - burst_payload_crc = crc_algorithm(burst_total_payload) - burst_payload_crc = burst_payload_crc.to_bytes(2, byteorder='big') - - print(burst_payload_crc) - static.ARQ_ACK_WAITING_FOR_ID = burst_payload_crc #set the global variable so we know for which ACK we are waiting for - - - - - #---------------------------------------------------------------------------------------------------------- - #-------------------- BUILD ARQBURSTS - - arqburst = [] - for i in range(static.ARQ_TX_N_FRAMES): - - - frame_type = 10 + static.ARQ_TX_N_FRAMES - frame_type = bytes([frame_type]) - - payload_data = bytes(static.TX_BUFFER[n_raw_frame + i]) - - arqframe = frame_type + burst_payload_crc + payload_data - - buffer = bytearray(static.FREEDV_PAYLOAD_PER_FRAME) # create TX buffer - buffer[:len(arqframe)] = arqframe # set buffersize to length of data which will be send + static.ARQ_PAYLOAD_PER_FRAME = static.FREEDV_DATA_PAYLOAD_PER_FRAME - 8 #3 ohne ARQ_TX_N_FRAMES_PER_BURST + frame_header_length = 4 - arqburst.append(buffer) + n_arq_frames_per_data_frame = (len(data_out)+frame_header_length) // static.ARQ_PAYLOAD_PER_FRAME + ((len(data_out)+frame_header_length) % static.ARQ_PAYLOAD_PER_FRAME > 0) # aufrunden 3.2 = 4 + + #print(static.FREEDV_DATA_PAYLOAD_PER_FRAME) + #print(static.ARQ_PAYLOAD_PER_FRAME) + #print(n_bursts_prediction) + ####static.ARQ_N_ARQ_FRAMES_PER_DATA_FRAME = n_arq_frames_per_data_frame.to_bytes(2, byteorder='big') #65535 + + frame_payload_crc = helpers.get_crc_16(data_out) + #print(frame_payload_crc) + + # This is the total frame with frame header, which will be send + data_out = frame_payload_crc + static.FRAME_BOF + data_out + static.FRAME_EOF + # 2 2 N 2 - #---------------------------------------------------------------------------------------------------------- + + # --------------------------------------------- LETS CREATE A BUFFER BY SPLITTING THE FILES INTO PEACES + static.TX_BUFFER = [data_out[i:i+static.ARQ_PAYLOAD_PER_FRAME] for i in range(0, len(data_out), static.ARQ_PAYLOAD_PER_FRAME)] + static.TX_BUFFER_SIZE = len(static.TX_BUFFER) + #print(static.TX_BUFFER) + + logging.info("ARQ | TX | DATA FRAME --- BYTES: " + str(len(data_out)) + " ARQ FRAMES: " + str(static.TX_BUFFER_SIZE)) + + # --------------------------------------------- THIS IS THE MAIN LOOP----------------------------------------------------------------- + + static.ARQ_N_SENT_FRAMES = 0 # SET N SENT FRAMES TO 0 FOR A NEW SENDING CYCLE + while static.ARQ_N_SENT_FRAMES <= static.TX_BUFFER_SIZE: + #print("static.ARQ_N_SENT_FRAMES: " + str(static.ARQ_N_SENT_FRAMES)) + static.ARQ_TX_N_FRAMES_PER_BURST = get_n_frames_per_burst() + + # ----------- CREATE FRAME TOTAL PAYLOAD TO BE ABLE TO CREATE CRC FOR IT + burst_total_payload = bytearray() + try: # DETECT IF LAST BURST TO PREVENT INDEX ERROR OF BUFFER + for i in range(static.ARQ_TX_N_FRAMES_PER_BURST): # Loop through TX_BUFFER LIST + + # make sure we have always a filled buffer with the length of payload per frame + burst_raw_payload = static.TX_BUFFER[static.ARQ_N_SENT_FRAMES + i] + burst_payload = bytearray(static.ARQ_PAYLOAD_PER_FRAME) + burst_payload[:len(burst_raw_payload)] = burst_raw_payload # get frame from TX_BUFFER + burst_total_payload = burst_total_payload + burst_payload # append single frame to total payload buffer + + except IndexError: # IF LAST BURST DETECTED BUILD CRC WITH LESS FRAMES AND SET static.ARQ_TX_N_FRAMES_PER_BURST TO VALUE OF REST! + + burst_total_payload = bytearray() # reset burst_total_payload because of possible input remaining of detecting loop one step above + if static.ARQ_N_SENT_FRAMES == 0 and (static.ARQ_TX_N_FRAMES_PER_BURST > static.TX_BUFFER_SIZE): #WE CANT DO MODULO 0 --> CHECK IF FIRST FRAME == LAST FRAME + static.ARQ_TX_N_FRAMES_PER_BURST = static.TX_BUFFER_SIZE + elif static.ARQ_N_SENT_FRAMES == 1 and (static.ARQ_TX_N_FRAMES_PER_BURST > static.TX_BUFFER_SIZE): # MODULO 1 WILL ALWAYS BE 0 --> THIS FIXES IT + static.ARQ_TX_N_FRAMES_PER_BURST = static.TX_BUFFER_SIZE - static.ARQ_N_SENT_FRAMES + else: + static.ARQ_TX_N_FRAMES_PER_BURST = (static.TX_BUFFER_SIZE % static.ARQ_N_SENT_FRAMES) - #--------------------------------------------- N ATTEMPTS TO SEND BURSTS IF ACK FAILS - for static.TX_N_RETRIES in range(static.TX_N_MAX_RETRIES): - - static.ACK_RECEIVED = 0 - - # ----------------------- Loop through ARQ FRAMES BUFFER with N = Numbers of frames which will be send at once - - for n in range(static.ARQ_TX_N_FRAMES): - logging.info("TX | SENDING BURST " + str(n+1) + " / " + str(static.ARQ_TX_N_FRAMES)) - modem.Transmit(arqburst[n]) - time.sleep(2) - #modem.RF.Transmit(arqburst[n]) - print(arqburst[n]) + #print("ARQ_TX_N_FRAMES_PER_BURST OF LAST BURST: " + str(static.ARQ_TX_N_FRAMES_PER_BURST)) + + for i in range(static.ARQ_TX_N_FRAMES_PER_BURST): #bytearray(b'111111111111111111111111222222222222222222222222') - # --------------------------- START TIMER ---> IF TIMEOUT REACHED, ACK_TIMEOUT = 1 - static.ACK_TIMEOUT = 0 - timer = threading.Timer(static.ACK_TIMEOUT_SECONDS * static.ARQ_TX_N_FRAMES, other.timeout) + # make sure we have always a filled buffer with the length of payload per frame + burst_raw_payload = static.TX_BUFFER[static.ARQ_N_SENT_FRAMES + i] + burst_payload = bytearray(static.ARQ_PAYLOAD_PER_FRAME) + burst_payload[:len(burst_raw_payload)] = burst_raw_payload # get frame from TX_BUFFER + burst_total_payload = burst_total_payload + burst_payload # append single frame to total payload buffer + + # ----------- GENERATE PAYLOAD CRC FOR ARQ_TX_N_FRAMES_PER_BURST + static.ARQ_BURST_PAYLOAD_CRC = helpers.get_crc_16(burst_total_payload) + + #--------------------------------------------- N ATTEMPTS TO SEND BURSTS IF ACK RECEPTION FAILS + for static.TX_N_RETRIES in range(static.TX_N_MAX_RETRIES): + + if static.ARQ_N_SENT_FRAMES+1 <= static.TX_BUFFER_SIZE: + logging.info("ARQ | TX | B:[" + str(static.ARQ_BURST_PAYLOAD_CRC.hex()) + "] F:[" + str(static.ARQ_N_SENT_FRAMES+1) + "-" + str(static.ARQ_N_SENT_FRAMES + static.ARQ_TX_N_FRAMES_PER_BURST) + "] | T:[" + str(static.ARQ_N_SENT_FRAMES) + "/" + str(static.TX_BUFFER_SIZE) + "] [" + str(int(static.ARQ_N_SENT_FRAMES/(static.TX_BUFFER_SIZE)*100)).zfill(3) + "%] | A:[" + str(static.TX_N_RETRIES+1) + "/" + str(static.TX_N_MAX_RETRIES) + "]") + + + + # lets start a thread to transmit nonblocking + TRANSMIT_ARQ_BURST_THREAD = threading.Thread(target=modem.transmit_arq_burst, name="TRANSMIT_ARQ_BURST") + TRANSMIT_ARQ_BURST_THREAD.start() + + # lets wait during sending. After sending is finished we will continue + while static.ARQ_STATE == 'SENDING_DATA': + time.sleep(0.05) + + + # --------------------------- START TIMER FOR WAITING FOR ACK ---> IF TIMEOUT REACHED, ACK_TIMEOUT = 1 + #reset timer and ack state + static.ARQ_FRAME_ACK_RECEIVED = 0 + static.ARQ_ACK_RECEIVED = 0 + static.ARQ_ACK_TIMEOUT = 0 + + logging.debug("ARQ | RX | WAITING FOR BURST ACK") + static.ARQ_STATE = 'RECEIVING_ACK' + + timer = threading.Timer(static.ARQ_ACK_TIMEOUT_SECONDS, arq_ack_timeout) timer.start() - # --------------------------- WHILE TIMEOUT NOT REACHED AND NO ACK RECEIVED --> LISTEN - logging.info("TX | WAITING FOR ACK") - while static.ACK_TIMEOUT == 0 and static.ACK_RECEIVED == 0: - static.MODEM_RECEIVE = True - else: - logging.info("TX | ACK TIMEOUT - SENDING AGAIN") - - #--------------- BREAK LOOP IF ACK HAS BEEN RECEIVED - if static.ACK_RECEIVED == 1: - #static.TX_N_RETRIES = 3 + # --------------------------- WHILE TIMEOUT NOT REACHED AND NO ACK RECEIVED --> LISTEN + while static.ARQ_ACK_TIMEOUT == 0 and static.ARQ_ACK_RECEIVED == 0: + time.sleep(0.01) # lets reduce CPU load a little bit + #print(static.ARQ_STATE) + + + if static.ARQ_ACK_RECEIVED == 0 and static.ARQ_ACK_TIMEOUT == 1: + #logging.info("ARQ | RX | ACK TIMEOUT | SENDING ARQ BURST AGAIN") + pass + + #--------------- BREAK LOOP IF ACK HAS BEEN RECEIVED OR FRAME ACK HAS BEEN RECEIVED + if static.ARQ_ACK_RECEIVED == 1: + #-----------IF ACK RECEIVED, INCREMENT ITERATOR FOR MAIN LOOP TO PROCEED WITH NEXT FRAMES/BURST + static.ARQ_N_SENT_FRAMES = static.ARQ_N_SENT_FRAMES + static.ARQ_TX_N_FRAMES_PER_BURST + break + + #--------------- BREAK LOOP IF FRAME ACK HAS BEEN RECEIVED EARLIER AS EXPECTED + if static.ARQ_FRAME_ACK_RECEIVED == 1: + logging.info("ARQ | RX | EARLY FRAME ACK RECEIVED - STOPPING TX") + #static.ARQ_N_SENT_FRAMES = #static.TX_BUFFER_SIZE + static.ARQ_N_SENT_FRAMES = static.ARQ_N_SENT_FRAMES + static.ARQ_TX_N_FRAMES_PER_BURST break # ----------- if no ACK received and out of retries.....stop frame sending - if static.ACK_RECEIVED == 0: - logging.info("TX | NO ACK RECEIVED | FRAME NEEDS TO BE RESEND!") - break + if static.ARQ_ACK_RECEIVED == 0 and static.ARQ_FRAME_ACK_RECEIVED == 0 and static.ARQ_ACK_TIMEOUT == 1: + logging.info("ARQ | TX | NO BURST OR FRAME ACK RECEIVED | DATA SHOULD BE RESEND!") + print(bytes(static.TX_BUFFER[static.ARQ_N_SENT_FRAMES])) + break + #-------------------------BREAK TX BUFFER LOOP IF ALL PACKETS HAVE BEEN SENT AND WE GOT A FRAME ACK + if static.ARQ_N_SENT_FRAMES == static.TX_BUFFER_SIZE and static.ARQ_FRAME_ACK_RECEIVED == 1: + logging.info("ARQ | RX | REGULAR FRAME ACK RECEIVED - DATA TRANSMITTED!") + break - #-------------------------BREAK TX BUFFER LOOP IF ALL PACKETS HAVE BEEN SENT - if n_raw_frame == static.TX_BUFFER_SIZE: - break - - # ------------ TIMER TO WAIT UNTIL NEXT PACKAGE WILL BE SEND TO PREVENT TIME ISSEUS - time.sleep(5) - logging.info("TX | BUFFER EMPTY") + # IF TX BUFFER IS EMPTY / ALL FRAMES HAVE BEEN SENT --> HERE WE COULD ADD AN static.VAR for IDLE STATE - - - -# BURST MACHINE TO DEFINE N BURSTS PER FRAME -def n_frames_per_burst(len_data): - - if len_data <= static.ARQ_PAYLOAD_PER_FRAME: - n_frames_per_burst = 1 - else: - n_frames_per_burst = 1 - - - return n_frames_per_burst \ No newline at end of file + logging.info("ARQ | TX | BUFFER EMPTY") + # - RESET COUNTERS + static.ARQ_N_SENT_FRAMES = 0 + static.ARQ_TX_N_FRAMES_PER_BURST = 0 + static.ARQ_ACK_RECEIVED = 0 + static.ARQ_FRAME_ACK_RECEIVED = 0 + +# 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 = 1 + return n_frames_per_burst diff --git a/helpers.py b/helpers.py new file mode 100644 index 00000000..7f37122e --- /dev/null +++ b/helpers.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Fri Dec 25 21:25:14 2020 + +@author: DJ2LS +""" + +import time +import threading +import logging +import crcengine + + +import static + + + +def get_crc_8(data): + crc_algorithm = crcengine.new('crc8-ccitt') #load crc8 library + crc_data = crc_algorithm(data) + crc_data = crc_data.to_bytes(1, byteorder='big') + return crc_data + +def get_crc_16(data): + crc_algorithm = crcengine.new('crc16-ccitt-false') #load crc16 library + crc_data = crc_algorithm(data) + crc_data = crc_data.to_bytes(2, byteorder='big') + return crc_data \ No newline at end of file diff --git a/install_jate.sh b/install_jate.sh index 500002e2..6262c987 100644 --- a/install_jate.sh +++ b/install_jate.sh @@ -21,6 +21,6 @@ cmake -DCODEC2_BUILD_DIR=~/FreeDV-JATE/codec2/build_linux ../ make -cd ~/FreeDV-Socket-TNC/codec2/build_linux && rm -Rf * +cd ~/FreeDV-JATE/codec2/build_linux && rm -Rf * cmake -DLPCNET_BUILD_DIR=~/FreeDV-JATE/LPCNet/build_linux .. make diff --git a/main.py b/main.py index aee9659b..8fdc7b28 100644 --- a/main.py +++ b/main.py @@ -8,49 +8,45 @@ Created on Tue Dec 22 16:58:45 2020 """ import socketserver -import threading import argparse import logging -import tnc +#import tnc import static -import modem - -modem = modem.RF() - - -#--------------------------------------------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) -parser.add_argument('--tx', dest="audio_output_device", default=0, help="sound card for transmitting.", type=int) -parser.add_argument('--port', dest="socket_port", default=3000, help="Set the port, the socket is listening on.", type=int) - -args = parser.parse_args() - - -static.AUDIO_INPUT_DEVICE = args.audio_input_device -static.AUDIO_OUTPUT_DEVICE = args.audio_output_device -static.PORT = args.socket_port - - -#-------------------------------------------- DEFINE LOGGER -logger = logging.getLogger() -logger.setLevel("INFO") #DEBUG>INFO>WARNING>ERROR>CRITICAL -#--------------------------------------------START AUDIO THREAD -logging.info("STARTING AUDIO THREAD") -static.MODEM_RECEIVE = True -audio_receiver_thread = threading.Thread(target=modem.Receive, name="Audio Listener") -audio_receiver_thread.start() -#--------------------------------------------START SERVER -logging.info("STARTING TCP/IP SOCKET ON PORT " + str(static.PORT)) -try: - socketserver.TCPServer.allow_reuse_address = True #https://stackoverflow.com/a/16641793 - server = socketserver.TCPServer((static.HOST, static.PORT), tnc.TCPRequestHandler) - server.serve_forever() -finally: - server.server_close() \ No newline at end of file + +if __name__ == '__main__': + + #--------------------------------------------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) + parser.add_argument('--tx', dest="audio_output_device", default=0, help="sound card for transmitting.", type=int) + parser.add_argument('--port', dest="socket_port", default=3000, help="Set the port, the socket is listening on.", type=int) + + args = parser.parse_args() + + static.AUDIO_INPUT_DEVICE = args.audio_input_device + static.AUDIO_OUTPUT_DEVICE = args.audio_output_device + static.PORT = args.socket_port + + import tnc # we need to wait until we got all parameters from argparse + + #-------------------------------------------- DEFINE LOGGER + logger = logging.getLogger() + logger.setLevel("INFO") #DEBUG>INFO>WARNING>ERROR>CRITICAL + + + + + #--------------------------------------------START SERVER + logging.info("STARTING TCP/IP SOCKET ON PORT " + str(static.PORT)) + try: + socketserver.TCPServer.allow_reuse_address = True #https://stackoverflow.com/a/16641793 + server = socketserver.TCPServer((static.HOST, static.PORT), tnc.TCPRequestHandler) + server.serve_forever() + finally: + server.server_close() diff --git a/modem.py b/modem.py index 5a572a63..5b55fdfa 100644 --- a/modem.py +++ b/modem.py @@ -10,174 +10,327 @@ import ctypes from ctypes import * import pathlib import pyaudio -import audioop import sys import logging +import time +import threading - +import helpers import static import arq -#arq = arq.ARQ() - - - class RF(): - def __init__(self): - - self.p = pyaudio.PyAudio() - self.defaultFrames = static.DEFAULT_FRAMES - self.audio_input_device = static.AUDIO_INPUT_DEVICE - self.audio_output_device = static.AUDIO_OUTPUT_DEVICE - self.tx_sample_state = static.TX_SAMPLE_STATE - self.rx_sample_state = static.RX_SAMPLE_STATE - self.audio_sample_rate = static.AUDIO_SAMPLE_RATE - self.modem_sample_rate = static.MODEM_SAMPLE_RATE - self.audio_frames_per_buffer = static.AUDIO_FRAMES_PER_BUFFER - self.audio_channels = static.AUDIO_CHANNELS - self.format = pyaudio.paInt16 - self.stream = None - - #self.data_input = "stdin" - self.data_input = "audio" - #self.data_output = "stdout" - self.data_output = "audio" - + def __init__(self): + #-------------------------------------------- LOAD FREEDV libname = pathlib.Path().absolute() / "codec2/build_linux/src/libcodec2.so" self.c_lib = ctypes.CDLL(libname) - - self.mode = static.FREEDV_MODE # define mode - - self.freedv = self.c_lib.freedv_open(self.mode) - self.bytes_per_frame = int(self.c_lib.freedv_get_bits_per_modem_frame(self.freedv)/8) - self.payload_per_frame = self.bytes_per_frame -2 - self.n_tx_modem_samples = self.c_lib.freedv_get_n_tx_modem_samples(self.freedv)*2 #get n_tx_modem_samples which defines the size of the modulation object - self.n_max_modem_samples = self.c_lib.freedv_get_n_max_modem_samples(self.freedv) - self.n_nom_modem_samples = self.c_lib.freedv_get_n_nom_modem_samples(self.freedv) - self.nin = self.c_lib.freedv_nin(self.freedv) - - static.FREEDV_BYTES_PER_FRAME = self.bytes_per_frame - static.FREEDV_PAYLOAD_PER_FRAME = self.payload_per_frame - - - # MODULATION-OUT OBJECT - def ModulationOut(self): - return (c_short * self.n_tx_modem_samples) - - # MODULATION-IN OBJECT - def ModulationIn(self): - return (c_short * (self.n_max_modem_samples)) - - # FRAME BYTES - # Pointer for changing buffer data type - def FrameBytes(self): - return (c_ubyte * self.bytes_per_frame) - + #--------------------------------------------CREATE PYAUDIO INSTANCE + self.p = pyaudio.PyAudio() + #--------------------------------------------GET SUPPORTED SAMPLE RATES FROM SOUND DEVICE + #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_RX = 8000 + #--------------------------------------------OPEN AUDIO CHANNEL RX + self.stream_rx = self.p.open(format=pyaudio.paInt16, + channels=static.AUDIO_CHANNELS, + rate=static.AUDIO_SAMPLE_RATE_RX, + frames_per_buffer=static.AUDIO_FRAMES_PER_BUFFER, + input=True, + input_device_index=static.AUDIO_INPUT_DEVICE, + ) + #--------------------------------------------OPEN AUDIO CHANNEL TX + self.stream_tx = self.p.open(format=pyaudio.paInt16, + channels=1, + rate=static.AUDIO_SAMPLE_RATE_TX, + 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 + ) + #--------------------------------------------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() +#-------------------------------------------------------------------------------------------------------- # GET DATA AND MODULATE IT - def Transmit(self,data_out): - - mod_out = self.ModulationOut()() # new modulation object and get pointer to it - - - data_list = [data_out[i:i+self.payload_per_frame] for i in range(0, len(data_out), self.payload_per_frame)] # split incomming bytes to size of 30bytes, create a list and loop through it + def transmit(self,mode,data_out): + + 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() + 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() + + 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 data_list_length = len(data_list) for i in range(data_list_length): # LOOP THROUGH DATA LIST - - if self.mode < 10: # don't generate CRC16 for modes 0 - 9 - - buffer = bytearray(self.bytes_per_frame) # use this if no CRC16 checksum is required - buffer[:len(data_list[i])] = data_list[i] # set buffersize to length of data which will be send - - if self.mode >= 10: #generate CRC16 for modes 10-12.. - - buffer = bytearray(self.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 = c_ushort(self.c_lib.freedv_gen_crc16(bytes(buffer), self.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 - - - data = self.FrameBytes().from_buffer_copy(buffer) #change data format from bytearray to ctypes.u_byte and copy from buffer to data + 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 - self.c_lib.freedv_rawdatatx(self.freedv,mod_out,data) # modulate DATA and safe it into mod_out pointer - - if self.data_output == "stdout": - sys.stdout.buffer.write(mod_out) # print data to terminal for piping the output to other programs - sys.stdout.flush() # flushing stdout - - if self.data_output == "audio": - #print(self.audio_channels) - stream_tx = self.p.open(format=self.format, - channels=self.audio_channels, - rate=self.audio_sample_rate, - frames_per_buffer=self.n_nom_modem_samples, - output=True, - output_device_index=self.audio_output_device, - ) - - audio = audioop.ratecv(mod_out,2,1,self.modem_sample_rate, self.audio_sample_rate, self.tx_sample_state) - stream_tx.write(audio[0]) - stream_tx.close() - - return mod_out + data = (ctypes.c_ubyte * bytes_per_frame).from_buffer_copy(buffer) + self.c_lib.freedv_rawdatapreambletx(freedv, mod_out_preamble) + self.c_lib.freedv_rawdatatx(freedv,mod_out,data) # modulate DATA and safe it into mod_out pointer + # -------------- preamble area + # WE NEED TO ADJUST IT FOR SINGLE TRANSMISSION + + txbuffer = bytearray() + txbuffer += bytes(mod_out_preamble) + txbuffer += bytes(mod_out) + txbuffer = txbuffer.rstrip(b'\x00') + + + # -------------- transmit audio + self.stream_tx.write(bytes(txbuffer)) + +#-------------------------------------------------------------------------------------------------------- + def transmit_arq_ack(self,ack_buffer): - # DEMODULATE DATA AND RETURN IT - def Receive(self): + static.ARQ_STATE = 'SENDING_ACK' + + 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() + mod_out_preamble = ctypes.c_short * (1760*2) #1760 for mode 10,11,12 #4000 for mode 9 + mod_out_preamble = mod_out_preamble() - # Open Audio Channel once - stream_rx = self.p.open(format=self.format, - channels=self.audio_channels, - rate=self.audio_sample_rate, - frames_per_buffer=self.n_max_modem_samples, - input=True, - input_device_index=self.audio_input_device, - ) - - - while static.MODEM_RECEIVE == True: # Listne to audio until data arrives - - - #if self.data_input == "stdin": - # samples = self.c_lib.freedv_nin(self.freedv)*2 ### MIT DER *2 funktioniert das irgendwie recht zuverlässig bei mode 5! Bei Mode 12 auch - # data_in = sys.stdin.buffer.read(samples) - if self.data_input == "audio": - - data_in = stream_rx.read(self.c_lib.freedv_nin(self.freedv), exception_on_overflow = False) - #print(bytes(data_in)) - buffer = bytearray(self.n_max_modem_samples*2) # N MAX SAMPLES * 2 - buffer[:len(data_in)] = data_in # copy across what we have - - - self.ModulationIn()() #Create new ModulationIn Object - modulation = self.ModulationIn()# get an empty modulation array - modulation = modulation.from_buffer_copy(buffer) # copy buffer across and get a pointer to it. - - bytes_out = self.FrameBytes()() # initilize a pointer to where bytes will be outputed - - nbytes = self.c_lib.freedv_rawdatarx(self.freedv, bytes_out, data_in) # Demodulated data and get number of demodulated bytes - - if nbytes == self.bytes_per_frame: # make sure, we receive a full frame - - print(bytes(bytes_out[:-2])) - self.c_lib.freedv_set_sync(self.freedv, 0) #FORCE UNSYNC - - - # CHECK IF FRAMETYPE CONTAINS ACK------------------------ - frametype = int.from_bytes(bytes(bytes_out[:1]), "big") - if 50 >= frametype >= 10 : - arq.data_received(bytes(bytes_out[:-2])) #send payload data to arq checker without CRC16 - - - # CHECK IF FRAME CONTAINS ACK------------------------ - if bytes(bytes_out[:1]) == b'\7': - arq.ack_received() - - - #return bytes(bytes_out[:-2]) - + 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) + + preamble_bytes = self.c_lib.freedv_rawdatapreambletx(freedv, mod_out_preamble) + self.c_lib.freedv_rawdatatx(freedv,mod_out,data) # modulate DATA and safe it into mod_out pointer + txbuffer = bytearray() + txbuffer += bytes(mod_out_preamble) + txbuffer = txbuffer.rstrip(b'\x00') #lets remove unallocated memory because of wrong buffer :-/ + txbuffer += bytes(mod_out) + txbuffer = txbuffer.rstrip(b'\x00') #lets remove unallocated memory because of wrong buffer :-/ + # -------------- transmit audio + self.stream_tx.write(bytes(txbuffer)) + self.stream_tx.write(bytes(txbuffer)) + + static.ARQ_STATE = 'RECEIVING_DATA' +#-------------------------------------------------------------------------------------------------------- + # GET ARQ BURST FRAME VOM BUFFER AND MODULATE IT + def transmit_arq_burst(self): + static.ARQ_STATE = 'SENDING_DATA' + + self.c_lib.freedv_open.restype = ctypes.POINTER(ctypes.c_ubyte) + 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 + 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() + 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() + + self.c_lib.freedv_rawdatapreambletx(freedv, mod_out_preamble); + txbuffer = bytearray() + txbuffer += bytes(mod_out_preamble) + txbuffer = txbuffer.rstrip(b'\x00') #lets remove unallocated memory because of wrong buffer :-/ + + for n in range(0,static.ARQ_TX_N_FRAMES_PER_BURST): + + #---------------------------BUILD ARQ BURST --------------------------------------------------------------------- + frame_type = 10 + n + 1 #static.ARQ_TX_N_FRAMES_PER_BURST + frame_type = bytes([frame_type]) + + payload_data = bytes(static.TX_BUFFER[static.ARQ_N_SENT_FRAMES + n]) + + n_current_arq_frame = static.ARQ_N_SENT_FRAMES + n + 1 + static.ARQ_TX_N_CURRENT_ARQ_FRAME = n_current_arq_frame.to_bytes(2, byteorder='big') + + n_total_arq_frame = len(static.TX_BUFFER) + static.ARQ_TX_N_TOTAL_ARQ_FRAMES = n_total_arq_frame.to_bytes(2, byteorder='big') + + + + # 1 # frame type and current number of arq frame of burst + # 1 # total number of arq frames per (current) burst + # 2 # current arq frame number + # 2 # total number arq frames + # 2 # arq crc + # N # payload data + + arqframe = frame_type + \ + bytes([static.ARQ_TX_N_FRAMES_PER_BURST]) + \ + static.ARQ_TX_N_CURRENT_ARQ_FRAME + \ + static.ARQ_TX_N_TOTAL_ARQ_FRAMES + \ + static.ARQ_BURST_PAYLOAD_CRC + \ + payload_data + + #print(arqframe) + + buffer = bytearray(static.FREEDV_DATA_PAYLOAD_PER_FRAME) # create TX buffer + buffer[:len(arqframe)] = arqframe # set buffersize to length of data which will be send + + crc = ctypes.c_ushort(self.c_lib.freedv_gen_crc16(bytes(buffer), static.FREEDV_DATA_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 + + 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) + txbuffer = txbuffer.rstrip(b'\x00') #lets remove unallocated memory because of wrong buffer :-/ + + # -------------- transmit audio + self.stream_tx.write(bytes(txbuffer)) + + static.ARQ_STATE = 'RECEIVING_ACK' + +#-------------------------------------------------------------------------------------------------------- + def receive(self,data_mode,signalling_mode): + + self.c_lib.freedv_open.restype = ctypes.POINTER(ctypes.c_ubyte) + + 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 + + # with this we can interrupt receiving + while static.FREEDV_RECEIVE == True: + time.sleep(0.01) + + # stuck in sync counter + stuck_in_sync_counter = 0 + stuck_in_sync_10_counter = 0 + # + + + + 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 = data_in.rstrip(b'\x00') + + #print(data_in) + + 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 + #print(self.c_lib.freedv_get_rx_status(freedv_data)) + + + #-------------STUCK IN SYNC DETECTOR + stuck_in_sync_counter += 1 + if self.c_lib.freedv_get_rx_status(freedv_data) == 10: + stuck_in_sync_10_counter += 1 + + + #print(stuck_in_sync_counter) + if stuck_in_sync_counter == 33 and self.c_lib.freedv_get_rx_status(freedv_data) == 10: + print("stuck in sync #1 --> DOING UNSYNC") + self.c_lib.freedv_set_sync(freedv_data, 0) #FORCE UNSYNC + stuck_in_sync_counter = 0 + stuck_in_sync_10_counter = 0 + data_in = None + + + if stuck_in_sync_counter >= 66 and stuck_in_sync_10_counter >= 2: + print("stuck in sync #2 --> DOING UNSYNC") + self.c_lib.freedv_set_sync(freedv_data, 0) #FORCE UNSYNC + stuck_in_sync_counter = 0 + stuck_in_sync_10_counter = 0 + data_in = None + #----------------------------------- + + + #modem_stats_snr = c_float() + #modem_stats_sync = c_int() + + #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) + + if nbytes == static.FREEDV_DATA_BYTES_PER_FRAME: + # counter reset for stuck in sync counter + stuck_in_sync_counter = 0 + stuck_in_sync_10_counter = 0 + # + + # CHECK IF FRAMETYPE IS BETWEEN 10 and 50 ------------------------ + frametype = int.from_bytes(bytes(data_bytes_out[:1]), "big") + frame = frametype - 10 + n_frames_per_burst = int.from_bytes(bytes(data_bytes_out[1:2]), "big") + if 50 >= frametype >= 10 and len(data_bytes_out) > 30: # --> The length check filters out random strings without CRC + arq.data_received(bytes(data_bytes_out[:-2])) #send payload data to arq checker without CRC16 + else: + print("MODE: " + str(data_mode) + " DATA: " + str(bytes(data_bytes_out))) + + # NEEDS TO BE OPTIMIZED + # DO UNSYNC AFTER LAST BURST by checking the frame numbers agains the total frames per burst + if frame == n_frames_per_burst: + self.c_lib.freedv_set_sync(freedv_data, 0) #FORCE UNSYNC + + # DETECT STUCK IN SYNC + # count rx status 10 + # if 10 greater 2 after 6 then unsync + + while static.ARQ_STATE == 'IDLE' or static.ARQ_STATE == 'RECEIVING_ACK': + time.sleep(0.01) + + 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 = data_in.rstrip(b'\x00') + + 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 + + # CHECK IF FRAME CONTAINS ACK------------------------ + frametype = int.from_bytes(bytes(signalling_bytes_out[:1]), "big") + + # BURST ACK + if frametype == 60 and nbytes == static.FREEDV_SIGNALLING_BYTES_PER_FRAME: + arq.burst_ack_received() + # FRAME ACK + if frametype == 61 and nbytes == static.FREEDV_SIGNALLING_BYTES_PER_FRAME: + arq.frame_ack_received() + + + rxstatus = self.c_lib.freedv_get_rx_status(freedv_signalling) + #print(rxstatus) + if nbytes == static.FREEDV_SIGNALLING_BYTES_PER_FRAME or rxstatus == 10: + self.c_lib.freedv_set_sync(freedv_signalling, 0) #FORCE UNSYNC + diff --git a/other.py b/other.py deleted file mode 100644 index 59fc98d7..00000000 --- a/other.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Fri Dec 25 21:25:14 2020 - -@author: DJ2LS -""" - -import time -import threading -import logging - -import static - - - -def timeout(): - static.ACK_TIMEOUT = 1 \ No newline at end of file diff --git a/static.py b/static.py index 051ff546..90555be3 100644 --- a/static.py +++ b/static.py @@ -3,17 +3,25 @@ """ Created on Wed Dec 23 11:13:57 2020 -@author: parallels +@author: DJ2LS """ +# ADDITION MESSUREMENT: +#AUDIO TIME: 7.451462268829346 #12 # 1 FRAME + PREAMBLE +#MODULATION TIME: 0.002051115036010742 #12 # 1 FRAME + PREAMBLE -# Modem States -MODEM_RECEIVE = True - +#MODULATION TIME: 0.004580974578857422 #12 # 2 FRAME + PREAMBLE +#AUDIO TIME: 14.750595331192017 #12 # 2 FRAME + PREAMBLE # FreeDV Defaults -FREEDV_MODE = 12 -FREEDV_BYTES_PER_FRAME = 0 -FREEDV_PAYLOAD_PER_FRAME = 0 +FREEDV_RECEIVE = True + +FREEDV_DATA_MODE = 12 +FREEDV_SIGNALLING_MODE = 14 + +FREEDV_DATA_BYTES_PER_FRAME = 0 +FREEDV_DATA_PAYLOAD_PER_FRAME = 0 +FREEDV_SIGNALLING_BYTES_PER_FRAME = 0 +FREEDV_SIGNALLING_PAYLOAD_PER_FRAME = 0 # Server Defaults HOST = "localhost" @@ -21,38 +29,78 @@ PORT = 3000 #AUdio Defaults -DEFAULT_FRAMES = 1024 AUDIO_INPUT_DEVICE = 1 AUDIO_OUTPUT_DEVICE = 1 -TX_SAMPLE_STATE = None -RX_SAMPLE_STATE = None -AUDIO_SAMPLE_RATE = 8000 -MODEM_SAMPLE_RATE = 8000 -AUDIO_FRAMES_PER_BUFFER = 1024 +#TX_SAMPLE_STATE = None +#RX_SAMPLE_STATE = None + +#AUDIO_SAMPLE_RATE_RX = 44100 +#AUDIO_SAMPLE_RATE_TX = 44100 +MODEM_SAMPLE_RATE = 8000 #8000 +AUDIO_FRAMES_PER_BUFFER = 2048 AUDIO_CHANNELS = 1 #TNC DEFAULTS -# ----ARQ STOP AND WAIT +# ARQ TX_N_MAX_RETRIES = 3 -TX_N_RETRIES = 3 -ACK_RECEIVED = 0 -ACK_TIMEOUT = 0 -ACK_TIMEOUT_SECONDS = 15.0 -ACK_RX_TIMEOUT = 0 -ACK_RX_TIMEOUT_SECONDS = 15.0 +TX_N_RETRIES = 0 + + + +ARQ_TX_N_FRAMES_PER_BURST = 0 +ARQ_TX_N_BURSTS = 0 -ARQ_TX_N_FRAMES = 2 ARQ_PAYLOAD_PER_FRAME = 0 ARQ_ACK_WAITING_FOR_ID = 0 - ARQ_RX_BURST_BUFFER = [] ARQ_RX_FRAME_BUFFER = [] +ARQ_RX_FRAME_N_BURSTS = 0 + +## TX +ARQ_TX_N_CURRENT_ARQ_FRAME = 0 +ARQ_TX_N_TOTAL_ARQ_FRAMES = 0 +## + +## RX +ARQ_N_ARQ_FRAMES_PER_DATA_FRAME = 0 #total number of arq frames per data frame +ARQ_RX_N_CURRENT_ARQ_FRAME = 0 +## + +ARQ_N_RX_ARQ_FRAMES = 0 # total number of received frames + +ARQ_N_RX_FRAMES_PER_BURSTS = 0 # NUMBER OF FRAMES WE ARE WAITING FOR --> GOT DATA FROM RECEIVED FRAME +ARQ_ACK_PAYLOAD_PER_FRAME = 0 # PAYLOAD per ACK frame +ARQ_ACK_RECEIVED = 0 # set to 1 if ACK received +ARQ_FRAME_ACK_RECEIVED = 0 # set to 1 if FRAME ACK received +ARQ_ACK_TIMEOUT = 0 # set to 1 if timeut reached +ARQ_ACK_TIMEOUT_SECONDS = 4.0 #timeout for waiting for ACK frames FRAME_CRC = b'' +FRAME_BOF = b'\xAA\xAA' #here we define 2 bytes for the BOF +FRAME_EOF = b'\xFF\xFF' #here we define 2 bytes for the EOF +ARQ_FRAME_BOF_RECEIVED = False # status, if we received a BOF of a data frame +ARQ_FRAME_EOF_RECEIVED = False # status, if we received a EOF of a data frame +ARQ_N_SENT_FRAMES = 0 #counter for already sent frames + + +# ARQ STATES: +# IDLE +# RECEIVING_DATA +# SENDING_DATA +# RECEIVING_ACK +# SENDING_ACK +# ACK_RECEIVED +# +# +# +ARQ_STATE = 'RECEIVING_DATA' # ------- TX BUFFER TX_BUFFER_SIZE = 0 - +TX_BUFFER = [] +# ------- RX BUFFER +RX_BUFFER = [] +RX_BUFFER_SIZE = 0 diff --git a/test/README.md b/test/README.md deleted file mode 100644 index 4339c265..00000000 --- a/test/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# TESTS - -### TEST_RX.py - -Receive string "HALLO" with a lot of debugging output -``` -./TEST_TX.py | ./TEST_RX.py -``` -Output should be: -``` -SYNC -INPUT PARSER: 1760 -INPUT LENGTH: 1760 -BUFFER LENGTH: 1848 -MODULATION LENGTH: 924 -SYNC -b'HALLO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15S' -INPUT PARSER: 1760 -INPUT LENGTH: 1760 -BUFFER LENGTH: 1848 -MODULATION LENGTH: 924 -SYNC -INPUT PARSER: 1760 -``` - -### TEST_TX.py - -Send string "HALLO" -``` -./TEST_TX.py | ./freedv_data_raw_rx DATAC3 - - | hexdump -C -``` -Output should be: -``` -payload bytes_per_modem_frame: 30 -00000000 54 45 53 54 00 00 00 00 00 00 00 00 00 00 00 00 |TEST............| -modem bufs processed: 22 output bytes: 30 output_frames: 1 -00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |..............| -0000001e -``` - - -### Checksum comparison FREEDV vs CRCENGINE -``` - - ###################### CHECKSUM COMPARISON FREEDV VS CRCENGINE ######## - #https://crccalc.com - - teststring = b'TEST' - - - - # freedv crc16 checksum - crctest2 = c_ushort(self.c_lib.freedv_gen_crc16(teststring, len(teststring))) - print("FREEDV2: " + str(crctest2.value) + " = " + hex(crctest2.value)) - - - #Python crc16 checksum - crc_algorithm = crcengine.new('crc16-ccitt-false') #load crc16 library - crctest3 = crc_algorithm(teststring) - print("CRCENGINE: " + str(crctest3) + " = " + hex(crctest3)) - - - ####################################################################### -``` diff --git a/test/TEST_RX.py b/test/TEST_RX.py deleted file mode 100644 index b13b4f94..00000000 --- a/test/TEST_RX.py +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Tue Dec 15 18:29:22 2020 - -@author: DJ2LS -""" - - -import ctypes -from ctypes import * -import pathlib - -import binascii #required for string.to_bytes() function -import sys - - -def main(): - - modem = FreeDV() #Load FreeDV Class as "modem" - - modem.Demodulate() - -class FreeDV(): - - def __init__(self): - - self.mode = 12 # define mode - - libname = pathlib.Path().absolute() / "libcodec2.so" - self.c_lib = ctypes.CDLL(libname) # Load FreeDV shared libary - - self.freedv = self.c_lib.freedv_open(self.mode) #Set FreeDV waveform ( 10,11,12 --> DATA1-3 ) - - self.bytes_per_frame = int(self.c_lib.freedv_get_bits_per_modem_frame(self.freedv)/8) #get bytes per frame from selected waveform - self.payload_per_frame = self.bytes_per_frame -2 #get frame payload because of 2byte CRC16 checksum - self.n_max_modem_samples = self.c_lib.freedv_get_n_max_modem_samples(self.freedv) - self.nin = self.c_lib.freedv_nin(self.freedv) - - print("N MAX MODEM SAMPLES: " + str(self.n_max_modem_samples)) - print("NIN: " + str(self.nin)) - print("BYTES PER FRAME: " + str(self.bytes_per_frame)) - print("---------------------------------") - - # MODULATION-IN OBJECT - def ModulationIn(self): - return (c_short * (self.n_max_modem_samples)) ## - - - # Pointer for changing buffer data type - def FrameBytes(self): - return (c_ubyte * self.bytes_per_frame) - - - # Modulate function which returns modulated data - def Demodulate(self): - - - nin = self.c_lib.freedv_nin(self.freedv) - #while (self.c_lib.freedv_nin(self.freedv) == nin): - #while sys.stdin.buffer != 0: - while True: - - samples = self.c_lib.freedv_nin(self.freedv)*2 ### MIT DER *2 funktioniert das irgendwie recht zuverlässig bei mode 5! Bei Mode 12 auch - data_in = sys.stdin.buffer.read(samples) - - - #buffer = bytearray(len(self.ModulationIn()())*sizeof(c_short)) # create empty byte array - #buffer = bytearray(len(self.ModulationIn()())*self.n_max_modem_samples) # create empty byte array - buffer = bytearray(self.n_max_modem_samples*2) # N MAX SAMPLES * 2 - buffer[:len(data_in)] = data_in # copy across what we have - - self.ModulationIn()() #Create new ModulationIn Object - modulation = self.ModulationIn()# get an empty modulation array - modulation = modulation.from_buffer_copy(buffer) # copy buffer across and get a pointer to it. - bytes_out = self.FrameBytes()() # initilize a pointer to where bytes will be outputed - - nbytes = self.c_lib.freedv_rawdatarx(self.freedv, bytes_out, data_in) # Demodulated data and get number of demodulated bytes - #self.nin = self.c_lib.freedv_nin(self.freedv) - - - # ------------- SOME DEBUGGING OUTPUT - #print("INPUT PARSER: " + str(samples)) - #print("INPUT LENGTH: " + str(len(data_in))) - #print("BUFFER LENGTH: " + str(len(buffer))) - #print("MODULATION LENGTH: " + str(len(modulation))) - - #sync_state = self.c_lib.freedv_get_sync(self.freedv) - #if sync_state > 0: - # print("SYNC") - #else: - # print("NO SYNC") - # ------------- - - - - # print data to terminal if nbytes == bytes_per_frame for selected mode - if nbytes == self.bytes_per_frame: - print(bytes(bytes_out)) - self.c_lib.freedv_set_sync(self.freedv, 0) - - - #Stop loop until data input is empty - #if len(data_in) == 0: - # False - - - - - -main() diff --git a/test/TEST_TX.py b/test/TEST_TX.py deleted file mode 100644 index 9a6ce03b..00000000 --- a/test/TEST_TX.py +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Sun Dec 13 13:45:58 2020 -@author: DJ2LS -""" - -import ctypes -from ctypes import * -import pathlib - -import binascii #required for string.to_bytes() function -import sys - - - -def main(): - - modem = FreeDV() #Load FreeDV Class as "modem" - - data = b'HALLO' #byte string which will be send - - modem.Modulate(data) #Call Modulate function, which modulates data and prints it to the terminal - - -class FreeDV(): - - def __init__(self): - - self.mode = 12 # define mode - - libname = pathlib.Path().absolute() / "libcodec2.so" - self.c_lib = ctypes.CDLL(libname) # Load FreeDV shared libary - - self.freedv = self.c_lib.freedv_open(self.mode) #Set FreeDV waveform ( 10,11,12 --> DATAC1-3 ) - - self.bytes_per_frame = int(self.c_lib.freedv_get_bits_per_modem_frame(self.freedv)/8) #get bytes per frame from selected waveform - self.payload_per_frame = self.bytes_per_frame -2 #get frame payload because of 2byte CRC16 checksum - self.n_tx_modem_samples = self.c_lib.freedv_get_n_tx_modem_samples(self.freedv)*2 #get n_tx_modem_samples which defines the size of the modulation object - - # MODULATION-OUT OBJECT - def ModulationOut(self): - return (c_short * self.n_tx_modem_samples) ## - - # Pointer for changing buffer data type - def FrameBytes(self): - return (c_ubyte * self.bytes_per_frame) - - # Modulate function which returns modulated data - def Modulate(self,data_out): - - mod_out = self.ModulationOut()() # new modulation object and get pointer to it - - data_list = [data_out[i:i+self.payload_per_frame] for i in range(0, len(data_out), self.payload_per_frame)] # split incomming bytes to size of 30bytes, create a list and loop through it - data_list_length = len(data_list) - for i in range(data_list_length): # LOOP THROUGH DATA LIST - - if self.mode < 10: # don't generate CRC16 for modes 0 - 9 - - buffer = bytearray(self.bytes_per_frame) # use this if no CRC16 checksum is required - buffer[:len(data_list[i])] = data_list[i] # set buffersize to length of data which will be send - - if self.mode >= 10: #generate CRC16 for modes 10-12.. - - buffer = bytearray(self.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 = c_ushort(self.c_lib.freedv_gen_crc16(bytes(buffer), self.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 - - - data = self.FrameBytes().from_buffer_copy(buffer) #change data format from bytearray to ctypes.u_byte and copy from buffer to data - - self.c_lib.freedv_rawdatatx(self.freedv,mod_out,data) # modulate DATA and safe it into mod_out pointer - - sys.stdout.buffer.write(mod_out) # print data to terminal for piping the output to other programs - sys.stdout.flush() # flushing stdout - - -main() \ No newline at end of file diff --git a/test/libcodec2.so b/test/libcodec2.so deleted file mode 100644 index 2a0b1961..00000000 Binary files a/test/libcodec2.so and /dev/null differ diff --git a/test/liblpcnetfreedv.so b/test/liblpcnetfreedv.so deleted file mode 100644 index 343b94ef..00000000 Binary files a/test/liblpcnetfreedv.so and /dev/null differ diff --git a/tnc.py b/tnc.py index 8a70d9cd..f0785dd4 100644 --- a/tnc.py +++ b/tnc.py @@ -1,37 +1,37 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +""" +Created on Fri Dec 25 21:25:14 2020 +@author: DJ2LS +""" import socketserver import threading import logging -import crcengine -import ctypes -from ctypes import * - - - - import static -#from other import * -import other import arq -#arq = arq.ARQ() - - -crc_algorithm = crcengine.new('crc16-ccitt-false') #load crc16 library class TCPRequestHandler(socketserver.BaseRequestHandler): def handle(self): + + self.data = bytes() + while True: + chunk = self.request.recv(8192)#.strip() + if chunk.endswith(b'\n'): + break + + self.data += chunk + # self.request is the TCP socket connected to the client - self.data = self.request.recv(1024).strip() - + #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: @@ -46,29 +46,31 @@ class TCPRequestHandler(socketserver.BaseRequestHandler): #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) + # BROADCAST PARSER ----------------------------------------------------------- if self.data.startswith(b'BC:'): #import modem #modem = modem.RF() - - static.MODEM_RECEIVE = True ####### FALSE.... - + data = self.data.split(b'BC:') #modem.Transmit(data[1]) - - static.MODEM_RECEIVE = True + # SEND AN ARQ FRAME ----------------------------------------------------------- - if self.data.startswith(b'ACK:'): - static.MODEM_RECEIVE = True ############## FALSE + if self.data.startswith(b'ARQ:'): - data = self.data.split(b'ACK:') + data = self.data.split(b'ARQ:') data_out = data[1] - - arq.transmit(data_out) + + #arq.transmit(data_out) + + TRANSMIT_ARQ = threading.Thread(target=arq.transmit, args=[data_out], name="TRANSMIT_ARQ") + TRANSMIT_ARQ.start() - \ No newline at end of file +