Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it possible to change size between header blocks in sweep mode? #1485

Open
holiman123 opened this issue Sep 30, 2024 · 5 comments
Open

Is it possible to change size between header blocks in sweep mode? #1485

holiman123 opened this issue Sep 30, 2024 · 5 comments
Labels
question question from the community that is not technical support

Comments

@holiman123
Copy link

holiman123 commented Sep 30, 2024

What would you like to know?

As I understood, right now sweep mode returns data in blocks of 16 384 bytes including header. Function hackrf_init_sweep has parameter num_bytes but it only defines count of blocks with headers. If I tell num_bytes to be 32 768, I'll get two blocks with headers with the same frequency in it. As I see in source code, the code to change tuning frequency calles regardless to num_bytes value. Size of blocks hardcoded in firmware. This behavior leads to a problem with timing data from this two blocks.

I searched firmware and libhackrf projects for all hardcoded values that related to the size between headers and doubled it.
List of changes I did in firmware:

  • In sgpio_m0.s file:
// Buffer that we're funneling data to/from.
.equ TARGET_DATA_BUFFER,                   0x20008000		// not changed
.equ TARGET_BUFFER_SIZE,                   0x10000		// default: 0x8000
.equ TARGET_BUFFER_MASK,                   0xffff		// default: 0x7fff
  • In usb_bulk_buffer.h file:
#define USB_BULK_BUFFER_SIZE 0x10000 // default: 0x8000
#define USB_BULK_BUFFER_MASK 0xFFFF // default: 0x7FFF
  • In usb_api_sweep.c file:
    Searched all 0x4000 and 0x8000 values, and defined them as 0x8000 or double.
#define SWEEP_BLOCK_SIZE  0x8000	// default: 0x4000

List of changes i did in libhackrf:

  • In hackrf.c file:
#define TRANSFER_COUNT        4 	// not changed
#define TRANSFER_BUFFER_SIZE  262144	// not changed
#define DEVICE_BUFFER_SIZE    65536 	// default: 32768
#define USB_MAX_SERIAL_LENGTH 32	// not changed
  • In hackrf.h file:
/**
 * Number of samples per tuning when sweeping
 * @ingroup streaming
 */
#define SAMPLES_PER_BLOCK 16384 // default: 8192

/**
 * Number of bytes per tuning for sweeping
 * @ingroup streaming
 */
#define BYTES_PER_BLOCK 32768 // default: 16384

But after this change M0 stucks in RX.

do I understand something wrong, or did I missed code with hardcoded value of 16 384, or maybe there is some hardware limitations in HackRF?

@holiman123 holiman123 added the question question from the community that is not technical support label Sep 30, 2024
@holiman123 holiman123 changed the title Is it possible to change size of header blocks in sweep mode? Is it possible to change size between header blocks in sweep mode? Sep 30, 2024
@martinling
Copy link
Member

The fixed sizes involved here are related to our available space for sample buffering.

In order to simultaneously capture samples from the ADC and send buffered samples over USB to the host, we need the MCU's USB peripheral to be able to read from one part of the sample buffer via DMA, whilst the M0 core writes samples into another part of the buffer, both with very tight timing requirements.

The only way we can reliably achieve that is to have the buffer straddle a boundary between two different SRAM blocks, such that each half has its own connection to the AHB bus matrix. Otherwise we get intermittent delays due to bus contention, which can cause us to miss deadlines and lose samples.

The only part of the LPC4320's memory map which satisfies this property is 32KB in size, with two 16KB halves. So everything happens in 16KB blocks. In RX mode, the M0 writes samples continuously, wrapping around the 32KB buffer. Whenever it fills one 16KB block, we tell the USB peripheral to grab that block over DMA and send it to the host. As long as the host keeps polling us for data fast enough, that transfer will complete before the M0 wraps back to the same half of the buffer again.

Sweep mode works a little differently, but is still oriented around 16KB blocks. We capture and transfer as many blocks as requested per frequency step, and then pause for two 16KB block periods while we retune the RF path.

However, currently even if you request multiple blocks, every block gets a header written to it, so even though the samples captured at each frequency are contiguous across the blocks captured, you don't get a continuous waveform.

If that's the part that's causing you issues, it would be possible to modify the code to take some extra options during sweep setup, so that when it captures multiple blocks per frequency step, it only writes a header to the first block.

@holiman123
Copy link
Author

holiman123 commented Sep 30, 2024

Thanks for a quick reply!
I tried to delete and to assign zero value to headers before, but still had problems with getting somewhat contiguous waveform. I'll try to modify code so headers will not appear and check again.

@holiman123
Copy link
Author

holiman123 commented Sep 30, 2024

I removed part of code that created headers in usb_api_sweep.c:

// Write metadata to buffer.
buffer = &usb_bulk_buffer[phase * 0x4000];
// *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;

After that, I set receiver like this:
sweep ranges: 1302MHz : 1306MHz
sweep step: 4MHz
Sweep num_bytes: 262144
Sample rate: 20MHz
Filter: 15MHz

Than I tried to record sine wave that tuned to 1300MHz and get this result:

image

Recorded signal is devided by blocks. Each of the block is 0.4096ms long. Which is correlate with size of block with header.

$$\large 0.4096 / 1000 = 0.0004096 - to.seconds$$ $$\large 0.0004096 * 20 000 000 = 8192 - samples.count$$

I expected this type of behavior after 131072 samples.
For example this is transfer record:

image

There we can see edges in sine each 6.5536ms. Which is exaclty 131072 samples.

I send my program which I used to record. Maybe problem is in the way I use receiver. It is on C# with connected hackrf.dll. All function names are the same.

using Frequencies;
using NetHackrfStandart;
using System.Runtime.InteropServices;

namespace HackRfTests;

internal class Program
{
    static unsafe void Main(string[] args)
    {
        Console.WriteLine("Hello, World!");
        FileStream file = File.OpenWrite("record.pcm");

        LibHackrf.hackrf_init();

        var l = LibHackrf.hackrf_device_list();
        LibHackrf.hackrf_device* device;

        LibHackrf.hackrf_open_by_serial(l->serial_numbers[0], &device);

        LibHackrf.hackrf_set_freq(device, (ulong)Frequency.FromMHz(1302).Hz);
        LibHackrf.hackrf_set_sample_rate(device, (ulong)Frequency.FromMHz(20).Hz);
        LibHackrf.hackrf_set_baseband_filter_bandwidth(device, (uint)Frequency.FromMHz(15).Hz);
        LibHackrf.hackrf_set_lna_gain(device, 16);
        LibHackrf.hackrf_set_vga_gain(device, 16);

        var ranges = new ushort[2] { 1302, 1306 };

        DateTime t = DateTime.Now;
        LibHackrf.hackrf_delegate callback = (LibHackrf.hackrf_transfer* transfer) => 
        {
            Console.WriteLine((DateTime.Now - t).TotalMilliseconds);
            t = DateTime.Now;

            byte[] data = new byte[transfer->valid_length];
            Marshal.Copy((IntPtr)transfer->buffer, data, 0, transfer->valid_length);

            file.Write(data, 0, transfer->valid_length);

            return 0;
        };

        fixed (ushort* ptr = ranges)
        {
            LibHackrf.hackrf_init_sweep(device, ptr, 1, 262144, 4000000, 0, 0);
        }
        LibHackrf.hackrf_start_rx_sweep(device, Marshal.GetFunctionPointerForDelegate(callback), null);

        //LibHackrf.hackrf_start_rx(device, Marshal.GetFunctionPointerForDelegate(callback), null);

        Console.ReadKey();

        LibHackrf.hackrf_stop_rx(device);
        LibHackrf.hackrf_close(device);
        LibHackrf.hackrf_exit();
    }
}

@straithe
Copy link
Member

I'm not sure quite what we can do for you at this point. It sounds like you may be developing a new feature. I suggest opening a pull request if that is the case.

@holiman123
Copy link
Author

I decided not to use sweep mode but change frequency manually in default mode. And I added an option to the original library that allows me to change USB transfer size (which is fixed to 262144 by default, as I remember). So I have something similar to sweep mode now, but with a contiguous waveform. This approach is almost as fast as sweep mode, except for the low USB transfer size (<32768, I think).

Do I understand it right? Sweep mode was originally developed to calculate the power level of each sweep step. In this case, a contiguous waveform would not be necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question question from the community that is not technical support
Projects
None yet
Development

No branches or pull requests

3 participants