2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 06:39:11 +00:00

76_SolarForecast: take into account the position of the sun in addition to cloud cover when determining the correction factor

git-svn-id: https://svn.fhem.de/fhem/trunk@28553 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
nasseeder1 2024-02-25 20:26:03 +00:00
parent 914598bf7e
commit bf5c9aee7d
2 changed files with 552 additions and 454 deletions

View File

@ -1,5 +1,8 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
- feature: 76_SolarForecast: take into account the position of the sun in
addition to cloud cover when determining the
correction factor
- change: 76_SolarForecast: check age of Rad1h data, use RR1c instead of R101
- feature: 70_PylonLowVoltage: print out faulty response with verbose 5
- bugfix: 88_HMCCU: fixed device detection bug

View File

@ -158,6 +158,11 @@ BEGIN {
# Versions History intern
my %vNotesIntern = (
"1.16.3" => "24.02.2024 store pvcorrf, quality, pvrlsum, pvfcsum, dnumsum with value <sunalt2bin>.<cloud2bin> in pvCircular ".
"get pvcorrf / quality from neff in combination with sun altitude (CircularSunCloudkorrVal) ".
"delete CircularCloudkorrVal, show sun position in beamgrafic weather mouse over ".
"split pvCorrection into pvCorrectionRead and pvCorrectionWrite ".
"_checkSetupNotComplete: improve setup Wizzard for ForecastSolar-API ",
"1.16.2" => "22.02.2024 minor changes, R101 -> RR1c, totalrain instead of weatherrainprob, delete wrp r101 ".
"delete wrp from circular & airaw, remove rain2bin, __getDWDSolarData: change \$runh, ".
"fix Illegal division by zero Forum: https://forum.fhem.de/index.php?msg=1304009 ".
@ -339,10 +344,10 @@ my $dwdcatgpx = $root."/FHEM/FhemUtils/DWDcat_SolarForecast.gpx";
my $aitrblto = 7200; # KI Training BlockingCall Timeout
my $aibcthhld = 0.2; # Schwelle der KI Trainigszeit ab der BlockingCall benutzt wird
my $aistdudef = 1095; # default Haltezeit KI Raw Daten (Tage)
my $aiSpreadUpLim = 150; # obere Abweichungsgrenze (%) AI 'Spread' von API Prognose
my $aiSpreadLowLim = 50; # untere Abweichungsgrenze (%) AI 'Spread' von API Prognose
my $aiAccUpLim = 170; # obere Abweichungsgrenze (%) AI 'Accurate' von API Prognose
my $aiAccLowLim = 30; # untere Abweichungsgrenze (%) AI 'Accurate' von API Prognose
my $aiSpreadUpLim = 130; # obere Abweichungsgrenze (%) AI 'Spread' von API Prognose
my $aiSpreadLowLim = 70; # untere Abweichungsgrenze (%) AI 'Spread' von API Prognose
my $aiAccUpLim = 150; # obere Abweichungsgrenze (%) AI 'Accurate' von API Prognose
my $aiAccLowLim = 50; # untere Abweichungsgrenze (%) AI 'Accurate' von API Prognose
my $calcmaxd = 30; # Anzahl Tage die zur Berechnung Vorhersagekorrektur verwendet werden
my @dweattrmust = qw(TTT Neff RR1c ww SunUp SunRise SunSet); # Werte die im Attr forecastProperties des Weather-DWD_Opendata Devices mindestens gesetzt sein müssen
@ -419,7 +424,8 @@ my @dd = qw( none
epiecesCalc
graphic
notifyHandling
pvCorrection
pvCorrectionRead
pvCorrectionWrite
radiationProcess
saveData2Cache
);
@ -694,8 +700,8 @@ my %htitles = (
DE => qq{Ein -> kein off-Kommando definiert!} },
natc => { EN => qq{automatic cycle:},
DE => qq{automatischer Zyklus:} },
predtime => { EN => qq{Prediction time Weather data:},
DE => qq{Vorhersagezeitpunkt Wetterdaten:} },
predtime => { EN => qq{Prediction time Radiation data:},
DE => qq{Vorhersagezeitpunkt Strahlungsdaten:} },
upd => { EN => qq{Click for update},
DE => qq{Klick f&#252;r Update} },
on => { EN => qq{switched on},
@ -706,6 +712,12 @@ my %htitles = (
DE => qq{undefiniert} },
dela => { EN => qq{delayed},
DE => qq{verzoegert} },
azimuth => { EN => qq{Azimuth},
DE => qq{Azimut} },
elevatio => { EN => qq{Elevation},
DE => qq{H&#246;he} },
sunpos => { EN => qq{Sun position (decimal degrees)},
DE => qq{Sonnenstand (Dezimalgrad)} },
conrec => { EN => qq{Current time is within the consumption planning},
DE => qq{Aktuelle Zeit liegt innerhalb der Verbrauchsplanung} },
conrecba => { EN => qq{Current time is within the consumption planning, Priority charging Battery is active},
@ -1439,6 +1451,13 @@ sub _setcurrentRadiationAPI { ## no critic "not used"
return "You have to install the required perl module: ".$rmf if($rmf);
}
readingsSingleUpdate ($hash, "currentRadiationAPI", $prop, 1);
createAssociatedWith ($hash);
writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben
setModel ($hash); # Model setzen
return if(_checkSetupNotComplete ($hash)); # keine Stringkonfiguration wenn Setup noch nicht komplett
if ($prop eq 'ForecastSolar-API') {
my ($set, $lat, $lon) = locCoordinates();
return qq{set attributes 'latitude' and 'longitude' in global device first} if(!$set);
@ -1450,15 +1469,6 @@ sub _setcurrentRadiationAPI { ## no critic "not used"
return qq{Please complete command "set $name moduleAzimuth".} if(!$dir);
}
if ($prop eq 'VictronVRM-API') {
}
readingsSingleUpdate ($hash, "currentRadiationAPI", $prop, 1);
createAssociatedWith ($hash);
writeCacheToFile ($hash, "plantconfig", $plantcfg.$name); # Anlagenkonfiguration File schreiben
setModel ($hash); # Model setzen
my $type = $hash->{TYPE};
$data{$type}{$name}{current}{allStringsFullfilled} = 0; # Stringkonfiguration neu prüfen lassen
@ -4691,7 +4701,7 @@ sub Attr {
if ($cmd eq 'set') {
if ($aName eq 'ctrlInterval' || $aName eq 'ctrlBackupFilesKeep' || $aName eq 'ctrlAIdataStorageDuration') {
unless ($aVal =~ /^[0-9]+$/x) {
return qq{The value for $aName is not valid. Use only figures 0-9 !};
return qq{Invalid value for $aName. Use only figures 0-9!};
}
}
@ -5681,15 +5691,94 @@ sub centralTask {
}
## nicht-Bin Werte löschen / wrp löschen
my $ra = '0|00|05|5|10|15|20|25|30|35|40|45|50|55|60|65|70|75|80|85|90|95|100|2.{2}|percentile|simple';
my $ra = '0|00|05|5|10|15|20|25|30|35|40|45|50|55|60|65|70|75|80|85|90|95|100|.*\..*|simple';
for my $hod (keys %{$data{$type}{$name}{circular}}) { # 30.01.2024
for my $range (keys %{$data{$type}{$name}{circular}{$hod}{pvcorrf}}) {
delete $data{$type}{$name}{circular}{$hod}{pvcorrf}{$range} if($range !~ /^($ra)$/xs);
if($range !~ /simple|\./xs) { # 24.02.2024
$data{$type}{$name}{circular}{$hod}{pvcorrf}{"5.$range"} = $data{$type}{$name}{circular}{$hod}{pvcorrf}{$range};
$data{$type}{$name}{circular}{$hod}{pvcorrf}{"10.$range"} = $data{$type}{$name}{circular}{$hod}{pvcorrf}{$range};
$data{$type}{$name}{circular}{$hod}{pvcorrf}{"15.$range"} = $data{$type}{$name}{circular}{$hod}{pvcorrf}{$range};
$data{$type}{$name}{circular}{$hod}{pvcorrf}{"20.$range"} = $data{$type}{$name}{circular}{$hod}{pvcorrf}{$range};
$data{$type}{$name}{circular}{$hod}{pvcorrf}{"25.$range"} = $data{$type}{$name}{circular}{$hod}{pvcorrf}{$range};
$data{$type}{$name}{circular}{$hod}{pvcorrf}{"30.$range"} = $data{$type}{$name}{circular}{$hod}{pvcorrf}{$range};
$data{$type}{$name}{circular}{$hod}{pvcorrf}{"35.$range"} = $data{$type}{$name}{circular}{$hod}{pvcorrf}{$range};
$data{$type}{$name}{circular}{$hod}{pvcorrf}{"40.$range"} = $data{$type}{$name}{circular}{$hod}{pvcorrf}{$range};
$data{$type}{$name}{circular}{$hod}{pvcorrf}{"45.$range"} = $data{$type}{$name}{circular}{$hod}{pvcorrf}{$range};
delete $data{$type}{$name}{circular}{$hod}{pvcorrf}{$range};
}
delete $data{$type}{$name}{circular}{$hod}{pvcorrf}{$range} if(!defined $data{$type}{$name}{circular}{$hod}{pvcorrf}{$range});
}
for my $range (keys %{$data{$type}{$name}{circular}{$hod}{quality}}) {
delete $data{$type}{$name}{circular}{$hod}{quality}{$range} if($range !~ /^($ra)$/xs);
if($range !~ /simple|\./xs) { # 24.02.2024
$data{$type}{$name}{circular}{$hod}{quality}{"5.$range"} = $data{$type}{$name}{circular}{$hod}{quality}{$range};
$data{$type}{$name}{circular}{$hod}{quality}{"10.$range"} = $data{$type}{$name}{circular}{$hod}{quality}{$range};
$data{$type}{$name}{circular}{$hod}{quality}{"15.$range"} = $data{$type}{$name}{circular}{$hod}{quality}{$range};
$data{$type}{$name}{circular}{$hod}{quality}{"20.$range"} = $data{$type}{$name}{circular}{$hod}{quality}{$range};
$data{$type}{$name}{circular}{$hod}{quality}{"25.$range"} = $data{$type}{$name}{circular}{$hod}{quality}{$range};
$data{$type}{$name}{circular}{$hod}{quality}{"30.$range"} = $data{$type}{$name}{circular}{$hod}{quality}{$range};
$data{$type}{$name}{circular}{$hod}{quality}{"35.$range"} = $data{$type}{$name}{circular}{$hod}{quality}{$range};
$data{$type}{$name}{circular}{$hod}{quality}{"40.$range"} = $data{$type}{$name}{circular}{$hod}{quality}{$range};
$data{$type}{$name}{circular}{$hod}{quality}{"45.$range"} = $data{$type}{$name}{circular}{$hod}{quality}{$range};
delete $data{$type}{$name}{circular}{$hod}{quality}{$range};
}
delete $data{$type}{$name}{circular}{$hod}{quality}{$range} if(!defined $data{$type}{$name}{circular}{$hod}{quality}{$range});
}
for my $range (keys %{$data{$type}{$name}{circular}{$hod}{pvrlsum}}) {
delete $data{$type}{$name}{circular}{$hod}{pvrlsum}{$range} if($range !~ /^($ra)$/xs);
if($range !~ /simple|\./xs) { # 24.02.2024
$data{$type}{$name}{circular}{$hod}{pvrlsum}{"5.$range"} = $data{$type}{$name}{circular}{$hod}{pvrlsum}{$range};
$data{$type}{$name}{circular}{$hod}{pvrlsum}{"10.$range"} = $data{$type}{$name}{circular}{$hod}{pvrlsum}{$range};
$data{$type}{$name}{circular}{$hod}{pvrlsum}{"15.$range"} = $data{$type}{$name}{circular}{$hod}{pvrlsum}{$range};
$data{$type}{$name}{circular}{$hod}{pvrlsum}{"20.$range"} = $data{$type}{$name}{circular}{$hod}{pvrlsum}{$range};
$data{$type}{$name}{circular}{$hod}{pvrlsum}{"25.$range"} = $data{$type}{$name}{circular}{$hod}{pvrlsum}{$range};
$data{$type}{$name}{circular}{$hod}{pvrlsum}{"30.$range"} = $data{$type}{$name}{circular}{$hod}{pvrlsum}{$range};
$data{$type}{$name}{circular}{$hod}{pvrlsum}{"35.$range"} = $data{$type}{$name}{circular}{$hod}{pvrlsum}{$range};
$data{$type}{$name}{circular}{$hod}{pvrlsum}{"40.$range"} = $data{$type}{$name}{circular}{$hod}{pvrlsum}{$range};
$data{$type}{$name}{circular}{$hod}{pvrlsum}{"45.$range"} = $data{$type}{$name}{circular}{$hod}{pvrlsum}{$range};
delete $data{$type}{$name}{circular}{$hod}{pvrlsum}{$range};
}
}
for my $range (keys %{$data{$type}{$name}{circular}{$hod}{pvfcsum}}) {
delete $data{$type}{$name}{circular}{$hod}{pvfcsum}{$range} if($range !~ /^($ra)$/xs);
if($range !~ /simple|\./xs) { # 24.02.2024
$data{$type}{$name}{circular}{$hod}{pvfcsum}{"5.$range"} = $data{$type}{$name}{circular}{$hod}{pvfcsum}{$range};
$data{$type}{$name}{circular}{$hod}{pvfcsum}{"10.$range"} = $data{$type}{$name}{circular}{$hod}{pvfcsum}{$range};
$data{$type}{$name}{circular}{$hod}{pvfcsum}{"15.$range"} = $data{$type}{$name}{circular}{$hod}{pvfcsum}{$range};
$data{$type}{$name}{circular}{$hod}{pvfcsum}{"20.$range"} = $data{$type}{$name}{circular}{$hod}{pvfcsum}{$range};
$data{$type}{$name}{circular}{$hod}{pvfcsum}{"25.$range"} = $data{$type}{$name}{circular}{$hod}{pvfcsum}{$range};
$data{$type}{$name}{circular}{$hod}{pvfcsum}{"30.$range"} = $data{$type}{$name}{circular}{$hod}{pvfcsum}{$range};
$data{$type}{$name}{circular}{$hod}{pvfcsum}{"35.$range"} = $data{$type}{$name}{circular}{$hod}{pvfcsum}{$range};
$data{$type}{$name}{circular}{$hod}{pvfcsum}{"40.$range"} = $data{$type}{$name}{circular}{$hod}{pvfcsum}{$range};
$data{$type}{$name}{circular}{$hod}{pvfcsum}{"45.$range"} = $data{$type}{$name}{circular}{$hod}{pvfcsum}{$range};
delete $data{$type}{$name}{circular}{$hod}{pvfcsum}{$range};
}
}
for my $range (keys %{$data{$type}{$name}{circular}{$hod}{dnumsum}}) {
delete $data{$type}{$name}{circular}{$hod}{dnumsum}{$range} if($range !~ /^($ra)$/xs);
if($range !~ /simple|\./xs) { # 24.02.2024
$data{$type}{$name}{circular}{$hod}{dnumsum}{"5.$range"} = $data{$type}{$name}{circular}{$hod}{dnumsum}{$range};
$data{$type}{$name}{circular}{$hod}{dnumsum}{"10.$range"} = $data{$type}{$name}{circular}{$hod}{dnumsum}{$range};
$data{$type}{$name}{circular}{$hod}{dnumsum}{"15.$range"} = $data{$type}{$name}{circular}{$hod}{dnumsum}{$range};
$data{$type}{$name}{circular}{$hod}{dnumsum}{"20.$range"} = $data{$type}{$name}{circular}{$hod}{dnumsum}{$range};
$data{$type}{$name}{circular}{$hod}{dnumsum}{"25.$range"} = $data{$type}{$name}{circular}{$hod}{dnumsum}{$range};
$data{$type}{$name}{circular}{$hod}{dnumsum}{"30.$range"} = $data{$type}{$name}{circular}{$hod}{dnumsum}{$range};
$data{$type}{$name}{circular}{$hod}{dnumsum}{"35.$range"} = $data{$type}{$name}{circular}{$hod}{dnumsum}{$range};
$data{$type}{$name}{circular}{$hod}{dnumsum}{"40.$range"} = $data{$type}{$name}{circular}{$hod}{dnumsum}{$range};
$data{$type}{$name}{circular}{$hod}{dnumsum}{"45.$range"} = $data{$type}{$name}{circular}{$hod}{dnumsum}{$range};
delete $data{$type}{$name}{circular}{$hod}{dnumsum}{$range};
}
}
for my $wp (keys %{$data{$type}{$name}{circular}{$hod}}) { # 19.02.204
@ -5697,6 +5786,7 @@ sub centralTask {
delete $data{$type}{$name}{circular}{$hod}{$wp};
}
}
## currentWeatherDev in Attr umsetzen
my $cwd = ReadingsVal ($name, 'currentWeatherDev', ''); # 30.01.2024
if ($cwd) {
@ -6779,17 +6869,17 @@ sub ___readCandQ {
my $cc = $paref->{cloudcover};
my ($acu, $aln) = isAutoCorrUsed ($name); # Autokorrekturmodus
my $sunalt = NexthoursVal ($hash, "NextHour".sprintf("%02d",$num), 'sunalt', ''); # Sun Altitude
my $sabin = sunalt2bin ($sunalt);
my $hc = ReadingsNum ($name, 'pvCorrectionFactor_'.sprintf("%02d",$fh1), 1.00); # Voreinstellung RAW-Korrekturfaktor
my $hq = '-'; # keine Qualität definiert
my $crang = 'simple';
delete $data{$type}{$name}{nexthours}{"NextHour".sprintf("%02d",$num)}{cloudrange};
my $sunalt = NexthoursVal ($hash, "NextHour".sprintf("%02d",$num), 'sunalt', ''); # Sun Altitude
my $safac = CircularSunaltkorrVal ($hash, sprintf("%02d",$fh1), $sunalt, 0); # Faktor gespeichert für eine Sun Altitude
if ($acu =~ /on_complex/xs) { # Autokorrektur complex soll genutzt werden
my $crang = cloud2bin ($cc); # Range errechnen
($hc, $hq) = CircularCloudkorrVal ($hash, sprintf("%02d",$fh1), $crang, undef); # Korrekturfaktor/Qualität der Stunde des Tages (complex)
$crang = cloud2bin ($cc); # Range errechnen
($hc, $hq) = CircularSunCloudkorrVal ($hash, sprintf("%02d",$fh1), $sabin, $crang, undef); # Korrekturfaktor/Qualität der Stunde des Tages (complex)
$hq //= '-';
$hc //= 1; # Korrekturfaktor = 1 (keine Korrektur) # keine Qualität definiert
$hc = 1 if(1 * $hc == 0); # 0.0-Werte ignorieren (Schleifengefahr)
@ -6797,19 +6887,21 @@ sub ___readCandQ {
$data{$type}{$name}{nexthours}{"NextHour".sprintf("%02d",$num)}{cloudrange} = $crang;
}
elsif ($acu =~ /on_simple/xs) {
($hc, $hq) = CircularCloudkorrVal ($hash, sprintf("%02d",$fh1), 'simple', undef); # Korrekturfaktor/Qualität der Stunde des Tages (simple)
($hc, $hq) = CircularSunCloudkorrVal ($hash, sprintf("%02d",$fh1), $sabin, 'simple', undef); # Korrekturfaktor/Qualität der Stunde des Tages (simple)
$hq //= '-';
$hc //= 1; # Korrekturfaktor = 1
$hc = 1 if(1 * $hc == 0); # 0.0-Werte ignorieren (Schleifengefahr)
}
else { # keine Autokorrektur
($hc, $hq) = CircularCloudkorrVal ($hash, sprintf("%02d",$fh1), 'simple', undef); # Korrekturfaktor/Qualität der Stunde des Tages (simple)
($hc, $hq) = CircularSunCloudkorrVal ($hash, sprintf("%02d",$fh1), $sabin, 'simple', undef); # Korrekturfaktor/Qualität der Stunde des Tages (simple)
$hq //= '-';
$hc = 1;
}
$hc = sprintf "%.2f", $hc;
debugLog ($paref, 'pvCorrectionRead', "read parameters - fd: $fd, hod: ".sprintf("%02d",$fh1).", Sun Altitude Bin: $sabin, Cloud range: $crang, corrf: $hc, quality: $hq");
$data{$type}{$name}{nexthours}{"NextHour".sprintf("%02d",$num)}{pvcorrf} = $hc."/".$hq;
if($fd == 0 && $fh1) {
@ -7418,8 +7510,8 @@ sub _batSocTarget {
my $pvfctm = ReadingsNum ($name, 'Tomorrow_PVforecast', 0); # PV Prognose morgen
my $pvfctd = ReadingsNum ($name, 'RestOfDayPVforecast', 0); # PV Prognose Rest heute
my $csopt = ReadingsNum ($name, 'Battery_OptimumTargetSoC', $lowSoc); # aktuelles SoC Optimum
my $pvexpect = $pvfctm > $pvfctd ? $pvfctm : $pvfctd;
my $pvexpect = $pvfctm > $pvfctd ? $pvfctm : $pvfctd;
my $batinstcap = CurrentVal ($hash, 'batinstcap', 0); # installierte Batteriekapazität Wh
my $cantarget = 100 - (100 / $batinstcap) * $pvexpect; # berechneter möglicher Min SOC nach Berücksichtigung Ladewahrscheinlichkeit
@ -9526,7 +9618,7 @@ sub _calcCaQcomplex {
if (!$aln) {
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_cloudcover', 'done');
debugLog ($paref, 'pvCorrection', "Autolearning is switched off for hour: $h -> skip the recalculation of the complex correction factor");
debugLog ($paref, 'pvCorrectionWrite', "Autolearning is switched off for hour: $h -> skip the recalculation of the complex correction factor");
return;
}
@ -9560,7 +9652,7 @@ sub _calcCaQcomplex {
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_cloudcover', 'done');
if ($acu =~ /on_complex/xs) {
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $factor." (automatic - old factor: $oldfac, cloudiness range: $crang, days in range: $dnum)");
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $factor." (automatic - old factor: $oldfac, Sun Alt range: $sabin, Cloud range: $crang, Days in range: $dnum)");
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h).'_autocalc', 'done');
}
@ -9584,13 +9676,13 @@ sub _calcCaQsimple {
my $sr = ReadingsVal ($name, '.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', '');
if($sr eq "done") {
# debugLog ($paref, 'pvCorrection', "Simple Corrf factor Hour: ".sprintf("%02d",$h)." already calculated");
# debugLog ($paref, 'pvCorrectionWrite', "Simple Corrf factor Hour: ".sprintf("%02d",$h)." already calculated");
return;
}
if (!$aln) {
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', 'done');
debugLog ($paref, 'pvCorrection', "Autolearning is switched off for hour: $h -> skip the recalculation of the simple correction factor");
debugLog ($paref, 'pvCorrectionWrite', "Autolearning is switched off for hour: $h -> skip the recalculation of the simple correction factor");
return;
}
@ -9602,8 +9694,12 @@ sub _calcCaQsimple {
return;
}
my $sunalt = HistoryVal ($hash, $day, sprintf("%02d",$h), 'sunalt', 0); # Sonne Altitude
my $sabin = sunalt2bin ($sunalt);
$paref->{pvrl} = $pvrl;
$paref->{pvfc} = $pvfc;
$paref->{sabin} = $sabin;
$paref->{crang} = 'simple';
$paref->{calc} = 'Simple';
@ -9611,13 +9707,14 @@ sub _calcCaQsimple {
delete $paref->{pvrl};
delete $paref->{pvfc};
delete $paref->{sabin};
delete $paref->{crang};
delete $paref->{calc};
storeReading ('.pvCorrectionFactor_'.sprintf("%02d",$h).'_apipercentil', 'done');
if ($acu =~ /on_simple/xs) {
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $factor." (automatic - old factor: $oldfac, days in range: $dnum)");
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h), $factor." (automatic - old factor: $oldfac, Days in range: $dnum)");
storeReading ('pvCorrectionFactor_'.sprintf("%02d",$h).'_autocalc', 'done');
}
@ -9644,10 +9741,14 @@ sub __calcNewFactor {
my $pvrlsum = $pvrl;
my $pvfcsum = $pvfc;
my ($oldfac, $oldq) = CircularCloudkorrVal ($hash, sprintf("%02d",$h), $crang, 0); # bisher definierter Korrekturfaktor
my ($pvhis, $fchis, $dnum) = CircularSumVal ($hash, sprintf("%02d",$h), $crang, 0);
debugLog ($paref, 'pvCorrectionWrite', "$calc Corrf -> Start calculation correction factor for hour: $h");
my ($oldfac, $oldq) = CircularSunCloudkorrVal ($hash, sprintf("%02d",$h), $sabin, $crang, 0); # bisher definierter Korrekturfaktor / Qualität
my ($pvhis, $fchis, $dnum) = CircularSumVal ($hash, sprintf("%02d",$h), $sabin, $crang, 0);
$oldfac = 1 if(1 * $oldfac == 0);
debugLog ($paref, 'pvCorrectionWrite', "$calc Corrf -> read historical values: pv real sum: $pvhis, pv forecast sum: $fchis, days sum: $dnum");
if ($dnum) { # Werte in History vorhanden -> haben Prio !
$dnum++;
$pvrlsum = $pvrl + $pvhis;
@ -9666,10 +9767,6 @@ sub __calcNewFactor {
$factor = sprintf "%.2f", ($pvrl / $pvfc);
}
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{pvrlsum}{$crang} = $pvrlsum; # PV Erzeugung Summe speichern
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{pvfcsum}{$crang} = $pvfcsum; # PV Prognose Summe speichern
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{dnumsum}{$crang} = $dnum; # Anzahl aller historischen Tade dieser Range
my $maxvar = AttrVal ($name, 'affectMaxDayVariance', $defmaxvar); # max. Korrekturvarianz
$factor = 1.00 if(1 * $factor == 0); # 0.00-Werte ignorieren (Schleifengefahr)
@ -9686,16 +9783,23 @@ sub __calcNewFactor {
my $qual = __calcFcQuality ($pvfc, $pvrl); # Qualität der Vorhersage für die vergangene Stunde
debugLog ($paref, 'pvCorrection', "$calc Corrf -> start calculation correction factor for hour: $h");
debugLog ($paref, 'pvCorrection', "$calc Corrf -> determined values - hour: $h, cloudiness range: $crang, old corrf: $oldfac, new corrf: $factor, days: $dnum");
debugLog ($paref, 'pvCorrection|saveData2Cache', "$calc Corrf -> write correction values into Circular - hour: $h, cloudiness range: $crang, factor: $factor, quality: $qual");
debugLog ($paref, 'pvCorrectionWrite', "$calc Corrf -> determined values - hour: $h, Sun Altitude range: $sabin, Cloud range: $crang, old factor: $oldfac, new factor: $factor, days: $dnum");
debugLog ($paref, 'pvCorrectionWrite|saveData2Cache', "$calc Corrf -> write correction values into Circular - hour: $h, Sun Altitude range: $sabin, Cloud range: $crang, factor: $factor, quality: $qual, days: $dnum");
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{$crang} = $factor; # Korrekturfaktor nach "Cloudcover" oder "Simple"
if ($crang ne 'simple') {
my $idx = $sabin.'.'.$crang; # value für pvcorrf Sonne Altitude
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{pvrlsum}{$idx} = $pvrlsum; # PV Erzeugung Summe speichern
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{pvfcsum}{$idx} = $pvfcsum; # PV Prognose Summe speichern
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{dnumsum}{$idx} = $dnum; # Anzahl aller historischen Tade dieser Range
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{$idx} = $factor;
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{quality}{$idx} = $qual;
}
else {
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{pvrlsum}{$crang} = $pvrlsum;
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{pvfcsum}{$crang} = $pvfcsum;
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{dnumsum}{$crang} = $dnum;
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{$crang} = $factor;
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{quality}{$crang} = $qual;
if (defined $sabin) { # Korrekturfaktor für Sonne Altitude
$sabin = 200 + $sabin; # 200 + value für pvcorrf Sonne Altitude
$data{$type}{$name}{circular}{sprintf("%02d",$h)}{pvcorrf}{$sabin} = $factor;
}
return ($oldfac, $factor, $dnum);
@ -10435,8 +10539,8 @@ sub _checkSetupNotComplete {
my $medev = ReadingsVal ($name, 'currentMeterDev', undef); # Meter Device
my $peaks = ReadingsVal ($name, 'modulePeakString', undef); # String Peak
my $dir = ReadingsVal ($name, 'moduleAzimuth', undef); # Modulausrichtung Konfig (Azimut)
my $ta = ReadingsVal ($name, 'moduleDeclination', undef); # Modul Neigungswinkel Konfig
my $maz = ReadingsVal ($name, 'moduleAzimuth', undef); # Modulausrichtung Konfig (Azimut)
my $mdec = ReadingsVal ($name, 'moduleDeclination', undef); # Modul Neigungswinkel Konfig
my $mrt = ReadingsVal ($name, 'moduleRoofTops', undef); # RoofTop Konfiguration (SolCast API)
my $vrmcr = SolCastAPIVal ($hash, '?VRM', '?API', 'credentials', ''); # Victron VRM Credentials gesetzt
@ -10473,7 +10577,7 @@ sub _checkSetupNotComplete {
my $chktitle = $htitles{plchk}{$lang};
if (!$is || !$wedev || !$radev || !$indev || !$medev || !$peaks ||
(isSolCastUsed ($hash) ? (!$rip || !$mrt) : isVictronKiUsed ($hash) ? !$vrmcr : (!$dir || !$ta )) ||
(isSolCastUsed ($hash) ? (!$rip || !$mrt) : isVictronKiUsed ($hash) ? !$vrmcr : (!$maz || !$mdec )) ||
(isForecastSolarUsed ($hash) ? !$coset : '') ||
!defined $pv0) {
$ret .= "<table class='roomoverview'>";
@ -10484,6 +10588,12 @@ sub _checkSetupNotComplete {
if (!$wedev) { ## no critic 'Cascading'
$ret .= $hqtxt{cfd}{$lang};
}
elsif (!$is) {
$ret .= $hqtxt{ist}{$lang};
}
elsif (!$peaks) {
$ret .= $hqtxt{mps}{$lang};
}
elsif (!$radev) {
$ret .= $hqtxt{crd}{$lang};
}
@ -10493,22 +10603,16 @@ sub _checkSetupNotComplete {
elsif (!$medev) {
$ret .= $hqtxt{mid}{$lang};
}
elsif (!$is) {
$ret .= $hqtxt{ist}{$lang};
}
elsif (!$peaks) {
$ret .= $hqtxt{mps}{$lang};
}
elsif (!$rip && isSolCastUsed ($hash)) {
$ret .= $hqtxt{rip}{$lang};
}
elsif (!$mrt && isSolCastUsed ($hash)) {
$ret .= $hqtxt{mrt}{$lang};
}
elsif (!$dir && !isSolCastUsed ($hash) && !isVictronKiUsed ($hash)) {
elsif (!$maz && !isSolCastUsed ($hash) && !isVictronKiUsed ($hash)) {
$ret .= $hqtxt{mdr}{$lang};
}
elsif (!$ta && !isSolCastUsed ($hash) && !isVictronKiUsed ($hash)) {
elsif (!$mdec && !isSolCastUsed ($hash) && !isVictronKiUsed ($hash)) {
$ret .= $hqtxt{mta}{$lang};
}
elsif (!$vrmcr && isVictronKiUsed ($hash)) {
@ -11685,6 +11789,8 @@ sub _beamGraphicFirstHour {
$hfcg->{0}{weather} = HistoryVal ($hash, $hfcg->{0}{day_str}, $hfcg->{0}{time_str}, 'weatherid', 999);
$hfcg->{0}{wcc} = HistoryVal ($hash, $hfcg->{0}{day_str}, $hfcg->{0}{time_str}, 'wcc', '-');
$hfcg->{0}{sunalt} = HistoryVal ($hash, $hfcg->{0}{day_str}, $hfcg->{0}{time_str}, 'sunalt', '-');
$hfcg->{0}{sunaz} = HistoryVal ($hash, $hfcg->{0}{day_str}, $hfcg->{0}{time_str}, 'sunaz', '-');
}
else {
$val1 = CircularVal ($hash, $hfcg->{0}{time_str}, 'pvfc', 0);
@ -11755,6 +11861,8 @@ sub _beamGraphicRemainingHours {
$hfcg->{$i}{weather} = HistoryVal ($hash, $ds, $hfcg->{$i}{time_str}, 'weatherid', 999);
$hfcg->{$i}{wcc} = HistoryVal ($hash, $ds, $hfcg->{$i}{time_str}, 'wcc', '-');
$hfcg->{$i}{sunalt} = HistoryVal ($hash, $ds, $hfcg->{$i}{time_str}, 'sunalt', '-');
$hfcg->{$i}{sunaz} = HistoryVal ($hash, $ds, $hfcg->{$i}{time_str}, 'sunaz', '-');
}
else {
$nh = sprintf '%02d', $i + $offset;
@ -11769,6 +11877,8 @@ sub _beamGraphicRemainingHours {
$val4 = NexthoursVal ($hash, 'NextHour'.$nh, 'confc', 0);
$hfcg->{$i}{weather} = NexthoursVal ($hash, 'NextHour'.$nh, 'weatherid', 999);
$hfcg->{$i}{wcc} = NexthoursVal ($hash, 'NextHour'.$nh, 'cloudcover', '-');
$hfcg->{$i}{sunalt} = NexthoursVal ($hash, 'NextHour'.$nh, 'sunalt', '-');
$hfcg->{$i}{sunaz} = NexthoursVal ($hash, 'NextHour'.$nh, 'sunaz', '-');
#$val4 = (ReadingsVal($name,"NextHour".$ii."_IsConsumptionRecommended",'no') eq 'yes') ? $icon : undef;
}
@ -12158,6 +12268,12 @@ sub __weatherOnBeam {
$wcc += 0 if(isNumeric ($wcc)); # Javascript Fehler vermeiden: https://forum.fhem.de/index.php/topic,117864.msg1233661.html#msg1233661
$title .= ': '.$wcc;
$title .= '&#10;';
$title .= $htitles{sunpos}{$lang}.':';
$title .= '&#10;';
$title .= $htitles{elevatio}{$lang}.' '.$hfcg->{$i}{sunalt};
$title .= '&#10;';
$title .= $htitles{azimuth}{$lang}.' '.$hfcg->{$i}{sunaz};
if ($icon_name eq 'unknown') {
debugLog ($paref, "graphic", "unknown weather id: ".$hfcg->{$i}{weather}.", please inform the maintainer");
@ -12708,7 +12824,7 @@ sub _addHourAiRawdata {
if (!$aln) {
storeReading ('.signaldone_'.sprintf("%02d",$h), 'done');
debugLog ($paref, 'pvCorrection', "Autolearning is switched off for hour: $h -> skip add AI raw data");
debugLog ($paref, 'pvCorrectionRead', "Autolearning is switched off for hour: $h -> skip add AI raw data");
return;
}
@ -13866,10 +13982,11 @@ sub listDataPool {
my $aihit = NexthoursVal ($hash, $idx, 'aihit', '-');
my $pvfc = NexthoursVal ($hash, $idx, 'pvfc', '-');
my $neff = NexthoursVal ($hash, $idx, 'cloudcover', '-');
my $sunalt = NexthoursVal ($hash, $idx, 'sunalt', '-');
my ($f,$q) = split "/", $pvcorrf;
$sq .= "\n" if($sq);
$sq .= "Start: $nhts, Quality: $q, Factor: $f, AI usage: $aihit, PV expect: $pvfc Wh, Cloud: $neff";
$sq .= "Start: $nhts, Quality: $q, Factor: $f, AI usage: $aihit, PV expect: $pvfc Wh, Sun Alt: $sunalt, Cloud: $neff";
}
}
@ -13980,17 +14097,17 @@ sub _ldchash2val {
for my $f (sort {$a<=>$b} keys %{$pool->{$idx}{$key}}) {
next if($f eq 'simple');
if ($f !~ /2.{2}/xs) {
if ($f !~ /\./xs) {
$ret .= " " if($ret);
$ret .= "$f=".$pool->{$idx}{$key}{$f};
my $ct = ($ret =~ tr/=// // 0) / 10;
$ret .= "\n " if($ct =~ /^([1-9])?$/);
$ret .= "\n " if($ct =~ /^[1-9](.{1})?$/);
}
elsif ($f =~ /2.{2}/xs) {
elsif ($f =~ /\./xs) {
$ret2 .= " " if($ret2);
$ret2 .= "$f=".$pool->{$idx}{$key}{$f};
my $ct2 = ($ret2 =~ tr/=// // 0) / 10;
$ret2 .= "\n " if($ct2 =~ /^([1-9])?$/);
$ret2 .= "\n " if($ct2 =~ /^[1-9](.{1})?$/);
}
}
@ -16026,8 +16143,7 @@ return $def;
# rr1c - Gesamtniederschlag (1-stündig) letzte 1 Stunde kg/m2
# temp - Außentemperatur
# pvcorrf - PV Autokorrekturfaktoren (HASH),
# - ohne Wertesummand: Faktoren bezogen auf Cloudcover
# - 200 + sunalt: Faktoren bezogen auf Sonne Altitude
# - <Sonne Altitude>.<Cloudcover>
# lastTsMaxSocRchd - Timestamp des letzten Erreichens von SoC >= maxSoC
# nextTsMaxSocChge - Timestamp bis zu dem die Batterie mindestens einmal maxSoC erreichen soll
# days2care - verbleibende Tage bis der Batterie Pflege-SoC (default $maxSoCdef) erreicht sein soll
@ -16063,107 +16179,75 @@ sub CircularVal {
return $def;
}
################################################################
# Wert des Autokorrekturfaktors und dessen Qualität
# für eine bestimmte Bewölkungs-Range aus dem circular-Hash
###################################################################
# Wert des Autokorrekturfaktors
# für eine bestimmte Sun Altitude-Range aus dem Circular-Hash
# zurückliefern
# Usage:
# ($f,$q) = CircularCloudkorrVal ($hash, $hod, $crang, $def)
# $f = CircularSunCloudkorrVal ($hash, $hod, $sabin, $crang, $def)
#
# $f: Korrekturfaktor f. Stunde des Tages
# $q: Qualität des Korrekturfaktors
#
# $hod: Stunde des Tages (01,02,...,24)
# $crang: Range Bewölkung (1...100) oder "simple"
# $sabin: Sun Altitude Bin (0..90)
# $crang: Bewölkung Bin (0..100) oder "simple"
# $def: Defaultwert
#
################################################################
sub CircularCloudkorrVal {
###################################################################
sub CircularSunCloudkorrVal {
my $hash = shift;
my $hod = shift;
my $sabin = shift;
my $crang = shift;
my $def = shift;
my $name = $hash->{NAME};
my $type = $hash->{TYPE};
my $corrf = $def;
my $quality = $def;
my $qual = $def;
my $idx = 'simple';
if ($crang ne 'simple') {
$idx = $sabin.'.'.$crang;
}
if (defined($data{$type}{$name}{circular}) &&
defined($data{$type}{$name}{circular}{$hod}) &&
defined($data{$type}{$name}{circular}{$hod}{pvcorrf}) &&
defined($data{$type}{$name}{circular}{$hod}{pvcorrf}{$crang})) {
$corrf = $data{$type}{$name}{circular}{$hod}{pvcorrf}{$crang};
defined($data{$type}{$name}{circular}{$hod}{pvcorrf}{$idx})) {
$corrf = $data{$type}{$name}{circular}{$hod}{pvcorrf}{$idx};
}
if (defined($data{$type}{$name}{circular}) &&
defined($data{$type}{$name}{circular}{$hod}) &&
defined($data{$type}{$name}{circular}{$hod}{quality}) &&
defined($data{$type}{$name}{circular}{$hod}{quality}{$crang})) {
$quality = $data{$type}{$name}{circular}{$hod}{quality}{$crang};
defined($data{$type}{$name}{circular}{$hod}{quality}{$idx})) {
$qual = $data{$type}{$name}{circular}{$hod}{quality}{$idx};
}
return ($corrf, $quality);
}
################################################################
# Wert des Autokorrekturfaktors
# für eine bestimmte Sun Altitude-Range aus dem Circular-Hash
# zurückliefern
# Usage:
# $f = CircularSunaltkorrVal ($hash, $hod, $crang, $def)
#
# $f: Korrekturfaktor f. Stunde des Tages
#
# $hod: Stunde des Tages (01,02,...,24)
# $sabin: Range Sun Altitude (0..90)
# $def: Defaultwert
#
################################################################
sub CircularSunaltkorrVal {
my $hash = shift;
my $hod = shift;
my $sunalt = shift;
my $def = shift;
my $name = $hash->{NAME};
my $type = $hash->{TYPE};
my $corrf = $def;
return $def if(!$sunalt);
my $sabin = sunalt2bin ($sunalt);
$sabin = 200 + $sabin;
if (defined($data{$type}{$name}{circular}) &&
defined($data{$type}{$name}{circular}{$hod}) &&
defined($data{$type}{$name}{circular}{$hod}{pvcorrf}) &&
defined($data{$type}{$name}{circular}{$hod}{pvcorrf}{$sabin})) {
$corrf = $data{$type}{$name}{circular}{$hod}{pvcorrf}{$sabin};
}
return ($corrf);
return ($corrf, $qual);
}
########################################################################################################
# Die durchschnittliche reale PV Erzeugung, PV Prognose und Tage
# einer bestimmten Bewölkungs-Range aus dem circular-Hash zurückliefern
# Usage:
# ($pvrlsum, $pvfcsum, $dnumsum) = CircularSumVal ($hash, $hod, $crang, $def)
# ($pvrlsum, $pvfcsum, $dnumsum) = CircularSumVal ($hash, $hod, $sabin, $crang, $def)
#
# $pvrlsum: Summe reale PV Erzeugung pro Bewölkungsbereich über die gesamte Laufzeit
# $pvfcsum: Summe PV Prognose pro Bewölkungsbereich über die gesamte Laufzeit
# $dnumsum: Anzahl Tage pro Bewölkungsbereich über die gesamte Laufzeit
#
# $hod: Stunde des Tages (01,02,...,24)
# $crang: Range Bewölkung (1...100) oder "simple"
# $hod: Stunde des Tages (01,02,..,24)
# $sabin: Sun Altitude Bin (0..90)
# $crang: Bewölkung Bin (1..100) oder "simple"
# $def: Defaultwert
#
#######################################################################################################
sub CircularSumVal {
my $hash = shift;
my $hod = shift;
my $sabin = shift;
my $crang = shift;
my $def = shift;
@ -16173,26 +16257,31 @@ sub CircularSumVal {
my $pvrlsum = $def;
my $pvfcsum = $def;
my $dnumsum = $def;
my $idx = 'simple';
if ($crang ne 'simple') {
$idx = $sabin.'.'.$crang;
}
if (defined($data{$type}{$name}{circular}) &&
defined($data{$type}{$name}{circular}{$hod}) &&
defined($data{$type}{$name}{circular}{$hod}{pvrlsum}) &&
defined($data{$type}{$name}{circular}{$hod}{pvrlsum}{$crang})) {
$pvrlsum = $data{$type}{$name}{circular}{$hod}{pvrlsum}{$crang};
defined($data{$type}{$name}{circular}{$hod}{pvrlsum}{$idx})) {
$pvrlsum = $data{$type}{$name}{circular}{$hod}{pvrlsum}{$idx};
}
if (defined($data{$type}{$name}{circular}) &&
defined($data{$type}{$name}{circular}{$hod}) &&
defined($data{$type}{$name}{circular}{$hod}{pvfcsum}) &&
defined($data{$type}{$name}{circular}{$hod}{pvfcsum}{$crang})) {
$pvfcsum = $data{$type}{$name}{circular}{$hod}{pvfcsum}{$crang};
defined($data{$type}{$name}{circular}{$hod}{pvfcsum}{$idx})) {
$pvfcsum = $data{$type}{$name}{circular}{$hod}{pvfcsum}{$idx};
}
if (defined($data{$type}{$name}{circular}) &&
defined($data{$type}{$name}{circular}{$hod}) &&
defined($data{$type}{$name}{circular}{$hod}{dnumsum}) &&
defined($data{$type}{$name}{circular}{$hod}{dnumsum}{$crang})) {
$dnumsum = $data{$type}{$name}{circular}{$hod}{dnumsum}{$crang};
defined($data{$type}{$name}{circular}{$hod}{dnumsum}{$idx})) {
$dnumsum = $data{$type}{$name}{circular}{$hod}{dnumsum}{$idx};
}
return ($pvrlsum, $pvfcsum, $dnumsum);
@ -17522,6 +17611,8 @@ to ensure that the system configuration is correct.
The hours 01 - 24 refer to the hour of the day, e.g. the hour 09 refers to the time from
08 - 09 o'clock. <br>
Hour 99 has a special function. <br>
The values of the keys pvcorrf, quality, pvrlsum, pvfcsum and dnumsum are coded in the form
&lt;range sun elevation&gt;.&lt;cloud cover range&gt;. <br>
Explanation of the values: <br><br>
<ul>
@ -18039,7 +18130,8 @@ to ensure that the system configuration is correct.
<tr><td> <b>epiecesCalc</b> </td><td>Calculation of specific energy consumption per operating hour and consumer </td></tr>
<tr><td> <b>graphic</b> </td><td>Module graphic information </td></tr>
<tr><td> <b>notifyHandling</b> </td><td>Sequence of event processing in the module </td></tr>
<tr><td> <b>pvCorrection</b> </td><td>Calculation and application PV correction factors </td></tr>
<tr><td> <b>pvCorrectionRead</b> </td><td>Application of PV correction factors </td></tr>
<tr><td> <b>pvCorrectionWrite</b> </td><td>Calculation of PV correction factors </td></tr>
<tr><td> <b>radiationProcess</b> </td><td>Collection and processing of solar radiation data </td></tr>
<tr><td> <b>saveData2Cache</b> </td><td>Data storage in internal memory structures </td></tr>
</table>
@ -19653,6 +19745,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
Die Stundenangaben 01 - 24 beziehen sich auf die Stunde des Tages, z.B. bezieht sich die Stunde 09 auf die Zeit von
08 - 09 Uhr. <br>
Die Stunde 99 hat eine Sonderfunktion. <br>
Die Werte der Schlüssel pvcorrf, quality, pvrlsum, pvfcsum und dnumsum sind in der Form
&lt;Bereich Sonnenstand Höhe&gt;.&lt;Bewölkungsbreich&gt; kodiert. <br>
Erläuterung der Werte: <br><br>
<ul>
@ -20171,7 +20265,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<tr><td> <b>epiecesCalc</b> </td><td>Berechnung des spezifischen Energieverbrauchs je Betriebsstunde und Verbraucher </td></tr>
<tr><td> <b>graphic</b> </td><td>Informationen der Modulgrafik </td></tr>
<tr><td> <b>notifyHandling</b> </td><td>Ablauf der Eventverarbeitung im Modul </td></tr>
<tr><td> <b>pvCorrection</b> </td><td>Berechnung und Anwendung PV Korrekturfaktoren </td></tr>
<tr><td> <b>pvCorrectionRead</b> </td><td>Anwendung PV Korrekturfaktoren </td></tr>
<tr><td> <b>pvCorrectionWrite</b> </td><td>Berechnung PV Korrekturfaktoren </td></tr>
<tr><td> <b>radiationProcess</b> </td><td>Sammlung und Verarbeitung der Solarstrahlungsdaten </td></tr>
<tr><td> <b>saveData2Cache</b> </td><td>Datenspeicherung in internen Speicherstrukturen </td></tr>
</table>