PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ] ); $searchQuery = trim((string) ($_GET['q'] ?? '')); $showMode = trim((string) ($_GET['mode'] ?? 'all')); $allowedModes = ['all', 'active']; if (!in_array($showMode, $allowedModes, true)) { $showMode = 'all'; } function h(?string $value): string { return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8'); } function normalizeSolarLongitude(float $value): float { $normalized = fmod($value, 360.0); if ($normalized < 0.0) { $normalized += 360.0; } return $normalized; } function currentSolarLongitude(DateTimeImmutable $dateTime): float { $julianDay = ((float) $dateTime->format('U')) / 86400.0 + 2440587.5; $daysSinceJ2000 = $julianDay - 2451545.0; $meanLongitude = normalizeSolarLongitude(280.46 + 0.9856474 * $daysSinceJ2000); $meanAnomalyRad = deg2rad(normalizeSolarLongitude(357.528 + 0.9856003 * $daysSinceJ2000)); return normalizeSolarLongitude( $meanLongitude + 1.915 * sin($meanAnomalyRad) + 0.020 * sin(2.0 * $meanAnomalyRad) ); } function fetchCurrentSolarLongitude(DateTimeZone $displayTimezone): ?float { $pythonResult = runPythonApi('current_solar_longitude', [$displayTimezone->getName()]); if (!$pythonResult['ok']) { return null; } $solarLongitude = $pythonResult['data']['result']['solar_longitude_deg'] ?? null; if (!is_numeric($solarLongitude)) { return null; } return normalizeSolarLongitude((float) $solarLongitude); } function runPythonApi(string $action, array $args): array { $scriptPath = __DIR__ . '/py/api.py'; $pythonCandidates = PHP_OS_FAMILY === 'Windows' ? ['python', 'py'] : ['/usr/bin/python3', '/usr/bin/python', 'python3', 'python']; $escapedArguments = array_map('escapeshellarg', array_merge([$scriptPath, $action], $args)); $lastError = 'Python konnte nicht gestartet werden.'; foreach ($pythonCandidates 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 isSolarLongitudeInRange(float $current, ?float $start, ?float $finish): bool { if ($start === null || $finish === null) { return false; } $current = normalizeSolarLongitude($current); $start = normalizeSolarLongitude($start); $finish = normalizeSolarLongitude($finish); if ($start <= $finish) { return $current >= $start && $current <= $finish; } return $current >= $start || $current <= $finish; } function solarLongitudeToUtcDate(?float $targetLongitude, int $year, DateTimeZone $displayTimezone): ?DateTimeImmutable { if ($targetLongitude === null) { return null; } static $cache = []; $cacheKey = $year . '|' . $displayTimezone->getName() . '|' . number_format((float) $targetLongitude, 6, '.', ''); if (array_key_exists($cacheKey, $cache)) { return $cache[$cacheKey]; } $pythonResult = runPythonApi('solar_longitude_to_datetime', [ (string) $targetLongitude, (string) $year, $displayTimezone->getName(), ]); if (!$pythonResult['ok']) { $cache[$cacheKey] = null; return null; } $resultData = $pythonResult['data']; $utcIso = trim((string) ($resultData['result']['utc_iso'] ?? '')); if ($utcIso === '') { $cache[$cacheKey] = null; return null; } try { $cache[$cacheKey] = new DateTimeImmutable($utcIso); } catch (Throwable $e) { $cache[$cacheKey] = null; return null; } return $cache[$cacheKey]; } function warmSolarLongitudeDateCache(array $showers, int $year, DateTimeZone $displayTimezone): void { $uniqueLongitudes = []; foreach ($showers as $shower) { foreach (['active_start_sol', 'active_finish_sol', 'peak_sol'] as $field) { if (!isset($shower[$field]) || $shower[$field] === null || $shower[$field] === '') { continue; } $key = number_format((float) $shower[$field], 6, '.', ''); $uniqueLongitudes[$key] = (float) $shower[$field]; } } foreach ($uniqueLongitudes as $solarLongitude) { solarLongitudeToUtcDate($solarLongitude, $year, $displayTimezone); } } function formatNullableNumber(mixed $value, int $decimals = 1, string $suffix = ''): string { if ($value === null || $value === '') { return '-'; } return number_format((float) $value, $decimals, ',', '.') . $suffix; } function formatSolarLongitudeWithDate(?float $solarLongitude, int $year, DateTimeZone $displayTimezone): string { if ($solarLongitude === null) { return '-'; } $label = number_format((float) $solarLongitude, 2, ',', '.') . '°'; $date = solarLongitudeToUtcDate($solarLongitude, $year, $displayTimezone); if (!$date instanceof DateTimeImmutable) { return $label; } return $label . '
' . htmlspecialchars($date->setTimezone($displayTimezone)->format('d.m.Y'), ENT_QUOTES, 'UTF-8') . ''; } $displayTimezone = new DateTimeZone(date_default_timezone_get() ?: 'Europe/Berlin'); $displayYear = (int) (new DateTimeImmutable('now', $displayTimezone))->format('Y'); $todaySolarLongitude = fetchCurrentSolarLongitude($displayTimezone) ?? currentSolarLongitude(new DateTimeImmutable('now', new DateTimeZone('UTC'))); $sql = " SELECT `id`, `shower_code`, `iau_no`, `designation`, `speed_kms`, `radiant_alpha_deg`, `radiant_delta_deg`, `parent_object`, `population_index`, `active_start_sol`, `active_finish_sol`, `peak_sol`, `zhr`, `zhr_variable`, `has_yearly_variants` FROM `meteorshowers` WHERE 1 = 1 "; $params = []; if ($searchQuery !== '') { $sql .= " AND ( `shower_code` LIKE :query OR `designation` LIKE :query OR `parent_object` LIKE :query ) "; $params[':query'] = '%' . $searchQuery . '%'; } $sql .= " ORDER BY COALESCE(`zhr`, 0) DESC, `peak_sol` IS NULL ASC, `peak_sol` ASC, `designation` ASC "; $stmt = $pdo->prepare($sql); $stmt->execute($params); $allShowers = $stmt->fetchAll(); $showers = []; $activeCount = 0; foreach ($allShowers as $shower) { $isActive = isSolarLongitudeInRange( $todaySolarLongitude, isset($shower['active_start_sol']) ? (float) $shower['active_start_sol'] : null, isset($shower['active_finish_sol']) ? (float) $shower['active_finish_sol'] : null ); if ($isActive) { $activeCount++; } $shower['is_active_now'] = $isActive; if ($showMode === 'active' && !$isActive) { continue; } $showers[] = $shower; } warmSolarLongitudeDateCache($showers, $displayYear, $displayTimezone); $topZhr = null; foreach ($allShowers as $shower) { if ($shower['zhr'] !== null) { $topZhr = (int) $shower['zhr']; break; } } require __DIR__ . '/header.php'; ?> 'Importierte Meteorstroeme', 'value' => (string) count($allShowers)], ['label' => 'Davon aktuell aktiv', 'value' => (string) $activeCount], ['label' => 'Hoechste ZHR', 'value' => $topZhr !== null ? (string) $topZhr : '-'], ['label' => 'Aktuelle solare Laenge', 'value' => number_format($todaySolarLongitude, 2, ',', '.') . '°'], ['label' => 'Datumsumrechnung', 'value' => (string) $displayYear], ]; ?>
Zuruecksetzen

Liste der Meteorstroeme

Die Gradangaben in Aktivitaet und Maximum sind Werte der solaren Laenge. Zusaetzlich wird dafuer ein Kalenderdatum im Jahr angezeigt.

Keine Meteorstroeme fuer diese Auswahl gefunden.

Status Code IAU Bezeichnung ZHR Aktivitaet (solare Laenge) Maximum (solare Laenge) Radiant vg Mutterkoerper
Aktiv Inaktiv
mit Jahresvarianten
bis RA
Dec