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