Logic Machine Forum
Outside compensation curve - Printable Version

+- Logic Machine Forum (https://forum.logicmachine.net)
+-- Forum: LogicMachine eco-system (https://forum.logicmachine.net/forumdisplay.php?fid=1)
+--- Forum: Scripting (https://forum.logicmachine.net/forumdisplay.php?fid=8)
+--- Thread: Outside compensation curve (/showthread.php?tid=1770)



Outside compensation curve - oyvindnordbo - 04.12.2018

I have tried to make an outside compensation curve for a heating system in the FB editor, but do not find out completely of unwanted blocks that are most powerful to use.
I need to write from four groups
-10 ° C = 45 ° C
-5 ° C = 40 ° C
0 ° C = 35 ° C
5 ° C = 30 ° C
In order to get a set point out of what outdoor temperature it is, is this possible?


RE: Outside compensation curve - admin - 04.12.2018

Do you really need a curve here? From your data it looks like simple linear compensation, for each 5 degrees of temperature the setpoint is lowered by 5 degrees:
Code:
setpoint = 35 - temperature



RE: Outside compensation curve - oyvindnordbo - 04.12.2018

Yes wee need a curve besause all values is going to be able to change in the visualizatione


RE: Outside compensation curve - admin - 04.12.2018

This might be helpful: http://lua-users.org/wiki/SimpleFit


RE: Outside compensation curve - Daniel - 04.12.2018

There is block in match advance curve function with 2 points, it might be good starting point.


RE: Outside compensation curve - merel - 04.12.2018

Hello.

You can try this:

Code:
function linear_by_table(input,curve,offset)
local FDY = offset or 0
    fk1= (curve["y"][1] - curve["y"][2]) / (curve["x"][1] - curve["x"][2])
    fk2= (curve["y"][2] - curve["y"][3]) / (curve["x"][2] - curve["x"][3])
    fk3= (curve["y"][3] - curve["y"][4]) / (curve["x"][3] - curve["x"][4])

    if input < curve["x"][1] then
        out = curve["y"][1] + FDY;
    elseif input >= curve["x"][1] and input < curve["x"][2] then    
        out = fk1 * (input - curve["x"][1]) + curve["y"][1] + FDY
    elseif input >= curve["x"][2] and input < curve["x"][3] then
        out = fk2 * (input - curve["x"][2]) + curve["y"][2] + FDY
    elseif input >= curve["x"][3] and input < curve["x"][4] then    
        out = fk3 * (input - curve["x"][3]) + curve["y"][3] + FDY
    else
        out = curve["y"][4] + FDY
    end
 return out
end

curve = {}
curve["x"] = {-20,-10,0,20}
curve["y"] = {80,70,65,18}

--offset=2
--log(linear_by_table(20,curve,offset))
log(linear_by_table(-15,curve))



RE: Outside compensation curve - jerryhenke - 16.01.2025

(04.12.2018, 13:45)merel Wrote: Hello.

You can try this:

Code:
function linear_by_table(input,curve,offset)
local FDY = offset or 0
    fk1= (curve["y"][1] - curve["y"][2]) / (curve["x"][1] - curve["x"][2])
    fk2= (curve["y"][2] - curve["y"][3]) / (curve["x"][2] - curve["x"][3])
    fk3= (curve["y"][3] - curve["y"][4]) / (curve["x"][3] - curve["x"][4])

    if input < curve["x"][1] then
        out = curve["y"][1] + FDY;
    elseif input >= curve["x"][1] and input < curve["x"][2] then    
        out = fk1 * (input - curve["x"][1]) + curve["y"][1] + FDY
    elseif input >= curve["x"][2] and input < curve["x"][3] then
        out = fk2 * (input - curve["x"][2]) + curve["y"][2] + FDY
    elseif input >= curve["x"][3] and input < curve["x"][4] then    
        out = fk3 * (input - curve["x"][3]) + curve["y"][3] + FDY
    else
        out = curve["y"][4] + FDY
    end
 return out
end

curve = {}
curve["x"] = {-20,-10,0,20}
curve["y"] = {80,70,65,18}

--offset=2
--log(linear_by_table(20,curve,offset))
log(linear_by_table(-15,curve))
Wow. Thanks! Used this today and made some small changes.

My code looks like this and runs nicely.
 

Code:
function linear_by_table(input, curve, offset)
    local FDY = offset or 0
    local n = #curve["x"] -- Number of points
    if n ~= #curve["y"] then
        error("x and y lists must have the same number of values")
    end
    local out
    if input < curve["x"][1] then
        out = curve["y"][1] + FDY
    elseif input >= curve["x"][n] then
        out = curve["y"][n] + FDY
    else
        for i = 1, n - 1 do
            if input >= curve["x"][i] and input < curve["x"][i + 1] then
                local fk = (curve["y"][i + 1] - curve["y"][i]) / (curve["x"][i + 1] - curve["x"][i])
                out = fk * (input - curve["x"][i]) + curve["y"][i] + FDY
                break
            end
        end
    end
    return out
end
-- Define the curve for interpolation
local curve = {x = {-20, -10, 0, 10, 20}, y = {42, 34, 32, 25, 15}}
-- Adjust offset to shift the entire curve
local offset = 0
-- Get outdoor temperature from KNX address 0/3/0
local outdoor_temp = grp.getvalue('0/3/0')
-- Calculate the flow setpoint based on the outdoor temperature
local flow_setpoint = linear_by_table(outdoor_temp, curve, offset)
-- Write the calculated value to KNX address 5/5/0
grp.write('5/5/0', flow_setpoint)
-- Log the results
log('Outdoor temperature: ' .. outdoor_temp .. ', Flow setpoint: ' .. flow_setpoint)



RE: Outside compensation curve - Erwin van der Zwart - 16.01.2025

For the outdoor temp combined with a curve i usually use a 3 days average, this can simply be done by attaching a trend to your measurement object and use this small script.

Code:
require('trends')
dateslastthreedays = {}
dateslastthreedays['start'] = os.date('*t', os.time() - (86400 * 4))
dateslastthreedays['end'] = os.date('*t', os.time() - (86400 * 1))
threedaysaverage = trends.fetchone('OUTDOOR TEMP', dateslastthreedays)



RE: Outside compensation curve - jerryhenke - 16.01.2025

(16.01.2025, 17:08)Erwin van der Zwart Wrote: For the outdoor temp combined with a curve i usually use a 3 days average, this can simply be done by attaching a trend to your measurement object and use this small script.

Code:
require('trends')
dateslastthreedays = {}
dateslastthreedays['start'] = os.date('*t', os.time() - (86400 * 4))
dateslastthreedays['end'] = os.date('*t', os.time() - (86400 * 1))
threedaysaverage = trends.fetchone('OUTDOOR TEMP', dateslastthreedays)

The only trend I have right now is outdoor temperature with a 5-min resolution.
I tried your script and can't really make it work.
I get incorrect values. I would rather like to use a span of the last 9 hours average temperature or so, not from four days agou until yesterday which i read your script.
I tried change 86400 * 4 to 86400 * 1 and the "end" to 86400 * 0, but that gives 0 as temperature. 

Any good suggestions?

Quote:require('trends')

-- Define time range for the timespan
local lastninehours = {}
lastninehours['start'] = os.date('*t', os.time() - (3600 * 9)) -- 9 hours back
lastninehours['end'] = os.date('*t', os.time()) -- Current time

-- Fetch the average temperature for the timespan
local ninehoursaverage = trends.fetchone('Utetemp', lastninehours)

-- Fetch the current temperature from KNX address 0/3/0
local currenttemperature = grp.getvalue('0/3/0') -- Assumes grp.getvalue function is used

if ninehoursaverage and currenttemperature then
    -- Calculate the highest value between the average temperature and the current temperature
    local highesttemperature = math.max(ninehoursaverage, currenttemperature)
   
    -- Send the highest value to KNX address 5/5/20
    grp.write('5/5/20', highesttemperature)
   
    -- Log both the average temperature and the temperature sent
    log("Average temperature for the last 9 hours: " .. tostring(ninehoursaverage))
    log("Temperature sent to 5/5/20: " .. tostring(highesttemperature))
else
    log("Could not fetch the average temperature or the current temperature.")
end



RE: Outside compensation curve - Erwin van der Zwart - 17.01.2025

You always fetch a full day, that is why my sample is using yesterday until 4 days back, so 3 full days..

From the knowledge base (time values are ignored) :
Code:
dates - Lua table with two items - start and end, each item must contain year, month, day keys, time values (hours, minutes and seconds) are ignored

You can get today via:

Code:
require('trends')
local today = {}
today['start'] = os.date('*t', os.time())
today['end'] = os.date('*t', os.time() + 86400)

-- Fetch the average temperature for the timespan
local todayaverage = trends.fetchone('Temperatuur Woonkamer', today)

log(todayaverage)
 
To see the full data used just change  -> trends.fetchone into  -> trends.fetch, here you will see all samples of today


RE: Outside compensation curve - admin - 17.01.2025

KB doc needs updating. It's possible to fetch data for the given period if start/end is a not a table but a timestamp number.

Fetch data for the last hour:
Code:
date = os.date('*t') -- current date as a table
time = os.time(date, true) -- convert UTC timestamp

-- fetch data for the last hour (3600 seconds)
datarange = {
  ['start'] = time - 3600,
  ['end'] = time,
}

data = trends.fetch('trend name', datarange)
log(data)



RE: Outside compensation curve - Erwin van der Zwart - 17.01.2025

Great! This makes it so much easier to use in many cases (:


RE: Outside compensation curve - jerryhenke - 17.01.2025

(17.01.2025, 08:18)admin Wrote: KB doc needs updating. It's possible to fetch data for the given period if start/end is a not a table but a timestamp number.

Fetch data for the last hour:
Code:
date = os.date('*t') -- current date as a table
time = os.time(date, true) -- convert UTC timestamp

-- fetch data for the last hour (3600 seconds)
datarange = {
  ['start'] = time - 3600,
  ['end'] = time,
}

data = trends.fetch('trend name', datarange)
log(data)

Amazing. Now it seems to work. 

I ended up with this script for a damped outdoor temperatur that i later use for my control of floor heating.
Thougts?

Also - do I have to define the resolution of the trend log that I use? Now mine is 5 minutes, but I am also starting a 1 hour-resolution trend log of outside temperature. I imagine less work for LM to calculate the average if I have a table with less values?

Quote:require('trends')

-- Function to calculate time range based on hours and days
local function get_time_range(start_hours, start_days, end_hours, end_days)
    local date = os.date('*t') -- current date and time as a table
    local current_time = os.time(date, true) -- convert to UTC timestamp
    local start_time = current_time - (3600 * start_hours + 86400 * start_days) -- calculate start time
    local end_time = current_time - (3600 * end_hours + 86400 * end_days) -- calculate end time

    return {
        ['start'] = start_time,
        ['end'] = end_time
    }
end

-- Define time range (replace the number of hours and days with your values)
local start_hours_back = 0 -- hours back from current time for start time
local start_days_back = 1 -- days back from current time for start time
local end_hours_back = 0 -- hours back from current time for end time
local end_days_back = 0 -- days back from current time for end time
local time_range = get_time_range(start_hours_back, start_days_back, end_hours_back, end_days_back)

-- Specify resolution for 5-minute data (360 seconds)
local resolution = 360

-- Fetch average temperature for the time period with specified resolution
local periodaverage = trends.fetchone('Utetemp', time_range, resolution)

-- Fetch the current temperature from KNX address 0/3/0
local currenttemperature = grp.getvalue('0/3/0') -- Assuming grp.getvalue function is used

if periodaverage and currenttemperature then
    -- Calculate the highest value between the average temperature and the current temperature
    local highesttemperature = math.max(periodaverage, currenttemperature)

    -- Send the highest value to KNX address 5/5/20
    grp.write('5/5/20', highesttemperature)

    -- Log both the average temperature and the temperature sent
    log("Average temperature for selected period: " .. tostring(periodaverage))
    log("Temperature sent to 5/5/20: " .. tostring(highesttemperature))
    log("Current temperature at 0/3/0: " .. tostring(currenttemperature))
else
    log("Could not fetch the average temperature or the current temperature.")
end