Skip to content

Commit 2f16741

Browse files
committed
add Sampler with AD effects
1 parent 77992a6 commit 2f16741

File tree

3 files changed

+85
-3
lines changed

3 files changed

+85
-3
lines changed

docs/src/tutorials/noise.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ z = ShiftIndex(Clock(0.1))
117117
@equations begin
118118
connect(input.output, quant.input)
119119
D(x) ~ 0 # Dummy equation
120+
# D(x) ~ Hold(quant.y) # Dummy equation
120121
end
121122
end
122123
@named m = QuantizationModel()
@@ -147,3 +148,32 @@ plot(u, [y_mr y_mt], label=["Midrise" "Midtread"], xlabel="Input", ylabel="Outpu
147148
Note how the default mid-rise quantizer mode has a rise at the middle of the interval, while the mid-tread mode has a flat region (a tread) centered around the middle of the interval.
148149

149150
The default option `midrise = true` includes both end points as possible output values, while `midrise = false` does not include the upper limit.
151+
152+
153+
## Sampling with AD effects
154+
The block [`SampleWithADEffects`](@ref) combines a [`Sampler`](@ref), a [`NormalNoise](@ref) and a [`Quantization`](@ref) block to simulate the effects of practical sampling, noise and quantization in an AD converter. The block has the connectors `input` and `output`, where the input is the continuous-time signal to be sampled, and the output is the quantized, noisy signal. Example
155+
156+
```@example QUANT
157+
@mtkmodel PracticalSamplerModel begin
158+
@components begin
159+
input = Sine(amplitude=1.2, frequency=1, smooth=false)
160+
sampling = SampleWithADEffects(; dt=0.03, bits=3, y_min = -1, y_max = 1, sigma = 0.05, noisy = false, quantized=false, midrise=true)
161+
end
162+
@variables begin
163+
x(t) = 0 # Dummy variable to work around a bug for models without continuous-time state
164+
end
165+
@equations begin
166+
connect(input.output, sampling.input)
167+
D(x) ~ Hold(sampling.y) # Dummy equation
168+
end
169+
end
170+
@named m = PracticalSamplerModel()
171+
m = complete(m)
172+
ssys = structural_simplify(IRSystem(m))
173+
# prob = ODEProblem(ssys, [m.sampling.noise.y(z-1) => 0], (0.0, 2.0))
174+
prob = ODEProblem(ssys, [], (0.0, 2.0))
175+
sol = solve(prob, Tsit5())
176+
plot(sol, idxs=m.input.output.u)
177+
plot!(sol, idxs=m.sampling.y, label="AD converted output")
178+
# plot!(sol, idxs=m.sampling.sampler.y, label="AD converted output")
179+
```

src/ModelingToolkitSampledData.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ using StableRNGs
55

66
export get_clock
77
export DiscreteIntegrator, DiscreteDerivative, Delay, Difference, ZeroOrderHold, Sampler,
8-
ClockChanger,
8+
ClockChanger, SampleWithADEffects,
99
DiscretePIDParallel, DiscretePIDStandard, DiscreteStateSpace,
1010
DiscreteTransferFunction, NormalNoise, UniformNoise, Quantization,
1111
ExponentialFilter

src/discrete_blocks.jl

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -913,8 +913,6 @@ This block is not differentiable, the derivative is zero everywhere exect for at
913913
bits::Int = 8, [description = "Number of bits of quantization"]
914914
quantized::Bool = true, [description = "If quantization effects shall be computed."]
915915
end
916-
begin
917-
end
918916
@equations begin
919917
y(z) ~ ifelse(quantized == true, quantize(u(z), bits, y_min, y_max, midrise), u(z))
920918
end
@@ -1013,4 +1011,58 @@ Discrete-time On-Off controller with hysteresis. The controller switches between
10131011
y(z) ~ k*(2*s(z) - 1)
10141012
end
10151013
end
1014+
end
1015+
1016+
"""
1017+
SampleWithADEffects(quantized = false, noisy = false)
1018+
1019+
A sampler with additional effects that appear in practical systems, such as measurement noise and quantization.
1020+
1021+
The operations occur in the order
1022+
1. Sampling
1023+
2. Noise addition
1024+
3. Quantization
1025+
1026+
# Structural parameters:
1027+
- `quantized`: If true, the output is quantized. When this option is used, the output is quantized to the number of bits specified by the `bits` parameter. The quantization is midrise if `midrise = true`, otherwise it is midtread. The output is also limited to the range `[y_min, y_max]`.
1028+
- `noisy`: If true, the output is corrupted by additive white Gaussian noise with standard deviation `sigma` (defaults to 0.1). If `noisy = false`, the noise block is a unit gain.
1029+
- `dt`: Sample interval of the sampler. If not specified, the sample interval is inferred from the clock of the system.
1030+
- `clock`: Clock signal of the system. If not specified, the sample interval is inferred from the clock of the system. If `clock` is specified, the parameter `dt` has no effect.
1031+
1032+
# Parameters:
1033+
- `y_min`: Lower limit of output, defaults to -1. Only used if `quantized = true`.
1034+
- `y_max`: Upper limit of output, defaults to 1. Only used if `quantized = true`.
1035+
- `bits`: Number of bits of quantization, defaults to 8 (256 output levels between `y_min` and `y_max`). Only used if `quantized = true`.
1036+
- `sigma`: Standard deviation of the additive Gaussian noise, defaults to 0.1. Only used if `noisy = true`.
1037+
"""
1038+
@mtkmodel SampleWithADEffects begin
1039+
@extend input, output = siso = SISO()
1040+
@structural_parameters begin
1041+
dt = nothing
1042+
clock = (dt === nothing ? InferredDiscrete() : Clock(dt))
1043+
midrise = true
1044+
quantized = false
1045+
noisy = false
1046+
end
1047+
@parameters begin
1048+
y_min = -1, [description = "Lower limit of output"]
1049+
y_max = 1, [description = "Upper limit of output"]
1050+
bits = 8, [description = "Number of bits of quantization"]
1051+
sigma = 0.1, [description = "Standard deviation of the additive noise"]
1052+
end
1053+
@components begin
1054+
sampler = Sampler(; clock)
1055+
if noisy
1056+
noise = NormalNoise(; sigma, additive = true)
1057+
else
1058+
noise = Gain(; k = 1)
1059+
end
1060+
quantization = Quantization(; y_min, y_max, midrise, quantized)
1061+
end
1062+
@equations begin
1063+
connect(input, sampler.input)
1064+
connect(sampler.output, noise.input)
1065+
connect(noise.output, quantization.input)
1066+
connect(quantization.output, output)
1067+
end
10161068
end

0 commit comments

Comments
 (0)