diff --git a/arq.py b/arq.py index 8262e26c..c6a70eb8 100644 --- a/arq.py +++ b/arq.py @@ -11,6 +11,7 @@ Created on Sun Dec 27 20:43:40 2020 import logging import crcengine import threading +import time import static import modem @@ -30,48 +31,104 @@ static.ARQ_PAYLOAD_PER_FRAME = static.FREEDV_PAYLOAD_PER_FRAME - 6 # print("TEST") -def receive(data_in): - print(data_in[:1]) +def data_received(data_in): + + ARQ_N_RX_BURSTS = int.from_bytes(bytes(data_in[:1]), "big") - 10 + static.ARQ_RX_BUFFER.append(data_in) #append data to RX BUFFER + + if len(static.ARQ_RX_BUFFER) == ARQ_N_RX_BURSTS: #if received bursts are equal to burst number in frame + + burst_total_payload = bytearray() + for n_raw_frame in range(0,len(static.ARQ_RX_BUFFER)): + + burst_frame = static.ARQ_RX_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 + + 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]) +"]") + + #BUILDING ACK FRAME ----------------------------------------------- + + ack_frame = b'\7' + bytes(burst_payload_crc) + ack_buffer = bytearray(static.ARQ_PAYLOAD_PER_FRAME) + ack_buffer[:len(ack_frame)] = ack_frame # set buffersize to length of data which will be send + + + #TRANSMIT ACK FRAME ----------------------------------------------- + + modem.Transmit(ack_buffer) + static.ARQ_RX_BUFFER = [] + else: #IF burst payload crc and input crc are NOT equal + print("CRC NOT EQUAL!!!!!") + print(data_in[1:3]) + static.ARQ_RX_BUFFER = [] + + +def ack_received(): + + logging.info("TX | 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 + + + def transmit(data_out): - static.ARQ_PAYLOAD_PER_FRAME = static.FREEDV_PAYLOAD_PER_FRAME - 6 + static.ARQ_PAYLOAD_PER_FRAME = static.FREEDV_PAYLOAD_PER_FRAME - 3 - 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, create a list and loop through it + 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) + + logging.info("TX | TOTAL PAYLOAD BYTES/FRAMES TO SEND: " + str(len(data_out)) + " / " + str(static.TX_BUFFER_SIZE)) + + 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 print("N_RAW_FRAME: " + str(n_raw_frame)) # ----------- GENERATE PAYLOAD CRC FOR ARQ_TX_N_FRAMES burst_total_payload = bytearray() - for i in range(static.ARQ_TX_N_FRAMES): #bytearray(b'111111111111111111111111222222222222222222222222') - burst_total_payload = burst_total_payload + static.TX_BUFFER[n_raw_frame + i] + # 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 burst_payload_crc = crc_algorithm(burst_total_payload) - burst_payload_crc = burst_payload_crc.to_bytes(2, byteorder='big') #b'\xa7\xd6' + 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): - - #print(n_raw_frame) - #print(i) - #print(n_raw_frame + i) - #print(static.TX_BUFFER[n_raw_frame + i]) 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 - arqburst.append(arqframe) + buffer = bytearray(static.FREEDV_PAYLOAD_PER_FRAME) # create TX buffer + buffer[:len(arqframe)] = arqframe # set buffersize to length of data which will be send + + arqburst.append(buffer) #--------------------------------------------- N ATTEMPTS TO SEND BURSTS IF ACK FAILS @@ -83,6 +140,7 @@ def transmit(data_out): 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]) diff --git a/modem.py b/modem.py index 0c7b994d..5a572a63 100644 --- a/modem.py +++ b/modem.py @@ -14,6 +14,7 @@ import audioop import sys import logging + import static import arq @@ -25,7 +26,6 @@ class RF(): def __init__(self): - self.p = pyaudio.PyAudio() self.defaultFrames = static.DEFAULT_FRAMES self.audio_input_device = static.AUDIO_INPUT_DEVICE @@ -38,21 +38,17 @@ class RF(): 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" - - + 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 @@ -83,32 +79,37 @@ class RF(): def Transmit(self,data_out): mod_out = self.ModulationOut()() # new modulation object and get pointer to it - - 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_out)] = data_out - if self.mode >= 10: #generate CRC16 for modes 10-12.. + + 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 - buffer = bytearray(self.payload_per_frame) # use this if CRC16 checksum is required ( DATA1-3) - buffer[:len(data_out)] = data_out + if self.mode >= 10: #generate CRC16 for modes 10-12.. - 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(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 + 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, @@ -161,37 +162,22 @@ class RF(): 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) - - + 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 20 >= frametype >= 10 : - arq.receive(bytes(bytes_out)) + 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[:6]) == b'REQACK': - - #logging.info("RX | ACK REQUESTED!") - #time.sleep(5) - #logging.info("TX | SENDING ACK FRAME") - #self.Transmit(b'ACK') - #---------------------------------------------------- - - # CHECK IF FRAME CONTAINS ACK------------------------ - if bytes(bytes_out[:3]) == b'ACK': - - logging.info("TX | 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 - - #---------------------------------------------------- + if bytes(bytes_out[:1]) == b'\7': + arq.ack_received() + #return bytes(bytes_out[:-2]) - diff --git a/static.py b/static.py index f2cb92c9..edf009a4 100644 --- a/static.py +++ b/static.py @@ -12,6 +12,8 @@ MODEM_RECEIVE = True # FreeDV Defaults FREEDV_MODE = 12 +FREEDV_BYTES_PER_FRAME = 0 +FREEDV_PAYLOAD_PER_FRAME = 0 # Server Defaults HOST = "localhost" @@ -38,6 +40,13 @@ ACK_RECEIVED = 0 ACK_TIMEOUT = 0 ACK_TIMEOUT_SECONDS = 10.0 +ARQ_TX_N_FRAMES = 2 +ARQ_PAYLOAD_PER_FRAME = 0 +ARQ_ACK_WAITING_FOR_ID = 0 + +ARQ_RX_BUFFER = [] + + # ------- TX BUFFER TX_BUFFER_SIZE = 0