Annus Mirabilis · Interactive critical edition in preparation
Doppler principle and aberration
Change the observer's speed and see a light wave's frequency and direction change together.
Doppler and aberration
Doppler principle and aberration
Static worked example
Model note
- Frame speed (
frameSpeed), Propagation angle (stationary system) (propagationAngleStationary), Propagation angle (moving system) (propagationAngleMoving), Wave frequency (stationary system) (waveFrequencyStationary), Wave frequency (moving system) (waveFrequencyMoving), Doppler factor (dopplerFactor), Wave phase (wavePhase), Lorentz factor (lorentzFactor), Classical observer Doppler factor (classicalObserverDopplerFactor), Classical source Doppler factor (classicalSourceDopplerFactor), Receding line-of-sight Doppler factor (recedingDopplerFactor) and Approaching line-of-sight Doppler factor (approachingDopplerFactor): Host calculation, by the site's own reference code. Computed inwaves. - Computed from the settings last applied (settings revision 1, result 1).
- Not modeled: media and dispersion; sound in a medium; gravitational redshift; finite packets (the finite light-complex laboratory); telescope optics and atmospheric refraction; photon picture; canal-ray apparatus beyond published values.
Predict before the numbers
When observing a light wave traveling in the same direction as the observer at 0.6c, how does the observed frequency compare to the source frequency?
Predict before the numbers
When observing a light wave traveling toward the observer at 0.6c, how does the observed frequency compare to the source frequency?
The result appears when you choose, say you have one in mind, or skip.
Worked example: a ray at 0° and 500 THz in the stationary frame K arrives in the frame moving at 0.6c at 0° and 250 THz, a Doppler factor of 0.5.
Values at these settings
| Relativistic ν′/ν | 0.5 |
|---|---|
| Lorentz factor γ | 1.25 |
| Medium, moving observer | 0.4 |
| Medium, moving source | 0.625 |
| Receding line of sight | 0.5 |
| Approaching line of sight | 2 |
| Angle in k | 0 ° |
At θ = 90° the two medium formulae both give 1. The paper's factor is γ. That is the transverse Doppler shift, which has no classical counterpart. The medium formulae are not declared refuted: they are right for sound and agree with the paper to first order in v/c.
Light looks redder to an observer moving away from its source and bluer to one moving toward it, and it arrives from a slightly different direction. Einstein derived both effects, for any speed below that of light, from one transformation.
§7 places a source of plane waves very far away in K and asks what an observer at rest in the moving system k finds. Transforming the fields by §6 and the coordinates and time by §3 gives a wave of the same form in k, with a new frequency and a new direction. If φ is the angle between the line from source to observer and the observer's velocity, measured in K, the observed frequency is ν′ = ν(1 − cos φ · v/V)/√(1 − (v/V)²), which §7 calls Doppler's principle for arbitrary speeds, and the direction obeys cos φ′ = (cos φ − v/V)/(1 − (v/V) cos φ), the law of aberration in its most general form. The lab's default is 500 THz light and an observer receding at 0.6c along the line to the source, φ = 0: the factor is √((1 − 0.6)/(1 + 0.6)) = 0.5, so the observer measures 250 THz. Reverse the motion, φ = 180°, and the factor is 2, giving 1000 THz. At φ = 90° the frequency rises by γ = 1.25, to 625 THz, although the observer moves across the line of sight, and the ray arrives at 126.87°, where cos φ′ = −0.6, as §7 prints for this case. For comparison the lab shows the two older formulas, one for a moving observer (0.4 at φ = 0) and one for a moving source (0.625). They differ because each assumes light moves at a fixed speed through a medium, which makes it matter who moves; the relativistic 0.5 depends only on the relative speed. §7 also transforms the amplitude, A′² = A²(1 − (v/V) cos φ)²/(1 − (v/V)²), and concludes that a source approached at the speed of light would appear infinitely intense.
Start with the factor γ. For v = 0.6c, γ = 1/√(1 − 0.36) = 1/0.8 = 1.25. The frequency formula is ν′ = νγ(1 − β cos φ), with β = v/V = 0.6. Receding along the line of sight, φ = 0 and cos φ = 1, so ν′/ν = 1.25 × (1 − 0.6) = 1.25 × 0.4 = 0.5, the same as √(0.4/1.6) = √0.25. So 500 THz becomes 250 THz. Approaching, φ = 180° and cos φ = −1, so ν′/ν = 1.25 × 1.6 = 2, and the observer measures 1000 THz. Across the line of sight, φ = 90° and cos φ = 0, so ν′/ν = 1.25 × 1 = 1.25, and the observer measures 625 THz. That last result has no counterpart in the older formulas, which give no change at 90°. It is the slowing of moving clocks from §4 showing up in the observer's clock: in K it runs slow by the factor 1/γ, so the observer counts more wave crests per second of their own time. Now the direction: cos φ′ = (cos φ − β)/(1 − β cos φ). At φ = 90° this is (0 − 0.6)/(1 − 0) = −0.6, so φ′ = arccos(−0.6) = 126.87°. The ray that met the observer's path at right angles in K meets it at 126.87° in k, and that change of direction is aberration. Compare the older formulas. If the observer moves through a medium in which light travels at c, the crests pass at the relative speed c − v, and the factor is 1 − β = 0.4. If the source moves instead, it emits the crests closer together or farther apart, giving 1/(1 + β) = 0.625. The two disagree because a medium makes it matter which one moves. The relativistic factor, 0.5, is exactly the geometric mean of the two, √(0.4 × 0.625) = √0.25, and depends only on the relative velocity. The amplitude transforms by the same factor: §7's A′² = A²(1 − β cos φ)²/(1 − β²) means A′/A = γ(1 − β cos φ), so receding at 0.6c the amplitude halves and the intensity, which goes as its square, falls to a quarter; approaching ever closer to the speed of light, both grow without limit, which is §7's closing remark. Last, why the frequencies can be compared at all: the phase of the wave, which counts the crests that have passed, is the same number for the same event in both frames, so a detector counting crests in k finds the transformed frequency.
Modern qualifications
Einstein's φ is the angle between the line from source to observer and the observer's velocity, measured in the source's frame; his β is the modern γ and V the speed of light. The formulas are written for a source at rest in K and an observer at rest in k, and because only the relative velocity enters, the result is the same when the source moves. §7 remarks that, contrary to the usual view, the frequency grows without bound as the speed of approach nears V. The shift by γ at 90° follows from the formula, but the paper does not single it out; Ives and Stilwell measured the second-order shift in canal rays in 1938, although they read it within Lorentz's theory. Treating frequency and wave vector as one four-vector is Minkowski's later language, not the paper's.
Not modeled: media and dispersion; sound in a medium; gravitational redshift; finite packets (the finite light-complex laboratory); telescope optics and atmospheric refraction; photon picture; canal-ray apparatus beyond published values.
Show the code
The site’s own reference code for this law, in TypeScript. It computes the numbers above when the page runs it, and it is what the page uses wherever a FrankenSim result is not available.
evaluateSr09 · src/physics/reference/waves.ts · this site’s source, as built · sha256:ef623131c3e8ea8ac949e8079db833adab903b09b5bb016549d9ecbd18d959b4
This function computes the listed outputs when it runs.
In words
Paper 3's § 7 in one evaluation. It takes the boost, the angle the wave travels at in the resting system and its frequency, and returns the transformed angle, the transformed frequency, the Doppler factor, the amplitude factor, the line-of-sight factors for a source receding and approaching, and the aberration in arcseconds for the Earth's orbital speed. It also returns the second-order shift at a stated small speed, which is the part that distinguishes this prediction from the classical one.
Implementation
export function evaluateSr09(input: Sr09Input): Sr09EvaluationResult {
const beta = input.beta;
const thetaDeg = input.propagationAngleDeg;
const thetaRad = (thetaDeg * Math.PI) / 180;
const nuK = input.frequencyHz;
const omegaK = 2 * Math.PI * nuK;
const secondOrderSpeed = input.secondOrderSpeed ?? 0.005;
if (!Number.isFinite(beta) || Math.abs(beta) >= 1) {
return Object.freeze({
beta,
gamma: Number.NaN,
propagationAngleStationaryDeg: thetaDeg,
propagationAngleStationaryRad: thetaRad,
propagationAngleMovingDeg: Number.NaN,
propagationAngleMovingRad: Number.NaN,
cosThetaStationary: Number.NaN,
sinThetaStationary: Number.NaN,
cosThetaMoving: Number.NaN,
sinThetaMoving: Number.NaN,
waveFrequencyStationaryHz: nuK,
waveFrequencyMovingHz: Number.NaN,
waveAngularFrequencyStationary: omegaK,
waveAngularFrequencyMoving: Number.NaN,
dopplerFactor: Number.NaN,
amplitudeFactor: Number.NaN,
lineOfSightRecedingFactor: Number.NaN,
lineOfSightApproachingFactor: Number.NaN,
detectorCrossings: Number.NaN,
detectorProperRateHz: Number.NaN,
earthOrbitAberrationArcsec: Number.NaN,
earthOrbitAberrationFormatted: "outside-domain",
secondOrderShift: Number.NaN,
secondOrderComparisonSpeed: secondOrderSpeed,
events: [],
results: [
sr09Outside("frameSpeed", "c", "speed"),
sr09Outside("propagationAngleStationary", "deg", "angle"),
sr09Outside("propagationAngleMoving", "deg", "angle"),
sr09Outside("waveFrequencyStationary", "Hz", "frequency"),
sr09Outside("waveFrequencyMoving", "Hz", "frequency"),
sr09Outside("dopplerFactor", "1", "ratio"),
sr09Outside("wavePhase", "rad", "angle"),
sr09Outside("lorentzFactor", "1", "lorentz-factor"),
sr09Outside("classicalObserverDopplerFactor", "1", "ratio"),
sr09Outside("classicalSourceDopplerFactor", "1", "ratio"),
sr09Outside("recedingDopplerFactor", "1", "ratio"),
sr09Outside("approachingDopplerFactor", "1", "ratio"),
],
status: "outside-domain",
});
}
const gResult = gamma(beta);
const g = gResult.status === "value" ? gResult.value : Number.NaN;
const cosThetaK = Math.cos(thetaRad);
const sinThetaK = Math.sin(thetaRad);
const kMag = omegaK / C_SI;
const boosted = transformWaveVector(
omegaK,
{ x: kMag * cosThetaK, y: kMag * sinThetaK, z: 0 },
beta,
C_SI,
);
const omega_k = boosted.omegaPrime;
const nu_k = omega_k / (2 * Math.PI);
const doppler = omega_k / omegaK;
const ab = aberration(beta, thetaRad);
let theta_k_deg = (ab.thetaPrimeRad * 180) / Math.PI;
if (theta_k_deg < 0) theta_k_deg += 360;
const classicalObserver = classicalObserverDopplerFactor(beta, thetaRad);
const classicalSource = classicalSourceDopplerFactor(beta, thetaRad);
const recedingFactor = Math.sqrt((1 - beta) / (1 + beta));
const approachingFactor = Math.sqrt((1 + beta) / (1 - beta));
const detectorSpeed = input.detectorSpeed ?? beta;
const detectorWindow = input.countingWindow ?? 10 / nuK;
const detectorCrossings = detectorCrossingCount({
omega: omegaK,
k: { x: (omegaK / C_SI) * cosThetaK, y: (omegaK / C_SI) * sinThetaK, z: 0 },
detectorVelocity: { x: detectorSpeed * C_SI, y: 0, z: 0 },
window: detectorWindow,
});
const detectorProperRateHz = detectorCrossings / detectorWindow;
const modernAberration = modernEarthOrbitAberration();
const earthOrbitAberrationArcsec = modernAberration.arcsec;
const earthOrbitAberrationFormatted = `${earthOrbitAberrationArcsec.toFixed(4)}" (${modernAberration.label})`;
const secondOrderShiftVal = secondOrderShift(secondOrderSpeed);
// Discrete named events for Phase Invariance verification
const kxK = (omegaK / C_SI) * cosThetaK;
const kyK = (omegaK / C_SI) * sinThetaK;
const kzK = 0;
const wave1_K = { omega: omegaK, kx: kxK, ky: kyK, kz: kzK };
const transW = transformWaveVector(omegaK, { x: kxK, y: kyK, z: kzK }, beta, C_SI);
const wave1_k = {
omega: transW.omegaPrime,
kx: transW.kPrime.x,
ky: transW.kPrime.y,
kz: transW.kPrime.z,
};
// Event 1: tick at t = 1e-15 s, x = 0, y = 0, z = 0
const event1_K = { t: 1e-15, x: 0, y: 0, z: 0 };
const event1_k = {
t: g * (event1_K.t - (beta * event1_K.x) / C_SI),
x: g * (event1_K.x - beta * C_SI * event1_K.t),
y: 0,
z: 0,
};
const p1_K = phaseAtEvent(event1_K, wave1_K);
const p1_k = phaseAtEvent(event1_k, wave1_k);
const val1_K = p1_K.status === "value" && typeof p1_K.value === "number" ? p1_K.value : 0;
const val1_k = p1_k.status === "value" && typeof p1_k.value === "number" ? p1_k.value : 0;
// Adversarial Phase: uses Galilean wave transformation where kx does not mix with omega
const adv_kx_k = g * (kxK + (beta * omegaK) / C_SI);
const adv_wave1_k = { omega: omega_k, kx: adv_kx_k, ky: kyK, kz: kzK };
const adv_p1_k = phaseAtEvent(event1_k, adv_wave1_k);
const adv_val1_k =
adv_p1_k.status === "value" && typeof adv_p1_k.value === "number" ? adv_p1_k.value : 0;
// Event 2: offset event (t = 2e-15 s, x = 3e-7 m, y = 0, z = 0)
const event2_K = { t: 2e-15, x: 3e-7, y: 0, z: 0 };
const event2_k = {
t: g * (event2_K.t - (beta * event2_K.x) / C_SI),
x: g * (event2_K.x - beta * C_SI * event2_K.t),
y: 0,
z: 0,
};
const p2_K = phaseAtEvent(event2_K, wave1_K);
const p2_k = phaseAtEvent(event2_k, wave1_k);
const val2_K = p2_K.status === "value" && typeof p2_K.value === "number" ? p2_K.value : 0;
const val2_k = p2_k.status === "value" && typeof p2_k.value === "number" ? p2_k.value : 0;
const adv_p2_k = phaseAtEvent(event2_k, adv_wave1_k);
const adv_val2_k =
adv_p2_k.status === "value" && typeof adv_p2_k.value === "number" ? adv_p2_k.value : 0;
const events: readonly Sr09NamedEventRecord[] = [
Object.freeze({
id: "sr-09-event-origin-tick",
label: "Origin cycle tick (t = 1 fs, x = 0)",
eventK: event1_K,
event_k: event1_k,
waveK: wave1_K,
wave_k: wave1_k,
phaseK: val1_K,
phase_k: val1_k,
phasesAgree:
classifyWithTolerance(val1_K - val1_k, { absolute: 1e-12 }).sign === "indeterminate" ||
val1_K === val1_k,
adversarialPhase_k: adv_val1_k,
}),
Object.freeze({
id: "sr-09-event-offset",
label: "Offset downstream event (t = 2 fs, x = 300 nm)",
eventK: event2_K,
event_k: event2_k,
waveK: wave1_K,
wave_k: wave1_k,
phaseK: val2_K,
phase_k: val2_k,
phasesAgree:
classifyWithTolerance(val2_K - val2_k, { absolute: 1e-12 }).sign === "indeterminate" ||
val2_K === val2_k,
adversarialPhase_k: adv_val2_k,
}),
];
const results: readonly ScientificResult[] = [
Object.freeze({
quantityId: "frameSpeed",
unit: "c",
semanticKind: "speed",
ownerId: OWNER_ID,
status: "value",
value: beta,
}),
Object.freeze({
quantityId: "propagationAngleStationary",
unit: "deg",
semanticKind: "angle",
ownerId: OWNER_ID,
status: "value",
value: thetaDeg,
}),
Object.freeze({
quantityId: "propagationAngleMoving",
unit: "deg",
semanticKind: "angle",
ownerId: OWNER_ID,
status: "value",
value: theta_k_deg,
}),
Object.freeze({
quantityId: "waveFrequencyStationary",
unit: "Hz",
semanticKind: "frequency",
ownerId: OWNER_ID,
status: "value",
value: nuK,
}),
Object.freeze({
quantityId: "waveFrequencyMoving",
unit: "Hz",
semanticKind: "frequency",
ownerId: OWNER_ID,
status: "value",
value: nu_k,
}),
Object.freeze({
quantityId: "dopplerFactor",
unit: "1",
semanticKind: "ratio",
ownerId: OWNER_ID,
status: "value",
value: doppler,
}),
Object.freeze({
quantityId: "wavePhase",
unit: "rad",
semanticKind: "angle",
ownerId: OWNER_ID,
status: "value",
value: val1_K,
}),
Object.freeze({
quantityId: "lorentzFactor",
unit: "1",
semanticKind: "lorentz-factor",
ownerId: OWNER_ID,
status: "value",
value: g,
}),
Object.freeze({
quantityId: "classicalObserverDopplerFactor",
unit: "1",
semanticKind: "ratio",
ownerId: OWNER_ID,
status: "value",
value: classicalObserver,
}),
Object.freeze({
quantityId: "classicalSourceDopplerFactor",
unit: "1",
semanticKind: "ratio",
ownerId: OWNER_ID,
status: "value",
value: classicalSource,
}),
Object.freeze({
quantityId: "recedingDopplerFactor",
unit: "1",
semanticKind: "ratio",
ownerId: OWNER_ID,
status: "value",
value: recedingFactor,
}),
Object.freeze({
quantityId: "approachingDopplerFactor",
unit: "1",
semanticKind: "ratio",
ownerId: OWNER_ID,
status: "value",
value: approachingFactor,
}),
];
return Object.freeze({
beta,
gamma: g,
propagationAngleStationaryDeg: thetaDeg,
propagationAngleStationaryRad: thetaRad,
propagationAngleMovingDeg: theta_k_deg,
propagationAngleMovingRad: ab.thetaPrimeRad,
cosThetaStationary: cosThetaK,
sinThetaStationary: sinThetaK,
cosThetaMoving: ab.cosThetaPrime,
sinThetaMoving: ab.sinThetaPrime,
waveFrequencyStationaryHz: nuK,
waveFrequencyMovingHz: nu_k,
waveAngularFrequencyStationary: omegaK,
waveAngularFrequencyMoving: omega_k,
dopplerFactor: doppler,
amplitudeFactor: doppler,
lineOfSightRecedingFactor: recedingFactor,
lineOfSightApproachingFactor: approachingFactor,
detectorCrossings,
detectorProperRateHz,
earthOrbitAberrationArcsec,
earthOrbitAberrationFormatted,
secondOrderShift: secondOrderShiftVal,
secondOrderComparisonSpeed: secondOrderSpeed,
events,
results,
status: "value",
});
}The site’s own reference code for this law, in TypeScript. It computes the numbers above when the page runs it, and it is what the page uses wherever a FrankenSim result is not available.
phaseAtEvent · src/physics/reference/waves.ts · this site’s source, as built · sha256:f28ee2f8d5ebf08961325ceeae3e003e27155b8fb8e75e8b79852f0aa2340adb
This function computes the listed outputs when it runs.
In words
The phase of a wave at one event: the wave vector dotted into the position, less the angular frequency times the time. It is the quantity that must come out the same in every frame, which is why it is computed here rather than assumed, and a nonfinite coordinate or component is refused by name before the arithmetic.
Implementation
/**
* Evaluates the plane wave phase phi = k . x - omega * t at a given 4-event.
*
* INDEPENDENCE RULE (am-ref-waves-r53 / am-sr-09-doppler-aberration-rabd):
* This function computes the inner product and time product from its arguments alone.
* It MUST NOT call dopplerFactor, aberration, transformWaveVector, or lightComplexFactors,
* directly or indirectly.
*/
export function phaseAtEvent(
coordinates: Event4,
waveComponents: Wave4Vector,
frame: "stationary" | "moving" | "frame-independent" | string = "frame-independent",
): ScientificResult & { readonly frame: string } {
const nonfiniteField = checkNonfinitePhaseField(coordinates, waveComponents);
if (nonfiniteField !== null) {
return Object.freeze({
quantityId: "wavePhase",
unit: "rad",
semanticKind: "angle",
ownerId: OWNER_ID,
status: "outside-domain",
condition: "nonfinite-input",
domainKind: "physical",
reason: `phaseAtEvent requires finite coordinates and wave components; ${nonfiniteField} is not finite.`,
boundary: { parameterId: nonfiniteField, value: 0 },
frame,
});
}
const kDotX =
waveComponents.kx * coordinates.x +
waveComponents.ky * coordinates.y +
waveComponents.kz * coordinates.z;
const omegaT = waveComponents.omega * coordinates.t;
const phase = kDotX - omegaT;
return Object.freeze({
quantityId: "wavePhase",
unit: "rad",
semanticKind: "angle",
ownerId: OWNER_ID,
status: "value",
value: phase,
frame,
});
}The explanation
Full explanation
The phase of a plane wave is the same number in every frame, and that fixes both the Doppler factor and the aberration of direction. At 0.6c, light travelling in the direction of motion drops from 500 THz to 250 THz.
Show every step of the investigation
Pick a direction for the light, set the speed, and read the new frequency and angle. Compare the case along the motion, where the frequency is halved at 0.6c, with the case across it, where it rises by γ to 625 THz.
An explanatory model, not an observation of nature. This embed starts from the laboratory’s worked defaults, not a saved run. Presentation options change the surrounding guide, never the numerical inputs.