Skip to content

Commit 73dfcf0

Browse files
committed
add support for multiple inputs in C-code generation
1 parent 6990e51 commit 73dfcf0

File tree

2 files changed

+51
-3
lines changed

2 files changed

+51
-3
lines changed

src/SymbolicControlSystems.jl

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,24 @@ end
131131

132132
sym2num(P::TransferFunction, args...) = sym2num(Sym(P), args...)
133133

134+
function numeric(x::Sym, pairs::Pair...)
135+
for (sym, val) in pairs
136+
x = subs(x, (sym, val))
137+
end
138+
float(x)
139+
end
140+
141+
"""
142+
sym2num(P::AbstractStateSpace, pairs::Pair...)
143+
144+
For statespace systems, `sym2num` does not take the sample time `h`.
145+
"""
146+
function sym2num(P::AbstractStateSpace, pairs::Pair...)
147+
A,B,C,D = ssdata(P)
148+
A,B,C,D = (numeric.(x, pairs...) for x in (A,B,C,D))
149+
ss(A,B,C,D,P.timeevol)
150+
end
151+
134152
"""
135153
sym2num(G, h::Real, pairs::Pair...)
136154
@@ -309,10 +327,10 @@ double transfer_function(double ui$(var_str)) {
309327
end
310328

311329
function ccode(sys::StateSpace{<:Discrete}; cse = true, function_name = "transfer_function")
312-
sys.nu == 1 || throw(ArgumentError("Multiple input not yet supported"))
313330
nx = sys.nx
331+
nu = sys.nu
314332
ny = sys.ny
315-
u = Sym("u")
333+
u = nu == 1 ? Sym("u") : [Sym("u[$(i-1)]") for i = 1:nu]
316334
x = [Sym("x[$(i-1)]") for i = 1:nx]
317335
# @show P
318336
if ControlSystems.numeric_type(sys) <: SymPy.Sym
@@ -334,10 +352,12 @@ function ccode(sys::StateSpace{<:Discrete}; cse = true, function_name = "transfe
334352
@show y = mul!(y, sys.C, x) + sys.D * u
335353
# @show y = sp.collect.(y, x)
336354

355+
u_str = nu == 1 ? "double u" : "double *u"
356+
337357
code = """
338358
#include <stdio.h>\n
339359
#include <math.h>\n
340-
void $(function_name)(double *y, double u$(var_str)) {
360+
void $(function_name)(double *y, $(u_str)$(var_str)) {
341361
static double x[$(nx)] = {0}; // Current state
342362
double xp[$(nx)] = {0}; // Next state
343363
int i;

test/runtests.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,18 @@ end
182182
end
183183
end
184184

185+
function c_lsim_ss_multiinput(u, T, d, w)
186+
y = zeros(1)
187+
Y = Libc.Libdl.dlopen(outname) do lib
188+
fn = Libc.Libdl.dlsym(lib, :transfer_function)
189+
map(eachcol(u)) do u
190+
@ccall $(fn)(y::Ref{Cdouble}, u::Ref{Cdouble}, T::Float64, d::Float64, w::Float64)::Cvoid
191+
y[]
192+
end
193+
end
194+
Y'
195+
end
196+
185197
function c_lsim_ss(u)
186198
Y = Libc.Libdl.dlopen(outname) do lib
187199
fn = Libc.Libdl.dlsym(lib, :transfer_function)
@@ -213,6 +225,22 @@ end
213225
y_,_ = lsim(c2d(ss(G_), h, :tustin), u);
214226
@test norm(y-y_)/norm(y_) < 1e-10 # TODO: figure out why this is more sensitive
215227

228+
## Multiple inputs
229+
@vars w T d # Define symbolic variables
230+
h = 0.01
231+
G = tf([w^2], [1, 2*d*w, w^2]) * tf(1, [T, 1])
232+
Gd = tustin(G, h) # Discretize
233+
Gd = [Gd 2Gd]
234+
u = randn(2,1000); # Random input signal
235+
T_, d_, w_ = 0.03, 0.2, 2.0 # Define system parameters
236+
237+
code = SymbolicControlSystems.ccode(ss(Gd), cse=true)
238+
write(joinpath(path, filename), code)
239+
run(`gcc $filename -lm -shared -o $outname`)
240+
y = c_lsim_ss_multiinput( u, T_, d_, w_); # Filter u through the
241+
Gd_ = sym2num(ss(Gd), Pair.((T, d, w), (T_, d_, w_))...) # Replace symbols with numeric constants
242+
y_,_ = lsim(ss(Gd_), u);
243+
@test norm(y-y_)/norm(y_) < 1e-10 # TODO: figure out why this is more sensitive
216244

217245

218246
# test without symbols

0 commit comments

Comments
 (0)