Commit 046001b7 authored by Sean Leavey's avatar Sean Leavey
Browse files

Add ability to plot and save op-amp data via CLI

parent af4f3fc5
......@@ -5,7 +5,6 @@ import os
import logging
import csv
from pprint import pformat
import numpy as np
import click
from tabulate import tabulate
......@@ -14,6 +13,7 @@ from .solution import Solution
from .liso import LisoInputParser, LisoOutputParser, LisoRunner, LisoParserError
from .datasheet import PartRequest
from .components import OpAmp
from .display import OpAmpGainPlotter
from .config import (ZeroConfig, OpAmpLibrary, ConfigDoesntExistException,
ConfigAlreadyExistsException, LibraryQueryEngine)
......@@ -362,6 +362,36 @@ def library_search(query, sort_a0, sort_gbw, sort_delay, sort_vnoise, sort_vcorn
writer.writerow(engine.parameters)
writer.writerows(rows)
@library.command("opamp")
@click.argument("model", type=str)
@click.option("--show/--no-show", is_flag=True, default=True, show_default=True,
help="Show op-amp data.")
@click.option("--plot/--no-plot", is_flag=True, default=False,
help="Display open loop gain as figure.")
@click.option("--fstart", type=float, default=1e0, show_default=True, help="Plot start frequency.")
@click.option("--fstop", type=float, default=1e9, show_default=True, help="Plot stop frequency.")
@click.option("--npoints", type=int, default=1000, show_default=True, help="Plot number of points.")
@click.option("--save-figure", type=click.File("wb", lazy=False), multiple=True,
help="Save image of figure to file. Can be specified multiple times.")
def opamp_tools(model, show, plot, fstart, fstop, npoints, save_figure):
library_opamp = LIBRARY.get_opamp(model)
if show:
print(repr(library_opamp))
opamp = OpAmp(model=OpAmpLibrary.format_name(model), node1="n1", node2="n2", node3="n3",
**LIBRARY.get_data(model))
# Determine whether to generate plot.
generate_plot = plot or save_figure
if generate_plot:
plotter = OpAmpGainPlotter(opamp, fstart=fstart, fstop=fstop, npoints=npoints)
plotter.plot()
if save_figure:
for save_path in save_figure:
# NOTE: use figure file's name so that Matplotlib can identify the file type
# appropriately.
plotter.save(save_path.name)
if plot:
plotter.show()
@cli.group()
def config():
"""Zero configuration functions."""
......
......@@ -55,6 +55,24 @@ class OpAmpLibrary(BaseConfig):
"""
return str(name).upper()
def get_opamp(self, model):
"""Get op-amp by model.
Parameters
----------
model : :class:`str`
The op-amp model.
Returns
-------
:class:`.LibraryOpAmp`
The op-amp.
"""
for opamp in self.opamps:
if opamp.model.upper() == model.upper():
return opamp
raise ValueError(f"op-amp model '{model}' not found in library.")
def get_data(self, name):
"""Get op-amp data.
......@@ -448,3 +466,38 @@ class LibraryOpAmp:
def __str__(self):
return f"{self.model}(a0={self.a0}, gbw={self.gbw}, delay={self.delay})"
def __repr__(self):
def format_poles(poles):
formatted_poles = []
for mag, q in poles:
frequency = Quantity(mag, units="Hz")
if q == 0.5:
q = "real"
else:
q = f"q={q}"
formatted_poles.append(f"{frequency} ({q})")
return ", ".join(formatted_poles)
if self.poles:
poles = format_poles(self.poles_mag_q)
else:
poles = "--"
if self.zeros:
zeros = format_poles(self.zeros_mag_q)
else:
zeros = "--"
return f"""{self.model}
a0: {self.a0}
gbw: {self.gbw}
delay: {self.delay}
vnoise: {self.vnoise}
vcorner: {self.vcorner}
inoise: {self.inoise}
icorner: {self.icorner}
vmax: {self.vmax}
imax: {self.imax}
sr: {self.sr}
poles: {poles}
zeros: {zeros}
"""
......@@ -789,3 +789,28 @@ class SpectralDensityPlotter(MplGroupPlotter):
sum_spectral_density.draw(self.axis, **kwargs)
# Add label to legend.
self.axis.legend()
class OpAmpGainPlotter(BodePlotter):
def __init__(self, opamp, frequencies=None, fstart=None, fstop=None, npoints=1000):
title = f"{opamp.model} open loop gain"
super().__init__(title=title)
self.opamp = opamp
if frequencies is None:
if any([param is None for param in (fstart, fstop, npoints)]):
raise ValueError("either frequencies, or all of fstart, fstop and npoints must be "
"specified")
frequencies = np.logspace(np.log10(fstart), np.log10(fstop), npoints)
self.frequencies = np.array(frequencies)
@property
def response(self):
gain = np.array([self.opamp.gain(frequency) for frequency in self.frequencies])
series = Series(self.frequencies, gain)
return Response(source=self.opamp.node1, sink=self.opamp.node3, series=series)
def plot(self):
super().plot([self.response])
def show(self):
plt.show()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment