diff --git a/.claude/settings.local.json b/.claude/settings.local.json index e7e1a8d..28489b3 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -16,7 +16,33 @@ "Bash(php --version)", "Bash(/c/Users/ich/Downloads/php/php -S localhost:8080 -t public)", "Bash(/c/Users/ich/Downloads/php/php -m)", - "Bash(/c/Users/ich/Downloads/php/php --ini)" + "Bash(/c/Users/ich/Downloads/php/php --ini)", + "Bash(php -S localhost:8080 -t c:/Users/HP/source/repos/skyview.astronomiemuseum.de/public)", + "Bash(where php:*)", + "Bash(/e/php/php.exe -S localhost:8080 -t \"c:/Users/HP/source/repos/skyview.astronomiemuseum.de/public\")", + "Bash(/e/php/php.exe -m)", + "Read(//e/php/ext//**)", + "Bash(/e/php/php.exe --ini)", + "Bash(sed -i 's|^;extension_dir = \"ext\"|extension_dir = \"ext\"|' /e/php/php.ini)", + "Bash(sed -i 's|^;extension=pdo_mysql|extension=pdo_mysql|' /e/php/php.ini)", + "Bash(sed -i 's|^;extension=mysqli|extension=mysqli|' /e/php/php.ini)", + "Bash(taskkill //F //IM php.exe)", + "Bash(/usr/bin/python3 --version)", + "Bash(/usr/bin/python --version)", + "Read(//c/Users/HP/AppData/Local/Programs/**)", + "Bash(ls /c/Python*/python.exe)", + "Bash(/c/Users/HP/AppData/Local/Python/bin/python.exe --version)", + "Bash(/c/Users/HP/AppData/Local/Python/bin/python.exe c:/Users/HP/source/repos/skyview.astronomiemuseum.de/public/py/api.py sun_moon_rise_set 50.35 11.17 360 2026-04-01 Europe/Berlin)", + "Bash(/c/Users/HP/AppData/Local/Python/bin/python.exe -c \"from zoneinfo import ZoneInfo; ZoneInfo\\('Europe/Berlin'\\)\")", + "Bash(/c/Users/HP/AppData/Local/Python/bin/python.exe -m pip install tzdata)", + "Bash(/e/php/php.exe -r ':*)", + "Bash(/e/php/php.exe -r \"echo ini_get\\('disable_functions'\\);\")", + "Bash(/e/php/php.exe -S localhost:8080 -r \"echo ini_get\\('disable_functions'\\);\")", + "Bash(cp /tmp/debug_exec.php \"c:/Users/HP/source/repos/skyview.astronomiemuseum.de/public/debug_exec.php\")", + "Bash(/e/php/php.exe -l \"c:/Users/HP/source/repos/skyview.astronomiemuseum.de/public/index.php\")", + "Bash(/e/php/php.exe \"c:/Users/HP/source/repos/skyview.astronomiemuseum.de/public/index.php\")", + "Bash(curl -s -o /dev/null -w \"%{http_code}\" http://localhost:8080/index.php)", + "Bash(curl -s http://localhost:8080/index.php)" ] } } diff --git a/public/css/style.css b/public/css/style.css index 519b337..8440eaa 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -828,6 +828,58 @@ footer a:hover { color: var(--gold); } padding-right: 1rem; } +/* Kompakte Auf-/Untergangs-Übersicht (Startseite) */ +.riseset-card { display: inline-block; } + +.riseset-grid { + display: grid; + grid-template-columns: auto auto auto; + column-gap: 1.2rem; + row-gap: 0.18rem; + align-items: baseline; + font-size: 0.88rem; +} + +.riseset-label { color: var(--text-dim); white-space: nowrap; } +.riseset-val { font-variant-numeric: tabular-nums; white-space: nowrap; } + +.riseset-main.riseset-label, +.riseset-main.riseset-val { + color: var(--gold-pale); + font-weight: 600; + font-size: 0.92rem; +} + +.riseset-moon.riseset-label, +.riseset-moon.riseset-val { + color: var(--cyan); + font-weight: 600; + font-size: 0.92rem; +} + +.riseset-dim.riseset-label, +.riseset-dim.riseset-val { + color: var(--text-dim); + font-size: 0.8rem; +} + +.riseset-sep { + grid-column: 1 / -1; + height: 0; + border-top: 1px solid var(--border); + margin: 0.3rem 0; +} + +.sat-passes-grid { + grid-template-columns: auto auto auto; +} + +.sat-passes-grid small { + color: var(--text-dim); + font-size: 0.75em; + margin-left: 0.2em; +} + /* --- Aktions-Zelle (Buttons in Tabellenzeilen) ------------ */ .action-cell { diff --git a/public/index.php b/public/index.php index 0a4c27e..de1adb7 100644 --- a/public/index.php +++ b/public/index.php @@ -1,58 +1,236 @@ PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - ] - ); - - $userId = (int)$_SESSION['user_id']; - $displayName = $_SESSION['display_name'] ?? ''; - $username = $_SESSION['username'] ?? ''; - $role = $_SESSION['role'] ?? 'user'; - - $stmtLocation = $pdo->prepare(" - SELECT - `name`, - `latitude`, - `longitude`, - `elevation`, - `timezone` - FROM `app_user_locations` - WHERE `user_id` = :user_id - AND `is_default` = 1 - LIMIT 1 - "); - $stmtLocation->execute([':user_id' => $userId]); - $defaultLocation = $stmtLocation->fetch(); +if (!function_exists('h')) { + function h(?string $value): string { return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); } +} +if (!function_exists('risesetTime')) { + function risesetTime(array $ev, string $key): string { return (!empty($ev[$key]['found'])) ? htmlspecialchars($ev[$key]['local_time'], ENT_QUOTES, 'UTF-8') : '—'; } +} +if (!function_exists('fmtLocalTime')) { + function fmtLocalTime(string $utcIso, string $tz): string { return (new DateTime($utcIso))->setTimezone(new DateTimeZone($tz))->format('H:i'); } +} +if (!function_exists('azDir')) { + function azDir(float $az): string { $dirs = ['N','NO','O','SO','S','SW','W','NW']; return $dirs[(int)round($az / 45) % 8]; } } -function h(?string $value): string +function idx_tleChecksum(string $line): int { - return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); + $sum = 0; + for ($i = 0; $i < 68; $i++) { + $c = $line[$i]; + if (ctype_digit($c)) $sum += (int)$c; + elseif ($c === '-') $sum += 1; + } + return $sum % 10; +} +function idx_tleExp(float $val): string +{ + if (abs($val) < 1e-99) return ' 00000-0'; + $sign = $val >= 0 ? '+' : '-'; + $abs = abs($val); + $exp = (int)floor(log10($abs)) + 1; + $mantissa = (int)round($abs / pow(10, $exp) * 1e5); + if ($mantissa >= 100000) { $mantissa = 10000; $exp++; } + return sprintf('%s%05d%s%d', $sign, $mantissa, $exp >= 0 ? '+' : '-', abs($exp)); +} +function idx_tleMmDot(float $val): string +{ + return sprintf('%s.%08d', $val >= 0 ? ' ' : '-', (int)round(abs($val) * 1e8)); +} +function idx_tleEpoch(string $epochStr): string +{ + $dt = new DateTimeImmutable($epochStr, new DateTimeZone('UTC')); + $yy = (int)$dt->format('Y') % 100; + $jan1 = new DateTimeImmutable($dt->format('Y') . '-01-01 00:00:00', new DateTimeZone('UTC')); + $day = ((float)$dt->format('U') - (float)$jan1->format('U')) / 86400.0 + 1.0; + return sprintf('%02d%012.8f', $yy, $day); +} +function idx_tleIntlDesig(?string $id): string +{ + if (!$id) return str_repeat(' ', 8); + if (preg_match('/^(\d{4})-(\d+)([A-Z]*)$/i', trim($id), $m)) { + $yy = substr($m[1], 2, 2); + return substr(str_pad($yy . str_pad($m[2], 3, '0', STR_PAD_LEFT) . str_pad(strtoupper($m[3]), 3), 8), 0, 8); + } + return str_pad(substr($id, 0, 8), 8); +} +function buildTleLine1(array $sat, array $tle): string +{ + $line = sprintf('1 %05d%1s %-8s %14s %10s %8s %8s %1d %4d', + (int)$sat['norad_cat_id'], + in_array($sat['classification_type'] ?? '', ['U','C','S'], true) ? $sat['classification_type'] : 'U', + idx_tleIntlDesig($sat['object_id'] ?? null), + idx_tleEpoch((string)$tle['epoch']), + idx_tleMmDot((float)($tle['mean_motion_dot'] ?? 0)), + idx_tleExp((float)($tle['mean_motion_ddot'] ?? 0)), + idx_tleExp((float)($tle['bstar'] ?? 0)), + (int)($tle['ephemeris_type'] ?? 0), + (int)($tle['element_set_no'] ?? 0) + ); + $line = str_pad($line, 68); + return $line . idx_tleChecksum($line); +} +function buildTleLine2(array $sat, array $tle): string +{ + $ecc = (float)($tle['eccentricity'] ?? 0); + $line = sprintf('2 %05d %8.4f %8.4f %7s %8.4f %8.4f %11.8f%5d', + (int)$sat['norad_cat_id'], + (float)($tle['inclination'] ?? 0), + (float)($tle['ra_of_asc_node'] ?? 0), + substr(sprintf('%.7f', $ecc), 2), + (float)($tle['arg_of_pericenter'] ?? 0), + (float)($tle['mean_anomaly'] ?? 0), + (float)($tle['mean_motion'] ?? 0), + (int)($tle['rev_at_epoch'] ?? 0) + ); + $line = str_pad($line, 68); + return $line . idx_tleChecksum($line); +} + +// Standort und astronomische Daten nur wenn eingeloggt und Standort vorhanden +$skyData = null; +$planetData = null; +$satData = null; +$pyError = null; + +if ($loggedIn) { + // header.php lädt $currentLocation – wir brauchen ihn vorab + $config = require __DIR__ . '/../config/database.php'; + $pdo = new PDO( + sprintf('mysql:host=%s;dbname=%s;charset=%s', $config['host'], $config['dbname'], $config['charset'] ?? 'utf8mb4'), + $config['user'], + $config['pass'], + [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC] + ); + + $stmtLoc = $pdo->prepare( + 'SELECT name, latitude, longitude, elevation, timezone + FROM app_user_locations WHERE user_id = ? AND is_default = 1 LIMIT 1' + ); + $stmtLoc->execute([(int)$_SESSION['user_id']]); + $defaultLocation = $stmtLoc->fetch() ?: null; + + if ($defaultLocation) { + $lat = (float)$defaultLocation['latitude']; + $lon = (float)$defaultLocation['longitude']; + $elev = (float)($defaultLocation['elevation'] ?? 0); + $tz = $defaultLocation['timezone'] ?? 'Europe/Berlin'; + $date = date('Y-m-d'); + + $scriptPath = realpath(__DIR__ . '/py/api.py'); + if (file_exists('/usr/bin/python3')) { + $pythonBin = '/usr/bin/python3'; + } elseif (file_exists('/usr/bin/python')) { + $pythonBin = '/usr/bin/python'; + } else { + $pythonBin = 'C:\\Users\\HP\\AppData\\Local\\Python\\bin\\python.exe'; + } + 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 = []; + exec($cmd, $out); + $raw = json_decode(implode('', $out), true); + if (is_array($raw) && ($raw['ok'] ?? false)) { + $skyData = $raw; + } else { + $pyError = $raw['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 = []; + exec($cmd2, $out2); + $raw2 = json_decode(implode('', $out2), true); + if (is_array($raw2) && ($raw2['ok'] ?? false)) { + $planetData = $raw2; + } elseif ($pyError === null) { + $pyError = $raw2['error'] ?? 'Fehler beim Berechnen der Planetenzeiten.'; + } + + // Satelliten-Überflüge heute — Favorit zuerst, dann alle gemerkten + $stmtSat = $pdo->prepare( + 'SELECT s.id, s.norad_cat_id, s.object_name, s.object_type, + s.object_id, s.classification_type, + t.epoch, t.mean_motion, t.eccentricity, t.inclination, + t.ra_of_asc_node, t.arg_of_pericenter, t.mean_anomaly, + t.ephemeris_type, t.element_set_no, t.rev_at_epoch, + t.bstar, t.mean_motion_dot, t.mean_motion_ddot, + us.is_favorite + FROM app_user_satellites us + JOIN sat_satellites s ON s.id = us.satellite_id + JOIN sat_tle_current t ON t.satellite_id = s.id + WHERE us.user_id = ? + AND t.epoch IS NOT NULL + ORDER BY us.is_favorite DESC, s.object_name ASC + LIMIT 3' + ); + $stmtSat->execute([(int)$_SESSION['user_id']]); + $userSats = $stmtSat->fetchAll(); + + if ($userSats) { + $tzObj = new DateTimeZone($tz); + $dayStart = new DateTime('today', $tzObj); + $dayStart->setTimezone(new DateTimeZone('UTC')); + $dayEnd = new DateTime('tomorrow', $tzObj); + $dayEnd->setTimezone(new DateTimeZone('UTC')); + $satsJson = json_encode(array_map(fn($r) => [ + 'id' => $r['id'], + 'norad_cat_id' => $r['norad_cat_id'], + 'object_name' => $r['object_name'], + 'tle_line1' => buildTleLine1($r, $r), + 'tle_line2' => buildTleLine2($r, $r), + 'is_favorite' => (bool)$r['is_favorite'], + ], $userSats), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + + $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($satsJson), + ]; + $cmd3 = implode(' ', $args3) . ' 2>&1'; + $out3 = []; + exec($cmd3, $out3); + $raw3 = json_decode(implode('', $out3), true); + if (is_array($raw3) && ($raw3['ok'] ?? false)) { + $satData = $raw3; + } + } + } } ?> @@ -70,36 +248,87 @@ function h(?string $value): string -
- Willkommen zurück, = h($displayName !== '' ? $displayName : $username) ?>. -
+ +Noch kein Standort hinterlegt. Jetzt anlegen
+ ++ Aufgangs- und Untergangszeiten für + = h($defaultLocation['name']) ?> + — = h(date('d.m.Y')) ?> +
-| Benutzername | = h($username) ?> |
| Rolle | = h($role) ?> |
| Name | = h($defaultLocation['name']) ?> |
| Breitengrad | = h((string)$defaultLocation['latitude']) ?> |
| Längengrad | = h((string)$defaultLocation['longitude']) ?> |
| Höhe | = h($defaultLocation['elevation'] !== null ? (string)$defaultLocation['elevation'] . ' m' : '—') ?> |
| Zeitzone | = h($defaultLocation['timezone']) ?> |
Heute keine Überflüge.
-Noch kein Standort hinterlegt. Jetzt anlegen
+