Jupitersystem GRF verbessert
This commit is contained in:
+30
-4
@@ -1,6 +1,32 @@
|
||||
{
|
||||
"refDate": "2026-04-13T00:31:40.897Z",
|
||||
"refAngle": 312227.6287687423,
|
||||
"calibratedAt": "2026-04-12T09:26:52+00:00",
|
||||
"calibratedBy": "Eskimue"
|
||||
"refDate": "2026-04-13T04:07:00.000Z",
|
||||
"refAngle": 312229.88248732366,
|
||||
"calibratedAt": "2026-04-12T09:35:41+00:00",
|
||||
"calibratedBy": "Eskimue",
|
||||
"history": [
|
||||
{
|
||||
"refDate": "2025-04-12T01:08:00.000Z",
|
||||
"refAngle": 306668.75732167566,
|
||||
"calibratedAt": "2026-04-12T09:33:58+00:00",
|
||||
"calibratedBy": "Eskimue"
|
||||
},
|
||||
{
|
||||
"refDate": "2026-01-01T09:49:00.000Z",
|
||||
"refAngle": 310684.2913082397,
|
||||
"calibratedAt": "2026-04-12T09:38:07+00:00",
|
||||
"calibratedBy": "Eskimue"
|
||||
},
|
||||
{
|
||||
"refDate": "2026-04-12T08:15:00.000Z",
|
||||
"refAngle": 312217.3093069398,
|
||||
"calibratedAt": "2026-04-12T09:32:29+00:00",
|
||||
"calibratedBy": "Eskimue"
|
||||
},
|
||||
{
|
||||
"refDate": "2026-04-13T04:07:00.000Z",
|
||||
"refAngle": 312229.88248732366,
|
||||
"calibratedAt": "2026-04-12T09:35:41+00:00",
|
||||
"calibratedBy": "Eskimue"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -24,12 +24,26 @@ if (isset($_GET['grs_api'])) {
|
||||
if (!$isAdmin) { http_response_code(403); echo json_encode(['error' => 'Nicht autorisiert']); exit; }
|
||||
$body = json_decode(file_get_contents('php://input'), true);
|
||||
if (!$body || !isset($body['refDate'], $body['refAngle'])) { http_response_code(400); echo json_encode(['error' => 'Ungültige Daten']); exit; }
|
||||
$data = [
|
||||
$existing = [];
|
||||
if (file_exists($dataFile)) {
|
||||
$existing = json_decode(file_get_contents($dataFile), true) ?? [];
|
||||
}
|
||||
$history = $existing['history'] ?? [];
|
||||
$history[] = [
|
||||
'refDate' => (string) $body['refDate'],
|
||||
'refAngle' => (float) $body['refAngle'],
|
||||
'calibratedAt' => date('c'),
|
||||
'calibratedBy' => $_SESSION['username'] ?? 'admin',
|
||||
];
|
||||
usort($history, fn($a, $b) => strcmp((string) $a['refDate'], (string) $b['refDate']));
|
||||
$latest = end($history);
|
||||
$data = [
|
||||
'refDate' => $latest['refDate'],
|
||||
'refAngle' => $latest['refAngle'],
|
||||
'calibratedAt' => $latest['calibratedAt'],
|
||||
'calibratedBy' => $latest['calibratedBy'],
|
||||
'history' => $history,
|
||||
];
|
||||
$written = file_put_contents($dataFile, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||
echo $written !== false ? json_encode(['ok' => true]) : json_encode(['error' => 'Schreibfehler – Dateiberechtigung prüfen']);
|
||||
exit;
|
||||
@@ -140,6 +154,15 @@ $bodyClass = 'solarsystem-page jupitersystem-page';
|
||||
<button class="btn btn-secondary btn-full mt-md" id="grsBtnShadowRays">Schattenstrahlen anzeigen</button>
|
||||
<button class="btn btn-primary btn-full mt-xs" id="grsBtnSave">Kalibrierung speichern</button>
|
||||
<div id="grsCalibStatus" class="hint inline-fade mt-sm"></div>
|
||||
|
||||
<div id="grsDriftSection" style="display:none">
|
||||
<div class="solarsystem-label mt-md">GRS Drift-Historie</div>
|
||||
<p class="hint mt-2xs">Abweichung vom System-II-Rhythmus zwischen je zwei Abgleichen.</p>
|
||||
<div id="grsDriftAvg" class="hint mt-2xs" style="color:var(--gold-pale)"></div>
|
||||
<div style="overflow-x:auto;margin-top:0.5rem">
|
||||
<table id="grsDriftTable" class="data-table" style="font-size:0.8rem;white-space:nowrap"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
@@ -779,6 +802,60 @@ function parseGrsTransitInput(str) {
|
||||
return isNaN(d.getTime()) ? null : d;
|
||||
}
|
||||
|
||||
function renderGrsDrift(history) {
|
||||
const section = document.getElementById('grsDriftSection');
|
||||
const table = document.getElementById('grsDriftTable');
|
||||
const avgEl = document.getElementById('grsDriftAvg');
|
||||
if (!section || !table || !avgEl || !Array.isArray(history) || history.length < 2) {
|
||||
if (section) section.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
const RAD_TO_DEG = 180 / Math.PI;
|
||||
|
||||
let rows = '';
|
||||
const driftRates = [];
|
||||
|
||||
for (let i = 1; i < history.length; i++) {
|
||||
const prev = history[i - 1];
|
||||
const curr = history[i];
|
||||
const t1 = new Date(prev.refDate).getTime();
|
||||
const t2 = new Date(curr.refDate).getTime();
|
||||
const elapsedDays = (t2 - t1) / 86400000;
|
||||
if (elapsedDays <= 0) continue;
|
||||
|
||||
const expectedDeg = (elapsedDays / GRS_SYSTEM_II_PERIOD_DAYS) * 360;
|
||||
const actualDeg = (curr.refAngle - prev.refAngle) * RAD_TO_DEG;
|
||||
// Drift auf den Bereich [-180, 180] normieren
|
||||
let drift = ((actualDeg - expectedDeg) % 360 + 540) % 360 - 180;
|
||||
const driftPerDay = drift / elapsedDays;
|
||||
driftRates.push(driftPerDay);
|
||||
|
||||
const sign = driftPerDay >= 0 ? '+' : '';
|
||||
const color = Math.abs(driftPerDay) < 0.05 ? 'var(--text-dim)' : (driftPerDay > 0 ? 'var(--gold-pale)' : 'var(--cyan)');
|
||||
const dateStr = new Date(curr.refDate).toLocaleString('de-DE', { timeZone: 'UTC', dateStyle: 'short', timeStyle: 'short' });
|
||||
|
||||
rows += `<tr>
|
||||
<td>${dateStr} UT</td>
|
||||
<td>${elapsedDays.toFixed(1)} d</td>
|
||||
<td style="color:${color};font-variant-numeric:tabular-nums">${sign}${drift.toFixed(2)}°</td>
|
||||
<td style="color:${color};font-variant-numeric:tabular-nums">${sign}${driftPerDay.toFixed(3)}°/d</td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
table.innerHTML = `<thead><tr>
|
||||
<th>Abgleich</th><th>Abstand</th><th>Drift ges.</th><th>Drift/Tag</th>
|
||||
</tr></thead><tbody>${rows}</tbody>`;
|
||||
|
||||
if (driftRates.length > 0) {
|
||||
const avg = driftRates.reduce((s, v) => s + v, 0) / driftRates.length;
|
||||
const sign = avg >= 0 ? '+' : '';
|
||||
avgEl.textContent = `Ø Drift: ${sign}${avg.toFixed(3)}°/Tag`;
|
||||
}
|
||||
|
||||
section.style.display = '';
|
||||
}
|
||||
|
||||
function formatGrsStatus(data) {
|
||||
if (!data.calibratedAt) return 'Noch keine Kalibrierung gespeichert.';
|
||||
const at = new Date(data.calibratedAt).toLocaleString('de-DE', { timeZone: 'UTC', dateStyle: 'short', timeStyle: 'short' });
|
||||
@@ -797,6 +874,7 @@ async function loadGrsCalibration() {
|
||||
}
|
||||
const statusEl = document.getElementById('grsCalibStatus');
|
||||
if (statusEl) statusEl.textContent = formatGrsStatus(data);
|
||||
renderGrsDrift(data.history);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
@@ -948,6 +1026,12 @@ function attachAdminHandlers() {
|
||||
grsBtnRot5R.addEventListener('click', () => shiftGrsRotation(5));
|
||||
|
||||
grsBtnSave.addEventListener('click', async () => {
|
||||
if (grsTransitInput.value.trim() === '') {
|
||||
grsParseError.textContent = 'Bitte zuerst einen GRS-Transitzeitpunkt eingeben und auf „Auf diese Zeit setzen" klicken.';
|
||||
grsParseError.classList.remove('is-hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
const elapsedDays = (simDate - grsRefDate) / 86400000;
|
||||
grsRefAngle = grsRefAngle + (elapsedDays / GRS_SYSTEM_II_PERIOD_DAYS) * Math.PI * 2;
|
||||
grsRefDate = new Date(simDate);
|
||||
@@ -956,6 +1040,10 @@ function attachAdminHandlers() {
|
||||
grsCalibStatus.textContent = ok === true
|
||||
? `Gespeichert: ${simDate.toISOString().slice(0, 16).replace('T', ' ')} UT\nFür alle Nutzer aktiv.`
|
||||
: `Fehler: ${ok}`;
|
||||
if (ok === true) {
|
||||
const res = await fetch('jupitersystem.php?grs_api').catch(() => null);
|
||||
if (res && res.ok) { const d = await res.json(); renderGrsDrift(d.history); }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user