Skip to content

Commit

Permalink
Merge 4ad9754 into 737ea0a
Browse files Browse the repository at this point in the history
  • Loading branch information
martinholters committed Sep 25, 2019
2 parents 737ea0a + 4ad9754 commit 3fc84a9
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 12 deletions.
3 changes: 2 additions & 1 deletion Project.toml
Expand Up @@ -22,6 +22,7 @@ julia = "1"
[extras]
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"

[targets]
test = ["Test", "SparseArrays"]
test = ["Test", "FFTW", "SparseArrays"]
42 changes: 31 additions & 11 deletions src/elements.jl
Expand Up @@ -439,21 +439,41 @@ Pins: `gate`, `source`, `drain`
end)
end

"""
opamp()
@doc doc"""
opamp(;maxgain=Inf, gain_bw_prod=Inf)
Creates an ideal operational amplifier. It enforces the voltage between the
input pins to be zero without sourcing any current while sourcing arbitrary
current on the output pins wihtout restricting their voltage.
Creates a linear operational amplifier as a voltage-controlled voltage source.
The input current is zero while the input voltage is mapped to the output
voltage according to the transfer function
Note that the opamp has two output pins, one of which will typically be
connected to a ground node and has to provide the current sourced on the other
output pin.
$H(f) = \frac{A_\text{max}}{\sqrt{A_\text{max}^2-1} i \frac{f}{f_\text{UG}} + 1}$
where $f$ is the signal frequency, $A_\text{max}$ (`maxgain`) is the maximum
open loop gain and $f_\text{UG}$ (`gain_bw_prod`) is the gain/bandwidth
product (unity gain bandwidth). For `gain_bw_prod=Inf` (the default), this
corresponds to a frequency-independent gain of `maxgain`. For `maxgain=Inf`
(the default), the amplifier behaves as a perfect integrator.
For both `maxgain=Inf` and `gain_bw_prod=Inf`, i.e. just `opamp()`, an ideal
operational amplifier is obtained that enforces the voltage between the input
pins to be zero while sourcing arbitrary current on the output pins without
restricting their voltage.
Note that the opamp has two output pins, where the negative one will typically
be connected to a ground node and has to provide the current sourced on the
positive one.
Pins: `in+` and `in-` for input, `out+` and `out-` for output
"""
opamp() = Element(mv=[0 0; 1 0], mi=[1 0; 0 0],
ports=["in+" => "in-", "out+" => "out-"])
""" function opamp(;maxgain=Inf, gain_bw_prod=Inf)
if gain_bw_prod==Inf # special case to avoid unnecessary state
Element(mv=[0 0; 1 -1/maxgain], mi=[1 0; 0 0],
ports=["in+" => "in-", "out+" => "out-"])
else
Element(mv=[0 0; -1/sqrt(1-1/maxgain^2) 0; 0 -1], mi=[1 0; 0 0; 0 0],
mx=[0; 1/sqrt(maxgain^2-1); 1], mxd=[0; 1/(2π*gain_bw_prod); 0],
ports=["in+" => "in-", "out+" => "out-"])
end
end

@doc doc"""
opamp(Val{:macak}, gain, vomin, vomax)
Expand Down
28 changes: 28 additions & 0 deletions test/runtests.jl
Expand Up @@ -5,6 +5,7 @@ include("checklic.jl")

using ACME
using Test: @test, @test_broken, @test_logs, @test_throws, @testset
using FFTW: rfft
using ProgressMeter
using SparseArrays: sparse, spzeros

Expand Down Expand Up @@ -519,6 +520,33 @@ end
end
end

@testset "op amp" begin
for Amax in (10, Inf), GBP in (50e3, Inf)
# test circuit: non-inverting amplifier in high shelving configuration
circ = @circuit begin
input = voltagesource(), [-] gnd
op = opamp(maxgain=Amax, gain_bw_prod=GBP), ["in+"] input[+], ["out-"] gnd
r1 = resistor(109e3), [1] op["out+"], [2] op["in-"]
r2 = resistor(1e3), [1] op["in-"]
c = capacitor(22e-9), [1] r2[2], [2] gnd
output = voltageprobe(), [+] op["out+"], [-] gnd
end
model = DiscreteModel(circ, 1/44100)
# obtain impulse response / transfer function
u = [1; zeros(4095)]'
y = run!(model, u)[1,:]
Y = rfft(y)
# inverse of op amp transfer function
G⁻¹(s) = sqrt(1-1/Amax^2)*s/(2π*GBP) + 1/Amax
# feedback transfer function
H(s) = (1e3*22e-9*s + 1) / ((109e3+1e3)*22e-9*s + 1)
# overall transfer function evaluated taking frequency warping of
# bilinear transform into account
Yref = [let ω=2*44100*tan*k/length(y)); 1/(G⁻¹(im*ω) + H(im*ω)); end for k in eachindex(Y).-1]
@test Y Yref
end
end

function checksteady!(model)
x_steady = steadystate!(model)
for s in model.solvers
Expand Down

0 comments on commit 3fc84a9

Please sign in to comment.