class Receiver:
    def __init__(
        self,
        bus,
        device,
    ):
        self.spiRecv = spidev.SpiDev()
        self.spiRecv.open(bus, device)
        self.spiRecv.max_speed_hz = 6250000  #976000
        self.spiRecv.mode = 0b00

        self.spiSend = spidev.SpiDev()
        self.spiSend.open(bus, 0)
        self.spiSend.max_speed_hz = 6250000  #976000
        self.spiSend.mode = 0b00
        spi_init()

        self.drop_id = 0
        self.glass = Glass(0)
        self.chunk_size = 0
        self.recv_done_flag = False
        self.feedback_ack_flag = False
        self.chunk_process = []
        self.feedback_idx = 0
        self.data_rec = ""
        self.recv_dir = os.path.join(
            RECV_PATH,
            time.asctime().replace(' ', '_').replace(':', '_'))

    '''LT喷泉码接收解码部分'''

    def begin_to_catch(self):
        a_drop_bytes = self.catch_a_drop_spi()  # bytes

        if a_drop_bytes is not None:
            self.drop_id += 1
            print("====================")
            print("Recv dropid : ", self.drop_id)
            print("====================")
            if len(a_drop_bytes) > 0:
                check_data = recv_check(a_drop_bytes)

                if not check_data == None:
                    self.drop_byte_size = len(check_data)
                    self.add_a_drop(check_data)  # bytes --- drop --- bits

    def catch_a_drop_spi(self):
        if GPIO.input(25):
            spi_recv = self.spiRecv.readbytes(239)
            rec_bytes = bytes(spi_recv)
            frame_len = len(rec_bytes)
            print("framelen: ", frame_len)

            if (frame_len > 1):
                while (rec_bytes[frame_len - 1] == 0 and frame_len >= 1):
                    frame_len = frame_len - 1
            rec_bytes = rec_bytes[:frame_len]
            print("droplen: ", frame_len)

            self.data_rec = rec_bytes
            if self.data_rec[0:2] == b'##' and self.data_rec[
                    frame_len - 2:frame_len] == b'$$':
                data_array = bytearray(self.data_rec)
                data_array.pop(0)
                data_array.pop(0)
                data_array.pop()
                data_array.pop()
                return bytes(data_array)
            else:
                print(self.data_rec)
                print('Wrong receive frame !')

    def add_a_drop(self, d_byte):
        drop = self.glass.droplet_from_Bytes(d_byte)  # drop
        if self.glass.num_chunks == 0:
            print('init num_chunks : ', drop.num_chunks)
            self.glass = Glass(drop.num_chunks)  # 初始化接收glass
            self.chunk_size = len(drop.data)

        #若为ew,则需要将drop转为ewdrop,两个drop里译码方式不一样
        ew_drop = EW_Droplet(drop.data, drop.seed, drop.num_chunks,
                             drop.process, drop.func_id, drop.feedback_idx)
        print('drop data len: ', len(drop.data))

        self.glass.addDroplet(drop)  # glass add drops

        logging.info('=============================')
        logging.info('Decode Progress: ' + str(self.glass.chunksDone()) + '/' +
                     str(self.glass.num_chunks))
        logging.info('=============================')

        # 接收完成
        if self.glass.isDone():
            self.recv_done_flag = True
            logging.info('============Recv done===========')
            # 接收完成写入图像
            img_data = self.glass.get_bits()
            os.mkdir(self.recv_dir)
            with open(os.path.join(self.recv_dir, "img_recv" + ".jpg"),
                      'wb') as f:
                f.write(img_data)
            self.send_recv_done_ack()  # 接收完成返回ack

        # 接收到K个数据包之后
        if self.drop_id >= self.glass.num_chunks:
            if (self.drop_id - self.glass.num_chunks) % 10 == 0:
                self.chunk_process = self.glass.getProcess()  # 用于返回进程包
                self.glass.glass_process_history.append(
                    self.chunk_process)  # 添加反馈历史数据,用于droplet参数,正确译码
                self.send_feedback()
                print("Feedback chunks: ", self.chunk_process)
                print("Feedback chunks num: ", len(self.chunk_process))
                print("Feedback idx: ", self.feedback_idx)
                self.feedback_idx += 1

    def send_recv_done_ack(self):
        if self.recv_done_flag:
            ack = b'#$'
            acklen = len(ack)
            acksend = bytearray(ack)
            while (acklen < 239):
                acksend.insert(acklen, 0)
                acklen += 1
            self.spiSend.xfer2(acksend)
            logging.info('Send fountain ACK done')
            logging.info('Recv drops: ' + str(self.drop_id))
            logging.info('Wrong frame drops: ' + str(self.drop_id))
            logging.info('Wrong checksum drops: ' + str(self.drop_id))

    def send_feedback(self):
        fb = b'$#'
        for chunk_id in self.chunk_process:
            chunk_id_bits = format(int(chunk_id), "016b")
            fb += bitarray.bitarray(chunk_id_bits).tobytes()

        fblen = len(fb)
        fbsend = bytearray(fb)
        while (fblen < 239):
            fbsend.insert(fblen, 0)
            fblen += 1
        self.spiSend.xfer2(fbsend)
Esempio n. 2
0
class Receiver:
    def __init__(
        self,
        bus,
        device,
        port,
        baudrate,
        timeout,
    ):
        self.spiRecv = spidev.SpiDev()
        self.spiRecv.open(bus, device)
        self.spiRecv.max_speed_hz = 6250000  #976000
        self.spiRecv.mode = 0b00

        self.spiSend = spidev.SpiDev()
        self.spiSend.open(bus, 0)
        self.spiSend.max_speed_hz = 6250000  #976000
        self.spiSend.mode = 0b00

        # 串口和spi初始化
        spi_init()
        self.port = serial.Serial(port, baudrate)

        self.packet_id = 0  # 数据包数量
        self.drop_id = 0  # 校验正确的有效数据包数量
        self.real_drop_id = 0  # dropid除去度函数切换和进度包更新时收到的drop
        self.glass = Glass(0)
        self.chunk_size = 0
        self.recv_done_flag = False
        self.feedback_ack_flag = False  # 用于度函数切换过程不接收
        self.feedback_send_done = False
        self.chunk_process = []
        self.feedback_idx = 0
        self.data_rec = ""
        self.recv_dir = os.path.join(
            RECV_PATH,
            time.asctime().replace(' ', '_').replace(':', '_'))

        # 吞吐量统计
        self.packid_save = []
        self.dropid_save = []
        self.valid_dropid_save = []
        self.packs_per_sec = []
        self.drops_per_sec = []
        self.valid_drops_per_sec = []
        self.ttl_timer_start = False
        self.t0 = 0
        self.t1 = 0

        self.process_bytes = b''
        self.decode_time = []  # 用于统计每个编码包的译码时间

    '''LT喷泉码接收解码部分'''

    def begin_to_catch(self):
        a_drop_bytes = self.catch_a_drop_spi()  # bytes

        if a_drop_bytes is not None:
            decode_t0 = time.time()
            # 从收到第一个包之后,开启定时记录吞吐量线程,记录时间
            if self.ttl_timer_start == False:
                self.t0 = time.time()
                self.creat_ttl_Timer()
                self.ttl_timer_start = True

            self.packet_id += 1
            print("Recv Packet id : ", self.packet_id)
            if len(a_drop_bytes) > 0:
                check_data = recv_check(a_drop_bytes)  # 接收校验

                if not check_data == None:
                    self.drop_byte_size = len(check_data)
                    self.add_a_drop(check_data)  # bytes --- drop --- bits
                    decode_t1 = time.time()
                    self.decode_time.append(decode_t1 - decode_t0)

    def catch_a_drop_spi(self):
        if GPIO.input(25):
            spi_recv = self.spiRecv.readbytes(239)
            rec_bytes = bytes(spi_recv)
            frame_len = len(rec_bytes)
            print("framelen: ", frame_len)

            # 去掉末尾补充的0
            if (frame_len > 1):
                while (rec_bytes[frame_len - 1] == 0 and frame_len >= 1):
                    frame_len = frame_len - 1
            rec_bytes = rec_bytes[:frame_len]
            print("droplen: ", frame_len)

            self.data_rec = rec_bytes
            # 判断帧头帧尾
            if self.data_rec[0:2] == b'##' and self.data_rec[
                    frame_len - 2:frame_len] == b'$$':
                data_array = bytearray(self.data_rec)
                data_array.pop(0)
                data_array.pop(0)
                data_array.pop()
                data_array.pop()
                return bytes(data_array)
            else:
                print(self.data_rec)
                print('Wrong receive frame !')

    def add_a_drop(self, d_byte):
        self.drop_id += 1
        print("====================")
        print("Recv dropid : ", self.drop_id)
        print("====================")

        print('glass_process_history len: ',
              len(self.glass.glass_process_history))
        drop = self.glass.droplet_from_Bytes(d_byte)  # drop
        if self.glass.num_chunks == 0:
            print('init num_chunks : ', drop.num_chunks)
            self.glass = Glass(drop.num_chunks)  # 初始化接收glass
            self.chunk_size = len(drop.data)

        #若为ew,则需要将drop转为ewdrop,两个drop里译码方式不一样
        ew_drop = EW_Droplet(drop.data, drop.seed, drop.num_chunks,
                             drop.process, drop.func_id, drop.feedback_idx)
        print('drop data len: ', len(drop.data))

        self.glass.addDroplet(ew_drop)

        # 这里解决度函数切换过程不接收,解决进度包更新过程不接收
        # if (self.feedback_ack_flag==False) or (self.feedback_ack_flag==True and ew_drop.func_id==1 and ew_drop.feedback_idx==self.feedback_idx-1):
        #     self.real_drop_id += 1
        #     self.glass.addDroplet(ew_drop)         # glass add drops
        #     print("=============================================================")
        #     print("除去度函数切换和进度包更新时收到的drop,Real_Recv_dropid : ", self.real_drop_id)
        #     print("=============================================================")
        #     self.feedback_send_done=False

        logging.info('=============================')
        logging.info('Decode Progress: ' + str(self.glass.chunksDone()) + '/' +
                     str(self.glass.num_chunks))
        logging.info('=============================')

        # 接收完成
        if self.glass.isDone():
            self.recv_done_flag = True
            self.t1 = time.time()
            logging.info('============Recv done===========')
            logging.info("Fountain time elapsed:" + str(self.t1 - self.t0))
            # 接收完成写入图像
            img_data = self.glass.get_bits()
            os.mkdir(self.recv_dir)
            with open(os.path.join(self.recv_dir, "img_recv" + ".bmp"),
                      'wb') as f:
                f.write(img_data)

            # 串口模拟水声延迟进行反馈
            t1 = threading.Timer(1.8, self.send_recv_done_ack)
            t1.start()

            # 实际写入水声通信机进行反馈
            # self.send_recv_done_ack() # 接收完成返回ack

            # 记录吞吐量
            self.cal_ttl()
            print('packet_id history: ', self.packid_save,
                  len(self.packid_save))
            print('packets_per_sec: ', self.packs_per_sec,
                  len(self.packs_per_sec))
            print('drop_id history: ', self.dropid_save, len(self.dropid_save))
            logging.info('Send Sonic Fountain ACK done')
            logging.info('Recv Packets: : ' + str(self.packet_id))
            logging.info('Recv drops: ' + str(self.drop_id))
            logging.info('Feedback num: ' + str(self.feedback_idx))

            print('drops_per_sec: ', self.drops_per_sec,
                  len(self.drops_per_sec))
            res = pd.DataFrame({
                'packet_id_history': self.packid_save,
                'packets_per_sec': self.packs_per_sec,
                'drop_id_history': self.dropid_save,
                'drops_per_sec': self.drops_per_sec
            })
            res.to_csv(
                ('data_save/Recv_ttl' + '_' +
                 time.asctime().replace(' ', '_').replace(':', '_') + '.csv'),
                mode='a')

            # print('avgs_decode_time: ', float(sum(self.decode_time)/len(self.decode_time)))
            # print('max_decode_time:', max(self.decode_time))
            # print('min_decode_time:', min(self.decode_time))

        # 反馈
        n1 = self.glass.w1_done_dropid
        n2 = 30
        if self.glass.is_w1_done(0.6) and self.recv_done_flag == False:
            if (self.drop_id - n1) % n2 == 0:
                process = self.glass.getProcess()
                # 用于添加反馈历史数据, 用于droplet参数,正确译码
                self.chunk_process = process[0]
                self.glass.glass_process_history.append(self.chunk_process)
                # 用于实际反馈
                process_bitmap = process[1]
                # self.process_bytes = self.bit2hex(process_bitmap) # 实际水声通信机反馈可能需要用到这个
                # print(process_bitmap)
                # print(self.process_bytes)
                process_bits = bitarray.bitarray(process_bitmap)
                self.process_bytes = process_bits.tobytes()

                # 串口模拟水声延迟进行反馈
                t = threading.Timer(1.8, self.send_feedback)
                t.start()

                # 实际写入水声通信机进行反馈
                # self.send_feedback()
                print("Feedback chunks: ", self.chunk_process)
                print("Feedback chunks num: ", len(self.chunk_process))
                print("Feedback idx: ", self.feedback_idx)
                self.feedback_idx += 1
                self.feedback_ack_flag = True
                self.feedback_send_done = True

    # 定时器线程每隔1s记录发包数,即吞吐量
    def save_throughout_put(self):
        if (self.recv_done_flag == False):
            self.packid_save.append(self.packet_id)
            self.dropid_save.append(self.drop_id)
            self.valid_dropid_save.append(self.real_drop_id)
            self.creat_ttl_Timer()

    def creat_ttl_Timer(self):
        if (self.recv_done_flag == False):
            t = threading.Timer(1, self.save_throughout_put)
            t.start()

    # 计算吞吐量
    def cal_ttl(self):
        idx = 0
        while idx < len(self.packid_save):
            if idx == 0:
                self.packs_per_sec.append(self.packid_save[0])
            else:
                self.packs_per_sec.append(self.packid_save[idx] -
                                          self.packid_save[idx - 1])
            idx += 1
        idx = 0
        while idx < len(self.dropid_save):
            if idx == 0:
                self.drops_per_sec.append(self.dropid_save[0])
            else:
                self.drops_per_sec.append(self.dropid_save[idx] -
                                          self.dropid_save[idx - 1])
            idx += 1

    # 串口发送译码完成ack
    def send_recv_done_ack(self):
        if self.recv_done_flag:
            # M = b'M\r\n'
            # self.port.write(M)
            # self.port.flushOutput()
            # time.sleep(0.01)

            ack = b'#$\r\n'
            acksend = bytearray(ack)
            self.port.write(acksend)
            self.port.flushOutput()

    # 串口反馈译码进度
    def send_feedback(self):
        fb = b'$#' + self.process_bytes + b'\r\n'
        # M = b'M\r\n'
        # self.port.write(M)
        # self.port.flushOutput()
        # time.sleep(0.01)
        self.port.write(fb)
        self.port.flushOutput()

    def bit2hex(self, bit_array_source):
        bit_array = bit_array_source
        a = len(bit_array) % 4
        for j in range(0, a):
            bit_array.append(0)
        result = b''
        i = 0
        while i < len(bit_array):
            bit4 = bit_array[i:i + 4]
            if (bit4 == [0, 0, 0, 0]):
                result += b'0'
            elif (bit4 == [0, 0, 0, 1]):
                result += b'1'
            elif (bit4 == [0, 0, 1, 0]):
                result += b'2'
            elif (bit4 == [0, 0, 1, 1]):
                result += b'3'
            elif (bit4 == [0, 1, 0, 0]):
                result += b'4'
            elif (bit4 == [0, 1, 0, 1]):
                result += b'5'
            elif (bit4 == [0, 1, 1, 0]):
                result += b'6'
            elif (bit4 == [0, 1, 1, 1]):
                result += b'7'
            elif (bit4 == [1, 0, 0, 0]):
                result += b'8'
            elif (bit4 == [1, 0, 0, 1]):
                result += b'9'
            elif (bit4 == [1, 0, 1, 0]):
                result += b'a'
            elif (bit4 == [1, 0, 1, 1]):
                result += b'b'
            elif (bit4 == [1, 1, 0, 0]):
                result += b'c'
            elif (bit4 == [1, 1, 0, 1]):
                result += b'd'
            elif (bit4 == [1, 1, 1, 0]):
                result += b'e'
            elif (bit4 == [1, 1, 1, 1]):
                result += b'f'
            i += 4
        return result
Esempio n. 3
0
class Receiver:
    def __init__(self, bus, device):
        self.spiRecv = spidev.SpiDev()
        self.spiRecv.open(bus, device)
        self.spiRecv.max_speed_hz = 6250000  #976000
        self.spiRecv.mode = 0b00

        self.spiSend = spidev.SpiDev()
        self.spiSend.open(bus, 0)
        self.spiSend.max_speed_hz = 6250000  #976000
        self.spiSend.mode = 0b00

        # spi初始化
        spi_init()

        self.packet_id = 0
        self.drop_id = 0
        self.real_drop_id = 0  # dropid除去度函数切换和进度包更新时收到的drop
        self.glass = Glass(0)
        self.chunk_size = 0
        self.recv_done_flag = False
        self.feedback_ack_flag = False  # 用于度函数切换过程不接收
        self.feedback_send_done = False
        self.chunk_process = []
        self.feedback_idx = 0
        self.data_rec = ""
        self.recv_dir = os.path.join(
            RECV_PATH,
            time.asctime().replace(' ', '_').replace(':', '_'))

        self.packid_save = []
        self.dropid_save = []
        self.valid_dropid_save = []
        self.packs_per_sec = []
        self.drops_per_sec = []
        self.valid_drops_per_sec = []
        self.ttl_timer_start = False
        self.t0 = 0
        self.t1 = 0
        self.process_bytes = b''

    '''LT喷泉码接收解码部分'''

    def begin_to_catch(self):
        a_drop_bytes = self.catch_a_drop_spi()  # bytes

        if a_drop_bytes is not None:
            # 从收到第一个包之后,开启定时记录吞吐量线程,记录时间
            if self.ttl_timer_start == False:
                self.t0 = time.time()
                self.creat_ttl_Timer()
                self.ttl_timer_start = True

            self.packet_id += 1
            print("Recv Packet id : ", self.packet_id)
            if len(a_drop_bytes) > 0:
                check_data = recv_check(a_drop_bytes)

                if not check_data == None:
                    self.drop_byte_size = len(check_data)
                    self.add_a_drop(check_data)  # bytes --- drop --- bits

    def catch_a_drop_spi(self):
        if GPIO.input(25):
            spi_recv = self.spiRecv.readbytes(239)
            rec_bytes = bytes(spi_recv)
            frame_len = len(rec_bytes)
            print("framelen: ", frame_len)

            if (frame_len > 1):
                while (rec_bytes[frame_len - 1] == 0 and frame_len >= 1):
                    frame_len = frame_len - 1
            rec_bytes = rec_bytes[:frame_len]
            print("droplen: ", frame_len)

            self.data_rec = rec_bytes
            if self.data_rec[0:2] == b'##' and self.data_rec[
                    frame_len - 2:frame_len] == b'$$':
                data_array = bytearray(self.data_rec)
                data_array.pop(0)
                data_array.pop(0)
                data_array.pop()
                data_array.pop()
                return bytes(data_array)
            else:
                print(self.data_rec)
                print('Wrong receive frame !')

    def add_a_drop(self, d_byte):
        self.drop_id += 1
        print("====================")
        print("Recv dropid : ", self.drop_id)
        print("====================")

        drop = self.glass.droplet_from_Bytes(d_byte)  # drop
        if self.glass.num_chunks == 0:
            print('init num_chunks : ', drop.num_chunks)
            self.glass = Glass(drop.num_chunks)  # 初始化接收glass
            self.chunk_size = len(drop.data)

        #若为ew,则需要将drop转为ewdrop,两个drop里译码方式不一样
        ew_drop = EW_Droplet(drop.data, drop.seed, drop.num_chunks,
                             drop.process, drop.func_id, drop.feedback_idx)
        print('drop data len: ', len(drop.data))

        self.glass.addDroplet(ew_drop)
        # 这里解决度函数切换过程不接收,解决进度包更新过程不接收
        # if (self.feedback_ack_flag==False) or (self.feedback_ack_flag==True and ew_drop.func_id==1 and ew_drop.feedback_idx==self.feedback_idx-1):
        #     self.real_drop_id += 1
        #     self.glass.addDroplet(ew_drop)         # glass add drops
        #     print("=============================================================")
        #     print("除去度函数切换和进度包更新时收到的drop,Real_Recv_dropid : ", self.real_drop_id)
        #     print("=============================================================")
        #     self.feedback_send_done=False

        logging.info('=============================')
        logging.info('Decode Progress: ' + str(self.glass.chunksDone()) + '/' +
                     str(self.glass.num_chunks))
        logging.info('=============================')

        # 接收完成
        if self.glass.isDone():
            self.recv_done_flag = True
            self.t1 = time.time()
            logging.info('============Recv done===========')
            logging.info("VLC Feedback Fountain time elapsed:" +
                         str(self.t1 - self.t0))
            # 接收完成写入图像
            img_data = self.glass.get_bits()
            os.mkdir(self.recv_dir)
            with open(os.path.join(self.recv_dir, "img_recv" + ".bmp"),
                      'wb') as f:
                f.write(img_data)

            self.send_recv_done_ack()  # 接收完成返回ack

            # 记录吞吐量
            self.cal_ttl()
            print('packet_id history: ', self.packid_save,
                  len(self.packid_save))
            print('packets per sec: ', self.packs_per_sec,
                  len(self.packs_per_sec))
            print('drop_id history: ', self.dropid_save, len(self.dropid_save))
            print('drops per sec: ', self.drops_per_sec,
                  len(self.drops_per_sec))
            res = pd.DataFrame({
                'packet_id_history': self.packid_save,
                'packets_per_sec': self.packs_per_sec,
                'drop_id_history': self.dropid_save,
                'drops_per_sec': self.drops_per_sec
            })
            res.to_csv(
                ('data_save/Recv_ttl' + '_' +
                 time.asctime().replace(' ', '_').replace(':', '_') + '.csv'),
                mode='a')

        # 反馈
        n1 = round(0.8 * self.glass.num_chunks)
        n2 = 30
        if self.drop_id >= n1 and self.recv_done_flag == False:
            if (self.drop_id - n1) % n2 == 0:
                process = self.glass.getProcess()
                # 用于添加反馈历史数据, 用于droplet参数,正确译码
                self.chunk_process = process[0]
                self.glass.glass_process_history.append(self.chunk_process)
                # 用于实际反馈
                process_bitmap = process[1]
                process_bits = bitarray.bitarray(process_bitmap)
                self.process_bytes = process_bits.tobytes()

                self.send_feedback()
                print("Feedback chunks: ", self.chunk_process)
                print("Feedback chunks num: ", len(self.chunk_process))
                print("Feedback idx: ", self.feedback_idx)
                self.feedback_idx += 1
                self.feedback_ack_flag = True
                self.feedback_send_done = True

        # if self.real_drop_id >= self.glass.num_chunks:
        #     if (self.real_drop_id - self.glass.num_chunks)%10==0 and self.feedback_send_done==False:
        #         self.chunk_process = self.glass.getProcess() # 用于返回进程包
        #         self.glass.glass_process_history.append(self.chunk_process) # 添加反馈历史数据,用于droplet参数,正确译码
        #         self.send_feedback()
        #         print("Feedback chunks: ", self.chunk_process)
        #         print("Feedback chunks num: ", len(self.chunk_process))
        #         print("Feedback idx: ", self.feedback_idx)
        #         self.feedback_idx += 1
        #         self.feedback_ack_flag = True
        #         self.feedback_send_done=True

    # 定时器线程每隔1s记录发包数,即吞吐量
    def save_throughout_put(self):
        if (self.recv_done_flag == False):
            self.packid_save.append(self.packet_id)
            self.dropid_save.append(self.drop_id)
            self.valid_dropid_save.append(self.real_drop_id)
            self.creat_ttl_Timer()

    def creat_ttl_Timer(self):
        if (self.recv_done_flag == False):
            t = threading.Timer(1, self.save_throughout_put)
            t.start()

    # 计算吞吐量
    def cal_ttl(self):
        idx = 0
        while idx < len(self.packid_save):
            if idx == 0:
                self.packs_per_sec.append(self.packid_save[0])
            else:
                self.packs_per_sec.append(self.packid_save[idx] -
                                          self.packid_save[idx - 1])
            idx += 1
        idx = 0
        while idx < len(self.dropid_save):
            if idx == 0:
                self.drops_per_sec.append(self.dropid_save[0])
            else:
                self.drops_per_sec.append(self.dropid_save[idx] -
                                          self.dropid_save[idx - 1])
            idx += 1

    def send_recv_done_ack(self):
        if self.recv_done_flag:
            ack = b'#$'
            acklen = len(ack)
            acksend = bytearray(ack)
            while (acklen < 239):
                acksend.insert(acklen, 0)
                acklen += 1
            self.spiSend.xfer2(acksend)
            logging.info('Send fountain ACK done')
            logging.info('Recv Packets: : ' + str(self.packet_id))
            logging.info('Recv drops: ' + str(self.drop_id))

    def send_feedback(self):
        fb = b'$#'
        for chunk_id in self.chunk_process:
            chunk_id_bits = format(int(chunk_id), "016b")
            fb += bitarray.bitarray(chunk_id_bits).tobytes()

        fblen = len(fb)
        fbsend = bytearray(fb)
        while (fblen < 239):
            fbsend.insert(fblen, 0)
            fblen += 1
        self.spiSend.xfer2(fbsend)
class NodeRLY:
    def __init__(self, portSNC, baudrateSNC, timeoutSNC, portROV, baudrateROV,
                 timeoutROV):
        self.ID = 1  #本节点id
        self.routing_table = [0, 1, 2]  #路由
        self.snc_handshake_done = False
        self.vlc_handshake_done = False
        self.vlc_handshake_timedout = False

        # 初始化spi发送
        self.spiSend = spidev.SpiDev()
        self.spiSend.open(0, 0)  # bus device
        self.spiSend.max_speed_hz = 6250000  #976000
        self.spiSend.mode = 0b00

        # 初始化spi接收
        self.spiRecv = spidev.SpiDev()
        self.spiRecv.open(0, 1)
        self.spiRecv.max_speed_hz = 6250000  #976000
        self.spiRecv.mode = 0b00

        # 初始化光通信spi、水声串口、ROV串口
        spi_init()
        self.acoustic = Acoustic(portSNC, baudrateSNC, timeoutSNC)
        self.rov = ROV(portROV, baudrateROV, timeoutROV)

        # 自动对准
        self.align_rov_state = 0  #记录上次循环的转向状态,0为左,1为右

        # 喷泉码接收
        self.rx_packet_id = 0
        self.rx_drop_id = 0
        self.glass = Glass(0)
        self.chunk_size = 0
        self.recv_done_flag = False
        self.feedback_ack_flag = False  # 用于度函数切换过程不接收
        self.chunk_process = []
        self.feedback_idx = 0
        self.data_rec = ""
        self.recv_dir = os.path.join(
            RECV_PATH,
            time.asctime().replace(' ', '_').replace(':', '_'))

        self.rx_packid_save = []
        self.rx_dropid_save = []
        self.rx_packs_per_sec = []
        self.rx_drops_per_sec = []
        self.rx_ttl_timer_start = False
        self.t0 = 0
        self.t1 = 0
        self.process_bytes = b''
        self.img_recv_bits = bitarray.bitarray(endian='big')
        self.decode_time = []

        # 喷泉码中继发送
        self.tx_drop_id = 0
        self.recvdone_ack = False
        self.chunk_process = []
        self.feedback_num = 0
        self.tx_dropid_save = []

    '''LT喷泉码接收解码部分'''

    def begin_to_catch(self):
        a_drop_bytes = self.catch_a_drop_spi()  # bytes

        if a_drop_bytes is not b'':
            decode_t0 = time.time()
            # 从收到第一个包之后,开启定时记录吞吐量线程,记录时间
            if self.rx_ttl_timer_start == False:
                self.t0 = time.time()
                self.creat_ttl_Timer()
                self.rx_ttl_timer_start = True

            self.rx_packet_id += 1
            print("Recv Packet id : ", self.rx_packet_id)
            if len(a_drop_bytes) > 0:
                check_data = recv_check(a_drop_bytes)
                if not check_data == b'':
                    self.add_a_drop(check_data)  # bytes --- drop --- bits

    def catch_a_drop_spi(self):
        if GPIO.input(25):
            spi_recv = self.spiRecv.readbytes(239)
            rec_bytes = bytes(spi_recv)
            frame_len = len(rec_bytes)
            print("framelen: ", frame_len)

            if (frame_len > 1):
                while (rec_bytes[frame_len - 1] == 0 and frame_len >= 1):
                    frame_len = frame_len - 1
            rec_bytes = rec_bytes[:frame_len]
            print("droplen: ", frame_len)

            self.data_rec = rec_bytes
            if self.data_rec[0:2] == b'##' and self.data_rec[
                    frame_len - 2:frame_len] == b'$$':
                data_array = bytearray(self.data_rec)
                data_array.pop(0)
                data_array.pop(0)
                data_array.pop()
                data_array.pop()
                return bytes(data_array)
            else:
                print(self.data_rec)
                print('Wrong receive frame !')
                return b''
        else:
            return b''

    def add_a_drop(self, d_byte):
        self.rx_drop_id += 1
        print("====================")
        print("Recv dropid : ", self.rx_drop_id)
        print("====================")

        print('glass_process_history len: ',
              len(self.glass.glass_process_history))
        drop = self.glass.droplet_from_Bytes(d_byte)  # drop
        if self.glass.num_chunks == 0:
            print('init num_chunks : ', drop.num_chunks)
            self.glass = Glass(drop.num_chunks)  # 初始化接收glass
            self.chunk_size = len(drop.data)

        #若为ew,则需要将drop转为ewdrop,两个drop里译码方式不一样
        ew_drop = EW_Droplet(drop.data, drop.seed, drop.num_chunks,
                             drop.process, drop.func_id, drop.feedback_idx)
        print('drop data len: ', len(drop.data))

        self.glass.addDroplet(ew_drop)

        logging.info('=============================')
        logging.info('Decode Progress: ' + str(self.glass.chunksDone()) + '/' +
                     str(self.glass.num_chunks))
        logging.info('=============================')

        # 接收完成
        if self.glass.isDone():
            self.recv_done_flag = True
            self.t1 = time.time()
            logging.info('============Recv done===========')
            logging.info("Fountain time elapsed:" + str(self.t1 - self.t0))
            # 接收完成写入图像
            img_data = self.glass.get_bits()
            self.img_recv_bits = img_data
            os.mkdir(self.recv_dir)
            with open(os.path.join(self.recv_dir, "img_recv" + ".bmp"),
                      'wb') as f:
                f.write(img_data)

            self.send_recv_done_ack()  # 接收完成返回ack

            # 记录吞吐量
            self.rx_cal_ttl()
            logging.info('Recv drops: ' + str(self.rx_drop_id))
            logging.info('Feedback num: ' + str(self.feedback_idx))

            print('drops_per_sec: ', self.rx_drops_per_sec,
                  len(self.rx_drops_per_sec))
            res = pd.DataFrame({
                'packet_id_history': self.rx_packid_save,
                'packets_per_sec': self.rx_packs_per_sec,
                'drop_id_history': self.rx_dropid_save,
                'drops_per_sec': self.rx_drops_per_sec
            })
            res.to_csv(
                ('data_save/Recv_ttl' + '_' +
                 time.asctime().replace(' ', '_').replace(':', '_') + '.csv'),
                mode='a')

        # 反馈
        n1 = self.glass.w1_done_dropid
        n2 = 30
        if self.glass.is_w1_done(0.6) and self.recv_done_flag == False:
            if (self.rx_drop_id - n1) % n2 == 0:
                process = self.glass.getProcess()
                # 用于添加反馈历史数据, 用于droplet参数,正确译码
                self.chunk_process = process[0]
                self.glass.glass_process_history.append(self.chunk_process)
                # 用于实际反馈
                process_bitmap = process[1]
                # self.process_bytes = self.bit2hex(process_bitmap)
                # print(process_bitmap)
                # print(self.process_bytes)
                process_bits = bitarray.bitarray(process_bitmap)
                self.process_bytes = process_bits.tobytes()

                self.send_feedback()
                print("Feedback chunks: ", self.chunk_process)
                print("Feedback chunks num: ", len(self.chunk_process))
                print("Feedback idx: ", self.feedback_idx)
                self.feedback_idx += 1
                self.feedback_ack_flag = True

    # 定时器线程每隔1s记录收包数,即接收吞吐量
    def rx_save_throughput(self):
        if (self.recv_done_flag == False):
            self.rx_packid_save.append(self.rx_packet_id)
            self.rx_dropid_save.append(self.rx_drop_id)
            self.rx_creat_ttl_Timer()

    def rx_creat_ttl_Timer(self):
        if (self.recv_done_flag == False):
            t = threading.Timer(1, self.rx_save_throughput)
            t.start()

    # 计算接收瞬时吞吐量
    def rx_cal_ttl(self):
        idx = 0
        while idx < len(self.rx_packid_save):
            if idx == 0:
                self.rx_packs_per_sec.append(self.rx_packid_save[0])
            else:
                self.rx_packs_per_sec.append(self.rx_packid_save[idx] -
                                             self.rx_packid_save[idx - 1])
            idx += 1
        idx = 0
        while idx < len(self.rx_dropid_save):
            if idx == 0:
                self.rx_drops_per_sec.append(self.rx_dropid_save[0])
            else:
                self.rx_drops_per_sec.append(self.rx_dropid_save[idx] -
                                             self.rx_dropid_save[idx - 1])
            idx += 1

    def send_recv_done_ack(self):
        if self.recv_done_flag:
            self.acoustic.mfsk_tx_config()
            self.acoustic.mfsk_tx(b'#$\r\n')
            self.port.flushOutput()

    def send_feedback(self):
        self.acoustic.mfsk_tx_config()
        fb = b'$#' + self.process_bytes + b'\r\n'
        self.acoustic.mfsk_tx(fb)
        self.port.flushOutput()

    '''基于水声辅助的光通信链路建立部分'''

    # 等待水声握手,并反馈水声握手ACK
    def wait_acoustic_handshake(self):
        print("===...waiting for connection...===")
        self.acoustic.mfsk_rx_config()
        while True:
            str_recv = self.acoustic.mfsk_rx()
            # 收到水声握手包
            if str_recv[0:2] == "##":
                var = self.acoustic.acoustic_handshake_pack_res(str_recv[2:])
                # 反馈水声握手ACK
                if var[0] == self.ID:
                    self.acoustic.mfsk_tx_config()
                    self.acoustic.mfsk_tx(b'##snc')
                    self.snc_handshake_done = True
                    break

    # 下面3个是ROV可见光通信自动对准程序
    def rov_vlc_align(self):
        last_state = 1  #右转
        start = time.time()
        align_flag = False  #对准标志位
        print("===Start to align===")
        while not align_flag:
            align_flag = self.align_stability_test()
            if not align_flag:
                if self.align_rov_state == 0:
                    if (last_state == 1):
                        print("===Rov turn right===")
                        self.rov.port.write(b'#8868$')  #右转
                else:
                    if (last_state == 0):
                        print("===Rov turn left===")
                        self.rov.port.write(b'#88a8$')  #左转
            last_state = self.align_rov_state
            #对准成功
            if align_flag:
                print('===Align Successed!===')
                print("===Sending VLC handshake ACK===")
                self.vlc_handshake_done = True
                self.acoustic.mfsk_tx_config()
                self.acoustic.mfsk_tx(b'$$vlc')
                break
            #超过30s,对准失败
            elif (time.time() - start) > 30:
                print('===Align Failed!===')
                self.vlc_handshake_done = False
                break

    # 稳定性测试
    def align_stability_test(self):
        if self.align_vlc_rx():  #当前时刻能接收光信号
            if self.align_rov_state == 0:  #记录旋转状态,如因惯性过转,下次反转
                self.align_rov_state = 1
            else:
                self.align_rov_state = 0
            print("===Rov stop===")
            self.rov.port.write(b'#0000$')  #rov停转
            time.sleep(1)
            #静止后仍然能接收光信号
            print("===stop and receive===")
            rnum = 0
            start = time.time()
            while True:
                if (self.align_vlc_rx()):
                    rnum += 1
                    print("rnum = ", rnum)
                if (time.time() - start) > 3:
                    if (rnum > 24):
                        return True
                    else:
                        return False
        else:
            return False

    # 接收可见光通信探测包并检测
    def align_vlc_rx(self):
        a_drop_bytes = self.catch_a_drop_spi()
        if a_drop_bytes is not b'':
            check_data = recv_check(a_drop_bytes)
            if check_data is not b'':
                print("Receive a valid packet!!!")
                return True
            else:
                return False
        else:
            return False

    '''中继转发部分'''

    def relay_forward(self):
        # 状态重新初始化
        self.align_rov_state = 0
        self.vlc_handshake_done = False
        self.snc_handshake_done = False
        self.vlc_handshake_timedout = False
        # 中继转发
        print(
            '========================================================================'
        )
        print(
            'INFO: srcID=0, selfID=1, desID=2, starting Relay Forwarding procedure...'
        )
        self.send_acoustic_handshake()
        self.wait_acoustic_handshake_ACK()
        if self.snc_handshake_done == True:
            self.rov_vlc_align()
            if self.vlc_handshake_done:
                time.sleep(3)
                print("===Starting data relay forwarding transfer===")
                self.fountain = EW_Fountain(self.img_recv_bits, chunk_size=215)
                self.send_drops_spi()
        else:
            print(
                "Acoustic handshake failed, Relay Forwarding Transmission Terminated !!!"
            )

    # 寻找下一跳节点id
    def find_next_id(self):
        idx = 0
        next_id = 0
        for id in self.routing_table:
            if id == self.ID:
                next_id = self.routing_table[idx + 1]
                return next_id
            idx += 1
        return None

    # 发送水声握手包
    def send_acoustic_handshake(self):
        id = self.find_next_id()
        if id is not None:
            nextid = format(int(id), "08b")
            srcid = format(int(0), "08b")
            desid = format(int(2), "08b")
            w1size = format(int(6), "08b")
            imgW = format(int(256), "016b")
            imgH = format(int(256), "016b")
            SPIHTlen = format(int(len(self.m)), "032b")
            level = format(int(3), "08b")
            wavelet = format(int(1), "08b")  # 1代表bior4.4
            mode = format(int(1), "08b")  # 1代表periodization
            acoustic_handshake = b'##' + bitarray.bitarray(
                nextid + srcid + desid + w1size + imgW + imgH + SPIHTlen +
                level + wavelet + mode).tobytes()
            # 切换成发送模式,发送水声握手包
            print('===Send desID=2 acoustic broadcast handshake===')
            self.acoustic.mfsk_tx_config()
            self.acoustic.mfsk_tx(acoustic_handshake)
        else:
            print("nextID Not Found !!!")

    # 等待水声握手包的ACK
    def wait_acoustic_handshake_ACK(self):
        self.acoustic.mfsk_rx_config()
        snc_shake_time0 = time.time()
        snc_shake_retry_times = 0
        while True:
            if self.acoustic.mfsk_rx() == "##snc":
                print(
                    '===Acoustic handshake ACK received, Acoustic handshake done!==='
                )
                self.snc_handshake_done = True
                break
            if (time.time() - snc_shake_time0) % 6 == 0:
                self.send_acoustic_handshake()
                snc_shake_retry_times += 1
                if snc_shake_retry_times >= 3:
                    break

    # 中继转发喷泉码
    def a_drop(self):
        return self.fountain.droplet().toBytes()

    def send_drops_spi(self):
        # 启动反馈检测、吞吐量计算线程
        self.tx_creat_ttl_Timer()
        self.creat_detect_feedback_Timer()
        # 主线程
        while True:
            self.tx_drop_id += 1
            # 发送一帧补0到239字节
            sendbytes = send_check(self.a_drop())
            sendbytearray = bytearray(sendbytes)
            datalen = len(sendbytearray)
            while (datalen < 239):
                sendbytearray.insert(datalen, 0)
                datalen += 1

            self.spiSend.xfer2(sendbytearray)
            print("====================")
            print("Relay Forward Send dropid: ", self.tx_drop_id)
            print("====================")
            print("FountainFrameLen: ", len(self.a_drop()))
            print("SendFrameLen: ", len(sendbytearray))

            if (self.recvdone_ack):
                logging.info('============Fountain Send done===========')
                logging.info('Send drops used: ' + str(self.tx_drop_id))
                logging.info('Feedback num: ' + str(self.feedback_num))

                # 记录吞吐量
                throughput_list = cal_ttl(self.tx_dropid_save)
                # print('dropid history: ', self.dropid_save, len(self.dropid_save))
                print('drops_per_sec: ', throughput_list, len(throughput_list))
                res = pd.DataFrame({
                    'dropid_history': self.tx_dropid_save,
                    'drops_per_sec': throughput_list
                })
                res.to_csv(
                    ('data_save/Send_ttl' + '_' +
                     time.asctime().replace(' ', '_').replace(':', '_') +
                     '.csv'),
                    mode='a')
                break

            time.sleep(0.09)  #发包间隔

    # 定时器线程每隔1s记录发包数,即吞吐量
    def tx_save_throughput(self):
        if (self.recvdone_ack == False):
            self.tx_dropid_save.append(self.tx_drop_id)
            self.tx_creat_ttl_Timer()

    def tx_creat_ttl_Timer(self):
        if (self.recvdone_ack == False):
            t = threading.Timer(1, self.tx_save_throughput)
            t.start()

    # 检测反馈
    def feedback_detect(self):
        data_rec = self.acoustic.modem_feedback()
        if data_rec is not b'':
            data_str = str(data_rec, encoding="utf8")
            idx = data_str.find('Received String: ')
            if idx >= 0:
                msg_str = data_str[idx + 17:]
                msg_bytes = bytes(msg_str, encoding="utf-8")

                # msg_bytes = data_rec
                # 接收完成
                if msg_bytes[:2] == b'#$':
                    self.recvdone_ack = True
                # 进度包
                elif msg_bytes[:2] == b'$#':
                    self.fountain.all_at_once = True
                    process_recv = self.get_process_from_feedback(msg_bytes)
                    if process_recv == []:
                        self.recvdone_ack = True
                    else:
                        self.chunk_process = process_recv
                        self.fountain.chunk_process = self.chunk_process
                        self.fountain.feedback_idx = self.feedback_num
                        self.feedback_num += 1
        self.creat_detect_feedback_Timer()

    def creat_detect_feedback_Timer(self):
        if self.recvdone_ack == False:
            t = threading.Timer(0.001, self.feedback_detect)
            t.start()

    # 从反馈中获取进度
    def get_process_from_feedback(self, rec_bytes):
        process = []
        chunk_id = 0
        process_bits = bitarray.bitarray(endian='big')
        process_bits.frombytes(rec_bytes[2:])
        while chunk_id < self.fountain.num_chunks:
            if (process_bits[chunk_id] == False):
                process.append(chunk_id)
            chunk_id += 1
        return process
class Receiver:
    def __init__(
        self,
        bus,
        device,
        port,
        baudrate,
        timeout,
    ):
        self.spiRecv = spidev.SpiDev()
        self.spiRecv.open(bus, device)
        self.spiRecv.max_speed_hz = 6250000  #976000
        self.spiRecv.mode = 0b00

        self.spiSend = spidev.SpiDev()
        self.spiSend.open(bus, 0)
        self.spiSend.max_speed_hz = 6250000  #976000
        self.spiSend.mode = 0b00

        # 串口和spi初始化
        spi_init()
        self.port = serial.Serial(port, baudrate)

        self.pack_id = 0
        self.drop_id = 0
        self.glass = Glass(0)
        self.chunk_size = 0
        self.recv_done_flag = False
        self.feedback_ack_flag = False
        self.chunk_process = []
        self.feedback_idx = 0
        self.data_rec = ""
        self.recv_dir = os.path.join(
            RECV_PATH,
            time.asctime().replace(' ', '_').replace(':', '_'))
        self.t0 = 0
        self.t0_start = False
        self.t1 = 0
        self.process_bytes = b''

    '''LT喷泉码接收解码部分'''

    def begin_to_catch(self):
        a_drop_bytes = self.catch_a_drop_spi()  # bytes

        if a_drop_bytes is not None:
            # 从接收到第一个包开始计时
            if self.t0_start == False:
                self.t0 = time.time()
                self.t0_start = True
            self.pack_id += 1
            print("Recv Packet_id : ", self.pack_id)

            if len(a_drop_bytes) > 0:
                check_data = recv_check(a_drop_bytes)

                if not check_data == None:
                    self.drop_byte_size = len(check_data)
                    self.add_a_drop(check_data)

    def catch_a_drop_spi(self):
        if GPIO.input(25):
            spi_recv = self.spiRecv.readbytes(239)
            rec_bytes = bytes(spi_recv)
            frame_len = len(rec_bytes)

            if (frame_len > 1):
                while (rec_bytes[frame_len - 1] == 0 and frame_len >= 1):
                    frame_len = frame_len - 1
            rec_bytes = rec_bytes[:frame_len]

            self.data_rec = rec_bytes
            if self.data_rec[0:2] == b'##' and self.data_rec[
                    frame_len - 2:frame_len] == b'$$':
                data_array = bytearray(self.data_rec)
                data_array.pop(0)
                data_array.pop(0)
                data_array.pop()
                data_array.pop()
                return bytes(data_array)
            else:
                print(self.data_rec)
                print('Wrong receive frame !')

    def add_a_drop(self, d_byte):
        self.drop_id += 1
        print("====================")
        print("Recv drop_id : ", self.drop_id)
        print("====================")
        drop = self.glass.droplet_from_Bytes(d_byte)  # drop
        if self.glass.num_chunks == 0:
            self.glass = Glass(drop.num_chunks)  # 初始化接收glass
            self.chunk_size = len(drop.data)

        #若为ew,则需要将drop转为ewdrop,两个drop里译码方式不一样
        ew_drop = EW_Droplet(drop.data, drop.seed, drop.num_chunks,
                             drop.process, drop.func_id, drop.feedback_idx)
        self.glass.addDroplet(ew_drop)  # glass add drops

        logging.info('=============================')
        logging.info('Decode Progress: ' + str(self.glass.chunksDone()) + '/' +
                     str(self.glass.num_chunks))
        logging.info('=============================')

        # 接收完成
        if self.glass.isDone():
            self.t1 = time.time()
            self.recv_done_flag = True
            # # 接收完成写入图像
            # img_data = self.glass.get_bits()
            # os.mkdir(self.recv_dir)
            # with open(os.path.join(self.recv_dir, "img_recv" + ".bmp"), 'wb') as f:
            #     f.write(img_data)

            t1 = threading.Timer(2.6, self.send_recv_done_ack)
            t1.start()
            # self.send_recv_done_ack() # 接收完成返回ack
            logging.info('============Recv done===========')
            logging.info('Send Sonic Fountain ACK done')
            logging.info('Recv packets: ' + str(self.drop_id))
            logging.info('Recv drops: ' + str(self.drop_id))
            logging.info("Sonic Feedback Fountain time elapsed:" +
                         str(self.t1 - self.t0))

        # 接收到K个数据包之后
        n1 = round(0.8 * self.glass.num_chunks)
        n2 = 30
        if self.drop_id >= n1 and self.recv_done_flag == False:
            if (self.drop_id - n1) % n2 == 0:
                process = self.glass.getProcess()
                # 用于添加反馈历史数据, 用于droplet参数,正确译码
                self.chunk_process = process[0]
                self.glass.glass_process_history.append(self.chunk_process)
                # 用于实际反馈
                process_bitmap = process[1]
                process_bits = bitarray.bitarray(process_bitmap)
                self.process_bytes = process_bits.tobytes()

                t = threading.Timer(2.6, self.send_feedback)
                t.start()

                print(self.process_bytes)
                print("Feedback chunks: ", self.chunk_process)
                print("Feedback chunks num: ", len(self.chunk_process))
                print("Feedback idx: ", self.feedback_idx)
                self.feedback_idx += 1

    def send_recv_done_ack(self):
        if self.recv_done_flag:
            ack = b'#$\r\n'
            acksend = bytearray(ack)
            self.port.write(acksend)
            self.port.flushOutput()

    # 用定时器线程反馈仿真修改
    def send_feedback(self):
        fb = b'$#' + self.process_bytes
        self.port.write(fb)
        self.port.flushOutput()