diff --git a/firmware/hackrf_usb/usb_api_sweep.c b/firmware/hackrf_usb/usb_api_sweep.c index 503e7f6a..35656e81 100644 --- a/firmware/hackrf_usb/usb_api_sweep.c +++ b/firmware/hackrf_usb/usb_api_sweep.c @@ -31,31 +31,53 @@ #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 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; 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, - endpoint->setup.length, NULL, NULL); + 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 = (uint64_t)frequencies[0] * FREQ_GRANULARITY; + set_freq(sweep_freq + offset); start_sweep_mode = true; usb_transfer_schedule_ack(endpoint->in); } @@ -64,8 +86,9 @@ 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; + unsigned int phase = 1; + bool odd = true; + uint16_t range = 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 = (uint64_t)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 = (uint64_t)frequencies[range*2] * FREQ_GRANULARITY; + } else { + sweep_freq += step_width; + } + } + set_freq(sweep_freq + offset); blocks_queued = 0; } } diff --git a/firmware/hackrf_usb/usb_api_sweep.h b/firmware/hackrf_usb/usb_api_sweep.h index 828647cd..20c27859 100644 --- a/firmware/hackrf_usb/usb_api_sweep.h +++ b/firmware/hackrf_usb/usb_api_sweep.h @@ -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 #include @@ -28,9 +28,14 @@ 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); void sweep_mode(void); -#endif /* __USB_API_SPCAN_H__ */ +#endif /* __USB_API_SWEEP_H__ */ diff --git a/host/hackrf-tools/src/hackrf_sweep.c b/host/hackrf-tools/src/hackrf_sweep.c index dca716cf..2edd68cf 100644 --- a/host/hackrf-tools/src/hackrf_sweep.c +++ b/host/hackrf-tools/src/hackrf_sweep.c @@ -1,6 +1,7 @@ /* * Copyright 2016 Dominic Spill * Copyright 2016 Mike Walters + * Copyright 2017 Michael Ossmann * * This file is part of HackRF. * @@ -34,6 +35,7 @@ #include #include #include +#include #define _FILE_OFFSET_BITS 64 @@ -87,21 +89,25 @@ int gettimeofday(struct timeval *tv, void* ignored) { #define FREQ_ONE_MHZ (1000000ull) -#define FREQ_MIN_HZ (0ull) /* 0 Hz */ -#define FREQ_MAX_HZ (7250000000ull) /* 7250MHz */ +#define FREQ_MIN_MHZ (0) /* 0 MHz */ +#define FREQ_MAX_MHZ (7250) /* 7250 MHz */ #define DEFAULT_SAMPLE_RATE_HZ (20000000) /* 20MHz default sample rate */ #define DEFAULT_BASEBAND_FILTER_BANDWIDTH (15000000) /* 5MHz default */ -#define FREQ_STEP (DEFAULT_SAMPLE_RATE_HZ / FREQ_ONE_MHZ) -#define MAX_FREQ_COUNT 1000 +#define TUNE_STEP (DEFAULT_SAMPLE_RATE_HZ / FREQ_ONE_MHZ) +#define OFFSET 7500000 #define DEFAULT_SAMPLE_COUNT 0x4000 +#define BLOCKS_PER_TRANSFER 16 #if defined _WIN32 #define sleep(a) Sleep( (a*1000) ) #endif +int num_ranges = 0; +uint16_t frequencies[MAX_SWEEP_RANGES*2]; + static float TimevalDiff(const struct timeval *a, const struct timeval *b) { return (a->tv_sec - b->tv_sec) + 1e-6f * (a->tv_usec - b->tv_usec); } @@ -166,15 +172,20 @@ uint32_t amp_enable; bool antenna = false; uint32_t antenna_enable; -uint32_t freq_min; -uint32_t freq_max; +bool binary_output = false; +bool one_shot = false; +volatile bool sweep_started = false; -int fftSize; +int fftSize = 20; +uint32_t fft_bin_width; fftwf_complex *fftwIn = NULL; fftwf_complex *fftwOut = NULL; fftwf_plan fftwPlan = NULL; float* pwr; float* window; +time_t time_now; +struct tm *fft_time; +char time_str[50]; float logPower(fftwf_complex in, float scale) { @@ -185,64 +196,112 @@ float logPower(fftwf_complex in, float scale) } int rx_callback(hackrf_transfer* transfer) { - /* This is where we need to do interesting things with the samples - * FFT - * Throw away unused bins - * write output to pipe - */ - ssize_t bytes_to_write; - ssize_t bytes_written; int8_t* buf; - float frequency; + uint8_t* ubuf; + uint64_t frequency; /* in Hz */ + float float_freq; int i, j; - if( fd != NULL ) { - byte_count += transfer->valid_length; - bytes_to_write = transfer->valid_length; - buf = (int8_t*) transfer->buffer; - for(j=0; j<16; j++) { - if(buf[0] == 0x7F && buf[1] == 0x7F) { - frequency = *(uint16_t*)&buf[2]; - } - /* copy to fftwIn as floats */ - buf += 16384 - (fftSize * 2); - for(i=0; i < fftSize; i++) { - fftwIn[i][0] = buf[i*2] * window[i] * 1.0f / 128.0f; - fftwIn[i][1] = buf[i*2+1] * window[i] * 1.0f / 128.0f; - } - buf += fftSize * 2; - fftwf_execute(fftwPlan); - for (i=0; i < fftSize; i++) { - // Start from the middle of the FFTW array and wrap - // to rearrange the data - int k = i ^ (fftSize >> 1); - pwr[i] = logPower(fftwOut[k], 1.0f / fftSize); - } - fwrite(&frequency, sizeof(float), 1, stdout); - fwrite(pwr, sizeof(float), fftSize, stdout); - } - - bytes_written = fwrite(transfer->buffer, 1, bytes_to_write, fd); - if (bytes_written != bytes_to_write) { - return -1; - } else { - return 0; - } - } else { + if(NULL == fd) { return -1; } + + byte_count += transfer->valid_length; + buf = (int8_t*) transfer->buffer; + for(j=0; j i; i++) { + printf(", %.2f", pwr[i]); + } + printf("\n"); + printf("%s, %" PRIu64 ", %" PRIu64 ", %.2f, %u", + time_str, + (uint64_t)(frequency+(DEFAULT_SAMPLE_RATE_HZ/2)), + (uint64_t)(frequency+((DEFAULT_SAMPLE_RATE_HZ*3)/4)), + (float)fft_bin_width, + fftSize); + for(i=1+fftSize/8; (1+(fftSize*3)/8) > i; i++) { + printf(", %.2f", pwr[i]); + } + printf("\n"); + } + if(one_shot && ((uint64_t)(frequency+((DEFAULT_SAMPLE_RATE_HZ*3)/4)) + >= (uint64_t)(FREQ_ONE_MHZ*frequencies[num_ranges*2-1]))) { + do_exit = true; + } + } + return 0; } static void usage() { fprintf(stderr, "Usage:\n"); fprintf(stderr, "\t[-h] # this help\n"); - fprintf(stderr, "\t[-d serial_number] # Serial number of desired HackRF.\n"); - fprintf(stderr, "\t[-a amp_enable] # RX RF amplifier 1=Enable, 0=Disable.\n"); - fprintf(stderr, "\t[-f freq_min:freq_max # Specify minimum & maximum sweep frequencies (MHz).\n"); - fprintf(stderr, "\t[-p antenna_enable] # Antenna port power, 1=Enable, 0=Disable.\n"); + fprintf(stderr, "\t[-d serial_number] # Serial number of desired HackRF\n"); + fprintf(stderr, "\t[-a amp_enable] # RX RF amplifier 1=Enable, 0=Disable\n"); + fprintf(stderr, "\t[-f freq_min:freq_max] # minimum and maximum frequencies in MHz\n"); + fprintf(stderr, "\t[-p antenna_enable] # Antenna port power, 1=Enable, 0=Disable\n"); fprintf(stderr, "\t[-l gain_db] # RX LNA (IF) gain, 0-40dB, 8dB steps\n"); fprintf(stderr, "\t[-g gain_db] # RX VGA (baseband) gain, 0-62dB, 2dB steps\n"); fprintf(stderr, "\t[-n num_samples] # Number of samples per frequency, 16384-4294967296\n"); + fprintf(stderr, "\t[-w bin_width] # FFT bin width (frequency resolution) in Hz\n"); + fprintf(stderr, "\t[-1] # one shot mode\n"); + fprintf(stderr, "\t[-B] # binary output\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Output fields:\n"); + fprintf(stderr, "\tdate, time, hz_low, hz_high, hz_bin_width, num_samples, dB, dB, . . .\n"); } static hackrf_device* device = NULL; @@ -265,18 +324,20 @@ 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]; uint32_t num_samples = DEFAULT_SAMPLE_COUNT; + int step_count; + uint32_t freq_min = 0; + uint32_t freq_max = 6000; - while( (opt = getopt(argc, argv, "a:f:p:l:g:d:n:h?")) != EOF ) { + + while( (opt = getopt(argc, argv, "a:f:p:l:g:d:n:w:1Bh?")) != EOF ) { result = HACKRF_SUCCESS; switch( opt ) { @@ -291,17 +352,29 @@ int main(int argc, char** argv) { case 'f': result = parse_u32_range(optarg, &freq_min, &freq_max); - fprintf(stderr, "Scanning %uMHz to %uMHz\n", freq_min, freq_max); - frequencies[ifreq++] = freq_min; - odd = true; - while(frequencies[ifreq-1] <= freq_max) { - if (odd) - frequencies[ifreq] = frequencies[ifreq-1] + FREQ_STEP / 4; - else - frequencies[ifreq] = frequencies[ifreq-1] + 3*(FREQ_STEP/4); - ifreq++; - odd = !odd; + if(freq_min >= freq_max) { + fprintf(stderr, + "argument error: freq_max must be greater than freq_min.\n"); + usage(); + return EXIT_FAILURE; } + if(FREQ_MAX_MHZ fftSize) { + fprintf(stderr, + "argument error: FFT bin width (-w) must be no more than one quarter the sample rate\n"); return EXIT_FAILURE; } - - fftSize = 64; - fftwIn = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize); - fftwOut = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize); - fftwPlan = fftwf_plan_dft_1d(fftSize, fftwIn, fftwOut, FFTW_FORWARD, FFTW_MEASURE); + + if(16368 < fftSize) { + fprintf(stderr, + "argument error: FFT bin width (-w) too small, resulted in more than 16368 FFT bins\n"); + return EXIT_FAILURE; + } + + /* In interleaved mode, the FFT bin selection works best if the total + * number of FFT bins is equal to an odd multiple of four. + * (e.g. 4, 12, 20, 28, 36, . . .) + */ + while((fftSize + 4) % 8) { + fftSize++; + } + + fft_bin_width = DEFAULT_SAMPLE_RATE_HZ / fftSize; + fftwIn = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize); + fftwOut = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize); + fftwPlan = fftwf_plan_dft_1d(fftSize, fftwIn, fftwOut, FFTW_FORWARD, FFTW_MEASURE); pwr = (float*)fftwf_malloc(sizeof(float) * fftSize); window = (float*)fftwf_malloc(sizeof(float) * fftSize); for (i = 0; i < fftSize; i++) { @@ -453,7 +559,21 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } - result = hackrf_init_sweep(device, frequencies, ifreq, num_samples); + /* + * For each range, plan a whole number of tuning steps of a certain + * bandwidth. Increase high end of range if necessary to accommodate a + * whole number of steps, minimum 1. + */ + for(i = 0; i < num_ranges; i++) { + step_count = 1 + (frequencies[2*i+1] - frequencies[2*i] - 1) + / TUNE_STEP; + frequencies[2*i+1] = frequencies[2*i] + step_count * TUNE_STEP; + fprintf(stderr, "Sweeping from %u MHz to %u MHz\n", + frequencies[2*i], frequencies[2*i+1]); + } + + result = hackrf_init_sweep(device, frequencies, num_ranges, 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); diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index 5bb71dc6..61de081d 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -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 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 ); diff --git a/host/libhackrf/src/hackrf.h b/host/libhackrf/src/hackrf.h index 904207e0..a92f5051 100644 --- a/host/libhackrf/src/hackrf.h +++ b/host/libhackrf/src/hackrf.h @@ -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);