Jump to content
  • 0

Signals Drift When using Multiple DAQs


Ricky Ooi

Question

Hi,

I am using two DAQ (USB1808 and QUAD08) to measure analog input and encoders respectively. The two DAQs are sync with the clock-input with USB1808 as Master and QUAD08 as Slave. At the beginning of the run, both encoder signal (blue line) and analog signal (red line) are sync (can observed in the image attached "Before Drift.png"). However, after run for about 5 minutes, the analog signal drift to the left ("After Drift.png"). Is there any way to avoid this or overcome this drift offset?

Before Drift.png

Before Drift.PNG
After Drift.png

After Drift.PNG

Edited by Ricky Ooi
Link to comment
Share on other sites

17 answers to this question

Recommended Posts

  • 0

Yes, I do. Below is the code for my DAQ.
I did print out the current index using the CurrentStatus function. Here is the result I observed:

// [USB1808, USB1808, QUAD08]
[25, 25, 27]
[67, 67, 65]
[100, 100, 99]

I notice both USB1808 are having the same count, but QUAD08 are having random deviate from USB1808 (sometime bigger, sometime lower).
 

IntPtr[] m_MemHandle = new IntPtr[MccObjectCollections.Count];
int[][] m_MemData = new int[MccObjectCollections.Count][];
ScanOptions SCAN_OPTIONS = ScanOptions.Continuous | ScanOptions.Background;
ScanOptions EXT_SCAN_OPTIONS = ScanOptions.Continuous | ScanOptions.Background | ScanOptions.ExtClock;
  
ErrorInfo daqError = new ErrorInfo();

// MccBoards = Array of 3 devices (1. USB1808 [Master]; 2. USB1808 [Slave]; 3. USB-QUAD08 [Slave])
// BUFFER_INTERVAL = 2
// NUM_OF_CHANNEL = Array of Number of channels to be read in each devices
// sampling_rate = 400
for(int deviceIdx = MccBoards.Length - 1; deviceIdx >= 0; deviceIdx--)
{
  int Samples_Per_Channel = sampling_rate * BUFFER_INTERVAL;
  int Total_Samples = Samples_Per_Channel * NUM_OF_CHANNEL[deviceIdx];

  m_MemHandle[deviceIdx] = MccService.WinBufAlloc32Ex(Total_Samples);
  m_MemData[deviceIdx] = new int[Total_Samples];

  ScanOptions _options = SCAN_OPTIONS;
  if(deviceIdx == 0)
    _options = EXT_SCAN_OPTIONS;
  
  switch (MccObjectCollections[deviceIdx].DAQSubsystem)
  {
    case DAQSubsystem.DAQI:
      short[] chanArray = DaqInChanDescriptorList[deviceIdx].Select(x => x.Channel).ToArray();
      ChannelType[] chanTypeArray = DaqInChanDescriptorList[deviceIdx].Select(x => x.ChannelType).ToArray();
      Range[] rangeArray = DaqInChanDescriptorList[deviceIdx].Select(x => x.Range).ToArray();

      int PretrigCount = 0;
      daqError = MccBoards[deviceIdx].DaqInScan(chanArray, chanTypeArray, rangeArray, NUM_OF_CHANNEL[deviceIdx], ref sampling_rate, ref PretrigCount, ref Total_Samples, m_MemHandle[deviceIdx], _options);
      break;

    case DAQSubsystem.Counter:
      daqError = MccBoards[deviceIdx].CInScan(0, NUM_OF_CHANNEL[deviceIdx] - 1, Total_Samples, ref sampling_rate, m_MemHandle[deviceIdx], _options | ScanOptions.Ctr32Bit);
      break;
  }

  if (daqError.Value != ErrorInfo.ErrorCode.NoErrors)
  {
    MessageBox.Show(daqError.Message, "DAQ Error", MessageBoxButton.OK, MessageBoxImage.Error);
    return false;
  }
}

// sampling_rate = 400
m_ScanThread = new Thread(() => ThreadScan(sampling_rate));
m_ScanThread.IsBackground = true;
m_ScanThread.Start();



// Loop Thread to read the data
private void ThreadScan(int sampling_rate)
{
  DateTime StartDateTime = DateTime.Now;

  int StartRowIndex = 0;
  int EndRowIndex = -1;
  m_IsScanning = true;

  (int Index,int CycleCount)[] LatestRow = Enumerable.Repeat((-1,0), MccObjectCollections.Count).ToArray();
  List<double[]>[] Datasets = new List<double[]>[MccObjectCollections.Count];

  while (m_IsScanning)
  {
    for(int deviceIdx = 0; deviceIdx < MccBoards.Length; deviceIdx++)
    {
      MccBoards[deviceIdx].GetStatus(out short transfer_status, out int current_count, out int last_index, m_FunctionType[deviceIdx]);

      if (transfer_status == MccBoard.Running)
      {
        if (last_index / NUM_OF_CHANNEL[deviceIdx] < LatestRow[deviceIdx].Index)
          LatestRow[deviceIdx].CycleCount++;

        LatestRow[deviceIdx].Index = last_index / NUM_OF_CHANNEL[deviceIdx];
      }
      else
        LatestRow[deviceIdx].Index = -1;
    }

    // The purpose of this is to get the index with the lowest value among 4 devices.
    EndRowIndex = LatestRow.Where(x => x.CycleCount == LatestRow.Min(y => y.CycleCount)).Min(x => x.Index);

    if(LatestRow.Select(x => x.CycleCount).Distinct().Count() == 1 && LatestRow.Select(x => x.CycleCount).Distinct().First() > 10)
    {
      for (int j = 0; j < LatestRow.Length; j++)
        LatestRow[j].CycleCount = 0;
    }

    
	// This part onwards is grabbing the data from buffer and process it.
    if (EndRowIndex > 0 && StartRowIndex != EndRowIndex)
    {
      for (int i = 0; i < MccBoards.Length; i++)
        Datasets[i] = GetInputData(i, StartRowIndex, EndRowIndex);

      int num_of_row = Datasets[0].Count;

      List<List<object>> Final_Datasets = new List<List<object>>();
      DateTime CurrDateTime = new DateTime();
      for (int rowIdx = 0; rowIdx < num_of_row; rowIdx++)
      {
        // 1 millisecond = 10000 ticks
        long microticks = (long)((double)rowIdx / (double)sampling_rate * 1000 * 10000);
        CurrDateTime = StartDateTime.AddTicks(microticks);

        List<object> rowData = new List<object>();
        Datasets.Select(x => x[rowIdx]).ToList().ForEach((x) => { rowData.AddRange(x.Select(y => (object)y)); });
        rowData.Insert(0, CurrDateTime);

        Final_Datasets.Add(rowData);
      }

      m_NewDataAvailable?.Invoke(this, Final_Datasets);

      // 1 millisecond = 10000 ticks
      long totalmicroticks = (long)((double)num_of_row / (double)sampling_rate * 1000 * 10000);
      StartDateTime = StartDateTime.AddTicks(totalmicroticks);
      StartRowIndex = EndRowIndex;
    }

    Thread.Sleep(10);
  }
}

 

Link to comment
Share on other sites

  • 0

I was afraid there might be an issue because the USB-1808 and the USB-QUAD08 have nothing in common and were designed by different engineering groups. I will look into this more and let you know later. 

Best regards,

John 

Link to comment
Share on other sites

  • 0

Hey Ricky,

I used a C# program to sync 1808 and QUAD08. I print out the GetStatus results, which show the two are in sync. If your GetStatus results show approximately the same number of samples for each device, they are in sync too. In this case, you could have a bug in your routine that reads and graphs the data. 

Best regards,
John

Link to comment
Share on other sites

  • 0

Hello,

We don't have example programs that demonstrate how to sync various devices. Below is an algorithm that reads each half of the buffer as samples become available. The lower half is read when the GetStatus index points to the upper half. Likewise, the upper half is read when GetStatus index points to the lower half. A boolean flag is used to avoid double reads. If it controlled four devices like your configuration, there would be four calls to WinBufToArray to transfer the data into a 2D user buffer array from each device.

 

                int[]   UserBuffer  = new int[Constants.halfbuf];
                bool    ReadLower   = true;
                short   daqStatus   = 0;
                int     Count       = 0;
                int     Index       = 0;

                int halfbuf = bufferSize / 2;

                int rows = halfbuf / ChanCount;

                do
                {

                    IsError(daq.GetStatus(out daqStatus, out Count, out Index, FunctionType.DaqiFunction));
                    if ((Index >= Constants.halfbuf) & ReadLower) //check for 50% more data
                    {
                        //get lower half of buffer
                        IsError(MccService.WinBufToArray32(buffer, UserBuffer, 0, halfbuf));

                        DisplayData(UserBuffer, rows);
                        ReadLower = false; //flag that controls the next read
                    }
                    else if ((Index < halfbuf) & !ReadLower)
                    {

                        //get the upper half
                        IsError(MccService.WinBufToArray32(buffer, UserBuffer, halfbuf, halfbuf));

                        DisplayData(UserBuffer, rows);
                        ReadLower = true;//flag that controls the next read
                    }

                } while (!Console.KeyAvailable);
                cki = Console.ReadKey();
 

Link to comment
Share on other sites

  • 0

Hi,

I had tested your solution advised, but I still having data unsync between multiple devices. Do you mind to have OEM to provide sample code that read data from different multiple devices (USB-1808 & USB-QUAD08) with sync mode for C#? 

For addition info, below are the mode that I used for each device:
USB-1808: DaqInScan
USB-QUAD08: CInScan

Edited by Ricky Ooi
Link to comment
Share on other sites

  • 0

Hi Ricky,

I've tested 1808 with a QUAD08 and found the external clock support to work. I wired the 1808's ICLKO to the Quad08's XPCR. Next, I enabled the 1808's TMR0 to output a square wave and wire it to the counter input on the 1808 and the Quad08, both channels set to totalize. By making both units count the same signal, I can prove they are synced. My test program reads one-half buffer at a time but only prints the first scan of each buffer. Both units are set to read three channels. 1808 uses two analog inputs and a totalizing counter. The Quad08 uses three channels, one totalizing counter, and two encoders. The totalizing counters are 3 & 4 out of the six channels printed. I have attached my test program for you to look over. You will need a C# 32-bit Console App project to use it.

Best regards,
John

Program.cs

Link to comment
Share on other sites

  • 0

Hi @JRys,

I notice you are using different CounterDebounceTime at different case, will this the root cause for the case above?

Besides, you are using Master device as the reference index number ("Index1808") for buffer comparison. Will Slave index always leading the Master device? 

Edited by Ricky Ooi
Link to comment
Share on other sites

  • 0

Hello,

The sample code I attached checks the status on the quad08 device, not 1808. However, it shouldn't matter, either one should work. 

If the debounce setting is set to none and your signals are noisy, it could cause the counter to double count. Usually, with encoders, it is different because the counter input looks at the rising and falling edge of the signals. You could experiment with the debounce setting to see if it makes a difference.

Best regards,
John

Link to comment
Share on other sites

  • 0

The 1808 input clock-out signal (ICLKO) is a square wave whose frequency is set by the sample rate. ICLKO is idle until the scan function is called. The Quad08 starts when the 1808's clock appears on its XPCR pin. The first pulse edge from 1808 is the Quad08 start trigger and marks the first scan acquired by both devices.  

-John

Link to comment
Share on other sites

  • 0

Hi,

I tested with the sample code shared by you, I still observed drift signal between 2 DAQs. 

At first, this raise my thought of the CLK is not sync between 2 DAQs, therefore, I purposely place a wire to connect ICLKO pin to the analog pin of USB1808 and observe the signal. Surprisingly, the signal does not generate pulse but a small signal of approx 0.017V (you can see the sample signal in the image attached). 

With this observation, I also suspect maybe the software side miss out the "EXTCLOCK" setting on USB-QUAD08, so I tested this by removing the connection of XPCR from USB-QUAD08. As result, QUAD08 does return any count value from every channel. Therefore, I can justify that software side does not miss out the sync function between DAQs.

In order to deep dive the sync issue, I calculated the "RowIndex" for each DAQ by using the current_index (obtained from GetStatus() function) divided by number of channels.
At first, when my motor is not running, the RowIndex looks stable and almost sync to each other. When it start runing (moving forward and backward in very fast motion), the RowIndex begin unsync. I also attached two images (FirstHalf.png and SecondHalf.png) to illustrated the row index for both DAQs. It seem like QUAD08 is leading of USB1808.

For your info, below is the configuration I used:
PACKET_SIZE = 1024
Sampling_Rate = 400

Clock Signal.PNG

First Half.png

Second Half.png

Link to comment
Share on other sites

  • 0

Hey Ricky,

I have verified that both my USB-1808 and USB-QUAD08 are synchronized. I have to admit, at first, I was getting some strange results, but it was because of my external clock wiring. Once I switched to using twist-pair wire and connecting both clocks and ground, I got excellent results. I've attached my C# test program project. The image shows eight columns. The first three are 1808, and the second is Quad08. The last two are the current counts from each device divided by the number of channels. To make sure, I connected an 1808 timer output to the first 1808 counter (column 2) and to the second Quad08 input (column 5). As you can see both are counting almost the same. Same for the last two columns.  

Best regards,
John

 

ricky1.png

CSharp_1808_QUAD08.zip

Link to comment
Share on other sites

  • 0

Hi @JRys,

Thank for this solution. After I connect the ground, the data is now looked okay. However, after I run for about one week, the data from USB-QUAD08 is lagging than data from USB-1808. I am suspecting that USB-QUAD08 are returning the data that having large value (e.g. ~300000), so the data transfer cannot catch up with USB-1808. Not sure if you have any idea on this or not.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...