Hallo zusammen,
die o.g. fehler von @n0b0dy90 kommen vermutlich daher, dass Du keine gültige Subscription von OWM hast, das neues Script nutzt die BrightSky API von DWD - da gibt’s die Daten ohne Subscription.
@Moritz_312 ich habe das Script nochmals etwas überarbeitet und angepasst.
Es gibt jetzt die Möglichkeit die Wetterdaten von DWD abzurufen, ohne Abo.
Ich habe das Script noch etwas erweitert:
- Der Temperaturkoeffizient der Module kann angegeben werden (Leistungsreduktion bei Hitze)
- DWD statt OpenWeathermap (Ohne Subscription)
- Beschattung der Anlage durch ein Objekt wird berücksichtigt, wenn notwendig
Hier das neue Script:
<?php
/* Forecast einer PV Analage berechnen
Klasse kann 2 Forecast Provider lesen
1. https://toolkit.solcast.com.au/world-api
2. https://api.forecast.solar/
3. Eigene Berechnung anhand Ausrichtung, Sonnenstand und Bewölkung von https://openweathermap.org/ oder brightsky (dwd-mosmix)
(c) 2022, STELE99
*/
/* Array der PV Anlagen definieren
kwp: Installierte Kilowatt Peak als String mit . für nachkommastellen
azimuth: Ausrichtung gegen Süden 0=Süden, -90=Osten, 90=Westen
tilt: Neigung der Anlage 0=flach, Senkrecht =90
verlust: Faktor um Leistungsverluste etc. zu kompensieren
lon: Längengrad des PV Standortes als String mit . für nachkommastellen
lat: Breitengrad des PV Standortes als String mit . für nachkommastellen
efficiency: Wirkungungsgrad der Anlage (zb. Abfall durch Beschattung etc. ) 0-100
cloudeffect: Auswirkung bei Bewölkung (0-100) Ertragsreduktion in Prozent
horizon: Grad des Horizontes (Bei Bergen wg. Sonnenaufgang)
tempkoeff: Temperaturkoeffizient der PV Module
obj_direction: Beschattung durch ein Objekt, Himmelsrichtung des Objektes in Grad
obj_height: Höhe des Objektes in meter
obj_size: Größe des Objektes (Abdeckung prozentualer Anteil des Schattens auf die PV Anlage)
obj_effect: Auswirkung auf die Reduktion des Ertrages
*/
$PVA["Dach"] = [ "kwp" => "6.7",
"azimuth" => 60,
"tilt" => 15,
"efficiency" => 90,
"pvtype" => "N", // N=Normale Dachfläche D= PV in Dachform geständert, Ausrichtung der offenen seite = azimuth
"lon" => "6.829772606",
"lat" => "45.524873309",
"tempkoeff" => 0.34,
"cloudeffect" => 90, // Effekt für Leistungsreduktion bei Bewölkung
"horizon" => 15, // Horizont für Einfallswinkel Sonne (Horizont ca. 5 Grad, dach jedoch 15 etwas gedreht weniger)
];
$PVA["Garage"] = [ "kwp" => "8.0",
"azimuth" => -50, // eigentlich sind es 30 aber soll ist vergleich zeigt leichte verschiebung vom abend - also mehr nach rechts drehen
"tilt" => 10,
"pvtype" => "D", // D= Dachform geständert, Ausrichtung der offenen seite = azimuth
"efficiency" => 105,
"cloudeffect" => 60, // Effekt für Leistungsreduktion bei Bewölkung in Prozent
"lon" => "10.29772606",
"lat" => "4.024873309",
"tempkoeff" => 0.38, // Temperaturkoeffizient lt. Datenblatt
"horizon" => 8, // Horizont für Einfallswinkel Sonne
// Beschattungsobjekt
"obj_direction" => 58.4, // Himmelsrichtung des Beschattungsobjektes Grad von Norden =0
"obj_distance" => 8, // Abstand des Objektes in Meter
"obj_height" => 4, // Höhe des Objektes in Meter
"obj_size" => 50, // Prozentualer Abdeckungsanteil der PV Anlage durch Objekt (Mittelwert)
"obj_effect" => 0.65 // Azuswirkung der Beschattung auf Ertrag (kwp mit gemessenem Ertrag bei Beschattung (grob))
];
/* Wie wird der ForeCast berechnet
1 = Solcast
2 = forecast.solar
3 = eigen
*/
$CALC_METHOD = 3;
/* Solcast API-KEY für Forecast Variante 1*/
$SO_APIKEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
/* Wetterdienst für Vorhersage der Bewölkung für ForeCast Version 3 (eigen) */
$WEATHER = "dwd"; // dwd (Deutscher Wetterdienst Mosmix über brightsky)
#$WEATHER = "owm"; // owm (Open Weather Map - bitte unten APPID angeben für API Aufruf)
/* Openweathermap () für Forecast eigene Berechnung Variante 3 APPID - API KEY */
$OW_APPID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
/* Verlgeich der Vorhersagemodelle in HTML Tabelle (Ausgabeüber String Variable) */
$HTML_COMPARE = true;
/* Alle Forecast Daten als JSON String in Variable speichern */
$SAVE_TO_VAR = true;
/* Wie stark hat Bewölkung Auswirkung auf Leistung (Faktor (linear)) Leistungsreduktion = Bewölkung * Faktor*/
$V3_CLOUD_FAKTOR = 0.55;
/*------------------------------- PROGRAM CODE, NO CHANGE HERE --------------------------------------------*/
// Falls noch nicht existend, Profil anlegen für kwh
$profname = "kWh";
if(!IPS_VariableProfileExists($profname)) {
// kWh Profil anlegen
$id = IPS_CreateVariableProfile ($profname, 2);
IPS_SetVariableProfileIcon($profname, "EnergyStorage");
IPS_SetVariableProfileText($profname, "", "kWh");
};
// Anlagen auswerten und je Anlage Forecast ermiteln und in einer Kategorie speichern:
foreach($PVA as $Aname => $A){
$catID = @IPS_GetCategoryIDByName ($Aname, $_IPS['SELF']);
// Kategroie pro PV Anlage erstellen
if(empty($catID)){
$catID = IPS_CreateCategory();
IPS_SetName($catID, $Aname); // Kategorie benennen
IPS_SetParent($catID, $_IPS['SELF']); // Kategorie einsortieren unter dem Objekt mit der ID "12345"
}
$pv = new SEPVForecast($A);
switch($CALC_METHOD){
case 1: $fc = $pv->getForecast($A);
break;
case 2: $fc = $pv->getForecast2($A);
break;
case 3: $fc = $pv->getForecast3($A);
break;
}
// Werte in Variablen schreiben ==========================================
// Wenn Job vor 8h dann wird der aktuelle Tag noch ausgewertet
$idVar1 = CreateVariableByName($catID,"PV heute 0-12h", 2, "kWh");
$idVar2 = CreateVariableByName($catID,"PV heute Gesamt", 2, "kWh");
if(date('H') < 9 ){
SetValue($idVar1, $fc[0]["morgens"]);
SetValue($idVar2, $fc[0]["gesamt"]);
}
$idVar = CreateVariableByName($catID,"PV heute 12-24h", 2, "kWh");
if(date('H')<13){
SetValue($idVar, $fc[0]["mittags"]);
}
// Werte für morgen.
$idVar = CreateVariableByName($catID,"PV morgen Gesamt", 2, "kWh");
SetValue($idVar, $fc[1]["gesamt"]);
$idVar = CreateVariableByName($catID,"PV morgen 8-12h", 2, "kWh");
SetValue($idVar, $fc[1]["morgens"]);
$idVar = CreateVariableByName($catID,"PV morgen 12-21h", 2, "kWh");
SetValue($idVar, $fc[1]["mittags"]);
// Ausgabe Vergleichstabelle der unterschiedlichen Varianten
if($HTML_COMPARE == true){
$fcA[1] = $pv->getForecast($A);
$fcA[2] = $pv->getForecast2($A);
$fcA[3] = $pv->getForecast3($A);
// Charts aufbauen und in eigene Variable ausgeben.
for($day = 0; $day < 2; $day++){
$str = "";
$data = "";
$chartUID = strtolower($Aname.$day);
$daytxt = ($day==0 )? "heute" : "morgen";
for($h = 4; $h < 23; $h++){
$e = @$fcA[1][$day][$h];
$hour = $h;
if(!empty($hour))$data .= "['$hour', ".str_replace(",", ".", floatval(@$e["pv_estimate"])).", ".str_replace(",", ".", floatval(@$fcA[2][$day][$hour]["pv_estimate"])).", ".str_replace(",", ".", floatval(@$fcA[3][$day][$hour]["pv_estimate"])).", ".@intval($fcA[3][$day][$hour]["clouds"])."],\n";
}
$data = substr($data, 0, strlen($data)-2);
$str .="<div id='sechart_$chartUID'>
<div id='chart".$chartUID."' style='width: 100%; height: 400px'></div>
<script type='text/javascript' src='https://www.gstatic.com/charts/loader.js'></script>
<script type='text/javascript'>
google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(drawChart".$chartUID.");
function drawChart".$chartUID."() {
var data".$chartUID." = google.visualization.arrayToDataTable([
['Uhrzeit', 'solcast ".round($fcA[1][$day]["gesamt"],0)."kWh', 'forecast.solar ".round($fcA[2][$day]["gesamt"],0)."kWh', 'eigen ".round($fcA[3][$day]["gesamt"],0)."kWh', 'Bewölkung'],
$data
]);
var options = {
title: 'PV Vorhersagemodelle für $Aname, $daytxt',
titleTextStyle: {color: '#FFFFFF'},
backgroundColor: '#1F3041',
colors: ['#F2F3AE', '#FC9E4F', '#F4442E', '#FFFFFF'],
hAxis: { gridlines: {color: '#576471'},
textStyle: {color: '#FFF'},
baselineColor:{color: '#FFF'},
title: 'Uhrzeit' ,
titleTextStyle:{color: '#FFF'},
},
vAxes: { 0:{
gridlines: {color: '#576471', count: 5},
minorGridlines: { color: 'transparent', count: 0 },
textStyle:{color: '#FFF'},
baselineColor:{color: '#FFF'},
title: 'kWh',
titleTextStyle:{color: '#FFF'},
viewWindowMode:'maximized',
viewWindow: { min: 0}
},
3:{
gridlines: {color: 'transparent', count: 0},
minorGridlines: { count: 0 },
textStyle:{color: '#FFF'},
baselineColor:{color: '#FFF'},
title: 'Bewölkung',
titleTextStyle:{color: '#FFF'},
viewWindowMode:'maximized',
viewWindow: {max: 100, min: 0}
} },
tooltip: {isHtml: true},
series:{ 0:{targetAxisIndex:0, type: 'line'},
1:{targetAxisIndex:0, type: 'line'},
2:{targetAxisIndex:0, type: 'line', lineWidth: 3},
3:{targetAxisIndex:3, type:'area', areaOpacity:0.25, lineWidth:0 }},
legend: {textStyle: {color: 'white'}, position: 'bottom'},
curveType: '' // oder function
};
var chart = new google.visualization.AreaChart(document.getElementById('chart".$chartUID."'));
chart.draw(data".$chartUID.", options);
}
</script>
</div>";
$idVar = CreateVariableByName($catID,"Forecast-Chart $Aname $daytxt", 3, "~HTMLBox");
if((date("H") < 11 && $day == 0 ) || $day != 0){
SetValue($idVar, $str);
}
}
}
if($SAVE_TO_VAR){
$str = json_encode($fc);
$idVar = CreateVariableByName($catID,"ForecastJSON", 3, "");
SetValue($idVar, $str);
}
}
class SEPVForecast {
private $w_cache = 3600; // Weather Cache
private $so_cache = 3600 * 2;
private $fc_cache = 3600 * 2;
public $fc;
/* ForeCast PV with SolCast ========================================================================= */
function getForecast($A){
global $SO_APIKEY;
global $SO_UTC;
$lon = $A["lon"];
$lat = $A["lat"];
$kwp = $A["kwp"];
if($A["azimuth"] > 0){
$azimuth = 180 - $A["azimuth"];
}elseif($A["azimuth"] < 0){
$azimuth = -180 - $A["azimuth"];
}else{
$azimuth = 0;
}
$url = "https://api.solcast.com.au/world_pv_power/forecasts?latitude=$lat&longitude=$lon&capacity=$kwp&tilt=".$A["tilt"]."&azimuth=$azimuth&hours=48&format=json&api_key=$SO_APIKEY";
$fcache = $_IPS['SELF']."_so_".md5(serialize($A)).".cache";
if(time() - @filemtime($fcache) > $this->so_cache){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($ch);
if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
}
curl_close($ch);
file_put_contents($fcache, $result);
}
$sodata = json_decode(file_get_contents($fcache));
$fc = [];
$hcount =0;
$hour_old="";
foreach($sodata->forecasts as $o){
$hour = date("G",strtotime($o->period_end));
$day = date("z",strtotime($o->period_end)) - date("z");
$fc[$day][$hour] = [
"hour" => $hour,
"pv_estimate" => $o->pv_estimate,
];
$hour_old = $hour;
}
//Morgen und Mittagswerte
foreach($fc as $d => $day){
$morgens = 0;
$mittags = 0;
foreach($day as $hour){
if($hour["hour"] <13 ){
$morgens += $hour["pv_estimate"];
}else{
$mittags += $hour["pv_estimate"];
}
}
$fc[$d]["morgens"] = $morgens * $A["efficiency"] / 100;
$fc[$d]["mittags"] = $mittags * $A["efficiency"] / 100;
$fc[$d]["gesamt"] = ($morgens + $mittags) * $A["efficiency"] / 100;
}
$this->so_fc = $fc;
return $fc;
}
/* ForeCast PV with forecast.solar ========================================================================= */
function getForecast2($A){
$lon = $A["lon"];
$lat = $A["lat"];
$kwp = $A["kwp"];
$url = "https://api.forecast.solar/estimate/$lat/$lon/".$A["tilt"]."/".$A["azimuth"]."/$kwp";
$fcache = $_IPS['SELF']."_fc_".md5(serialize($A)).".cache";
if(time() - @filemtime($fcache) > $this->fc_cache ){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($ch);
if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
}
curl_close($ch);
file_put_contents($fcache, $result);
}
$data = json_decode(file_get_contents($fcache));
$fc = [];
foreach($data->result->watts as $t => $watts){
$hour = date("G",strtotime($t));
$day = date("z",strtotime($t)) - date("z");
$fc[$day][$hour] = [
"hour" => $hour,
"pv_estimate" => @$fc[$day][$hour]["pv_estimate"] + ($watts / 1000),
];
}
//Morgen und Mittagswerte
foreach($fc as $d => $day){
$morgens = 0;
$mittags = 0;
foreach($day as $hour){
if($hour["hour"] <13 ){
$morgens += $hour["pv_estimate"];
}else{
$mittags += $hour["pv_estimate"];
}
}
$fc[$d]["morgens"] = $morgens * $A["efficiency"] / 100;
$fc[$d]["mittags"] = $mittags * $A["efficiency"] / 100;
$fc[$d]["gesamt"] = ($morgens + $mittags) * $A["efficiency"] / 100;
}
$this->fc = $fc;
return $fc;
}
/* ForeCast PV eigene Ermittlung====================================================================== */
function getForecast3($A){
global $OW_APPID;
global $V3_CLOUD_FAKTOR;
global $WEATHER;
$lon = $A["lon"];
$lat = $A["lat"];
$kwp = $A["kwp"];
// Wetterdaten lesen und cachen
switch($WEATHER){
case "owm":
$url = "https://api.openweathermap.org/data/2.5/onecall?lat=$lat&lon=$lon&exclude=minutely,alerts,current&units=metric&lang=de_de&appid=$OW_APPID";
break;
default:
$url = "https://api.brightsky.dev/weather?date=".date("Y-m-d")."&last_date=".date("Y-m-d", time()+3600*24*3)."&lat=$lat&lon=$lon";
}
$fcache = $_IPS['SELF']."_w".md5($WEATHER.$A["lon"].$A["lat"]).".cache";
if(time() - @filemtime($fcache) > $this->w_cache){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($ch);
if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
}
curl_close($ch);
file_put_contents($fcache, $result);
}
$data = json_decode(file_get_contents($fcache), true);
// Wetterservice Datena aufbereiten je nach Service
switch($WEATHER){
case "owm": // OpenWeather
foreach($data["hourly"] as $w){
$ts = $w["dt"];
$day = date("z", $ts) - date("z");
$hour = date("G", $ts);
$fcE["ts"] = $ts;
$fcE["dateiso"] = date("Y-m-d, H:i:s", $ts);
$fcE["day"] = $day;
$fcE["hour"] = $hour;
$fcE["clouds"] = $w["clouds"];
$fcE["temperature"] = $w["temp"];
$fcE["visibility"] = $w["visibility"];
$fcE["precipitation"] = @$w["rain"] + @$w["snow"];
$fcW[] = $fcE;
}
break;
default: // dwd-mosmix
foreach($data["weather"] as $w){
$ts = strtotime($w["timestamp"]);
$day = date("z", $ts) - date("z");
$hour = date("G", $ts);
$fcE["ts"] = $ts;
$fcE["dateiso"] = date("Y-m-d, H:i:s", $ts);
$fcE["day"] = $day;
$fcE["hour"] = $hour;
$fcE["clouds"] = $w["cloud_cover"];
$fcE["temperature"] = $w["temperature"];
$fcE["visibility"] = $w["visibility"];
$fcE["precipitation"] = $w["precipitation"];
$fcW[] = $fcE;
}
break;
}
for($cnt=0; $cnt <= 48; $cnt++){
$fcE = $fcW[$cnt];
$day = $fcE["day"];
$hour = $fcE["hour"];
$clouds = $fcE["clouds"];
$temp = $fcE["temperature"];
#print_r($fcE);
$lf = 100;
$ret = $this->calcSun($fcE["ts"], $lon, $lat);
$sun_azimuth = $ret["azimuth"];
$sun_elevation = $ret["elevation"];
/* --------- Kalkulation der Beschattung egal wie die Anlage ausgerichtet ist. */
if(isset($A["obj_direction"])){
// 2x berechnen und mittelwert bilden für volle stunde und 30min
for($x=0; $x <=1; $x++){
$time = $fcE["ts"] + $x * 1800;
$sunpos = $this->calcSun($time, $lon, $lat);
$alpha = deg2rad($sunpos["elevation"]);
$azimuth = $sunpos["azimuth"];
$shadeAngle = $azimuth - $A["obj_direction"] - 90;
$shadeLength = round($A["obj_height"] / (tan($alpha) ),2);
if($shadeLength < $A["obj_distance"] || $shadeAngle >0){
$lf_minusShadeA[$x] = 100;
}else{
$lf_minusShadeA[$x] = $lf*($A["obj_size"]/100)*$A["obj_effect"] + $lf*( 1 - ($A["obj_size"]/100));
}
}
$lf_minusShade = ($lf_minusShadeA[0] + $lf_minusShadeA[1]) / 2;
$lf = $lf_minusShade;
}
/* ------------ Kalkulation des Ertrages
Daten von https://echtsolar.de/wp-content/uploads/2021/02/Ausrichtungstabelle-Photovoltaik-in-Deutschland-768x705.png
Formel über Newton Polynom: https://de.planetcalc.com/9023/
*/
if($A["pvtype"] == "D"){
// Dachständerung /\/\/\
/* 1/2 der Leistung muss um 90 grad nach ost und 90 grad nach west gedreht werden
*/
$lfA = $lf_minusShade;
$lfB = $lf_minusShade;
$az_abwA = round(abs($sun_azimuth - 180 + 90 - $A["azimuth"]),1);
$az_abwB = round(abs($sun_azimuth - 180 - 90 - $A["azimuth"]),1);
#$lf_minusA = round(0.002019* pow( $az_abw, 2) -0.02878 * $az_abw + 0.5471, 1); // Formel via funktion aus 3-4 punkten
if($az_abwA > 180 ){
$lf_minusAA = 90;
}else{
#$lf_minusAA = (-0.00000000147) * pow( $az_abwA, 5) + (0.00000057668) * pow ($az_abwA, 4) + (-0.00008107497) * pow ($az_abwA, 3) + (0.00559429998 * pow ($az_abwA, 2)) + (0.05160253657 * $az_abwA);
$lf_minusAA = sin( (1/58) * $az_abwA + 155.5) * 26 + 26;
}
if($az_abwB > 180 ){
$lf_minusBA = 90;
}else{
#$lf_minusBA = (-0.00000000147) * pow( $az_abwB, 5) + (0.00000057668) * pow ($az_abwB, 4) + (-0.00008107497) * pow ($az_abwB, 3) + (0.00559429998 * pow ($az_abwB, 2)) + (0.05160253657 * $az_abwB);
$lf_minusBA = sin( (1/58) * $az_abwB + 155.5) * 26 + 26; // Mit Daten aus Tabelle und näherung über probieren der Sinus-Funktion
}
#echo "$hour h: $sun_azimuth : $az_abw -> $lf_minusA\n";
$lfA = $lfA - $lf_minusAA * 0.90; // fällt schwächer ins gewicht
$lfB = $lfB - $lf_minusBA * 0.90; // fällt schwächer ins gewicht
#echo "Azimuth $lf_minusA >> $lf \n";
// Neigungswikel und Sonne
$elev_abw = 90 - $sun_elevation - $A["tilt"];
#echo "h: $hour - $elev_abw\n";
$x = $elev_abw;
#$lf_minusE = round( 0.0000474 * pow( $elev_abw, 3) + 0.002798 * pow( $elev_abw, 2) + 0.2894 * $elev_abw - 2.453, 1);
#$lf_minusE = (-0.00000000184772 * pow($x, 6)) + (0.00000055691964 * pow($x, 5)) + (-0.00006230158730 * pow($x, 4)) + (0.00319568452381 * pow($x, 3)) + (-0.06371230158730 * pow($x, 2)) + (-0.13880952380952 * $x) + 14;
$lf_minusE = (1/100) * pow ($x, 2); // Neue Formel anhand Tabellen und Werten
if($sun_elevation < @$A["horizon"] + 5) $lf_minusE = 50; // Sonnenuntergang
if($sun_elevation < @$A["horizon"] ) $lf_minusE = 100; // Sonnenuntergang
$lf_minusE = round($lf_minusE,1);
$lfA = $lfA - $lf_minusE * 0.9; // fällt schwächer ins gewicht;
$lfB = $lfB - $lf_minusE * 0.9; // fällt schwächer ins gewicht;
// Bewölkung hängt von der Wolkendicke und von dem Einstrahlungswinkel ab.
$lf_minusC = (pow($clouds,2.4)/600) * $A["cloudeffect"]/100;
#$lf_minusC = 100;
$lfA = $lfA - $lf_minusC;
$lfB = $lfB - $lf_minusC;
$lfA = ($lfA< 0 )? 0 : $lfA;
$lfB = ($lfB< 0 )? 0 : $lfB;
$pv_estimate = ( $A["kwp"] / 2) * ($lfA / 100) * $A["efficiency"]/100;
$pv_estimate += ( $A["kwp"] / 2) * ($lfB / 100) * $A["efficiency"]/100;
#echo "$hour: $pv_estimate (Schatten: $lf_minusShade, Azimut $lf_minusAA - $lf_minusBA, Elv: $lf_minusE, Cloud: $lf_minusC)\n";
}else{
$az_abw = round(abs($sun_azimuth - 180 - $A["azimuth"]),1);
#$lf_minusA = round(0.002019* pow( $az_abw, 2) -0.02878 * $az_abw + 0.5471, 1); // Formel via funktion aus 3-4 punkten
if($az_abw > 180){
$lf_minusA = 100;
}else{
$lf_minusA = (-0.00000000147) * pow( $az_abw, 5) + (0.00000057668) * pow ($az_abw, 4) + (-0.00008107497) * pow ($az_abw, 3) + (0.00559429998 * pow ($az_abw, 2)) + (0.05160253657 * $az_abw);
}
#echo "$hour h: $sun_azimuth : $az_abw -> $lf_minusA\n";
$lf = $lf - $lf_minusA;
#echo "Azimuth $lf_minusA >> $lf \n";
// Neigungswikel und Sonne
$elev_abw = 90 - $sun_elevation - $A["tilt"];
$x = $elev_abw;
#$lf_minusE = round( 0.0000474 * pow( $elev_abw, 3) + 0.002798 * pow( $elev_abw, 2) + 0.2894 * $elev_abw - 2.453, 1);
#$lf_minusE = (-0.00000000184772 * pow($x, 6)) + (0.00000055691964 * pow($x, 5)) + (-0.00006230158730 * pow($x, 4)) + (0.00319568452381 * pow($x, 3)) + (-0.06371230158730 * pow($x, 2)) + (-0.13880952380952 * $x) + 14;
$lf_minusE = (1/100) * pow ($x, 2); // Neue Formel anhand Tabellen und Werten
if($sun_elevation < @$A["horizon"] + 5) $lf_minusE = 50; // Sonnenuntergang
if($sun_elevation < @$A["horizon"] ) $lf_minusE = 100; // Sonnenuntergang
$lf_minusE = round($lf_minusE,1);
$lf = $lf - $lf_minusE;
// Bewölkung hängt von der Wolkendicke und von dem Einstrahlungswinkel ab.
$lf_minusC = (pow($clouds,2.4)/600) * $A["cloudeffect"]/100;
#echo $clouds.": " .$lf_minusC."\n";
$lf = $lf - $lf_minusC;
#echo "Clouds $clouds >> $lf \n";
$lf = ($lf < 0 )? 0 : $lf;
$pv_estimate = $A["kwp"] * ($lf / 100) * $A["efficiency"]/100;
}
// Temperaturverlust
#$temp = 0;
if($temp > 18){
$tempDelta = $temp - 18;
// Annahme MaxTemp = 50 bei 5 Grad Unterschied und keine Wolken
$tempMinus = $A["tempkoeff"] * (( (2.917 * $temp) - 27.5) - 25 ) * (100 - $clouds) / 100;
#echo "$hour: $tempMinus\n";
$pv_estimate = $pv_estimate * (100-$tempMinus)/100;
}
$pv_estimate = round($pv_estimate,1);
# echo "$hour: A: $sun_azimuth -$lf_minusA, E: $sun_elevation (Abw: $elev_abw) -$lf_minusE, Clouds: $clouds -$lf_minusC, PVE: $pv_estimate\n";
$fc[$day][$hour] = [
"ts" => $fcE["ts"],
"day" => $day,
"hour" => $hour,
"clouds" => $clouds,
"temperature" => $fcE["temperature"],
"precipitation" => $fcE["precipitation"],
"visibility" => $fcE["visibility"],
"pv_estimate" => $pv_estimate,
];
}
#print_r($fc);
//Morgen und Mittagswerte
foreach($fc as $d => $day){
$morgens = 0;
$mittags = 0;
foreach($day as $hour){
if($hour["hour"] <13 ){
$morgens += $hour["pv_estimate"];
}else{
$mittags += $hour["pv_estimate"];
}
}
$fc[$d]["morgens"] = $morgens * $A["efficiency"] / 100;
$fc[$d]["mittags"] = $mittags * $A["efficiency"] / 100;
$fc[$d]["gesamt"] = ($morgens + $mittags) * $A["efficiency"] / 100;
}
$this->fc = $fc;
return $fc;
}
private function calcSun($ts, $dLongitude, $dLatitude){
// Correction Timezone
$ts = $ts - 2*3600;
$iYear = date("Y", $ts);
$iMonth = date("m", $ts);
$iDay = date("d", $ts);
$dHours = date("H", $ts);
$dMinutes = date("i", $ts);
$dSeconds = date("s", $ts);
$pi = 3.14159265358979323846;
$twopi = (2*$pi);
$rad = ($pi/180);
$dEarthMeanRadius = 6371.01; // In km
$dAstronomicalUnit = 149597890; // In km
// Calculate difference in days between the current Julian Day
// and JD 2451545.0, which is noon 1 January 2000 Universal Time
// Calculate time of the day in UT decimal hours
$dDecimalHours = floatval($dHours) + (floatval($dMinutes) + floatval($dSeconds) / 60.0 ) / 60.0;
// Calculate current Julian Day
$iYfrom2000 = $iYear;//expects now as YY ;
$iA= (14 - ($iMonth)) / 12;
$iM= ($iMonth) + 12 * $iA -3;
$liAux3=(153 * $iM + 2)/5;
$liAux4= 365 * ($iYfrom2000 - $iA);
$liAux5= ( $iYfrom2000 - $iA)/4;
$dElapsedJulianDays= floatval(($iDay + $liAux3 + $liAux4 + $liAux5 + 59)+ -0.5 + $dDecimalHours/24.0);
// Calculate ecliptic coordinates (ecliptic longitude and obliquity of the
// ecliptic in radians but without limiting the angle to be less than 2*Pi
// (i.e., the result may be greater than 2*Pi)
$dOmega= 2.1429 - 0.0010394594 * $dElapsedJulianDays;
$dMeanLongitude = 4.8950630 + 0.017202791698 * $dElapsedJulianDays; // Radians
$dMeanAnomaly = 6.2400600 + 0.0172019699 * $dElapsedJulianDays;
$dEclipticLongitude = $dMeanLongitude + 0.03341607 * sin( $dMeanAnomaly ) + 0.00034894 * sin( 2 * $dMeanAnomaly ) -0.0001134 -0.0000203 * sin($dOmega);
$dEclipticObliquity = 0.4090928 - 6.2140e-9 * $dElapsedJulianDays +0.0000396 * cos($dOmega);
// Calculate celestial coordinates ( right ascension and declination ) in radians
// but without limiting the angle to be less than 2*Pi (i.e., the result may be
// greater than 2*Pi)
$dSin_EclipticLongitude = sin( $dEclipticLongitude );
$dY1 = cos( $dEclipticObliquity ) * $dSin_EclipticLongitude;
$dX1 = cos( $dEclipticLongitude );
$dRightAscension = atan2( $dY1,$dX1 );
if( $dRightAscension < 0.0 ) $dRightAscension = $dRightAscension + $twopi;
$dDeclination = asin( sin( $dEclipticObliquity )* $dSin_EclipticLongitude );
// Calculate local coordinates ( azimuth and zenith angle ) in degrees
$dGreenwichMeanSiderealTime = 6.6974243242 + 0.0657098283 * $dElapsedJulianDays + $dDecimalHours;
$dLocalMeanSiderealTime = ($dGreenwichMeanSiderealTime*15 + $dLongitude)* $rad;
$dHourAngle = $dLocalMeanSiderealTime - $dRightAscension;
$dLatitudeInRadians = $dLatitude * $rad;
$dCos_Latitude = cos( $dLatitudeInRadians );
$dSin_Latitude = sin( $dLatitudeInRadians );
$dCos_HourAngle= cos( $dHourAngle );
$dZenithAngle = (acos( $dCos_Latitude * $dCos_HourAngle * cos($dDeclination) + sin( $dDeclination )* $dSin_Latitude));
$dY = -sin( $dHourAngle );
$dX = tan( $dDeclination )* $dCos_Latitude - $dSin_Latitude * $dCos_HourAngle;
$dAzimuth = atan2( $dY, $dX );
if ( $dAzimuth < 0.0 )
$dAzimuth = $dAzimuth + $twopi;
$dAzimuth = $dAzimuth / $rad;
// Parallax Correction
$dParallax = ($dEarthMeanRadius / $dAstronomicalUnit) * sin( $dZenithAngle);
$dZenithAngle = ($dZenithAngle + $dParallax) / $rad;
$dElevation = 90 - $dZenithAngle;
return Array("azimuth" => $dAzimuth, "elevation" => $dElevation);
}
}
// Helper Funktion
function CreateVariableByName($id, $name, $type, $profile = "")
{
# type: 0=boolean, 1 = integer, 2 = float, 3 = string;
global $_IPS;
$vid = @IPS_GetVariableIDByName($name, $id);
if($vid === false)
{
$vid = IPS_CreateVariable($type);
IPS_SetParent($vid, $id);
IPS_SetName($vid, $name);
IPS_SetInfo($vid, "this variable was created by script #".$_IPS['SELF']);
if($profile !== "") { IPS_SetVariableCustomProfile($vid, $profile); }
}
return $vid;
}