Switching time between Tx CH A and Tx CH B

Hello,

I have a LimePCIe-SDR and I am trying to find ways to reduce switching time between Tx CH-A and TX CH-B. With the code below I get a switching time of around 3 msec.

  1. Has any one measured the switching time at their end?
  2. What is the minimum time that can be achieved?
  3. What are other methods that can used to reduce switching time?
  4. I am switching channels by turning TBB module of respective channel ON/OFF. Is there any other better method?

/*******************************************************************************

  • INCLUDE FILES
    ******************************************************************************/
    #include
    #include
    #include
    #include
    #include <unistd.h>
    #include
    #include <signal.h>
    #include
    #include
    #include
    #include “lime/LimeSuite.h”
    #include “lime/Logger.h”
    #include “lime/dataTypes.h”
    #define USE_GNU_PLOT
    #ifdef USE_GNU_PLOT
    #include “gnuPlotPipe.h”
    #endif

/*******************************************************************************

  • NAMESPACE
    ******************************************************************************/
    using namespace std;
    using namespace lime;

/*******************************************************************************

  • LOCAL DATA
    ******************************************************************************/
    int samplesToCapture = 16384;
    int onTime = 1;
    volatile sig_atomic_t stop;

//Device structure, should be initialize to NULL
lms_device_t* device = NULL;

/*******************************************************************************

  • LOCAL FUNCTION DECLARATION
    ******************************************************************************/
    static void TxThread(void);
    static void RxThread(void);
    static inline void SwitchTx(void);
    static void inthand(int signum);
    static int error();

/*******************************************************************************

  • @brief This function is the main application

  • @param argc argument count from command line

  • @param argv array of pointers to arguments passed

  • @return Fail: -1 or Success: 0
    ****************************************************************************/
    int main(int argc, char
    argv)
    {
    //Find devices
    int n;
    signal(SIGINT, inthand);

    if(argc > 1)
    {
    for(int i=1; i < argc;)
    {
    if((std::string)argv[i] == “–sample”)
    {
    if(atoi(argv[2]) > 1)
    samplesToCapture = atoi(argv[2]);
    }

         if((std::string)argv[i] == "--ontime")
             onTime = atoi(argv[4]);
         
         i = i + 2;
    
     }
    

    }
    lms_info_str_t list[8]; //should be large enough to hold all detected devices
    if ((n = LMS_GetDeviceList(list)) < 0) //NULL can be passed to only get number of devices
    error();

    cout << "Devices found: " << n << endl; //print number of devices
    if (n < 1)
    return -1;

    //open the first device
    if (LMS_Open(&device, list[0], NULL))
    error();

    //Initialize device with default configuration
    //Do not use if you want to keep existing configuration
    //Use LMS_LoadConfig(device, “/path/to/file.ini”) to load config from INI
    //if (LMS_Init(device) != 0)
    // error();
    if(LMS_LoadConfig(device,"/Desktop/mimo-test.ini") != 0)
    error();

    //Get number of channels
    if ((n = LMS_GetNumChannels(device, LMS_CH_RX)) < 0)
    error();
    cout << "Number of RX channels: " << n << endl;
    if ((n = LMS_GetNumChannels(device, LMS_CH_TX)) < 0)
    error();
    cout << "Number of TX channels: " << n << endl;

    LMS_WriteParam(device,LMS7_MAC,2);
    LMS_WriteParam(device,LMS7_LOSS_MAIN_TXPAD_R3,0);
    LMS_WriteParam(device,LMS7_CG_IAMP_TBB_R3,63);
    LMS_WriteParam(device,LMS7_EN_G_TBB,0);
    LMS_WriteParam(device,LMS7_MAC,1);
    LMS_WriteParam(device,LMS7_LOSS_MAIN_TXPAD_R3,0);
    LMS_WriteParam(device,LMS7_EN_G_TBB,0);

    std::thread t1(TxThread);
    std::thread t2(RxThread);

    t1.join();
    t2.join();

    if (LMS_EnableChannel(device, LMS_CH_RX, 0, false) != 0)
    error();
    if (LMS_EnableChannel(device, LMS_CH_RX, 1, false) != 0)
    error();
    //Enable TX channels
    if (LMS_EnableChannel(device, LMS_CH_TX, 0, false) != 0)
    error();
    if (LMS_EnableChannel(device, LMS_CH_TX, 1, false) != 0)
    error();
    //Close device
    LMS_Close(device);

    return 0;
    }

/*******************************************************************************

  • @brief This function is tx thread. This function switches the Tx port after

  • every 1 msec

  • @param void

  • @param void

  • @return void
    ******************************************************************************/
    void TxThread(void)
    {
    static uint16_t i = 0;
    auto start = chrono::high_resolution_clock::now();
    auto switchTime = start;
    while(!stop)
    {
    switchTime = chrono::high_resolution_clock::now();
    SwitchTx();
    while ((chrono::high_resolution_clock::now() - switchTime) < chrono::milliseconds(onTime))
    {
    }
    }

    auto finish = std::chrono::high_resolution_clock::now();
    auto elapsed = std::chrono::duration_castchrono::microseconds(finish - start);
    cout<<“TxT:”<< elapsed.count() <<endl;
    cout<<endl;
    }

/*******************************************************************************

  • @brief This function is rx thread. This function gathers data from all Rx ports

  • and dumps it onto a file for offline analysis

  • @param void

  • @param void

  • @return void
    ***************************************************************************/
    void RxThread(void)
    {
    //Initialize stream
    lms_stream_t streamId[2];
    lime::complex16_t
    buffers;
    const int channelsCount = 2;
    int avgCount = 512;
    int noOfSamples = samplesToCapture;
    unsigned short samplesToGetPerCall = 16384;
    const int fifoSize = samplesToGetPerCall
    512;

    streamId[0].channel = 0; //channel number
    streamId[0].fifoSize = fifoSize;//1024 * 1024; //fifo size in samples
    streamId[0].throughputVsLatency = 1.0; //optimize for max throughput
    streamId[0].isTx = false; //RX channel
    streamId[0].dataFmt = lms_stream_t::LMS_FMT_I16;
    if (LMS_SetupStream(device, &streamId[0]) != 0)
    error();

    streamId[1].channel = 1; //channel number
    streamId[1].fifoSize = fifoSize;//1024 * 1024; //fifo size in samples
    streamId[1].throughputVsLatency = 1.0; //optimize for max throughput
    streamId[1].isTx = false; //RX channel
    streamId[1].dataFmt = lms_stream_t::LMS_FMT_I16;
    if (LMS_SetupStream(device, &streamId[1]) != 0)
    error();

    buffers = new lime::complex16_t*[channelsCount];
    for (int i = 0; i < channelsCount; ++i)
    buffers[i] = new complex16_t[samplesToGetPerCall];

    vector<complex16_t> captureBuffer[channelsCount];
    uint32_t samplesToCapture[channelsCount];
    uint32_t samplesCaptured[channelsCount];

    for(int ch=0; ch<channelsCount; ++ch)
    {
    samplesToCapture[ch] = noOfSamples;
    captureBuffer[ch].resize(samplesToCapture[ch]);
    samplesCaptured[ch] = 0;
    }

    LMS_StartStream(&streamId[0]);
    LMS_StartStream(&streamId[1]);

    lms_stream_meta_t meta;
    meta.waitForTimestamp = 0;//syncTx;
    meta.flushPartialPacket = false;
    int fftCounter = 0;

    auto start = std::chrono::high_resolution_clock::now();
    auto t1 = std::chrono::high_resolution_clock::now();

    while(!stop)
    {
    do{
    uint32_t samplesPopped[channelsCount];
    uint64_t ts[channelsCount];
    for(int i=0; i<channelsCount; ++i)
    {
    samplesPopped[i] = LMS_RecvStream(&streamId[i], &buffers[i][0], samplesToGetPerCall, &meta, 1000);
    ts[i] = meta.timestamp + fifoSize/4;
    }

         for(int ch=0; ch<channelsCount; ++ch)
         {
             uint32_t samplesToCopy = min(samplesPopped[ch], samplesToCapture[ch]);
             if(samplesToCopy <= 0)
                 continue;
             memcpy((captureBuffer[ch].data() + samplesCaptured[ch]), buffers[ch], samplesToCopy*sizeof(complex16_t));
             samplesToCapture[ch] -= samplesToCopy;
             samplesCaptured[ch] += samplesToCopy;
         }
    }while(samplesToCapture[0] != 0 && samplesToCapture[1] != 0);
    
     if(samplesToCapture[0] == 0 && samplesToCapture[1] == 0)
     {
         ofstream fout;
         fout.open("samples.txt",std::ofstream::trunc | std::ofstream::out);
         
         #if 1
         fout << "AI\tAQ";
         if(channelsCount > 1)
             fout << "\tBI\tBQ";
         fout << endl;
         #endif
    
         int samplesCnt = captureBuffer[0].size();
         for(int i=0; i<samplesCnt; ++i)
         {
             for(int ch=0; ch<channelsCount; ++ch)
             {
                 fout << captureBuffer[ch][i].i << "\t" << captureBuffer[ch][i].q << "\t";
             }
             fout << endl;
         }
    
         fout.close();
         auto finish = std::chrono::high_resolution_clock::now();
         auto elapsed = std::chrono::duration_cast<chrono::microseconds>(finish - start);
         cout<<"RxT:"<< elapsed.count() <<endl;
         cout << endl <<"samplesCaptured Ch 1:" << samplesCaptured[0]<< endl;
         cout << endl <<"samplesCaptured Ch 2:" << samplesCaptured[1]<< endl;
         stop = 1;
     }
    
    // }while(1);//while(++fftCounter < avgCount);//while(++fftCounter < avgCount);
     
     //Reset values for next run ; required only if continuous acquisiton is required
     #if 0
     samplesToCapture[0] = noOfSamples;
     samplesToCapture[1] = noOfSamples;
     samplesCaptured[0] = 0;
     samplesCaptured[1] = 0;
     fftCounter = 0;
     #endif
    

#if 0
string filename = “samples.absdbfs”;
fout.open(filename.c_str());

    fout << "AI\tAQ";
    if(channelsCount > 1)
        fout << "\tBI\tBQ";
    fout << endl;

    for(int i=0; i<samplesCnt; ++i)
    {
        for(int ch=0; ch<channelsCount; ++ch)
        {
            fout
            << (captureBuffer[ch][i].i == 0 ? -67 : 20*log10(abs(captureBuffer[ch][i].i)/2048))<< "\t"
            << (captureBuffer[ch][i].q == 0 ? -67 : 20*log10(abs(captureBuffer[ch][i].q)/2048))<< "\t";
        }
        fout << endl;
    }
    fout.close();

#endif
}
for(int i=0; i<channelsCount; ++i)
{
LMS_StopStream(&streamId[i]);
LMS_DestroyStream(device, &streamId[i]);
}

    for (int i = 0; i < channelsCount; ++i)
        delete [] buffers[i];
    delete [] buffers;

}

/*******************************************************************************

  • @brief This function controls the switching of Tx ports.

  • @param void

  • @param void

  • @return void
    ******************************************************************************/
    inline void SwitchTx(void)
    {
    static uint16_t toggle = 0xFF;

    if(toggle)
    {
    //Disable TBB Module A
    LMS_WriteParam(device,LMS7_EN_G_TBB,0);

     //Change MAC to select channel B
     LMS_WriteParam(device,LMS7_MAC,2);
    
     //Enable TBB Module B
     LMS_WriteParam(device,LMS7_EN_G_TBB,1);
    

    }
    else
    {

     //Disable TBB Module B
     LMS_WriteParam(device,LMS7_EN_G_TBB,0);
    
     //Change MAC to select channel A
     LMS_WriteParam(device,LMS7_MAC,1);
    
     //Enable TBB Module A
     LMS_WriteParam(device,LMS7_EN_G_TBB,1);
    

    }
    toggle ^= 0xFF;
    }

/*******************************************************************************

  • @brief This function is a utility funtion to toggle stop variable upon receiving
  • ctrl^c signal from user. This terminates the application.
  • @param void
  • @param void
  • @return void
    ******************************************************************************/
    static void inthand(int signum) {
    stop = 1;
    }

/*******************************************************************************

  • @brief This function is a utility funtion for error handling
  • @param void
  • @param void
  • @return void
    ******************************************************************************/
    static int error()
    {
    if (device != NULL)
    LMS_Close(device);
    exit(-1);
    }

Thanks.