From 755ca960e560d4cfa84171c76eb24c72b4239eb0 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Fri, 9 Sep 2022 05:15:15 -0400 Subject: [PATCH 1/4] hackrf_transfer: report power, not amplitude improve accuracy and reliability of digital signal power measurement --- host/hackrf-tools/src/hackrf_transfer.c | 69 ++++++++++++------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/host/hackrf-tools/src/hackrf_transfer.c b/host/hackrf-tools/src/hackrf_transfer.c index 4978316d..b4044d6d 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -343,17 +343,8 @@ uint32_t stream_tail = 0; uint32_t stream_drop = 0; uint8_t* stream_buf = NULL; -/* - * To report amplitude, best would be dB(fullscale) and the variance, - * but that would require more math per sample (dB(amplitude) = log(sqrt(i^2 + q^2) and sum of squares). - * For now, just sum iabs(i)+iabs(q) and divide by the number of samples*2. - * That allows us to give a measure of dB(fullscale). - * I don't know whether overload causes wrapping or clamping the 8-bit values. - * Clamping would produce a sigmoid curve, so with a signal of variable intensity you're - * probably getting substantial overload anytime this reports more than about -6dBfs. - */ -uint64_t stream_amplitude = - 0; /* sum of magnitudes of all I&Q samples, reset on the periodic report */ +/* sum of power of all samples, reset on the periodic report */ +volatile uint64_t stream_power = 0; bool transmit = false; struct timeval time_start; @@ -419,8 +410,18 @@ int rx_callback(hackrf_transfer* transfer) return -1; } - byte_count += transfer->valid_length; + /* Accumulate power (magnitude squared). */ bytes_to_write = transfer->valid_length; + uint64_t sum = 0; + for (i = 0; i < bytes_to_write; i++) { + int8_t value = transfer->buffer[i]; + sum += value * value; + } + + /* Update both running totals at approximately the same time. */ + byte_count += transfer->valid_length; + stream_power += sum; + if (limit_num_samples) { if (bytes_to_write >= bytes_to_xfer) { bytes_to_write = bytes_to_xfer; @@ -428,11 +429,6 @@ int rx_callback(hackrf_transfer* transfer) bytes_to_xfer -= bytes_to_write; } - // accumulate stream_amplitude: - for (i = 0; i < bytes_to_write; i++) { - stream_amplitude += abs((signed char) transfer->buffer[i]); - } - if (receive_wav) { /* convert .wav contents from signed to unsigned */ for (i = 0; i < bytes_to_write; i++) { @@ -487,12 +483,19 @@ int tx_callback(hackrf_transfer* transfer) stop_main_loop(); return -1; } - byte_count += transfer->valid_length; + + /* Accumulate power (magnitude squared). */ bytes_to_read = transfer->valid_length; + uint64_t sum = 0; for (i = 0; i < bytes_to_read; i++) { - stream_amplitude += abs((signed char) transfer->buffer[i]); + int8_t value = transfer->buffer[i]; + sum += value * value; } + /* Update both running totals at approximately the same time. */ + byte_count += transfer->valid_length; + stream_power += sum; + if (file == NULL) { // transceiver_mode == TRANSCEIVER_MODE_SS /* Transmit continuous wave with specific amplitude */ if (limit_num_samples) { @@ -1271,7 +1274,7 @@ int main(int argc, char** argv) #endif while ((hackrf_is_streaming(device) == HACKRF_TRUE) && (do_exit == false)) { - uint32_t byte_count_now; + uint64_t byte_count_now; struct timeval time_now; float time_difference, rate; if (stream_size > 0) { @@ -1304,7 +1307,7 @@ int main(int argc, char** argv) } #endif } else { - uint64_t stream_amplitude_now; + uint64_t stream_power_now; #ifdef _WIN32 // Wait for interval timer event, or interrupt event. HANDLE handles[] = {timer_handle, interrupt_handle}; @@ -1315,13 +1318,11 @@ int main(int argc, char** argv) #endif gettimeofday(&time_now, NULL); + /* Read and reset both totals at approximately the same time. */ byte_count_now = byte_count; + stream_power_now = stream_power; byte_count = 0; - stream_amplitude_now = stream_amplitude; - stream_amplitude = 0; - if (byte_count_now < - sample_rate_hz / 20) // Don't report on very short frames - stream_amplitude_now = 0; + stream_power = 0; time_difference = TimevalDiff(&time_now, &time_start); rate = (float) byte_count_now / time_difference; @@ -1329,21 +1330,15 @@ int main(int argc, char** argv) hw_sync_enable != 0) { fprintf(stderr, "Waiting for sync...\n"); } else { - // This is only an approximate measure, to assist getting receive levels right: - double full_scale_ratio = - ((double) stream_amplitude_now / - (byte_count_now ? byte_count_now : 1)) / - 128; - double dB_full_scale_ratio = 10 * log10(full_scale_ratio); - // Guard against ridiculous reports - if (dB_full_scale_ratio > 1) - dB_full_scale_ratio = -0.0; + double full_scale_ratio = (double) stream_power_now / + (byte_count_now * 128 * 128); + double dB_full_scale = 10 * log10(full_scale_ratio); fprintf(stderr, - "%4.1f MiB / %5.3f sec = %4.1f MiB/second, amplitude %3.1f dBfs", + "%4.1f MiB / %5.3f sec = %4.1f MiB/second, average power %3.1f dBfs", (byte_count_now / 1e6f), time_difference, (rate / 1e6f), - dB_full_scale_ratio); + dB_full_scale); if (display_stats) { bool tx = transmit || signalsource; result = update_stats(device, &state, &stats); From e7b19ef2c232b0edbdc82c577efdf7400da1051a Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Thu, 8 Sep 2022 15:58:22 -0400 Subject: [PATCH 2/4] hackrf_transfer: accept CW amplitude up to 128 --- host/hackrf-tools/src/hackrf_transfer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/host/hackrf-tools/src/hackrf_transfer.c b/host/hackrf-tools/src/hackrf_transfer.c index b4044d6d..c6bb7e4e 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -506,7 +506,7 @@ int tx_callback(hackrf_transfer* transfer) } for (i = 0; i < bytes_to_read; i++) - transfer->buffer[i] = amplitude; + transfer->buffer[i] = -(uint8_t) amplitude; if (limit_num_samples && (bytes_to_xfer == 0)) { stop_main_loop(); @@ -628,7 +628,7 @@ static void usage() printf("\t[-S buf_size] # Enable receive streaming with buffer size buf_size.\n"); #endif printf("\t[-B] # Print buffer statistics during transfer\n"); - printf("\t[-c amplitude] # CW signal source mode, amplitude 0-127 (DC value to DAC).\n"); + printf("\t[-c amplitude] # CW signal source mode, amplitude 0-128 (DC value to DAC).\n"); printf("\t[-R] # Repeat TX mode (default is off) \n"); printf("\t[-b baseband_filter_bw_hz] # Set baseband filter bandwidth in Hz.\n"); printf("\tPossible values: 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz, default <= 0.75 * sample_rate_hz.\n"); @@ -1015,9 +1015,9 @@ int main(int argc, char** argv) if (signalsource) { transceiver_mode = TRANSCEIVER_MODE_SS; - if (amplitude > 127) { + if (amplitude > 128) { fprintf(stderr, - "argument error: amplitude shall be in between 0 and 127.\n"); + "argument error: amplitude must be between 0 and 128.\n"); usage(); return EXIT_FAILURE; } From 70a04855acb74378d8d09b071783a201aa72dbfd Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Fri, 9 Sep 2022 16:16:24 -0400 Subject: [PATCH 3/4] libhackrf: add transfer buffer size access calls Applications may need to learn the size of the USB transfer buffer queue, for example to determine how many bytes will be preloaded with calls to the TX callback. --- host/libhackrf/src/hackrf.c | 20 ++++++++++++++++++++ host/libhackrf/src/hackrf.h | 5 +++++ 2 files changed, 25 insertions(+) diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index ab49a920..49f3a49d 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -2685,6 +2685,26 @@ int ADDCALL hackrf_start_rx_sweep( return result; } +/** + * Get USB transfer buffer size. + * @return size in bytes + */ +size_t ADDCALL hackrf_get_transfer_buffer_size(hackrf_device* device) +{ + (void) device; + return TRANSFER_BUFFER_SIZE; +} + +/** + * Get the total number of USB transfer buffers. + * @return number of buffers + */ +uint32_t ADDCALL hackrf_get_transfer_queue_depth(hackrf_device* device) +{ + (void) device; + return TRANSFER_COUNT; +} + #ifdef __cplusplus } // __cplusplus defined. #endif diff --git a/host/libhackrf/src/hackrf.h b/host/libhackrf/src/hackrf.h index 0d3efc8f..4f9ddbc8 100644 --- a/host/libhackrf/src/hackrf.h +++ b/host/libhackrf/src/hackrf.h @@ -25,6 +25,7 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSI #define __HACKRF_H__ #include +#include #ifdef _WIN32 #define ADD_EXPORTS @@ -458,6 +459,10 @@ extern ADDAPI int ADDCALL hackrf_start_rx_sweep( hackrf_sample_block_cb_fn callback, void* rx_ctx); +extern ADDAPI size_t ADDCALL hackrf_get_transfer_buffer_size(hackrf_device* device); + +extern ADDAPI uint32_t ADDCALL hackrf_get_transfer_queue_depth(hackrf_device* device); + #ifdef __cplusplus } // __cplusplus defined. #endif From 8a9af7a1add97f82ed4b62d4d0b84e021e0a0219 Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Fri, 9 Sep 2022 16:32:07 -0400 Subject: [PATCH 4/4] hackrf_transfer: report on only actual transfers The statistics reported to the user now reflect only completed USB transfers and do not include information about the empty buffers that are preloaded with data at the start of a TX operation. --- host/hackrf-tools/src/hackrf_transfer.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/host/hackrf-tools/src/hackrf_transfer.c b/host/hackrf-tools/src/hackrf_transfer.c index c6bb7e4e..89ee4832 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -683,6 +683,7 @@ int main(int argc, char** argv) unsigned int lna_gain = 8, vga_gain = 20, txvga_gain = 0; hackrf_m0_state state; stats_t stats = {0, 0}; + static int32_t preload_bytes = 0; while ((opt = getopt(argc, @@ -1272,6 +1273,8 @@ int main(int argc, char** argv) .it_value = {.tv_sec = 1, .tv_usec = 0}}; setitimer(ITIMER_REAL, &interval_timer, NULL); #endif + preload_bytes = hackrf_get_transfer_queue_depth(device) * + hackrf_get_transfer_buffer_size(device); while ((hackrf_is_streaming(device) == HACKRF_TRUE) && (do_exit == false)) { uint64_t byte_count_now; @@ -1324,6 +1327,23 @@ int main(int argc, char** argv) byte_count = 0; stream_power = 0; + /* + * The TX callback is called to preload the USB + * transfer buffers at the start of TX. This results in + * invalid statistics collected about the empty buffers + * before any USB transfer is completed. We skip these + * statistics and do not report them to the user. + */ + if (preload_bytes > 0) { + if (preload_bytes > byte_count_now) { + preload_bytes -= byte_count_now; + byte_count_now = 0; + } else { + byte_count_now -= preload_bytes; + preload_bytes = 0; + } + } + time_difference = TimevalDiff(&time_now, &time_start); rate = (float) byte_count_now / time_difference; if (byte_count_now == 0 && hw_sync == true &&