Moved sweep mode frequency computation into firmware.

Changed from long list of tuning frequencies to short list of ranges.
This commit is contained in:
Michael Ossmann
2017-02-07 13:57:49 -07:00
parent 9916920e11
commit e1b1dbd647
5 changed files with 179 additions and 70 deletions

View File

@ -31,31 +31,55 @@
#define MIN(x,y) ((x)<(y)?(x):(y))
#define MAX(x,y) ((x)>(y)?(x):(y))
#define FREQ_GRANULARITY 1000000
#define MIN_FREQ 1
#define MAX_FREQ 6000
#define MAX_FREQ_COUNT 1000
#define MAX_RANGES 10
#define THROWAWAY_BUFFERS 2
volatile bool start_sweep_mode = false;
static uint64_t sweep_freq;
bool odd = true;
static uint16_t frequencies[MAX_FREQ_COUNT];
static uint16_t frequency_count = 0;
static bool odd = true;
static uint16_t frequencies[MAX_RANGES * 2];
static unsigned char data[9 + MAX_RANGES * 2 * sizeof(frequencies[0])];
static uint16_t num_ranges = 0;
static uint32_t dwell_blocks = 0;
static uint32_t step_width = 0;
static uint32_t offset = 0;
static enum sweep_style style = LINEAR;
static uint16_t range = 0;
usb_request_status_t usb_vendor_request_init_sweep(
usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage)
{
uint32_t dwell_time;
uint32_t num_samples;
int i;
if (stage == USB_TRANSFER_STAGE_SETUP) {
dwell_time = (endpoint->setup.index << 16) | endpoint->setup.value;
dwell_blocks = dwell_time / 0x4000;
frequency_count = endpoint->setup.length / sizeof(uint16_t);
usb_transfer_schedule_block(endpoint->out, &frequencies,
num_samples = (endpoint->setup.index << 16) | endpoint->setup.value;
dwell_blocks = num_samples / 0x4000;
if(1 > dwell_blocks) {
return USB_REQUEST_STATUS_STALL;
}
num_ranges = (endpoint->setup.length - 9) / (2 * sizeof(frequencies[0]));
if((1 > num_ranges) || (MAX_RANGES < num_ranges)) {
return USB_REQUEST_STATUS_STALL;
}
usb_transfer_schedule_block(endpoint->out, &data,
endpoint->setup.length, NULL, NULL);
} else if (stage == USB_TRANSFER_STAGE_DATA) {
sweep_freq = frequencies[0];
set_freq(sweep_freq*FREQ_GRANULARITY);
step_width = ((uint32_t)(data[3]) << 24) | ((uint32_t)(data[2]) << 16)
| ((uint32_t)(data[1]) << 8) | data[0];
if(1 > step_width) {
return USB_REQUEST_STATUS_STALL;
}
offset = ((uint32_t)(data[7]) << 24) | ((uint32_t)(data[6]) << 16)
| ((uint32_t)(data[5]) << 8) | data[4];
style = data[8];
if(INTERLEAVED < style) {
return USB_REQUEST_STATUS_STALL;
}
for(i=0; i<(num_ranges*2); i++) {
frequencies[i] = ((uint16_t)(data[10+i*2]) << 8) + data[9+i*2];
}
sweep_freq = frequencies[0] * FREQ_GRANULARITY;
set_freq(sweep_freq + offset);
start_sweep_mode = true;
usb_transfer_schedule_ack(endpoint->in);
}
@ -65,7 +89,6 @@ usb_request_status_t usb_vendor_request_init_sweep(
void sweep_mode(void) {
unsigned int blocks_queued = 0;
unsigned int phase = 0;
unsigned int ifreq = 0;
uint8_t *buffer;
bool transfer = false;
@ -88,8 +111,16 @@ void sweep_mode(void) {
}
if (transfer) {
*(uint16_t*)buffer = 0x7F7F;
*(uint16_t*)(buffer+2) = sweep_freq;
*buffer = 0x7f;
*(buffer+1) = 0x7f;
*(buffer+2) = sweep_freq & 0xff;
*(buffer+3) = (sweep_freq >> 8) & 0xff;
*(buffer+4) = (sweep_freq >> 16) & 0xff;
*(buffer+5) = (sweep_freq >> 24) & 0xff;
*(buffer+6) = (sweep_freq >> 32) & 0xff;
*(buffer+7) = (sweep_freq >> 40) & 0xff;
*(buffer+8) = (sweep_freq >> 48) & 0xff;
*(buffer+9) = (sweep_freq >> 56) & 0xff;
if (blocks_queued > THROWAWAY_BUFFERS) {
usb_transfer_schedule_block(
&usb_endpoint_bulk_in,
@ -102,10 +133,27 @@ void sweep_mode(void) {
}
if ((dwell_blocks + THROWAWAY_BUFFERS) <= blocks_queued) {
if(++ifreq >= frequency_count)
ifreq = 0;
sweep_freq = frequencies[ifreq];
set_freq(sweep_freq*FREQ_GRANULARITY);
if(INTERLEAVED == style) {
if(!odd && ((sweep_freq + step_width) >= ((uint64_t)frequencies[1+range*2] * FREQ_GRANULARITY))) {
range = (range + 1) % num_ranges;
sweep_freq = frequencies[range*2] * FREQ_GRANULARITY;
} else {
if(odd) {
sweep_freq += step_width/4;
} else {
sweep_freq += 3*step_width/4;
}
}
odd = !odd;
} else {
if((sweep_freq + step_width) >= ((uint64_t)frequencies[1+range*2] * FREQ_GRANULARITY)) {
range = (range + 1) % num_ranges;
sweep_freq = frequencies[range*2] * FREQ_GRANULARITY;
} else {
sweep_freq += step_width;
}
}
set_freq(sweep_freq + offset);
blocks_queued = 0;
}
}

View File

@ -19,8 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#ifndef __USB_API_SCAN_H__
#define __USB_API_SCAN_H__
#ifndef __USB_API_SWEEP_H__
#define __USB_API_SWEEP_H__
#include <stdbool.h>
#include <usb_type.h>
@ -28,6 +28,11 @@
extern volatile bool start_sweep_mode;
enum sweep_style {
LINEAR = 0,
INTERLEAVED = 1,
};
usb_request_status_t usb_vendor_request_init_sweep(
usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage);

View File

@ -37,10 +37,6 @@
#include <inttypes.h>
#define _FILE_OFFSET_BITS 64
#define BLOCKS_PER_TRANSFER 16
#define SAMPLES_PER_BLOCK 16384
#define STEP_SIZE_IN_HZ 312500
#define FFT_SIZE 64
#ifndef bool
typedef int bool;
@ -99,9 +95,12 @@ int gettimeofday(struct timeval *tv, void* ignored) {
#define DEFAULT_BASEBAND_FILTER_BANDWIDTH (15000000) /* 5MHz default */
#define TUNE_STEP (DEFAULT_SAMPLE_RATE_HZ / FREQ_ONE_MHZ)
#define MAX_FREQ_COUNT 1000
#define OFFSET 7500000
#define DEFAULT_SAMPLE_COUNT 0x4000
#define BLOCKS_PER_TRANSFER 16
#define FFT_BIN_WIDTH_HZ 312500
#define FFT_SIZE 64
#if defined _WIN32
#define sleep(a) Sleep( (a*1000) )
@ -201,7 +200,8 @@ int rx_callback(hackrf_transfer* transfer) {
* write output to pipe
*/
int8_t* buf;
uint16_t frequency; /* in MHz */
uint8_t* ubuf;
uint64_t frequency; /* in Hz */
float float_freq;
int i, j;
@ -209,13 +209,16 @@ int rx_callback(hackrf_transfer* transfer) {
byte_count += transfer->valid_length;
buf = (int8_t*) transfer->buffer;
for(j=0; j<BLOCKS_PER_TRANSFER; j++) {
if(buf[0] == 0x7F && buf[1] == 0x7F) {
frequency = *(uint16_t*)&buf[2];
ubuf = (uint8_t*) buf;
if(ubuf[0] == 0x7F && ubuf[1] == 0x7F) {
frequency = ((uint64_t)(ubuf[9]) << 56) | ((uint64_t)(ubuf[8]) << 48) | ((uint64_t)(ubuf[7]) << 40)
| ((uint64_t)(ubuf[6]) << 32) | ((uint64_t)(ubuf[5]) << 24) | ((uint64_t)(ubuf[4]) << 16)
| ((uint64_t)(ubuf[3]) << 8) | ubuf[2];
} else {
buf += SAMPLES_PER_BLOCK;
break;
}
if(FREQ_MAX_MHZ < frequency) {
if((FREQ_MAX_MHZ * FREQ_ONE_MHZ) < frequency) {
buf += SAMPLES_PER_BLOCK;
break;
}
@ -236,6 +239,7 @@ int rx_callback(hackrf_transfer* transfer) {
}
if(binary_output) {
float_freq = frequency;
float_freq /= FREQ_ONE_MHZ;
fwrite(&float_freq, sizeof(float), 1, stdout);
fwrite(pwr, sizeof(float), fftSize, stdout);
} else {
@ -244,9 +248,9 @@ int rx_callback(hackrf_transfer* transfer) {
strftime(time_str, 50, "%Y-%m-%d, %H:%M:%S", fft_time);
printf("%s, %" PRIu64 ", %" PRIu64 ", %.2f, %u",
time_str,
(uint64_t)((FREQ_ONE_MHZ*frequency)-((DEFAULT_SAMPLE_RATE_HZ*3)/8)),
(uint64_t)((FREQ_ONE_MHZ*frequency)-(DEFAULT_SAMPLE_RATE_HZ/8)),
(float)STEP_SIZE_IN_HZ,
(uint64_t)(frequency),
(uint64_t)(frequency+DEFAULT_SAMPLE_RATE_HZ/4),
(float)FFT_BIN_WIDTH_HZ,
FFT_SIZE);
for(i=fftSize/8; (fftSize*3)/8 > i; i++) {
printf(", %.2f", pwr[i]);
@ -254,9 +258,9 @@ int rx_callback(hackrf_transfer* transfer) {
printf("\n");
printf("%s, %" PRIu64 ", %" PRIu64 ", %.2f, %u",
time_str,
(uint64_t)((FREQ_ONE_MHZ*frequency)+(DEFAULT_SAMPLE_RATE_HZ/8)),
(uint64_t)((FREQ_ONE_MHZ*frequency)+((DEFAULT_SAMPLE_RATE_HZ*3)/8)),
(float)STEP_SIZE_IN_HZ,
(uint64_t)(frequency+(DEFAULT_SAMPLE_RATE_HZ/2)),
(uint64_t)(frequency+((DEFAULT_SAMPLE_RATE_HZ*3)/4)),
(float)FFT_BIN_WIDTH_HZ,
FFT_SIZE);
for(i=(fftSize*5)/8; (fftSize*7)/8 > i; i++) {
printf(", %.2f", pwr[i]);
@ -303,15 +307,14 @@ void sigint_callback_handler(int signum) {
#endif
int main(int argc, char** argv) {
int opt, i, result, ifreq = 0;
bool odd;
int opt, i, result = 0;
const char* path = "/dev/null";
const char* serial_number = NULL;
int exit_code = EXIT_SUCCESS;
struct timeval t_end;
float time_diff;
unsigned int lna_gain=16, vga_gain=20;
uint16_t frequencies[MAX_FREQ_COUNT];
uint16_t frequencies[MAX_SWEEP_RANGES*2];
uint32_t num_samples = DEFAULT_SAMPLE_COUNT;
int step_count;
@ -409,10 +412,6 @@ int main(int argc, char** argv) {
return EXIT_FAILURE;
}
/* Plan a whole number of steps with bandwidth equal to the sample rate. */
step_count = 1 + (freq_max - freq_min - 1) / TUNE_STEP;
freq_max = freq_min + step_count * TUNE_STEP;
if (FREQ_MAX_MHZ <freq_max) {
fprintf(stderr, "argument error: freq_max may not be higher than %u.\n",
FREQ_MAX_MHZ);
@ -420,18 +419,6 @@ int main(int argc, char** argv) {
return EXIT_FAILURE;
}
fprintf(stderr, "Sweeping from %u MHz to %u MHz\n", freq_min, freq_max);
frequencies[0] = freq_min;
odd = true;
for(ifreq = 1; ifreq < step_count*2; ifreq++) {
if (odd) {
frequencies[ifreq] = frequencies[ifreq-1] + TUNE_STEP / 4;
} else {
frequencies[ifreq] = frequencies[ifreq-1] + 3*(TUNE_STEP/4);
}
odd = !odd;
}
fftSize = FFT_SIZE;
fftwIn = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize);
fftwOut = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize);
@ -508,7 +495,20 @@ int main(int argc, char** argv) {
return EXIT_FAILURE;
}
result = hackrf_init_sweep(device, frequencies, ifreq, num_samples);
/*
* Plan a whole number of tuning steps of a certain bandwidth.
* Increase freq_max if necessary to accomodate a whole number of steps,
* minimum 1.
*/
step_count = 1 + (freq_max - freq_min - 1) / TUNE_STEP;
freq_max = freq_min + step_count * TUNE_STEP;
fprintf(stderr, "Sweeping from %u MHz to %u MHz\n", freq_min, freq_max);
frequencies[0] = freq_min;
frequencies[1] = freq_max;
result = hackrf_init_sweep(device, frequencies, 1, num_samples,
TUNE_STEP * FREQ_ONE_MHZ, OFFSET, INTERLEAVED);
if( result != HACKRF_SUCCESS ) {
fprintf(stderr, "hackrf_init_sweep() failed: %s (%d)\n",
hackrf_error_name(result), result);

View File

@ -1810,23 +1810,70 @@ int ADDCALL hackrf_set_hw_sync_mode(hackrf_device* device, const uint8_t value)
}
}
/* Initialise sweep mode with alist of frequencies and dwell time in samples */
int ADDCALL hackrf_init_sweep(hackrf_device* device, uint16_t* frequency_list, int length, uint32_t dwell_time)
{
/*
* Initialize sweep mode:
* frequency_list is a list of start/stop pairs of frequencies in MHz.
* num_ranges is the number of pairs in frequency_list (1 to 10)
* num_samples is the number of samples to capture after each tuning.
* step_width is the width in Hz of the tuning step.
* offset is a number of Hz added to every tuning frequency.
* Use to select center frequency based on the expected usable bandwidth.
* sweep_mode
* LINEAR means step_width is added to the current frequency at each step.
* INTERLEAVED invokes a scheme in which each step is divided into two
* interleaved sub-steps, allowing the host to select the best portions
* of the FFT of each sub-step and discard the rest.
*/
int ADDCALL hackrf_init_sweep(hackrf_device* device,
const uint16_t* frequency_list, const int num_ranges,
const uint32_t num_samples, const uint32_t step_width,
const uint32_t offset, const enum sweep_style style) {
USB_API_REQUIRED(device, 0x0102)
int result, i;
int size = length * sizeof(frequency_list[0]);
unsigned char data[9 + MAX_SWEEP_RANGES * 2 * sizeof(frequency_list[0])];
int size = 9 + num_ranges * 2 * sizeof(frequency_list[0]);
for(i=0; i<length; i++)
frequency_list[i] = TO_LE(frequency_list[i]);
if((num_ranges < 1) || (num_ranges > MAX_SWEEP_RANGES)){
return HACKRF_ERROR_INVALID_PARAM;
}
if(num_samples % SAMPLES_PER_BLOCK) {
return HACKRF_ERROR_INVALID_PARAM;
}
if(SAMPLES_PER_BLOCK < num_samples) {
return HACKRF_ERROR_INVALID_PARAM;
}
if(1 > step_width) {
return HACKRF_ERROR_INVALID_PARAM;
}
if(INTERLEAVED < style) {
return HACKRF_ERROR_INVALID_PARAM;
}
data[0] = step_width & 0xff;
data[1] = (step_width >> 8) & 0xff;
data[2] = (step_width >> 16) & 0xff;
data[3] = (step_width >> 24) & 0xff;
data[4] = offset & 0xff;
data[5] = (offset >> 8) & 0xff;
data[6] = (offset >> 16) & 0xff;
data[7] = (offset >> 24) & 0xff;
data[8] = style;
for(i=0; i<(num_ranges*2); i++) {
data[9+i*2] = frequency_list[i] & 0xff;
data[10+i*2] = (frequency_list[i] >> 8) & 0xff;
}
result = libusb_control_transfer(
device->usb_device,
LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
HACKRF_VENDOR_REQUEST_INIT_SWEEP,
dwell_time & 0xffff,
(dwell_time >> 16) & 0xffff,
(unsigned char*)frequency_list,
num_samples & 0xffff,
(num_samples >> 16) & 0xffff,
data,
size,
0
);

View File

@ -47,6 +47,9 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSI
#endif
#define SAMPLES_PER_BLOCK 16384
#define MAX_SWEEP_RANGES 10
enum hackrf_error {
HACKRF_SUCCESS = 0,
HACKRF_TRUE = 1,
@ -95,6 +98,11 @@ enum operacake_ports {
OPERACAKE_PB4 = 7,
};
enum sweep_style {
LINEAR = 0,
INTERLEAVED = 1,
};
typedef struct hackrf_device hackrf_device;
typedef struct {
@ -218,10 +226,11 @@ extern ADDAPI uint32_t ADDCALL hackrf_compute_baseband_filter_bw(const uint32_t
/* set hardware sync mode */
extern ADDAPI int ADDCALL hackrf_set_hw_sync_mode(hackrf_device* device, const uint8_t value);
/* Start scan mode */
/* Start sweep mode */
extern ADDAPI int ADDCALL hackrf_init_sweep(hackrf_device* device,
uint16_t* frequency_list,
int length, uint32_t dwell_time);
const uint16_t* frequency_list, const int num_ranges,
const uint32_t num_samples, const uint32_t step_width,
const uint32_t offset, const enum sweep_style style);
/* Operacake functions */
extern ADDAPI int ADDCALL hackrf_get_operacake_boards(hackrf_device* device, uint8_t* boards);