FUNCTION_BLOCK "LGF_Astro"
{ S7_Optimized_Access := 'TRUE' }
VAR_INPUT
latitudeDD : Real; // latitude decimal degrees; North = positive; South = negative; [-90.0..90.0]
longitudeDD : Real; // longitude decimal degrees; East=Positive; West = negative; [-180.0..180.0]
modeDMS : Bool; // "1" = ModeDegMinSec, "0" = Mode DecDeg
latitudeDMS : "LGF_typeAstroDMS"; // latitudeDegMinSec; North = positive; South = negative
longitudeDMS : "LGF_typeAstroDMS"; // longitude DegMinSec; East=Positive; West = negative
offsetSunrise : Time; // offset to sunrise
offsetSunset : Time; // offset to sunset
END_VAR
VAR_OUTPUT
sunrise {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // sunrise (localtime)
sunset {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // sunset (localtime)
daytime : Bool; // time between OffsetSunrise and OffesetSunset
actSystemTime {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // current systemt ime
actLocalTime {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // current local time
error : Bool; // "0" = no error; "1" = error
statusID : UInt; // Error destination: "1" = LGF_Astro; "2" = RD_SYS_T; "3" = RD_LOC_T
status : Word; // status and error codes
END_VAR
VAR_TEMP
tempSysTime {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // actual system time UTC
tempOfficLocTime {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // official actual local time
tempTimeZone : Real; // official local time zone
tempDayOfYear : Real; // day of the year
templngHour : Real; // unit = hours; East=Positive; West = negative
tempLatitudeRad : Real; // unit = radian; North = positive; South = negative
tempAppRiseTime : Real; // approximate Sun Rising time
tempRiseMeanAnomaly : Real; // sun's rising time mean anomaly
tempRiseMeanRad : Real; // sun's rising time mean anomaly in radian
tempRiseLongitude : Real; // sun's rising true longitude
tempRiseLonRad : Real; // sun's rising true longitude in radian
tempRiseAscension : Real; // sun's rising rigth ascension
tempRiseAscensRad : Real; // sun's rising rigth ascension in radian
tempSinDeclination : Real; // sin of Sun's declination
tempCosDeclination : Real; // cos of Sun's declination
tempCosLocHourAngle : Real; // cos Sun's local hour angle
tempLocHourAngle : Real; // Sun's local hour angle
tempLocalMeamTime : Real; // local mean time of rising / setting
tempUTC : Real; // UTC time of rising / setting
tempLocalTime : Real; // local time of rising / setting
tempRetval : Int; // status of the used instructions
tempIntSunrise {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // calculation int Sunrise
tempIntSunset {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // calculation int Sunset
tempIntLatDD : Real; // latitude decimal degrees; North = positive; South = negative
tempIntLonDD : Real; // longitude decimal degrees; East=Positive; West = negative
tempLatAuxDMS : Real; // latitude in decimal degrees
tempLatAuxMinDMS : Real; // minutes in decimal degrees
tempLatAuxSecDMS : Real; // seconds in decimal degrees
tempLonAuxDMS : Real; // longitude in decimal degrees
tempLonAuxMinDMS : Real; // minutes in decimal degrees
tempLonAuxSecDMS : Real; // seconds in decimal degrees
tempDate1Jan {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL;
END_VAR
VAR CONSTANT
NORTH1 : Char := 'n'; // north
NORTH2 : Char := 'N'; // North
SOUTH1 : Char := 's'; // south
SOUTH2 : Char := 'S'; // South
EAST1 : Char := 'e'; // east
EAST2 : Char := 'E'; // East
WEST1 : Char := 'w'; // west
WEST2 : Char := 'W'; // West
PI : Real := 3.141593; // Mathmatical constant 4*ATN(1)
DEGREE_TO_RADIAN_0_0174 : Real := 0.01745329; // multiply factor to get radian from degree
RADIAN_TO_DEGREE_57_2957 : Real := 57.29578; // multiply factor to get degree from radian
ZENIT_1_58534 : LReal := 1.58534073722818; // Sun's zenith for sunrise/sunset (in radian units)
"0_0_DEG" : Real := 0.0; // 0.0 degrees
"15_0_DEG" : Real := 15.0; // 15.0 degrees
"90_0_DEG" : Real := 90.0; // 90.0 degrees
"180_0_DEG" : Real := 180.0; // 180.0 degrees
"360_0_DEG" : Real := 360.0; // 360.0 degrees
"59_MIN_OR_SEC" : UInt := 59; // 59 minutes/seconds
SECONDS_PER_HOUR : Real := 3600.0; // conversion factor seconds to hour
MINUTES_PER_HOUR : Real := 60.0; // conversion factor minutes to hour
HOURS_PER_DAY : Real := 24.0; // 24 hours
MS_PER_HOUR : Real := 3600000.0;
TIME_ZERO {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL; // output hours, minutes, seconds to zero
APPROXIMATE_TIME_6 : Int := 6; // approximate time 6 hours
APPROXIMATE_TIME_18 : Int := 18; // approximate time 18 hours
SUN_MEAN_ANOMALY_0_9856 : Real := 0.9856; // sun mean anomaly 0.9856
SUN_MEAN_ANOMALY_3_289 : Real := 3.289; // sun mean anomaly 3.289
SUN_TRUE_LONGITUDE_0_02 : Real := 0.02; // sun true longitude 0.02
SUN_TRUE_LONGITUDE_1_916 : Real := 1.916; // sun true longitude 1.916
SUN_TRUE_LONGITUDE_2_0 : Real := 2.0; // sun true longitude 2.0
SUN_TRUE_LONGITUDE_282_634 : Real := 282.634; // sun true longitude 282.634
SUN_RIGHT_ASCENSION_0_91764 : Real := 0.91764; // sun right ascension 0.91764
SUN_RIGHT_ASCENSION_2_0 : Real := 2.0; // sun right ascension 2.0
SUN_DECLINATION_0_39782 : Real := 0.39782; // sun declination 0.39782
SUM_LOCAL_HOUR_ANGLE : Int := 1; // summ local hour angle
LOCAL_MEAN_TIME_0_06571 : Real := 0.06571; // -
LOCAL_MEAN_TIME_6_622 : Real := 6.622; // -
NO_ERROR : Word := 16#0000; // -
NO_CURRENT_JOBS : Word := 16#7000; // -
ERROR_LAT_DMS_DIR : Word := 16#8200; // error Latitude DMS direction
ERROR_LAT_DMS_VAL : Word := 16#8201; // error Latitude DMS value
ERROR_LON_DMS_DIR : Word := 16#8202; // error Longitude DMS direction
ERROR_LON_DMS_VAL : Word := 16#8203; // error Longitude DMS value
ERROR_LAT_DD_VAL : Word := 16#8204; // error Latitude DD value
ERROR_LON_DD_VAL : Word := 16#8205; // error Longitude DD value
ERROR_IN_THIS_BLOCK : UInt := 1; // error in this block
ERROR_RD_SYS_T : UInt := 2; // error instruction RD_SYS_T
ERROR_RD_LOC_T : UInt := 3; // error instruction RD_LOC_T
END_VAR
BEGIN
//=============================================================================
// SIEMENS AG
// (c)Copyright 2017
//-----------------------------------------------------------------------------
// Library: LGF (Library General Functions)
// Tested with: CPU1212C DC/DC/DC FW:V4.2
// Engineering: TIA Portal V14 Update 1
// Restrictions: -
// Requirements: PLC (S7-1200 / S7-1500)
// Functionality: Astronomical clock, optional offset, input format DMS or DD
//-----------------------------------------------------------------------------
// Change log table:
// Version Date In charge / Changes applied
// 01.00.00 19.08.2015 Siemens Industry Online Support
// First released version
// 01.00.01 01.10.2015 Siemens Industry Online Support
// T_ADD instruction is replaced with "+"
// 01.00.02 16.11.2015 Siemens Industry Online Support
// "offsetSunrise", "offsetSunset" is calculated in
// "daytime"
// Bug fix at "Adjust back TO UTC"
// 01.01.00 07.06.2015 Siemens Industry Online Support
// Add output actSystemTime and actLocalTime
// 01.01.01 15.06.2015 Siemens Industry Online Support
// Add comments
// 01.01.02 04.01.2017 Siemens Industry Online Support
// Bug fix at calculation sunrise and sunset
// 01.01.03 20.01.2017 Siemens Industry Online Support
// Upgrade: TIA V14 Update 1
// 01.01.04 22.02.2017 Siemens Industry Online Support
// Code optimization
// 01.01.05 09.07.2018 Siemens Industry Online Support
// Initialize #tempIntSunrise, #tempIntSunset,#tempDate1Jan
//=============================================================================
// Function:
// Your position may be entered at LatitudeDD/LongitudeDD, ModeDMS = 0
// Format signed degree(decimal)
// or
// your position may be entered at LatitudeDMS/LongitudeDMS, ModeDMS = 1
// Format direction, degree, minute, second
//
// OffsetSunrise is added to Latitude and output at Sunrise
// OffsetSunset is added to Longitude and output at Sunset
//
// daytime is set when the actual time is inbetween Sunrise and Sunset
// ============================================================================
// Advice:
// please take care that the system time is permanently synchronizes anyhow
// ============================================================================
// Abbreviations
// lon: Longitude
// lat: Latitude
// dir: direction (valid characters: n, N, s, S, e, E, w, W)
// DD: decimal degrees (type Real)
// DMS: degrees, minutes, seconds (all types UINT)
//=============================================================================
// If a faulty value is entered at LatitudeDD and ModeDMS = 0
// then the value of Sunrise is set to zero
// and the value of Sunset is set to zero
// and daytime is set to zero
//
// If a faulty value is entered at LatitudeDMS and ModeDMS = 1
// then the value of Sunrise ist set to zero
// and the value of Sunset is set to zero
// and daytime is set to zero
//=============================================================================
// Set "No current job" status
#error := false;
#statusID := #ERROR_IN_THIS_BLOCK;
#status := #NO_CURRENT_JOBS;
// Initialization
#sunrise := #TIME_ZERO;
#sunset := #TIME_ZERO;
#tempIntSunrise := #TIME_ZERO;
#tempIntSunset := #TIME_ZERO;
#tempDate1Jan := #TIME_ZERO;
REGION System time, local time, time zone
// Reading system and local time ==============================================
#tempRetval := RD_SYS_T(#tempSysTime); // Reading system time UTC
#actSystemTime := #tempSysTime; // Output system time UTC
IF (#tempRetval > 1)
THEN
#error := true;
#statusID := #ERROR_RD_SYS_T;
#status := INT_TO_WORD(#tempRetval);
RETURN;
END_IF;
#tempRetval := RD_LOC_T(#tempOfficLocTime); // Reading official actual local time
#actLocalTime := #tempOfficLocTime; // Output official actual local time
IF (#tempRetval > 1)
THEN
#error := true;
#statusID := #ERROR_RD_LOC_T;
#status := INT_TO_WORD(#tempRetval);
RETURN;
END_IF;
// Calculation of time difference #tempSysTime - #tempOfficLocTime ============
#tempTimeZone := DINT_TO_REAL(TIME_TO_DINT(T_DIFF(IN1 := #tempSysTime, IN2 := #tempOfficLocTime))) / #MS_PER_HOUR;
END_REGION //System time, local time, time zone
REGION DMS input value check and convert to DD
// DMS input value check and convert to DD ====================================
IF (#modeDMS = 1)
THEN
// Latitude =================================================================
// Convert seconds to decimal degrees
#tempLatAuxSecDMS := UINT_TO_REAL(#latitudeDMS.sec)
/ #SECONDS_PER_HOUR;
// Convert minutes to decimal degrees
#tempLatAuxMinDMS := UINT_TO_REAL(#latitudeDMS.min)
/ #MINUTES_PER_HOUR;
// Sum of decimal degrees, minutes and seconds
#tempLatAuxDMS := #latitudeDMS.deg + #tempLatAuxMinDMS + #tempLatAuxSecDMS;
IF (#latitudeDMS.dir = #SOUTH1)
THEN
#tempLatAuxDMS := - (#tempLatAuxDMS); // Negate value if south 's'
ELSIF (#latitudeDMS.dir = #SOUTH2)
THEN
#tempLatAuxDMS := - (#tempLatAuxDMS); // Negate value if south 'S'
ELSIF (#latitudeDMS.dir = #NORTH1)
THEN
; // Positive value if north 'n'
ELSIF (#latitudeDMS.dir = #NORTH2)
THEN
; // Positive value if north 'N'
ELSE // ErrorLatDMS: direction
#error := true;
#statusID := #ERROR_IN_THIS_BLOCK;
#status := #ERROR_LAT_DMS_DIR;
END_IF;
// Sum input latitude decimal degrees, minutes & seconds > 90°, set fault
IF (ABS(#tempLatAuxDMS) > #"90_0_DEG")
THEN
#error := true;
#statusID := #ERROR_IN_THIS_BLOCK;
#status := #ERROR_LAT_DMS_VAL;
END_IF;
// Check value input limits, latitude minutes > 59, set fault
IF (#latitudeDMS.min > #"59_MIN_OR_SEC")
THEN
#error := true;
#statusID := #ERROR_IN_THIS_BLOCK;
#status := #ERROR_LAT_DMS_VAL;
RETURN;
END_IF;
// Check value limits input, latitude seconds > 59, set fault
IF (#latitudeDMS.sec > #"59_MIN_OR_SEC")
THEN
#error := true;
#statusID := #ERROR_IN_THIS_BLOCK;
#status := #ERROR_LAT_DMS_VAL;
END_IF;
// Longitude ================================================================
// Convert seconds TO decimal degrees
#tempLonAuxSecDMS := UINT_TO_REAL(#longitudeDMS.sec)
/ #SECONDS_PER_HOUR;
// Convert minutes to decimal degrees
#tempLonAuxMinDMS := UINT_TO_REAL(#longitudeDMS.min)
/ #MINUTES_PER_HOUR;
// Sum of decimal degrees, minutes and seconds
#tempLonAuxDMS := #longitudeDMS.deg + #tempLonAuxMinDMS + #tempLonAuxSecDMS;
IF (#longitudeDMS.dir = #WEST1)
THEN
#tempLonAuxDMS := - (#tempLonAuxDMS); // Negate value if west 'w'
ELSIF (#longitudeDMS.dir = #WEST2)
THEN
#tempLonAuxDMS := - (#tempLonAuxDMS); // Negate value if west 'W'
ELSIF (#longitudeDMS.dir = #EAST1)
THEN
; // Positive value if east 'e'
ELSIF (#longitudeDMS.dir = #EAST2)
THEN
; // Positive value if east 'E'
ELSE // ErrorLonDMS: direction
#error := true;
#statusID := #ERROR_IN_THIS_BLOCK;
#status := #ERROR_LON_DMS_DIR;
END_IF;
// Sum of longitude decimal degrees, minutes and seconds > 180°
IF (ABS(#tempLonAuxDMS) > #"180_0_DEG")
THEN
#error := true;
#statusID := #ERROR_IN_THIS_BLOCK;
#status := #ERROR_LON_DMS_VAL;
END_IF;
// Check value limits input, longitude minutes > 59, set fault
IF (#longitudeDMS.min > #"59_MIN_OR_SEC")
THEN
#error := true;
#statusID := #ERROR_IN_THIS_BLOCK;
#status := #ERROR_LON_DMS_VAL;
END_IF;
// Check value limits input, longitude seconds > 59, set fault
IF (#longitudeDMS.sec > #"59_MIN_OR_SEC")
THEN
#error := true;
#statusID := #ERROR_IN_THIS_BLOCK;
#status := #ERROR_LON_DMS_VAL;
END_IF;
// Case of wrong DMS input
IF (#error = true)
THEN
#tempLatAuxDMS := #"0_0_DEG"; // Set value latitude to zero
#tempLonAuxDMS := #"0_0_DEG"; // Set value longitude to zero
RETURN;
END_IF;
// Copy DMS values to work variables
#tempIntLatDD := #tempLatAuxDMS;
#tempIntLonDD := #tempLonAuxDMS;
END_IF;
END_REGION // DMS input value check and convert to DD
REGION DD input value check
// DD input value check =======================================================
IF (#modeDMS = 0)
THEN
// input latitude DD > 90°, set fault
IF (ABS(#latitudeDD) > #"90_0_DEG")
THEN
#error := true;
#statusID := #ERROR_IN_THIS_BLOCK;
#status := #ERROR_LAT_DD_VAL;
END_IF;
// input longitude DD > 180°, set fault
IF (ABS(#longitudeDD) > #"180_0_DEG")
THEN
#error := true;
#statusID := #ERROR_IN_THIS_BLOCK;
#status := #ERROR_LON_DD_VAL;
END_IF;
// Case of wrong DD input
IF (#error = true) THEN
#tempIntLatDD := #"0_0_DEG"; // Set value latitude to zero
#tempIntLonDD := #"0_0_DEG"; // Set value longitude to zero
RETURN;
END_IF;
// Copy DD values to work variables
#tempIntLatDD := #latitudeDD;
#tempIntLonDD := #longitudeDD;
END_IF;
// Converting the input values to work units ==================================
#tempLatitudeRad := #tempIntLatDD * #DEGREE_TO_RADIAN_0_0174;
// Longitude [hours] = longitude [degrees] * 24/360 = longitude [degrees] /15
#templngHour := #tempIntLonDD / #"15_0_DEG";
END_REGION // DD input value check
REGION Day of the year
// Day of the year
#tempDate1Jan.YEAR := #tempOfficLocTime.YEAR;
#tempDate1Jan.MONTH := 1;
#tempDate1Jan.DAY := 1;
#tempDayOfYear := UDINT_TO_REAL(DATE_TO_UDINT(DTL_TO_DATE(#tempOfficLocTime))
- DATE_TO_UDINT(DTL_TO_DATE(#tempDate1Jan)) + 1);
END_REGION // Day of the year
REGION Calculate Sunrise
// Calculate Sunrise ==========================================================
// Aproximate time
#tempAppRiseTime := #tempDayOfYear + (#APPROXIMATE_TIME_6 - #templngHour)
/ #HOURS_PER_DAY;
// Sun's mean anomaly
#tempRiseMeanAnomaly := #SUN_MEAN_ANOMALY_0_9856 * #tempAppRiseTime
- #SUN_MEAN_ANOMALY_3_289;
#tempRiseMeanRad := #tempRiseMeanAnomaly * #DEGREE_TO_RADIAN_0_0174;
// Sun's true longitude
#tempRiseLongitude := #tempRiseMeanAnomaly
+ #SUN_TRUE_LONGITUDE_1_916 * SIN(#tempRiseMeanRad)
+ #SUN_TRUE_LONGITUDE_0_02 * SIN(#SUN_TRUE_LONGITUDE_2_0
* #tempRiseMeanRad)
+ #SUN_TRUE_LONGITUDE_282_634;
WHILE #tempRiseLongitude > #"360_0_DEG" DO
#tempRiseLongitude := #tempRiseLongitude - #"360_0_DEG";
END_WHILE;
WHILE #tempRiseLongitude < #"0_0_DEG" DO
#tempRiseLongitude := #tempRiseLongitude + #"360_0_DEG";
END_WHILE;
#tempRiseLonRad := #tempRiseLongitude * #DEGREE_TO_RADIAN_0_0174;
// Sun's right ascension
#tempRiseAscensRad := ATAN(#SUN_RIGHT_ASCENSION_0_91764
* TAN(#tempRiseLonRad));
WHILE #tempRiseAscensRad > #SUN_RIGHT_ASCENSION_2_0 * #PI DO
#tempRiseAscensRad := #tempRiseAscensRad - #SUN_RIGHT_ASCENSION_2_0 * #PI;
END_WHILE;
WHILE #tempRiseAscensRad < #"0_0_DEG" DO
#tempRiseAscensRad := #tempRiseAscensRad + #SUN_RIGHT_ASCENSION_2_0 * #PI;
END_WHILE;
#tempRiseAscension := #tempRiseAscensRad * #RADIAN_TO_DEGREE_57_2957;
#tempRiseAscension := (#tempRiseAscension
+ DINT_TO_REAL(FLOOR(#tempRiseLongitude / #"90_0_DEG"))
* #"90_0_DEG"
- DINT_TO_REAL(FLOOR(#tempRiseAscension / #"90_0_DEG"))
* #"90_0_DEG")
/ #"15_0_DEG";
// Sun's declination
#tempSinDeclination := #SUN_DECLINATION_0_39782 * SIN(#tempRiseLonRad);
#tempCosDeclination := COS(ASIN(#tempSinDeclination));
// Sun's local hour angle angle
#tempCosLocHourAngle := LREAL_TO_REAL((COS(#ZENIT_1_58534)
- REAL_TO_LREAL(#tempSinDeclination
* SIN(#tempLatitudeRad)))
/ (#tempCosDeclination * COS(#tempLatitudeRad)));
IF (#tempCosLocHourAngle > #SUM_LOCAL_HOUR_ANGLE)
THEN
#tempCosLocHourAngle := #SUM_LOCAL_HOUR_ANGLE;
END_IF;
IF (#tempCosLocHourAngle < - #SUM_LOCAL_HOUR_ANGLE)
THEN
#tempCosLocHourAngle := - #SUM_LOCAL_HOUR_ANGLE;
END_IF;
// Calculate local hour angle for sunrise
#tempLocHourAngle := #"360_0_DEG" - ACOS(#tempCosLocHourAngle)
* #RADIAN_TO_DEGREE_57_2957;
#tempLocHourAngle := #tempLocHourAngle / #"15_0_DEG";
// Local mean time of rising/setting
#tempLocalMeamTime := #tempLocHourAngle + #tempRiseAscension
- (#LOCAL_MEAN_TIME_0_06571 * #tempAppRiseTime)
- #LOCAL_MEAN_TIME_6_622;
// Adjust back TO UTC
#tempUTC := #tempLocalMeamTime - #templngHour;
IF (#tempUTC > #HOURS_PER_DAY)
THEN
#tempUTC := #tempUTC - #HOURS_PER_DAY;
END_IF;
IF (#tempUTC < 0)
THEN
#tempUTC := #tempUTC + #HOURS_PER_DAY;
END_IF;
#tempLocalTime := #tempUTC - (#tempTimeZone);
IF (#tempLocalTime >= #HOURS_PER_DAY)
THEN
#tempLocalTime := #tempLocalTime - DINT_TO_REAL(TRUNC(#tempLocalTime / #HOURS_PER_DAY) * 24);
END_IF;
IF (#tempLocalTime < 0)
THEN
#tempLocalTime := #tempLocalTime + #HOURS_PER_DAY;
END_IF;
// Convert #tempLocalTime to DTL (#tempIntSunrise)
#tempIntSunrise.YEAR := #tempOfficLocTime.YEAR;
#tempIntSunrise.MONTH := #tempOfficLocTime.MONTH;
#tempIntSunrise.DAY := #tempOfficLocTime.DAY;
#tempIntSunrise := #tempIntSunrise + UDINT_TO_TIME(REAL_TO_UDINT(#tempLocalTime * #MS_PER_HOUR));
#tempIntSunrise.NANOSECOND := 0;
END_REGION // Calculate Sunrise
REGION Calculate Sunset
// Calculate Sunset ===========================================================
// Aproximate time
#tempAppRiseTime := #tempDayOfYear + (#APPROXIMATE_TIME_18 - #templngHour)
/ #HOURS_PER_DAY;
// Sun's mean anomaly
#tempRiseMeanAnomaly := #SUN_MEAN_ANOMALY_0_9856 * #tempAppRiseTime
- #SUN_MEAN_ANOMALY_3_289;
#tempRiseMeanRad := #tempRiseMeanAnomaly * #DEGREE_TO_RADIAN_0_0174;
// Sun's true longitude
#tempRiseLongitude := #tempRiseMeanAnomaly
+ #SUN_TRUE_LONGITUDE_1_916 * SIN(#tempRiseMeanRad)
+ #SUN_TRUE_LONGITUDE_0_02 * SIN(#SUN_TRUE_LONGITUDE_2_0
* #tempRiseMeanRad)
+ #SUN_TRUE_LONGITUDE_282_634;
WHILE #tempRiseLongitude > #"360_0_DEG" DO
#tempRiseLongitude := #tempRiseLongitude - #"360_0_DEG";
END_WHILE;
WHILE #tempRiseLongitude < #"0_0_DEG" DO
#tempRiseLongitude := #tempRiseLongitude + #"360_0_DEG";
END_WHILE;
#tempRiseLonRad := #tempRiseLongitude * #DEGREE_TO_RADIAN_0_0174;
// Sun's right ascension
#tempRiseAscensRad := ATAN(#SUN_RIGHT_ASCENSION_0_91764
* TAN(#tempRiseLonRad));
WHILE #tempRiseAscensRad > #SUN_RIGHT_ASCENSION_2_0 * #PI DO
#tempRiseAscensRad := #tempRiseAscensRad - #SUN_RIGHT_ASCENSION_2_0 * #PI;
END_WHILE;
WHILE #tempRiseAscensRad < #"0_0_DEG" DO
#tempRiseAscensRad := #tempRiseAscensRad + #SUN_RIGHT_ASCENSION_2_0 * #PI;
END_WHILE;
#tempRiseAscension := #tempRiseAscensRad * #RADIAN_TO_DEGREE_57_2957;
#tempRiseAscension := (#tempRiseAscension
+ DINT_TO_REAL(FLOOR(#tempRiseLongitude / #"90_0_DEG"))
* #"90_0_DEG"
- DINT_TO_REAL(FLOOR(#tempRiseAscension / #"90_0_DEG"))
* #"90_0_DEG")
/ #"15_0_DEG";
// Sun's declination
#tempSinDeclination := #SUN_DECLINATION_0_39782 * SIN(#tempRiseLonRad);
#tempCosDeclination := COS(ASIN(#tempSinDeclination));
// Sun's local hour angle
#tempCosLocHourAngle := LREAL_TO_REAL((COS(#ZENIT_1_58534)
- REAL_TO_LREAL(#tempSinDeclination
* SIN(#tempLatitudeRad)))
/ (#tempCosDeclination * COS(#tempLatitudeRad)));
IF (#tempCosLocHourAngle > #SUM_LOCAL_HOUR_ANGLE)
THEN
#tempCosLocHourAngle := #SUM_LOCAL_HOUR_ANGLE;
END_IF;
IF (#tempCosLocHourAngle < - #SUM_LOCAL_HOUR_ANGLE)
THEN
#tempCosLocHourAngle := - #SUM_LOCAL_HOUR_ANGLE;
END_IF;
// Calculation local hour angle for sunset
#tempLocHourAngle := ACOS(#tempCosLocHourAngle) * #RADIAN_TO_DEGREE_57_2957;
#tempLocHourAngle := #tempLocHourAngle / #"15_0_DEG";
// Local mean time of rising/setting
#tempLocalMeamTime := #tempLocHourAngle + #tempRiseAscension
- (#LOCAL_MEAN_TIME_0_06571 * #tempAppRiseTime)
- #LOCAL_MEAN_TIME_6_622;
// Adjust back TO UTC
#tempUTC := #tempLocalMeamTime - #templngHour;
IF (#tempUTC > #HOURS_PER_DAY)
THEN
#tempUTC := #tempUTC - #HOURS_PER_DAY;
END_IF;
IF (#tempUTC < 0)
THEN
#tempUTC := #tempUTC + #HOURS_PER_DAY;
END_IF;
#tempLocalTime := #tempUTC - (#tempTimeZone);
IF (#tempLocalTime >= #HOURS_PER_DAY)
THEN
#tempLocalTime := #tempLocalTime - DINT_TO_REAL(TRUNC(#tempLocalTime / #HOURS_PER_DAY) * 24);
END_IF;
IF (#tempLocalTime < 0)
THEN
#tempLocalTime := #tempLocalTime + #HOURS_PER_DAY;
END_IF;
// Convert #tempLocalTime to DTL (#tempIntSunset)
#tempIntSunset.YEAR := #tempOfficLocTime.YEAR;
#tempIntSunset.MONTH := #tempOfficLocTime.MONTH;
#tempIntSunset.DAY := #tempOfficLocTime.DAY;
#tempIntSunset := #tempIntSunset + UDINT_TO_TIME(REAL_TO_UDINT(#tempLocalTime * #MS_PER_HOUR));
#tempIntSunset.NANOSECOND := 0;
END_REGION // Calculate Sunset
REGION Write output (sunrise, sunset, daytime)
// Considering offset and write output ========================================
// Sunrise
#tempIntSunrise := #tempIntSunrise + #offsetSunrise;
#sunrise := #tempIntSunrise;
// Sunset
#tempIntSunset := #tempIntSunset + #offsetSunset;
#sunset := #tempIntSunset;
// Evaluate Day (between Sunrise and Sunset) ==================================
IF (#tempOfficLocTime >= #tempIntSunrise)
AND (#tempOfficLocTime <= #tempIntSunset)
THEN
#daytime := TRUE;
ELSE
#daytime := FALSE;
END_IF;
END_REGION // Write output
// No errors
#status := #NO_ERROR;
END_FUNCTION_BLOCK