Post Go back to editing

ad9371-iiostream.c example with multiple frequencies

Hello everyone.

I'm new to IIO and Analog Devices, so please be patient with me. I not sure if I'm missing something simple or fundamental. 

I have three frequencies that I need to stream data through.

Frequency1    ----                               -----

Frequency2             -----                                   ----------

Frequency3                       ----------                                  -------

Problem:

The receivers do not get all packages sent by AD93 when I switch between frequencies

Question 1: why ad9371-iiostream.c example calls  iio_buffer_push() function first , then loops through the buffer to write data using calls iio_buffer_step() iio_buffer_end() ? A strange concept , intuitively I would assume it should look like  

       // 1.  prepare the buffer - using iio_buffer_step() and iio_buffer_end()  functions

       // 2. then call iio_buffer_push() - to push the data to the device and transfer it through RF

instead ad9371-iiostream.c example does the opposite. I don't get that and I don't get how it works. How the AD93 device knows when to send the buffer to RF ? what triggers start of the physical RF signal?

Question 2: I switch the frequency in the beginning of the loop and it doesn't work as I would expect. What am I doing wrong?

My current solution:

I'm using this well documented example to stream data and it works well with one frequency.

https://github.com/analogdevicesinc/libiio/blob/master/examples/ad9371-iiostream.c

I'm doing the frequency switch in the beginning of the loop. 

Is that a correct solution? if yes, then why am I loosing packages right after doing a frequency switch?

    while (!stop)
	{
myCustomSetFrequency(); // this is the only change I made in ad9371-iiostream.c example

ssize_t nbytes_rx, nbytes_tx; char *p_dat, *p_end; ptrdiff_t p_inc; // Schedule TX buffer nbytes_tx = iio_buffer_push(txbuf); if (nbytes_tx < 0) { printf("Error pushing buf %d\n", (int) nbytes_tx); shutdown(); }
// commented out because I don't need to read here
/* // Refill RX buffer // nbytes_rx = iio_buffer_refill(rxbuf); // if (nbytes_rx < 0) { printf("Error refilling buf %d\n",(int) nbytes_rx); shutdown(); } // READ: Get pointers to RX buf and read IQ from RX buf port 0 p_inc = iio_buffer_step(rxbuf); p_end = iio_buffer_end(rxbuf); for (p_dat = (char *)iio_buffer_first(rxbuf, rx0_i); p_dat < p_end; p_dat += p_inc) { // Example: swap I and Q const int16_t i = ((int16_t*)p_dat)[0]; // Real (I) const int16_t q = ((int16_t*)p_dat)[1]; // Imag (Q) ((int16_t*)p_dat)[0] = q; ((int16_t*)p_dat)[1] = i; } */
// WRITE: Get pointers to TX buf and write IQ to TX buf port 0 p_inc = iio_buffer_step(txbuf); p_end = iio_buffer_end(txbuf); for (p_dat = (char *)iio_buffer_first(txbuf, tx0_i); p_dat < p_end; p_dat += p_inc) { // Example: fill with zeros // 12-bit sample needs to be MSB aligned so shift by 4 // wiki.analog.com/.../basic_iq_datafiles ((int16_t*)p_dat)[0] = 0 << 4; // Real (I) ((int16_t*)p_dat)[1] = 0 << 4; // Imag (Q) } // Sample counter increment and status output nrx += nbytes_rx / iio_device_get_sample_size(rx); ntx += nbytes_tx / iio_device_get_sample_size(tx); printf("\tRX %8.2f MSmp, TX %8.2f MSmp\n", nrx/1e6, ntx/1e6); }

I have defined my custom function to set the current frequency based on the business logic defined in another function myCustomGetCurrentFrequency(). This implementation changes the frequency only when the change is required.

function myCustomSetFrequency()

{

   long long new_frequency = myCustomGetCurrentFrequency();

   if(current_frequency != new_frequency )

   {

       current_frequency = new_frequency;

       iio_channel_attr_write_longlong(
            iio_device_find_channel(config.phy, "altvoltage1", true),
            "frequency",
            current_frequency);

   }

}

Thank you!

  • 1. This is just an example, conceptually the example is trying to receive what you transmit. Hence the transmit first then receive.

    How the AD93 device knows when to send the buffer to RF ? what triggers start of the physical RF signal?

    The stack is best effort, so as soon as you push a buffer it sends it to the device and it will start transmitting. There is no other trigger and no inherit synchronization between TX and RX.

    2. Please put the refill back or you won't get new data. By default, there are multiple buffers in the system which queue behind one another. The default is 4. You can change this through the iio_device_set_kernel_buffers_count function. When refill is called the first time, all buffers in the queue fill until there is no room left. Once you pull a buffer space is made for the next one.

    With this in mind, based on your code you will not see the frequency update in your code until you have pulled the 4th buffer. You can set the number of buffers to 1 or pull all 4 buffers.

    -Travis

  • Thanks Travis, that was helpful. I have two more questions

    Question 1:

    The stack is best effort, so as soon as you push a buffer it sends it to the device and it will start transmitting. There is no other trigger and no inherit synchronization between TX and RX.

    There is no RX (I don't need to read data in my case). The TX logic looks like this

        // 1. Schedule TX buffer
        nbytes_tx = iio_buffer_push(txbuf);
        // 2. WRITE: Get pointers to TX buf and write IQ to TX buf port 0
        p_inc = iio_buffer_step(txbuf);
        p_end = iio_buffer_end(txbuf);
        for (p_dat = (char *)iio_buffer_first(txbuf, tx0_i); p_dat < p_end; p_dat += p_inc) {
            ((int16_t*)p_dat)[0] = 0 << 4; // Real (I)
            ((int16_t*)p_dat)[1] = 0 << 4; // Imag (Q)
        }
    

    It basically call iio_buffer_push() function then writes the data to txbuf. It's counter intuitive. I would assume the txbuf will be send out to RF right after iio_buffer_push() call. 

    It like calling linux function write(fd, buf, buf_size) to write to a file and then modifying buf array. My assumption was that right after iio_buffer_push(txbuf) call API pushes whatever is in the buffer to the device or the cache. The buffer modifications after iio_buffer_push(txbuf) call should have no effect on the transmitting data.

    You're saying "there is no trigger to send out the data". How much time do I have after iio_buffer_push(txbuf) call to modify the data before it gets send out (1 ms, 10, ms 100 ms)? If I call iio_buffer_push(txbuf) with out later calling iio_buffer_step(txbuf) and iio_buffer_end(txbuf) and the writing loop, what is going to be sent out?

    Question 2:

    2. Please put the refill back or you won't get new data. By default, there are multiple buffers in the system which queue behind one another. The default is 4. You can change this through the iio_device_set_kernel_buffers_count function.

    In my case I need to transmit only, I don't need to receive data. Are those buffers for RX or TX, or both? if I call iio_buffer_push() function a loop, can I end up pushing new buffers faster than the device can sent out the data through RF? Will it be queueing my data in the system queues before sending them out? If yes, are those the same 4 default kernel_buffers?

    Thank you!

    -Cristian

  • It basically call iio_buffer_push() function then writes the data to txbuf. It's counter intuitive. I would assume the txbuf will be send out to RF right after iio_buffer_push() call. 

    Data is sent only after the push. It's just in the first iteration of the loop it pushes an empty buffer.

    You're saying "there is no trigger to send out the data". How much time do I have after iio_buffer_push(txbuf) call to modify the data before it gets send out (1 ms, 10, ms 100 ms)? If I call iio_buffer_push(txbuf) with out later calling iio_buffer_step(txbuf) and iio_buffer_end(txbuf) and the writing loop, what is going to be sent out?

    Modifying the data after push will have no effect.

    re those buffers for RX or TX, or both?

    Yes

    if I call iio_buffer_push() function a loop, can I end up pushing new buffers faster than the device can sent out the data through RF?

    Push will block so you cannot do this.

    Will it be queueing my data in the system queues before sending them out? If yes, are those the same 4 default kernel_buffers?

    It will queue to that count then block.

    -Travis

  • Thanks a lot Travis. Now I'm getting a better understanding of the process.

    Sorry, I have 4 more questions

    Question 1:

    Data is sent only after the push. It's just in the first iteration of the loop it pushes an empty buffer.

    I changed the example code - forming the buffer then sending it out. It worked. What is the reason you had it in the opposite order in the example? Why do you have iio_buffer_push() sending out an empty or uninitialized buffer on the first iteration? Why don't you have the iio_buffer_push() call at the end of the loop in your example?

        // 1. WRITE: Get pointers to TX buf and write IQ to TX buf port 0
        p_inc = iio_buffer_step(txbuf);
        p_end = iio_buffer_end(txbuf);
        for (p_dat = (char *)iio_buffer_first(txbuf, tx0_i); p_dat < p_end; p_dat += p_inc) {
            ((int16_t*)p_dat)[0] = 0 << 4; // Real (I)
            ((int16_t*)p_dat)[1] = 0 << 4; // Imag (Q)
        }

    // 2. Schedule TX buffer nbytes_tx = iio_buffer_push(txbuf);

    Question 2:

    re those buffers for RX or TX, or both?

    Yes

    If I have TX and RX happening simultaneously, am I going to get 4 queued buffers for TX and 4 queued buffers for RX (total 8 queues buffers) or is it the 4 queued buffers shared between TX and RX. Can I end up having 3 TX queued buffers and 1 RX queues buffer because I'm calling TX more frequently than RX?

    Question 3:

    RX queues - is there a mechanism to determine how many RX buffers are currently in use? How to determine how many RX buffers are ready to be read by calling iio_buffer_refill(rxbuf) with out actually calling iio_buffer_refill(rxbuf) function and being blocked if nothing is ready.

    Question 4:

    TX queues - is there a mechanism to determine how many TX buffers are currently in use? If I'm calling iio_buffer_push(txbuf) too frequently and all 4 buffers are full is there a way to determine that state? Or a way to read the number of currently queued buffers?

    Thank you!

  • 1. This code pre-dates me so I don't know the idea behind why it was written that way. It would honestly ignore that detail.

    2. RX and TX have separate queues and can be independently configured. They only share memory space on the board.

    3. You could look at the interrupts to see if they increased or use the buffers in a non-blocking mode or use a pollable file descriptor (http://analogdevicesinc.github.io/libiio/master/libiio/group__Buffer.html#ga2ae96ee9f0748e55dfad996d6e9883f2). Nonblocking buffer queries would tell you how much data is in the current buffer.

    4. Similar to 3. A pollable file descriptor would be probably best.

    -Travis