fuzzyworld

tower / interior-flight / files / src / canon / constants.ts

Branch interior-flight · default branch · all branches

constants.ts

Blame: the commit that last changed each line · file · history

ec7e78a8 2026-09-07 Pavel Safronov1/**
2 * Tower canon. See design.md §1.
3 *
4 * Per decisions.md D-007, only the values in FREE PARAMETERS are chosen. Everything
5 * below them is derived, so changing the radius changes the whole world coherently.
6 * test/canon.test.ts pins every derived figure to the number printed in design.md.
7 */
8
9// ---------------------------------------------------------------- free parameters
10export const RADIUS_M = 50_000
11export const LENGTH_M = 540_000
12/**
13 * Canon default: **none**. The hull is sealed (design.md §1.4, D-017).
14 * The 0–16 control in src/canon/slats.ts is retained as an exploration tool for
15 * the author, not because canon is undecided.
16 */
17export const SLAT_COUNT_DEFAULT = 0
18export const SLAT_COUNT_MIN = 0
19export const SLAT_COUNT_MAX = 16
20/** Wisconsin's land area. Slats are sized to leave exactly this much land (D-002). */
21export const TARGET_LAND_AREA_M2 = 140_268e6
22
23// ------------------------------------------------------------------------ physics
24export const G0 = 9.80665
25export const SOLAR_CONSTANT_W_M2 = 1361
26/** Speed of light, for radiation pressure. */
27export const C_M_S = 2.998e8
28
29// ----------------------------------------------------------------------- geometry
30export const CIRCUMFERENCE_M = 2 * Math.PI * RADIUS_M
31export const INNER_AREA_M2 = CIRCUMFERENCE_M * LENGTH_M
32
33// ------------------------------------------------------------- spin (design.md §1.2)
34/** Angular velocity giving exactly 1 g at the floor. */
35export const OMEGA_RAD_S = Math.sqrt(G0 / RADIUS_M)
36export const SPIN_PERIOD_S = (2 * Math.PI) / OMEGA_RAD_S
37export const RIM_SPEED_M_S = OMEGA_RAD_S * RADIUS_M
38/** Gravity at radius `s` from the axis. Zero on the axis, 1 g at the floor. */
39export const gravityAtRadius = (s: number): number => OMEGA_RAD_S ** 2 * s
40/** Gravity at height `h` above the floor. */
41export const gravityAtHeight = (h: number): number => gravityAtRadius(RADIUS_M - h)
42
43// ------------------------------------------------------------ slats (design.md §1.4)
44/**
45 * Total slat width is fixed by the land-area target (D-002) and does NOT depend on
46 * how many slats that width is divided into. So changing the slat count changes
47 * how wide each one is, and leaves Tower's land area exactly Wisconsin's.
48 */
49export const SLAT_TOTAL_WIDTH_M = (INNER_AREA_M2 - TARGET_LAND_AREA_M2) / LENGTH_M
50/** Land area with at least one slat. With none, the land is the whole surface. */
51export const LAND_AREA_M2 = CIRCUMFERENCE_M * LENGTH_M - SLAT_TOTAL_WIDTH_M * LENGTH_M
52
53// ------------------------------------------------------- sun system (design.md §1.3)
54export const BEAM_CIRCUMFERENCE_M = 10_000
55export const BEAM_RADIUS_M = BEAM_CIRCUMFERENCE_M / (2 * Math.PI)
56export const BEAM_AREA_M2 = Math.PI * BEAM_RADIUS_M ** 2
57/** Odeillo-class, and 11% of the étendue limit. See D-003. */
58export const BEAM_CONCENTRATION = 5_000
59export const BEAM_INTENSITY_W_M2 = SOLAR_CONSTANT_W_M2 * BEAM_CONCENTRATION
60export const BEAM_POWER_W = BEAM_AREA_M2 * BEAM_INTENSITY_W_M2
61/** Thermodynamic ceiling on passive concentration at 1 AU (D-003). */
62export const ETENDUE_LIMIT = 46_223
63
9c891d2e 2026-09-08 Pavel Safronov64/** The sun's disc from Tower's orbit — 0.53°, and the reason the eclipse is total. */
65export const SUN_ANGULAR_DIAMETER_RAD = 0.53 * Math.PI / 180
ec7e78a8 2026-09-07 Pavel Safronov66export const COLLECTOR_EFFICIENCY = 0.85
67export const COLLECTOR_AREA_M2 = BEAM_POWER_W / SOLAR_CONSTANT_W_M2 / COLLECTOR_EFFICIENCY
68export const COLLECTOR_DIAMETER_M = 2 * Math.sqrt(COLLECTOR_AREA_M2 / Math.PI)
9c891d2e 2026-09-08 Pavel Safronov69/**
70 * How far ahead of the sun cap the collector flies. Not fixed by canon, but it has
71 * to live here rather than in one view: both 3D views draw the collector now, and
72 * the interior needs it to decide which part of the sun cap window it covers.
73 */
74export const COLLECTOR_STANDOFF_M = 320_000
75/**
76 * The collector is **41.8° across from the sun cap — 79 times the sun's diameter**,
77 * so it occults the sun completely and Tower sits in its umbra permanently. Nobody
78 * aboard can see the sun, for the same reason nobody can see Earth (§1.4). From
79 * inside, the back of it is the only object in the sky that is neither Tower nor a
80 * star, and it never moves. D-050.
81 */
82export const COLLECTOR_ANGULAR_DIAMETER_RAD =
83 2 * Math.atan(COLLECTOR_DIAMETER_M / 2 / COLLECTOR_STANDOFF_M)
49b0d64d 2026-09-08 Pavel Safronov84/**
85 * The sunlit rim.
86 *
87 * The collector's face is a mirror pointed away from Tower, so from inside it is a
88 * black disc — but it is a **backlit** black disc, and its edge is not. The array
89 * has to hold 46,938 km² flat, and the structure that does it stands proud of the
90 * mirror plane at the rim, where the sun strikes it side-on. Add the scatter any
91 * real mirror throws at grazing incidence and the edge reads as a thin sun-coloured
92 * ring: a crescent of lit structure with nothing behind it. 4 km on a 244 km disc.
93 *
94 * Note what it is *not*: sunlight leaking past the edge. Tower sits wholly inside
95 * the umbra (below), so the sun's disc is hidden from every point in the world. The
96 * ring is lit structure, not a sliver of star. D-054.
97 */
98export const COLLECTOR_RIM_M = 4000
99
17ccee99 2026-09-08 Pavel Safronov100// ------------------------------------------------ the collector clock (D-055)
101/**
102 * Tower's clock is painted on the outside of the world, because the collector is
103 * the only thing in its sky that does not turn.
104 *
105 * Everything inside co-rotates with the hull, and everything outside appears to
106 * wheel once every 7 min 29 s (§1.4's pinwheel). The collector is the exception
107 * twice over: it flies in formation rather than spinning, and it is always in the
108 * same place — dead ahead through the sun cap window. That makes it the one surface
109 * in the world worth writing on.
110 *
111 * **It is read as an angle, not as a hand against a dial**, and it has to be. From
112 * inside, the whole face appears to rotate at Tower's spin, so any single mark is
113 * useless — but the angle *between* two marks is invariant under that rotation.
114 * Hence one fixed line and one moving one: a clock for people who cannot stop the
115 * dial turning.
116 */
117export const COLLECTOR_CLOCK_MARK_M = 2200
118/** The hand stops short of the rim, so it is never confused with the lit edge. */
119export const COLLECTOR_CLOCK_HAND_FRACTION = 0.72
120/**
121 * Lit deliberately rather than found: it is an instrument, so Tower runs it. A
122 * 2.2 km line down a 122 km radius is ~5.4e8 m²; at the ~1 W/m² it takes to read
123 * against the haze that is **0.5 GW**, which against 54.3 TW is one part in a
124 * hundred thousand. The swarm spends more than that holding its own station.
125 */
126export const COLLECTOR_CLOCK_POWER_W = 0.5e9
127
9c891d2e 2026-09-08 Pavel Safronov128/**
129 * Umbra radius at the sun cap. Tower's 51 km sits well inside it, which is what
130 * makes the eclipse total rather than partial.
131 */
132export const COLLECTOR_UMBRA_RADIUS_M =
133 COLLECTOR_DIAMETER_M / 2 - COLLECTOR_STANDOFF_M * (SUN_ANGULAR_DIAMETER_RAD / 2)
ec7e78a8 2026-09-07 Pavel Safronov134
135// ---------------------------------------------------- diffuser swarm (design.md §1.3.1)
136export const MIRRORLET_EDGE_M = 0.012
137export const MIRRORLET_AREA_M2 = MIRRORLET_EDGE_M ** 2
138export const MIRRORLET_MASS_KG = 2.0e-6
139/** Projected mirror area needed to divert the whole beam, tilted ~45°. */
140export const SWARM_AREA_M2 = BEAM_AREA_M2 * Math.SQRT2
141export const SWARM_COUNT = SWARM_AREA_M2 / MIRRORLET_AREA_M2
142export const SWARM_MASS_KG = SWARM_COUNT * MIRRORLET_MASS_KG
143export const SWARM_OPTICAL_DEPTH = (SWARM_COUNT / (BEAM_AREA_M2 * LENGTH_M)) * MIRRORLET_AREA_M2 * LENGTH_M
144/** Green-light diffraction limit of one mirrorlet — sets Tower's aiming resolution. */
145export const MIRRORLET_DIFFRACTION_RAD = 550e-9 / MIRRORLET_EDGE_M
146export const AIM_SPOT_AT_FLOOR_M = MIRRORLET_DIFFRACTION_RAD * RADIUS_M
147
148// --------------------------------------------------- illumination (design.md §1.3)
149/** Mean insolation settings, W/m² over the whole inner surface. */
150export const BRIGHTNESS_TWILIGHT_W_M2 = 30
151export const BRIGHTNESS_TEMPERATE_W_M2 = 200
152export const BRIGHTNESS_FULL_W_M2 = BEAM_POWER_W / INNER_AREA_M2
da6bcb0e 2026-09-08 Pavel Safronov153// ------------------------------------------- the far cap mirror (design.md §1.3.2)
154/**
155 * What survives the swarm arrives collimated, in the beam's own cross-section —
156 * 7.96 km², a thousandth of the far cap's 7,854. So the load is not 13 TW spread
157 * over a disc; it is **1.7 MW/m² in a 3.2 km spot**, some 1,200 suns, which an
158 * absorber would sit at 2,330 K to re-radiate. D-048 puts a mirror there instead.
159 */
160export const BEAM_FAR_CAP_FLUX_W_M2 =
161 (BEAM_POWER_W * Math.exp(-SWARM_OPTICAL_DEPTH)) / BEAM_AREA_M2
162/** Sized to the beam's 1,592 m with a margin, since the spot is not held perfectly. */
163export const FAR_CAP_MIRROR_RADIUS_M = 2000
164/**
165 * A mirror is the cheap thermal answer, not the expensive one: at 99.9% it absorbs
166 * about 1.2 Earth solar constants, which is a warm plate rather than a glowing one.
167 */
168export const FAR_CAP_MIRROR_REFLECTIVITY = 0.999
169export const FAR_CAP_MIRROR_ABSORBED_W_M2 =
170 BEAM_FAR_CAP_FLUX_W_M2 * (1 - FAR_CAP_MIRROR_REFLECTIVITY)
171/**
172 * Returning the residual for a second pass makes the optical column 1,080 km
173 * rather than 540, so utilisation is 1 - e^(-2τ) instead of 1 - e^(-τ): **94%**
174 * against 76%. What survives even that exits the beam aperture toward the
175 * collector, which returns it again — so in the limit the whole 54.3 TW lands on
176 * the inner surface, and BRIGHTNESS_FULL_W_M2 is right as it stands (D-048).
177 */
178export const BEAM_UTILISATION_ONE_PASS = 1 - Math.exp(-SWARM_OPTICAL_DEPTH)
179export const BEAM_UTILISATION_TWO_PASS = 1 - Math.exp(-2 * SWARM_OPTICAL_DEPTH)
180
181/**
182 * The beam's own glow, seen from the floor (design.md §1.3).
183 *
184 * Radiance is the figure §1.3 states — 26x a clear blue sky — and is taken from
185 * the doc rather than re-derived here; the derivation needs the Rayleigh phase
186 * function and the air density at the axis, and a second version of it that
187 * disagreed by 30% would be worse than none. The angular width IS derived, and
188 * comes out at the 3.65° §1.3 prints.
189 */
190export const BEAM_RADIANCE_W_M2_SR = 779
191export const BEAM_ANGULAR_WIDTH_RAD = 2 * Math.atan(BEAM_RADIUS_M / RADIUS_M)
192
392bb8a7 2026-09-08 Pavel Safronov193/**
194 * Night is made by banking the beam, not by aiming the swarm away — §1.3 says the
195 * glow belongs to the beam and the air, so a lit beam lights the world whatever
196 * the diffusers do. A thousandth of full leaves a rod you can still find overhead
197 * and a world you can just make out under it. D-046.
198 */
199export const NIGHT_BEAM_FRACTION = 0.001
200export const BRIGHTNESS_NIGHT_W_M2 = BRIGHTNESS_FULL_W_M2 * NIGHT_BEAM_FRACTION
ec7e78a8 2026-09-07 Pavel Safronov201
2bb3e0de 2026-09-08 Pavel Safronov202// ------------------------------------------------ the day cycle (design.md §1.3.4)
203/**
204 * Tower's day. Nothing physical sets it — there is no rotation to inherit one from,
205 * since the spin is 7.5 minutes and has nothing to do with light. It is chosen for
206 * the people living under it, so it is Earth's.
207 */
208export const DAY_LENGTH_S = 86_400
209/**
210 * How long the light takes to cross the world at dawn, and again at dusk.
211 *
212 * The swarm cannot brighten everywhere at once and would not want to: the beam
213 * enters at the sun cap and the diffusers hand it down the axis, so the natural
214 * thing — and the only thing that gives a world with no east a direction for its
215 * mornings — is to bring it up as a front running deepward. 40 minutes over 540 km
216 * is **225 m/s**, which is a shadow line moving at about the rim speed of Tower
217 * itself, and slow enough to stand and watch arrive. D-053.
218 */
219export const DAWN_SWEEP_S = 2400
220export const DAWN_FRONT_SPEED_M_S = LENGTH_M / DAWN_SWEEP_S
221/** The lag between the sun cap's morning and the far cap's — Tower's only longitude. */
222export const DAY_SWEEP_FRACTION = DAWN_SWEEP_S / DAY_LENGTH_S
223
de65ab10 2026-09-08 Pavel Safronov224// --------------------------------------------------------- structure (design.md §1.1)
225/**
226 * Wall depth, floor to outer skin. Canon had no figure for this; 1 km is 2% of the
227 * radius — enough to read as built from outside and to give the cap windows a
228 * reveal you can see the depth of, without making Tower a pipe. D-044.
229 */
230export const HULL_DEPTH_M = 1000
231
ec7e78a8 2026-09-07 Pavel Safronov232// --------------------------------------------------- atmosphere (design.md §1.5)
233export const SPECIFIC_GAS_CONSTANT = 287
234export const TEMPERATURE_K = 288
235export const FLOOR_PRESSURE_PA = 101_325
236export const FLOOR_DENSITY_KG_M3 = FLOOR_PRESSURE_PA / (SPECIFIC_GAS_CONSTANT * TEMPERATURE_K)
237/** Rayleigh volume scattering coefficient at floor density, 550 nm. */
238export const RAYLEIGH_FLOOR_PER_M = 1.16e-5