Skip to content

Instantly share code, notes, and snippets.

@tallakt
Last active November 30, 2017 14:49
Show Gist options
  • Save tallakt/df90fa808b02052214f6a89994e2cee5 to your computer and use it in GitHub Desktop.
Save tallakt/df90fa808b02052214f6a89994e2cee5 to your computer and use it in GitHub Desktop.
@doc """
# Mud Sync
Koden er basert på en modell hvor man har x pumper, hver med y stempel som går i
(360 / y) graders fase på hverandre. Man får en puls per omdreining av kamakselen. Hver
pumpe har sine egne pulser som brukes til synkronisering
"""
defmodule MudSync do
@strokes_per_rev 3
@number_of_pumps 3
@sectors @strokes_per_rev * @number_of_pumps # angir optimal spredning av fasene
@spm_grense 20
@all_pump_ids (0..(@number_of_pumps - 1))
# ved valg av proporsjonal faktor kan vi ta utgangspunkt i P=10
# et avvik på 1/10 revs vil da gi korreksjon på 1.0 RPM
# korreksjonen skal være utført på over 1/10 minutter=6 sek
# anslagsvis 20 sekunder siden korreksjonen er eksponensialt avtagende
@proportional_feedback 10.0
# gir tilbake pådrag i RPM for motoren
# pulsetimes er angitt i minutter fra et fast starttidspunkt
def mudsync(id, pulsetime_0, my_pulsetime, rpm_0, my_rpm)
def mudsync(0, _, _, _, my_rpm) do
# mudpumpe 0 kjører uansett som master
my_rpm
end
def mudsync(_, _, _, rpm_0, my_rpm) when abs(my_rpm - rpm_0) > @spm_grense do
# forskjellige spm, bare kjør som normalt
my_spm
end
def mudsync(id, pulsetime_0, my_pulsetime, rpm_0, _) do
e = calc_error_signal_in_revolutions(id, pulsetime_0, my_pulsetime, rpm_0)
rpm_0 - e * @proportional_feedback
end
def calc_error_signal_in_revolutions(id, pulsetime_0, my_pulsetime, rpm_0) do
# id skal være 0, 1, 2, .. , number_of_pumps - 1
period = 1 / rpm_0
reference_time = pulsetime_0 + period * id / @sectors
delta_time = balanced_rem(reference_time - pulsetime_0, period / @strokes_per_rev)
delta_time / period
end
@doc """
The rem(...) function in Elixir will return -1 for rem(-1,4). This function
will return always positive and provide a function that is cyclic for all
positive and negative values of dividend
https://en.wikipedia.org/wiki/Modulo_operation
For Elixir and Siemens TIA, the REM is implemented as "Truncated division"
according to the article. This cuntion converts "Truncated division" to
"Floored dicisvion" which is a cyclic function and what we need here.
## Examples
iex> positive_rem(1, 4)
1
iex> positive_rem(5, 4)
1
iex> positive_rem(-1, 4)
3
"""
def positive_rem(dividend, divisor) do
rem(rem(dividend, divisor) + divisor, divisor)
end
@doc """
balanced_rem provides a modulus like function that is cyclic over the divisor
but returns values in the range -(divisor/2)..(divisor/2).
This is achieved bu phase shifting the positive_rem function and
offseting the result by half the period.
## Examples
iex> balanced_rem(-4, 4)
0
iex> balanced_rem(-3, 4)
1
iex> balanced_rem(-2, 4)
-2
iex> balanced_rem(-1, 4)
-1
iex> balanced_rem(0, 4)
0
iex> balanced_rem(1, 4)
1
iex> balanced_rem(2, 4)
-2
iex> balanced_rem(3, 4)
-1
iex> balanced_rem(4, 4)
0
iex> balanced_rem(5, 4)
1
"""
defp balanced_rem(dividend, divisor) do
half_divisor = divisor / 2
positive_rem(dividend + half_divisor, divisor) - half_divisor
end
#
# RESTEN AV KODEN ER IKKE VIKTIG. JEG LAGET DEN SOM ET TANKEEKSPERIMENT.
# TENK AT FUNKSJONEN OVER KALLES HVERT SCAN MED ID 0, 1, 2 ETC, OG AT DEN
# SOM KALLER FUNKSJONEN HOLDER STYR PÅ NÅR PULSENE KOMMER
#
def latest_pulse_time(id) do
# returns the time of the last measured pulse for a mud pump
# the time is returned as minutes since a common time t0, eg. like
#
# https://en.wikipedia.org/wiki/Unix_time
#
# The function must return nil if the pulse has not been measured
# yet. Important to reset the time if the motor stops
#
1_000_000 # dummy value, must be implemented
end
def get_rpm_from_hmi(id) do
# implementeres som konnunikasjon mot hmi
1100
end
def update_rpm_for_vfd(id, spm) do
# implementeres mot VFD
end
def loop() do
calc_and_update_all_spm
:timer.sleep(10)
loop
end
def calc_and_update_all_rpm() do
for id <- @all_pump_ids, do
rpm = loop_calc_rpm(id)
update_rpm_for_vfd(id, rpm)
end
end
def loop_calc_rpm(id) do
pulsetime_0 = latest_pulse_time(0)
my_pulsetime = latest_pulse_time(id)
rpm = case {pulsetime_0, updated_pulsetime} do
{nil, _} ->
get_rpm_from_hmi(id)
{_, nil} ->
get_rpm_from_hmi(id)
_ ->
mudsync(id, pulsetime_0, updated_pulsetime, get_rpm_from_hmi(0), get_rpm_from_hmi(id))
end
rpm
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment