From ce1781624a7df3343a89c82eda5b906dee1d98e2 Mon Sep 17 00:00:00 2001 From: Eskimue Date: Fri, 15 May 2026 16:15:15 +0200 Subject: [PATCH 1/2] Satellitenhimmer verbessert --- public/satellitenhimmel.php | 57 ++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/public/satellitenhimmel.php b/public/satellitenhimmel.php index 90878e0..38a7ec9 100644 --- a/public/satellitenhimmel.php +++ b/public/satellitenhimmel.php @@ -429,7 +429,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
-
Lade Ueberfluege...
+
Lade Überflüge...
@@ -599,7 +599,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) { } function azToDirectionName(az) { - const dirs = ['Norden','Nordosten','Osten','Suedosten','Sueden','Suedwesten','Westen','Nordwesten']; + const dirs = ['Norden','Nord Osten','Osten','Süd Osten','Süden','Süd Westen','Westen','Nord Westen']; return dirs[Math.round(az/45) % 8]; } @@ -625,7 +625,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) { function classifySkyBrightness(sunAltDeg) { if (sunAltDeg > -4) return {key:'day', label:'Tag'}; - if (sunAltDeg >= -12) return {key:'twilight', label:'Daemmerung'}; + if (sunAltDeg >= -12) return {key:'twilight', label:'Dämmerung'}; return {key:'night', label:'Nacht'}; } @@ -670,7 +670,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) { function getDarknessCondition(sunAltDeg, visiblePeak) { if (sunAltDeg > -4) return { key: 'daylight', score: 0.05, label: 'Tageshimmel' }; - if (sunAltDeg >= -12) return { key: 'twilight', score: 0.65, label: 'Daemmerung' }; + if (sunAltDeg >= -12) return { key: 'twilight', score: 0.65, label: 'Dämmerung' }; if ((visiblePeak?.elevationDeg ?? -1) < 0) return { key: 'deep_night', score: 0.20, label: 'Nicht sichtbar / Erdschatten' }; return { key: 'ideal', score: 1.00, label: 'Dunkler Himmel, Satellit sonnenbeschienen' }; } @@ -785,8 +785,43 @@ if (!empty($currentLocation) && is_array($currentLocation)) { }).filter((v,i,a) => i===0 || v!==a[i-1]) : descriptors; if (limited.length === 1) return `Sie ist dabei ${limited[0]} zu beobachten.`; - if (limited.length === 2) return `Sie ist zunaechst ${limited[0]} zu beobachten und zuletzt ${limited[1]}.`; - return `Sie ist zunaechst ${limited[0]} zu beobachten, spaeter ${limited.slice(1,-1).join(', ')} und zuletzt ${limited[limited.length-1]}.`; + if (limited.length === 2) return `Sie ist zunächst ${limited[0]} zu beobachten und zuletzt ${limited[1]}.`; + return `Sie ist zunächst ${limited[0]} zu beobachten, später ${limited.slice(1,-1).join(', ')} und zuletzt ${limited[limited.length-1]}.`; + } + + function buildFlightNarrative(satName, visiblePhase, visiblePeak) { + if (!visiblePhase.visiblePath.length) return ''; + const startPoint = visiblePhase.visiblePath[0]; + const endPoint = visiblePhase.visiblePath[visiblePhase.visiblePath.length - 1]; + const startDir = azToDirectionName(startPoint.azimuthDeg); + const endDir = azToDirectionName(endPoint.azimuthDeg); + + let peakText = `steigt bis auf etwa ${visiblePeak.elevationDeg.toFixed(0)}° Höhe im ${azToDirectionName(visiblePeak.azimuthDeg)}`; + if (visiblePeak.elevationDeg >= 75) { + peakText = 'steigt bis nahe an den Zenit'; + } else if (visiblePeak.elevationDeg >= 55) { + peakText = `steigt hoch über den ${azToDirectionName(visiblePeak.azimuthDeg)} auf`; + } + + return `${satName} taucht gegen ${formatTime(visiblePhase.visibleStartTime)} Uhr am ${startDir}horizont auf, ${peakText} und zieht dann weiter Richtung ${endDir}, wo sie um ${formatTime(visiblePhase.visibleEndTime)} Uhr wieder verschwindet.`; + } + + function buildObservationSummaryClean(satName, pass, visibility) { + if (visibility.darkness.key === 'daylight') return `${satName} ist bei diesem Überflug am Tageshimmel praktisch nicht zu sehen.`; + const visiblePhase = getVisiblePhase(pass); + if (!visiblePhase.visiblePath.length) { + return `${satName} liegt bei diesem Überflug während der gesamten Passage im Erdschatten und ist deshalb nicht sichtbar.`; + } + const visiblePeak = visibility.visiblePeak ?? getVisiblePeak(pass, visiblePhase); + const flightText = buildFlightNarrative(satName, visiblePhase, visiblePeak); + const shadowText = pass.shadowEntry + ? ` Sie tritt um ${formatTime(pass.shadowEntry.time)} Uhr in den Erdschatten ein.` : ''; + const magText = pass.peakMag !== null + ? ` Ihre maximale Helligkeit beträgt ${formatMag(pass.peakMag)} mag.` : ''; + return `Der Überflug der ${satName} am ${formatDateLocal(pass.aosTime)} (${formatWeekdayShort(pass.aosTime)}) um ${formatTime(pass.aosTime)} Uhr ist ${visibility.label} (${visibility.scorePercent} von 100 Punkten). ` + + `Der Überflug ist ${formatDurationLong(visiblePhase.visibleDurationMs)} lang und bis ${formatTime(visiblePhase.visibleEndTime)} Uhr zu sehen. ` + + `${flightText} Die maximale Horizonthöhe der Raumstation ${satName} beträgt ${visiblePeak.elevationDeg.toFixed(0)}°.` + + `${shadowText}${magText}`; } function getVisiblePhase(pass) { @@ -842,14 +877,14 @@ if (!empty($currentLocation) && is_array($currentLocation)) { return `${satName} liegt bei diesem Überflug waehrend der gesamten Passage im Erdschatten und ist deshalb nicht sichtbar.`; } const visiblePeak = visibility.visiblePeak ?? getVisiblePeak(pass, visiblePhase); - const pathText = buildPathDescription(visiblePhase.visiblePath); + const flightText = buildFlightNarrative(satName, visiblePhase, visiblePeak); const shadowText = pass.shadowEntry ? ` Sie tritt um ${formatTime(pass.shadowEntry.time)} Uhr in den Erdschatten ein.` : ''; const magText = pass.peakMag !== null - ? ` Ihre maximale Helligkeit betraegt ${formatMag(pass.peakMag)} mag.` : ''; - return `Die Beobachtungsqualitaet von ${satName} am ${formatDateLocal(pass.aosTime)} (${formatWeekdayShort(pass.aosTime)}) um ${formatTime(pass.aosTime)} Uhr ist ${visibility.label} (${visibility.scorePercent} von 100 Punkten). ` + ? ` Ihre maximale Helligkeit beträgt ${formatMag(pass.peakMag)} mag.` : ''; + return `Der Überflug der ${satName} am ${formatDateLocal(pass.aosTime)} (${formatWeekdayShort(pass.aosTime)}) um ${formatTime(pass.aosTime)} Uhr ist ${visibility.label} (${visibility.scorePercent} von 100 Punkten). ` + `Der Überflug ist ${formatDurationLong(visiblePhase.visibleDurationMs)} lang und bis ${formatTime(visiblePhase.visibleEndTime)} Uhr zu sehen. ` - + `${pathText} Die fuer die Bewertung relevante maximale sichtbare Horizonthoehe betraegt ${visiblePeak.elevationDeg.toFixed(0)}°.` + + `${flightText} Die maximale Horizonthöhe der Raumstation ${satName} beträgt ${visiblePeak.elevationDeg.toFixed(0)}°.` + `${shadowText}${magText}`; } @@ -1084,7 +1119,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) { lastSelectedSatelliteName = satResult.name; lastSelectedPass = pass; const visibility = classifyVisibility(pass); - const observationSummary = buildObservationSummary(satResult.name, pass, visibility); + const observationSummary = buildObservationSummaryClean(satResult.name, pass, visibility); passInfoEl.innerHTML = ` From 65be4bdf9b70b6b4f405d70e52ef7c99c1bcb498 Mon Sep 17 00:00:00 2001 From: Eskimue Date: Fri, 15 May 2026 16:37:53 +0200 Subject: [PATCH 2/2] index verbessert --- public/index.php | 195 ++++++++++++++++++++++++++--------------------- 1 file changed, 107 insertions(+), 88 deletions(-) diff --git a/public/index.php b/public/index.php index 858fdfb..2ea51e7 100644 --- a/public/index.php +++ b/public/index.php @@ -119,6 +119,68 @@ if (!function_exists('planetVisibilityClass')) { return ['class' => 'planet-visibility-bad', 'label' => 'am Tag']; } } +if (!function_exists('indexPythonCandidates')) { + function indexPythonCandidates(): array + { + if (PHP_OS_FAMILY === 'Windows') { + $candidates = []; + $localAppData = getenv('LOCALAPPDATA'); + if (is_string($localAppData) && $localAppData !== '') { + $localPython = $localAppData . '\\Python\\bin\\python.exe'; + if (is_file($localPython)) { + $candidates[] = escapeshellarg($localPython); + } + } + + return array_values(array_unique(array_merge( + $candidates, + ['py -3', 'py', 'python'] + ))); + } + + return ['/usr/bin/python3', '/usr/bin/python', 'python3', 'python']; + } +} +if (!function_exists('runIndexPythonCommand')) { + function runIndexPythonCommand(string $scriptPath, array $args): array + { + putenv('PYTHONUTF8=1'); + putenv('PYTHONIOENCODING=utf-8'); + + $escapedArguments = array_map('escapeshellarg', array_merge([$scriptPath], $args)); + $lastError = 'Python konnte nicht gestartet werden.'; + + foreach (indexPythonCandidates() as $pythonBinary) { + $command = $pythonBinary . ' ' . implode(' ', $escapedArguments) . ' 2>&1'; + $output = []; + $resultCode = 0; + exec($command, $output, $resultCode); + + $joined = trim(implode("\n", $output)); + $decoded = json_decode($joined, true); + + if (is_array($decoded)) { + return [ + 'ok' => (bool) ($decoded['ok'] ?? false), + 'data' => $decoded, + 'raw' => $joined, + 'command' => $command, + ]; + } + + if ($joined !== '') { + $lastError = $joined; + } elseif ($resultCode !== 0) { + $lastError = 'Fehlercode ' . $resultCode . ' bei ' . $pythonBinary; + } + } + + return [ + 'ok' => false, + 'error' => $lastError, + ]; + } +} function idx_tleChecksum(string $line): int { @@ -227,82 +289,47 @@ if ($loggedIn) { $date = (new DateTimeImmutable('now', new DateTimeZone($tz)))->format('Y-m-d'); $scriptPath = realpath(__DIR__ . '/py/api.py'); - if (PHP_OS_FAMILY === 'Windows') { - $pythonBin = 'python'; - } elseif (file_exists('/usr/bin/python3')) { - $pythonBin = '/usr/bin/python3'; - } elseif (file_exists('/usr/bin/python')) { - $pythonBin = '/usr/bin/python'; - } else { - $pythonBin = 'python'; - } - putenv('PYTHONUTF8=1'); - putenv('PYTHONIOENCODING=utf-8'); // Sonne + Mond - $args = [ - $pythonBin, - escapeshellarg($scriptPath), - escapeshellarg('sun_moon_rise_set'), - escapeshellarg((string)$lat), - escapeshellarg((string)$lon), - escapeshellarg((string)$elev), - escapeshellarg($date), - escapeshellarg($tz), - ]; - $cmd = implode(' ', $args) . ' 2>&1'; - $out = []; - $resultCode = 0; - exec($cmd, $out, $resultCode); - $rawText = trim(implode("\n", $out)); - $raw = json_decode($rawText, true); - if (is_array($raw) && ($raw['ok'] ?? false)) { - $skyData = $raw; + $pythonResult = runIndexPythonCommand($scriptPath, [ + 'sun_moon_rise_set', + (string)$lat, + (string)$lon, + (string)$elev, + $date, + $tz, + ]); + if ($pythonResult['ok'] ?? false) { + $skyData = $pythonResult['data']; $nextDate = (new DateTimeImmutable($date, new DateTimeZone($tz)))->modify('+1 day')->format('Y-m-d'); - $argsNext = [ - $pythonBin, - escapeshellarg($scriptPath), - escapeshellarg('sun_moon_rise_set'), - escapeshellarg((string)$lat), - escapeshellarg((string)$lon), - escapeshellarg((string)$elev), - escapeshellarg($nextDate), - escapeshellarg($tz), - ]; - $cmdNext = implode(' ', $argsNext) . ' 2>&1'; - $outNext = []; - $resultCodeNext = 0; - exec($cmdNext, $outNext, $resultCodeNext); - $rawTextNext = trim(implode("\n", $outNext)); - $rawNext = json_decode($rawTextNext, true); - if (is_array($rawNext) && ($rawNext['ok'] ?? false)) { - $skyDataNext = $rawNext; + $pythonResultNext = runIndexPythonCommand($scriptPath, [ + 'sun_moon_rise_set', + (string)$lat, + (string)$lon, + (string)$elev, + $nextDate, + $tz, + ]); + if ($pythonResultNext['ok'] ?? false) { + $skyDataNext = $pythonResultNext['data']; } } else { - $pyError = $raw['error'] ?? ($rawText !== '' ? $rawText : 'Fehler beim Berechnen der Aufgangszeiten.'); + $pyError = $pythonResult['data']['error'] ?? $pythonResult['error'] ?? 'Fehler beim Berechnen der Aufgangszeiten.'; } // Planeten - $args2 = [ - $pythonBin, - escapeshellarg($scriptPath), - escapeshellarg('planet_rise_set'), - escapeshellarg((string)$lat), - escapeshellarg((string)$lon), - escapeshellarg((string)$elev), - escapeshellarg($date), - escapeshellarg($tz), - ]; - $cmd2 = implode(' ', $args2) . ' 2>&1'; - $out2 = []; - $resultCode2 = 0; - exec($cmd2, $out2, $resultCode2); - $rawText2 = trim(implode("\n", $out2)); - $raw2 = json_decode($rawText2, true); - if (is_array($raw2) && ($raw2['ok'] ?? false)) { - $planetData = $raw2; + $pythonResult2 = runIndexPythonCommand($scriptPath, [ + 'planet_rise_set', + (string)$lat, + (string)$lon, + (string)$elev, + $date, + $tz, + ]); + if ($pythonResult2['ok'] ?? false) { + $planetData = $pythonResult2['data']; } elseif ($pyError === null) { - $pyError = $raw2['error'] ?? ($rawText2 !== '' ? $rawText2 : 'Fehler beim Berechnen der Planetenzeiten.'); + $pyError = $pythonResult2['data']['error'] ?? $pythonResult2['error'] ?? 'Fehler beim Berechnen der Planetenzeiten.'; } // Satelliten-Überflüge heute — Favorit zuerst, dann alle gemerkten @@ -341,28 +368,20 @@ if ($loggedIn) { ], $userSats), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); $satsPayload = base64_encode((string) $satsJson); - $args3 = [ - $pythonBin, - escapeshellarg($scriptPath), - escapeshellarg('satellite_passes'), - escapeshellarg((string)$lat), - escapeshellarg((string)$lon), - escapeshellarg((string)$elev), - escapeshellarg($tz), - escapeshellarg($dayStart->format('Y-m-d\TH:i:s\Z')), - escapeshellarg($dayEnd->format('Y-m-d\TH:i:s\Z')), - escapeshellarg($satsPayload), - ]; - $cmd3 = implode(' ', $args3) . ' 2>&1'; - $out3 = []; - $resultCode3 = 0; - exec($cmd3, $out3, $resultCode3); - $rawText3 = trim(implode("\n", $out3)); - $raw3 = json_decode($rawText3, true); - if (is_array($raw3) && ($raw3['ok'] ?? false)) { - $satData = $raw3; + $pythonResult3 = runIndexPythonCommand($scriptPath, [ + 'satellite_passes', + (string)$lat, + (string)$lon, + (string)$elev, + $tz, + $dayStart->format('Y-m-d\TH:i:s\Z'), + $dayEnd->format('Y-m-d\TH:i:s\Z'), + $satsPayload, + ]); + if ($pythonResult3['ok'] ?? false) { + $satData = $pythonResult3['data']; } else { - $satError = $raw3['error'] ?? ($rawText3 !== '' ? $rawText3 : 'Fehler beim Berechnen der Satellitenüberflüge.'); + $satError = $pythonResult3['data']['error'] ?? $pythonResult3['error'] ?? 'Fehler beim Berechnen der Satellitenüberflüge.'; } } }