Last active
November 30, 2017 14:49
-
-
Save tallakt/df90fa808b02052214f6a89994e2cee5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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