diff --git a/openair2/LAYER2/MAC/defs.h b/openair2/LAYER2/MAC/defs.h index e2f2ba04cc6bbe303719646b1460508ff2d033f9..4d18dc46258563229df6f107fd27645dca40c2bb 100644 --- a/openair2/LAYER2/MAC/defs.h +++ b/openair2/LAYER2/MAC/defs.h @@ -446,14 +446,21 @@ typedef struct { uint32_t total_dlsch_bytes_tx; // uint32_t total_dlsch_pdus_tx; - + + // here for RX // uint32_t ulsch_bitrate; // uint32_t ulsch_bytes_rx; // - uint64_t ulsch_pdus_rx; - // here for RX + uint64_t ulsch_pdus_rx; + + uint32_t total_ulsch_bitrate; + // + uint32_t total_ulsch_bytes_rx; + // + uint32_t total_ulsch_pdus_rx; + } eNB_STATS; /*! \brief eNB statistics for the connected UEs*/ typedef struct { @@ -490,13 +497,7 @@ typedef struct { // total rb used for retransmission uint32_t total_rbs_used_retx; - /// preassigned mcs after rate adaptation - uint8_t ulsch_mcs1; - /// adjusted mcs - uint8_t ulsch_mcs2; - - - /// TX + /// TX /// Num pkt uint32_t num_pdu_tx[NB_RB_MAX]; /// num bytes @@ -528,6 +529,12 @@ typedef struct { // uint32_t avg_pdu_size; /// RX + + /// preassigned mcs after rate adaptation + uint8_t ulsch_mcs1; + /// adjusted mcs + uint8_t ulsch_mcs2; + /// estimated average pdu inter-departure time uint32_t avg_pdu_idt; /// estimated average pdu size @@ -536,6 +543,22 @@ typedef struct { uint32_t aggregated_pdu_size; uint32_t aggregated_pdu_arrival; + /// uplink transport block size + uint32_t ulsch_TBS; + + /// total rb used for a new uplink transmission + uint32_t num_retransmission_rx; + /// total rb used for a new uplink transmission + uint32_t rbs_used_rx; + /// total rb used for a new uplink retransmission + uint32_t rbs_used_retx_rx; + /// total rb used for a new uplink transmission + uint32_t total_rbs_used_rx; + /// normalized rx power + int32_t normalized_rx_power; + /// target rx power + int32_t target_rx_power; + /// num rx pdu uint32_t num_pdu_rx[NB_RB_MAX]; /// num bytes rx @@ -544,8 +567,19 @@ typedef struct { // uint32_t tti_goodput[NB_RB_MAX]; /// errors uint32_t num_errors_rx; + + uint64_t overhead_bytes_rx; + /// headers+ CE + padding bytes for a MAC PDU + uint64_t total_overhead_bytes_rx; + /// headers+ CE + padding bytes for a MAC PDU + uint64_t avg_overhead_bytes_rx; + // + uint32_t ulsch_bitrate; + //total + uint32_t total_ulsch_bitrate; /// overall - + /// MAC pdu bytes + uint64_t pdu_bytes_rx; /// total MAC pdu bytes uint64_t total_pdu_bytes_rx; /// total num pdu diff --git a/openair2/LAYER2/MAC/eNB_scheduler_ulsch.c b/openair2/LAYER2/MAC/eNB_scheduler_ulsch.c index f967dc4f49e3ede376e76f189556d769be2657f6..0eae8f16bb4dd4a5ac52bfc9ff958d6b67638677 100644 --- a/openair2/LAYER2/MAC/eNB_scheduler_ulsch.c +++ b/openair2/LAYER2/MAC/eNB_scheduler_ulsch.c @@ -104,9 +104,12 @@ void rx_sdu( } LOG_D(MAC,"[eNB %d] CC_id %d Received ULSCH sdu from PHY (rnti %x, UE_id %d), parsing header\n",enb_mod_idP,CC_idP,rntiP,UE_id); - + payload_ptr = parse_ulsch_header(sduP,&num_ce,&num_sdu,rx_ces,rx_lcids,rx_lengths,sdu_lenP); - + + eNB->eNB_stats[CC_idP].ulsch_bytes_rx=sdu_lenP; + eNB->eNB_stats[CC_idP].total_ulsch_bytes_rx+=sdu_lenP; + eNB->eNB_stats[CC_idP].total_ulsch_pdus_rx+=1; // control element for (i=0; i<num_ce; i++) { @@ -343,7 +346,6 @@ void rx_sdu( } payload_ptr+=rx_lengths[i]; - } /* NN--> FK: we could either check the payload, or use a phy helper to detect a false msg3 */ @@ -357,6 +359,7 @@ void rx_sdu( } } } else { + UE_list->eNB_UE_stats[CC_idP][UE_id].pdu_bytes_rx=sdu_lenP; UE_list->eNB_UE_stats[CC_idP][UE_id].total_pdu_bytes_rx+=sdu_lenP; UE_list->eNB_UE_stats[CC_idP][UE_id].total_num_pdus_rx+=1; } @@ -769,7 +772,9 @@ void schedule_ulsch_rnti(module_id_t module_idP, ndi = 1-UE_template->oldNDI_UL[harq_pid]; UE_template->oldNDI_UL[harq_pid]=ndi; - UE_list->eNB_UE_stats[CC_id][UE_id].ulsch_mcs2=UE_template->pre_assigned_mcs_ul; + UE_list->eNB_UE_stats[CC_id][UE_id].normalized_rx_power=normalized_rx_power; + UE_list->eNB_UE_stats[CC_id][UE_id].target_rx_power=target_rx_power; + UE_list->eNB_UE_stats[CC_id][UE_id].ulsch_mcs1=UE_template->pre_assigned_mcs_ul; mcs = cmin (UE_template->pre_assigned_mcs_ul, openair_daq_vars.target_ue_ul_mcs); // adjust, based on user-defined MCS if (UE_template->pre_allocated_rb_table_index_ul >=0) { @@ -788,6 +793,8 @@ void schedule_ulsch_rnti(module_id_t module_idP, } TBS = mac_xface->get_TBS_UL(mcs,rb_table[rb_table_index]); + UE_list->eNB_UE_stats[CC_id][UE_id].total_rbs_used_rx+=rb_table[rb_table_index]; + UE_list->eNB_UE_stats[CC_id][UE_id].ulsch_TBS=TBS; buffer_occupancy -= TBS; rballoc = mac_xface->computeRIV(frame_parms->N_RB_UL, first_rb[CC_id], @@ -817,17 +824,22 @@ void schedule_ulsch_rnti(module_id_t module_idP, } - LOG_D(MAC,"[eNB %d][PUSCH %d/%x] CC_id %d Frame %d subframeP %d Scheduled UE retransmission (mcs %d, first rb %d, nb_rb %d, TBS %d, harq_pid %d, round %d)\n", + LOG_D(MAC,"[eNB %d][PUSCH %d/%x] CC_id %d Frame %d subframeP %d Scheduled UE retransmission (mcs %d, first rb %d, nb_rb %d, harq_pid %d, round %d)\n", module_idP,UE_id,rnti,CC_id,frameP,subframeP,mcs, first_rb[CC_id],UE_template->nb_rb_ul[harq_pid], - TBS,//mac_xface->get_TBS_UL(mcs,UE_template->nb_rb_ul[harq_pid]), - harq_pid, round); + harq_pid, round); rballoc = mac_xface->computeRIV(frame_parms->N_RB_UL, first_rb[CC_id], UE_template->nb_rb_ul[harq_pid]); first_rb[CC_id]+=UE_template->nb_rb_ul[harq_pid]; // increment for next UE allocation - } + + UE_list->eNB_UE_stats[CC_id][UE_id].num_retransmission_rx+=1; + UE_list->eNB_UE_stats[CC_id][UE_id].rbs_used_retx_rx=UE_template->nb_rb_ul[harq_pid]; + UE_list->eNB_UE_stats[CC_id][UE_id].total_rbs_used_rx+=UE_template->nb_rb_ul[harq_pid]; + UE_list->eNB_UE_stats[CC_id][UE_id].ulsch_mcs1=mcs; + UE_list->eNB_UE_stats[CC_id][UE_id].ulsch_mcs2=mcs; + } // Cyclic shift for DM RS if(cooperation_flag == 2) { diff --git a/openair2/LAYER2/openair2_proc.c b/openair2/LAYER2/openair2_proc.c index 6d1570b029c905f5e7a9f5952bc35cafb7ef6d9d..523258b1a562a2df6f323defd2bf9f6ab04a60bc 100644 --- a/openair2/LAYER2/openair2_proc.c +++ b/openair2/LAYER2/openair2_proc.c @@ -102,23 +102,14 @@ int dump_eNB_l2_stats(char *buffer, int length) eNB->eNB_stats[CC_id].total_bcch_buffer, eNB->eNB_stats[CC_id].bcch_mcs); - len += sprintf(&buffer[len],"CCCH , NB_TX_MAC = %d, transmitted bytes (TTI %d, total %d) MCS (TTI %d)\n", - eNB->eNB_stats[CC_id].total_num_bcch_pdu, - eNB->eNB_stats[CC_id].bcch_buffer, - eNB->eNB_stats[CC_id].total_bcch_buffer, - eNB->eNB_stats[CC_id].bcch_mcs); - - len += sprintf(&buffer[len],"DCCH , NB_TX_MAC = %d, transmitted bytes (TTI %d, total %d) MCS (TTI %d)\n", - eNB->eNB_stats[CC_id].total_num_bcch_pdu, - eNB->eNB_stats[CC_id].bcch_buffer, - eNB->eNB_stats[CC_id].total_bcch_buffer, - eNB->eNB_stats[CC_id].bcch_mcs); - eNB->eNB_stats[CC_id].dlsch_bitrate=((eNB->eNB_stats[CC_id].dlsch_bytes_tx*8)/((eNB->frame + 1)*10)); eNB->eNB_stats[CC_id].total_dlsch_pdus_tx+=eNB->eNB_stats[CC_id].dlsch_pdus_tx; eNB->eNB_stats[CC_id].total_dlsch_bytes_tx+=eNB->eNB_stats[CC_id].dlsch_bytes_tx; eNB->eNB_stats[CC_id].total_dlsch_bitrate=((eNB->eNB_stats[CC_id].total_dlsch_bytes_tx*8)/((eNB->frame + 1)*10)); + eNB->eNB_stats[CC_id].ulsch_bitrate=((eNB->eNB_stats[CC_id].ulsch_bytes_rx*8)/((eNB->frame + 1)*10)); + eNB->eNB_stats[CC_id].total_ulsch_bitrate=((eNB->eNB_stats[CC_id].total_ulsch_bytes_rx*8)/((eNB->frame + 1)*10)); + len += sprintf(&buffer[len],"DLSCH bitrate (TTI %u, avg %u) kbps, Transmitted bytes (TTI %u, total %u), Transmitted PDU (TTI %u, total %u) \n", eNB->eNB_stats[CC_id].dlsch_bitrate, eNB->eNB_stats[CC_id].total_dlsch_bitrate, @@ -126,6 +117,14 @@ int dump_eNB_l2_stats(char *buffer, int length) eNB->eNB_stats[CC_id].total_dlsch_bytes_tx, eNB->eNB_stats[CC_id].dlsch_pdus_tx, eNB->eNB_stats[CC_id].total_dlsch_pdus_tx); + + len += sprintf(&buffer[len],"ULSCH bitrate (TTI %u, avg %u) kbps, Received bytes (TTI %u, total %u), Received PDU (TTI %u, total %lu) \n", + eNB->eNB_stats[CC_id].ulsch_bitrate, + eNB->eNB_stats[CC_id].total_ulsch_bitrate, + eNB->eNB_stats[CC_id].ulsch_bytes_rx, + eNB->eNB_stats[CC_id].total_ulsch_bytes_rx, + eNB->eNB_stats[CC_id].ulsch_pdus_rx, + eNB->eNB_stats[CC_id].total_ulsch_pdus_rx); } len += sprintf(&buffer[len],"\n"); @@ -133,12 +132,16 @@ int dump_eNB_l2_stats(char *buffer, int length) for (UE_id=UE_list->head; UE_id>=0; UE_id=UE_list->next[UE_id]) { for (i=0; i<UE_list->numactiveCCs[UE_id]; i++) { CC_id=UE_list->ordered_CCids[i][UE_id]; - UE_list->eNB_UE_stats[CC_id][UE_id].dlsch_bitrate=((UE_list->eNB_UE_stats[CC_id][UE_id].TBS*8)/((eNB->frame + 1)*10)); + + UE_list->eNB_UE_stats[CC_id][UE_id].dlsch_bitrate=((UE_list->eNB_UE_stats[CC_id][UE_id].TBS*8)/((eNB->frame + 1)*10)); UE_list->eNB_UE_stats[CC_id][UE_id].total_dlsch_bitrate= ((UE_list->eNB_UE_stats[CC_id][UE_id].total_pdu_bytes*8)/((eNB->frame + 1)*10)); UE_list->eNB_UE_stats[CC_id][UE_id].total_overhead_bytes+= UE_list->eNB_UE_stats[CC_id][UE_id].overhead_bytes; UE_list->eNB_UE_stats[CC_id][UE_id].avg_overhead_bytes=((UE_list->eNB_UE_stats[CC_id][UE_id].total_overhead_bytes*8)/((eNB->frame + 1)*10)); - - len += sprintf(&buffer[len],"UE %d %s, RNTI %x : CQI %d, MCS1 %d, MCS2 %d, RB (tx %d, retx %d, total %d), ncce (tx %d, retx %d) \n", + + UE_list->eNB_UE_stats[CC_id][UE_id].ulsch_bitrate=((UE_list->eNB_UE_stats[CC_id][UE_id].ulsch_TBS*8)/((eNB->frame + 1)*10)); + UE_list->eNB_UE_stats[CC_id][UE_id].total_ulsch_bitrate= ((UE_list->eNB_UE_stats[CC_id][UE_id].total_pdu_bytes_rx*8)/((eNB->frame + 1)*10)); + + len += sprintf(&buffer[len],"UE %d %s (DLSCH), RNTI %x : CQI %d, MCS1 %d, MCS2 %d, RB (tx %d, retx %d, total %d), ncce (tx %d, retx %d) \n", UE_id, map_int_to_str(rrc_status_names, UE_list->eNB_UE_stats[CC_id][UE_id].rrc_status), UE_list->eNB_UE_stats[CC_id][UE_id].crnti, @@ -165,12 +168,30 @@ int dump_eNB_l2_stats(char *buffer, int length) UE_list->eNB_UE_stats[CC_id][UE_id].total_overhead_bytes, UE_list->eNB_UE_stats[CC_id][UE_id].avg_overhead_bytes ); - len += sprintf(&buffer[len], - "[MAC] ULSCH received bytes (total %"PRIu64")," - "Total received PDU %d, Total errors %d\n", + + len += sprintf(&buffer[len],"UE %d %s (ULSCH), RNTI %x : rx power (normalized %d, target %d), MCS (pre %d, post %d), RB (rx %d, retx %d, total %d), Current TBS %d \n", + UE_id, + map_int_to_str(rrc_status_names, UE_list->eNB_UE_stats[CC_id][UE_id].rrc_status), + UE_list->eNB_UE_stats[CC_id][UE_id].crnti, + UE_list->eNB_UE_stats[CC_id][UE_id].normalized_rx_power, + UE_list->eNB_UE_stats[CC_id][UE_id].target_rx_power, + UE_list->eNB_UE_stats[CC_id][UE_id].ulsch_mcs1, + UE_list->eNB_UE_stats[CC_id][UE_id].ulsch_mcs2, + UE_list->eNB_UE_stats[CC_id][UE_id].rbs_used_rx, + UE_list->eNB_UE_stats[CC_id][UE_id].rbs_used_retx_rx, + UE_list->eNB_UE_stats[CC_id][UE_id].total_rbs_used_rx, + UE_list->eNB_UE_stats[CC_id][UE_id].ulsch_TBS + ); + + len += sprintf(&buffer[len], + "[MAC] ULSCH bitrate (TTI %d, avg %d), received bytes (total %"PRIu64")," + "Total received PDU %d, Total errors %d\n", + UE_list->eNB_UE_stats[CC_id][UE_id].ulsch_bitrate, + UE_list->eNB_UE_stats[CC_id][UE_id].total_ulsch_bitrate, UE_list->eNB_UE_stats[CC_id][UE_id].total_pdu_bytes_rx, UE_list->eNB_UE_stats[CC_id][UE_id].total_num_pdus_rx, UE_list->eNB_UE_stats[CC_id][UE_id].num_errors_rx); + len+= sprintf(&buffer[len],"Received PHR PH = %d (db)\n", UE_list->UE_template[CC_id][UE_id].phr_info); len+= sprintf(&buffer[len],"Received BSR LCGID[0][1][2][3] = %u %u %u %u\n", UE_list->UE_template[CC_id][UE_id].bsr_info[LCGID0], diff --git a/openair2/RRC/LITE/rrc_UE.c b/openair2/RRC/LITE/rrc_UE.c index 664c169e3a98368945495814252281d0654ed704..15d24e2914dc765f59a52d4a3b3ff420b991bfd1 100644 --- a/openair2/RRC/LITE/rrc_UE.c +++ b/openair2/RRC/LITE/rrc_UE.c @@ -710,7 +710,7 @@ rrc_ue_establish_drb( RADIO_ACCESS_BEARER,Rlc_info_um); */ #ifdef PDCP_USE_NETLINK -# if !defined(OAI_NW_DRIVER_TYPE_ETHERNET) && !defined(EXMIMO) && !defined(OAI_USRP) && !defined(LINK_ENB_PDCP_TO_GTPV1U) +# if !defined(OAI_NW_DRIVER_TYPE_ETHERNET) && !defined(EXMIMO) && !defined(OAI_USRP) && !defined(OAI_BLADERF) && !defined(LINK_ENB_PDCP_TO_GTPV1U) # ifdef OAI_EMU ip_addr_offset3 = oai_emulation.info.nb_enb_local; ip_addr_offset4 = NB_eNB_INST; diff --git a/openair2/RRC/LITE/rrc_eNB.c b/openair2/RRC/LITE/rrc_eNB.c index 7fc755c7d704a5cc11605af01ce3ca425da9aa5e..52069fe54b66fbea730fe63b5fa8fa063aa8f295 100644 --- a/openair2/RRC/LITE/rrc_eNB.c +++ b/openair2/RRC/LITE/rrc_eNB.c @@ -3005,7 +3005,7 @@ rrc_eNB_process_RRCConnectionReconfigurationComplete( ctxt_pP->module_id, ctxt_pP->frame, (int)DRB_configList->list.array[i]->drb_Identity); #if defined(PDCP_USE_NETLINK) && !defined(LINK_ENB_PDCP_TO_GTPV1U) // can mean also IPV6 since ether -> ipv6 autoconf -# if !defined(OAI_NW_DRIVER_TYPE_ETHERNET) && !defined(EXMIMO) && !defined(OAI_USRP) +# if !defined(OAI_NW_DRIVER_TYPE_ETHERNET) && !defined(EXMIMO) && !defined(OAI_USRP) && !defined(OAI_BLADERF) LOG_I(OIP, "[eNB %d] trying to bring up the OAI interface oai%d\n", ctxt_pP->module_id, ctxt_pP->module_id); diff --git a/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.c b/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.c index ef3265db89e18e8c9c32f556a5fe6af94ab48921..bbb8463714c7696124114636feb1bf098e5a537b 100644 --- a/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.c +++ b/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.c @@ -37,142 +37,98 @@ #include <stdlib.h> #include <inttypes.h> #include "bladerf_lib.h" -#include "common_lib.h" -#include "log.h" - -#define SAMPLE_LEN samples_per_frame*sizeof(int32_t) int num_devices=0; /*These items configure the underlying asynch stream used by the the sync interface. */ -unsigned int num_buffers; -unsigned int buffer_size; -unsigned int num_transfers; -unsigned int timeout_ms; - int trx_brf_init(openair0_device *openair0) { } -struct bladerf * open_bladerf_from_serial(const char *serial) { + + +openair0_timestamp trx_get_timestamp(openair0_device *device) { int status; - struct bladerf *dev; - struct bladerf_devinfo info; - /* Initialize all fields to "don't care" wildcard values. - * - * Immediately passing this to bladerf_open_with_devinfo() would cause - * libbladeRF to open any device on any available backend. */ - bladerf_init_devinfo(&info); - /* Specify the desired device's serial number, while leaving all other - * fields in the info structure wildcard values */ - strncpy(info.serial, serial, BLADERF_SERIAL_LENGTH - 1); - info.serial[BLADERF_SERIAL_LENGTH - 1] = '\0'; - status = bladerf_open_with_devinfo(&dev, &info); + struct bladerf_metadata meta; + brf_state_t *brf = (brf_state_t*)device->priv; - if (status == BLADERF_ERR_NODEV) { - printf("No devices available with serial=%s\n", serial); - return NULL; - } else if (status != 0) { - fprintf(stderr, "Failed to open device with serial=%s (%s)\n", serial, bladerf_strerror(status)); - return NULL; + if ((status=bladerf_get_timestamp(brf->dev, BLADERF_MODULE_TX, &meta.timestamp)) != 0) { + fprintf(stderr,"Failed to get current RX timestamp: %s\n",bladerf_strerror(status)); } else { - return dev; + printf("Current TX timestampe 0x%016"PRIx64"\n", meta.timestamp); } + return meta.timestamp; } - - - int trx_brf_start(openair0_device *openair0) { + return 0; } -int trx_brf_write(openair0_device *device,openair0_timestamp ptimestamp, void **buff, int nsamps, int cc) { +static void trx_brf_write(openair0_device *device,openair0_timestamp ptimestamp, void **buff, int nsamps, int cc) { + int status; - struct bladerf_metadata meta; - int16_t zeros[] = { 0, 0, 0, 0 }; - struct bladerf *dev = (struct bladerf*) device->priv; - openair0_timestamp current_timestamp; + brf_state_t *brf = (brf_state_t*)device->priv; /* BRF has only 1 rx/tx chaine : is it correct? */ - void *sample = (void*)buff[0]; - memset(&meta, 0, sizeof(meta)); - - /* Retrieve the current timestamp */ -#ifdef BRF_TEST - if ((status=bladerf_get_timestamp(dev, BLADERF_MODULE_TX, &meta.timestamp)) != 0) { - fprintf(stderr,"Failed to get current RX timestamp: %s\n",bladerf_strerror(status)); - } else { - current_timestamp = meta.timestamp; - printf("Current TX timestampe 0x%016"PRIx64" and current timestamp: 0x%016"PRIx64"\n", ptimestamp, meta.timestamp); - } -#endif - meta.timestamp= ptimestamp; - - meta.flags |= BLADERF_META_FLAG_TX_NOW; + void *samples = (void*)buff[0]; + brf->meta_tx.timestamp= ptimestamp; + //brf->meta_tx.flags |= BLADERF_META_FLAG_TX_NOW; + + status = bladerf_sync_tx(brf->dev, samples, (unsigned int) nsamps, &brf->meta_tx, brf->timeout_ms); - status = bladerf_sync_tx(dev, sample, nsamps, &meta, timeout_ms); if (status != 0) { fprintf(stderr, "Failed to TX sample: %s\n", bladerf_strerror(status)); + brf->num_tx_errors++; brf_error(status); } - } -int trx_brf_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc) { +static int trx_brf_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc) { int status, ret; - struct bladerf_metadata meta; + unsigned int i; - struct bladerf *dev = (struct bladerf*) device->priv; - /* BRF has only onerx/tx chain: is it correct? */ - void *sample = (void*)buff[0]; - memset(&meta, 0, sizeof(meta)); - /* Retrieve the current timestamp */ - if ((status=bladerf_get_timestamp(dev, BLADERF_MODULE_RX, &meta.timestamp)) != 0) { - fprintf(stderr,"Failed to get current RX timestamp: %s\n",bladerf_strerror(status)); - } else { - *ptimestamp = meta.timestamp; - printf("Current RX timestamp: 0x%016"PRIx64"\n", meta.timestamp); - } + brf_state_t *brf = (brf_state_t*)device->priv; + + // BRF has only one rx/tx chain + void *samples = (void*)buff[0]; + + brf->meta_rx.flags |= BLADERF_META_FLAG_RX_NOW; + status = bladerf_sync_rx(brf->dev, samples, (unsigned int) nsamps, &brf->meta_rx, brf->timeout_ms); - meta.flags |= BLADERF_META_FLAG_RX_NOW; - //fflush(stdout); - - status = bladerf_sync_rx(dev, sample, nsamps, &meta, timeout_ms); - - /* if (meta.actual_count < nsamps ) { - printf("[BRF][RX] received %d samples out of %d\n", meta.actual_count, nsamps); - } - */ if (status != 0) { - fprintf(stderr, "RX failed: %s\n", bladerf_strerror(status)); - } else if (meta.status & BLADERF_META_STATUS_OVERRUN) { - fprintf(stderr, "Overrun detected in RX. %u valid samples were read (nsymps %d)\n", meta.actual_count,nsamps); - } else if (meta.status & BLADERF_META_STATUS_UNDERRUN) { - fprintf(stderr, "Underrun detected in RX. %u valid samples were read (nsymps %d) \n", meta.actual_count,nsamps); - }else { - printf("Got %u samples at t=0x%016"PRIx64"\n", meta.actual_count, meta.timestamp); - } - - return meta.actual_count; + fprintf(stderr, "RX failed: %s\n", bladerf_strerror(status)); + brf->num_rx_errors++; + } else if ( brf->meta_rx.status & BLADERF_META_STATUS_OVERRUN) { + brf->num_overflows++; + fprintf(stderr, "RX overrun (%d) in read @ t=0x%"PRIu64". Got %u samples. nsymps %d\n", + brf->num_overflows,brf->meta_rx.timestamp, brf->meta_rx.actual_count, nsamps); + //brf->meta_rx.timestamp=(unsigned int)(nsamps-brf->meta_rx.actual_count); + } //else printf("[BRF] (buff %p) ts=0x%"PRIu64" %s\n",samples, brf->meta_rx.timestamp,bladerf_strerror(status)); + + brf->rx_actual_count+=brf->meta_rx.actual_count; + brf->rx_count+=nsamps; + *ptimestamp = brf->meta_rx.timestamp; + + return brf->meta_rx.actual_count; } int trx_brf_end(openair0_device *device) { int status; - struct bladerf *dev = (struct bladerf*) device->priv; + brf_state_t *brf = (brf_state_t*)device->priv; // Disable RX module, shutting down our underlying RX stream - if ((status=bladerf_enable_module(device->priv, BLADERF_MODULE_RX, false)) != 0) { + if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_RX, false)) != 0) { fprintf(stderr, "Failed to disable RX module: %s\n", bladerf_strerror(status)); } - if ((status=bladerf_enable_module(device->priv, BLADERF_MODULE_TX, false)) != 0) { + if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_TX, false)) != 0) { fprintf(stderr, "Failed to disable TX module: %s\n", bladerf_strerror(status)); } - bladerf_close(dev); + bladerf_close(brf->dev); return 0; } @@ -181,108 +137,130 @@ int openair0_device_init(openair0_device *device, openair0_config_t *openair0_cf int status; int card=0; - //struct bladerf_version *version; - //printf("Opening the brf device (version %s)...\n", bladerf_version(version)); - // opaque data struct - struct bladerf *dev;// = (struct bladerf*)malloc(sizeof(struct bladerf)); - //memset(dev, 0, sizeof(struct bladerf)); - // An empty ("") or NULL device identifier will result in the first encountered device being opened (using the first discovered backend) - + brf_state_t *brf = (brf_state_t*)malloc(sizeof(brf_state_t)); + memset(brf, 0, sizeof(brf_state_t)); // init required params for BRF - num_buffers = 16; - buffer_size = openair0_cfg[card]. samples_per_packet*sizeof(int32_t); // buffer size = 4096 for sample_len of 1024 - num_transfers = 8; // ? device->openair0_cfg.samples_per_packets - timeout_ms = 1; // or 0/ - - printf("the buffer_size is set to %d\n", buffer_size); - -if ((status=bladerf_open(&dev, "")) != 0 ) { + brf->num_buffers = 128; + brf->buffer_size = (unsigned int)openair0_cfg[card].samples_per_packet*sizeof(int32_t); // buffer size = 4096 for sample_len of 1024 + brf->num_transfers = 16; + brf->timeout_ms = 0; + brf->sample_rate=(unsigned int)openair0_cfg[card].sample_rate; + + printf("\n[BRF] sampling_rate %d, num_buffers %d, buffer_size %d, num transfer %d, timeout_ms %d\n", + brf->sample_rate, brf->num_buffers, brf->buffer_size,brf->num_transfers, brf->timeout_ms); + + if ((status=bladerf_open(&brf->dev, "")) != 0 ) { fprintf(stderr,"Failed to open brf device: %s\n",bladerf_strerror(status)); brf_error(status); } - printf("[BRF] device speed is %d\n",bladerf_device_speed(dev) ); - + printf("[BRF] init dev %p\n", brf->dev); + switch(bladerf_device_speed(brf->dev)){ + case BLADERF_DEVICE_SPEED_SUPER: + printf("[BRF] Device operates at max speed\n"); + break; + default: + printf("[BRF] Device does not operates at max speed, change the USB port\n"); + brf_error(BLADERF_ERR_UNSUPPORTED); + } // RX // Example of CLI output: RX Frequency: 2539999999Hz - if ((status=bladerf_set_frequency(dev, BLADERF_MODULE_RX, openair0_cfg[card].rx_freq[0])) != 0){ + if ((status=bladerf_set_frequency(brf->dev, BLADERF_MODULE_RX, (unsigned int) openair0_cfg[card].rx_freq[0])) != 0){ fprintf(stderr,"Failed to set RX frequency: %s\n",bladerf_strerror(status)); brf_error(status); - } - // RX sample rate: 7680000 0/1 - if ((status=bladerf_set_sample_rate(dev, BLADERF_MODULE_RX, openair0_cfg[card].sample_rate, NULL)) != 0){ + } else + printf("[BRF] set RX frequency to %f\n",openair0_cfg[card].rx_freq[0]); + + + if ((status=bladerf_set_sample_rate(brf->dev, BLADERF_MODULE_RX, (unsigned int)openair0_cfg[card].sample_rate, NULL)) != 0){ fprintf(stderr,"Failed to set RX sample rate: %s\n", bladerf_strerror(status)); brf_error(status); - } - // RX Bandwidth: 14000000Hz - if ((status=bladerf_set_bandwidth(dev, BLADERF_MODULE_RX, openair0_cfg[card].rx_bw, NULL)) != 0){ + }else + printf("[BRF] set RX sample rate to %f\n",openair0_cfg[card].sample_rate); + + if ((status=bladerf_set_bandwidth(brf->dev, BLADERF_MODULE_RX, (unsigned int) openair0_cfg[card].rx_bw, NULL)) != 0){ fprintf(stderr,"Failed to set RX bandwidth: %s\n", bladerf_strerror(status)); brf_error(status); - } - // desired gain = 16 - if ((status=bladerf_set_gain(dev, BLADERF_MODULE_RX, openair0_cfg[card].rx_gain[0])) != 0) { + }else + printf("[BRF] set RX bandwidth to %f\n",openair0_cfg[card].rx_bw); + + if ((status=bladerf_set_gain(brf->dev, BLADERF_MODULE_RX, (int) openair0_cfg[card].rx_gain[0])) != 0) { fprintf(stderr,"Failed to set RX gain: %s\n",bladerf_strerror(status)); brf_error(status); - } + } else + printf("[BRF] set RX gain to %f\n",openair0_cfg[card].rx_gain[0]); /* Configure the device's RX module for use with the sync interface. * SC16 Q11 samples *with* metadata are used. */ - if ((status=bladerf_sync_config(dev, BLADERF_MODULE_RX, BLADERF_FORMAT_SC16_Q11_META,num_buffers,buffer_size,num_transfers,timeout_ms)) != 0 ) { + if ((status=bladerf_sync_config(brf->dev, BLADERF_MODULE_RX, BLADERF_FORMAT_SC16_Q11_META,brf->num_buffers,brf->buffer_size,brf->num_transfers,brf->timeout_ms)) != 0 ) { fprintf(stderr,"Failed to configure RX sync interface: %s\n", bladerf_strerror(status)); brf_error(status); - } - + }else + printf("[BRF] configured Rx for sync interface \n"); + /* We must always enable the RX module after calling bladerf_sync_config(), and * before attempting to RX samples via bladerf_sync_rx(). */ - if ((status=bladerf_enable_module(dev, BLADERF_MODULE_RX, true)) != 0) { + if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_RX, true)) != 0) { fprintf(stderr,"Failed to enable RX module: %s\n", bladerf_strerror(status)); brf_error(status); - } - + }else + printf("[BRF] RX module enabled \n"); + // TX - if ((status=bladerf_set_frequency(dev, BLADERF_MODULE_TX, openair0_cfg[card].tx_freq[0])) != 0){ + if ((status=bladerf_set_frequency(brf->dev, BLADERF_MODULE_TX, (unsigned int) openair0_cfg[card].tx_freq[0])) != 0){ fprintf(stderr,"Failed to set TX frequency: %s\n",bladerf_strerror(status)); brf_error(status); - } - if ((status=bladerf_set_sample_rate(dev, BLADERF_MODULE_TX, openair0_cfg[card].sample_rate, NULL)) != 0){ + }else + printf("[BRF] set Tx Frequenct to %f \n", openair0_cfg[card].tx_freq[0]); + + if ((status=bladerf_set_sample_rate(brf->dev, BLADERF_MODULE_TX, (unsigned int) openair0_cfg[card].sample_rate, NULL)) != 0){ fprintf(stderr,"Failed to set TX sample rate: %s\n", bladerf_strerror(status)); brf_error(status); - } - if ((status=bladerf_set_bandwidth(dev, BLADERF_MODULE_TX,openair0_cfg[card].tx_bw, NULL)) != 0){ + }else + printf("[BRF] set Tx sampling rate to %f \n", openair0_cfg[card].sample_rate); + + if ((status=bladerf_set_bandwidth(brf->dev, BLADERF_MODULE_TX,(unsigned int)openair0_cfg[card].tx_bw, NULL)) != 0){ fprintf(stderr, "Failed to set RX bandwidth: %s\n", bladerf_strerror(status)); brf_error(status); - } - if ((status=bladerf_set_gain(dev, BLADERF_MODULE_TX, openair0_cfg[card].tx_gain[0])) != 0) { + }else + printf("[BRF] set Tx sampling ratebandwidth to %f \n", openair0_cfg[card].tx_bw); + + if ((status=bladerf_set_gain(brf->dev, BLADERF_MODULE_TX, (int)openair0_cfg[card].tx_gain[0])) != 0) { fprintf(stderr,"Failed to set TX gain: %s\n",bladerf_strerror(status)); brf_error(status); - } + }else + printf("[BRF] set the Tx gain to %f \n", openair0_cfg[card].tx_gain[0]); + /* Configure the device's TX module for use with the sync interface. * SC16 Q11 samples *with* metadata are used. */ - if ((status=bladerf_sync_config(dev, BLADERF_MODULE_TX,BLADERF_FORMAT_SC16_Q11_META,num_buffers,buffer_size,num_transfers,timeout_ms)) != 0 ) { + if ((status=bladerf_sync_config(brf->dev, BLADERF_MODULE_TX,BLADERF_FORMAT_SC16_Q11_META,brf->num_buffers,brf->buffer_size,brf->num_transfers,brf->timeout_ms)) != 0 ) { fprintf(stderr,"Failed to configure TX sync interface: %s\n", bladerf_strerror(status)); brf_error(status); - } + }else + printf("[BRF] configured tx for sync interface \n"); /* We must always enable the TX module after calling bladerf_sync_config(), and * before attempting to TX samples via bladerf_sync_tx(). */ - if ((status=bladerf_enable_module(dev, BLADERF_MODULE_TX, true)) != 0) { + if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_TX, true)) != 0) { fprintf(stderr,"Failed to enable TX module: %s\n", bladerf_strerror(status)); brf_error(status); - } - + } else + printf("[BRF] RX module enabled \n"); bladerf_log_set_verbosity(get_brf_log_level(openair0_cfg[card].log_level)); printf("BLADERF: Initializing openair0_device\n"); - device->priv = dev; + device->priv = brf; device->Mod_id = num_devices++; device->trx_start_func = trx_brf_start; device->trx_end_func = trx_brf_end; device->trx_read_func = trx_brf_read; device->trx_write_func = trx_brf_write; memcpy((void*)&device->openair0_cfg,(void*)openair0_cfg,sizeof(openair0_config_t)); + + return 0; } void brf_error(int status) { @@ -290,9 +268,49 @@ void brf_error(int status) { exit(-1); } +int openair0_stop(int card) { + + return(0); + +} +int openair0_set_frequencies(openair0_device* device, openair0_config_t *openair0_cfg,int dummy) { + return 0; + +} +int openair0_set_rx_frequencies(openair0_device* device, openair0_config_t *openair0_cfg) { + return 0; +} + +struct bladerf * open_bladerf_from_serial(const char *serial) { + + int status; + struct bladerf *dev; + struct bladerf_devinfo info; + /* Initialize all fields to "don't care" wildcard values. + * + * Immediately passing this to bladerf_open_with_devinfo() would cause + * libbladeRF to open any device on any available backend. */ + bladerf_init_devinfo(&info); + /* Specify the desired device's serial number, while leaving all other + * fields in the info structure wildcard values */ + strncpy(info.serial, serial, BLADERF_SERIAL_LENGTH - 1); + info.serial[BLADERF_SERIAL_LENGTH - 1] = '\0'; + status = bladerf_open_with_devinfo(&dev, &info); + + if (status == BLADERF_ERR_NODEV) { + printf("No devices available with serial=%s\n", serial); + return NULL; + } else if (status != 0) { + fprintf(stderr, "Failed to open device with serial=%s (%s)\n", serial, bladerf_strerror(status)); + return NULL; + } else { + return dev; + } +} int get_brf_log_level(int log_level){ int level=BLADERF_LOG_LEVEL_INFO; + return BLADERF_LOG_LEVEL_DEBUG; switch(log_level) { case LOG_DEBUG: level=BLADERF_LOG_LEVEL_DEBUG; diff --git a/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.h b/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.h index 8d98a1751ab4c715f4417a731337430c5b19ad5d..7a73352e71a1f1a8ba278d52692e05cffd60fa5b 100644 --- a/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.h +++ b/targets/ARCH/BLADERF/USERSPACE/LIB/bladerf_lib.h @@ -33,44 +33,47 @@ */ #include <libbladeRF.h> -/* -enum brf_err_num{ - BLADERF_ERR_MIN=0, - BLADERF_ERR_UNEXPECTED=-1, - BLADERF_ERR_RANGE=-2, - BLADERF_ERR_INVAL=-3, - BLADERF_ERR_MEM=-4, - BLADERF_ERR_IO=-5, - BLADERF_ERR_TIMEOUT=-6, - BLADERF_ERR_NODEV=-7, - BLADERF_ERR_UNSUPPORTED=-8, - BLADERF_ERR_MISALIGNED=-9, - BLADERF_ERR_CHECKSUM=-10, - BLADERF_ERR_NO_FILE=-11, - BLADERF_ERR_UPDATE_FPGA=-12, - BLADERF_ERR_UPDATE_FW=-13, - BLADERF_ERR_TIME_PAST=-14, - BLADERF_ERR_MAX=-15, -} -mapping brf_err_names[] = { - {"BLADERF_OK", BLADERF_ERR_MIN}, - {"BLADERF_ERR_UNEXPECTED",BLADERF_ERR_UNEXPECTED}, - {"BLADERF_ERR_RANGE",BLADERF_ERR_RANGE}, - {"BLADERF_ERR_INVAL",BLADERF_ERR_INVAL}, - {"BLADERF_ERR_MEM",BLADERF_ERR_MEM}, - {"BLADERF_ERR_IO",BLADERF_ERR_IO}, - {"BLADERF_ERR_TIMEOUT",BLADERF_ERR_TIMEOUT}, - {"BLADERF_ERR_NODEV",BLADERF_ERR_NODEV}, - {"BLADERF_ERR_UNSUPPORTED",BLADERF_ERR_UNSUPPORTED}, - {"BLADERF_ERR_MISALIGNED",BLADERF_ERR_MISALIGNED}, - {"BLADERF_ERR_CHECKSUM",BLADERF_ERR_CHECKSUM}, - {"BLADERF_ERR_NO_FILE",BLADERF_ERR_NO_FILE}, - {"BLADERF_ERR_UPDATE_FPGA",BLADERF_ERR_UPDATE_FPGA}, - {"BLADERF_ERR_UPDATE_FW",BLADERF_ERR_UPDATE_FW}, - {"BLADERF_ERR_TIME_PAST",BLADERF_ERR_TIME_PAST}, - {NULL, BLADERF_ERR_MAX} -}; -*/ + +#include "common_lib.h" +#include "log.h" + +typedef struct { + + // opaque BRF data struct + struct bladerf *dev; + // An empty ("") or NULL device identifier will result in the first encountered device being opened (using the first discovered backend) + + unsigned int num_buffers; + unsigned int buffer_size; + unsigned int num_transfers; + unsigned int timeout_ms; + + struct bladerf_metadata meta_rx; + struct bladerf_metadata meta_tx; + + unsigned int sample_rate; + // time offset between transmiter timestamp and receiver timestamp; + double tdiff; + // use brf_time_offset to get this value + int tx_forward_nsamps; //166 for 20Mhz + + + // -------------------------------- + // Debug and output control + // -------------------------------- + int num_underflows; + int num_overflows; + int num_seq_errors; + int num_rx_errors; + int num_tx_errors; + + uint64_t tx_actual_count; + uint64_t rx_actual_count; + uint64_t tx_count; + uint64_t rx_count; + openair0_timestamp rx_timestamp; + +} brf_state_t; /* * func prototypes */ diff --git a/targets/RT/USER/lte-softmodem.c b/targets/RT/USER/lte-softmodem.c index 25e84ecfd909f44922280c4d62a4609a382a0b4d..c1689fd9473e924cf66c00f4f889f9308d7861b9 100644 --- a/targets/RT/USER/lte-softmodem.c +++ b/targets/RT/USER/lte-softmodem.c @@ -292,7 +292,7 @@ char ref[128] = "internal"; char channels[128] = "0"; unsigned int samples_per_frame = 307200; -unsigned int tx_forward_nsamps; +unsigned int tx_forward_nsamps=0; int tx_delay; #endif @@ -990,14 +990,18 @@ void do_OFDM_mod_rt(int subframe,PHY_VARS_eNB *phy_vars_eNB) ((short*)&phy_vars_eNB->lte_eNB_common_vars.txdata[0][aa][tx_offset])[0]= #ifdef EXMIMO ((short*)dummy_tx_b)[2*i]<<4; +#elif OAI_BLADRF + ((short*)dummy_tx_b)[2*i]; #else ((short*)dummy_tx_b)[2*i]<<5; #endif - ((short*)&phy_vars_eNB->lte_eNB_common_vars.txdata[0][aa][tx_offset])[1]= + ((short*)&phy_vars_eNB->lte_eNB_common_vars.txdata[0][aa][tx_offset])[1]= #ifdef EXMIMO - ((short*)dummy_tx_b)[2*i+1]<<4; + ((short*)dummy_tx_b)[2*i+1]<<4; +#elif OAI_BLADRF + ((short*)dummy_tx_b)[2*i+1]; #else - ((short*)dummy_tx_b)[2*i+1]<<5; + ((short*)dummy_tx_b)[2*i+1]<<5; #endif } // if S-subframe switch to RX in second subframe @@ -1078,7 +1082,7 @@ static void* eNB_thread_tx( void* param ) return &eNB_thread_tx_status[proc->subframe]; } - LOG_I( HW, "[SCHED] eNB TX deadline thread %d(tid %ld) started on CPU %d\n", proc->subframe, gettid(), sched_getcpu() ); + LOG_I( HW, "[SCHED] eNB TX deadline thread %d(Tid %ld) started on CPU %d\n", proc->subframe, gettid(), sched_getcpu() ); #else LOG_I( HW, "[SCHED][eNB] TX thread %d started on CPU %d TID %d\n", proc->subframe, sched_getcpu(),gettid() ); #endif @@ -1256,8 +1260,8 @@ static void* eNB_thread_rx( void* param ) /* This creates a 2ms reservation every 10ms period*/ attr.sched_policy = SCHED_DEADLINE; - attr.sched_runtime = 0.9 * 1000000; // each rx thread must finish its job in the worst case in 2ms - attr.sched_deadline = 2 * 1000000; // each rx thread will finish within 2ms + attr.sched_runtime = 1 * 1000000; // each rx thread must finish its job in the worst case in 2ms + attr.sched_deadline = 1 * 1000000; // each rx thread will finish within 2ms attr.sched_period = 1 * 10000000; // each rx thread has a period of 10ms from the starting point if (sched_setattr(0, &attr, flags) < 0 ) { @@ -1265,7 +1269,7 @@ static void* eNB_thread_rx( void* param ) return &eNB_thread_rx_status[proc->subframe]; } - LOG_I( HW, "[SCHED] eNB RX deadline thread %d(id %ld) started on CPU %d\n", proc->subframe, gettid(), sched_getcpu() ); + LOG_I( HW, "[SCHED] eNB RX deadline thread %d(TID %ld) started on CPU %d\n", proc->subframe, gettid(), sched_getcpu() ); #else LOG_I( HW, "[SCHED][eNB] RX thread %d started on CPU %d TID %d\n", proc->subframe, sched_getcpu(),gettid() ); #endif @@ -1573,8 +1577,8 @@ static void* eNB_thread( void* arg ) /* This creates a .5 ms reservation */ attr.sched_policy = SCHED_DEADLINE; - attr.sched_runtime = 0.1 * 1000000; - attr.sched_deadline = 0.5 * 1000000; + attr.sched_runtime = 0.2 * 1000000; + attr.sched_deadline = 0.9 * 1000000; attr.sched_period = 1.0 * 1000000; @@ -1769,9 +1773,10 @@ static void* eNB_thread( void* arg ) stop_meas( &softmodem_stats_hw ); clock_gettime( CLOCK_MONOTONIC, &trx_time1 ); - if (rxs != spp) - exit_fun( "problem receiving samples" ); - + if (frame > 10){ + if (rxs != spp) + exit_fun( "problem receiving samples" ); + } VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 0 ); // Transmit TX buffer based on timestamp from RX @@ -2798,7 +2803,6 @@ int main( int argc, char **argv ) } - dump_frame_parms(frame_parms[0]); if(frame_parms[0]->N_RB_DL == 100) { @@ -2806,7 +2810,9 @@ int main( int argc, char **argv ) bw = 10.0e6; #ifndef EXMIMO openair0_cfg[0].samples_per_packet = 2048; - samples_per_frame = 307200; + samples_per_frame = 307200; + openair0_cfg[card].tx_bw = 10e6; + openair0_cfg[card].rx_bw = 10e6; // from usrp_time_offset tx_forward_nsamps = 175; tx_delay = 8; @@ -2817,6 +2823,8 @@ int main( int argc, char **argv ) #ifndef EXMIMO openair0_cfg[0].samples_per_packet = 2048; samples_per_frame = 153600; + openair0_cfg[card].tx_bw = 5e6; + openair0_cfg[card].rx_bw = 5e6; tx_forward_nsamps = 95; tx_delay = 5; #endif @@ -2824,10 +2832,17 @@ int main( int argc, char **argv ) sample_rate = 7.68e6; bw = 2.5e6; #ifndef EXMIMO - openair0_cfg[0].samples_per_packet = 1024; samples_per_frame = 76800; + openair0_cfg[card].tx_bw = 2.5e6; + openair0_cfg[card].rx_bw = 2.5e6; + openair0_cfg[0].samples_per_packet = 1024; +#ifdef OAI_USRP + tx_forward_nsamps = 70; + tx_delay = 6; +#elif OAI_BLADERF tx_forward_nsamps = 70; tx_delay = 6; +#endif #endif } else if (frame_parms[0]->N_RB_DL == 6) { sample_rate = 1.92e6; @@ -2835,6 +2850,8 @@ int main( int argc, char **argv ) #ifndef EXMIMO openair0_cfg[0].samples_per_packet = 256; samples_per_frame = 19200; + openair0_cfg[card].tx_bw = 1.5e6; + openair0_cfg[card].rx_bw = 1.5e6; tx_forward_nsamps = 40; tx_delay = 8; #endif