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 = 'Diagramm über alle Filter';
$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]}