TX bursts with timestamps

Hi,
I am trying to transmit bursts with a certain periodicity from my LimeSDR-USB using timestamps, but I just can’t understand how to get this up and running. I have looked through the forum and tried to copy examples of code that others have been using. I activate the RX stream as I understand this is required for getting the timestamps correct.

When running my code I get the following print outs:

sdr: TX stream has been successfully activated!
sdr: mtu_tx=8160 [Sa], mtu_rx=8160 [Sa]
sample count per send call: 992
sdr: RX stream has been successfully activated!
Starting stream loop, press Ctrl+C to exit…
burst_time: 1000000000 current time: 0 diff: 1000000000
[WARNING] popping from TX, samples popped 0/1020
Stream status: SOAPY_SDR_UNDERFLOW
burst_time: 1100000000 current time: 1003075556 diff: 96924444
[WARNING] popping from TX, samples popped 0/1020
Stream status: SOAPY_SDR_UNDERFLOW
burst_time: 1200000000 current time: 2003949206 diff: -803949206
[WARNING] L
Stream status: SOAPY_SDR_TIME_ERROR
burst_time: 1300000000 current time: 2005071746 diff: -705071746
Stream status: SOAPY_SDR_TIME_ERROR
burst_time: 1400000000 current time: 2006107937 diff: -606107937
Stream status: SOAPY_SDR_TIME_ERROR
burst_time: 1500000000 current time: 2007144127 diff: -507144127

I do not understand why the “diff” becomes negative. The writeStream call always report back that all samples were transmitted, but still I get SOAPY_SDR_UNDERFLOW and SOAPY_SDR_TIME_ERROR, why is this?

Any suggestions would be very much appreciated.

My code is below.

#include "tx_test.h"
SoapySDR::Device *device;
static sig_atomic_t stop = false;

void sigIntHandler(const int)
{
    stop = true;
}

int main()
{
    run_test();
    return EXIT_SUCCESS;
}
void run_test()
{
    const double frequency(500e6);
    double f_clk(189e6);
    uint16_t D_tx = 8;
    const double sampling_rate(f_clk / D_tx);
    const double tone_freq(2e3);
    const double f_ratio = tone_freq/sampling_rate;
    const double tx_gain(40);
    const double tx_bw(-1);
    std::string clock_source = "";
	std::string time_source = "";
    double T_timeout(2);
    double time_in_future(1);
    double burst_period(100e-3);
    double tx_burst_length(4.2e-5);
    SoapySDR::setLogLevel(SoapySDR::LogLevel::SOAPY_SDR_DEBUG);
    SoapySDR::KwargsList results = SoapySDR::Device::enumerate();
    if (results.size() > 0) {
            std::cout << "Found Device!" << std::endl;
    } else {
            throw std::runtime_error("Found no device!");
    }
    device = SoapySDR::Device::make();
    if (device == nullptr) {
            throw std::runtime_error("Could not open device!");
    }
    if (!device->hasHardwareTime()) {
            throw std::runtime_error("This device does not support timed streaming!");
    }
    if (clock_source != "") {
            device->setClockSource(clock_source);
    }
    if (time_source != "") {
            device->setTimeSource(time_source);
    }
    device->setMasterClockRate(f_clk);
    const size_t tx_ch(0);
    const size_t rx_ch(0);
    device->setSampleRate(SOAPY_SDR_TX, tx_ch, sampling_rate);
    double act_sample_rate = device->getSampleRate(SOAPY_SDR_TX, tx_ch);
    std::cout << "Actual TX rate: " << act_sample_rate << " Msps" << std::endl;
    if (tx_bw != -1) {
            device->setBandwidth(SOAPY_SDR_TX, tx_ch, tx_bw);
    }
    device->setGain(SOAPY_SDR_TX, tx_ch, tx_gain);
    device->setAntenna(SOAPY_SDR_TX, tx_ch, "BAND1");
    device->setFrequency(SOAPY_SDR_TX, tx_ch, frequency);
    bool tx_lo_locked = false;
    while (!(stop || tx_lo_locked)) {
            std::string tx_locked = device->readSensor(SOAPY_SDR_TX,
                                                       tx_ch,
                                                       "lo_locked");
            if (tx_locked == "true") {
                    tx_lo_locked = true;
            }
            usleep(100);
    }
    std::cout << "sdr: TX LO lock detected on channel "
              << std::to_string(tx_ch) << std::endl;
    std::cout << "sdr: Actual TX frequency on channel "
              << std::to_string(tx_ch) << ": "
              << std::to_string(device->getFrequency(SOAPY_SDR_TX,
                                                     tx_ch)/1e6)
              << " [MHz]" << std::endl;
    SoapySDR::Stream *tx_stream;
    tx_stream = device->setupStream(SOAPY_SDR_TX,
                                    SOAPY_SDR_CF32,
                                    std::vector<size_t>{(size_t)tx_ch});
    if (tx_stream == nullptr) {
            throw std::runtime_error("Unable to setup TX stream!");
    } else {
            std::cout << "sdr: TX stream has been successfully set up!"
                      << std::endl;
    }
    SoapySDR::Stream *rx_stream;
    rx_stream = device->setupStream(SOAPY_SDR_RX,
                                    "CF32",
                                    std::vector<size_t>{(size_t)rx_ch});
    if (rx_stream == nullptr) {
            throw std::runtime_error("Unable to setup RX stream!");
    } else {
            std::cout << "sdr: RX stream has been successfully set up!"
                      << std::endl;
    }
    int ret = device->activateStream(tx_stream);
    if (ret != 0) {
            std::string err = "sdr: Following problem occurred while";
            err += " activating TX stream: ";
            err += SoapySDR::errToStr(ret);
            throw std::runtime_error(err);
    } else {
            std::cout << "sdr: TX stream has been successfully activated!"
                      << std::endl;
    }
    int mtu_tx = device->getStreamMTU(tx_stream);
    int mtu_rx = device->getStreamMTU(rx_stream);
    std::cout << "sdr: mtu_tx="
              << std::to_string(mtu_tx)
              << " [Sa], mtu_rx="
              << std::to_string(mtu_rx) + " [Sa]"
              << std::endl;
    device->setHardwareTime(0);
    size_t buffer_size_tx = tx_burst_length * sampling_rate;
    size_t no_of_tx_samples = buffer_size_tx;
    int64_t tmp = D_tx * burst_period * sampling_rate;
    int64_t no_of_ticks_per_bursts_period = tmp;
    std::vector<std::complex<float>> tx_buff_data(no_of_tx_samples,
                                                  std::complex<float>(1.0f, 0.0f));
    for (size_t i = 0; i<no_of_tx_samples; i++) {
            const double pi = acos(-1);
            double w = 2*pi*i*f_ratio;
            tx_buff_data[i] = std::complex<float>(cos(w), sin(w));
    }
    std::vector<void *> tx_buffs_data;
    tx_buffs_data.push_back(tx_buff_data.data());

    std::cout << "sample count per send call: "
              << no_of_tx_samples << std::endl;
    int64_t current_hardware_time = device->getHardwareTime();
    int64_t now_tick = SoapySDR::timeNsToTicks(current_hardware_time,
                                               f_clk);
    int64_t rx_start_tick = now_tick;
    int64_t rx_future = time_in_future + burst_period;
    rx_start_tick += SoapySDR::timeNsToTicks(rx_future * 1e9, f_clk);
    int64_t tx_start_tick = now_tick;
    int64_t tx_future = time_in_future + 2 * burst_period;
    tx_start_tick += SoapySDR::timeNsToTicks((tx_future) * 1e9, f_clk);
    int64_t burst_time = SoapySDR::ticksToTimeNs(rx_start_tick, f_clk);
    int rx_flags = SOAPY_SDR_HAS_TIME | SOAPY_SDR_END_BURST | SOAPY_SDR_ONE_PACKET;
    size_t no_of_requested_samples(100);
    ret = device->activateStream(rx_stream,
                                 rx_flags,
                                 burst_time,
                                 no_of_requested_samples);
    if (ret != 0) {
            std::string err = "sdr: Following problem occurred while";
            err += " activating RX stream: ";
            err += SoapySDR::errToStr(ret);
            throw std::runtime_error(err);
    } else {
            std::cout << "sdr: RX stream has been successfully activated!"
                      << std::endl;
    }
    int64_t tx_tick = tx_start_tick;
    std::cout << "Starting stream loop, press Ctrl+C to exit..."
              << std::endl;
    signal(SIGINT, sigIntHandler);
    while (not stop) {
            long long int burst_time = SoapySDR::ticksToTimeNs(tx_tick,
                                                               f_clk);
            current_hardware_time = device->getHardwareTime();
            std::cout << "burst_time: " << burst_time
                      << " current time:  " << current_hardware_time
                      << " diff: " << burst_time - current_hardware_time
                      << std::endl;
            int tx_flags = SOAPY_SDR_HAS_TIME | SOAPY_SDR_END_BURST | SOAPY_SDR_ONE_PACKET;
            uint32_t no_of_transmitted_samples;
            no_of_transmitted_samples = device->writeStream(
                    tx_stream,
                    tx_buffs_data.data(),
                    no_of_tx_samples,
                    tx_flags,
                    burst_time,
                    1e6*T_timeout);
            tx_tick += no_of_ticks_per_bursts_period;
            if (no_of_transmitted_samples != no_of_tx_samples) {
                    std::string tx_verbose_msg = "Transmit failed: ";
                    switch (no_of_transmitted_samples) {
                    case SOAPY_SDR_TIMEOUT:
                            tx_verbose_msg += "SOAPY_SDR_TIMEOUT";
                            break;
                    case SOAPY_SDR_STREAM_ERROR:
                            tx_verbose_msg += "SOAPY_SDR_STREAM_ERROR";
                            break;
                    case SOAPY_SDR_CORRUPTION:
                            tx_verbose_msg += "SOAPY_SDR_CORRUPTION";
                            break;
                    case SOAPY_SDR_OVERFLOW:
                            tx_verbose_msg += "SOAPY_SDR_OVERFLOW";
                            break;
                    case SOAPY_SDR_NOT_SUPPORTED:
                            tx_verbose_msg += "SOAPY_SDR_NOT_SUPPORTED";
                            break;
                    case SOAPY_SDR_END_BURST:
                            tx_verbose_msg += "SOAPY_SDR_END_BURST";
                            break;
                    case SOAPY_SDR_TIME_ERROR:
                            tx_verbose_msg += "SOAPY_SDR_TIME_ERROR";
                            break;
                    case SOAPY_SDR_UNDERFLOW:
                            tx_verbose_msg += "SOAPY_SDR_UNDERFLOW";
                            break;
                    }
                    std::cout << tx_verbose_msg << std::endl;
            } else {
                    size_t chan_mask = 0;
                    int stream_status = device->readStreamStatus(
                            tx_stream,
                            chan_mask,
                            tx_flags,
                            burst_time,
                            1e6*T_timeout);
                    std::string tx_verbose_msg = "Stream status: ";
                    switch(stream_status) {
                    case SOAPY_SDR_TIMEOUT:
                            tx_verbose_msg += "SOAPY_SDR_TIMEOUT";
                            break;
                    case SOAPY_SDR_STREAM_ERROR:
                            tx_verbose_msg += "SOAPY_SDR_STREAM_ERROR";
                            break;
                    case SOAPY_SDR_CORRUPTION:
                            tx_verbose_msg += "SOAPY_SDR_CORRUPTION";
                            break;
                    case SOAPY_SDR_OVERFLOW:
                            tx_verbose_msg += "SOAPY_SDR_OVERFLOW";
                            break;
                    case SOAPY_SDR_NOT_SUPPORTED:
                            tx_verbose_msg += "SOAPY_SDR_NOT_SUPPORTED";
                            break;
                    case SOAPY_SDR_END_BURST:
                            tx_verbose_msg += "SOAPY_SDR_END_BURST";
                            break;
                    case SOAPY_SDR_TIME_ERROR:
                            tx_verbose_msg += "SOAPY_SDR_TIME_ERROR";
                            break;
                    case SOAPY_SDR_UNDERFLOW:
                            tx_verbose_msg += "SOAPY_SDR_UNDERFLOW";
                            break;
                    default:
                            tx_verbose_msg += "NO_ERROR";
                            break;
                    }
                    std::cout << tx_verbose_msg << std::endl;
            }
    }
    device->deactivateStream(tx_stream);
    device->closeStream(tx_stream);
    device->deactivateStream(rx_stream);
    device->closeStream(rx_stream);
    SoapySDR::Device::unmake(device);
}