Skip to content

Commit 2cd1767

Browse files
authored
Add excitation-signal generators (#189)
* add input generation * tidy up * better examples * add period argument * add file
1 parent b30bfbe commit 2cd1767

6 files changed

Lines changed: 660 additions & 0 deletions

File tree

‎docs/make.jl‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ makedocs(
2929
pages = [
3030
"Home" => "index.md",
3131
"Identification data" => "iddata.md",
32+
"Input signal generation" => "input_signals.md",
3233
"State-space estimation" => "ss.md",
3334
"Transfer-function estimation" => "tf.md",
3435
"Impulse-response estimation" => "impulse.md",

‎docs/src/input_signals.md‎

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
# Input Signal Generation
2+
3+
This page describes the input signal generation capabilities of ControlSystemIdentification.jl. The `InputSignals` submodule provides functions for generating various types of excitation signals commonly used in system identification.
4+
5+
## Overview
6+
7+
Proper excitation signal design is crucial for successful system identification. The quality of the input signal directly affects the quality of the identified model. This package provides several standard signal types:
8+
9+
- **PRBS (Pseudo-Random Binary Sequence)**: Binary signals with white noise-like properties
10+
- **Chirp signals**: Frequency sweeps for analyzing system response across frequency ranges
11+
- **Multi-sine signals**: Controlled spectral content at specific frequencies
12+
- **Step signals**: Fundamental for transient response analysis
13+
14+
## PRBS (Pseudo-Random Binary Sequence)
15+
16+
```@docs
17+
prbs
18+
```
19+
20+
### Example
21+
22+
```@example input_signals
23+
using ControlSystemIdentification, Plots
24+
25+
# Generate a standard PRBS signal
26+
N = 200
27+
u_prbs = prbs(N; seed=42)
28+
29+
# Generate a PRBS with custom amplitude levels
30+
u_custom = prbs(N; low=0, high=5, seed=42)
31+
32+
# Generate a slower PRBS with period=10 (holds values for 10 samples)
33+
u_slow = prbs(N; period=10, seed=42)
34+
35+
# Plot the signals
36+
p1 = plot(u_prbs, title="Standard PRBS", xlabel="Sample", ylabel="Amplitude",
37+
label="u(t)", linewidth=2)
38+
p2 = plot(u_custom, title="Custom PRBS (0 to 5)", xlabel="Sample", ylabel="Amplitude",
39+
label="u(t)", linewidth=2)
40+
p3 = plot(u_slow, title="Slow PRBS (period=10)", xlabel="Sample", ylabel="Amplitude",
41+
label="u(t)", linewidth=2)
42+
43+
plot(p1, p2, p3, layout=(3,1), size=(600, 600))
44+
```
45+
46+
## Chirp Signals
47+
48+
```@docs
49+
chirp
50+
```
51+
52+
### Example
53+
54+
```@example input_signals
55+
# Generate chirp signals
56+
N = 1000
57+
Ts = 0.01
58+
f0 = 1.0 # Start frequency (Hz)
59+
f1 = 10.0 # End frequency (Hz)
60+
61+
u_log = chirp(N; Ts, f0, f1, logspace=true)
62+
u_lin = chirp(N; Ts, f0, f1, logspace=false)
63+
64+
# Time vector for plotting
65+
t = (0:N-1) * Ts
66+
67+
# Plot time domain signals
68+
p1 = plot(t, u_log, title="Logarithmic Chirp", xlabel="Time (s)", ylabel="Amplitude",
69+
label="u(t)", linewidth=1)
70+
p2 = plot(t, u_lin, title="Linear Chirp", xlabel="Time (s)", ylabel="Amplitude",
71+
label="u(t)", linewidth=1)
72+
73+
plot(p1, p2, layout=(2,1), size=(600, 400))
74+
```
75+
76+
### Frequency Content Analysis
77+
78+
```@example input_signals
79+
using ControlSystemIdentification.FFTW
80+
81+
# Analyze frequency content of the chirp signals
82+
U_log = abs.(fft(u_log))
83+
U_lin = abs.(fft(u_lin))
84+
85+
# Frequency axis (only plot positive frequencies)
86+
freqs = (0:N-1) / (N*Ts)
87+
half_N = N÷2
88+
89+
# Plot frequency domain (first half only, up to Nyquist frequency)
90+
p1 = plot(freqs[2:half_N], U_log[2:half_N], title="Logarithmic Chirp Spectrum",
91+
xlabel="Frequency (Hz)", ylabel="Magnitude", label="Log chirp",
92+
xscale=:log10, yscale=:log10, linewidth=2)
93+
p2 = plot(freqs[2:half_N], U_lin[2:half_N], title="Linear Chirp Spectrum",
94+
xlabel="Frequency (Hz)", ylabel="Magnitude", label="Linear chirp",
95+
xscale=:log10, yscale=:log10, linewidth=2)
96+
vline!(p1, [f0, f1], label="", linestyle=:dash, color=:red, alpha=0.7)
97+
vline!(p2, [f0, f1], label="", linestyle=:dash, color=:red, alpha=0.7)
98+
99+
plot(p1, p2, layout=(2,1), size=(600, 400))
100+
```
101+
102+
Chirp signals are excellent for:
103+
- Frequency response estimation
104+
- Analyzing system behavior across frequency ranges
105+
- Identifying resonances and anti-resonances
106+
- Logarithmic chirps provide equal energy per frequency decade
107+
108+
## Multi-sine Signals
109+
110+
```@docs
111+
multisine
112+
```
113+
114+
### Example
115+
116+
```@example input_signals
117+
# Generate multi-sine signal
118+
N = 1000
119+
Ts = 0.01
120+
frequencies = [0.5, 2.0, 5.0, 10.0] # Hz
121+
122+
u_multi = multisine(N; frequencies=frequencies, Ts)
123+
124+
# Time vector
125+
t = (0:N-1) * Ts
126+
127+
# Plot time domain signal
128+
p1 = plot(t, u_multi, title="Multi-sine Signal", xlabel="Time (s)", ylabel="Amplitude",
129+
label="u(t)", linewidth=1)
130+
131+
# Analyze frequency content
132+
U_multi = abs.(fft(u_multi))
133+
freqs = (0:N-1) / (N*Ts)
134+
half_N = N÷2
135+
136+
# Plot frequency domain
137+
p2 = plot(freqs[1:half_N], U_multi[1:half_N], title="Multi-sine Spectrum",
138+
xlabel="Frequency (Hz)", ylabel="Magnitude", label="Spectrum",
139+
linewidth=2)
140+
141+
# Mark the target frequencies with vertical lines
142+
for f in frequencies
143+
if f <= freqs[half_N]
144+
plot!(p2, [f, f], [0, maximum(U_multi[1:half_N])],
145+
label="", linestyle=:dash, color=:red, alpha=0.7)
146+
end
147+
end
148+
149+
plot(p1, p2, layout=(2,1), size=(600, 400))
150+
```
151+
152+
Multi-sine signals are ideal for:
153+
- Avoiding problematic frequencies (resonances)
154+
- Controlled spectral content
155+
- Nonlinear system identification
156+
- Minimizing peak factor (crest factor)
157+
158+
## Step Signals
159+
160+
```@docs
161+
step_signal
162+
```
163+
164+
### Example
165+
166+
```@example input_signals
167+
# Generate step signals
168+
N = 200
169+
Ts = 0.1
170+
u_step1 = step_signal(N; step_time=5.0, Ts) # Step at 5.0 seconds
171+
u_step2 = step_signal(N; step_time=10.0, Ts) # Step at 10.0 seconds
172+
173+
# Plot the signals
174+
p1 = plot(u_step1, title="Step at 5.0 seconds", xlabel="Sample", ylabel="Amplitude",
175+
label="u(t)", linewidth=2)
176+
p2 = plot(u_step2, title="Step at 10.0 seconds", xlabel="Sample", ylabel="Amplitude",
177+
label="u(t)", linewidth=2)
178+
179+
plot(p1, p2, layout=(2,1), size=(600, 400))
180+
```
181+
182+
## Design Guidelines Video
183+
184+
```@raw html
185+
<iframe style="height: 315px; width: 560px" src="https://www.youtube.com/embed/QMO8cDpjw5U?si=MZuC1BwKoLtgGJ_y" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
186+
```

‎src/ControlSystemIdentification.jl‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ export kautz, laguerre, laguerre_oo, laguerre_id, adhocbasis, sum_basis, basis_r
7474

7575
export autocorplot, crosscorplot, modelfit, residualplot
7676

77+
export prbs, chirp, multisine, step_signal
78+
7779
include("utils.jl")
7880
include("types.jl")
7981
include("frd.jl")
@@ -85,6 +87,7 @@ include("spectrogram.jl")
8587
include("frequency_weights.jl")
8688
include("basis_functions.jl")
8789
include("plotting.jl")
90+
include("input_signals.jl")
8891

8992
"""
9093
predict(sys, d::AbstractIdData, args...)
@@ -398,4 +401,6 @@ function DSP.resample(sys::AbstractStateSpace{<:Discrete}, Qd::AbstractMatrix, n
398401
c2d(sys2, Qc)
399402
end
400403

404+
using .InputSignals
405+
401406
end # module

0 commit comments

Comments
 (0)