/** * The simulated sky, as a plan rather than as positions. * * The engine's `SimulatedFlights` already evaluates a `SimRoute[]` in closed * form against wall-clock time, so the server has nothing to simulate — it hands * over the routes, a fixed phase origin and a seed, and every browser arrives at * the same answer. One cacheable request replaces a poll per second, and two * people on different machines see the same aircraft in the same places, which * a per-client simulation cannot promise and which is the point of doing it this * way. * * ### Where the coordinates came from * * The seven airport positions — SFO, OAK and SJC in the north, LAX, BUR, LGB and * SNA in the south — are published FAA airport reference points, typed in by * hand. They are US government facts in the public domain, and — as with every * other coordinate in this repo — emphatically not derived from OpenStreetMap. * Everything else here is a waypoint someone made up so the legs go the right * way. See ARCHITECTURE.md §3.2 and CONTRACT.md §8. * * The callsigns are invented, with operator prefixes that belong to nobody. A * repo that refuses to ship other people's logos should not ship their flight * numbers either. */ import type { WireSimRoute } from "../../../src/server/wire.ts"; const SFO: [number, number] = [37.6188, -122.375]; const OAK: [number, number] = [37.7213, -122.2207]; const SJC: [number, number] = [37.3639, -121.9289]; /** * Departures climb, arrivals descend, and a third of the traffic is just passing * through at cruise. That mix is what makes the sky read as a working airspace * rather than a carousel. */ const BAY_AREA: WireSimRoute[] = [ // Departures — out over the Pacific, north-east over the Central Valley, and // south down the peninsula. { callsign: "LMB231", from: SFO, to: [37.3, -123.2], fromAlt: 20, toAlt: 10500, duration: 420 }, { callsign: "LMB778", from: SFO, to: [38.4, -121.3], fromAlt: 20, toAlt: 11000, duration: 480 }, { callsign: "PAC1082", from: SFO, to: [38.6, -122.9], fromAlt: 20, toAlt: 10000, duration: 460 }, { callsign: "BAY412", from: OAK, to: [37.0, -121.2], fromAlt: 20, toAlt: 9500, duration: 400 }, { callsign: "SIE1440", from: SJC, to: [36.6, -121.6], fromAlt: 20, toAlt: 9800, duration: 430 }, // Arrivals — the long descent over Point Reyes, the south-east downwind, and // the two east-bay finals. { callsign: "GLD566", from: [38.3, -123.1], to: SFO, fromAlt: 6000, toAlt: 20, duration: 500 }, { callsign: "LMB1889", from: [37.1, -121.6], to: SFO, fromAlt: 5500, toAlt: 20, duration: 520 }, { callsign: "RDW915", from: [37.9, -121.3], to: OAK, fromAlt: 5200, toAlt: 20, duration: 470 }, { callsign: "PAC331", from: [38.1, -122.3], to: SJC, fromAlt: 5800, toAlt: 20, duration: 540 }, // Overflights, level the whole way. { callsign: "GLD55", from: [38.8, -122.6], to: [36.6, -121.4], fromAlt: 11000, toAlt: 11000, duration: 620, }, { callsign: "PAC21", from: [37.9, -121.4], to: [37.2, -123.4], fromAlt: 10500, toAlt: 10500, duration: 700, }, { callsign: "RDW1205", from: [36.5, -122.4], to: [38.9, -122.0], fromAlt: 10800, toAlt: 10800, duration: 660, }, // Low and slow across the bay: general aviation is most of what is actually // visible from the ground, and it is the only traffic that reads as *near*. { callsign: "BAY7789", from: [37.45, -122.1], to: [38.05, -122.45], fromAlt: 900, toAlt: 900, duration: 520, }, { callsign: "SIE4402", from: [37.95, -122.55], to: [37.35, -121.85], fromAlt: 1400, toAlt: 1400, duration: 560, }, ]; const LAX: [number, number] = [33.9425, -118.4081]; const BUR: [number, number] = [34.2007, -118.3585]; const LGB: [number, number] = [33.8177, -118.1516]; const SNA: [number, number] = [33.6757, -117.8682]; /** * The Southland, which is a different shape of airspace and not a translated * copy of the Bay Area. * * Almost everything here runs east–west, because the terrain does: the basin is * walled by the San Gabriels to the north, so departures go out over the water * and turn, and arrivals come down the length of the valley. Four fields instead * of three, and the two small ones matter more than they do in the north — a * board four times the area at a third of the scale reads as empty very fast if * everything is at cruise. */ const SOCAL: WireSimRoute[] = [ // Departures — the standard west-over-the-ocean climb, the north-east haul // over the Cajon Pass, and the two valley fields going their own way. { callsign: "LMB604", from: LAX, to: [33.6, -119.3], fromAlt: 20, toAlt: 10500, duration: 440 }, { callsign: "PAC712", from: LAX, to: [34.6, -117.1], fromAlt: 20, toAlt: 11500, duration: 520 }, { callsign: "SIE288", from: BUR, to: [34.9, -118.9], fromAlt: 20, toAlt: 9800, duration: 460 }, { callsign: "BAY1516", from: SNA, to: [33.1, -118.6], fromAlt: 20, toAlt: 9600, duration: 420 }, { callsign: "GLD843", from: LGB, to: [34.5, -116.9], fromAlt: 20, toAlt: 10200, duration: 500 }, // Arrivals — the long straight-in from the east, the coastal descent from the // north-west, and one down the back of the mountains into Burbank. { callsign: "LMB1170", from: [34.1, -116.8], to: LAX, fromAlt: 6200, toAlt: 20, duration: 560 }, { callsign: "RDW425", from: [34.7, -119.4], to: LAX, fromAlt: 6800, toAlt: 20, duration: 600 }, { callsign: "PAC96", from: [34.8, -117.6], to: BUR, fromAlt: 5400, toAlt: 20, duration: 480 }, { callsign: "SIE1901", from: [33.2, -117.2], to: SNA, fromAlt: 5000, toAlt: 20, duration: 450 }, // Overflights, level the whole way: the coastal corridor and the desert one. { callsign: "GLD1330", from: [34.9, -119.2], to: [32.9, -117.0], fromAlt: 11200, toAlt: 11200, duration: 690, }, { callsign: "RDW78", from: [33.0, -119.1], to: [34.9, -116.8], fromAlt: 10600, toAlt: 10600, duration: 720, }, // Low across the basin. General aviation is most of what anybody standing in // Culver City actually sees, and it is the only traffic that reads as *near*. { callsign: "BAY3312", from: [33.78, -118.42], to: [34.16, -117.75], fromAlt: 850, toAlt: 850, duration: 540, }, { callsign: "LMB2044", from: [34.22, -118.72], to: [33.72, -117.98], fromAlt: 1300, toAlt: 1300, duration: 570, }, ]; /** * The airspaces this file has actually laid out, and how close a region's centre * has to be to get one. * * Degrees rather than kilometres because the comparison is a box, not a circle, * and the radii are generous: the Bay Area's is roughly downtown to the far end * of the bay, and the Southland's spans a basin that runs from Ventura to * Riverside. A region centre that lands inside one of these is close enough that * the real airports are the right answer; anything else gets spokes. */ const AIRSPACES: { centre: [number, number]; radius: number; routes: WireSimRoute[] }[] = [ { centre: [37.7749, -122.4194], radius: 0.75, routes: BAY_AREA }, { centre: [33.82, -118.05], radius: 1.0, routes: SOCAL }, ]; /** * A plan for a city this file has never heard of. * * A self-hoster pointing `TERA_REGIONS` (or `TERA_ORIGIN_LAT/LNG`) at somewhere * that is neither of the two boards should get moving aircraft rather than an * empty sky, so eight legs are laid out on evenly-spaced bearings through the * region centre. It is not their city's real airspace and does not pretend to be * — it is motion in the right kind of place, which is all the map ever wanted * from this. */ function genericPlan(lat: number, lng: number): WireSimRoute[] { const routes: WireSimRoute[] = []; const span = 0.55; for (let i = 0; i < 8; i++) { const bearing = (i / 8) * Math.PI * 2; const dLat = Math.cos(bearing) * span; const dLng = (Math.sin(bearing) * span) / Math.max(0.2, Math.cos((lat * Math.PI) / 180)); const cruise = 8000 + (i % 4) * 900; routes.push({ callsign: `LMB${100 + i * 37}`, from: [lat - dLat, lng - dLng], to: [lat + dLat, lng + dLng], fromAlt: i % 3 === 0 ? 600 : cruise, toAlt: cruise, duration: 380 + i * 45, }); } return routes; } export function planFor(centre: { lat: number; lng: number }): WireSimRoute[] { for (const airspace of AIRSPACES) { const [lat, lng] = airspace.centre; if ( Math.abs(centre.lat - lat) < airspace.radius && Math.abs(centre.lng - lng) < airspace.radius ) { return airspace.routes; } } return genericPlan(centre.lat, centre.lng); }