/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: LTSpiceOut.java
* Input/output tool: reader for LTSpice output (.raw)
* Written by Steven M. Rubin, Sun Microsystems.
*
* Copyright (c) 2009 Sun Microsystems and Static Free Software
*
* 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.io.input;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.text.TextUtils;
import com.sun.electric.tool.simulation.AnalogAnalysis;
import com.sun.electric.tool.simulation.AnalogSignal;
import com.sun.electric.tool.simulation.Stimuli;
import com.sun.electric.tool.simulation.Waveform;
import com.sun.electric.tool.simulation.WaveformImpl;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
* Class for reading and displaying waveforms from LTSpice Raw output.
* These are contained in .raw files.
*/
public class LTSpiceOut extends Simulate
{
private static final boolean DEBUG = false;
private boolean complexValues;
/**
* Class for handling swept signals.
*/
private static class SweepAnalysisLT extends AnalogAnalysis
{
double [][] commonTime; // sweep, signal
List<List<double[]>> theSweeps = new ArrayList<List<double[]>>(); // sweep, event, signal
private SweepAnalysisLT(Stimuli sd, AnalogAnalysis.AnalysisType type)
{
super(sd, type, false);
}
protected Waveform[] loadWaveforms(AnalogSignal signal)
{
int sigIndex = signal.getIndexInAnalysis();
Waveform[] waveforms = new Waveform[commonTime.length];
for (int sweep = 0; sweep < waveforms.length; sweep++)
{
double[] times = commonTime[sweep];
List<double[]> theSweep = theSweeps.get(sweep);
Waveform waveform = new WaveformImpl(times, theSweep.get(sigIndex));
waveforms[sweep] = waveform;
}
return waveforms;
}
}
LTSpiceOut() {}
/**
* Method to read an LTSpice output file.
*/
protected void readSimulationOutput(Stimuli sd, URL fileURL, Cell cell)
throws IOException
{
// open the file
if (openBinaryInput(fileURL)) return;
// show progress reading .raw file
startProgressDialog("LTSpice output", fileURL.getFile());
// read the actual signal data from the .raw file
readRawLTSpiceFile(cell, sd);
// stop progress dialog, close the file
stopProgressDialog();
closeInput();
}
private void readRawLTSpiceFile(Cell cell, Stimuli sd)
throws IOException
{
complexValues = false;
boolean realValues = false;
int signalCount = -1;
String[] signalNames = null;
int rowCount = -1;
AnalogAnalysis.AnalysisType aType = AnalogAnalysis.ANALYSIS_TRANS;
for(;;)
{
String line = getLineFromBinary();
if (line == null) break;
updateProgressDialog(line.length());
// find the ":" separator
int colonPos = line.indexOf(':');
if (colonPos < 0) continue;
String keyWord = line.substring(0, colonPos);
String restOfLine = line.substring(colonPos+1).trim();
if (keyWord.equals("Plotname"))
{
// see if known analysis is specified
if (restOfLine.equals("AC Analysis")) aType = AnalogAnalysis.ANALYSIS_AC;
continue;
}
if (keyWord.equals("Flags"))
{
// the first signal is Time
int complex = restOfLine.indexOf("complex");
if (complex >= 0) complexValues = true;
int r = restOfLine.indexOf("real");
if (r >= 0) realValues = true;
continue;
}
if (keyWord.equals("No. Variables"))
{
// the first signal is Time
signalCount = TextUtils.atoi(restOfLine) - 1;
continue;
}
if (keyWord.equals("No. Points"))
{
rowCount = TextUtils.atoi(restOfLine);
continue;
}
if (keyWord.equals("Variables"))
{
if (signalCount < 0)
{
System.out.println("Missing variable count in file");
return;
}
signalNames = new String[signalCount];
for(int i=0; i<=signalCount; i++)
{
restOfLine = getLineFromBinary();
if (restOfLine == null) break;
updateProgressDialog(restOfLine.length());
restOfLine = restOfLine.trim();
int indexOnLine = TextUtils.atoi(restOfLine);
if (indexOnLine != i)
System.out.println("Warning: Variable " + i + " has number " + indexOnLine);
int nameStart = 0;
while (nameStart < restOfLine.length() && !Character.isWhitespace(restOfLine.charAt(nameStart))) nameStart++;
while (nameStart < restOfLine.length() && Character.isWhitespace(restOfLine.charAt(nameStart))) nameStart++;
int nameEnd = nameStart;
while (nameEnd < restOfLine.length() && !Character.isWhitespace(restOfLine.charAt(nameEnd))) nameEnd++;
String name = restOfLine.substring(nameStart, nameEnd);
if (name.startsWith("V(") && name.endsWith(")"))
{
name = name.substring(2, name.length()-1);
}
if (i > 0) signalNames[i-1] = name;
}
continue;
}
if (keyWord.equals("Binary"))
{
if (signalCount < 0)
{
System.out.println("Missing variable count in file");
return;
}
if (rowCount < 0)
{
System.out.println("Missing point count in file");
return;
}
// initialize the stimuli object
SweepAnalysisLT an = new SweepAnalysisLT(sd, aType);
sd.setCell(cell);
if (DEBUG)
{
System.out.println(signalCount+" VARIABLES, "+rowCount+" SAMPLES");
for(int i=0; i<signalCount; i++)
System.out.println("VARIABLE "+i+" IS "+signalNames[i]);
}
// read all of the data in the RAW file
double[][] values = new double[signalCount][rowCount];
double [] timeValues = new double[rowCount];
for(int j=0; j<rowCount; j++)
{
double time = getNextDouble();
if (DEBUG) System.out.println("TIME AT "+j+" IS "+time);
timeValues[j] = Math.abs(time);
for(int i=0; i<signalCount; i++)
{
double value = 0;
if (realValues) value = getNextFloat(); else
value = getNextDouble();
if (DEBUG) System.out.println(" DATA POINT "+i+" ("+signalNames[i]+") IS "+value);
values[i][j] = value;
}
}
// figure out where the sweep breaks occur
List<Integer> sweepLengths = new ArrayList<Integer>();
// int sweepLength = -1;
int sweepStart = 0;
int sweepCount = 0;
for(int j=1; j<=rowCount; j++)
{
if (j == rowCount || timeValues[j] < timeValues[j-1])
{
int sl = j - sweepStart;
sweepLengths.add(new Integer(sl));
sweepStart = j;
sweepCount++;
an.addSweep(Integer.toString(sweepCount));
}
}
if (DEBUG) System.out.println("FOUND " + sweepCount + " SWEEPS");
// place data into the Analysis object
an.commonTime = new double[sweepCount][];
int offset = 0;
for(int s=0; s<sweepCount; s++)
{
int sweepLength = sweepLengths.get(s).intValue();
an.commonTime[s] = new double[sweepLength];
for (int j = 0; j < sweepLength; j++)
an.commonTime[s][j] = timeValues[j + offset];
List<double[]> allTheData = new ArrayList<double[]>();
for(int i=0; i<signalCount; i++)
{
double[] oneSetOfData = new double[sweepLength];
for(int j=0; j<sweepLength; j++)
oneSetOfData[j] = values[i][j+offset];
allTheData.add(oneSetOfData);
}
an.theSweeps.add(allTheData);
offset += sweepLength;
}
// add signal names to the analysis
for (int i = 0; i < signalCount; i++)
{
String name = signalNames[i];
int lastDotPos = name.lastIndexOf('.');
String context = null;
if (lastDotPos >= 0)
{
context = name.substring(0, lastDotPos);
name = name.substring(lastDotPos + 1);
}
double minTime = 0, maxTime = 0, minValues = 0, maxValues = 0;
an.addSignal(signalNames[i], context, minTime, maxTime, minValues, maxValues);
}
return;
}
}
}
private double getNextDouble()
throws IOException
{
// double values appear with reversed bytes
long lt = dataInputStream.readLong();
lt = Long.reverseBytes(lt);
double t = Double.longBitsToDouble(lt);
int amtRead = 8;
// for complex plots, ignore imaginary part
if (complexValues) { amtRead *= 2; dataInputStream.readLong(); }
updateProgressDialog(amtRead);
return t;
}
private float getNextFloat()
throws IOException
{
// float values appear with reversed bytes
int lt = dataInputStream.readInt();
lt = Integer.reverseBytes(lt);
float t = Float.intBitsToFloat(lt);
int amtRead = 4;
// for complex plots, ignore imaginary part
if (complexValues) { amtRead *= 2; dataInputStream.readInt(); }
updateProgressDialog(amtRead);
return t;
}
}