PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ] ); $loggedIn = isset($_SESSION['user_id']); $pageTitle = 'T CrB Lichtkurve'; $headerIntroPre = 'Variabler Stern'; $headerIntroTitle = 'T CrB Lichtkurve'; $headerIntroSub = 'AAVSO-WebObs-Daten mit waehlbaren Filtern in einem gemeinsamen Plotly-Diagramm.'; $errors = []; $eruptionIntervalDays = 227.5687; $lastEruptionUtc = new DateTimeImmutable('1946-02-09 00:00:00', new DateTimeZone('UTC')); $todayUtc = new DateTimeImmutable('now', new DateTimeZone('UTC')); $secondsPerDay = 86400; $eruptionIntervalSeconds = (int) round($eruptionIntervalDays * $secondsPerDay); $elapsedSecondsSinceLastEruption = max(0, $todayUtc->getTimestamp() - $lastEruptionUtc->getTimestamp()); $elapsedIntervals = (int) floor($elapsedSecondsSinceLastEruption / $eruptionIntervalSeconds); $nextPossibleEruptionUtc = $lastEruptionUtc->modify('+' . (($elapsedIntervals + 1) * $eruptionIntervalSeconds) . ' seconds'); $daysUntilNextEruption = ($nextPossibleEruptionUtc->getTimestamp() - $todayUtc->getTimestamp()) / $secondsPerDay; function h(?string $value): string { return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8'); } function formatGermanDate(?string $value, bool $includeTime = true): string { if ($value === null || trim($value) === '') { return '—'; } try { $date = new DateTimeImmutable($value, new DateTimeZone('UTC')); } catch (Exception) { return $value; } return $date->format($includeTime ? 'd.m.Y H:i:s' : 'd.m.Y'); } $filterRows = $pdo->query(" SELECT `filter_code`, COUNT(*) AS `entry_count` FROM `aavso_tcrb_observations` WHERE `filter_code` IS NOT NULL AND `filter_code` <> '' GROUP BY `filter_code` ORDER BY `filter_code` ASC ")->fetchAll(); $availableFilters = []; foreach ($filterRows as $filterRow) { $filterCode = trim((string) ($filterRow['filter_code'] ?? '')); if ($filterCode === '') { continue; } $availableFilters[] = [ 'code' => $filterCode, 'count' => (int) ($filterRow['entry_count'] ?? 0), ]; } $selectedFilters = array_values(array_unique(array_filter(array_map( static fn ($value): string => trim((string) $value), is_array($_GET['filters'] ?? null) ? $_GET['filters'] : [] ), static fn (string $value): bool => $value !== ''))); if (empty($selectedFilters)) { $selectedFilters = in_array('V', array_column($availableFilters, 'code'), true) ? ['V'] : array_column($availableFilters, 'code'); } $selectedFilters = array_values(array_intersect($selectedFilters, array_column($availableFilters, 'code'))); if (empty($availableFilters)) { $errors[] = 'In der Tabelle aavso_tcrb_observations sind noch keine T-CrB-Daten vorhanden.'; } $observations = []; $summary = [ 'count' => 0, 'min_utc' => null, 'max_utc' => null, 'min_mag' => null, 'max_mag' => null, ]; if (empty($errors) && !empty($availableFilters)) { $queryFilters = array_column($availableFilters, 'code'); $placeholders = implode(',', array_fill(0, count($queryFilters), '?')); $stmt = $pdo->prepare(" SELECT `observed_at_utc`, `jd`, `magnitude`, `magnitude_error`, `filter_code`, `observer_code`, `notes_raw` FROM `aavso_tcrb_observations` WHERE `filter_code` IN ({$placeholders}) ORDER BY `observed_at_utc` ASC, `jd` ASC "); $stmt->execute($queryFilters); $rows = $stmt->fetchAll(); foreach ($rows as $row) { $observedAtUtc = (string) ($row['observed_at_utc'] ?? ''); $magnitude = isset($row['magnitude']) ? (float) $row['magnitude'] : null; if ($observedAtUtc === '' || $magnitude === null) { continue; } $observations[] = [ 'observed_at_utc' => $observedAtUtc, 'jd' => isset($row['jd']) ? (float) $row['jd'] : null, 'magnitude' => $magnitude, 'magnitude_error' => $row['magnitude_error'] !== null ? (float) $row['magnitude_error'] : null, 'filter_code' => (string) ($row['filter_code'] ?? ''), 'observer_code' => (string) ($row['observer_code'] ?? ''), 'notes_raw' => (string) ($row['notes_raw'] ?? ''), ]; $summary['count']++; $summary['min_utc'] = $summary['min_utc'] === null || $observedAtUtc < $summary['min_utc'] ? $observedAtUtc : $summary['min_utc']; $summary['max_utc'] = $summary['max_utc'] === null || $observedAtUtc > $summary['max_utc'] ? $observedAtUtc : $summary['max_utc']; $summary['min_mag'] = $summary['min_mag'] === null || $magnitude < $summary['min_mag'] ? $magnitude : $summary['min_mag']; $summary['max_mag'] = $summary['max_mag'] === null || $magnitude > $summary['max_mag'] ? $magnitude : $summary['max_mag']; } } if (empty($errors) && empty($observations)) { $errors[] = 'Fuer die ausgewaehlten Filter wurden keine Daten gefunden.'; } $filterMetadata = []; $filterMetadataPath = __DIR__ . '/../config/filter_colors.json'; if (is_file($filterMetadataPath)) { $filterMetadataRaw = file_get_contents($filterMetadataPath); $filterMetadataDecoded = is_string($filterMetadataRaw) ? json_decode($filterMetadataRaw, true) : null; if (is_array($filterMetadataDecoded)) { $filterMetadata = $filterMetadataDecoded; } } $filterPalette = []; $filterDescriptions = []; foreach ($filterMetadata as $filterCode => $filterConfig) { if (!is_array($filterConfig)) { continue; } $filterPalette[$filterCode] = (string) ($filterConfig['color'] ?? ''); $filterDescriptions[$filterCode] = (string) ($filterConfig['description'] ?? $filterCode); } $chartDatasets = []; foreach ($availableFilters as $availableFilter) { $filterCode = $availableFilter['code']; $chartDatasets[$filterCode] = [ 'filter' => $filterCode, 'color' => $filterPalette[$filterCode] ?? sprintf('#%06X', crc32($filterCode) & 0xFFFFFF), 'points' => [], ]; } foreach ($observations as $observation) { $filterCode = $observation['filter_code']; if (!isset($chartDatasets[$filterCode])) { continue; } $chartDatasets[$filterCode]['points'][] = $observation; } $chartDatasets = array_values(array_filter($chartDatasets, static fn (array $dataset): bool => !empty($dataset['points']))); $plotlyTraces = []; foreach ($chartDatasets as $dataset) { $xValues = []; $yValues = []; $errorValues = []; $customData = []; $hasErrors = false; foreach ($dataset['points'] as $point) { $xValues[] = (string) $point['observed_at_utc']; $yValues[] = (float) $point['magnitude']; $errorValue = $point['magnitude_error'] !== null ? (float) $point['magnitude_error'] : 0.0; $errorValues[] = $errorValue; if ($errorValue > 0) { $hasErrors = true; } $customData[] = [ $point['jd'], $point['observer_code'] !== '' ? $point['observer_code'] : '—', $point['magnitude_error'] !== null ? number_format((float) $point['magnitude_error'], 3, '.', '') : '—', ]; } $plotlyTraces[] = [ 'type' => 'scatter', 'mode' => 'lines+markers', 'name' => $dataset['filter'], 'x' => $xValues, 'y' => $yValues, 'line' => [ 'color' => $dataset['color'], 'width' => 2, ], 'marker' => [ 'color' => $dataset['color'], 'size' => 7, 'line' => [ 'color' => '#0f172a', 'width' => 1, ], ], 'error_y' => [ 'type' => 'data', 'array' => $errorValues, 'visible' => $hasErrors, 'thickness' => 1.1, 'width' => 3, 'color' => $dataset['color'], ], 'customdata' => $customData, 'hovertemplate' => 'Filter %{fullData.name}
' . 'Magnitude %{y:.3f}
' . 'Fehler %{customdata[2]}
' . 'UTC %{x}
' . 'JD %{customdata[0]}
' . 'Observer %{customdata[1]}', ]; } $plotlyPayload = [ 'traces' => $plotlyTraces, 'datasets' => $chartDatasets, 'selectedFilters' => $selectedFilters, 'summary' => $summary, ]; ?>

Filter

Lichtkurve

Messpunkte
Erster Beobachtungszeitpunkt
Letzter Beobachtungszeitpunkt
bis Magnitudenbereich
format('d.m.Y')) ?> Nächster möglicher Ausbruch
(in Tagen)
Noch keine darstellbaren Daten vorhanden.