return 1; computer engineering and electronics
  • Home
  • Projects
  • About
  • Archive
Introduction to threads with C++11

Featured Article

Introduction to threads with C++11

Installing Eagle 6 on Fedora 17

Featured Article

Installing Eagle 6 on Fedora 17

Popular Articles

  1. No data yet..

Plotting ngspice results with Python

Written by Lucas on January 22nd, 2012

My favourite schematic capture program on Linux is Gschem, part of the gEDA project. The gEDA project contains a lot of open source tools which aid in electronic design automation. Think of programs to create schematics and PCB boards, to view Gerber files and more.

They're not as "user friendly" as OrCad or Multisim, but they work fine. For example, unlike most commercial schematic capture programs, most symbols in gEDA don't include a SPICE model by default, for most symbols you have to explicitly define which SPICE model to use when you want to start simulating. When you know what to do it's not that hard, and a project like spicelib makes it even easier.

gEDA also doesn't integrate a circuit simulator into its user interface. We do actually have some nice simulators on Linux, which are both command line based. Examples are ngspice and gnucap. Both work great, but they aren't tightly integrated as most commercial software. This can be a bit overwhelming for users who are just starting with gEDA.

Luckily, there's a nice tutorial on the gEDA wiki on how to simulate a circuit created with gEDA, and I'm not going to repeat that in this post. The point is, I don't like the builtin plot viewer of ngspice. When viewing a graph, you can't zoom, you can't move the viewport, it's just really basic. So, I've written a Python script which uses the matplotlib package to plot the results of ngspice, which looks a lot better than the builtin viewer. Details after the break!

Preparing your data

The Python script below reads a file with all calculated points of the plot. Of course, this file has to be generated by ngspice first, and that's done with 'wrdata' command, when you're in the ngspice interpreter. Usage:

wrdata file netname1 [netname2 ...]

Ngspice automatically adds the ".data" extension to the given filename. An example of a generated file:

 1.000000e+00  1.000000e+00  1.000000e+00  9.999747e-01 
 1.047129e+00  1.000000e+00  1.047129e+00  9.999723e-01 
 1.096478e+00  1.000000e+00  1.096478e+00  9.999696e-01 
 1.148154e+00  1.000000e+00  1.148154e+00  9.999667e-01 
 1.202264e+00  1.000000e+00  1.202264e+00  9.999635e-01 
 1.258925e+00  1.000000e+00  1.258925e+00  9.999600e-01 
 1.318257e+00  1.000000e+00  1.318257e+00  9.999561e-01 
 1.380384e+00  1.000000e+00  1.380384e+00  9.999519e-01 
 1.445440e+00  1.000000e+00  1.445440e+00  9.999472e-01 
 1.513561e+00  1.000000e+00  1.513561e+00  9.999421e-01 
 1.584893e+00  1.000000e+00  1.584893e+00  9.999365e-01 
 1.659587e+00  1.000000e+00  1.659587e+00  9.999304e-01 
...
...

The first column are the used points on the x-axis for the first netname, the second the corresponding y-values, the third column contains the points on the x-axis for the second netname, and the fourth the y-values, and so forth (if you're plotting more netnames).

Script usage

usage: ngspice.py [-h] [--plotfunc PLOT_FUNC] file [legend [legend ...]]

Plots ngspice results

positional arguments:
  file                  The file with the data generated by the ngspice wrdata
                        command
  legend                Optional legend names for the plotted lines

optional arguments:
  -h, --help            show this help message and exit
  --plotfunc PLOT_FUNC, -p PLOT_FUNC
                        The matplotlib (pylab) plot function to use, default
                        pylab.plot

As you can see, you can enter labels for the lines which will be displayed in the legend, and you can give an other plot function (which must be in the pylab module). For example, if you want to want a logarithmic scale on the x-axis you can use the following command:

python ngspice.py -p semilogx simulation.data Vin Vout

Matplotlib is a HUGE library, and there are probably tons of more options for plotting, but for now this suits my needs. :)

Example

The builtin ngspice viewer: ngspice plot

A plot using matplotlib: matplotlib plot

The code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
from numpy import array, zeros
from pylab import plot, figure, grid, show, legend

import argparse
import sys

class NGSpiceData:
    """
        Class which represents ngspice simulation data
    """

    def __init__(self, filename=""):
        """
            Constructor, intializes member variables

            :Args:
                * filename (str): If filename is given it immediately loads the data from it
        """

        self.data = None
        if filename:
            self.load_data(filename)

    def load_data(self, filename):
        """
            Reads ngspice data file, generated by the ngspice 'wrdata' command,
            and puts it in a numpy array.

            :Args:
                * filename (str): The file to read

            :Returns:
                NumPy array containing the data
        """

        with open(filename) as file:
            lines = file.readlines()

            for i, line in enumerate(lines):
                parts = [float(part) for part in line.split()]

                if self.data == None:
                    self.data = zeros((len(lines), len(parts)))

                self.data[i] = array(parts)

            self.data = self.data.transpose()

    def plot(self, plot_function=plot, *legends, **kwargs):
        """
            Plots the results in a single figure

            :Args:
                * plot_function (func): The Matplotlib plot function to use
                * legends (list): Other positional arguments given to the function will be used as legends
                * Keyword arguments are forwarded to the plot function
        """

        if not 'figure' in kwargs:
            kwargs['figure'] = figure()

        i = 0

        artists = []
        while i <= len(self.data) / 2:
            artists.append(plot_function(self.data[i], self.data[i+1], **kwargs))

            i += 2

        grid(which='both')

        if legends:
            legend(artists, legends)

class PyLabAction(argparse.Action):
    """
        Retrieves a pylab function based on the argument
    """

    def __call__(self, parser, namespace, values, option_string):
        import pylab

        func = getattr(pylab, values) if hasattr(pylab, values) else pylab.plot
        setattr(namespace, self.dest, func)

if __name__ == "__main__":  
    parser = argparse.ArgumentParser(description="Plots ngspice results")
    parser.add_argument('file', type=str, nargs=1, action='store',
        help="The file with the data generated by the ngspice wrdata command")
    parser.add_argument('legend', type=str, nargs='*', default=[], 
        help="Optional legend names for the plotted lines")
    parser.add_argument('--plotfunc', '-p', type=str, dest="plot_func", default=plot,
        action=PyLabAction, help="The matplotlib (pylab) plot function to use, default pylab.plot")

    arguments = parser.parse_args(sys.argv[1:])

    results = NGSpiceData(arguments.file[0])
    results.plot(arguments.plot_func, *arguments.legend)
    show()

Filled under: electronics geda python

Share on twitter Share on google+

blog comments powered by Disqus