Skip to content

Commit 22c2d08

Browse files
committed
add moving-average filter
1 parent b115a34 commit 22c2d08

File tree

4 files changed

+62
-5
lines changed

4 files changed

+62
-5
lines changed

docs/src/tutorials/noise.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ plot(figy, figu, plot_title = "DC Motor with Discrete-time Speed Controller")
8787
## Noise filtering
8888
You may, e.g.
8989
- Use [`ExponentialFilter`](@ref) to add exponential filtering using `y(k) ~ (1-a)y(k-1) + a*u(k)`, where `a` is the filter coefficient and `u` is the signal to be filtered.
90-
- Add moving average filtering using `y(k) ~ 1/N sum(i->u(k-i), i=0:N-1)`, where `N` is the number of samples to average over.
90+
No discrete-time filter components are available yet. You may, e.g.
91+
- Add moving average filtering using [`MovingAverageFilter`](@ref) according to `y(k) ~ 1/N * sum(u(k-i) for i=0:N-1)`, where `N` is the number of samples to average over.
9192

9293
## Colored noise
9394
Colored noise can be achieved by filtering white noise through a filter with the desired spectrum. No components are available for this yet.

src/ModelingToolkitSampledData.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ export DiscreteIntegrator, DiscreteDerivative, Delay, Difference, ZeroOrderHold,
88
ClockChanger, SampleWithADEffects,
99
DiscretePIDParallel, DiscretePIDStandard, DiscreteStateSpace,
1010
DiscreteTransferFunction, NormalNoise, UniformNoise, Quantization,
11-
DiscreteSlewRateLimiter, ExponentialFilter
11+
DiscreteSlewRateLimiter, ExponentialFilter, MovingAverageFilter
1212
export DiscreteOnOffController
13+
1314
include("discrete_blocks.jl")
1415

1516

src/discrete_blocks.jl

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,34 @@ Y(z) = \\dfrac{a}{1 - (1 - a) z^{-1}} U(z)
10391039
end
10401040
end
10411041

1042+
"""
1043+
MovingAverageFilter(N = 3)
1044+
1045+
Exponential filtering with input-output relation ``y(z) ~ sum(u(z-i) for i in 0:N-1) / N``.
1046+
1047+
Please note: this implementation of a moving average filter is not optimized for very large number of filter taps `N`.
1048+
1049+
# Parameters:
1050+
- `N`: (structural) Number of samples to average over
1051+
# Variables:
1052+
- `u`: Input signal
1053+
- `y`: Output signal
1054+
1055+
# Connectors:
1056+
- `input::RealInput`: Input signal
1057+
- `output::RealOutput`: Output signal
1058+
"""
1059+
@mtkmodel MovingAverageFilter begin
1060+
@extend u, y = siso = SISO()
1061+
@structural_parameters begin
1062+
z = ShiftIndex()
1063+
N = 3
1064+
end
1065+
@equations begin
1066+
y(z) ~ sum(u(z-i) for i in 0:N-1) / N
1067+
end
1068+
end
1069+
10421070
"""
10431071
DiscreteOnOffController(b = 0.1, bool = true)
10441072
@@ -1132,4 +1160,4 @@ The operations occur in the order
11321160
connect(noise.output, quantization.input)
11331161
connect(quantization.output, output)
11341162
end
1135-
end
1163+
end

test/test_discrete_blocks.jl

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,6 @@ end
512512
@test 0.89 <= sol(4, idxs=m.x) <= 1.11
513513
end
514514

515-
516515
@testset "ExponentialFilter" begin
517516
@info "Testing ExponentialFilter"
518517
z = ShiftIndex(Clock(0.1))
@@ -540,6 +539,34 @@ end
540539

541540
end
542541

542+
@testset "MovingAverageFilter" begin
543+
@info "Testing MovingAverageFilter"
544+
z = ShiftIndex(Clock(0.1))
545+
@mtkmodel MovingAverageFilterModel begin
546+
@components begin
547+
input = Step(start_time=1, smooth=false)
548+
filter = MovingAverageFilter(; N=3, z)
549+
end
550+
@variables begin
551+
x(t) = 0 # Dummy variable to workaround JSCompiler bug
552+
end
553+
@equations begin
554+
connect(input.output, filter.input)
555+
D(x) ~ 0
556+
end
557+
end
558+
559+
@named m = MovingAverageFilterModel()
560+
m = complete(m)
561+
ssys = structural_simplify(IRSystem(m))
562+
prob = ODEProblem(ssys, [m.filter.u(z-i) => 0 for i = 0:3], (0.0, 2.0))
563+
sol = solve(prob, Tsit5(), dtmax=0.1)
564+
# plot(sol, idxs=m.filter.y)
565+
@test sol(1.5, idxs=m.filter.y) == 1
566+
@test sol(0.999, idxs=m.filter.y) == 0
567+
@test 0 < sol(1.1, idxs=m.filter.y) < 1
568+
end
569+
543570
@testset "sampling with AD effects" begin
544571
@info "Testing sampling with AD effects"
545572
z = ShiftIndex()
@@ -565,4 +592,4 @@ end
565592
@test length(unique(sol[m.sampling.y])) == 8
566593
@test maximum(abs, sol(0:0.03:1, idxs=m.sampling.y) - sol(0:0.03:1, idxs=m.sampling.u)) < 0.3
567594

568-
end
595+
end

0 commit comments

Comments
 (0)