Skip to content

Add smooth sources in Blocks + Add Square and Triangular sources #59

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Blocks/Blocks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export Abs, Sign, Sqrt, Sin, Cos, Tan, Asin, Acos, Atan, Atan2, Sinh, Cosh, Tanh
export Log, Log10
include("math.jl")

export Constant, Sine, Cosine, ContinuousClock, Ramp, Step, ExpSine
export Constant, Sine, Cosine, ContinuousClock, Ramp, Step, ExpSine, Square, Triangular
include("sources.jl")

export Limiter, DeadZone, SlewRateLimiter
Expand Down
229 changes: 204 additions & 25 deletions src/Blocks/sources.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,47 @@
# Define and register smooth functions
# These are "smooth" aka differentiable and avoid Gibbs effect
# These follow: `offset` + `smooth_wave` * `smooth_step` with zero output for `t < start_time`
function smooth_cos(x, δ, f, amplitude, ϕ, offset, start_time)
offset + amplitude * cos(2*π*f*(x - start_time) + ϕ) * smooth_step(x, δ, one(x), zero(x), start_time)
end

function smooth_damped_sin(x, δ, f, amplitude, damping, ϕ, offset, start_time)
offset + exp((start_time - x)*damping)*amplitude*sin(2*π*f*(x - start_time) + ϕ) * smooth_step(x, δ, one(x), zero(x), start_time)
end

function smooth_ramp(x, δ, height, duration, offset, start_time)
offset + height/(duration) * (smooth_xH(x, δ, start_time) - smooth_xH(x, δ, start_time+duration))
end

function smooth_sin(x, δ, f, amplitude, ϕ, offset, start_time)
offset + amplitude * sin(2*pi*f*(x - start_time) + ϕ) * smooth_step(x, δ, one(x), zero(x), start_time)
end

function smooth_square(x, δ, f, amplitude, offset, start_time)
offset + amplitude*2atan(sin(2π*(x - start_time)*f)/δ)/π * smooth_step(x, δ, one(x), zero(x), start_time)
end

function smooth_step(x, δ, height, offset, start_time)
offset + height*(atan((x - start_time)/δ)/π + 0.5)
end

function smooth_triangular(x, δ, f, amplitude, offset, start_time)
offset + amplitude * (1-2acos((1 - δ)sin(2π*(x - start_time)*f))/π) * smooth_step(x, δ, one(x), zero(x), start_time)
end

function smooth_xH(x, δ, tₒ)
0.5*(x-tₒ) * (1+((x-tₒ)/sqrt((x-tₒ)^2+δ^2)))
end

function square(x, f, amplitude, offset, start_time)
offset + (x > start_time) * (amplitude * (4*floor(f*(x - start_time)) - 2*floor(2*(x - start_time)*f) + 1))
end

function triangular(x, f, amplitude, offset, start_time)
p = 1/f # period
offset + (x > start_time) * (4 * amplitude * f * abs(abs((x - p/4 - start_time) % p) - p/2) - amplitude)
end

"""
Generate constant signal.

Expand All @@ -22,25 +66,36 @@ Generate sine signal.
# Parameters:
- `frequency`: [Hz] Frequency of sine wave
- `amplitude`: Amplitude of sine wave
- `phase`: [rad] Phase of sine wave
- `phase`: [rad] Phase of sine wave
- `offset`: Offset of output signal
- `start_time`: [s] Output `y = offset` for `t < start_time`
- `smooth`: If `true`, returns a smooth wave. Defaults to `false`
It uses a smoothing factor of `δ=1e-5`

# Connectors:
- `output`
"""
function Sine(;name,
frequency,
function Sine(;name,
frequency,
amplitude=1,
phase=0,
offset=0,
start_time=0)
start_time=0,
smooth=false)

@named output = RealOutput()
pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase
equation = if smooth == false
offset + ifelse(t < start_time, 0, amplitude* sin(2*pi*frequency*(t - start_time) + phase))
else
δ = 1e-5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this parameter be exposed and documented?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps yes. Although having δ = 1e-6 or such makes a miniscule change to the result, it can upset high precision tests

smooth_sin(t, δ, frequency, amplitude, phase, offset, start_time)
end

eqs = [
output.u ~ offset + ifelse(t < start_time, 0, amplitude* sin(2*pi*frequency*(t - start_time) + phase))
output.u ~ equation
]

compose(ODESystem(eqs, t, [], pars; name=name), [output])
end

Expand All @@ -50,32 +105,43 @@ Generate cosine signal.
# Parameters:
- `frequency`: [Hz] Frequency of sine wave
- `amplitude`: Amplitude of sine wave
- `phase`: [rad] Phase of sine wave
- `phase`: [rad] Phase of sine wave
- `offset`: Offset of output signal
- `start_time`: [s] Output `y = offset` for `t < start_time`
- `smooth`: If `true`, returns a smooth wave. Defaults to `false`
It uses a smoothing factor of `δ=1e-5`

# Connectors:
- `output`
"""
function Cosine(;name,
frequency,

function Cosine(;name,
frequency,
amplitude=1,
phase=0,
offset=0,
start_time=0)
start_time=0,
smooth=false)

@named output = RealOutput()
pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase
equation = if smooth == false
offset + ifelse(t < start_time, zero(t), amplitude* cos(2*pi*frequency*(t - start_time) + phase))
else
δ = 1e-5
smooth_cos(t, δ, frequency, amplitude, phase, offset, start_time)
end
eqs = [
output.u ~ offset + ifelse(t < start_time, 0, amplitude* cos(2*pi*frequency*(t - start_time) + phase))
output.u ~ equation
]

compose(ODESystem(eqs, t, [], pars; name=name), [output])
end

"""
Generate current time signal.

# Parameters:
# Parameters:
- `offset`: Offset of output signal
- `start_time`: [s] Output `y = offset` for `t < start_time`

Expand All @@ -86,8 +152,9 @@ function ContinuousClock(;name, offset=0, start_time=0)
@named output = RealOutput()
pars = @parameters offset=offset start_time=start_time
eqs = [
output.u ~ offset + ifelse(t < start_time, 0, t - start_time)
output.u ~ offset + ifelse(t < start_time, zero(t), t - start_time)
]

compose(ODESystem(eqs, t, [], pars; name=name), [output])
end

Expand All @@ -99,22 +166,73 @@ Generate ramp signal.
- `duration`: [s] Duration of ramp (= 0.0 gives a Step)
- `offset`: Offset of output signal
- `start_time`: [s] Output `y = offset` for `t < start_time`
- `smooth`: If `true`, returns a smooth wave. Defaults to `false`
It uses a smoothing factor of `δ=1e-5`

# Connectors:
- `output`
"""
function Ramp(;name,
offset=0,
function Ramp(;name,
height=1,
duration=1,
start_time=0)
duration=1,
offset=0,
start_time=0,
smooth=false)

@named output = RealOutput()
pars = @parameters offset=offset start_time=start_time height=height duration=duration
eqs = [
output.u ~ offset + ifelse(t < start_time, 0,
equation = if smooth == false
offset + ifelse(t < start_time, 0,
ifelse(t < (start_time + duration), (t - start_time) * height / duration, height))
else
δ = 1e-5
smooth_ramp(t, δ, height, duration, offset, start_time)
end

eqs = [
output.u ~ equation
]

compose(ODESystem(eqs, t, [], pars; name=name), [output])
end

"""
Generate smooth square signal.

# Parameters:
- `frequency`: [Hz] Frequency of square wave
- `amplitude`: Amplitude of square wave
- `offset`: Offset of output signal
- `start_time`: [s] Output `y = offset` for `t < start_time`
- `smooth`: If `true`, returns a smooth wave. Defaults to `false`
It uses a smoothing factor of `δ=1e-5`

# Connectors:
- `output`
"""
function Square(; name, frequency=1.0, amplitude=1.0,
offset=0.0, start_time=0.0, smooth=false)
δ = 1e-5

@named output = RealOutput()
pars = @parameters begin
frequency=frequency
amplitude=amplitude
offset=offset
start_time=start_time
end

equation = if smooth == false
square(t, frequency, amplitude, offset, start_time)
else
δ = 1e-5
smooth_square(t, δ, frequency, amplitude, offset, start_time)
end

eqs = [
output.u ~ equation
]

compose(ODESystem(eqs, t, [], pars; name=name), [output])
end

Expand All @@ -125,16 +243,26 @@ Generate step signal.
- `height`: Height of step
- `offset`: Offset of output signal
- `start_time`: [s] Output `y = offset` for `t < start_time`
- `smooth`: If `true`, returns a smooth wave. Defaults to `false`
It uses a smoothing factor of `δ=1e-5`

# Connectors:
- `output`
"""
function Step(;name, offset=0, height=1, start_time=0)
function Step(;name, height=1, offset=0, start_time=0, smooth=true)
@named output = RealOutput()
pars = @parameters offset=offset start_time=start_time height=height
equation = if smooth == false
offset + ifelse(t < start_time, zero(t), height)
else
δ = 1e-5
smooth_step(t, δ, height, offset, start_time)
end

eqs = [
output.u ~ offset + ifelse(t < start_time, 0, height)
output.u ~ equation
]

compose(ODESystem(eqs, t, [], pars; name=name), [output])
end

Expand All @@ -145,26 +273,77 @@ Generate exponentially damped sine signal.
- `frequency`: [Hz] Frequency of sine wave
- `amplitude`: Amplitude of sine wave
- `damping`: [1/s] Damping coefficient of sine wave
- `phase`: [rad] Phase of sine wave
- `phase`: [rad] Phase of sine wave
- `offset`: Offset of output signal
- `start_time`: [s] Output `y = offset` for `t < start_time`
- `smooth`: If `true`, returns a smooth wave. Defaults to `false`
It uses a smoothing factor of `δ=1e-5`

# Connectors:
- `output`
"""
function ExpSine(;name,
frequency,
function ExpSine(; name,
frequency,
amplitude=1,
damping=0.1,
phase=0,
offset=0,
start_time=0)
start_time=0,
smooth=false)

@named output = RealOutput()
pars = @parameters offset=offset start_time=start_time amplitude=amplitude frequency=frequency phase=phase damping=damping

equation = if smooth == false
offset + ifelse(t < start_time, 0, amplitude * exp(-damping * (t - start_time)) * sin(2*pi*frequency*(t - start_time) + phase))
else
δ = 1e-5
smooth_damped_sin(t, δ, frequency, amplitude, damping, phase, offset, start_time)
end

eqs = [
output.u ~ offset + ifelse(t < start_time, 0, amplitude * exp(-damping * (t - start_time)) * sin(2*pi*frequency*(t - start_time) + phase))
output.u ~ equation
]

compose(ODESystem(eqs, t, [], pars; name=name), [output])
end

"""
Generate smooth triangular signal for frequencies less than or equal to 25 Hz

# Parameters:
- `frequency`: [Hz] Frequency of square wave
- `amplitude`: Amplitude of square wave
- `offset`: Offset of output signal.
- `start_time`: [s] Output `y = offset` for `t < start_time`
- `smooth`: If `true`, returns a smooth wave. Defaults to `false`
It uses a smoothing factor of `δ=1e-5`

# Connectors:
- `output`
"""
function Triangular(; name, amplitude=1.0, frequency=1.0,
offset=0.0, start_time=0.0, smooth=false)

@named output = RealOutput()
pars = @parameters begin
amplitude=amplitude
frequency=frequency
offset=offset
start_time=start_time
end

equation = if smooth == false
triangular(t, frequency, amplitude, offset, start_time)
else
δ = 1e-5
smooth_triangular(t, δ, frequency, amplitude, offset, start_time)
end

eqs = [
output.u ~ equation
]

compose(ODESystem(eqs, t, [], pars; name=name), [output])
end

Expand Down
Loading