plotting tab and comma delimited file (LTSpice waveform export)

The LTSpice circuit simulation program outputs a file that looks like this:

Freq. V(n003) V(n005) V(n007)
1.00000e+000 (-1.68072e+002dB,1.79085e+002°)
(-1.71453e-006dB,-3.60000e-002°) (-8.40364e+001dB,8.99964e+001°)
1.07177e+000 (-1.66868e+002dB,1.79145e+002°)
(-1.96947e-006dB,-3.85838e-002°) (-8.34343e+001dB,8.99961e+001°)
1.14870e+000 (-1.65664e+002dB,1.79202e+002°)
(-2.26233e-006dB,-4.13531e-002°) (-8.28323e+001dB,8.99959e+001°)
...

As can be seen there are 4 tab-delimited columns. The first column is
the dependent (horizontal) axis and represents Frequency. The
following 3 columns each contain 2 comma-delimited data points
including their units. These are the magnitude expressed in decibels
and the phase expressed in degrees.

I'd like to be able to plot this file. 6 separate plots should
result, or 3 pairs of 2 waveforms that share the same plot yet have 2
different axes. What is the best method to accomplish this?

-Kameron

Complicated text file formats like this can be a mess to deal with. One trick I do to simplify the problem is to (if the files are not large) load an entire file into a string and replace any un-needed characters with whitespace. So, replacing the ‘"’, ‘dB’, ‘(’, ‘)’, and ‘,’ with a space would yield:

1.00000e+000 -1.68072e+002 1.79085e+002 -1.71453e-006 -3.60000e-002 -8.40364e+001 8.99964e+001
1.07177e+000 -1.66868e+002 1.79145e+002 -1.96947e-006 -3.85838e-002 -8.34343e+001 8.99961e+001
1.14870e+000 -1.65664e+002 1.79202e+002 -2.26233e-006 -4.13531e-002 -8.28323e+001 8.99959e+001

Which could be put into a StringIO object, and then fed into numpy.loadtxt() function to get your numpy arrays. Does that help?

Ben Root

···

On Fri, Dec 3, 2010 at 8:34 PM, K. Larsen <kroylar@…287…> wrote:

The LTSpice circuit simulation program outputs a file that looks like this:

Freq. V(n003) V(n005) V(n007)

1.00000e+000 (-1.68072e+002dB,1.79085e+002°)

(-1.71453e-006dB,-3.60000e-002°) (-8.40364e+001dB,8.99964e+001°)

1.07177e+000 (-1.66868e+002dB,1.79145e+002°)

(-1.96947e-006dB,-3.85838e-002°) (-8.34343e+001dB,8.99961e+001°)

1.14870e+000 (-1.65664e+002dB,1.79202e+002°)

(-2.26233e-006dB,-4.13531e-002°) (-8.28323e+001dB,8.99959e+001°)

The matplotlib examples api/two_scales.py & pylab_examples/shared_axis_demo.py may be what you require to display your data. Though I must admit to not having tried both at the same time :slight_smile:

For extracting data my personal preference is to use the csv module with generators and regular expressions - keeping results in namedtuples. Quickly written example for your data:

# -*- mode: python tab-width: 4 coding: utf-8 -*-
# tested with Python 2.6
from future_builtins import map

import csv
import re
from collections import deque, namedtuple
import numpy as np

RowTuple = namedtuple('RowTuple', 'freq col1 col2 col3')
ColumnTuple = namedtuple('ColumnTuple', 'magnitude phase')
DequeTuple = namedtuple('DequeTuple', 'freq magnitude1 phase1 magnitude2 phase2 magnitude3 phase3')

def readLTSpice(filename):
     # try codec.open() if not ascii
     with open(filename, 'rb') as fp:
         fp.next() # discard the first row
         for row in csv.reader(fp, delimiter='\t'):
             yield RowTuple(*row)

def splitColumn(col):
     # use a regular expression to extract floating point number
     # n.b. regular expressions are cached by the re module
     exp = re.compile(r"[+-]? *(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?")
     return ColumnTuple(*map(float, exp.findall(col)))

def processFile(filename):
     rows = readLTSpice(filename)
     rows = (RowTuple(row.freq, splitColumn(row.col1), splitColumn(row.col2), splitColumn(row.col3)) for row in rows)

     # if there is a large number of points then deque() is more efficient
     # than lists as there is no copying when more memory is required
     result = DequeTuple(deque(), deque(), deque(), deque(), deque(), deque(), deque())

     for row in rows:
         result.freq.append(row.freq)
         result.magnitude1.append(row.col1.magnitude)
         result.magnitude2.append(row.col2.magnitude)
         result.magnitude3.append(row.col3.magnitude)
         result.phase1.append(row.col1.phase)
         result.phase2.append(row.col2.phase)
         result.phase3.append(row.col3.phase)

     return result

processed = processFile('ltspice.data')

# convert to numpy arrays for use with matplotlib
freq = np.asarray(processed.freq)
magnitude1 = np.asarray(processed.magnitude1)
magnitude2 = np.asarray(processed.magnitude2)
magnitude3 = np.asarray(processed.magnitude3)
phase1 = np.asarray(processed.phase1)
phase2 = np.asarray(processed.phase2)
phase3 = np.asarray(processed.phase3)

# ready for plotting...

Stephen Evans

···

On 04/12/2010 02:34, K. Larsen wrote:

The LTSpice circuit simulation program outputs a file that looks like this:

Freq. V(n003) V(n005) V(n007)
1.00000e+000 (-1.68072e+002dB,1.79085e+002�)
(-1.71453e-006dB,-3.60000e-002�) (-8.40364e+001dB,8.99964e+001�)
1.07177e+000 (-1.66868e+002dB,1.79145e+002�)
(-1.96947e-006dB,-3.85838e-002�) (-8.34343e+001dB,8.99961e+001�)
1.14870e+000 (-1.65664e+002dB,1.79202e+002�)
(-2.26233e-006dB,-4.13531e-002�) (-8.28323e+001dB,8.99959e+001�)
...

As can be seen there are 4 tab-delimited columns. The first column is
the dependent (horizontal) axis and represents Frequency. The
following 3 columns each contain 2 comma-delimited data points
including their units. These are the magnitude expressed in decibels
and the phase expressed in degrees.

I'd like to be able to plot this file. 6 separate plots should
result, or 3 pairs of 2 waveforms that share the same plot yet have 2
different axes. What is the best method to accomplish this?