Add power-ladder scene to opening paragraph
This commit is contained in:
parent
83cdeb5fce
commit
9d81de447d
2 changed files with 109 additions and 0 deletions
|
|
@ -10,6 +10,10 @@ draft = true
|
||||||
|
|
||||||
Take an antenna. Point it at the Moon. Transmit. A microscopic fraction of what you sent makes it across 384,000 km of empty space, smacks into the pocked gray surface, scatters off in every direction because the Moon is a deeply unimpressive mirror, and a microscopic fraction of that comes back. A few kilowatts leave; a few attowatts return. On the other side of the planet, someone pointed at the same Moon hears it.
|
Take an antenna. Point it at the Moon. Transmit. A microscopic fraction of what you sent makes it across 384,000 km of empty space, smacks into the pocked gray surface, scatters off in every direction because the Moon is a deeply unimpressive mirror, and a microscopic fraction of that comes back. A few kilowatts leave; a few attowatts return. On the other side of the planet, someone pointed at the same Moon hears it.
|
||||||
|
|
||||||
|
<div id="scene-scale"></div>
|
||||||
|
|
||||||
|
A kilowatt is 1000 watts. An attowatt is 10<sup>-18</sup> watts. That's twenty-one decades between what you put in and what eventually comes out the other end, which is roughly the difference between the Sun's output and a single LED in somebody's living room.
|
||||||
|
|
||||||
This is moonbounce. As a method of having a radio contact it is absurd in a way that very little else in the hobby manages. You can sit a non-ham at a reasonable HF station and explain how a signal gets from Texas to Argentina, and if they squint they'll accept it. Try to explain moonbounce with a straight face and you look slightly unwell.
|
This is moonbounce. As a method of having a radio contact it is absurd in a way that very little else in the hobby manages. You can sit a non-ham at a reasonable HF station and explain how a signal gets from Texas to Argentina, and if they squint they'll accept it. Try to explain moonbounce with a straight face and you look slightly unwell.
|
||||||
|
|
||||||
It does, annoyingly, just work. You need respectable antennas, respectable power, and a computer willing to spend about a minute per transmission sifting what came back from what didn't.
|
It does, annoyingly, just work. You need respectable antennas, respectable power, and a computer willing to spend about a minute per transmission sifting what came back from what didn't.
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,110 @@
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------- Scene 0: kW to aW power ladder ----------
|
||||||
|
function sceneScale() {
|
||||||
|
const s = scene('scene-scale', {
|
||||||
|
height: 230,
|
||||||
|
caption: 'A logarithmic ladder from 1 kW on the right to 1 aW on the left. Each tick is a factor of ten. There are twenty-one of them, and the pulse that starts out at the transmitter has to fall all the way to the far end before anyone can decode it.',
|
||||||
|
});
|
||||||
|
if (!s) return;
|
||||||
|
|
||||||
|
const marks = [
|
||||||
|
{ p: 1e3, label: '1 kW — your transmitter', color: C.red },
|
||||||
|
{ p: 1, label: '1 W — a handheld HT', color: C.orange },
|
||||||
|
{ p: 1e-3, label: '1 mW — an indicator LED', color: C.yellow },
|
||||||
|
{ p: 1e-6, label: '1 μW', color: C.green },
|
||||||
|
{ p: 1e-9, label: '1 nW — strong Wi-Fi at the receiver', color: C.cyan },
|
||||||
|
{ p: 1e-12, label: '1 pW', color: C.blue },
|
||||||
|
{ p: 1e-15, label: '1 fW — weak HF DX at the antenna', color: '#b199e4' },
|
||||||
|
{ p: 1e-18, label: '1 aW — what comes back from the Moon', color: C.magenta },
|
||||||
|
];
|
||||||
|
|
||||||
|
const start = performance.now();
|
||||||
|
function draw() {
|
||||||
|
const { w, h } = s.getSize();
|
||||||
|
const ctx = s.ctx;
|
||||||
|
clear(ctx, w, h);
|
||||||
|
|
||||||
|
const m = { l: 30, r: 30, t: 70, b: 70 };
|
||||||
|
const pW = w - m.l - m.r;
|
||||||
|
const barY = m.t + 20;
|
||||||
|
const barH = 14;
|
||||||
|
|
||||||
|
// log scale: logP from -18 to +3
|
||||||
|
const xOf = logP => m.l + (logP + 18) / 21 * pW;
|
||||||
|
|
||||||
|
// Gradient bar
|
||||||
|
const grad = ctx.createLinearGradient(m.l, 0, m.l + pW, 0);
|
||||||
|
grad.addColorStop(0, 'rgba(199, 141, 224, 0.22)');
|
||||||
|
grad.addColorStop(0.5, 'rgba(109, 197, 211, 0.30)');
|
||||||
|
grad.addColorStop(1, 'rgba(229, 128, 137, 0.55)');
|
||||||
|
ctx.fillStyle = grad;
|
||||||
|
if (ctx.roundRect) { ctx.beginPath(); ctx.roundRect(m.l, barY, pW, barH, 4); ctx.fill(); }
|
||||||
|
else ctx.fillRect(m.l, barY, pW, barH);
|
||||||
|
ctx.strokeStyle = C.grid;
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
if (ctx.roundRect) { ctx.beginPath(); ctx.roundRect(m.l, barY, pW, barH, 4); ctx.stroke(); }
|
||||||
|
else ctx.strokeRect(m.l, barY, pW, barH);
|
||||||
|
|
||||||
|
// Minor ticks at every decade (22 positions from -18..+3)
|
||||||
|
for (let d = -18; d <= 3; d++) {
|
||||||
|
const x = xOf(d);
|
||||||
|
const important = marks.some(mk => Math.abs(Math.log10(mk.p) - d) < 0.01);
|
||||||
|
ctx.strokeStyle = important ? C.fg : C.dim;
|
||||||
|
ctx.lineWidth = important ? 1.5 : 0.75;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, barY - (important ? 10 : 5));
|
||||||
|
ctx.lineTo(x, barY + barH + (important ? 10 : 5));
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Labels — alternate above / below to avoid collisions
|
||||||
|
marks.forEach((mk, i) => {
|
||||||
|
const x = xOf(Math.log10(mk.p));
|
||||||
|
const above = i % 2 === 0;
|
||||||
|
const y = above ? barY - 18 : barY + barH + 22;
|
||||||
|
pill(ctx, mk.label, x, y, mk.color, 'center');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Animated pulse from kW (right) to aW (left) every 6 s
|
||||||
|
const t = ((performance.now() - start) / 1000) % 6;
|
||||||
|
const pulseLogP = 3 - (t / 6) * 21;
|
||||||
|
const pulseX = xOf(pulseLogP);
|
||||||
|
const pulseY = barY + barH / 2;
|
||||||
|
// Visual radius shrinks *slightly* (log-visible), not exponentially or it becomes invisible
|
||||||
|
const visualR = 3 + 12 * (pulseLogP + 18) / 21;
|
||||||
|
// Glow
|
||||||
|
const glow = ctx.createRadialGradient(pulseX, pulseY, 0, pulseX, pulseY, visualR * 3);
|
||||||
|
glow.addColorStop(0, 'rgba(226, 195, 125, 0.9)');
|
||||||
|
glow.addColorStop(0.5, 'rgba(226, 195, 125, 0.2)');
|
||||||
|
glow.addColorStop(1, 'rgba(226, 195, 125, 0)');
|
||||||
|
ctx.fillStyle = glow;
|
||||||
|
ctx.beginPath(); ctx.arc(pulseX, pulseY, visualR * 3, 0, Math.PI * 2); ctx.fill();
|
||||||
|
ctx.fillStyle = C.yellow;
|
||||||
|
ctx.beginPath(); ctx.arc(pulseX, pulseY, visualR, 0, Math.PI * 2); ctx.fill();
|
||||||
|
|
||||||
|
// Current-power readout riding the pulse
|
||||||
|
const currentW = Math.pow(10, pulseLogP);
|
||||||
|
let currentStr;
|
||||||
|
if (currentW >= 1) currentStr = `${currentW.toFixed(0)} W`;
|
||||||
|
else if (currentW >= 1e-3) currentStr = `${(currentW * 1e3).toFixed(1)} mW`;
|
||||||
|
else if (currentW >= 1e-6) currentStr = `${(currentW * 1e6).toFixed(1)} μW`;
|
||||||
|
else if (currentW >= 1e-9) currentStr = `${(currentW * 1e9).toFixed(1)} nW`;
|
||||||
|
else if (currentW >= 1e-12) currentStr = `${(currentW * 1e12).toFixed(1)} pW`;
|
||||||
|
else if (currentW >= 1e-15) currentStr = `${(currentW * 1e15).toFixed(1)} fW`;
|
||||||
|
else currentStr = `${(currentW * 1e18).toFixed(1)} aW`;
|
||||||
|
pill(ctx, currentStr, pulseX, pulseY + 42, C.yellow, 'center');
|
||||||
|
|
||||||
|
// Title
|
||||||
|
text(ctx, 'power out vs power back: 21 orders of magnitude', w / 2, 22, C.fg, 'center', 'alphabetic', 13);
|
||||||
|
text(ctx, '(+30 dBW on one end, −180 dBW on the other)', w / 2, 40, C.dim, 'center', 'alphabetic', 11);
|
||||||
|
|
||||||
|
requestAnimationFrame(draw);
|
||||||
|
}
|
||||||
|
requestAnimationFrame(draw);
|
||||||
|
}
|
||||||
|
|
||||||
// ---------- Scene 1: Earth-Moon geometry ----------
|
// ---------- Scene 1: Earth-Moon geometry ----------
|
||||||
function sceneGeometry() {
|
function sceneGeometry() {
|
||||||
const s = scene('scene-geometry', {
|
const s = scene('scene-geometry', {
|
||||||
|
|
@ -1155,6 +1259,7 @@
|
||||||
|
|
||||||
// Init after DOM
|
// Init after DOM
|
||||||
function init() {
|
function init() {
|
||||||
|
sceneScale();
|
||||||
sceneGeometry();
|
sceneGeometry();
|
||||||
sceneDistance();
|
sceneDistance();
|
||||||
scenePathloss();
|
scenePathloss();
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue