Author Topic: [Python] [SCPI DMM] High Frequency Data Acquisition on the Keysight 34420A  (Read 586 times)

0 Members and 1 Guest are viewing this topic.

Offline potato24Topic starter

  • Newbie
  • Posts: 3
  • Country: ca
I have a Keysight/Agilent 34420A 7.5 Digit Nano Volt/Micro Ohm Meter. I am using this for continuous resistance measurements on a system. I need a high sample rate to detect changes in the system in real-time

This is quite similar to what is done here: https://www.eevblog.com/forum/projects/(python)-flexible-data-acquisition-on-the-keysight-34465a/msg4898915/#msg4898915

The Digital Multi Meter (DMM) is able to get very fast data points, down to 0.3 ms per sample (3000 Hz). I only need 100 Hz as a sample rate. I am able to achieve an average of about 60 Hz sample rate, with some points being much slower (~30 Hz) and some points being faster with the following code:

Code: [Select]
import pyvisa as visa
import time
import csv
from datetime import datetime
 
# Connect instrument
rm = visa.ResourceManager()
inst= rm.open_resource('GPIB0::22::INSTR')  # Open instrument over USB - connected to GPIB of instrument
print(inst.query("*IDN?")) # Should return the meter's identification string: "HEWLETT-PACKARD, 34420A, 0,X.X-X.X-X.X"
# Setup instrument
inst.write("SENSe:FUNCtion \"FRESistance\"") # Set the instrument to the 4-wire resistance mode - FRES = Four resistance mode
inst.write("INPut:FILTer:STATe OFF") # Turn off the instrument filter - This filter is a moving average
inst.write("SENSe:FRESistance:NPLC 0.2") # Set the integration time to 0.2 power cycles -- If 60 Hz current, 0.2*(1/60)s = 3.3 ms or 300 readings/s
inst.write("SENSe:FRESistance:RANGE:AUTO OFF") # Turn off the AUTO range mode for 4-wire resistance - Will run faster -
inst.write("SENSe:FRESistance:RANGE 100") # Set the 4-wire resistance range to 100 ohm
inst.write("SENSe:FRESistance:OCOMpensated OFF") # Turn off the offset compensation for the 4-wire resistance mode
inst.write("SENSe:FRESistance:POWer:LIMit OFF ") # Turn off the lower power limit mode
inst.write("SENSe:FRESistance:NULL OFF") # Turn off the null function (reading = measurement - null)
inst.write("DISPlay OFF") # Turn off the instrument display - Will run faster
# Setting the Trigger settings
inst.write("TRIGger:DELay:AUTO OFF") # Turn off the auto trigger delay - Will run faster
inst.write("TRIGger:DELay 0") # Set the trigger delay to 0 ms - Will get another reading instantly after completing the reading
inst.write("TRIGger:COUNt 1") # Get infinite 1 data point when triggered
inst.write("TRIGger:SOURce IMMediate") # Set the instrument in the wait for trigger state, and trigger immediately with any command
 
# Start recording
# Writing to a file
with open("Resistance_Data_"+start_time.strftime("%Y-%m-%d_%H-%M-%S")+".csv", "w", newline="") as file:
       csv_writer = csv.writer(file, dialect="excel") # Create a csv writer for the file
       csv_writer.writerow(["Resistance [ohm]","Time [datetime]"])
       print("recording starting")
       while not flag: # Flag used to stop the recording in another thread
              csv_writer.writerow([float(inst.query("READ?")),datetime.now()]) #Perform reads as fast as possible
              # The READ? query tells the isntrument to get 1 reading and return it. Loops when done.
              # This happens at a variable speed.
print("recording stopped")
# Recording is now done, set the instrument into a more normal mode and close the connection
inst.write("DISPlay ON") # Turn off the instrument display - Will run faster
inst.write("SENSe:FRESistance:NPLC 10") # Reset the number of power cycles per integration
inst.write("SENSe:FRESistance:RANGE:AUTO ON")
inst.close()

Things to note about the code:
  • Each point only needs 1/300 seconds to be measured
  • The code gets 1 data point per "READ?" query
  • Since the setup time of the instrument is slower than the sampling rate (Configuration Rates = 26/s to 50/s vs Measurement rate of 300/s), the actual measurement rate is slower
  • The setup time being non-constant or variable, which is effectively the time per measurement, basically breaks the possibility of doing fast fourrier transform (FFT) (which requires a constant time interval for each point).

I am trying to modify the code to "TRIGger:COUNt INFinity" instead of "TRIGger:COUNT 1" so that it will continuously get data at the desired sample rate, and store it to memory. The issue is that the memory fills up really fast (Internal memory limited to 1024 readings). I am looking to modify the code in the following way, but I can't get it to work. My thoughts: roughly every second, when 300 points are stored in memory, query those and then the limited internal memory doesn't roll over and write over itself.

Main changes:
  • "TRIGger:COUNt INFinity" instead of "TRIGger:COUNT 1"
  • Triggering with "INITiate" first
  • Using "READ?" every so often to get the data dumped back

Code: [Select]
import pyvisa as visa
import time
import csv
from datetime import datetime
 
# Connect instrument
rm = visa.ResourceManager()
inst= rm.open_resource('GPIB0::22::INSTR')  # Open instrument over USB - connected to GPIB of instrument
print(inst.query("*IDN?")) # Should return the meter's identification string: "HEWLETT-PACKARD, 34420A, 0,X.X-X.X-X.X"
# Setup instrument
inst.write("SENSe:FUNCtion \"FRESistance\"") # Set the instrument to the 4-wire resistance mode - FRES = Four resistance mode
inst.write("INPut:FILTer:STATe OFF") # Turn off the instrument filter - This filter is a moving average
inst.write("SENSe:FRESistance:NPLC 0.2") # Set the integration time to 0.2 power cycles -- If 60 Hz current, 0.2*(1/60)s = 3.3 ms or 300 readings/s
inst.write("SENSe:FRESistance:RANGE:AUTO OFF") # Turn off the AUTO range mode for 4-wire resistance - Will run faster -
inst.write("SENSe:FRESistance:RANGE 100") # Set the 4-wire resistance range to 100 ohm
inst.write("SENSe:FRESistance:OCOMpensated OFF") # Turn off the offset compensation for the 4-wire resistance mode
inst.write("SENSe:FRESistance:POWer:LIMit OFF ") # Turn off the lower power limit mode
inst.write("SENSe:FRESistance:NULL OFF") # Turn off the null function (reading = measurement - null)
inst.write("DISPlay OFF") # Turn off the instrument display - Will run faster
# Setting the Trigger settings
inst.write("TRIGger:DELay:AUTO OFF") # Turn off the auto trigger delay - Will run faster
inst.write("TRIGger:DELay 0") # Set the trigger delay to 0 ms - Will get another reading instantly after completing the reading
inst.write("TRIGger:COUNt INFinity") # Get infinite data until stopped
inst.write("TRIGger:SOURce IMMediate") # Set the instrument in the wait for trigger state, and trigger immediately with any command
 
# Start recording
# Writing to a file
with open("Resistance_Data_"+start_time.strftime("%Y-%m-%d_%H-%M-%S")+".csv", "w", newline="") as file:
       csv_writer = csv.writer(file, dialect="excel") # Create a csv writer for the file
       csv_writer.writerow(["Resistance [ohm]"])
       print("recording starting")
       inst.write("INITiate") # Trigger the instrument to start recording.
       while not flag: # Flag used to stop the recording in another thread
              time.sleep(1) # wait 1 second to get the readings, and then read
              csv_writer.writerow([float(inst.query("READ?"))])
              # I think this should dump all the values stored in the memory into the csv (I can format later)
       inst.write("DCL") # Device clear to stop the infinite looping
print("recording stopped")
# Recording is now done, set the instrument into a more normal mode and close the connection
inst.write("DISPlay ON") # Turn off the instrument display - Will run faster
inst.write("SENSe:FRESistance:NPLC 10") # Reset the number of power cycles per integration
inst.write("SENSe:FRESistance:RANGE:AUTO ON")
inst.close()

I can't get this to work. I get some error with the DMM when I "INITiate" and I also tried using "READ?" to start, but then I get a timeout error. I think I might also have an issue because I am not emptying the memory? I'm not really sure, and I was hoping to get some help.

Some additional information from the user manual attached:

Number of Triggers
Normally, the meter will accept only one trigger before returning to the “idle”
trigger state. You can, however, instruct the meter to accept multiple triggers.
This feature is available only from the remote interface. If you set the trigger count
and then go to local (front panel), the meter ignores the trigger count setting;
when you return to remote, the trigger count returns to the value you selected.
– The selected number of triggers is stored in volatile memory; the meter sets
the trigger count to 1 when power has been off or after a remote interface
reset or preset. The CONFigure and MEASure? commands automatically set
the trigger count to 1.
– The number of triggers can be set to any number between 1 (MIN) and 50000
(MAX) or can be set to be INFinity. When the trigger count is set to be infinite,
use the READ? command to collect the readings. Send a device clear to stop
the measurements. See page 211 for information about device clear.
TRIGger:COUNt {<value>|MIN|MAX|INFinity}


Internal Triggering
In the internal trigger mode (remote interface only), the
trigger signal is always present. When you place the meter in the wait-for-trigger
state, the trigger is issued immediately. This is the power-on trigger source for
remote interface operation.
To select the internal trigger source, send the following command. The
CONFigure and MEASure? commands automatically set the trigger source to
IMMediate.
TRIGger:SOURce IMMediate
After setting the source to IMMediate, an INITitate or a READ? command will
trigger the measurement.

Thanks in advance.
 

Offline BennoG

  • Regular Contributor
  • *
  • Posts: 65
  • Country: nl
Why not let the meter running in free running mode and only request the latest measurement as fast as you can ?

Benno
 

Offline potato24Topic starter

  • Newbie
  • Posts: 3
  • Country: ca
From my understanding, I thought that is what my code is doing in the first bit of code. But there seems to be an autotriggering mode, which triggers (measures) as fast as possible for the current configuration.

I'm just not sure if I can get autotriggering to work with a remote connection: "You must specify the source from which the meter will accept a trigger. – The trigger source is stored in volatile memory; the source is set to autotrigger (front panel) or immediate (remote interface) when power has been off or after a remote interface reset. Autotriggering In the autotrigger mode (front panel only), the meter continuously takes readings at the fastest rate possible for the present configuration. This is the power-on trigger source for front-panel operation."

The notes on immediate triggering (which is what I currently have written into the code): "Internal Triggering In the internal trigger mode (remote interface only), the trigger signal is always present. When you place the meter in the wait-for-trigger state, the trigger is issued immediately. This is the power-on trigger source for remote interface operation. To select the internal trigger source, send the following command. The CONFigure and MEASure? commands automatically set the trigger source to IMMediate.
TRIGger:SOURce IMMediate
After setting the source to IMMediate, an INITitate or a READ? command will trigger the measurement."

I also read that: "When using the READ? function Readings are not stored in the meter’s internal memory." Which may be one of the reasons I am having issues.

I'll try using the FETCH? command maybe: "Use the FETCh? command to transfer the readings from the meter’s internal memory to the meter’s output buffer where you can read them into your bus controller. The INIT and FETCh? commands use the meter’s memory."
 

Online Kleinstein

  • Super Contributor
  • ***
  • Posts: 14209
  • Country: de
A 100 Hz sampling rate is a bit odd, as this is just a little faster than 1 PLC ( 60Hz in Canada) and would be quite sensitive to mains hum. If 60 SPS is still fast enough this would make things a lot lower noise / interference. If not, one could get better result from considerably faster (e.g. 500 SPS) readings and than externally subtracting the 60 Hz mains hum. This would however need a really stable reading rate.

The HP34420 linke the 34401 is not really that suitable for fast readings. The ADC is far from 7 digits, maybe not even 5 digits at higher speed. So unless you really need the low noise (but without AZ mode not necessary low dift) for really low votlages the HP34420 may not be the best choice.
 

Offline potato24Topic starter

  • Newbie
  • Posts: 3
  • Country: ca
The 100 Hz minimum is specifically because the physical phenomena trying to be observed will affect the measurement at a frequency between 2-50 Hz. So as per the Nyquist theorum, I'm trying to get 2x50 as a minimum sampling rate. There isn't an issue to go faster though.

You bring up a good point with the power. I am eventually looking to also change my recording methods to set up the resistance measurement to be real-time with a pressure sensor. Currently I have pressure transducers like these (4-20 mA with fast response time <1 ms: https://canada.newark.com/omega/px309-005a5v/pressure-sensor-analogue-5psi/dp/30AC8106). I don't know how I will set up the recording of resistance and pressure to be done at the same time. I was considering a DAQ like a https://labjack.com/ but these only seem to do voltage measurements, and the pressure transducer does current signal. I'm also not sure how to integrate a 4-point resistivity measurement in recording system in this way.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf