|
|
|
@@ -0,0 +1,752 @@
|
|
|
|
|
<?php
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
ini_set('display_errors', '1');
|
|
|
|
|
error_reporting(E_ALL);
|
|
|
|
|
session_start();
|
|
|
|
|
|
|
|
|
|
$loggedIn = isset($_SESSION['user_id']);
|
|
|
|
|
$pageTitle = 'Astronomische Umrechnungen';
|
|
|
|
|
$headerIntroPre = 'Astronomie-Werkzeuge';
|
|
|
|
|
$headerIntroTitle = 'Astronomische Umrechnungen';
|
|
|
|
|
$headerIntroSub = 'Entfernungen zwischen haeufigen astronomischen Einheiten umrechnen.';
|
|
|
|
|
|
|
|
|
|
function h(?string $value): string
|
|
|
|
|
{
|
|
|
|
|
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
|
|
|
|
|
}
|
|
|
|
|
?>
|
|
|
|
|
<?php require __DIR__ . '/header.php'; ?>
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
.astro-conv-layout {
|
|
|
|
|
display: grid;
|
|
|
|
|
gap: 1.25rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.astro-conv-tabs {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 0.75rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.astro-conv-tab {
|
|
|
|
|
padding: 0.75rem 1rem;
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
border: 1px solid rgba(130, 170, 255, 0.18);
|
|
|
|
|
background: rgba(255,255,255,0.04);
|
|
|
|
|
color: #dce8ff;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.astro-conv-tab.is-active {
|
|
|
|
|
background: linear-gradient(180deg, rgba(82, 153, 255, 0.28), rgba(49, 112, 255, 0.18));
|
|
|
|
|
border-color: rgba(130, 170, 255, 0.4);
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.astro-conv-panel {
|
|
|
|
|
padding: 1.2rem;
|
|
|
|
|
border-radius: 1rem;
|
|
|
|
|
background:
|
|
|
|
|
radial-gradient(circle at top left, rgba(82, 153, 255, 0.12), transparent 36%),
|
|
|
|
|
linear-gradient(180deg, rgba(13, 19, 33, 0.96), rgba(8, 11, 20, 0.98));
|
|
|
|
|
border: 1px solid rgba(130, 170, 255, 0.16);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.astro-conv-grid {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
|
|
gap: 0.85rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.astro-conv-field {
|
|
|
|
|
display: grid;
|
|
|
|
|
gap: 0.45rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.astro-conv-field label {
|
|
|
|
|
color: #c7d6f4;
|
|
|
|
|
font-size: 0.94rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.astro-conv-label-help {
|
|
|
|
|
border-bottom: 1px dotted rgba(199, 214, 244, 0.45);
|
|
|
|
|
cursor: help;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.astro-conv-input {
|
|
|
|
|
width: 100%;
|
|
|
|
|
padding: 0.72rem 0.8rem;
|
|
|
|
|
border-radius: 0.8rem;
|
|
|
|
|
border: 1px solid rgba(255,255,255,0.12);
|
|
|
|
|
background: rgba(255,255,255,0.05);
|
|
|
|
|
color: #f8fbff;
|
|
|
|
|
font-size: 0.96rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.astro-conv-input:focus {
|
|
|
|
|
outline: none;
|
|
|
|
|
border-color: rgba(130, 170, 255, 0.6);
|
|
|
|
|
box-shadow: 0 0 0 3px rgba(82, 153, 255, 0.16);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.astro-conv-field {
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.astro-conv-tooltip {
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 0;
|
|
|
|
|
top: calc(100% + 0.45rem);
|
|
|
|
|
z-index: 20;
|
|
|
|
|
min-width: 14rem;
|
|
|
|
|
max-width: 22rem;
|
|
|
|
|
padding: 0.7rem 0.8rem;
|
|
|
|
|
border-radius: 0.8rem;
|
|
|
|
|
background: rgba(10, 15, 27, 0.96);
|
|
|
|
|
border: 1px solid rgba(130, 170, 255, 0.2);
|
|
|
|
|
color: #eef4ff;
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
line-height: 1.45;
|
|
|
|
|
white-space: pre-line;
|
|
|
|
|
box-shadow: 0 12px 30px rgba(0,0,0,0.35);
|
|
|
|
|
opacity: 0;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
transform: translateY(-0.25rem);
|
|
|
|
|
transition: opacity 140ms ease, transform 140ms ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.astro-conv-field.is-tooltip-open .astro-conv-tooltip {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.astro-conv-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 0.8rem;
|
|
|
|
|
margin-top: 1rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.astro-conv-tab-panel {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.astro-conv-tab-panel.is-active {
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 960px) {
|
|
|
|
|
.astro-conv-grid {
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-layout">
|
|
|
|
|
<div class="astro-conv-tabs" role="tablist" aria-label="Umrechnungsbereiche">
|
|
|
|
|
<button type="button" class="astro-conv-tab is-active" data-tab-target="distanceTab" role="tab" aria-selected="true">Entfernung</button>
|
|
|
|
|
<button type="button" class="astro-conv-tab" data-tab-target="redshiftTab" role="tab" aria-selected="false">Rotverschiebung</button>
|
|
|
|
|
<button type="button" class="astro-conv-tab" data-tab-target="timeTab" role="tab" aria-selected="false">Zeit</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<section class="card astro-conv-panel astro-conv-tab-panel is-active" id="distanceTab" role="tabpanel">
|
|
|
|
|
<h2>Entfernungs-Umrechner</h2>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-grid" id="distanceConverter">
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="distanceKm">Kilometer (km)</label>
|
|
|
|
|
<input class="astro-conv-input" id="distanceKm" type="text" inputmode="decimal" value="384400">
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="distanceAu">Astronomische Einheiten (AU)</label>
|
|
|
|
|
<input class="astro-conv-input" id="distanceAu" type="text" inputmode="decimal">
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="distanceLightMinute">Lichtminuten (lm)</label>
|
|
|
|
|
<input class="astro-conv-input" id="distanceLightMinute" type="text" inputmode="decimal">
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="distanceLightHour">Lichtstunden (lh)</label>
|
|
|
|
|
<input class="astro-conv-input" id="distanceLightHour" type="text" inputmode="decimal">
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="distanceLy">Lichtjahre (ly)</label>
|
|
|
|
|
<input class="astro-conv-input" id="distanceLy" type="text" inputmode="decimal">
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="distancePc">Parsec (pc)</label>
|
|
|
|
|
<input class="astro-conv-input" id="distancePc" type="text" inputmode="decimal">
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="distanceMpc">Megaparsec (Mpc)</label>
|
|
|
|
|
<input class="astro-conv-input" id="distanceMpc" type="text" inputmode="decimal">
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-actions">
|
|
|
|
|
<button type="button" class="btn btn-secondary" id="distanceClear">Leeren</button>
|
|
|
|
|
<button type="button" class="btn btn-primary" id="distanceMoon">Mondentfernung</button>
|
|
|
|
|
<button type="button" class="btn btn-primary" id="distanceSun">1 AU</button>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section class="card astro-conv-panel astro-conv-tab-panel" id="redshiftTab" role="tabpanel">
|
|
|
|
|
<h2>Rotverschiebung (z)</h2>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-grid" id="redshiftConverter">
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="redshiftZ">Rotverschiebung z</label>
|
|
|
|
|
<input class="astro-conv-input" id="redshiftZ" type="text" inputmode="decimal" value="0,1">
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="redshiftFactor"><span class="astro-conv-label-help" title="Der Faktor, um den sich Wellenlaengen durch die Expansion des Universums verlaengern.">Faktor (1 + z)</span></label>
|
|
|
|
|
<input class="astro-conv-input" id="redshiftFactor" type="text" inputmode="decimal" readonly>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="redshiftScaleFactor"><span class="astro-conv-label-help" title="Der kosmologische Skalenfaktor a = 1 / (1 + z). Er beschreibt, wie gross das Universum zum Zeitpunkt der Aussendung relativ zu heute war.">Skalenfaktor a</span></label>
|
|
|
|
|
<input class="astro-conv-input" id="redshiftScaleFactor" type="text" inputmode="decimal" readonly>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="redshiftVelocityApprox"><span class="astro-conv-label-help" title="Einfache Naeherung fuer kleine Rotverschiebungen: v ungefaehr gleich z mal Lichtgeschwindigkeit.">Geschwindigkeit naeherungsweise (km/s)</span></label>
|
|
|
|
|
<input class="astro-conv-input" id="redshiftVelocityApprox" type="text" inputmode="decimal" readonly>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="redshiftVelocityRel"><span class="astro-conv-label-help" title="Relativistisch korrigierte Geschwindigkeit aus der Doppler-Formel. Sinnvoller als die lineare Naeherung bei groesserem z.">Geschwindigkeit relativistisch (km/s)</span></label>
|
|
|
|
|
<input class="astro-conv-input" id="redshiftVelocityRel" type="text" inputmode="decimal" readonly>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="redshiftVelocityC"><span class="astro-conv-label-help" title="Dieselbe relativistische Geschwindigkeit, aber als Anteil der Lichtgeschwindigkeit in Prozent.">Geschwindigkeit relativistisch (% c)</span></label>
|
|
|
|
|
<input class="astro-conv-input" id="redshiftVelocityC" type="text" inputmode="decimal" readonly>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="redshiftRestNm">Ruhewellenlaenge (nm)</label>
|
|
|
|
|
<input class="astro-conv-input" id="redshiftRestNm" type="text" inputmode="decimal" value="656,3">
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="redshiftObservedNm">Beobachtete Wellenlaenge (nm)</label>
|
|
|
|
|
<input class="astro-conv-input" id="redshiftObservedNm" type="text" inputmode="decimal" readonly>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="redshiftFrequencyFactor"><span class="astro-conv-label-help" title="Die beobachtete Frequenz ist um diesen Faktor kleiner als die urspruengliche Frequenz.">Frequenzfaktor 1 / (1 + z)</span></label>
|
|
|
|
|
<input class="astro-conv-input" id="redshiftFrequencyFactor" type="text" inputmode="decimal" readonly>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="redshiftLookbackGyr"><span class="astro-conv-label-help" title="Zeit, die das Licht seit seiner Aussendung bis heute unterwegs war. Gyr steht fuer Milliarden Jahre.">Rueckblickzeit (Gyr)</span></label>
|
|
|
|
|
<input class="astro-conv-input" id="redshiftLookbackGyr" type="text" inputmode="decimal" readonly>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="redshiftComovingMpc"><span class="astro-conv-label-help" title="Kosmologische Entfernung im mitbewegten Koordinatensystem. Sie bleibt fuer mit der Expansion mitbewegte Objekte zeitlich konstant.">Mitbewegte Entfernung (Mpc)</span></label>
|
|
|
|
|
<input class="astro-conv-input" id="redshiftComovingMpc" type="text" inputmode="decimal" readonly>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="redshiftLuminosityMpc"><span class="astro-conv-label-help" title="Entfernung, die man bei der beobachteten Helligkeit eines Objekts in einem expandierenden Universum verwendet. Wichtig fuer Standardkerzen.">Leuchtkraftentfernung (Mpc)</span></label>
|
|
|
|
|
<input class="astro-conv-input" id="redshiftLuminosityMpc" type="text" inputmode="decimal" readonly>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="redshiftAngularDiameterMpc"><span class="astro-conv-label-help" title="Entfernung, die den beobachteten Winkel mit der echten Groesse eines Objekts verknuepft. Wichtig fuer scheinbare Groessen am Himmel.">Winkeldurchmesser-Entfernung (Mpc)</span></label>
|
|
|
|
|
<input class="astro-conv-input" id="redshiftAngularDiameterMpc" type="text" inputmode="decimal" readonly>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-actions">
|
|
|
|
|
<button type="button" class="btn btn-secondary" id="redshiftClear">Leeren</button>
|
|
|
|
|
<button type="button" class="btn btn-primary" id="redshiftGalaxy">z = 0,1</button>
|
|
|
|
|
<button type="button" class="btn btn-primary" id="redshiftQuasar">z = 6</button>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section class="card astro-conv-panel astro-conv-tab-panel" id="timeTab" role="tabpanel">
|
|
|
|
|
<h2>Zeit-Umrechner</h2>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-grid" id="timeConverter">
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="timeUtc">UTC</label>
|
|
|
|
|
<input class="astro-conv-input" id="timeUtc" type="text" value="2026-04-06 12:00:00">
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="timeJd">Julianisches Datum (JD)</label>
|
|
|
|
|
<input class="astro-conv-input" id="timeJd" type="text" inputmode="decimal">
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="timeMjd">Modifiziertes Julianisches Datum (MJD)</label>
|
|
|
|
|
<input class="astro-conv-input" id="timeMjd" type="text" inputmode="decimal">
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-field">
|
|
|
|
|
<label for="timeUnix">Unix-Zeit</label>
|
|
|
|
|
<input class="astro-conv-input" id="timeUnix" type="text" inputmode="decimal">
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="astro-conv-actions">
|
|
|
|
|
<button type="button" class="btn btn-secondary" id="timeClear">Leeren</button>
|
|
|
|
|
<button type="button" class="btn btn-primary" id="timeNow">Jetzt</button>
|
|
|
|
|
<button type="button" class="btn btn-primary" id="timeJ2000">J2000</button>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<script src="js/number_to_german_words.js"></script>
|
|
|
|
|
<script>
|
|
|
|
|
(function () {
|
|
|
|
|
const SPEED_OF_LIGHT_KM_S = 299792.458;
|
|
|
|
|
const H0 = 70;
|
|
|
|
|
const OMEGA_M = 0.3;
|
|
|
|
|
const OMEGA_LAMBDA = 0.7;
|
|
|
|
|
const MPC_IN_KM = 3.0856775814913673e19;
|
|
|
|
|
const SECONDS_PER_GYR = 365.25 * 24 * 3600 * 1e9;
|
|
|
|
|
const KM_PER_AU = 149597870.7;
|
|
|
|
|
const KM_PER_LIGHT_MINUTE = 17987547.48;
|
|
|
|
|
const KM_PER_LIGHT_HOUR = 1079252848.8;
|
|
|
|
|
const KM_PER_LY = 9460730472580.8;
|
|
|
|
|
const KM_PER_PC = 30856775814913.67;
|
|
|
|
|
const KM_PER_MPC = KM_PER_PC * 1000000;
|
|
|
|
|
|
|
|
|
|
const distanceFields = {
|
|
|
|
|
km: document.getElementById('distanceKm'),
|
|
|
|
|
au: document.getElementById('distanceAu'),
|
|
|
|
|
lm: document.getElementById('distanceLightMinute'),
|
|
|
|
|
lh: document.getElementById('distanceLightHour'),
|
|
|
|
|
ly: document.getElementById('distanceLy'),
|
|
|
|
|
pc: document.getElementById('distancePc'),
|
|
|
|
|
mpc: document.getElementById('distanceMpc')
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const redshiftFields = {
|
|
|
|
|
z: document.getElementById('redshiftZ'),
|
|
|
|
|
factor: document.getElementById('redshiftFactor'),
|
|
|
|
|
scaleFactor: document.getElementById('redshiftScaleFactor'),
|
|
|
|
|
velocityApprox: document.getElementById('redshiftVelocityApprox'),
|
|
|
|
|
velocityRel: document.getElementById('redshiftVelocityRel'),
|
|
|
|
|
velocityC: document.getElementById('redshiftVelocityC'),
|
|
|
|
|
restNm: document.getElementById('redshiftRestNm'),
|
|
|
|
|
observedNm: document.getElementById('redshiftObservedNm'),
|
|
|
|
|
frequencyFactor: document.getElementById('redshiftFrequencyFactor'),
|
|
|
|
|
lookbackGyr: document.getElementById('redshiftLookbackGyr'),
|
|
|
|
|
comovingMpc: document.getElementById('redshiftComovingMpc'),
|
|
|
|
|
luminosityMpc: document.getElementById('redshiftLuminosityMpc'),
|
|
|
|
|
angularDiameterMpc: document.getElementById('redshiftAngularDiameterMpc')
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const timeFields = {
|
|
|
|
|
utc: document.getElementById('timeUtc'),
|
|
|
|
|
jd: document.getElementById('timeJd'),
|
|
|
|
|
mjd: document.getElementById('timeMjd'),
|
|
|
|
|
unix: document.getElementById('timeUnix')
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const unitToKm = {
|
|
|
|
|
km: 1,
|
|
|
|
|
au: KM_PER_AU,
|
|
|
|
|
lm: KM_PER_LIGHT_MINUTE,
|
|
|
|
|
lh: KM_PER_LIGHT_HOUR,
|
|
|
|
|
ly: KM_PER_LY,
|
|
|
|
|
pc: KM_PER_PC,
|
|
|
|
|
mpc: KM_PER_MPC
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const germanNumberFormatter = new Intl.NumberFormat('de-DE', {
|
|
|
|
|
maximumFractionDigits: 10
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const parseGermanNumber = function (value) {
|
|
|
|
|
if (typeof value !== 'string') {
|
|
|
|
|
return Number.NaN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const normalized = value
|
|
|
|
|
.trim()
|
|
|
|
|
.replace(/\s+/g, '')
|
|
|
|
|
.replace(/\./g, '')
|
|
|
|
|
.replace(',', '.');
|
|
|
|
|
|
|
|
|
|
if (normalized === '') {
|
|
|
|
|
return Number.NaN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Number(normalized);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const formatValue = function (value) {
|
|
|
|
|
if (!Number.isFinite(value)) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (value === 0) {
|
|
|
|
|
return '0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return germanNumberFormatter.format(value);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const cosmologyE = function (z) {
|
|
|
|
|
return Math.sqrt(OMEGA_M * Math.pow(1 + z, 3) + OMEGA_LAMBDA);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const integrateSimpson = function (fn, start, end, steps) {
|
|
|
|
|
if (steps % 2 !== 0) {
|
|
|
|
|
steps += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const h = (end - start) / steps;
|
|
|
|
|
let sum = fn(start) + fn(end);
|
|
|
|
|
|
|
|
|
|
for (let i = 1; i < steps; i += 1) {
|
|
|
|
|
const x = start + i * h;
|
|
|
|
|
sum += fn(x) * (i % 2 === 0 ? 2 : 4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (h / 3) * sum;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const updateTooltip = function (field) {
|
|
|
|
|
const wrapper = field.closest('.astro-conv-field');
|
|
|
|
|
const tooltipId = field.getAttribute('data-tooltip-id');
|
|
|
|
|
const tooltip = tooltipId ? document.getElementById(tooltipId) : null;
|
|
|
|
|
const numericValue = parseGermanNumber(field.value);
|
|
|
|
|
|
|
|
|
|
if (!wrapper || !tooltip || !Number.isFinite(numericValue)) {
|
|
|
|
|
if (wrapper) {
|
|
|
|
|
wrapper.classList.remove('is-tooltip-open');
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (typeof window.numberToGermanText !== 'function') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tooltip.textContent = window.numberToGermanText(numericValue);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const attachTooltip = function (field, key) {
|
|
|
|
|
const wrapper = field.closest('.astro-conv-field');
|
|
|
|
|
if (!wrapper) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const tooltip = document.createElement('div');
|
|
|
|
|
tooltip.className = 'astro-conv-tooltip';
|
|
|
|
|
tooltip.id = 'tooltip-' + key;
|
|
|
|
|
wrapper.appendChild(tooltip);
|
|
|
|
|
field.setAttribute('data-tooltip-id', tooltip.id);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const wireTooltipHover = function (field) {
|
|
|
|
|
field.addEventListener('mouseenter', function () {
|
|
|
|
|
updateTooltip(field);
|
|
|
|
|
if (field.value.trim() === '') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const wrapper = field.closest('.astro-conv-field');
|
|
|
|
|
if (wrapper) {
|
|
|
|
|
wrapper.classList.add('is-tooltip-open');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
field.addEventListener('mouseleave', function () {
|
|
|
|
|
const wrapper = field.closest('.astro-conv-field');
|
|
|
|
|
if (wrapper) {
|
|
|
|
|
wrapper.classList.remove('is-tooltip-open');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const distanceFieldEntries = Object.entries(distanceFields);
|
|
|
|
|
distanceFieldEntries.forEach(function ([unit, field]) {
|
|
|
|
|
attachTooltip(field, 'distance-' + unit);
|
|
|
|
|
wireTooltipHover(field);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const redshiftFieldEntries = Object.entries(redshiftFields);
|
|
|
|
|
redshiftFieldEntries.forEach(function ([unit, field]) {
|
|
|
|
|
attachTooltip(field, 'redshift-' + unit);
|
|
|
|
|
wireTooltipHover(field);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const timeFieldEntries = Object.entries(timeFields);
|
|
|
|
|
timeFieldEntries.forEach(function ([unit, field]) {
|
|
|
|
|
attachTooltip(field, 'time-' + unit);
|
|
|
|
|
wireTooltipHover(field);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const updateDistanceFrom = function (sourceUnit) {
|
|
|
|
|
const rawValue = distanceFields[sourceUnit].value.trim();
|
|
|
|
|
if (rawValue === '') {
|
|
|
|
|
Object.values(distanceFields).forEach(function (field, index) {
|
|
|
|
|
if (Object.keys(distanceFields)[index] !== sourceUnit) {
|
|
|
|
|
field.value = '';
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const numericValue = parseGermanNumber(rawValue);
|
|
|
|
|
if (!Number.isFinite(numericValue)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const valueInKm = numericValue * unitToKm[sourceUnit];
|
|
|
|
|
distanceFields[sourceUnit].value = formatValue(numericValue);
|
|
|
|
|
updateTooltip(distanceFields[sourceUnit]);
|
|
|
|
|
|
|
|
|
|
Object.entries(distanceFields).forEach(function ([unit, field]) {
|
|
|
|
|
if (unit === sourceUnit) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
field.value = formatValue(valueInKm / unitToKm[unit]);
|
|
|
|
|
updateTooltip(field);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
distanceFieldEntries.forEach(function ([unit, field]) {
|
|
|
|
|
field.addEventListener('input', function () {
|
|
|
|
|
updateDistanceFrom(unit);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('distanceClear').addEventListener('click', function () {
|
|
|
|
|
Object.values(distanceFields).forEach(function (field) {
|
|
|
|
|
field.value = '';
|
|
|
|
|
});
|
|
|
|
|
distanceFields.km.focus();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('distanceMoon').addEventListener('click', function () {
|
|
|
|
|
distanceFields.km.value = '384400';
|
|
|
|
|
updateDistanceFrom('km');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('distanceSun').addEventListener('click', function () {
|
|
|
|
|
distanceFields.au.value = '1';
|
|
|
|
|
updateDistanceFrom('au');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const updateRedshift = function () {
|
|
|
|
|
const z = parseGermanNumber(redshiftFields.z.value.trim());
|
|
|
|
|
const restNm = parseGermanNumber(redshiftFields.restNm.value.trim());
|
|
|
|
|
|
|
|
|
|
if (!Number.isFinite(z) || !Number.isFinite(restNm) || z <= -1) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const factor = 1 + z;
|
|
|
|
|
const scaleFactor = 1 / factor;
|
|
|
|
|
const observedNm = restNm * factor;
|
|
|
|
|
const frequencyFactor = 1 / factor;
|
|
|
|
|
const velocityApprox = z * SPEED_OF_LIGHT_KM_S;
|
|
|
|
|
const betaRel = ((factor * factor) - 1) / ((factor * factor) + 1);
|
|
|
|
|
const velocityRel = betaRel * SPEED_OF_LIGHT_KM_S;
|
|
|
|
|
const velocityC = betaRel * 100;
|
|
|
|
|
const integrationSteps = Math.max(200, Math.ceil(z * 200));
|
|
|
|
|
const comovingIntegral = integrateSimpson(function (currentZ) {
|
|
|
|
|
return 1 / cosmologyE(currentZ);
|
|
|
|
|
}, 0, z, integrationSteps);
|
|
|
|
|
const lookbackIntegral = integrateSimpson(function (currentZ) {
|
|
|
|
|
return 1 / ((1 + currentZ) * cosmologyE(currentZ));
|
|
|
|
|
}, 0, z, integrationSteps);
|
|
|
|
|
const hubbleTimeSeconds = MPC_IN_KM / H0;
|
|
|
|
|
const comovingMpc = (SPEED_OF_LIGHT_KM_S / H0) * comovingIntegral;
|
|
|
|
|
const luminosityMpc = comovingMpc * factor;
|
|
|
|
|
const angularDiameterMpc = comovingMpc / factor;
|
|
|
|
|
const lookbackGyr = (hubbleTimeSeconds * lookbackIntegral) / SECONDS_PER_GYR;
|
|
|
|
|
|
|
|
|
|
redshiftFields.z.value = formatValue(z);
|
|
|
|
|
redshiftFields.restNm.value = formatValue(restNm);
|
|
|
|
|
redshiftFields.factor.value = formatValue(factor);
|
|
|
|
|
redshiftFields.scaleFactor.value = formatValue(scaleFactor);
|
|
|
|
|
redshiftFields.observedNm.value = formatValue(observedNm);
|
|
|
|
|
redshiftFields.frequencyFactor.value = formatValue(frequencyFactor);
|
|
|
|
|
redshiftFields.velocityApprox.value = formatValue(velocityApprox);
|
|
|
|
|
redshiftFields.velocityRel.value = formatValue(velocityRel);
|
|
|
|
|
redshiftFields.velocityC.value = formatValue(velocityC);
|
|
|
|
|
redshiftFields.lookbackGyr.value = formatValue(lookbackGyr);
|
|
|
|
|
redshiftFields.comovingMpc.value = formatValue(comovingMpc);
|
|
|
|
|
redshiftFields.luminosityMpc.value = formatValue(luminosityMpc);
|
|
|
|
|
redshiftFields.angularDiameterMpc.value = formatValue(angularDiameterMpc);
|
|
|
|
|
|
|
|
|
|
Object.values(redshiftFields).forEach(function (field) {
|
|
|
|
|
updateTooltip(field);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
['z', 'restNm'].forEach(function (key) {
|
|
|
|
|
redshiftFields[key].addEventListener('input', updateRedshift);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('redshiftClear').addEventListener('click', function () {
|
|
|
|
|
Object.values(redshiftFields).forEach(function (field) {
|
|
|
|
|
field.value = '';
|
|
|
|
|
});
|
|
|
|
|
redshiftFields.z.focus();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('redshiftGalaxy').addEventListener('click', function () {
|
|
|
|
|
redshiftFields.z.value = '0,1';
|
|
|
|
|
redshiftFields.restNm.value = '656,3';
|
|
|
|
|
updateRedshift();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('redshiftQuasar').addEventListener('click', function () {
|
|
|
|
|
redshiftFields.z.value = '6';
|
|
|
|
|
redshiftFields.restNm.value = '121,567';
|
|
|
|
|
updateRedshift();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const parseUtcDateTime = function (value) {
|
|
|
|
|
const match = String(value).trim().match(/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?)?$/);
|
|
|
|
|
if (!match) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const year = Number(match[1]);
|
|
|
|
|
const month = Number(match[2]);
|
|
|
|
|
const day = Number(match[3]);
|
|
|
|
|
const hour = Number(match[4] || '0');
|
|
|
|
|
const minute = Number(match[5] || '0');
|
|
|
|
|
const second = Number(match[6] || '0');
|
|
|
|
|
const date = new Date(Date.UTC(year, month - 1, day, hour, minute, second));
|
|
|
|
|
|
|
|
|
|
return Number.isNaN(date.getTime()) ? null : date;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const formatUtcDateTime = function (date) {
|
|
|
|
|
if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const parts = [
|
|
|
|
|
date.getUTCFullYear().toString().padStart(4, '0'),
|
|
|
|
|
(date.getUTCMonth() + 1).toString().padStart(2, '0'),
|
|
|
|
|
date.getUTCDate().toString().padStart(2, '0')
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const timeParts = [
|
|
|
|
|
date.getUTCHours().toString().padStart(2, '0'),
|
|
|
|
|
date.getUTCMinutes().toString().padStart(2, '0'),
|
|
|
|
|
date.getUTCSeconds().toString().padStart(2, '0')
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return parts.join('-') + ' ' + timeParts.join(':');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const dateToJulianDate = function (date) {
|
|
|
|
|
return (date.getTime() / 86400000) + 2440587.5;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const julianDateToDate = function (jd) {
|
|
|
|
|
return new Date((jd - 2440587.5) * 86400000);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const updateTimeFrom = function (sourceUnit) {
|
|
|
|
|
let date = null;
|
|
|
|
|
|
|
|
|
|
if (sourceUnit === 'utc') {
|
|
|
|
|
date = parseUtcDateTime(timeFields.utc.value);
|
|
|
|
|
} else if (sourceUnit === 'jd') {
|
|
|
|
|
const jd = parseGermanNumber(timeFields.jd.value.trim());
|
|
|
|
|
date = Number.isFinite(jd) ? julianDateToDate(jd) : null;
|
|
|
|
|
} else if (sourceUnit === 'mjd') {
|
|
|
|
|
const mjd = parseGermanNumber(timeFields.mjd.value.trim());
|
|
|
|
|
date = Number.isFinite(mjd) ? julianDateToDate(mjd + 2400000.5) : null;
|
|
|
|
|
} else if (sourceUnit === 'unix') {
|
|
|
|
|
const unixSeconds = parseGermanNumber(timeFields.unix.value.trim());
|
|
|
|
|
date = Number.isFinite(unixSeconds) ? new Date(unixSeconds * 1000) : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const jd = dateToJulianDate(date);
|
|
|
|
|
const mjd = jd - 2400000.5;
|
|
|
|
|
const unixSeconds = Math.floor(date.getTime() / 1000);
|
|
|
|
|
|
|
|
|
|
timeFields.utc.value = formatUtcDateTime(date);
|
|
|
|
|
timeFields.jd.value = formatValue(jd);
|
|
|
|
|
timeFields.mjd.value = formatValue(mjd);
|
|
|
|
|
timeFields.unix.value = formatValue(unixSeconds);
|
|
|
|
|
|
|
|
|
|
Object.values(timeFields).forEach(function (field) {
|
|
|
|
|
updateTooltip(field);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
timeFields.utc.addEventListener('input', function () {
|
|
|
|
|
updateTimeFrom('utc');
|
|
|
|
|
});
|
|
|
|
|
['jd', 'mjd', 'unix'].forEach(function (key) {
|
|
|
|
|
timeFields[key].addEventListener('input', function () {
|
|
|
|
|
updateTimeFrom(key);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('timeClear').addEventListener('click', function () {
|
|
|
|
|
Object.values(timeFields).forEach(function (field) {
|
|
|
|
|
field.value = '';
|
|
|
|
|
});
|
|
|
|
|
timeFields.utc.focus();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('timeNow').addEventListener('click', function () {
|
|
|
|
|
timeFields.utc.value = formatUtcDateTime(new Date());
|
|
|
|
|
updateTimeFrom('utc');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById('timeJ2000').addEventListener('click', function () {
|
|
|
|
|
timeFields.utc.value = '2000-01-01 12:00:00';
|
|
|
|
|
updateTimeFrom('utc');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const tabs = Array.from(document.querySelectorAll('.astro-conv-tab'));
|
|
|
|
|
const tabPanels = Array.from(document.querySelectorAll('.astro-conv-tab-panel'));
|
|
|
|
|
|
|
|
|
|
const activateTab = function (targetId) {
|
|
|
|
|
tabs.forEach(function (tab) {
|
|
|
|
|
const isActive = tab.getAttribute('data-tab-target') === targetId;
|
|
|
|
|
tab.classList.toggle('is-active', isActive);
|
|
|
|
|
tab.setAttribute('aria-selected', isActive ? 'true' : 'false');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
tabPanels.forEach(function (panel) {
|
|
|
|
|
panel.classList.toggle('is-active', panel.id === targetId);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
tabs.forEach(function (tab) {
|
|
|
|
|
tab.addEventListener('click', function () {
|
|
|
|
|
activateTab(tab.getAttribute('data-tab-target'));
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
updateDistanceFrom('km');
|
|
|
|
|
updateRedshift();
|
|
|
|
|
updateTimeFrom('utc');
|
|
|
|
|
})();
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<?php require __DIR__ . '/footer.php'; ?>
|