diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt index 3dbb61b5aec615544e728523304a10c0b48474fb..c5ca88d5b84808c945142e9122dccb3a6f486775 100644 --- a/cmake_targets/CMakeLists.txt +++ b/cmake_targets/CMakeLists.txt @@ -241,6 +241,7 @@ add_boolean_option(UE_AUTOTEST_TRACE False "Activate UE autotest specific logs add_boolean_option(UE_DEBUG_TRACE False "Activate UE debug trace") add_boolean_option(UE_TIMING_TRACE False "Activate UE timing trace") add_boolean_option(DISABLE_LOG_X False "Deactivate all LOG_* macros") +add_boolean_option(USRP_REC_PLAY False "Enable USRP record playback mode") add_boolean_option(DEBUG_CONSOLE False "makes debugging easier, disables stdout/stderr buffering") diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai index d3a13c41c93cab31eda220e1e7c5d2240b709322..d625dfa159e3dbc768115f2eba37913fb2d08ff9 100755 --- a/cmake_targets/build_oai +++ b/cmake_targets/build_oai @@ -66,6 +66,7 @@ UE_AUTOTEST_TRACE="False" UE_DEBUG_TRACE="False" UE_TIMING_TRACE="False" DISABLE_LOG_X="False" +USRP_REC_PLAY="False" BUILD_ECLIPSE=0 trap handle_ctrl_c INT @@ -158,6 +159,8 @@ Options Disable all LOG_* macros --build-eclipse Build eclipse project files. Paths are auto corrected by fixprj.sh +--usrp-recplay + Build for I/Q record-playback modes Usage (first build): oaisim (eNB + UE): ./build_oai -I --oaisim -x --install-system-files Eurecom EXMIMO + COTS UE : ./build_oai -I --eNB -x --install-system-files @@ -350,6 +353,10 @@ function main() { BUILD_TELNETSRV=1 echo_info "Build embedded telnet server" shift ;; + --usrp-recplay) + USRP_REC_PLAY="True" + echo_info "Enabling USRP record playback mode" + shift 1;; -h | --help) print_help exit 1;; @@ -540,6 +547,7 @@ function main() { echo "set (UE_DEBUG_TRACE $UE_DEBUG_TRACE)" >> $cmake_file echo "set (UE_TIMING_TRACE $UE_TIMING_TRACE)" >> $cmake_file echo "set (DISABLE_LOG_X $DISABLE_LOG_X)" >> $cmake_file + echo "set (USRP_REC_PLAY $USRP_REC_PLAY)" >> $cmake_file if [ "$UE" = 1 -a "$NOS1" = "0" ] ; then echo_info "Compiling UE S1 build : enabling Linux and NETLINK" echo "set (LINUX True )" >> $cmake_file diff --git a/openair1/PHY/LTE_TRANSPORT/pucch.c b/openair1/PHY/LTE_TRANSPORT/pucch.c index 61d2cc0e9b3a841e904e45452fe511df20cab387..dacafa6b290ddd0b1ab2682c67837a40facab4ae 100644 --- a/openair1/PHY/LTE_TRANSPORT/pucch.c +++ b/openair1/PHY/LTE_TRANSPORT/pucch.c @@ -1871,6 +1871,23 @@ uint32_t rx_pucch(PHY_VARS_eNB *eNB, eNB->pucch1ab_stats_cnt[j][i]=0; } } +#if defined(USRP_REC_PLAY) + // It's probably bad to do this statically only once. + // Looks like the above is incomplete. + // Such reset needs to be done once a UE PHY structure is being used/re-used + // Don't know if this is ever possible in current architecture + for (i=0;i<10240;i++) { + for (j=0;j<NUMBER_OF_UE_MAX;j++) { + eNB->pucch1_stats[j][i]=0; + eNB->pucch1_stats_thres[j][i]=0; + } + } + for (i=0;i<20480;i++) { + for (j=0;j<NUMBER_OF_UE_MAX;j++) { + eNB->pucch1ab_stats[j][i]=0; + } + } +#endif first_call=0; } @@ -2289,8 +2306,12 @@ uint32_t rx_pucch(PHY_VARS_eNB *eNB, stat_im=0; // Do detection now +#if defined(USRP_REC_PLAY) + // It looks like the value is a bit messy when RF is replayed. + if (sigma2_dB<=(dB_fixed(stat_max)-pucch1_thres+2)) {// +#else if (sigma2_dB<(dB_fixed(stat_max)-pucch1_thres)) {// - +#endif chL = (nsymb>>1)-4; chest_mag=0; cfo = (frame_parms->Ncp==0) ? &cfo_pucch_np[14*phase_max] : &cfo_pucch_ep[12*phase_max]; @@ -2431,7 +2452,11 @@ uint32_t rx_pucch(PHY_VARS_eNB *eNB, if (fmt==pucch_format1b) *(1+payload) = (stat_im<0) ? 1 : 2; } else { // insufficient energy on PUCCH so NAK +#if defined(USRP_REC_PLAY) + LOG_I(PHY,"PUCCH 1a/b: NAK subframe %d : sigma2_dB %d, stat_max %d, pucch1_thres %d\n",subframe,sigma2_dB,dB_fixed(stat_max),pucch1_thres); +#else LOG_I(PHY,"PUCCH 1a/b: subframe %d : sigma2_dB %d, stat_max %d, pucch1_thres %d\n",subframe,sigma2_dB,dB_fixed(stat_max),pucch1_thres); +#endif *payload = 4; // DTX ((int16_t*)&eNB->pucch1ab_stats[UE_id][(subframe<<10) + (eNB->pucch1ab_stats_cnt[UE_id][subframe])])[0] = (int16_t)(stat_re); ((int16_t*)&eNB->pucch1ab_stats[UE_id][(subframe<<10) + (eNB->pucch1ab_stats_cnt[UE_id][subframe])])[1] = (int16_t)(stat_im); diff --git a/openair2/LAYER2/MAC/defs.h b/openair2/LAYER2/MAC/defs.h index ba02a3569419dbb1afb07b3e4bbe157abfade4ae..0c9e1498208c8d63896dc01366e18db5b6134689 100644 --- a/openair2/LAYER2/MAC/defs.h +++ b/openair2/LAYER2/MAC/defs.h @@ -128,7 +128,11 @@ /*!\brief Maximum number od control elemenets */ #define MAX_NUM_CE 5 /*!\brief Maximum number of random access process */ +#if defined(USRP_REC_PLAY) +#define NB_RA_PROC_MAX 1 +#else #define NB_RA_PROC_MAX 4 +#endif /*!\brief size of buffer status report table */ #define BSR_TABLE_SIZE 64 /*!\brief The power headroom reporting range is from -23 ...+40 dB and beyond, with step 1 */ diff --git a/openair2/LAYER2/MAC/eNB_scheduler_RA.c b/openair2/LAYER2/MAC/eNB_scheduler_RA.c index c6df031876cfba8b7ff1f5a371519ee97250957b..9ddb78e01fe05033abbea3640ef1802a02d0cae4 100644 --- a/openair2/LAYER2/MAC/eNB_scheduler_RA.c +++ b/openair2/LAYER2/MAC/eNB_scheduler_RA.c @@ -1244,7 +1244,25 @@ void initiate_ra_proc(module_id_t module_idP, RA_template[i].Msg2_subframe = (subframeP+4)%10; /* TODO: find better procedure to allocate RNTI */ do { +#if defined(USRP_REC_PLAY) // deterministic rnti in usrp record/playback mode + static int drnti[NUMBER_OF_UE_MAX] = { 0xbda7, 0x71da, 0x9c40, 0xc350, 0x2710, 0x4e20, 0x7530, 0x1388, 0x3a98, 0x61a8, 0x88b8, 0xafc8, 0xd6d8, 0x1b58, 0x4268, 0x6978 }; + int j = 0; + int nb_ue = 0; + for (j = 0; j < NUMBER_OF_UE_MAX; j++) { + if (UE_RNTI(module_idP, j) > 0) { + nb_ue++; + } else { + break; + } + } + if (nb_ue >= NUMBER_OF_UE_MAX) { + printf("No more free RNTI available, increase NUMBER_OF_UE_MAX\n"); + abort(); + } + RA_template[i].rnti = drnti[nb_ue]; +#else RA_template[i].rnti = taus(); +#endif loop++; } while (loop != 100 && /* TODO: this is not correct, the rnti may be in use without diff --git a/openair2/LAYER2/MAC/eNB_scheduler_primitives.c b/openair2/LAYER2/MAC/eNB_scheduler_primitives.c index 19ff3cf39fa2ab77426feff29581365eefc95b32..08b04f518859a958a1efab0d589c3c87cb5dd8a2 100644 --- a/openair2/LAYER2/MAC/eNB_scheduler_primitives.c +++ b/openair2/LAYER2/MAC/eNB_scheduler_primitives.c @@ -1717,6 +1717,9 @@ int add_new_ue(module_id_t mod_idP, int cc_idP, rnti_t rntiP,int harq_pidP UE_list->ordered_ULCCids[0][UE_id] = cc_idP; UE_list->num_UEs++; UE_list->active[UE_id] = TRUE; +#if defined(USRP_REC_PLAY) // not specific to record/playback ? + UE_list->UE_template[cc_idP][UE_id].pre_assigned_mcs_ul = 0; +#endif #ifdef Rel14 UE_list->UE_template[cc_idP][UE_id].rach_resource_type = rach_resource_type ; diff --git a/openair2/LAYER2/PDCP_v10.1.0/pdcp_security.c b/openair2/LAYER2/PDCP_v10.1.0/pdcp_security.c index d395f959819098e9ff7e4db191afeddd1d823b6e..2554eb145ab9de975100795e207efe6276e39978 100644 --- a/openair2/LAYER2/PDCP_v10.1.0/pdcp_security.c +++ b/openair2/LAYER2/PDCP_v10.1.0/pdcp_security.c @@ -220,7 +220,7 @@ pdcp_validate_security( stream_decrypt(pdcp_pP->cipheringAlgorithm, &decrypt_params, &buffer_decrypted); - +#if !defined(USRP_REC_PLAY) if (srb_flagP) { /* Now check the integrity of the complete PDU */ decrypt_params.message = pdcp_pdu_buffer; @@ -241,7 +241,7 @@ pdcp_validate_security( return -1; } } - +#endif VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_VALIDATE_SECURITY, VCD_FUNCTION_OUT); return 0; diff --git a/openair2/RRC/LITE/defs.h b/openair2/RRC/LITE/defs.h index 0811818c722717b6bda8236cc20879cc3140a1d8..9d7f7fdf2d169d4104de8c5ff5662d51aa2bfc85 100644 --- a/openair2/RRC/LITE/defs.h +++ b/openair2/RRC/LITE/defs.h @@ -242,7 +242,11 @@ typedef enum HO_STATE_e { // #define NUM_MAX_CBA_GROUP 4 // in the platform_constants /* TS 36.331: RRC-TransactionIdentifier ::= INTEGER (0..3) */ +#if defined(USRP_REC_PLAY) +#define RRC_TRANSACTION_IDENTIFIER_NUMBER 1 +#else #define RRC_TRANSACTION_IDENTIFIER_NUMBER 3 +#endif typedef struct { unsigned short transport_block_size; /*!< \brief Minimum PDU size in bytes provided by RLC to MAC layer interface */ diff --git a/openair2/RRC/LITE/rrc_eNB_S1AP.c b/openair2/RRC/LITE/rrc_eNB_S1AP.c index 55adb217facd8ecaacde5e83e08fccd6bb5091ec..24ee3c06dafa7a46b8920697db8969b87a806f55 100644 --- a/openair2/RRC/LITE/rrc_eNB_S1AP.c +++ b/openair2/RRC/LITE/rrc_eNB_S1AP.c @@ -390,8 +390,10 @@ rrc_pdcp_config_security( ue_context_pP->ue_context.kenb, &kRRCint); +#if !defined(USRP_REC_PLAY) #define DEBUG_SECURITY 1 - +#endif + #if defined (DEBUG_SECURITY) #undef msg #define msg printf diff --git a/targets/ARCH/COMMON/common_lib.h b/targets/ARCH/COMMON/common_lib.h index 6214c81cc693b86c3de7235d4681c11bb22f2be7..2dc1650bfd719208ef5e182b0b79f76e5f12ff55 100644 --- a/targets/ARCH/COMMON/common_lib.h +++ b/targets/ARCH/COMMON/common_lib.h @@ -207,6 +207,15 @@ typedef struct { int iq_rxrescale; //! Configuration file for LMS7002M char *configFilename; +#if defined(USRP_REC_PLAY) + unsigned short sf_mode; // 1=record, 2=replay + char sf_filename[1024]; // subframes file path + unsigned int sf_max; // max number of recorded subframes + unsigned int sf_loops; // number of loops in replay mode + unsigned int sf_read_delay; // read delay in replay mode + unsigned int sf_write_delay; // write delay in replay mode + unsigned int eth_mtu; // ethernet MTU +#endif } openair0_config_t; /*! \brief RF mapping */ diff --git a/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp b/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp index 30faf8d2b7aefc890973ee59da70274bd203dc0a..8b62a8e69855a36410e191c6832aa91009170906 100644 --- a/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp +++ b/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.cpp @@ -270,11 +270,53 @@ static int sync_to_gps(openair0_device *device) return EXIT_SUCCESS; } +#if defined(USRP_REC_PLAY) +#include "usrp_lib.h" +static FILE *pFile = NULL; +int mmapfd = 0; +struct stat sb; +iqrec_t *ms_sample = NULL; // memory for all subframes +unsigned int nb_samples = 0; +unsigned int cur_samples = 0; +int64_t wrap_count = 0; +int64_t wrap_ts = 0; +unsigned int u_sf_mode = 0; // 1=record, 2=replay +unsigned int u_sf_record = 0; // record mode +unsigned int u_sf_replay = 0; // replay mode +char u_sf_filename[1024]; // subframes file path +unsigned int u_sf_max = DEF_NB_SF; // max number of recorded subframes +unsigned int u_sf_loops = DEF_SF_NB_LOOP; // number of loops in replay mode +unsigned int u_sf_read_delay = DEF_SF_DELAY_READ; // read delay in replay mode +unsigned int u_sf_write_delay = DEF_SF_DELAY_WRITE; // write delay in replay mode +char *tmp_filename[1]; // use an array of pointer (libconfig does not seems to work with char array yet) + +char config_opt_sf_file[] = CONFIG_OPT_SF_FILE; +char config_def_sf_file[] = DEF_SF_FILE; +char config_hlp_sf_file[] = CONFIG_HLP_SF_FILE; +char config_opt_sf_rec[] = CONFIG_OPT_SF_REC; +char config_hlp_sf_rec[] = CONFIG_HLP_SF_REC; +char config_opt_sf_rep[] = CONFIG_OPT_SF_REP; +char config_hlp_sf_rep[] = CONFIG_HLP_SF_REP; +char config_opt_sf_max[] = CONFIG_OPT_SF_MAX; +char config_hlp_sf_max[] = CONFIG_HLP_SF_MAX; +char config_opt_sf_loops[] = CONFIG_OPT_SF_LOOPS; +char config_hlp_sf_loops[] = CONFIG_HLP_SF_LOOPS; +char config_opt_sf_rdelay[] = CONFIG_OPT_SF_RDELAY; +char config_hlp_sf_rdelay[] = CONFIG_HLP_SF_RDELAY; +char config_opt_sf_wdelay[] = CONFIG_OPT_SF_WDELAY; +char config_hlp_sf_wdelay[] = CONFIG_HLP_SF_WDELAY; + +#endif + /*! \brief Called to start the USRP transceiver. Return 0 if OK, < 0 if error @param device pointer to the device structure specific to the RF hardware target */ static int trx_usrp_start(openair0_device *device) { +#if defined(USRP_REC_PLAY) + if (u_sf_mode != 2) { // not replay mode +#endif + usrp_state_t *s = (usrp_state_t*)device->priv; @@ -313,12 +355,21 @@ static int trx_usrp_start(openair0_device *device) { s->rx_count = 0; s->tx_count = 0; s->rx_timestamp = 0; +#if defined(USRP_REC_PLAY) + } +#endif return 0; } /*! \brief Terminate operation of the USRP transceiver -- free all associated resources * \param device the hardware to use */ static void trx_usrp_end(openair0_device *device) { +#if defined(USRP_REC_PLAY) // For some ugly reason, this can be called several times... + static int done = 0; + if (done == 1) return; + done = 1; + if (u_sf_mode != 2) { // not subframes replay +#endif usrp_state_t *s = (usrp_state_t*)device->priv; s->rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); @@ -326,7 +377,45 @@ static void trx_usrp_end(openair0_device *device) { s->tx_md.end_of_burst = true; s->tx_stream->send("", 0, s->tx_md); s->tx_md.end_of_burst = false; - +#if defined(USRP_REC_PLAY) + } +#endif +#if defined(USRP_REC_PLAY) + if (u_sf_mode == 1) { // subframes store + pFile = fopen (u_sf_filename,"wb+"); + if (pFile == NULL) { + std::cerr << "Cannot open " << u_sf_filename << std::endl; + } else { + unsigned int i = 0; + unsigned int modu = 0; + if ((modu = nb_samples % 10) != 0) { + nb_samples -= modu; // store entire number of frames + } + std::cerr << "Writing " << nb_samples << " subframes to " << u_sf_filename << " ..." << std::endl; + for (i = 0; i < nb_samples; i++) { + fwrite(ms_sample+i, sizeof(unsigned char), sizeof(iqrec_t), pFile); + } + fclose (pFile); + std::cerr << "File " << u_sf_filename << " closed." << std::endl; + } + } + if (u_sf_mode == 1) { // record + if (ms_sample != NULL) { + free((void*)ms_sample); + ms_sample = NULL; + } + } + if (u_sf_mode == 2) { // replay + if (ms_sample != MAP_FAILED) { + munmap(ms_sample, sb.st_size); + ms_sample = NULL; + } + if (mmapfd != 0) { + close(mmapfd); + mmapfd = 0; + } + } +#endif } /*! \brief Called to send samples to the USRP RF target @@ -339,6 +428,9 @@ static void trx_usrp_end(openair0_device *device) { */ static int trx_usrp_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) { int ret=0; +#if defined(USRP_REC_PLAY) + if (u_sf_mode != 2) { // not replay mode +#endif usrp_state_t *s = (usrp_state_t*)device->priv; int nsamps2; // aligned to upper 32 or 16 byte boundary @@ -405,6 +497,15 @@ static int trx_usrp_write(openair0_device *device, openair0_timestamp timestamp, if (ret != nsamps) LOG_E(PHY,"[xmit] tx samples %d != %d\n",ret,nsamps); +#if defined(USRP_REC_PLAY) + } else { + struct timespec req; + req.tv_sec = 0; + req.tv_nsec = u_sf_write_delay * 1000; + nanosleep(&req, NULL); + ret = nsamps; + } +#endif return ret; } @@ -421,9 +522,12 @@ static int trx_usrp_write(openair0_device *device, openair0_timestamp timestamp, * \returns the number of sample read */ static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc) { - usrp_state_t *s = (usrp_state_t*)device->priv; - int samples_received=0,i,j; - int nsamps2; // aligned to upper 32 or 16 byte boundary + usrp_state_t *s = (usrp_state_t*)device->priv; + int samples_received=0,i,j; + int nsamps2; // aligned to upper 32 or 16 byte boundary +#if defined(USRP_REC_PLAY) + if (u_sf_mode != 2) { // not replay mode +#endif #if defined(__x86_64) || defined(__i386__) #ifdef __AVX2__ nsamps2 = (nsamps+7)>>3; @@ -490,7 +594,45 @@ static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp s->rx_count += nsamps; s->rx_timestamp = s->rx_md.time_spec.to_ticks(s->sample_rate); *ptimestamp = s->rx_timestamp; - return samples_received; +#if defined (USRP_REC_PLAY) + } +#endif +#if defined(USRP_REC_PLAY) + if (u_sf_mode == 1) { // record mode + // Copy subframes to memory (later dump on a file) + if (nb_samples < u_sf_max) { + (ms_sample+nb_samples)->header = BELL_LABS_IQ_HEADER; + (ms_sample+nb_samples)->ts = *ptimestamp; + memcpy((ms_sample+nb_samples)->samples, buff[0], nsamps*4); + nb_samples++; + } + } else if (u_sf_mode == 2) { // replay mode + if (cur_samples == nb_samples) { + cur_samples = 0; + wrap_count++; + if (wrap_count == u_sf_loops) { + std::cerr << "USRP device terminating subframes replay mode after " << u_sf_loops << " loops." << std::endl; + return 0; // should make calling process exit + } + wrap_ts = wrap_count * (nb_samples * (((int)(device->openair0_cfg[0].sample_rate)) / 1000)); + } + if (cur_samples < nb_samples) { + *ptimestamp = (ms_sample[0].ts + (cur_samples * (((int)(device->openair0_cfg[0].sample_rate)) / 1000))) + wrap_ts; + if (cur_samples == 0) { + std::cerr << "starting subframes file with wrap_count=" << wrap_count << " wrap_ts=" << wrap_ts + << " ts=" << *ptimestamp << std::endl; + } + memcpy(buff[0], &ms_sample[cur_samples].samples[0], nsamps*4); + cur_samples++; + } + struct timespec req; + req.tv_sec = 0; + req.tv_nsec = u_sf_read_delay * 1000; + nanosleep(&req, NULL); + return nsamps; + } +#endif + return samples_received; } /*! \brief Compares two variables within precision @@ -684,12 +826,135 @@ int trx_usrp_reset_stats(openair0_device* device) { return(0); } +#if defined(USRP_REC_PLAY) +extern "C" { +/*! \brief Initializer for USRP record/playback config + * \param parameter array description + * \returns 0 on success + */ +int trx_usrp_recplay_config_init(paramdef_t *usrp_recplay_params) { + // --subframes-file + memcpy(usrp_recplay_params[0].optname, config_opt_sf_file, strlen(config_opt_sf_file)); + usrp_recplay_params[0].helpstr = config_hlp_sf_file; + usrp_recplay_params[0].paramflags=PARAMFLAG_NOFREE; + usrp_recplay_params[0].strptr=(char **)&tmp_filename[0]; + usrp_recplay_params[0].defstrval = NULL; + usrp_recplay_params[0].type=TYPE_STRING; + usrp_recplay_params[0].numelt=sizeof(u_sf_filename); + // --subframes-record + memcpy(usrp_recplay_params[1].optname, config_opt_sf_rec, strlen(config_opt_sf_rec)); + usrp_recplay_params[1].helpstr = config_hlp_sf_rec; + usrp_recplay_params[1].paramflags=PARAMFLAG_BOOL; + usrp_recplay_params[1].uptr=&u_sf_record; + usrp_recplay_params[1].defuintval=0; + usrp_recplay_params[1].type=TYPE_UINT; + usrp_recplay_params[1].numelt=0; + // --subframes-replay + memcpy(usrp_recplay_params[2].optname, config_opt_sf_rep, strlen(config_opt_sf_rep)); + usrp_recplay_params[2].helpstr = config_hlp_sf_rep; + usrp_recplay_params[2].paramflags=PARAMFLAG_BOOL; + usrp_recplay_params[2].uptr=&u_sf_replay; + usrp_recplay_params[2].defuintval=0; + usrp_recplay_params[2].type=TYPE_UINT; + usrp_recplay_params[2].numelt=0; + // --subframes-max + memcpy(usrp_recplay_params[3].optname, config_opt_sf_max, strlen(config_opt_sf_max)); + usrp_recplay_params[3].helpstr = config_hlp_sf_max; + usrp_recplay_params[3].paramflags=0; + usrp_recplay_params[3].uptr=&u_sf_max; + usrp_recplay_params[3].defuintval=DEF_NB_SF; + usrp_recplay_params[3].type=TYPE_UINT; + usrp_recplay_params[3].numelt=0; + // --subframes-loops + memcpy(usrp_recplay_params[4].optname, config_opt_sf_loops, strlen(config_opt_sf_loops)); + usrp_recplay_params[4].helpstr = config_hlp_sf_loops; + usrp_recplay_params[4].paramflags=0; + usrp_recplay_params[4].uptr=&u_sf_loops; + usrp_recplay_params[4].defuintval=DEF_SF_NB_LOOP; + usrp_recplay_params[4].type=TYPE_UINT; + usrp_recplay_params[4].numelt=0; + // --subframes-read-delay + memcpy(usrp_recplay_params[5].optname, config_opt_sf_rdelay, strlen(config_opt_sf_rdelay)); + usrp_recplay_params[5].helpstr = config_hlp_sf_rdelay; + usrp_recplay_params[5].paramflags=0; + usrp_recplay_params[5].uptr=&u_sf_read_delay; + usrp_recplay_params[5].defuintval=DEF_SF_DELAY_READ; + usrp_recplay_params[5].type=TYPE_UINT; + usrp_recplay_params[5].numelt=0; + // --subframes-write-delay + memcpy(usrp_recplay_params[6].optname, config_opt_sf_wdelay, strlen(config_opt_sf_wdelay)); + usrp_recplay_params[6].helpstr = config_hlp_sf_wdelay; + usrp_recplay_params[6].paramflags=0; + usrp_recplay_params[6].uptr=&u_sf_write_delay; + usrp_recplay_params[6].defuintval=DEF_SF_DELAY_WRITE; + usrp_recplay_params[6].type=TYPE_UINT; + usrp_recplay_params[6].numelt=0; + + return 0; // always ok +} +} +#endif + extern "C" { /*! \brief Initialize Openair USRP target. It returns 0 if OK * \param device the hardware to use * \param openair0_cfg RF frontend parameters set by application */ int device_init(openair0_device* device, openair0_config_t *openair0_cfg) { +#if defined(USRP_REC_PLAY) + paramdef_t usrp_recplay_params[7]; + // to check + static int done = 0; + if (done == 1) { + return 0; + } // prevent from multiple init + done = 1; + // end to check + memset(usrp_recplay_params, 0, 7*sizeof(paramdef_t)); + memset(&u_sf_filename[0], 0, 1024); + tmp_filename[0] = u_sf_filename; + if (trx_usrp_recplay_config_init(usrp_recplay_params) != 0) { + std::cerr << "USRP device record/replay mode configuration error exiting" << std::endl; + return -1; + } + config_process_cmdline(usrp_recplay_params,sizeof(usrp_recplay_params)/sizeof(paramdef_t),NULL); + + if (strlen(tmp_filename[0]) != 0) { + (void) strcpy(u_sf_filename, tmp_filename[0]); + } else { + (void) strcpy(u_sf_filename, DEF_SF_FILE); + } + + if (u_sf_replay == 1) u_sf_mode = 2; + if (u_sf_record == 1) u_sf_mode = 1; + + if (u_sf_mode == 2) { + // Replay subframes from from file + int bw_gain_adjust=0; + device->openair0_cfg = openair0_cfg; + device->type = USRP_B200_DEV; + openair0_cfg[0].rx_gain_calib_table = calib_table_b210_38; + bw_gain_adjust=1; + openair0_cfg[0].tx_sample_advance = 80; + openair0_cfg[0].tx_bw = 20e6; + openair0_cfg[0].rx_bw = 20e6; + openair0_cfg[0].iq_txshift = 4;//shift + openair0_cfg[0].iq_rxrescale = 15;//rescale iqs + set_rx_gain_offset(&openair0_cfg[0],0,bw_gain_adjust); + device->priv = NULL; + device->trx_start_func = trx_usrp_start; + device->trx_write_func = trx_usrp_write; + device->trx_read_func = trx_usrp_read; + device->trx_get_stats_func = trx_usrp_get_stats; + device->trx_reset_stats_func = trx_usrp_reset_stats; + device->trx_end_func = trx_usrp_end; + device->trx_stop_func = trx_usrp_stop; + device->trx_set_freq_func = trx_usrp_set_freq; + device->trx_set_gains_func = trx_usrp_set_gains; + device->openair0_cfg = openair0_cfg; + std::cerr << "USRP device initialized in subframes replay mode for " << u_sf_loops << " loops." << std::endl; + } else { +#endif uhd::set_thread_priority_safe(1.0); usrp_state_t *s = (usrp_state_t*)calloc(sizeof(usrp_state_t),1); @@ -705,6 +970,11 @@ extern "C" { int vers=0,subvers=0,subsubvers=0; int bw_gain_adjust=0; +#if defined(USRP_REC_PLAY) + if (u_sf_mode == 1) { + std::cerr << "USRP device initialized in subframes record mode" << std::endl; + } +#endif sscanf(uhd::get_version_string().c_str(),"%d.%d.%d",&vers,&subvers,&subsubvers); LOG_I(PHY,"Checking for USRPs : UHD %s (%d.%d.%d)\n", uhd::get_version_string().c_str(),vers,subvers,subsubvers); @@ -742,7 +1012,9 @@ extern "C" { //s->usrp->set_master_clock_rate(usrp_master_clock); openair0_cfg[0].rx_gain_calib_table = calib_table_x310; - +#if defined(USRP_REC_PLAY) + std::cerr << "-- Using calibration table: calib_table_x310" << std::endl; // Bell Labs info +#endif switch ((int)openair0_cfg[0].sample_rate) { case 30720000: // from usrp_time_offset @@ -801,9 +1073,15 @@ extern "C" { if ((vers == 3) && (subvers == 9) && (subsubvers>=2)) { openair0_cfg[0].rx_gain_calib_table = calib_table_b210; bw_gain_adjust=0; +#if defined(USRP_REC_PLAY) + std::cerr << "-- Using calibration table: calib_table_b210" << std::endl; // Bell Labs info +#endif } else { openair0_cfg[0].rx_gain_calib_table = calib_table_b210_38; bw_gain_adjust=1; +#if defined(USRP_REC_PLAY) + std::cerr << "-- Using calibration table: calib_table_b210_38" << std::endl; // Bell Labs info +#endif } switch ((int)openair0_cfg[0].sample_rate) { @@ -965,6 +1243,45 @@ extern "C" { } } +#if defined(USRP_REC_PLAY) + } +#endif +#if defined(USRP_REC_PLAY) + if (u_sf_mode == 1) { // record mode + ms_sample = (iqrec_t*) malloc(u_sf_max * sizeof(iqrec_t)); + if (ms_sample == NULL) { + std::cerr<< "Memory allocation failed for subframe record or replay mode." << std::endl; + exit(-1); + } + memset(ms_sample, 0, u_sf_max * BELL_LABS_IQ_BYTES_PER_SF); + } + if (u_sf_mode == 2) { + // use mmap + mmapfd = open(u_sf_filename, O_RDONLY | O_LARGEFILE); + if (mmapfd != 0) { + fstat(mmapfd, &sb); + std::cerr << "Loading subframes using mmap() from " << u_sf_filename << " size=" << (uint64_t)sb.st_size << " bytes ..." << std::endl; + ms_sample = (iqrec_t*) mmap(NULL, sb.st_size, PROT_WRITE, MAP_PRIVATE, mmapfd, 0); + if (ms_sample != MAP_FAILED) { + nb_samples = (sb.st_size / sizeof(iqrec_t)); + int aligned = (((unsigned long)ms_sample & 31) == 0)? 1:0; + std::cerr<< "Loaded "<< nb_samples << " subframes." << std::endl; + if (aligned == 0) { + std::cerr<< "mmap address is not 32 bytes aligned, exiting." << std::endl; + close(mmapfd); + exit(-1); + } + } else { + std::cerr << "Cannot mmap file, exiting." << std::endl; + close(mmapfd); + exit(-1); + } + } else { + std::cerr << "Cannot open " << u_sf_filename << " , exiting." << std::endl; + exit(-1); + } + } +#endif return 0; } } diff --git a/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.h b/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.h new file mode 100644 index 0000000000000000000000000000000000000000..38635712734fa6d21a20078d03899f3b9c8393d6 --- /dev/null +++ b/targets/ARCH/USRP/USERSPACE/LIB/usrp_lib.h @@ -0,0 +1,89 @@ +#ifndef __USRP_LIB_H +#define __USRP_LIB_H +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +/** usrp_lib.h + * + * \author: bruno.mongazon-cazavet@nokia-bell-labs.com + */ + +#if defined (USRP_REC_PLAY) + +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "common/config/config_paramdesc.h" +#include "common/config/config_userapi.h" + +#define BELL_LABS_IQ_HEADER 0xabababababababab +#define BELL_LABS_IQ_PER_SF 7680 // Up to 5MHz bw for now +#define BELL_LABS_IQ_BYTES_PER_SF (BELL_LABS_IQ_PER_SF * 4) +typedef struct { + int64_t header; + int64_t ts; + int64_t rfu1; + int64_t rfu2; // pad for 256 bits alignement required by AVX2 + unsigned char samples[BELL_LABS_IQ_BYTES_PER_SF]; // iq's for one subframe +} iqrec_t; +#define DEF_NB_SF 120000 // default nb of sf or ms to capture (2 minutes at 5MHz) +#define DEF_SF_FILE "/home/nokia/iqfile" // default subframes file name +#define DEF_SF_DELAY_READ 860 // default read delay µs (860=real) +#define DEF_SF_DELAY_WRITE 15 // default write delay µs (15=real) +#define DEF_SF_NB_LOOP 5 // default nb loops + + +/* help strings definition for command line options, used in CMDLINE_XXX_DESC macros and printed when -h option is used */ +#define CONFIG_HLP_SF_FILE "Path of the file used for subframes record or replay" +#define CONFIG_HLP_SF_REC "Record subframes from USRP driver into a file for later replay" +#define CONFIG_HLP_SF_REP "Replay subframes into USRP driver from a file" +#define CONFIG_HLP_SF_MAX "Maximum count of subframes to be recorded in subframe file" +#define CONFIG_HLP_SF_LOOPS "Number of loops to replay of the entire subframes file" +#define CONFIG_HLP_SF_RDELAY "Delay in microseconds to read a subframe in replay mode" +#define CONFIG_HLP_SF_WDELAY "Delay in microseconds to write a subframe in replay mode" + +/* keyword strings for command line options, used in CMDLINE_XXX_DESC macros and printed when -h option is used */ +#define CONFIG_OPT_SF_FILE "subframes-file" +#define CONFIG_OPT_SF_REC "subframes-record" +#define CONFIG_OPT_SF_REP "subframes-replay" +#define CONFIG_OPT_SF_MAX "subframes-max" +#define CONFIG_OPT_SF_LOOPS "subframes-loops" +#define CONFIG_OPT_SF_RDELAY "subframes-read-delay" +#define CONFIG_OPT_SF_WDELAY "subframes-write-delay" + +/* For information only - the macro is not usable in C++ */ +/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ +/* command line parameters for USRP record/playback */ +/* optname helpstr paramflags XXXptr defXXXval type numelt */ +/*---------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ +#define USRP_RECPLAY_PARAMS_DESC { \ +{"subframes-file", CONFIG_HLP_SF_FILE, 0, strptr:(char **)&u_sf_filename, defstrval:DEF_SF_FILE, TYPE_STRING, sizeof(u_sf_filename)}, \ +{"subframes-record", CONFIG_HLP_SF_REC, PARAMFLAG_BOOL, uptr:&u_sf_record, defuintval:0, TYPE_UINT, 0}, \ +{"subframes-replay", CONFIG_HLP_SF_REP, PARAMFLAG_BOOL, uptr:&u_sf_replay, defuintval:0, TYPE_UINT, 0}, \ +{"subframes-max", CONFIG_HLP_SF_MAX, 0, uptr:&u_sf_max, defintval:DEF_NB_SF, TYPE_UINT, 0}, \ +{"subframes-loops", CONFIG_HLP_SF_LOOPS, 0, uptr:&u_sf_loops, defintval:DEF_SF_NB_LOOP, TYPE_UINT, 0}, \ +{"subframes-read-delay", CONFIG_HLP_SF_RDELAY, 0, uptr:&u_sf_read_delay, defintval:DEF_SF_DELAY_READ, TYPE_UINT, 0}, \ +{"subframes-write-delay", CONFIG_HLP_SF_WDELAY, 0, uptr:&u_sf_write_delay, defintval:DEF_SF_DELAY_WRITE, TYPE_UINT, 0}, \ +} +#endif // BELL_LABS_MUST +#endif // __USRP_LIB_H +