DAQ - Digital multimeter data acquisition software
Keysight 34461A digital multimeter could measure up to
1000 samples/s and store 10,000 samples to internal memory. At
maximum sample rate that's enough just for ten seconds of
measurement. DAQ software
extends 34461A DMM measurement time by fetching data from internal DMM buffer
thus making space for new readings.
DAQ is written in C#, using .NET 4 and WPF (Windows
Presentation Foundation) for user interface. DMM is controlled through the
SCPI language (SCPI
- Standard Commands for Programmable Instruments), so it
should work on other DMM's beside 34461A (currently DAQ is tested on
34461A and 34465A). DAQ
supports USB and Ethernet connections to DMM.
For establishing communication
with DMM, information from David Tu's blog were used: 'Continuous
Measurements with a 34461A Digital Multimeter'.
Prerequisites:
- Windows 7 or above
-
Keysight IO Library Suite (should be installed prior to DAQ
installation)
-
DMM firmware should be up to date. DAQ is tested for firmware
version 2.11
-
Microsoft Visual Studio 2010 or above for modifying project
Below is a detailed description of installation and measurement
procedure. You may start immediately by downloading
DAQ installation
and/or DAQ project
containing complete Visual Studio project with source files. Current
version is 1.0.3, please check revision history.
DAQ installation
Download installation file
DAQsetup.zip, extract it to the folder of your choice and run setup.
Installer will create desktop icon and Start menu shortcut.
Starting either way DAQ will open the main window:
Preparing and performing data acquisition
1. Setting communication options
Selecting Settings menu option opens dialog for specifying
communication options and selecting AC main frequency for timing
calculations:
DAQ currently supports connection through USB and Ethernet. Connection string for connected instrument is easily determined
with Connection Expert, which comes with
Keysight IO Library Suite. Sample USB connection string is
shown below.
2. Connecting instrument
Assuming that DMM is properly connected by USB or Ethernet cable
and settings are correct, pressing Connect button will
establish connection with DMM and displays DMM ID right to the Connect button.
'Connected to DAQ' message will be displayed at the instrument panel.
3. Setting acquisition parameters
DAQ supports Auto (internal) and External trigger source. For
Auto setting DMM takes single measurement on periodic intervals
determined by Sample rate value.
For External option TTL level trigger signal should be connected to rear
Ext Trg connector. DMM takes single measurement on selected
active slope of external trigger signal. If frequency of external trigger
signal is known it should be written into Sample rate field.
That will ensure that output data time column contains accurate
time, otherwise it will be set to zero. Frequency of external
trigger signal should be between 1 Hz and 1000 Hz.
Sample rate should be between 1 and 1000 samples per
second. Recording time could be set only for auto
triggering, where DAQ will automatically stop acquisition when the
time expires. For external trigger it is necessary to manually stop
acquisition.
Select file button opens file manager dialog to select
folder and data file.
4. Setting measurement type and range
For high speed sampling auto range DMM mode is not possible
(because it takes time for DMM to adjust range), so it is necessary
to select range according to highest expected measured value.
Currently DAQ supports DC voltage, current and resistance measurement. If needed
it is easy to implement other types of measurements that DMM support
(frequency, temperature, ...)
From version 1.0.3 setting of measurement type and range are
separated:
5. Starting data acquisition
Pressing Start button starts acquisition. Acquisition task
is executed in separate thread, so application GUI stays responsive.
Remaining time is displayed both at DAQ progress bar and at DMM
panel. Pressing Stop button will abort acquisition. Already
acquired data will be saved.
Stored data format
Data are saved to text file. Header (lines beginning with '*')
contains information about measurements. Measurements are formatted in two
Tab delimited columns representing time and measured value.
Recorded data
* Date and time:2.2.2016. 15:06:38
* Instrument:Agilent
Technologies,34461A,MY53209449,A.01.10-02.25-01.10-00.35-01-01
* Sample rate: 1000 samples/s
* Recording time: 1 s
* Measurement range: VOLT - 1000
* -------- DAQ utility by Davor Antonic --------
*
* Data format: [time value] (Tab delimited)
0.001000 -194.976644
0.002000 -271.193274
0.003000 -311.453219
0.004000 -317.691102
0.005000 -315.381838
0.006000 -253.534825
0.007000 -180.038568
0.008000 -93.833547
0.009000 6.122927
0.010000 105.606809
0.011000 193.140149
. . .
0.993000 152.967992
0.994000 235.015273
0.995000 301.608158
0.996000 315.640466
0.997000 318.318834
0.998000 292.383062
0.999000 220.685963
1.000000 133.152638 |
Formatted data is easy to import into chosen application for
further processing. Sample MATLAB code for importing and plotting data is provided below:
MATLAB functions
% Read data from file into two-column array
function [measurements]=readDAQ(fname)
fid=fopen(fname);
% skip header (beginning with '*') and read data
C = textscan(fid,'%f %f','CommentStyle','*');
measurements = [C{1} C{2}];
fclose(fid);
% Plot data
function plotDAQ(data)
h = figure('Name', 'DAQ', 'OuterPosition',
[100,100,1000,600]);
plot(data(:,1),data(:,2),'-bx','LineWidth',1.5,'MarkerEdgeColor','r');
ha = gca;
set(ha, 'Box', 'on');
grid on
xlabel('t [s]');
ylabel('U [V]'); |
Following plot presents first 200 ms of acquired data. DMM was set to 1000V
DC range and connected to main voltage (220V AC, 50 Hz)
Known issues:
- DMM timing calculation is adjusted for 50Hz main. For 60Hz
main TRIG:DEL should be adjusted accordingly (file 'InstrumentControl.cs',
line 88) - FIXED
- DAQ one million samples limit is imposed by TRIG:COUNT
limit. Setting number of samples to INF doesn't work (instrument
doesn't record any data). -
FIXED
Code for performing measurements
Function btnStart_Click is event handler for Start button. It performs setting validity check and starts acquisition thread.
Function btnStart_Click
/// <summary>
/// Check settings validity (sample rate, recording time, file) and start data acquisition
/// in separate thread
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStart_Click(object sender, RoutedEventArgs e)
{
. . .
SystemSounds.Beep.Play();
btnStart.Background = Brushes.PaleGreen;
// Data acquisition thread
DAQThread = new Thread(new ThreadStart(DAQmethod));
// Thread updating GUI should be STA (Single Threaded Apartment)
DAQThread.SetApartmentState(ApartmentState.STA);
DAQThread.IsBackground = true;
DAQThread.Start();
Settings.newFile = false;
}
|
Function DAQmethod (executed in separate thread)
/// <summary>
/// Configure DMM for selected measurement, acquire and write data to file
/// (running in separate thread)
/// Revisions:
/// 2016-02-24 D.A. added:
/// - external trigger support
/// - timing calculation for 50Hz / 60Hz AC power
/// </summary>
public void DAQmethod()
{
Settings.Abort = false; // not aborted
try
{
Stopwatch stopWatch = new Stopwatch();
Settings.FileHandle = new StreamWriter(Settings.FileName);
// Header
Settings.FileHandle.WriteLine("* Date and time:" + DateTime.Now);
Settings.FileHandle.WriteLine("* Instrument:" + Settings.IDN);
if (Settings.ExtTrigger)
{
if (Settings.SampleRate != 0)
Settings.FileHandle.WriteLine("* External trigger - Sample rate: " + Settings.SampleRate + " samples/s");
else
Settings.FileHandle.WriteLine("* External trigger - 'time' should be determined" + " from trigger source");
}
else
{
Settings.FileHandle.WriteLine("* Sample rate: " + Settings.SampleRate + " samples/s");
Settings.FileHandle.WriteLine("* Recording time: " + Settings.RecTime + " s");
}
Settings.FileHandle.WriteLine("* Measurement range: " + Settings.MeasurementType + " - " +
Settings.MeasurementRange);
Settings.FileHandle.WriteLine("* -------- DAQ utility by Davor Antonic --------");
Settings.FileHandle.WriteLine("*");
Settings.FileHandle.WriteLine("* Data format: [time value] (Tab delimited)");
//Configure the DMM measurement state
// DC voltage/current range selection
Settings.Dmm.WriteString("CONF:" + Settings.MeasurementType + ":DC " + Settings.MeasurementRange, true);
if (Settings.MeasurementType == "VOLT")
{
//10MOhm voltage input impedance setting , "ON" sets >10GOhm
Settings.Dmm.WriteString(Settings.MeasurementType + ":IMP:AUTO OFF", true);
}
// Autozero Off Settings.Dmm.WriteString("SENS:" + Settings.MeasurementType + ":DC:ZERO:AUTO OFF", true);
//Set integration time to .02 PLC (400 us at 50 Hz; 333 us at 60 Hz)
Settings.Dmm.WriteString(Settings.MeasurementType + ":NPLC .02", true);
CheckDMMError(Settings.Dmm); // Check the DMM for errors
// Trigger settings
int totNoSamples = 0;
if (Settings.ExtTrigger)
{
// Set trigger to external and set slope
if (Settings.UpTrigger) Settings.Dmm.WriteString("TRIG:SOUR EXT;SLOP POS", true);
else Settings.Dmm.WriteString("TRIG:SOUR EXT;SLOP NEG", true);
Settings.Dmm.WriteString("TRIG:DEL:AUTO OFF", true); //Turn OFF Automatic trigger delay
Settings.Dmm.WriteString("TRIG:DEL 0", true);
Settings.Dmm.WriteString("TRIG:COUN INF", true); //Set trigger count
}
else
{
totNoSamples = Settings.SampleRate * Settings.RecTime;
Settings.Dmm.WriteString("TRIG:SOUR IMM", true); //Set trigger source to internal
Settings.Dmm.WriteString("TRIG:DEL:AUTO OFF", true); //Turn OFF Automatic trigger delay
// Trigger delay: T = delay + Integration time + 0.51ms => Delay = T - 0.51ms - 0.02 / MainFreq
double delay = 1 / (double)Settings.SampleRate - 0.00051 - 0.02 / Settings.MainFreq;
string strDelay = delay.ToString(CultureInfo.InvariantCulture);
Settings.Dmm.WriteString("TRIG:DEL " + strDelay, true);
// Set trigger count to infinity - requires Firmware version 2.11 or above
Settings.Dmm.WriteString("TRIG:COUN INF", true);
}
CheckDMMError(Settings.Dmm); // Check the DMM for errors
// Allow up to 25 seconds for DMM to make readings
Settings.Dmm.IO.Timeout = 25000;
// Start acquisition
Settings.Dmm.WriteString("SYST:BEEP", true); // DMM beep
Settings.Dmm.WriteString("INIT", true); // Start
// Show progress bar
Dispatcher.BeginInvoke(new Action(() =>
{
if (Settings.ExtTrigger) pbrAcquisition.IsIndeterminate = true;
else pbrAcquisition.IsIndeterminate = false;
pbrAcquisition.Visibility = Visibility.Visible;
tbkAcquisition.Visibility = Visibility.Visible;
tbkAcquisition.Text = "";
btnStart.IsEnabled = false;
}));
double time, value = 0;
double totalTime = 0; // beginning time
// Acquired number of samples
int noChunks;
// For external trigger acquisition should be stopped manually
if (Settings.ExtTrigger) noChunks = Int32.MaxValue;
else noChunks = totNoSamples / Settings.ChunkSize;
stopWatch.Start();
// Acquire requested number of data chunks and check for abort condition
for (int i = 0; i < noChunks && !Settings.Abort; i++)
{
// Acquire and remove chunk from instrument buffer and save to file
Settings.Dmm.WriteString("DATA:REM? " + Settings.ChunkSize.ToString() + ",WAIT", true);
//Read the result into a ',' separated array
Object[] DCVResult = (object[])Settings.Dmm.ReadList(IEEEASCIIType.ASCIIType_Any, ",;");
for (int j = 0; j < Settings.ChunkSize && !Settings.Abort; j++)
{
if (Settings.SampleRate == 0) time = 0;
else time = totalTime + (double)(j + 1) / Settings.SampleRate;
value = Convert.ToDouble(DCVResult[j]);
// Save chunk to file
string line = time.ToString(Settings.SampleRate == 0 ? "F0" : "F6", CultureInfo.InvariantCulture) +
"\t" + value.ToString("F6", CultureInfo.InvariantCulture);
Settings.FileHandle.WriteLine(line);
}
Settings.FileHandle.Flush();
// update time
if (Settings.SampleRate > 0) totalTime += (double)(Settings.ChunkSize) / Settings.SampleRate;
// Display progress (for internal trigger remaining time; for external trigger elapsed time)
if (!Settings.ExtTrigger)
{
int remainingTime = Settings.RecTime - (int)totalTime;
double progress = 1 - ((double)(remainingTime) / Settings.RecTime);
Dispatcher.BeginInvoke(new Action(() =>
{
pbrAcquisition.Value = 100 * progress;
tbkAcquisition.Text = "Remaining time: " + remainingTime.ToString() + " s";
}));
// Show remaining time at DMM display
Settings.Dmm.WriteString("DISP:TEXT \"" + remainingTime.ToString() + " s left\n" +
value.ToString("F4") + " " + Settings.MeasurementUnit + "\n\nDAQ by D.A." + "\"", true);
}
else // for external trigger display elapsed time
{
Dispatcher.BeginInvoke(new Action(() =>
{
tbkAcquisition.Text = String.Format("Time elapsed: {0:hh\\:mm\\:ss}", stopWatch.Elapsed);
}));
// Show remaining time at DMM display
Settings.Dmm.WriteString("DISP:TEXT \"" + String.Format("Elapsed: {0:hh\\:mm\\:ss}", stopWatch.Elapsed) + "\n" + value.ToString("F4") + " " + Settings.MeasurementUnit + "\n\nDAQ by D.A." + "\"", true);
}
}
// Measurement completed or aborted
Settings.FileHandle.Close();
// Double beep
Settings.Dmm.WriteString("SYST:BEEP", true);
Thread.Sleep(500);
Settings.Dmm.WriteString("SYST:BEEP", true);
Settings.Dmm.WriteString("DISP:TEXT \"DONE\"", true);
Settings.Dmm.WriteString("*RST", true); //Reset DMM
}
catch (Exception e)
{
MessageBox.Show("Acquisition error (" + e.Message + ")", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
// Hide progress bar
Dispatcher.BeginInvoke(new Action(() =>
{
btnStart.Background = Brushes.LightGray;
btnStart.IsEnabled = true;
pbrAcquisition.Visibility = Visibility.Hidden;
tbkAcquisition.Visibility = Visibility.Hidden;
}));
}
}
|
Revision history
Version 1.0.3 (2016-11-11)
- Resistance measurement added (2W and 4W)
- Measurement ranges grouped by measurement type
Version 1.0.2 (2016-02-26)
- Unlimited number of samples
- Support for external trigger source
- Selectable AC power frequency (50/60 Hz) for accurate timing