Files
skyview.astronomiemuseum.de/public/asteroid_temp.php
T
2026-04-23 10:36:31 +02:00

765 lines
28 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
ini_set('display_errors', '1');
error_reporting(E_ALL);
session_start();
$loggedIn = isset($_SESSION['user_id']);
$pageTitle = 'Asteroidtemperatur – AstroTools';
$headerIntroPre = 'Astronomie-Werkzeuge';
$headerIntroTitle = 'Asteroid-Temperaturrechner';
$headerIntroSub = 'Gleichgewichts- und Subsolartemperatur eines Asteroiden berechnen (NEATM).';
?>
<?php require __DIR__ . '/header.php'; ?>
<style>
.at-layout {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
align-items: start;
}
@media (max-width: 720px) {
.at-layout { grid-template-columns: 1fr; }
}
/* Parameter-Grid */
.at-param-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.75rem 1rem;
}
@media (max-width: 480px) {
.at-param-grid { grid-template-columns: 1fr; }
}
.at-param {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.at-param label {
font-size: 0.78rem;
color: var(--text-dim);
letter-spacing: 0.04em;
}
.at-param input[type=range] {
width: 100%;
accent-color: var(--gold);
cursor: pointer;
}
.at-param .at-num {
display: flex;
align-items: center;
gap: 0.5rem;
}
.at-param .at-num input[type=number] {
flex: 1;
background: var(--bg);
border: 1px solid var(--border);
border-radius: var(--radius);
color: var(--text);
padding: 0.35rem 0.6rem;
font-size: 0.92rem;
min-width: 0;
}
.at-param .at-num input[type=number]:focus {
outline: none;
border-color: var(--gold-dim);
}
.at-param .at-unit {
font-size: 0.78rem;
color: var(--text-dim);
white-space: nowrap;
}
.at-hint {
font-size: 0.75rem;
color: var(--text-dim);
line-height: 1.45;
}
/* Albedo-Modus Toggle */
.at-mode-toggle {
display: flex;
gap: 0;
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
width: fit-content;
}
.at-mode-toggle input[type=radio] { display: none; }
.at-mode-toggle label {
padding: 0.3rem 0.85rem;
font-size: 0.78rem;
color: var(--text-dim);
cursor: pointer;
transition: background 0.15s, color 0.15s;
letter-spacing: 0.03em;
border: none;
}
.at-mode-toggle input[type=radio]:checked + label {
background: var(--gold-dim);
color: var(--bg);
font-weight: 600;
}
/* Abgeleitete Bond-Albedo Anzeige */
.at-derived {
display: flex;
align-items: center;
gap: 0.5rem;
background: rgba(201,168,76,0.07);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 0.35rem 0.7rem;
font-size: 0.85rem;
}
.at-derived-label { color: var(--text-dim); }
.at-derived-val { color: var(--gold); font-weight: 600; }
/* Albedo-Vorschau */
.at-albedo-wrap {
display: flex;
align-items: center;
gap: 0.75rem;
}
.at-albedo-swatch {
width: 2rem;
height: 2rem;
border-radius: 50%;
border: 1px solid var(--border);
flex-shrink: 0;
transition: background 0.2s;
}
.at-albedo-caption { font-size: 0.75rem; color: var(--text-dim); }
/* Preset-Buttons */
.at-presets {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
}
/* Ergebnisse */
.at-results { display: flex; flex-direction: column; gap: 1rem; }
.at-result-row {
display: flex;
align-items: baseline;
gap: 0.6rem;
flex-wrap: wrap;
}
.at-result-label { font-size: 0.78rem; color: var(--text-dim); min-width: 5rem; }
.at-result-sym {
font-family: 'Georgia', serif;
font-style: italic;
color: var(--gold-pale);
font-size: 1rem;
min-width: 3rem;
}
.at-result-val {
font-size: 1.45rem;
font-weight: 600;
color: var(--gold);
letter-spacing: 0.01em;
}
.at-result-c { font-size: 0.9rem; color: var(--text-dim); }
/* Durchmesser */
.at-diam-row {
display: flex;
align-items: center;
gap: 0.75rem;
flex-wrap: wrap;
}
.at-diam-val {
font-size: 1.6rem;
font-weight: 700;
color: var(--cyan);
}
.at-diam-unit { font-size: 0.9rem; color: var(--text-dim); }
.at-diam-note { font-size: 0.75rem; color: var(--text-dim); }
/* Temperaturprofil Canvas */
.at-theta-wrap {
position: relative;
width: 100%;
}
#at-theta-canvas {
display: block;
width: 100%;
height: 170px;
border-radius: var(--radius);
}
/* Größenschätzung hidden state */
.at-size-section { display: none; }
.at-size-section.is-visible { display: block; }
/* Trennlinie */
.at-divider {
border: none;
border-top: 1px solid var(--border);
margin: 0.8rem 0;
}
</style>
<div class="at-layout">
<!-- Linke Spalte: Eingaben -->
<div class="card">
<h2>Parameter</h2>
<div class="at-param-grid">
<div class="at-param">
<label for="at-r">Sonnenabstand <em>r</em></label>
<div class="at-num">
<input type="number" id="at-r" min="0.05" max="50" step="0.01" value="1.00">
<span class="at-unit">AE</span>
</div>
<input type="range" id="at-r-slider" min="0.05" max="10" step="0.01" value="1.00">
<p class="at-hint">Abstand zur Sonne. Einstrahlung skaliert mit 1/r².</p>
</div>
<div class="at-param">
<label for="at-eps">Emissivität <em>ε</em></label>
<div class="at-num">
<input type="number" id="at-eps" min="0.01" max="1" step="0.01" value="0.90">
<span class="at-unit">0–1</span>
</div>
<input type="range" id="at-eps-slider" min="0.01" max="1" step="0.01" value="0.90">
<p class="at-hint">Wärmeabstrahlung. Typisch 0,90–0,95.</p>
</div>
<div class="at-param" style="grid-column:1/-1">
<label for="at-eta">Beaming-Faktor <em>η</em></label>
<div class="at-num">
<input type="number" id="at-eta" min="0.1" max="3" step="0.01" value="1.00">
<span class="at-unit">> 0</span>
</div>
<input type="range" id="at-eta-slider" min="0.1" max="3" step="0.01" value="1.00">
<p class="at-hint">Rauigkeits-/Anisotropie-Korrektur (NEATM). Typisch 0,7–1,4.</p>
</div>
</div>
<hr class="at-divider">
<!-- Albedo-Modus -->
<div style="display:flex;align-items:center;gap:0.75rem;margin-bottom:0.65rem;flex-wrap:wrap;">
<span class="at-hint" style="min-height:0;">Albedo-Modus:</span>
<div class="at-mode-toggle">
<input type="radio" name="at-mode" id="at-mode-bond" value="bond" checked>
<label for="at-mode-bond">Bond-Albedo</label>
<input type="radio" name="at-mode" id="at-mode-geo" value="geo">
<label for="at-mode-geo">Geometrische Albedo</label>
</div>
</div>
<!-- Bond-Modus -->
<div id="at-bond-section">
<div class="at-param">
<label for="at-a">Bond-Albedo <em>A</em></label>
<div class="at-num">
<input type="number" id="at-a" min="0" max="0.999" step="0.001" value="0.050">
<span class="at-unit">0–1</span>
</div>
<input type="range" id="at-a-slider" min="0" max="0.999" step="0.001" value="0.050">
<p class="at-hint">Reflektierter Anteil der gesamten Strahlungsenergie. Dunkel ≈ 0,02–0,10.</p>
</div>
</div>
<!-- Geo-Modus -->
<div id="at-geo-section" style="display:none;">
<div class="at-param-grid">
<div class="at-param">
<label for="at-p">Geometrische Albedo <em>p</em></label>
<div class="at-num">
<input type="number" id="at-p" min="0.001" max="0.999" step="0.001" value="0.100">
<span class="at-unit">0–1</span>
</div>
<input type="range" id="at-p-slider" min="0.001" max="0.999" step="0.001" value="0.100">
<p class="at-hint">Katalogwert (MPC/JPL). C-Typ ≈ 0,04–0,10.</p>
</div>
<div class="at-param">
<label for="at-G">Phasenkoeffizient <em>G</em></label>
<div class="at-num">
<input type="number" id="at-G" min="0" max="0.9" step="0.01" value="0.15">
<span class="at-unit">IAU</span>
</div>
<input type="range" id="at-G-slider" min="0" max="0.9" step="0.01" value="0.15">
<p class="at-hint">IAU H-G Standard. Typisch 0,15; dunkel ≈ 0,02.</p>
</div>
</div>
<div style="margin-top:0.6rem;">
<div class="at-derived">
<span class="at-derived-label">→ Bond-Albedo <em>A</em> =</span>
<span class="at-derived-val" id="at-derived-A">–</span>
<span class="at-derived-label" style="margin-left:0.25rem;font-size:0.7rem;">(= p · q, &nbsp;q = 0,290 + 0,684·G)</span>
</div>
</div>
<hr class="at-divider">
<div class="at-param">
<label for="at-H">Absolute Helligkeit <em>H</em> <span style="color:var(--text-dim);font-style:normal;">(optional, für Größe)</span></label>
<div class="at-num">
<input type="number" id="at-H" min="-5" max="35" step="0.1" value="" placeholder="z. B. 20,5">
<span class="at-unit">mag</span>
</div>
<p class="at-hint">H-Magnitude aus MPC/JPL → berechnet Durchmesser D.</p>
</div>
</div>
<hr class="at-divider">
<div class="at-albedo-wrap" style="margin-bottom:0.6rem;">
<div class="at-albedo-swatch" id="at-albedo-swatch"></div>
<span class="at-albedo-caption">Albedo-Vorschau (Helligkeit der Oberfläche)</span>
</div>
<hr class="at-divider">
<p class="at-hint" style="margin-bottom:0.4rem;">Bekannte Asteroiden</p>
<div class="at-presets">
<button class="btn btn-sm btn-secondary" data-preset="c">C-Typ</button>
<button class="btn btn-sm btn-secondary" data-preset="s">S-Typ</button>
<button class="btn btn-sm btn-secondary" data-preset="vesta">Vesta</button>
<button class="btn btn-sm btn-secondary" data-preset="eros">Eros</button>
<button class="btn btn-sm btn-secondary" data-preset="bennu">Bennu</button>
<button class="btn btn-sm btn-secondary" data-preset="ryugu">Ryugu</button>
<button class="btn btn-sm btn-secondary" data-preset="neufang">(2183) Neufang</button>
<button class="btn btn-sm btn-danger" id="at-reset">Zurücksetzen</button>
</div>
</div>
<!-- Rechte Spalte -->
<div style="display:flex;flex-direction:column;gap:1.25rem;">
<!-- Temperaturen -->
<div class="card">
<h2>Temperaturen</h2>
<div class="at-results">
<div>
<div class="at-result-row">
<span class="at-result-label">Gleichgewicht</span>
<span class="at-result-sym">T<sub>eq</sub></span>
<span class="at-result-val" id="at-teq-k">–</span>
<span class="at-result-c" id="at-teq-c"></span>
</div>
<p class="at-hint" style="margin-top:0.2rem;">Globaler Mittelwert (gesamte Oberfläche, schnelle Rotation).</p>
</div>
<hr class="at-divider">
<div>
<div class="at-result-row">
<span class="at-result-label">Subsolar</span>
<span class="at-result-sym">T<sub>ss</sub></span>
<span class="at-result-val" id="at-tss-k">–</span>
<span class="at-result-c" id="at-tss-c"></span>
</div>
<p class="at-hint" style="margin-top:0.2rem;">Heißester Punkt direkt unter der Sonne (Zenitwinkel θ = 0°).</p>
</div>
</div>
</div>
<!-- Temperaturprofil -->
<div class="card">
<div style="display:flex;align-items:center;justify-content:space-between;gap:0.5rem;margin-bottom:0.5rem;">
<h2 style="margin:0;">Temperaturprofil Tagseite</h2>
<div class="at-mode-toggle">
<input type="radio" name="at-unit" id="at-unit-k" value="K" checked>
<label for="at-unit-k">K</label>
<input type="radio" name="at-unit" id="at-unit-c" value="C">
<label for="at-unit-c">°C</label>
</div>
</div>
<div class="at-theta-wrap">
<canvas id="at-theta-canvas"></canvas>
</div>
<p class="at-hint" style="margin-top:0.5rem;">
T(θ) = T<sub>ss</sub> · cos(θ)<sup>¼</sup> — Temperatur als Funktion des Sonnenzenitwinkels.
Gestrichelt: T<sub>eq</sub>.
</p>
</div>
<!-- Größenschätzung (nur geo-Modus + H-Eingabe) -->
<div class="card at-size-section" id="at-size-card">
<h2>Größenabschätzung</h2>
<div class="at-diam-row">
<span class="at-diam-val" id="at-diam-val">–</span>
<span class="at-diam-unit">km</span>
</div>
<p class="at-hint" style="margin-top:0.4rem;" id="at-diam-note"></p>
<p class="at-diam-note" style="margin-top:0.3rem;">
D = (1329 km / √p) · 10<sup>−H/5</sup>
</p>
</div>
<!-- Formeln -->
<div class="card">
<h2>Formeln</h2>
<table class="data-table">
<tr><td>T<sub>eq</sub></td><td>⁴√[ (1−A)·S / (4·ε·σ·η) ]</td></tr>
<tr><td>T<sub>ss</sub></td><td>⁴√[ (1−A)·S / (ε·σ·η) ]</td></tr>
<tr><td>T(θ)</td><td>T<sub>ss</sub> · cos(θ)<sup>¼</sup></td></tr>
<tr><td>S</td><td>S₀ / r² &nbsp;&nbsp;(S₀ = 1361 W/m²)</td></tr>
<tr><td>A</td><td>p · q &nbsp;&nbsp;(q = 0,290 + 0,684·G)</td></tr>
<tr><td>D</td><td>(1329 km / √p) · 10<sup>−H/5</sup></td></tr>
<tr><td>σ</td><td>5,670 × 10⁻⁸ W m⁻² K⁻⁴</td></tr>
</table>
<p class="hint" style="margin-top:0.75rem;">
Modell: NEATM · Albedo: IAU H-G-System (Bowell et al. 1989)
</p>
</div>
</div>
</div>
<script>
(function () {
const SIGMA = 5.670374419e-8;
const S0 = 1361.0;
// Phasenintegral (Bowell et al. 1989, IAU H-G)
function phaseIntegral(G) { return 0.290 + 0.684 * G; }
function bondAlbedo(p, G) { return p * phaseIntegral(G); }
function asteroidDiam(H, p) { return (1329.0 / Math.sqrt(p)) * Math.pow(10, -H / 5.0); }
// Presets: geometrische Albedo p + G (Bond A wird daraus abgeleitet)
// Quellen: Dawn, NEAR-Shoemaker, OSIRIS-REx, Hayabusa2, JPL/MPC
const PRESETS = {
// r p G eps eta
default: { r:1.00, p:0.100, G:0.15, eps:0.90, eta:1.00 },
c: { r:2.50, p:0.057, G:0.15, eps:0.90, eta:1.00 }, // C-Typ typisch
s: { r:2.00, p:0.202, G:0.24, eps:0.90, eta:1.00 }, // S-Typ typisch
vesta: { r:2.36, p:0.423, G:0.32, eps:0.90, eta:1.00 }, // Dawn-Mission
eros: { r:1.46, p:0.250, G:0.22, eps:0.90, eta:1.00 }, // NEAR-Shoemaker
bennu: { r:1.13, p:0.044, G:0.02, eps:0.90, eta:1.00 }, // OSIRIS-REx
ryugu: { r:1.19, p:0.045, G:0.09, eps:0.90, eta:1.00 }, // Hayabusa2
neufang: { r:3.01, p:0.115, G:0.15, eps:0.90, eta:1.00 }, // Schätzwert
};
// Eingaben
const numR = document.getElementById('at-r');
const numA = document.getElementById('at-a');
const numP = document.getElementById('at-p');
const numG = document.getElementById('at-G');
const numH = document.getElementById('at-H');
const numEps = document.getElementById('at-eps');
const numEta = document.getElementById('at-eta');
const sldR = document.getElementById('at-r-slider');
const sldA = document.getElementById('at-a-slider');
const sldP = document.getElementById('at-p-slider');
const sldG = document.getElementById('at-G-slider');
const sldEps = document.getElementById('at-eps-slider');
const sldEta = document.getElementById('at-eta-slider');
const modeRadios = document.querySelectorAll('input[name="at-mode"]');
const bondSection = document.getElementById('at-bond-section');
const geoSection = document.getElementById('at-geo-section');
const derivedA = document.getElementById('at-derived-A');
// Ausgaben
const outTeqK = document.getElementById('at-teq-k');
const outTeqC = document.getElementById('at-teq-c');
const outTssK = document.getElementById('at-tss-k');
const outTssC = document.getElementById('at-tss-c');
const swatch = document.getElementById('at-albedo-swatch');
const sizeCard = document.getElementById('at-size-card');
const diamVal = document.getElementById('at-diam-val');
const diamNote = document.getElementById('at-diam-note');
function fmt(val, d) { return val.toFixed(d).replace('.', ','); }
function fmtSI(val) {
if (val >= 1e6) return fmt(val / 1e6, 2) + ' ×10⁶';
if (val >= 1000) return fmt(val, 0);
return fmt(val, 1);
}
function getMode() {
for (const r of modeRadios) { if (r.checked) return r.value; }
return 'bond';
}
function getBondAlbedo() {
if (getMode() === 'geo') {
const p = parseFloat(numP.value);
const G = parseFloat(numG.value);
if (!isFinite(p) || !isFinite(G)) return NaN;
return bondAlbedo(p, G);
}
return parseFloat(numA.value);
}
function calculate() {
const r = parseFloat(numR.value);
const A = getBondAlbedo();
const eps = parseFloat(numEps.value);
const eta = parseFloat(numEta.value);
const mode = getMode();
// Bond-Albedo Anzeige im Geo-Modus
if (mode === 'geo') {
derivedA.textContent = isFinite(A) ? fmt(A, 4) : '–';
}
// Albedo-Swatch (immer mit A)
if (isFinite(A)) {
const v = Math.round(Math.min(1, Math.max(0, A)) * 255);
swatch.style.background = `rgb(${v},${v},${v})`;
}
if (!isFinite(r) || r <= 0 || !isFinite(A) || A < 0 || A >= 1 ||
!isFinite(eps) || eps <= 0 || !isFinite(eta) || eta <= 0) {
outTeqK.textContent = '–'; outTeqC.textContent = '';
outTssK.textContent = '–'; outTssC.textContent = '';
drawThetaProfile(null, null);
updateSize(mode, NaN, NaN);
return;
}
const S = S0 / (r * r);
const Teq = Math.pow((1 - A) * S / (4.0 * eps * SIGMA * eta), 0.25);
const Tss = Math.pow((1 - A) * S / ( eps * SIGMA * eta), 0.25);
outTeqK.textContent = fmt(Teq, 2) + ' K';
outTeqC.textContent = '(' + fmt(Teq - 273.15, 2) + ' °C)';
outTssK.textContent = fmt(Tss, 2) + ' K';
outTssC.textContent = '(' + fmt(Tss - 273.15, 2) + ' °C)';
drawThetaProfile(Tss, Teq);
const p = mode === 'geo' ? parseFloat(numP.value) : NaN;
updateSize(mode, p, NaN);
}
function updateSize(mode, p, _unused) {
if (mode !== 'geo') { sizeCard.classList.remove('is-visible'); return; }
const H = parseFloat(numH.value);
if (!isFinite(H) || !isFinite(p) || p <= 0) {
sizeCard.classList.remove('is-visible');
return;
}
const D = asteroidDiam(H, p);
sizeCard.classList.add('is-visible');
diamVal.textContent = fmtSI(D) + ' km';
diamNote.textContent = 'H = ' + fmt(H, 1) + ' mag, p = ' + fmt(p, 3);
}
function getTempUnit() {
return document.getElementById('at-unit-c').checked ? 'C' : 'K';
}
function toUnit(K) { return getTempUnit() === 'C' ? K - 273.15 : K; }
function unitLabel() { return getTempUnit() === 'C' ? '°C' : 'K'; }
document.querySelectorAll('input[name="at-unit"]').forEach(r =>
r.addEventListener('change', () => {
const Tss = parseFloat(outTssK.textContent);
const Teq = parseFloat(outTeqK.textContent);
if (isFinite(Tss) && isFinite(Teq)) drawThetaProfile(Tss, Teq);
})
);
// T(θ) Profil Canvas
function drawThetaProfile(Tss, Teq) {
const canvas = document.getElementById('at-theta-canvas');
const dpr = Math.min(window.devicePixelRatio || 1, 2);
const cssW = canvas.offsetWidth || 400;
const cssH = 170;
canvas.style.height = cssH + 'px';
canvas.width = Math.round(cssW * dpr);
canvas.height = Math.round(cssH * dpr);
const ctx = canvas.getContext('2d');
ctx.scale(dpr, dpr);
ctx.clearRect(0, 0, cssW, cssH);
const PAD = { top: 20, right: 14, bottom: 34, left: 52 };
const cW = cssW - PAD.left - PAD.right;
const cH = cssH - PAD.top - PAD.bottom;
// Hintergrund
ctx.fillStyle = '#06080f';
ctx.fillRect(0, 0, cssW, cssH);
if (!Tss || !Teq) {
ctx.fillStyle = '#8899bb';
ctx.font = '12px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('Bitte Parameter eingeben', cssW / 2, cssH / 2);
return;
}
const TssU = toUnit(Tss);
const TeqU = toUnit(Teq);
const unit = unitLabel();
// Y-Achse: bei Celsius kann Minimum negativ sein → Bereich anpassen
const Tmin_u = getTempUnit() === 'C' ? Math.min(0, toUnit(0)) : 0;
const Tmax_u = TssU * (getTempUnit() === 'C' ? 1.08 : 1.08);
const Trange = Tmax_u - Tmin_u || 1;
const xOf = t => PAD.left + (t / 90) * cW;
const yOf = T => PAD.top + cH - ((T - Tmin_u) / Trange) * cH;
// Grid
ctx.strokeStyle = 'rgba(136,153,187,0.12)';
ctx.lineWidth = 1;
for (const theta of [0, 30, 60, 90]) {
ctx.beginPath(); ctx.moveTo(xOf(theta), PAD.top); ctx.lineTo(xOf(theta), PAD.top + cH); ctx.stroke();
}
const nY = 4;
for (let i = 0; i <= nY; i++) {
const T = Tmin_u + (i / nY) * Trange;
ctx.beginPath(); ctx.moveTo(PAD.left, yOf(T)); ctx.lineTo(PAD.left + cW, yOf(T)); ctx.stroke();
}
// T_eq gestrichelt (cyan)
ctx.strokeStyle = '#4fc3d8';
ctx.lineWidth = 1.5;
ctx.setLineDash([5, 4]);
ctx.beginPath(); ctx.moveTo(PAD.left, yOf(TeqU)); ctx.lineTo(PAD.left + cW, yOf(TeqU)); ctx.stroke();
ctx.setLineDash([]);
// T(θ) Kurve (gold, Füllung darunter)
const tCurve = px => toUnit(Tss * Math.pow(Math.cos((px / cW) * 90 * Math.PI / 180), 0.25));
ctx.beginPath();
for (let px = 0; px <= cW; px++) {
px === 0 ? ctx.moveTo(xOf(px / cW * 90), yOf(tCurve(px))) : ctx.lineTo(xOf(px / cW * 90), yOf(tCurve(px)));
}
ctx.lineTo(PAD.left + cW, PAD.top + cH);
ctx.lineTo(PAD.left, PAD.top + cH);
ctx.closePath();
ctx.fillStyle = 'rgba(201,168,76,0.08)';
ctx.fill();
ctx.strokeStyle = '#c9a84c';
ctx.lineWidth = 2;
ctx.beginPath();
for (let px = 0; px <= cW; px++) {
px === 0 ? ctx.moveTo(xOf(px / cW * 90), yOf(tCurve(px))) : ctx.lineTo(xOf(px / cW * 90), yOf(tCurve(px)));
}
ctx.stroke();
// Achsen
ctx.strokeStyle = 'rgba(136,153,187,0.35)';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(PAD.left, PAD.top);
ctx.lineTo(PAD.left, PAD.top + cH);
ctx.lineTo(PAD.left + cW, PAD.top + cH);
ctx.stroke();
// X-Beschriftung
ctx.fillStyle = '#8899bb';
ctx.font = '10px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
for (const theta of [0, 30, 60, 90]) {
ctx.fillText(theta + '°', xOf(theta), PAD.top + cH + 5);
}
ctx.fillText('Zenitwinkel θ', PAD.left + cW / 2, PAD.top + cH + 19);
// Y-Beschriftung
ctx.textAlign = 'right';
ctx.textBaseline = 'middle';
for (let i = 0; i <= nY; i++) {
const T = Tmin_u + (i / nY) * Trange;
ctx.fillText(Math.round(T) + ' ' + unit, PAD.left - 5, yOf(T));
}
// Labels an den Kurven
ctx.font = 'bold 10px sans-serif';
ctx.textAlign = 'left';
ctx.textBaseline = 'bottom';
ctx.fillStyle = '#c9a84c';
ctx.fillText('T_ss = ' + TssU.toFixed(0) + ' ' + unit, xOf(0) + 4, yOf(TssU) - 2);
ctx.fillStyle = '#4fc3d8';
ctx.font = '10px sans-serif';
const labelX = Math.min(xOf(45), PAD.left + cW - 90);
ctx.fillText('T_eq = ' + TeqU.toFixed(0) + ' ' + unit, labelX, yOf(TeqU) - 3);
}
// Slider ↔ Zahl synchronisieren
function sync(num, sld) {
num.addEventListener('input', () => { sld.value = num.value; calculate(); });
sld.addEventListener('input', () => { num.value = sld.value; calculate(); });
}
sync(numR, sldR);
sync(numA, sldA);
sync(numP, sldP);
sync(numG, sldG);
sync(numEps, sldEps);
sync(numEta, sldEta);
numH.addEventListener('input', calculate);
// Modus-Toggle
modeRadios.forEach(r => r.addEventListener('change', () => {
const geo = getMode() === 'geo';
bondSection.style.display = geo ? 'none' : '';
geoSection.style.display = geo ? '' : 'none';
if (geo) {
// Bond→Geo: A in p umrechnen (näherungsweise mit aktuellem G)
const A = parseFloat(numA.value);
const G = parseFloat(numG.value) || 0.15;
const q = phaseIntegral(G);
if (isFinite(A) && q > 0) {
const p = Math.min(0.999, Math.max(0.001, A / q));
numP.value = sldP.value = p.toFixed(3);
}
} else {
// Geo→Bond: berechnetes A übernehmen
const A = getBondAlbedo();
if (isFinite(A)) numA.value = sldA.value = A.toFixed(3);
}
calculate();
}));
// Presets
function applyPreset(pr) {
numR.value = sldR.value = pr.r;
numEps.value = sldEps.value = pr.eps;
numEta.value = sldEta.value = pr.eta;
// Preset immer im Geo-Modus anwenden
document.getElementById('at-mode-geo').checked = true;
bondSection.style.display = 'none';
geoSection.style.display = '';
numP.value = sldP.value = pr.p;
numG.value = sldG.value = pr.G;
const A = bondAlbedo(pr.p, pr.G);
numA.value = sldA.value = A.toFixed(3);
calculate();
}
document.querySelectorAll('[data-preset]').forEach(btn => {
btn.addEventListener('click', () => {
const k = btn.dataset.preset;
if (PRESETS[k]) applyPreset(PRESETS[k]);
});
});
document.getElementById('at-reset').addEventListener('click', () => {
document.getElementById('at-mode-bond').checked = true;
bondSection.style.display = '';
geoSection.style.display = 'none';
numR.value = sldR.value = 1.00;
numA.value = sldA.value = 0.050;
numP.value = sldP.value = 0.100;
numG.value = sldG.value = 0.15;
numH.value = '';
numEps.value = sldEps.value = 0.90;
numEta.value = sldEta.value = 1.00;
calculate();
});
window.addEventListener('resize', () => {
const Tss = parseFloat(outTssK.textContent);
const Teq = parseFloat(outTeqK.textContent);
if (isFinite(Tss) && isFinite(Teq)) drawThetaProfile(Tss, Teq);
});
calculate();
})();
</script>
<?php require __DIR__ . '/footer.php'; ?>