/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: RawSpiceOut.java
* Input/output tool: reader for Raw Spice output (.raw)
* Written by Steven M. Rubin, 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.io.input;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.tool.simulation.MutableSignal;
import com.sun.electric.tool.simulation.ScalarSample;
import com.sun.electric.tool.simulation.Signal;
import com.sun.electric.tool.simulation.SignalCollection;
import com.sun.electric.tool.simulation.Stimuli;
import com.sun.electric.tool.simulation.SweptSample;
import com.sun.electric.util.TextUtils;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Class for reading and displaying waveforms from Raw Spice output
* (including LTSpice). These are contained in .raw files.
*/
public class RawSpiceOut extends Input<Stimuli>
{
RawSpiceOut() {}
private static final boolean DEBUG = false;
private boolean complexValues;
/**
* Method to read an LTSpice output file.
*/
protected Stimuli processInput(URL fileURL, Cell cell, Stimuli sd)
throws IOException
{
sd.setNetDelimiter(" ");
// open the file
if (openBinaryInput(fileURL)) return sd;
// show progress reading .raw file
System.out.println("Reading LTSpice/SmartSpice raw output file: " + fileURL.getFile());
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();
return sd;
}
private void readRawLTSpiceFile(Cell cell, Stimuli sd)
throws IOException
{
complexValues = false;
boolean realValues = false;
int signalCount = -1;
boolean firstFieldIsTime = false;
String[] signalNames = null;
int rowCount = -1;
boolean isLTSpice = false;
boolean first = true;
sd.setCell(cell);
SignalCollection sc = null;
double[] time = null;
for(;;)
{
String line = getLineFromBinary();
if (line == null) break;
updateProgressDialog(line.length());
// make sure this isn't an HSPICE deck (check first line)
if (first)
{
first = false;
if (line.length() >= 20)
{
String hsFormat = line.substring(16, 20);
if (hsFormat.equals("9007") || hsFormat.equals("9601"))
{
System.out.println("This is an HSPICE file, not a RAWFILE file");
System.out.println("Change the SPICE format (in Preferences) and reread");
return;
}
}
}
// 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("Command"))
{
isLTSpice = restOfLine.indexOf("LTspice")!=-1;
continue;
}
if (keyWord.equals("Plotname"))
{
// see if known analysis is specified
// terminate any previous analysis
if (sc != null)
{
signalCount = -1;
rowCount = -1;
signalNames = null;
}
// start reading a new analysis
if (restOfLine.startsWith("Transient Analysis"))
{
sc = Stimuli.newSignalCollection(sd, "TRANS SIGNALS");
} else if (restOfLine.startsWith("DC "))
{
sc = Stimuli.newSignalCollection(sd, "DC SIGNALS");
} else if (restOfLine.startsWith("AC "))
{
sc = Stimuli.newSignalCollection(sd, "AC SIGNALS");
} else if (restOfLine.startsWith("Operating "))
{
sc = Stimuli.newSignalCollection(sd, "OPERATING POINT");
} else
{
System.out.println("Warning: unknown analysis: " + restOfLine);
sc = Stimuli.newSignalCollection(sd, "TRANS SIGNALS");
}
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);
continue;
}
if (keyWord.equals("No. Points"))
{
rowCount = TextUtils.atoi(restOfLine);
time = new double[rowCount];
continue;
}
if (!isLTSpice)
{
if (keyWord.equals("Variables"))
{
if (signalCount < 0)
{
System.out.println("Missing variable count in file");
return;
}
signalNames = new String[signalCount];
int trueSignalCount = 0;
for(int i=0; i<signalCount; i++)
{
if (restOfLine.length() > 0)
{
line = restOfLine;
restOfLine = "";
} else
{
line = getLineAndUpdateProgressBinary();
if (line == null)
{
System.out.println("Error: end of file during signal names");
return;
}
}
line = line.trim();
int numberOnLine = TextUtils.atoi(line);
if (numberOnLine != i)
System.out.println("Warning: Variable " + i + " has number " + numberOnLine);
int spacePos = line.indexOf(" "); if (spacePos < 0) spacePos = line.length();
int tabPos = line.indexOf("\t"); if (tabPos < 0) tabPos = line.length();
int pos = Math.min(spacePos, tabPos);
String name = line.substring(pos).trim();
spacePos = name.indexOf(" "); if (spacePos < 0) spacePos = name.length();
tabPos = name.indexOf("\t"); if (tabPos < 0) tabPos = name.length();
pos = Math.min(spacePos, tabPos);
name = name.substring(0, pos);
if (i == 0)
{
if (name.equals("time"))
{
firstFieldIsTime = true;
continue;
}
}
signalNames[trueSignalCount++] = name;
}
signalCount = trueSignalCount;
continue;
}
if (keyWord.equals("Values"))
{
if (signalCount < 0)
{
System.out.println("Missing variable count in file");
return;
}
if (rowCount < 0)
{
System.out.println("Missing point count in file");
return;
}
double[][] values = new double[signalCount][rowCount];
for(int j=0; j<rowCount; j++)
{
int valueFill = 0;
int valuesToRead = signalCount;
if (firstFieldIsTime) valuesToRead++; else
time[j] = j;
for(int i = -1; i < valuesToRead; )
{
line = getLineAndUpdateProgressBinary();
if (line == null)
{
System.out.println("Error: end of file during data points (read " + j + " out of " + rowCount);
return;
}
line = line.trim();
if (line.length() == 0) continue;
int charPos = 0;
while (charPos <= line.length())
{
int tabPos = line.indexOf("\t", charPos);
if (tabPos < 0) tabPos = line.length();
String field = line.substring(charPos, tabPos);
charPos = tabPos+1;
while (charPos < line.length() && line.charAt(charPos) == '\t') charPos++;
if (i < 0)
{
int lineNumber = TextUtils.atoi(field);
if (lineNumber != j)
System.out.println("Warning: event " + j + " has wrong event number: " + lineNumber);
} else
{
double val = TextUtils.atof(field);
if (i == 0 && firstFieldIsTime) time[j] = val; else
values[valueFill++][j] = val;
}
i++;
if (i >= valuesToRead) break;
}
}
}
for (int i = 0; i < signalCount; i++)
ScalarSample.createSignal(sc, sd, signalNames[i], null, time, values[i]);
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;
}
// read the data
double[][] values = new double[signalCount][rowCount];
for(int j=0; j<rowCount; j++)
{
time[j] = dataInputStream.readDouble();
for(int i=0; i<signalCount; i++)
values[i][j] = dataInputStream.readDouble();
}
for (int i = 0; i < signalCount; i++)
ScalarSample.createSignal(sc, sd, signalNames[i], null, time, values[i]);
continue;
}
} else
{
boolean OLD = true;
if (OLD) firstFieldIsTime = true;
if (keyWord.equals("Variables"))
{
if (signalCount < 0)
{
System.out.println("Missing variable count in file");
return;
}
signalNames = new String[signalCount];
int trueSignalCount = 0;
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)
{
if (OLD) continue;
if (name.equals("time"))
{
firstFieldIsTime = true;
continue;
}
}
signalNames[trueSignalCount++] = name;
}
signalCount = trueSignalCount;
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;
}
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];
time = new double[rowCount];
for(int j=0; j<rowCount; j++)
{
double t = j;
if (firstFieldIsTime) t = getNextDouble();
if (DEBUG) System.out.println("TIME AT "+j+" IS "+t);
time[j] = Math.abs(t);
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 sweepStart = 0;
int sweepCount = 0;
for(int j=1; j<=rowCount; j++)
{
if (j == rowCount || time[j] < time[j-1])
{
int sl = j - sweepStart;
sweepLengths.add(new Integer(sl));
sweepStart = j;
sweepCount++;
}
}
if (DEBUG) System.out.println("FOUND " + sweepCount + " SWEEPS");
Signal<?> [][] signals = new Signal<?>[signalCount][sweepCount];
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);
}
for(int s=0; s<sweepCount; s++)
signals[i][s] = ScalarSample.createSignal(sc, sd, name, context);
}
// place data into the Sample object
int offset = 0;
for(int s=0; s<sweepCount; s++)
{
int sweepLength = sweepLengths.get(s).intValue();
for(int i=0; i<signalCount; i++)
{
MutableSignal<ScalarSample> ms = (MutableSignal<ScalarSample>)signals[i][s];
for(int j=0; j<sweepLength; j++)
{
ms.addSample(time[j + offset], new ScalarSample(values[i][j+offset]));
}
}
offset += sweepLength;
}
String[] sweepNames = new String[sweepCount];
for(int s=0; s<sweepCount; s++) sweepNames[s] = "" + (s+1);
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);
}
SweptSample.createSignal(sc, sd, name, context, false, (Signal<ScalarSample>[])signals[i]);
}
sc.setSweepNames(sweepNames);
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;
}
}