/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: HP548xxA.java
* Written by Ron Ho and Tom O'Neill, Sun Microsystems.
*
* Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.simulation.test;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
/**
* API for controlling Agilent 5485xA oscilloscopes. Seems to work for 5484xA
* oscilloscopes as well.
*/
public class HP548xxA extends Equipment {
// Add 1 to these to get index in results string
public static final int STAT_LAST = 0;
public static final int STAT_MIN = 1;
public static final int STAT_MAX = 2;
public static final int STAT_MEAN = 3;
public static final int STAT_DEV = 4;
public static final int STAT_NSAMP = 5;
/**
* Minimum number of samples required for each channel during
* accumulateFrequencies().
*
* @see #accumulateFrequencies
*/
public static final int MIN_NSAMP = 10;
/** Number of oscilloscope channels */
public static final int NUM_CHAN = 4;
/**
* Number of statistics reported by accumulateFrequencies() for each channel
*
* @see #accumulateFrequencies
*/
public static final int RESULTS_PER_CHANNEL = 6;
/**
* Number of seconds to accumulate statistics in accumulateFrequencies().
* Due to GPIB and other delays, this number should be at least 3.
*/
public static final float DELAY_FOR_SAMPLES = 4.f;
/*
* Start of GPIB return value expected from RESULTS? inquiry in
* accumulateFrequencies()
*/
private static final String FREQ_NAME_START = "Frequency(";
/** Creates a new instance of HP54855A */
public HP548xxA(String name) {
super(name);
}
/**
* Performs a single measurement of frequency using first full cycle on
* screen for the waveform on channel <code>channel</code> (=1..3). For
* greater accuracy, use accumulateFrequencies().
*
* @see #accumulateFrequencies
*/
public float getFrequency(int channel) {
write("MEAS:FREQ? CHAN" + channel);
String s = read(20).trim();
return new Float(s.split("\\s")[0]).floatValue();
}
/**
* Accumulates frequency statistics on oscilloscope channels
* <code>ichan = 1..nchan</code> until at least <code>MIN_NSAMP</code>
* frequency samples are recorded for each channel. The statistics are
* defined by the constants <code>STAT_LAST</code>, etc. Channel
* <code>ichan = 0</code> contains cumulative/average statistics for all
* <code>nchan</code> channels.
*
* @param nchan
* Number of scope channels to measure frequency on
* @return Frequency statistics array <code>float[ichan][statistic]</code>
* @see #STAT_LAST
* @see #STAT_MEAN
* @see #STAT_DEV
*/
public float[][] accumulateFrequencies(int nchan) {
checkChannelNumber(nchan);
write("MEAS:CLEAR");
write("MEAS:STATISTICS ON");
write("MEAS:FREQ CHAN1");
if (nchan > 1)
write("MEAS:FREQ CHAN2");
if (nchan > 2)
write("MEAS:FREQ CHAN3");
if (nchan > 3)
write("MEAS:FREQ CHAN4");
float[][] results = new float[nchan + 1][RESULTS_PER_CHANNEL];
int minSamples = MIN_NSAMP + 1;
int minSamplesOld = 0;
do {
try { Thread.sleep((int)(1000*DELAY_FOR_SAMPLES)); } catch (InterruptedException _) { }
getStatistics(nchan, results);
System.out.print("Got");
for (int ichan = 1; ichan <= nchan; ichan++) {
int nsamp = Math.round(results[ichan][STAT_NSAMP]);
System.out.print(" " + nsamp);
if (nsamp < minSamples)
minSamples = nsamp;
}
System.out.println(" samples");
if (minSamples <= minSamplesOld) {
Infrastructure.fatal("At least one channel not accumulating");
}
minSamplesOld = minSamples;
} while (minSamples < MIN_NSAMP);
return results;
}
/*
* Returns the statistics reported by the oscilloscope. Queries the scope
* for results and decodes the result string.
*/
private void getStatistics(int nchan, float[][] results) {
/*
* For each channel, the result string includes the sequence
* "Frequency(n),current,minimum,maximum,mean,std_dev,nsamp". The
* strings for each channel are also separated by commas. Note
* measurements don't come in channel order.
*/
write("MEAS:RESULTS?");
String s = read(400).trim();
String[] strings = s.split(",");
// Channel 0 will get cumulative results
results[0][STAT_LAST] = results[0][STAT_MEAN] = results[0][STAT_DEV] = 0.f;
results[0][STAT_MIN] = Float.MAX_VALUE;
results[0][STAT_MAX] = 0.f;
results[0][STAT_NSAMP] = 0.f;
for (int imeas = 1; imeas <= nchan; imeas++) {
int ind = (RESULTS_PER_CHANNEL + 1) * (imeas - 1);
String freqName = strings[ind];
if (freqName.startsWith(FREQ_NAME_START) == false) {
Infrastructure
.fatal("Expected Frequency(n), found " + freqName);
}
String freqNum = freqName.substring(FREQ_NAME_START.length(),
freqName.length() - 1);
int ichan = Integer.parseInt(freqNum);
for (int iresult = 0; iresult < RESULTS_PER_CHANNEL; iresult++) {
results[ichan][iresult] = Float.parseFloat(strings[ind
+ iresult + 1]);
}
results[0][STAT_NSAMP] += results[ichan][STAT_NSAMP];
results[0][STAT_MEAN] += results[ichan][STAT_MEAN];
results[0][STAT_MIN] = Math.min(results[0][STAT_MIN],
results[ichan][STAT_MIN]);
results[0][STAT_MAX] = Math.max(results[0][STAT_MAX],
results[ichan][STAT_MAX]);
}
// Convert sum of means to average of means
results[0][STAT_MEAN] = results[0][STAT_MEAN] / nchan;
}
/**
* Creates files <code>(name)(n).dat</code> containing the frequencies
* measured on scope channels n=1..nchan-1 as functions of the voltage
* provided on the specified <code>PowerChannel</code>. Another file
* <code>(name)all.dat</code> provides all <code>nchan</code> results at
* once for convenience. Voltage is returned to the original value at end of
* the sweep.
*
* @param nameStart
* Start of file name
* @param supply
* Voltage control
* @param startV
* Starting voltage, in Volts
* @param endV
* Ending voltage, in Volts
* @param stepV
* Voltage step, in Volts
* @param nchan
* Number of scope channels to measure frequency on
*/
public void frequencyVsVoltage(String nameStart, PowerChannel supply,
float startV, float endV, float stepV, int nchan) {
checkChannelNumber(nchan);
int startMilliV = Math.round(startV * 1000.f);
int endMilliV = Math.round(endV * 1000.f);
int stepMilliV = Math.round(stepV * 1000.f);
float origVdd = supply.getVoltageSetpoint();
System.out.println("HP548xxA.frequencyVsVoltage() scan:");
try {
PrintWriter[] files = new PrintWriter[nchan + 1];
for (int ichan = 0; ichan <= nchan; ichan++) {
files[ichan] = openFile(nameStart + ichan + ".dat");
}
PrintWriter file = openFile(nameStart + "all.dat");
for (int mV = startMilliV; mV <= endMilliV; mV += stepMilliV) {
float thisV = mV / 1000.f;
supply.setVoltageWait(thisV);
float[][] frequencies = accumulateFrequencies(nchan);
System.out.print(thisV + ":");
file.print(thisV);
for (int ichan = 0; ichan <= nchan; ichan++) {
System.out.print(" "
+ frequencies[ichan][HP548xxA.STAT_MEAN]);
file.print(" " + frequencies[ichan][HP548xxA.STAT_MEAN]);
file.print(" " + frequencies[ichan][HP548xxA.STAT_DEV]);
files[ichan].print(thisV + " " + ichan);
files[ichan].print(" "
+ frequencies[ichan][HP548xxA.STAT_MEAN]);
files[ichan].print(" "
+ frequencies[ichan][HP548xxA.STAT_DEV]);
files[ichan].print(" "
+ frequencies[ichan][HP548xxA.STAT_MIN]);
files[ichan].print(" "
+ frequencies[ichan][HP548xxA.STAT_MAX]);
files[ichan].println(" "
+ frequencies[ichan][HP548xxA.STAT_NSAMP]);
}
System.out.println();
file.println();
}
file.close();
for (int ichan = 0; ichan <= nchan; ichan++) {
files[ichan].close();
}
} catch (Exception e) {
System.err.println("exception occurred: " + e);
} //end catch
supply.setVoltageWait(origVdd);
}
/* Creates a frequencyVsVoltage() file and writes a SPICE header to it */
private PrintWriter openFile(String name) throws IOException {
PrintWriter file = new PrintWriter(new FileWriter(name));
file.println("$DATA1 SOURCE='Lab' VERSION='1'");
file.println(".TITLE '* file " + name + "'");
file.println("vdd osctype freq_ave sigma freqmax freqmin samples");
return file;
}
// Can be used to check channel index or number of channels
private void checkChannelNumber(int nchan) {
if (nchan <= 0 || nchan > NUM_CHAN) {
Infrastructure.fatal("Bad channel index " + nchan);
}
}
}