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 idx_tleChecksum(string $line): int { $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; $satError = 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 = (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; } else { $pyError = $raw['error'] ?? ($rawText !== '' ? $rawText : '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; } elseif ($pyError === null) { $pyError = $raw2['error'] ?? ($rawText2 !== '' ? $rawText2 : '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); $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; } else { $satError = $raw3['error'] ?? ($rawText3 !== '' ? $rawText3 : 'Fehler beim Berechnen der Satellitenüberflüge.'); } } } } ?>

Das Universum erwartet dich

Satellitentracking, Himmelsvorschau und Astronomiewerkzeuge für Sonneberg und darüber hinaus.

Anmelden Registrieren

Noch kein Standort hinterlegt. Jetzt anlegen

Aufgangs- und Untergangszeiten für — format('d.m.Y')) ?>

Heute

☀ Sonne ↑ ↓ Bürgerl. Nautisch Astronom. ☽ Mond ↑ ↓ ↑ ↓

☆ Satellit

strcmp($a['aos_utc'], $b['aos_utc'])); ?>

☆

Heute keine Überflüge.

AOS LOS Max °