= '0' && $value <= '9') { return (int) $value; } $upper = strtoupper($value); if ($upper >= 'A' && $upper <= 'V') { return ord($upper) - ord('A') + 10; } return null; } function decodeMpcPackedDate(?string $packed): ?string { if ($packed === null || strlen($packed) !== 5) { return null; } $centuryMap = [ 'I' => 1800, 'J' => 1900, 'K' => 2000, ]; $centuryCode = strtoupper($packed[0]); if (!isset($centuryMap[$centuryCode])) { return null; } $yearPart = substr($packed, 1, 2); if (!ctype_digit($yearPart)) { return null; } $month = decodeMpcPackedDateChar($packed[3]); $day = decodeMpcPackedDateChar($packed[4]); if ($month === null || $day === null || $month < 1 || $month > 12 || $day < 1 || $day > 31) { return null; } return sprintf('%04d-%02d-%02d', $centuryMap[$centuryCode] + (int) $yearPart, $month, $day); } function formatMinorplanetDisplayName(array $minorplanet): string { $designation = trim((string) ($minorplanet['designation'] ?? $minorplanet['designation_text'] ?? '')); $designation = preg_replace('/^\(\d+\)\s*/', '', $designation) ?? $designation; $number = $minorplanet['number'] ?? $minorplanet['mp_number'] ?? null; if ($number !== null && $number !== '') { return '(' . (int) $number . ') ' . $designation; } return $designation !== '' ? $designation : (string) ($minorplanet['packedDesignation'] ?? $minorplanet['desig_packed'] ?? '--'); } if ($loggedIn) { try { $config = require __DIR__ . '/../config/database.php'; $dsn = sprintf( 'mysql:host=%s;dbname=%s;charset=%s', $config['host'], $config['dbname'], $config['charset'] ?? 'utf8mb4' ); $pdo = new PDO( $dsn, $config['user'], $config['pass'], [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ] ); $stmtTrackedMinorplanets = $pdo->prepare(" SELECT mp.`id`, mp.`mp_number`, mp.`designation_text`, mp.`desig_packed`, mp.`epoch_packed`, mp.`mean_anomaly_deg`, mp.`arg_perihelion_deg`, mp.`ascending_node_deg`, mp.`inclination_deg`, mp.`eccentricity`, mp.`mean_motion_deg_per_day`, mp.`semimajor_axis_au`, mp.`absolute_magnitude_h`, mp.`slope_g`, aum.`is_favorite` FROM `app_user_minorplanets` aum INNER JOIN `minorplanets_mpc` mp ON mp.`id` = aum.`minorplanet_id` WHERE aum.`user_id` = :user_id ORDER BY aum.`is_favorite` DESC, mp.`mp_number` IS NULL ASC, mp.`mp_number` ASC, mp.`designation_text` ASC "); $stmtTrackedMinorplanets->execute([ ':user_id' => (int) $_SESSION['user_id'], ]); foreach ($stmtTrackedMinorplanets->fetchAll() as $minorplanet) { $trackedMinorplanetsForJs[] = [ 'id' => (int) $minorplanet['id'], 'number' => $minorplanet['mp_number'] !== null ? (int) $minorplanet['mp_number'] : null, 'designation' => (string) $minorplanet['designation_text'], 'packedDesignation' => (string) $minorplanet['desig_packed'], 'epochPacked' => (string) $minorplanet['epoch_packed'], 'epochDateIso' => decodeMpcPackedDate($minorplanet['epoch_packed']), 'meanAnomalyDeg' => (float) $minorplanet['mean_anomaly_deg'], 'argPerihelionDeg' => (float) $minorplanet['arg_perihelion_deg'], 'ascendingNodeDeg' => (float) $minorplanet['ascending_node_deg'], 'inclinationDeg' => (float) $minorplanet['inclination_deg'], 'eccentricity' => (float) $minorplanet['eccentricity'], 'meanMotionDegPerDay' => (float) $minorplanet['mean_motion_deg_per_day'], 'semimajorAxisAu' => (float) $minorplanet['semimajor_axis_au'], 'absoluteMagnitudeH' => $minorplanet['absolute_magnitude_h'] !== null ? (float) $minorplanet['absolute_magnitude_h'] : null, 'slopeG' => $minorplanet['slope_g'] !== null ? (float) $minorplanet['slope_g'] : null, 'isFavorite' => (bool) $minorplanet['is_favorite'], ]; } } catch (Throwable $e) { $trackedMinorplanetsForJs = []; } } $pageTitle = 'Sonnensystem - AstroTools'; $bodyClass = 'solarsystem-page'; ?>