/* Require a 10% margin on the sample rate to ensure we can keep up, and * warn if this margin is violated */ static void check_samplerate(struct cli_state *s, struct rxtx_data *rxtx) { int status; uint64_t samplerate_min; /* Min required sample rate */ unsigned int samplerate_dev; /* Device's current sample rate */ unsigned int n_xfers, samp_per_buf; pthread_mutex_lock(&rxtx->data_mgmt.lock); n_xfers = (unsigned int)rxtx->data_mgmt.num_transfers; samp_per_buf = (unsigned int)rxtx->data_mgmt.samples_per_buffer; pthread_mutex_unlock(&rxtx->data_mgmt.lock); samplerate_min = (uint64_t)n_xfers * samp_per_buf; samplerate_min += (samplerate_min + 9) / 10; status = bladerf_get_sample_rate(s->dev, rxtx->module, &samplerate_dev); if (status < 0) { cli_err(s, "Error", "Failed read device's current sample rate. " "Unable to perform sanity check."); } else if (samplerate_dev < samplerate_min) { if (samplerate_min <= 40000000) { printf("\n Warning: The current sample rate may be too low. " "For %u transfers and\n" " %u samples per buffer, a sample rate >= %" PRIu64" Hz is\n recommended to avoid timeouts.\n\n", n_xfers, samp_per_buf, samplerate_min); } else { printf("\n Warning: The current configuraion with %u transfers and" "%u samples per buffer requires a sample rate above 40MHz.\n" "Timeouts will likely occur with these settings.\n", n_xfers, samp_per_buf); } } }
static int set_and_check(struct bladerf *dev, bladerf_module m, unsigned int rate) { int status; unsigned int actual, readback; status = bladerf_set_sample_rate(dev, m, rate, &actual); if (status != 0) { PR_ERROR("Failed to set sample rate: %s\n", bladerf_strerror(status)); return status; } status = bladerf_get_sample_rate(dev, m, &readback); if (status != 0) { PR_ERROR("Failed to read back sample rate: %s\n", bladerf_strerror(status)); return status; } return 0; }
/* Require a 10% margin on the sample rate to ensure we can keep up, and * warn if this margin is violated. * * We effectively ensuring: * * Min sample rate > (xfers / timeout period) * samples / buffer * ^ * 1 xfer = buffer --' */ static void check_samplerate(struct cli_state *s, struct rxtx_data *rxtx) { int status; uint64_t samplerate_min; /* Min required sample rate */ unsigned int samplerate_dev; /* Device's current sample rate */ unsigned int n_xfers, samp_per_buf, timeout_ms; MUTEX_LOCK(&rxtx->data_mgmt.lock); n_xfers = (unsigned int)rxtx->data_mgmt.num_transfers; samp_per_buf = (unsigned int)rxtx->data_mgmt.samples_per_buffer; timeout_ms = rxtx->data_mgmt.timeout_ms; MUTEX_UNLOCK(&rxtx->data_mgmt.lock); samplerate_min = (uint64_t)n_xfers * samp_per_buf * 1000 / timeout_ms; samplerate_min += (samplerate_min + 9) / 10; status = bladerf_get_sample_rate(s->dev, rxtx->module, &samplerate_dev); if (status < 0) { cli_err(s, "Error", "Failed read device's current sample rate. " "Unable to perform sanity check.\n"); } else if (samplerate_dev < samplerate_min) { if (samplerate_min <= 40000000) { printf("\n"); printf(" Warning: The current sample rate may be too low. For %u transfers,\n" " %u samples per buffer, and a %u ms timeout, a sample rate\n" " over %"PRIu64" Hz may be required. Alternatively, the 'timeout'\n" " parameter could be increased, but may yield underruns.\n\n", n_xfers, samp_per_buf, timeout_ms, samplerate_min); } else { printf("\n"); printf(" Warning: The current configuraion with %u transfers,\n" " %u samples per buffer, and a %u ms timeout requires a\n" " sample rate above 40MHz. Timeouts may occur with these settings.\n\n", n_xfers, samp_per_buf, timeout_ms); } } }
static int tx_task_exec_running(struct rxtx_data *tx, struct cli_state *s) { int status = 0; unsigned int samples_per_buffer; int16_t *tx_buffer; struct tx_params *tx_params = tx->params; unsigned int repeats_remaining; unsigned int delay_us; unsigned int delay_samples; unsigned int delay_samples_remaining; bool repeat_infinite; unsigned int timeout_ms; unsigned int sample_rate; enum state { INIT, READ_FILE, DELAY, PAD_TRAILING, DONE }; enum state state = INIT; /* Fetch the parameters required for the TX operation */ MUTEX_LOCK(&tx->param_lock); repeats_remaining = tx_params->repeat; delay_us = tx_params->repeat_delay; MUTEX_UNLOCK(&tx->param_lock); repeat_infinite = (repeats_remaining == 0); MUTEX_LOCK(&tx->data_mgmt.lock); samples_per_buffer = (unsigned int)tx->data_mgmt.samples_per_buffer; timeout_ms = tx->data_mgmt.timeout_ms; MUTEX_UNLOCK(&tx->data_mgmt.lock); status = bladerf_get_sample_rate(s->dev, tx->module, &sample_rate); if (status != 0) { set_last_error(&tx->last_error, ETYPE_BLADERF, status); return CLI_RET_LIBBLADERF; } /* Compute delay time as a sample count */ delay_samples = (unsigned int)((uint64_t)sample_rate * delay_us / 1000000); delay_samples_remaining = delay_samples; /* Allocate a buffer to hold each block of samples to transmit */ tx_buffer = (int16_t*) malloc(samples_per_buffer * 2 * sizeof(int16_t)); if (tx_buffer == NULL) { status = CLI_RET_MEM; set_last_error(&tx->last_error, ETYPE_ERRNO, errno == 0 ? ENOMEM : errno); } /* Keep writing samples while there is more data to send and no failures * have occurred */ while (state != DONE && status == 0) { unsigned char requests; unsigned int buffer_samples_remaining = samples_per_buffer; int16_t *tx_buffer_current = tx_buffer; /* Stop stream on STOP or SHUTDOWN, but only clear STOP. This will keep * the SHUTDOWN request around so we can read it when determining * our state transition */ requests = rxtx_get_requests(tx, RXTX_TASK_REQ_STOP); if (requests & (RXTX_TASK_REQ_STOP | RXTX_TASK_REQ_SHUTDOWN)) { break; } /* Keep adding to the buffer until it is full or a failure occurs */ while (buffer_samples_remaining > 0 && status == 0 && state != DONE) { size_t samples_populated = 0; switch (state) { case INIT: case READ_FILE: MUTEX_LOCK(&tx->file_mgmt.file_lock); /* Read from the input file */ samples_populated = fread(tx_buffer_current, 2 * sizeof(int16_t), buffer_samples_remaining, tx->file_mgmt.file); assert(samples_populated <= UINT_MAX); /* If the end of the file was reached, determine whether * to delay, re-read from the file, or pad the rest of the * buffer and finish */ if (feof(tx->file_mgmt.file)) { repeats_remaining--; if ((repeats_remaining > 0) || repeat_infinite) { if (delay_samples != 0) { delay_samples_remaining = delay_samples; state = DELAY; } } else { state = PAD_TRAILING; } /* Clear the EOF condition and rewind the file */ clearerr(tx->file_mgmt.file); rewind(tx->file_mgmt.file); } /* Check for errors */ else if (ferror(tx->file_mgmt.file)) { status = errno; set_last_error(&tx->last_error, ETYPE_ERRNO, status); } MUTEX_UNLOCK(&tx->file_mgmt.file_lock); break; case DELAY: /* Insert as many zeros as are necessary to realize the * specified repeat delay */ samples_populated = uint_min(buffer_samples_remaining, delay_samples_remaining); memset(tx_buffer_current, 0, samples_populated * 2 * sizeof(uint16_t)); delay_samples_remaining -= (unsigned int)samples_populated; if (delay_samples_remaining == 0) { state = READ_FILE; } break; case PAD_TRAILING: /* Populate the remainder of the buffer with zeros */ memset(tx_buffer_current, 0, buffer_samples_remaining * 2 * sizeof(uint16_t)); state = DONE; break; case DONE: default: break; } /* Advance the buffer pointer. * Remember, two int16_t's make up 1 sample in the SC16Q11 format */ buffer_samples_remaining -= (unsigned int)samples_populated; tx_buffer_current += (2 * samples_populated); } /* If there were no errors, transmit the data buffer */ if (status == 0) { bladerf_sync_tx(s->dev, tx_buffer, samples_per_buffer, NULL, timeout_ms); } } free(tx_buffer); return status; }