Jump to content
  • 0

MCC128 DAQ hats - Signal oscillating between value and 0.


JonDe

Question

We have 2 DAQ hats set up to run at 50 kSa/s, over 3 channels, and the signal is oscillating between the Signal value and 0. This wasn't happening when we fed it with a DC voltage using a potentiometer. We are now testing with a function generator, and optical detector and this keeps happening. What is the solution to this? Is it a software or hardware problem. Charging Capacitor? With the function generator, the sin wave is basically hatched under the curve, as it oscillates between Asin(theta) and 0.

Edited by JonDe
clarity
Link to comment
Share on other sites

17 answers to this question

Recommended Posts

  • 0

If the MCC 128 input is in differential mode and your signal generator has an isolated output, then the system is hunting for a ground reference. The typical solution is to switch to a single-ended mode so that it measures with reference to the system ground. Another solution is to attach a 47k - 100k ohm reference resistor from the differential low side to the system ground. 

Link to comment
Share on other sites

  • 0

Thanks for the response, we are in Single Ended Mode. This is 4s of data, 10kHz from the function generator, and how it appears on the ADC. Yellow wire is the signal we are inputing, black is GND and blue and green are the other inputs which are not turned on.

Figure_1.png

IMG_5666.jpg

Link to comment
Share on other sites

  • 0

Thanks for the response, we are in Single Ended Mode. This is 4s of data, 10kHz from the function generator, and how it appears on the ADC. Yellow wire is the signal we are inputing, black is GND and blue and green are the other inputs which are not turned on.

Figure_1.png

IMG_5666.jpg

Link to comment
Share on other sites

  • 0

Thanks 

This code runs the ADC, second code plots.  Doesn't seem to matter which of the 3 inputs you connect you get the same output from each of the 3 channels in use.

#!/usr/bin/env python
#  -*- coding: utf-8 -*-
"""
MCC 128 Functions Demonstrated:
mcc128.trigger_mode
mcc128.a_in_scan_start
mcc128.a_in_scan_status
mcc128.a_in_scan_read
mcc128.a_in_scan_stop
mcc128_a_in_scan_cleanup
mcc128.a_in_mode_write
mcc128.a_in_range_write

Purpose:
Get synchronous data from multiple MCC 128 HAT devices.

Description:
This example demonstrates acquiring data synchronously from multiple
MCC 128 HAT devices.  This is done using the external clock and
external trigger scan options.  The CLK terminals must be connected
together on all MCC 128 HAT devices being used and an external trigger
source must be provided to the TRIG terminal on the master MCC 128 HAT
device.  The EXTCLOCK scan option is set on all of the MCC 128
HAT devices except the master and the EXTTRIGGER scan option is
set on the master.
"""
from __future__ import print_function
from sys import stdout
from daqhats import hat_list, mcc128, OptionFlags, HatIDs, TriggerModes, \
HatError, AnalogInputMode, AnalogInputRange
from daqhats_utils import enum_mask_to_string, chan_list_to_mask, \
validate_channels, input_mode_to_string, input_range_to_string
import numpy as np
import csv
import matplotlib.pyplot as plt
import datetime
import time


# Constants
DEVICE_COUNT = 2
MASTER = 0
CURSOR_SAVE = "\x1b[s"
CURSOR_RESTORE = "\x1b[u"
CURSOR_BACK_2 = '\x1b[2D'
ERASE_TO_END_OF_LINE = '\x1b[0K'

def main():
"""
This function is executed automatically when the module is run directly.
"""
hats = []
# Define the input modes for each MCC 128
input_modes = [
AnalogInputMode.SE,
AnalogInputMode.SE
]
# Define the input ranges for each MCC 128
input_ranges = [
AnalogInputRange.BIP_10V,
AnalogInputRange.BIP_10V
]
# Define the channel list for each HAT device
#chans = [{0, 1},{0, 1}]
chans = [{0},{0,1}]
# Define the options for each HAT device
options = [
OptionFlags.CONTINUOUS,
OptionFlags.CONTINUOUS
]
samples_per_channel = 50000
sample_rate = 50000.0  # Samples per second
#trigger_mode = TriggerModes.RISING_EDGE

try:
# Get an instance of the selected hat device object.
hats = select_hat_devices(HatIDs.MCC_128, DEVICE_COUNT)

# Validate the selected channels, set the modes and ranges.
for i, hat in enumerate(hats):
validate_channels(chans[i], hat.info().NUM_AI_CHANNELS[input_modes[i]])
hat.a_in_mode_write(input_modes[i])
hat.a_in_range_write(input_ranges[i])

# Set the trigger mode for the master device.
#hats[MASTER].trigger_mode(trigger_mode)

# Calculate the actual sample rate.
actual_rate = hats[MASTER].a_in_scan_actual_rate(len(chans[MASTER]),
sample_rate)

print('MCC 128 multiple HAT example using external clock and',
 'external trigger options')
print('    Functions demonstrated:')
print('      mcc128.trigger_mode')
print('      mcc128.a_in_scan_start')
print('      mcc128.a_in_scan_status')
print('      mcc128.a_in_scan_read')
print('      mcc128.a_in_scan_stop')
print('      mcc128.a_in_scan_cleanup')
print('      mcc128.a_in_mode_write')
print('      mcc128.a_in_range_write')
print('    Samples per channel:', samples_per_channel)
print('    Requested Sample Rate: {:.3f} Hz'.format(sample_rate))
print('    Actual Sample Rate: {:.3f} Hz'.format(actual_rate))
#print('    Trigger type:', trigger_mode.name)

for i, hat in enumerate(hats):
print('    HAT {}:'.format(i))
print('      Address:', hat.address())
print('      Input mode: ', input_mode_to_string(input_modes[i]))
print('      Input range: ', input_range_to_string(input_ranges[i]))
print('      Channels: ', end='')
print(', '.join([str(chan) for chan in chans[i]]))
options_str = enum_mask_to_string(OptionFlags, options[i])
print('      Options:', options_str)

#print('\n*NOTE: Connect the CLK terminals together on each MCC 128')
#print('       HAT device being used. Connect a trigger source')
#print('       to the TRIG input terminal on HAT 0.')

try:
input("\nPress 'Enter' to continue")
print("\n Press CTRL+C to end")
except (NameError, SyntaxError):
pass

# Start the scan.
for i, hat in enumerate(hats):
chan_mask = chan_list_to_mask(chans[i])
hat.a_in_scan_start(chan_mask, samples_per_channel, sample_rate,
options[i])

#print('\nWaiting for trigger ... Press Ctrl-C to stop scan\n')

try:
# Monitor the trigger status on the master device.
#wait_for_trigger(hats[MASTER])
global start
start = time.time()
# Read and display data for all devices until scan completes
# or overrun is detected.
read_and_display_data(hats, chans)

except KeyboardInterrupt:
# Clear the '^C' from the display.
print(CURSOR_BACK_2, ERASE_TO_END_OF_LINE, '\nAborted\n')
end = time.time()

except (HatError, ValueError) as error:
print('\n', error)

finally:
for hat in hats:
hat.a_in_scan_stop()
hat.a_in_scan_cleanup()

def wait_for_trigger(hat):
"""
Monitor the status of the specified HAT device in a loop until the
triggered status is True or the running status is False.

Args:
hat (mcc128): The mcc128 HAT device object on which the status will
be monitored.

Returns:
None

"""
# Read the status only to determine when the trigger occurs.
is_running = True
is_triggered = False
while is_running and not is_triggered:
status = hat.a_in_scan_status()
is_running = status.running
is_triggered = status.triggered

def read_and_display_data(hats, chans):
"""
Reads data from the specified channels on the specified DAQ HAT devices
and updates the data on the terminal display.  The reads are executed in a
loop that continues until either the scan completes or an overrun error
is detected.

Args:
hats (list[mcc128]): A list of mcc128 HAT device objects.
chans (list[int][int]): A 2D list to specify the channel list for each
mcc128 HAT device.

Returns:
None

"""
samples_to_read = 500
timeout = 5  # Seconds
samples_per_chan_read = [0] * DEVICE_COUNT
total_samples_per_chan = [0] * DEVICE_COUNT
is_running = True

# Create blank lines where the data will be displayed
for _ in range(DEVICE_COUNT * 4 + 1):
print('')
# Move the cursor up to the start of the data display.
print('\x1b[{0}A'.format(DEVICE_COUNT * 4 + 1), end='')
print(CURSOR_SAVE, end='')

while True:
data = [None] * DEVICE_COUNT
# Read the data from each HAT device.
for i, hat in enumerate(hats):
read_result = hat.a_in_scan_read(samples_to_read, timeout)
data[i] = read_result.data
is_running &= read_result.running
samples_per_chan_read[i] = int(len(data[i]) / len(chans[i]))
total_samples_per_chan[i] += samples_per_chan_read[i]

if read_result.buffer_overrun:
print('\nError: Buffer overrun')
break
if read_result.hardware_overrun:
print('\nError: Hardware overrun')
break

with open("adc_demo_multi.txt", "a") as f:
for item in read_result.data:
f.write('{:.8f}'.format(time.time()-start) + '\t')
f.write('{:10.5f}'.format(read_result.data[0]) + '\t')
f.write('{:10.5f}'.format(read_result.data[1])+ '\t')
f.write('{:10.5f}'.format(read_result.data[2]) + '\n')

print(CURSOR_RESTORE, end='')

stdout.flush()

if not is_running:
break


def select_hat_devices(filter_by_id, number_of_devices):
"""
This function performs a query of available DAQ HAT devices and determines
the addresses of the DAQ HAT devices to be used in the example.  If the
number of HAT devices present matches the requested number of devices,
a list of all mcc128 objects is returned in order of address, otherwise the
user is prompted to select addresses from a list of displayed devices.

Args:
filter_by_id (int): If this is :py:const:`HatIDs.ANY` return all DAQ
HATs found.  Otherwise, return only DAQ HATs with ID matching this
value.
number_of_devices (int): The number of devices to be selected.

Returns:
list[mcc128]: A list of mcc128 objects for the selected devices
(Note: The object at index 0 will be used as the master).

Raises:
HatError: Not enough HAT devices are present.

"""
selected_hats = []

# Get descriptors for all of the available HAT devices.
hats = hat_list(filter_by_id=filter_by_id)
number_of_hats = len(hats)

# Verify at least one HAT device is detected.
if number_of_hats < number_of_devices:
error_string = ('Error: This example requires {0} MCC 128 HATs - '
'found {1}'.format(number_of_devices, number_of_hats))
raise HatError(0, error_string)
elif number_of_hats == number_of_devices:
for i in range(number_of_devices):
selected_hats.append(mcc128(hats[i].address))
else:
# Display available HAT devices for selection.
for hat in hats:
print('Address ', hat.address, ': ', hat.product_name, sep='')
print('')

for device in range(number_of_devices):
valid = False
while not valid:
input_str = 'Enter address for HAT device {}: '.format(device)
address = int(input(input_str))

# Verify the selected address exists.
if any(hat.address == address for hat in hats):
valid = True
else:
print('Invalid address - try again')

# Verify the address was not previously selected
if any(hat.address() == address for hat in selected_hats):
print('Address already selected - try again')
valid = False

if valid:
selected_hats.append(mcc128(address))

return selected_hats


if __name__ == '__main__':
# This will only be run when the module is called directly.
main()    

 

Link to comment
Share on other sites

  • 0
#library imports
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
from scipy.signal import butter,filtfilt

file = 'adc_demo_multi.txt'
df = pd.read_csv(file,delimiter='\t', header=None, index_col=False)
df.columns = ['Timestamp', 'Voltage', 'Opt_Voltage', 'Side_Voltage']
df = df.astype('float')
df['Voltage'] = df['Voltage'].apply(lambda x: round(x, 4))
df['Opt_Voltage'] = df['Opt_Voltage'].apply(lambda x: round(x, 4))

df.reset_index(inplace=True)
df.drop('index', axis=1, inplace=True)

df['Voltage'] = (df['Voltage'] - df['Voltage'].mean())
df['Opt_Voltage'] = (df['Opt_Voltage'] - df['Opt_Voltage'].mean())

df= df.reset_index()
df.drop(columns='index', inplace=True)
print(df.head())


time = df['Timestamp']  

plt.figure(1)
plt.plot(time,df['Opt_Voltage'])
plt.xlim(0,0.5)

plt.figure(2)
plt.plot(time,df['Side_Voltage'])
plt.xlim(0,5)

plt.figure(3)
plt.plot(time,df['Voltage'])
plt.xlim(0,5)

voltage = df['Voltage']
cutoff=10
order=2
T = len(time)  # Sample Period
fs = 1 / np.mean(np.diff(time))  # sample rate, Hz
nyq = 0.5 * fs  # Nyquist Frequency
normal_cutoff = cutoff / nyq
# Get the filter coefficients
b, a = butter(order, normal_cutoff, btype='high', analog=False)
y_filt = filtfilt(b, a, voltage)
y_filt_HP = pd.DataFrame(y_filt)
y_filt_HP['Timestamp'] = time
y_filt_HP = y_filt_HP.rename(columns={0: 'Voltage'})
print(y_filt_HP.head())


#notch filter
time = y_filt_HP['Timestamp']
f = [50, 100, 150, 200]
Q = [20, 10, 20, 20]
fs = 1 / np.mean(np.diff(time))
count = 0
for i, j in zip(f, Q):
  b, a = signal.iirnotch(i, j, fs)
  if count == 0:
     notch_filtered = signal.filtfilt(b, a, y_filt_HP['Voltage'])
  else:
     notch_filtered = signal.filtfilt(b, a, notch_filtered)
  count = count + 1

filtered_df = pd.DataFrame(notch_filtered)
filtered_df['Timestamp'] = df['Timestamp']
filtered_df = filtered_df.rename(columns={0: 'Voltage'})_________

 

Link to comment
Share on other sites

  • 0

whatever i do i seem to components of each channel in use on the other channels in use. I have tried connecting all the unused channels to ground. and connecting all the grounds together but it only made it worses. How you connect the grounds does seem to affect it. Best I have managed is only 2 of the 3 channels to have cross talk.

Link to comment
Share on other sites

  • 0
Posted (edited)

I think the issue maybe that all the channels are mixed together. 


 

with open("adc_demo_multi.txt", "a") as f:
for item in read_result.data:
f.write('{:.8f}'.format(time.time()-start) + '\t')
f.write('{:10.5f}'.format(read_result.data[0]) + '\t')
f.write('{:10.5f}'.format(read_result.data[1])+ '\t')
f.write('{:10.5f}'.format(read_result.data[2]) + '\n')

when i look at the output of these lines of the code; dT gives a sample rate of 322kSa/s which I believe is the max aggregated sample rate. How would we solve this? we are looking to save the output of the 3 channels in use, at 50kSa/s

 

I would expect dT to give 50kSa/s

Edited by JonDe
Link to comment
Share on other sites

  • 0

I have attached a Python script to capture a few seconds of data and plot the first 100 values. Attached to CH0 is a 10k Hz sine wave; CH1 is connected to the ground.  The sample rate is set to 50,000. The plot displays A/D counts where 65535 is +10 volts and 0 counts is -10 volts. Because the MCC 128 is a multiplexed board, source impedance must be kept below 100 ohms to avoid channel cross-talk. 

 

image.png

testCont.py

Link to comment
Share on other sites

  • 0

ok i have run the code on each of the hats in the stack up individually and it works and is logging, now to figure out how to run both simultaneously and to save the data, any advice much appreciated

Link to comment
Share on other sites

  • 0

I have attached a new Python script. It is our continuous_scan.py example modified to save the data to a binary file, which is faster than a CSV  or TXT file. It uses time() to determine if four seconds have elapsed and, if so, stop.

Please review our multi_hat_sychronuous_scan.py example, especially the comments about connecting the external clock pins and applying an external trigger. You can adapt part of my testCont_to_file.py example to write two binary files, one for each board. Once you have it working with two files, you can combine them into one file. 

testCont_to_file.py

Link to comment
Share on other sites

  • 0

look at the following code in the read_data function. 

       new_time = time()
        elapsed_time = new_time - start_time
        if(elapsed_time > 5):
            break

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