import type { ElementSet } from "../satellites/types.ts"; /** * Where the watcher stands: degrees of latitude (north is positive) and longitude (east is positive), and the height above sea level in metres (0 by default). * * @example * ```ts * import type { Observer } from "@johnmorrisdotca/chikyu-earth/passes"; * * const tokyo: Observer = { latitude: 35.68, longitude: 139.69, heightM: 40 }; * console.log(tokyo.latitude); * ``` */ export interface Observer { latitude: number; longitude: number; heightM?: number; } /** * The sixteen points of the compass, the words a pass table uses for a direction. * * @example * ```ts * import type { Compass } from "@johnmorrisdotca/chikyu-earth/passes"; * * const south: Compass = "S"; * console.log(south); * ``` */ export type Compass = "N" | "NNE" | "NE" | "ENE" | "E" | "ESE" | "SE" | "SSE" | "S" | "SSW" | "SW" | "WSW" | "W" | "WNW" | "NW" | "NNW"; /** * The compass word for a direction. * * @param azimuthDeg - Degrees clockwise from north: 0 is north, 90 east, 180 south, 270 west. * @returns One of sixteen words, "N" to "NNW". * * @example * ```ts * import { compassOf } from "@johnmorrisdotca/chikyu-earth/passes"; * * console.log(compassOf(200)); * ``` */ export declare function compassOf(azimuthDeg: number): Compass; /** * How dark the watcher's sky is, by the Sun's altitude: `day` (the Sun is up), `civil` twilight (the Sun is below the horizon by up to 6 degrees: the brightest stars and planets show), `nautical` (6 to 12 degrees), * `astronomical` (12 to 18 degrees) and `night` (more than 18 degrees below). * * @example * ```ts * import type { SkyName } from "@johnmorrisdotca/chikyu-earth/passes"; * * const sky: SkyName = "civil"; * console.log(sky); * ``` */ export type SkyName = "day" | "civil" | "nautical" | "astronomical" | "night"; /** * The name of the sky for a Sun altitude. * * @param sunAltitudeDeg - The Sun's altitude in degrees: above 0 it is up. * @returns `day` above -0.833 degrees (sunrise and sunset), then `civil`, `nautical`, `astronomical` and `night` at -6, -12 and -18. * * @example * ```ts * import { skyAt } from "@johnmorrisdotca/chikyu-earth/passes"; * * console.log(skyAt(-8)); * ``` */ export declare function skyAt(sunAltitudeDeg: number): SkyName; /** * The Sun's altitude over a place at a moment, in degrees (above 0 the Sun is up). It uses the package's own low-precision Sun, good to about 0.02 degree, and does not add refraction. * * @param ms - A moment, in milliseconds since the Unix epoch. * @param observer - Where the watcher stands. * @returns The altitude of the Sun's centre, in degrees. * * @example * ```ts * import { sunAltitude } from "@johnmorrisdotca/chikyu-earth/passes"; * * // Tokyo at 9 in the evening local time (12:00 UTC) on 9 November 2026 * console.log(sunAltitude(Date.UTC(2026, 10, 9, 12), { latitude: 35.68, longitude: 139.69 }).toFixed(0)); * ``` */ export declare function sunAltitude(ms: number, observer: Observer): number; /** * One moment of a pass: when, which way, how high and how far. * * @example * ```ts * import type { PassPoint } from "@johnmorrisdotca/chikyu-earth/passes"; * * const describe = (point: PassPoint): string => `${point.compass} ${point.altitude.toFixed(0)} degrees`; * console.log(typeof describe); * ``` */ export interface PassPoint { /** The moment, milliseconds since the Unix epoch (UTC; add the place's own offset for local time). */ ms: number; /** Degrees clockwise from north. */ azimuth: number; /** The compass word for `azimuth`. */ compass: Compass; /** Degrees above the horizon (geometric: no refraction). */ altitude: number; /** Distance from the watcher, kilometres. */ rangeKm: number; } /** * Why a pass is or is not visible: `visible`; `daylight` (the watcher's sky is too bright for the whole pass); `shadow` (the sky is dark enough but the satellite is in the Earth's shadow for all of it); * `faint` (it is lit and the sky is dark, but it is fainter than the limiting magnitude). * * @example * ```ts * import type { PassReason } from "@johnmorrisdotca/chikyu-earth/passes"; * * const reason: PassReason = "shadow"; * console.log(reason); * ``` */ export type PassReason = "visible" | "daylight" | "shadow" | "faint"; /** * The part of a pass a person can see: from when the satellite is lit, the sky is dark enough and it is bright enough and above the lowest altitude, to when one of them stops; and the highest point of that part. * It is what a sky-watcher's table lists as the pass. * * @example * ```ts * import type { VisiblePart } from "@johnmorrisdotca/chikyu-earth/passes"; * * const seconds = (part: VisiblePart): number => (part.end.ms - part.start.ms) / 1000; * console.log(typeof seconds); * ``` */ export interface VisiblePart { start: PassPoint; highest: PassPoint; end: PassPoint; } /** * One pass of a satellite over a place: it comes up through `minAltitude`, reaches its highest and goes down below `minAltitude`, with whether and how it can be seen. Times are UTC milliseconds; show them in the place's own time * by adding its offset. * * @example * ```ts * import type { SatellitePass } from "@johnmorrisdotca/chikyu-earth/passes"; * * const line = (pass: SatellitePass): string => `${pass.name}: ${pass.highest.altitude.toFixed(0)} degrees ${pass.highest.compass}, ${pass.reason}`; * console.log(typeof line); * ``` */ export interface SatellitePass { noradId: number; name: string; /** The pass above `minAltitude`, lit or not. */ start: PassPoint; highest: PassPoint; end: PassPoint; durationSeconds: number; /** The part that can be seen, or undefined when none can. */ visible: VisiblePart | undefined; /** `visible`, or why not. */ reason: PassReason; /** The sky at the watcher's place, at the highest point of the visible part (or of the pass when none is visible). */ sky: SkyName; /** The Sun's altitude then, degrees. */ sunAltitude: number; /** True when the satellite is in sunlight for at least part of the pass above `minAltitude`. */ sunlit: boolean; /** The brightest it is, as a magnitude (smaller is brighter), over the visible part or, when none is visible, over the part that is lit. Undefined when it is never lit or its standard magnitude is not known. */ magnitude: number | undefined; /** The moment of the elements, milliseconds since the Unix epoch. */ elementEpochMs: number; /** Days between the elements' moment and the pass's highest point (negative for a pass before it). */ elementAgeDays: number; /** True when the elements are older than `DEFAULT_DIM_AFTER_DAYS` at the pass: its times are less sure. */ stale: boolean; } /** * What `findPasses` may be asked. * * @example * ```ts * import type { PassOptions } from "@johnmorrisdotca/chikyu-earth/passes"; * * const options: PassOptions = { minAltitude: 10, sky: "civil", all: false }; * console.log(options.minAltitude); * ``` */ export interface PassOptions { /** The lowest altitude, degrees, at which a pass counts as begun and ended (10 by default, as sky-watchers' tables use: below it there are trees, buildings and haze). */ minAltitude?: number; /** The lightest sky a watcher may be in for a pass to count as visible: `civil` (the default: twilight or darker), `nautical`, `astronomical` or `night`. */ sky?: Exclude; /** List every pass, with the reason it cannot be seen, and not only the visible ones (false by default). */ all?: boolean; /** A satellite fainter than this (a larger magnitude) counts as not visible (4 by default: a suburban sky). Only when the satellite's standard magnitude is known. */ limitingMagnitude?: number; /** Standard magnitudes by NORAD number, added to (and over) `STANDARD_MAGNITUDES`. */ magnitudes?: Readonly>; /** Elements older than this many days at a pass leave the pass out (30 by default, `DEFAULT_MAX_AGE_DAYS`). */ maxAgeDays?: number; } /** * The span of time to look in: a pass is listed when its highest point is in it. * * @example * ```ts * import type { PassWindow } from "@johnmorrisdotca/chikyu-earth/passes"; * * const week: PassWindow = { from: Date.UTC(2026, 10, 9), to: Date.UTC(2026, 10, 16) }; * console.log(week.to - week.from); * ``` */ export interface PassWindow { /** The start, milliseconds since the Unix epoch. */ from: number; /** The end, milliseconds since the Unix epoch. */ to: number; } /** * Where one satellite is in the sky over a place at one moment, and what the light is like: its direction and height, its distance, whether the Sun lights it, how high the Sun is, and how bright it looks. * * @param set - The satellite's elements. * @param observer - Where the watcher stands. * @param ms - The moment, milliseconds since the Unix epoch. * @param options - `magnitudes` for your own standard magnitudes (the ISS, Tianhe and Hubble are known). * @returns Its place in the sky (`altitude` below 0 means below the horizon), `sunlit`, the Sun's altitude and the magnitude (undefined when it is in shadow or its standard magnitude is not known); undefined where SGP4 gives no place. * * @example * ```ts * import { satelliteAt } from "@johnmorrisdotca/chikyu-earth/passes"; * * const iss = { * OBJECT_NAME: "ISS (ZARYA)", * EPOCH: "2026-11-02T10:25:21.580320", * MEAN_MOTION: 15.49150046, * ECCENTRICITY: 0.0007139, * INCLINATION: 51.6332, * RA_OF_ASC_NODE: 335.9757, * ARG_OF_PERICENTER: 339.3739, * MEAN_ANOMALY: 20.6961, * NORAD_CAT_ID: 25544, * BSTAR: 0.00012, * MEAN_MOTION_DOT: 0.0001, * MEAN_MOTION_DDOT: 0, * }; * const where = satelliteAt(iss, { latitude: 51.5, longitude: -0.13 }, Date.UTC(2026, 10, 3, 12)); * console.log(where?.compass, where?.altitude.toFixed(0)); * ``` */ export declare function satelliteAt(set: ElementSet, observer: Observer, ms: number, options?: Pick): (PassPoint & { sunlit: boolean; sunAltitude: number; magnitude: number | undefined; }) | undefined; /** * Every pass of the satellites over a place in a span of time, the ones a person can see or all of them. For each satellite the search steps through the span every 30 seconds, then narrows each rise, highest point and * set to a tenth of a second; for each pass it works out where the satellite is lit by the Sun, how dark the watcher's sky is, and how bright the satellite looks. It needs `satellite.js` (MIT, an optional peer dependency) and * imports in a browser or on a server. It is for satellites that rise and set within hours (low orbits); one that stays above the horizon (a geostationary one) has no passes. * * Passes depend on fresh elements: SGP4's error grows by about a second of time a day or two for the ISS, and a reboost (the ISS has one every month or two) moves the station by more than that all at once, so a pass is as good as the * elements are new. Each pass says how old they are (`elementAgeDays`) and flags `stale` after two weeks. * * @param sets - One element set or several, in CelesTrak's JSON format (`ElementSet`), fetched by your page. * @param observer - Where the watcher stands. * @param window - The span to look in, `from` to `to` in milliseconds since the Unix epoch; a pass is listed when its highest point is in it. * @param options - The lowest altitude, the lightest sky, whether to list the passes that cannot be seen, the limiting magnitude and your own standard magnitudes. * @returns The passes, earliest first, each with its start, highest point and end, the part that can be seen and why not when none can. * * @example * ```ts * import { findPasses } from "@johnmorrisdotca/chikyu-earth/passes"; * * const iss = { * OBJECT_NAME: "ISS (ZARYA)", * OBJECT_ID: "1998-067A", * EPOCH: "2026-11-02T10:25:21.580320", * MEAN_MOTION: 15.49150046, * ECCENTRICITY: 0.0007139, * INCLINATION: 51.6332, * RA_OF_ASC_NODE: 335.9757, * ARG_OF_PERICENTER: 339.3739, * MEAN_ANOMALY: 20.6961, * NORAD_CAT_ID: 25544, * BSTAR: 0.00012, * MEAN_MOTION_DOT: 0.0001, * MEAN_MOTION_DDOT: 0, * }; * const day = Date.UTC(2026, 10, 3); * const passes = findPasses(iss, { latitude: 51.5, longitude: -0.13 }, { from: day, to: day + 5 * 86_400_000 }, { all: true }); * console.log(passes.length > 0, passes[0]?.highest.compass); * ``` */ export declare function findPasses(sets: ElementSet | readonly ElementSet[], observer: Observer, window: PassWindow, options?: PassOptions): SatellitePass[]; /** * One sample along a pass: when, where in the sky, and whether the satellite is in sunlight (the track is drawn solid there and dashed in the shadow). * * @example * ```ts * import type { PathPoint } from "@johnmorrisdotca/chikyu-earth/passes"; * * const lit = (points: readonly PathPoint[]): number => points.filter((point) => point.sunlit).length; * console.log(typeof lit); * ``` */ export interface PathPoint { ms: number; azimuth: number; altitude: number; sunlit: boolean; } /** * The satellite's track across the sky for a pass: a point every `stepSeconds` from the pass's start to its end, with the first and last on the start and the end. Draw it with `skySvg`. * * @param set - The satellite's elements (the one the pass was found from). * @param observer - Where the watcher stands. * @param pass - A pass from `findPasses`. * @param stepSeconds - Seconds between points (5 by default). * @returns The points, in time order. * * @example * ```ts * import { findPasses, passPath } from "@johnmorrisdotca/chikyu-earth/passes"; * * const iss = { * OBJECT_NAME: "ISS (ZARYA)", * EPOCH: "2026-11-02T10:25:21.580320", * MEAN_MOTION: 15.49150046, * ECCENTRICITY: 0.0007139, * INCLINATION: 51.6332, * RA_OF_ASC_NODE: 335.9757, * ARG_OF_PERICENTER: 339.3739, * MEAN_ANOMALY: 20.6961, * NORAD_CAT_ID: 25544, * BSTAR: 0.00012, * MEAN_MOTION_DOT: 0.0001, * MEAN_MOTION_DDOT: 0, * }; * const place = { latitude: 51.5, longitude: -0.13 }; * const day = Date.UTC(2026, 10, 3); * const [first] = findPasses(iss, place, { from: day, to: day + 3 * 86_400_000 }, { all: true }); * console.log(first ? passPath(iss, place, first).length : 0); * ``` */ export declare function passPath(set: ElementSet, observer: Observer, pass: SatellitePass, stepSeconds?: number): PathPoint[]; /** * One point of a satellite's ground track: the place on the Earth straight under it. * * @example * ```ts * import type { GroundPoint } from "@johnmorrisdotca/chikyu-earth/passes"; * * const north = (points: readonly GroundPoint[]): boolean => points.every((point) => point.lat > 0); * console.log(typeof north); * ``` */ export interface GroundPoint { ms: number; /** Degrees north. */ lat: number; /** Degrees east, -180 to 180. */ lon: number; /** Height above the Earth's surface, kilometres. */ altitudeKm: number; } /** * The places straight under a satellite from one moment to another: its ground track. Use a pass's `start.ms` and `end.ms` for the track of one pass, to draw on a globe. * * @param set - The satellite's elements. * @param fromMs - The first moment, milliseconds since the Unix epoch. * @param toMs - The last moment. * @param stepSeconds - Seconds between points (10 by default). * @returns The points, in time order; a moment SGP4 gives no place for is left out. * * @example * ```ts * import { groundTrack } from "@johnmorrisdotca/chikyu-earth/passes"; * * const iss = { * OBJECT_NAME: "ISS (ZARYA)", * EPOCH: "2026-11-02T10:25:21.580320", * MEAN_MOTION: 15.49150046, * ECCENTRICITY: 0.0007139, * INCLINATION: 51.6332, * RA_OF_ASC_NODE: 335.9757, * ARG_OF_PERICENTER: 339.3739, * MEAN_ANOMALY: 20.6961, * NORAD_CAT_ID: 25544, * BSTAR: 0.00012, * MEAN_MOTION_DOT: 0.0001, * MEAN_MOTION_DDOT: 0, * }; * const noon = Date.UTC(2026, 10, 3, 12); * const track = groundTrack(iss, noon, noon + 10 * 60_000); * console.log(track.length, Math.abs(track[0]!.lat) < 52); * ``` */ export declare function groundTrack(set: ElementSet, fromMs: number, toMs: number, stepSeconds?: number): GroundPoint[];