Jump to content
  • 0

Incorrect voltage readings for sample acquisition


Thomas1983

Question

I want to acquire 1000 samples and associate a timestamp with them, the way I am doing it is by collecting 1 sample and then using a loop to repeat the process 1000 times. Doing this I have no issue with the timestamp, however I am getting the wrong voltage values and I am not sure why. 

some additional info: I am sending a 2kHz square wave to channel 1 (from a function generator) that has a dc offset of 2.5 V and a 2kHz sine wave to channel 2 (from wavegen 1) and I am triggering off of channel 2 

If anyone can help, it’ll be greatly appreciated! I have pasted my code below:

 

# Declare ctype variables

dwRead = c_uint32()

hdwf = c_int()

sts = c_byte()

hzAcq = c_double(100000)  # 100 kHz sampling rate

nSamples = 1

timestamp = []

 

def OscilloscopeSet():

    # Set up acquisition for both channels

    dwf.FDwfAnalogInBufferSizeSet(hdwf, c_int(nSamples))

    dwf.FDwfAnalogInChannelEnableSet(hdwf, c_int(0), c_bool(True))

    dwf.FDwfAnalogInChannelRangeSet(hdwf, c_int(0), c_double(10))  # Set the range to 10V

    dwf.FDwfAnalogInChannelEnableSet(hdwf, c_int(1), c_bool(True))

    dwf.FDwfAnalogInChannelRangeSet(hdwf, c_int(1), c_double(10))  # Set the range to 10V

    dwf.FDwfAnalogInAcquisitionModeSet(hdwf, c_int(0))  # Acquisition mode

    dwf.FDwfAnalogInFrequencySet(hdwf, hzAcq)

 

    # Configure the signal generation

    dwf.FDwfAnalogOutNodeEnableSet(hdwf, c_int(0), c_int(0), c_bool(True))

    dwf.FDwfAnalogOutNodeFunctionSet(hdwf, c_int(0), c_int(0), c_ubyte(1))

    dwf.FDwfAnalogOutNodeFrequencySet(hdwf, c_int(0), c_int(0), c_double(2000))

    dwf.FDwfAnalogOutNodeAmplitudeSet(hdwf, c_int(0), c_int(0), c_double(1))

    dwf.FDwfAnalogOutOffsetSet(hdwf, c_int(0), c_double(2))

    dwf.FDwfAnalogOutConfigure(hdwf, c_int(0), c_bool(True))

 

    # Set up trigger

    dwf.FDwfAnalogInTriggerAutoTimeoutSet(hdwf, c_double(2))  # 2 second auto trigger timeout

    dwf.FDwfAnalogInTriggerSourceSet(hdwf, c_ubyte(2))  # trigsrcDetectorAnalogIn

    dwf.FDwfAnalogInTriggerTypeSet(hdwf, c_int(0))  # trigtypeEdge

    dwf.FDwfAnalogInTriggerChannelSet(hdwf, c_int(1))  # Channel 2

    dwf.FDwfAnalogInTriggerLevelSet(hdwf, c_double(2))  # Trigger level set to 2V

    dwf.FDwfAnalogInTriggerHysteresisSet(hdwf, c_double(0.01))  # 0.01V

    dwf.FDwfAnalogInTriggerConditionSet(hdwf, c_int(0))  # trigcondRisingPositive

    dwf.FDwfAnalogInConfigure(hdwf, c_bool(False), c_bool(False))  # Acquisition mode

 

    # Wait at least 2 seconds for the offset to stabilize

    time.sleep(2)

 

OscilloscopeSet()

 

def acquire_samples():

    time.sleep(0.01)

    dwf.FDwfAnalogInStatus(hdwf, False, None)

    channel1_samples = c_double()

    channel2_samples = c_double()

    dwf.FDwfAnalogInStatusSample(hdwf, c_int(0), byref(channel1_samples))  # get channel 1 data

    dwf.FDwfAnalogInStatusSample(hdwf, c_int(1), byref(channel2_samples))  # get channel 2 data

    return channel1_samples, channel2_samples

 

# Create the file for writing

file_name = 'Voltage.txt'

with open(file_name, 'w') as f:

        f.write("Timestamp, Channel 2, Channel 1\n")

        for j in range(1000):

            print("test")

            channel1_value, channel2_value = acquire_samples()

            start_time = time.perf_counter()

            timestamp.append(start_time)

            f.write(f"{timestamp[j]}, {channel2_value}, {channel1_value}\n")

 

print(f'Data successfully saved to {file_name}')

dwf.FDwfAnalogOutReset(hdwf, c_int(0))

dwf.FDwfDeviceCloseAll()

Link to comment
Share on other sites

14 answers to this question

Recommended Posts

  • 0

Hi @Thomas1983

In this code you are collecting samples with poor software timing at about 10ms rate, depending on OS and system load probably varying between 10-20ms.
The FDwfAnalogInStatusSample returns one samples/channel. The trigger and buffer are only used with data array captures.

See the WF SDK/ samples/ py/ AnalogIn_Trigger.py and other examples

image.png

Link to comment
Share on other sites

  • 0
Posted (edited)

Hi @attila

Thank you for the swift reply. I revamped my code and am now using “dwf.FdwfAnalogInStatusData”. However, I am still having issues obtaining the correct voltage values. Could the problem be that I am collecting only one sample and then looping it 1000 times? How can I fix this? 

Here is the revised code (I left every thing the same except my "acquire_samples" function and my printing loop:

def acquire_samples(Channel1_samples, Channel2_samples):

    while True:

        dwf.FDwfAnalogInStatus(hdwf, c_int(1), byref(sts))

        if sts.value == 2:

            break

    time.sleep(0.01)

    dwf.FDwfAnalogInStatusData(hdwf, 0, Channel1_samples, nSamples)  # get channel 1 data

    dwf.FDwfAnalogInStatusData(hdwf, 1, Channel2_samples, nSamples)  # get channel 2 data

 

samples = (rgdSamples1_off, rgdSamples2_off)

 

# Create file for writing

file_name = 'Voltage.txt'

 

with open(file_name, 'w') as f:

    for iteration in range(iterations):

        f.write("Timestamp, Channel 2, Channel 1\n")

       

        for j in range(1000):

            acquire_samples(*samples)

            start_time = time.perf_counter()

            sampleOff.append(rgdSamples1_off[0])

            timestamp.append(start_time)

           

            f.write(f"{timestamp[j]}, {rgdSamples2_off[0]}, {rgdSamples1_off[0]}\n")

 

print(f'Data successfully saved to {file_name}')

 

dwf.FDwfAnalogOutReset(hdwf, c_int(0))

dwf.FDwfDeviceCloseAll()

Edited by Thomas1983
Link to comment
Share on other sites

  • 0

Hi @Thomas1983

As it is in most of the examples configure, wait, configure or ignore the first capture since this was performed before settle time.

dwf.FDwfAnalogInConfigure(hdwf, c_int(1), c_int(0))
# wait at least 2 seconds with Analog Discovery for the offset to stabilize, before the first reading after device open or offset/range change
time.sleep(2)

print("Starting repeated acquisitions")
dwf.FDwfAnalogInConfigure(hdwf, c_int(0), c_int(1))

For AD1,2 about 2sec is required and around 0.1sec with newer devices, mostly for the offset R/C to settle.
The wait also depends on the accuracy you want to achieve. For more accurate measurement with any device you should wait about 10 minutes to reach its stable operation al temperature.

You can also set the following before device open, used in many examples, 1 stop to have the device powered to prevent temperature drifts, or 0 continue running to also keep the last set offsets.

dwf.FDwfParamSet(DwfParamOnClose, c_int(0)) # 0 = run, 1 = stop, 2 = shutdown
 

 

Link to comment
Share on other sites

  • 0
Posted (edited)

Hi @attila

I have included the additional configuration, yet I am still encountering issues. Below is a graph generated by my Python script. I am observing unexpected "spikes" and irregular "longer/shorter" square wave-like features. I cannot seem to figure out why this is happening. Any insights would be greatly appreciated.

edit: I forgot to mention that I am using an AD2

SquareWave.png

Edited by Thomas1983
Link to comment
Share on other sites

  • 0

Hi @Thomas1983

I don't know what are you doing in your script. Are you still performing 1000 captures and saving only one sample from each ?
Start from a working example like the earlier mentioned AnalogIn_Trigger.py and try to customize it.

image.png

image.png

Link to comment
Share on other sites

  • 0
Posted (edited)

Hi @attila

Yes, I am doing that. However, instead of 8192 samples, I set it to 1 so I can associate a timestamp with each sample. I have included my Python file to provide more context.

Thank you again for your help

Additionally, I should mention this is what I am obtaining using Waveforms

VoltageWithTimestamp.pywaveforms.thumb.png.df2873c98b9a3f9987ea52d31ac87618.png

Edited by Thomas1983
Link to comment
Share on other sites

  • 0

Hi @attila

I want to associate a real timestamp with each sample. While I understand the approach of using (start_time + j/hzAcq.value) to calculate timestamps, I am concerned about whether the samples are actually being collected at the specified rate or if this method is simply generating an arbitrary range of values. Can you clarify this?

Thank you again for your help

Link to comment
Share on other sites

  • 0

Hi @Thomas1983

The samples are collected at the specified rate, at system frequency / N. FDwfAnalogInFrequencyGet returns the actual rate.

The perfcounter you have used usually returns high resolution 'CPU' counter value, useful for dt measurements, but not for date&time.

With AD1,2 you can use software timestamp + sample index/rate

Newer devices AD3, ADP2230, ADP3X50... have a high resolution counter used for trigger timestamp (8-10ns) which is synchronized on software connection. The FDwfAnalogInStatusTime returns this and can be used to annotate +/- sample index relative to T0.

 

 

Link to comment
Share on other sites

  • 0

Hi @attila

If I understand correctly, the 1000 samples I am collecting are being sampled at the specified rate of 100kHz. Does this mean that if I used my original perfcounter method correctly it would yield the same results?

Additionally, Is there even a way to use the perfcounter method to timestamp each sample? When inputting the 1000 samples into the FDwfAnalogInStatus function. It seems to collect all the samples instantly and I am unsure how to get a timestamp (using perfcounter) for each interval.

As a workaround, I resorted to inputting 1 sample at a time to get the timestamp and then using a for loop to iterate 1000 times.

Link to comment
Share on other sites

  • 0

Hi @Thomas1983

The FDwfAnalogInStatusSample return only one sample for each FDwfAnalogInStatus(hdwf, 0 or 1, &sts)
Collecting data this way will be done at inaccurate software/usb timing, 0.125ms to 10ms depending on system, load...

The FDwfAnalogInStatus(hdwf, 1, &sts) FDwfAnalogInStatusData(...) returns capture with the specified frequency.
The spacing between samples will be very precise, down to picoseconds, and trigger position at sample period precision, or better if you use FDwfAnalogInTriggerPositionStatus. If you don't use trigger the first sample will be collected immediately, with software/usb latency with us-ms delay. So, the approximate timing will be start_time+j/hzAcq.value as it is in VoltageWithTimestamp2.py Just use proper data&time instead perfcounter.

Link to comment
Share on other sites

  • 0

Hi @attila

Thank you for all your help.

I just have one last question.

I have an 8 channel multiplexer with channel 1 connected to the output and a function generator is sending a 2 kHz sine wave with a DC offset of 2.5 V to one of the inputs. I modified the code you provided and obtained the graph below (signals from mux, without looping). And I modified my code and obtained the graph below (signals from mux, with looping). 

My goal is to have the signals layered on top of each other (as seen in the “With Looping” graph) rather than appearing as a staircase. How can I fix my code in order to obtain a sine wave? Could it be an issue with my Trigger?

(The legend indicates which DIO pins are on, I am using DIO1,2,3)

WithLooping.png

WithoutLooping.png

WithoutLooping_Zoom.png

ReadSignalsFrom8x1Mux_WithoutLooping.py ReadSignalsFrom8x1Mux_WithLooping.py

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...