Commit 396cadfc authored by Florian Kaltenberger's avatar Florian Kaltenberger

adding API for USRP function issue_stream_cmd so that we can do timed reads in...

adding API for USRP function issue_stream_cmd so that we can do timed reads in the nr-ru. This functionality is only enabled using a new flag use_single_antenna_port_for_tdd in openair0_cfg

this flaf will also put TX and RX on the same TRX antenna port

further usrp time now synchronized on pps, even when not using gps
parent d15d86cf
......@@ -625,55 +625,80 @@ void *emulatedRF_thread(void *param) {
void rx_rf(RU_t *ru,int *frame,int *slot) {
RU_proc_t *proc = &ru->proc;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
nfapi_nr_config_request_scf_t *cfg = &ru->gNB_list[0]->gNB_config;
void *rxp[ru->nb_rx];
unsigned int rxs;
unsigned int rxs, siglen;
int i;
uint32_t samples_per_slot = fp->get_samples_per_slot(*slot,fp);
openair0_timestamp ts,old_ts;
openair0_timestamp ts;
AssertFatal(*slot<fp->slots_per_frame && *slot>=0, "slot %d is illegal (%d)\n",*slot,fp->slots_per_frame);
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 1 );
for (i=0; i<ru->nb_rx; i++)
rxp[i] = (void *)&ru->common.rxdata[i][fp->get_samples_slot_timestamp(*slot,fp,0)];
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 1 );
old_ts = proc->timestamp_rx;
LOG_D(PHY,"Reading %d samples for slot %d (%p)\n",samples_per_slot,*slot,rxp[0]);
//when the USRP starts, it initializes the timestamp to 0. We wait 1 frames until we program the first rx.
if (proc->first_rx==1) {
proc->timestamp_rx = fp->samples_per_frame;
proc->first_rx = 0;
}
else {
//we always advance the timestamp by samples_per_slot, even if we have not read the (full) slot. This is to keep the timestamp updated even when there is no RX.
proc->timestamp_rx += fp->get_samples_per_slot(*slot%fp->slots_per_frame,fp);
}
int slot_type = nr_slot_select(cfg,*frame,*slot%fp->slots_per_frame);
if (slot_type == NR_UPLINK_SLOT || slot_type == NR_MIXED_SLOT || IS_SOFTMODEM_RFSIM) {
if (slot_type == NR_MIXED_SLOT) {
uint16_t rxsymb = 0;
for(uint16_t symbol_count =0;symbol_count<fp->symbols_per_slot;symbol_count++) {
if (cfg->tdd_table.max_tdd_periodicity_list[*slot].max_num_of_symbol_per_slot_list[symbol_count].slot_config.value==1)
rxsymb++;
}
AssertFatal(rxsymb>0,"illegal rxsymb %d\n",rxsymb);
//TODO: this has to be adapted for numerology!=1
siglen = (fp->ofdm_symbol_size + fp->nb_prefix_samples0) + (rxsymb - 1) * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
proc->timestamp_rx += fp->get_samples_per_slot(*slot%fp->slots_per_frame,fp) - siglen;
//TODO: the 3rd parameter has to be adapted for arbitrary TDD configurations
ru->rfdevice.trx_issue_stream_cmd(&ru->rfdevice,
proc->timestamp_rx,
siglen+2*fp->get_samples_per_slot(*slot,fp));
}
else {
siglen = samples_per_slot;
}
LOG_D(PHY,"Reading %d samples for slot %d at timestamp %ld\n",siglen,*slot,proc->timestamp_rx);
if(emulate_rf) {
wait_on_condition(&proc->mutex_emulateRF,&proc->cond_emulateRF,&proc->instance_cnt_emulateRF,"emulatedRF_thread");
release_thread(&proc->mutex_emulateRF,&proc->instance_cnt_emulateRF,"emulatedRF_thread");
rxs = samples_per_slot;
ts = old_ts + rxs;
} else {
rxs = siglen;
}
else {
rxs = ru->rfdevice.trx_read_func(&ru->rfdevice,
&ts,
rxp,
samples_per_slot,
siglen,
ru->nb_rx);
}
//AssertFatal(rxs == siglen,"rx_rf: Asked for %d samples, got %d from USRP\n",siglen,rxs);
if (rxs != siglen) LOG_E(PHY, "rx_rf: Asked for %d samples, got %d from USRP\n",siglen,rxs);
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 0 );
proc->timestamp_rx = ts-ru->ts_offset;
}
//AssertFatal(rxs == fp->samples_per_subframe,
//"rx_rf: Asked for %d samples, got %d from USRP\n",fp->samples_per_subframe,rxs);
if (rxs != samples_per_slot) LOG_E(PHY, "rx_rf: Asked for %d samples, got %d from USRP\n",samples_per_slot,rxs);
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 0 );
if (proc->first_rx == 1) {
ru->ts_offset = proc->timestamp_rx;
proc->timestamp_rx = 0;
} else {
if (proc->timestamp_rx - old_ts != fp->get_samples_per_slot((*slot-1)%fp->slots_per_frame,fp)) {
LOG_D(PHY,"rx_rf: rfdevice timing drift of %"PRId64" samples (ts_off %"PRId64")\n",proc->timestamp_rx - old_ts - samples_per_slot,ru->ts_offset);
ru->ts_offset += (proc->timestamp_rx - old_ts - samples_per_slot);
proc->timestamp_rx = ts-ru->ts_offset;
}
}
proc->frame_rx = *frame;
proc->tti_rx = *slot;
proc->frame_rx = (proc->timestamp_rx / (fp->samples_per_subframe*10))&1023;
uint32_t idx_sf = proc->timestamp_rx / fp->samples_per_subframe;
proc->tti_rx = (idx_sf * fp->slots_per_subframe + (int)round((float)(proc->timestamp_rx % fp->samples_per_subframe) / fp->samples_per_slot0))%(fp->slots_per_frame);
// synchronize first reception to frame 0 subframe 0
LOG_D(PHY,"RU %d/%d TS %llu (off %d), frame %d, slot %d.%d / %d\n",
ru->idx,
......@@ -687,29 +712,9 @@ void rx_rf(RU_t *ru,int *frame,int *slot) {
VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_RX0_RU, proc->tti_rx );
}
if (proc->first_rx == 0) {
if (proc->tti_rx != *slot) {
LOG_E(PHY,"Received Timestamp (%llu) doesn't correspond to the time we think it is (proc->tti_rx %d, slot %d)\n",(long long unsigned int)proc->timestamp_rx,proc->tti_rx,*slot);
exit_fun("Exiting");
}
if (proc->frame_rx != *frame) {
LOG_E(PHY,"Received Timestamp (%llu) doesn't correspond to the time we think it is (proc->frame_rx %d frame %d)\n",(long long unsigned int)proc->timestamp_rx,proc->frame_rx,*frame);
exit_fun("Exiting");
}
} else {
proc->first_rx = 0;
*frame = proc->frame_rx;
*slot = proc->tti_rx;
}
//printf("timestamp_rx %lu, frame %d(%d), subframe %d(%d)\n",ru->timestamp_rx,proc->frame_rx,frame,proc->tti_rx,subframe);
VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TS, proc->timestamp_rx&0xffffffff );
if (rxs != samples_per_slot) {
//exit_fun( "problem receiving samples" );
LOG_E(PHY, "problem receiving samples\n");
}
}
......@@ -735,7 +740,7 @@ void tx_rf(RU_t *ru,int frame,int slot, uint64_t timestamp) {
if(slot_type == NR_MIXED_SLOT) {
txsymb = 0;
for(int symbol_count =0;symbol_count<NR_NUMBER_OF_SYMBOLS_PER_SLOT;symbol_count++) {
for(int symbol_count =0;symbol_count<fp->symbols_per_slot;symbol_count++) {
if (cfg->tdd_table.max_tdd_periodicity_list[slot].max_num_of_symbol_per_slot_list[symbol_count].slot_config.value==0)
txsymb++;
}
......@@ -920,6 +925,7 @@ int wakeup_synch(RU_t *ru) {
return(0);
}
/*
void do_ru_synch(RU_t *ru) {
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
RU_proc_t *proc = &ru->proc;
......@@ -991,7 +997,7 @@ void do_ru_synch(RU_t *ru) {
ru->rfdevice.trx_set_freq_func(&ru->rfdevice,ru->rfdevice.openair0_cfg,0);
}
*/
void wakeup_gNB_L1s(RU_t *ru) {
......@@ -1150,6 +1156,9 @@ void fill_rf_config(RU_t *ru, char *rf_config_file) {
cfg->num_rb_dl=N_RB;
cfg->tx_num_channels=ru->nb_tx;
cfg->rx_num_channels=ru->nb_rx;
cfg->clock_source=get_softmodem_params()->clock_source;
cfg->use_single_antenna_port_for_tdd=1; //TODO: make this parameterzable
for (i=0; i<ru->nb_tx; i++) {
if (ru->if_frequency == 0) {
......@@ -1481,7 +1490,7 @@ void *ru_thread( void *param ) {
} else LOG_I(PHY,"RU %d no asynch_south interface\n",ru->idx);
// if this is a slave RRU, try to synchronize on the DL frequency
if ((ru->is_slave) && (ru->if_south == LOCAL_RF)) do_ru_synch(ru);
//if ((ru->is_slave) && (ru->if_south == LOCAL_RF)) do_ru_synch(ru);
}
pthread_mutex_lock(&proc->mutex_FH1);
......
......@@ -235,6 +235,8 @@ typedef struct {
double tx_sample_rate;
//! check for threequarter sampling rate
int8_t threequarter_fs;
//! flag to indicate use of same antenna port for RX and TX (for TDD)
int8_t use_single_antenna_port_for_tdd;
} openair0_config_t;
/*! \brief RF mapping */
......@@ -357,6 +359,8 @@ struct openair0_device_t {
*/
int (*trx_read_func)(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps,int antenna_id);
int (*trx_issue_stream_cmd)(openair0_device *device, openair0_timestamp ptimestamp, int nsamps);
/*! \brief print the device statistics
* \param device the hardware to use
* \returns 0 on success
......
......@@ -104,7 +104,8 @@ typedef struct {
int64_t rx_count;
int wait_for_first_pps;
int use_gps;
int first_tx;
//int first_tx;
//int first_rx;
//! timestamp of RX packet
openair0_timestamp rx_timestamp;
} usrp_state_t;
......@@ -278,31 +279,34 @@ static int trx_usrp_start(openair0_device *device) {
// set pin 5 (Shutdown LNA) to 1 when the radio is transmitting and receiveing (ATR_XX)
// (we use full duplex here, because our RX is on all the time - this might need to change later)
s->usrp->set_gpio_attr("FP0", "ATR_XX", (1<<5), 0x7f);
// set the output pins to 0
// set the output pins to 1
s->usrp->set_gpio_attr("FP0", "OUT", 7<<7, 0xf80);
// init recv and send streaming
uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
LOG_I(HW,"Time in secs now: %llu \n", s->usrp->get_time_now().to_ticks(s->sample_rate));
LOG_I(HW,"Time in secs last pps: %llu \n", s->usrp->get_time_last_pps().to_ticks(s->sample_rate));
if (s->use_gps == 1 || device->openair0_cfg[0].time_source == external) {
s->wait_for_first_pps = 1;
cmd.time_spec = s->usrp->get_time_last_pps() + uhd::time_spec_t(1.0);
} else {
s->wait_for_first_pps = 0;
cmd.time_spec = s->usrp->get_time_now() + uhd::time_spec_t(0.005);
s->rx_count = 0;
s->tx_count = 0;
//s->first_tx = 1;
//s->first_rx = 1;
s->rx_timestamp = 0;
s->usrp->set_time_next_pps(uhd::time_spec_t(0.0));
// wait for the pps to change
uhd::time_spec_t time_last_pps = s->usrp->get_time_last_pps();
while (time_last_pps == s->usrp->get_time_last_pps()) {
boost::this_thread::sleep(boost::posix_time::milliseconds(1));
}
// init recv and send streaming command
if (device->openair0_cfg->use_single_antenna_port_for_tdd==0) {
uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
cmd.time_spec = uhd::time_spec_t(1.0);
cmd.stream_now = false; // start at constant delay
s->rx_stream->issue_stream_cmd(cmd);
/*s->tx_md.time_spec = cmd.time_spec + uhd::time_spec_t(1-(double)s->tx_forward_nsamps/s->sample_rate);
s->tx_md.has_time_spec = true;
s->tx_md.start_of_burst = true;
s->tx_md.end_of_burst = false;*/
s->rx_count = 0;
s->tx_count = 0;
s->rx_timestamp = 0;
}
// else the issue stream mode command needs to be called every TDD period
return 0;
}
/*! \brief Terminate operation of the USRP transceiver -- free all associated resources
......@@ -318,6 +322,15 @@ static void trx_usrp_end(openair0_device *device) {
return;
iqrecorder_end(device);
if (device->openair0_cfg->use_single_antenna_port_for_tdd==0) {
s->rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
//send a mini EOB packet
s->tx_md.end_of_burst = true;
s->tx_stream->send("", 0, s->tx_md);
s->tx_md.end_of_burst = false;
sleep(1);
}
}
......@@ -465,7 +478,6 @@ static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp
int16x8_t buff_tmp[2][nsamps2];
#endif
if (device->type == USRP_B200_DEV) {
if (cc>1) {
// receive multiple channels (e.g. RF A and RF B)
std::vector<void *> buff_ptrs;
......@@ -478,7 +490,7 @@ static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp
samples_received=0;
while (samples_received != nsamps) {
samples_received += s->rx_stream->recv(buff_tmp[0]+samples_received,
samples_received += s->rx_stream->recv((void*)((int32_t*)buff_tmp[0]+samples_received),
nsamps-samples_received, s->rx_md);
if ((s->wait_for_first_pps == 0) && (s->rx_md.error_code!=uhd::rx_metadata_t::ERROR_CODE_NONE))
......@@ -488,7 +500,6 @@ static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp
printf("sleep...\n"); //usleep(100);
}
}
if (samples_received == nsamps) s->wait_for_first_pps=0;
}
......@@ -514,22 +525,10 @@ static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp
#endif
}
}
} else if (device->type == USRP_X300_DEV) {
if (cc>1) {
// receive multiple channels (e.g. RF A and RF B)
std::vector<void *> buff_ptrs;
for (int i=0; i<cc; i++) buff_ptrs.push_back(buff[i]);
samples_received = s->rx_stream->recv(buff_ptrs, nsamps, s->rx_md,1.0);
} else {
// receive a single channel (e.g. from connector RF A)
samples_received = s->rx_stream->recv(buff[0], nsamps, s->rx_md,1.0);
}
}
if (samples_received < nsamps)
if (samples_received < nsamps) {
LOG_E(HW,"[recv] received %d samples out of %d\n",samples_received,nsamps);
}
if ( s->rx_md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE)
LOG_E(HW, "%s\n", s->rx_md.to_pp_string(true).c_str());
......@@ -551,6 +550,19 @@ static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp
return samples_received;
}
static int trx_usrp_issue_stream_cmd(openair0_device *device, openair0_timestamp ptimestamp, int nsamps){
usrp_state_t *s = (usrp_state_t *)device->priv;
uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE);
stream_cmd.num_samps = nsamps;
stream_cmd.stream_now = false;
stream_cmd.time_spec = uhd::time_spec_t::from_ticks(ptimestamp,s->sample_rate);
s->rx_stream->issue_stream_cmd(stream_cmd);
return 0;
}
/*! \brief Compares two variables within precision
* \param a first variable
* \param b second variable
......@@ -1088,11 +1100,17 @@ extern "C" {
s->tx_stream = s->usrp->get_tx_stream(stream_args_tx);
/* Setting TX/RX BW after streamers are created due to USRP calibration issue */
for(int i=0; i<((int) s->usrp->get_tx_num_channels()) && i<openair0_cfg[0].tx_num_channels; i++)
for(int i=0; i<((int) s->usrp->get_tx_num_channels()) && i<openair0_cfg[0].tx_num_channels; i++) {
s->usrp->set_tx_bandwidth(openair0_cfg[0].tx_bw,i);
if (device->openair0_cfg->use_single_antenna_port_for_tdd==1)
s->usrp->set_tx_antenna("TX/RX",i);
}
for(int i=0; i<((int) s->usrp->get_rx_num_channels()) && i<openair0_cfg[0].rx_num_channels; i++)
for(int i=0; i<((int) s->usrp->get_rx_num_channels()) && i<openair0_cfg[0].rx_num_channels; i++) {
s->usrp->set_rx_bandwidth(openair0_cfg[0].rx_bw,i);
if (device->openair0_cfg->use_single_antenna_port_for_tdd==1)
s->usrp->set_rx_antenna("TX/RX",i);
}
for (int i=0; i<openair0_cfg[0].rx_num_channels; i++) {
LOG_I(HW,"RX Channel %d\n",i);
......@@ -1116,6 +1134,7 @@ extern "C" {
LOG_I(HW,"Device timestamp: %f...\n", s->usrp->get_time_now().get_real_secs());
device->trx_write_func = trx_usrp_write;
device->trx_read_func = trx_usrp_read;
device->trx_issue_stream_cmd = trx_usrp_issue_stream_cmd;
s->sample_rate = openair0_cfg[0].sample_rate;
// TODO:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment