first commit
This commit is contained in:
27
docs/geospatial/geo/universe/README.md
Normal file
27
docs/geospatial/geo/universe/README.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# @suaveplan/universe - Universe-scene aggregation client: typed solar / star / tidal / weather provider interfaces, observer-position discriminated union, coordinate-frame conversions, and binary packet codecs for celestial bodies and stars.
|
||||
|
||||
## Summary
|
||||
|
||||
Observer model, coordinate frames, wire protocol, and provider registry.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [client](./src/client/client.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [client](./src/client/client.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [frames](./src/frames/frames.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [frames](./src/frames/frames.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [observer](./src/observer/observer.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [observer](./src/observer/observer.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [packets](./src/packets/packets.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [packets](./src/packets/packets.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [providers](./src/providers/providers.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [providers](./src/providers/providers.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
<!-- end:auto-docs --> - [client](./src/client/client.md) — <!-- begin:auto-docs --> _(no submodules)_- [frames](./src/frames/frames.md) — <!-- begin:auto-docs --> _(no submodules)_- [frames](./src/frames/frames.md) — <!-- begin:auto-docs --> _(no submodules)_- [observer](./src/observer/observer.md) — <!-- begin:auto-docs --> _(no submodules)_- [observer](./src/observer/observer.md) — <!-- begin:auto-docs --> _(no submodules)_- [packets](./src/packets/packets.md) — <!-- begin:auto-docs --> _(no submodules)_- [packets](./src/packets/packets.md) — <!-- begin:auto-docs --> _(no submodules)_- [providers](./src/providers/providers.md) — <!-- begin:auto-docs --> _(no submodules)_- [providers](./src/providers/providers.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [client](./src/client/client.md) — <!-- begin:auto-docs --> _(no submodules)_- [frames](./src/frames/frames.md) — <!-- begin:auto-docs --> _(no submodules)_- [frames](./src/frames/frames.md) — <!-- begin:auto-docs --> _(no submodules)_- [observer](./src/observer/observer.md) — <!-- begin:auto-docs --> _(no submodules)_- [observer](./src/observer/observer.md) — <!-- begin:auto-docs --> _(no submodules)_- [packets](./src/packets/packets.md) — <!-- begin:auto-docs --> _(no submodules)_- [packets](./src/packets/packets.md) — <!-- begin:auto-docs --> _(no submodules)_- [providers](./src/providers/providers.md) — <!-- begin:auto-docs --> _(no submodules)_- [providers](./src/providers/providers.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [client](./src/client/client.md) — <!-- begin:auto-docs --> _(no submodules)_- [frames](./src/frames/frames.md) — <!-- begin:auto-docs --> _(no submodules)_- [frames](./src/frames/frames.md) — <!-- begin:auto-docs --> _(no submodules)_- [observer](./src/observer/observer.md) — <!-- begin:auto-docs --> _(no submodules)_- [observer](./src/observer/observer.md) — <!-- begin:auto-docs --> _(no submodules)_- [packets](./src/packets/packets.md) — <!-- begin:auto-docs --> _(no submodules)_- [packets](./src/packets/packets.md) — <!-- begin:auto-docs --> _(no submodules)_- [providers](./src/providers/providers.md) — <!-- begin:auto-docs --> _(no submodules)_- [providers](./src/providers/providers.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- [client](./src/client/client.md) — <!-- begin:auto-docs --> _(no submodules)_- [frames](./src/frames/frames.md) — <!-- begin:auto-docs --> _(no submodules)_ - [frames](./src/frames/frames.md) — <!-- begin:auto-docs --> _(no submodules)_- [observer](./src/observer/observer.md) — <!-- begin:auto-docs --> _(no submodules)_ - [observer](./src/observer/observer.md) — <!-- begin:auto-docs --> _(no submodules)_- [packets](./src/packets/packets.md) — <!-- begin:auto-docs --> _(no submodules)_ - [packets](./src/packets/packets.md) — <!-- begin:auto-docs --> _(no submodules)_- [providers](./src/providers/providers.md) — <!-- begin:auto-docs --> _(no submodules)_ - [providers](./src/providers/providers.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ - [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @suaveplan/universe
|
||||
```
|
||||
138
docs/geospatial/geo/universe/src/client/client.md
Normal file
138
docs/geospatial/geo/universe/src/client/client.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# UniverseClient
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`UniverseClient` is the single composition point that aggregates solar, star, tidal, and weather provider implementations into one cohesive `UniverseScene` snapshot for a given observer at a given instant. It owns no domain knowledge of its own — every datum on the returned scene is delegated to a provider the consumer registered. This indirection lets the same client power a real-time astronomy app, a deterministic game world, a server-rendered ephemeris page, or a unit test, simply by swapping providers.
|
||||
|
||||
The client is intentionally minimal: no caching, no retry, no fan-out; it propagates the caller's `AbortSignal` to every registered provider and assembles their results into one `UniverseScene` value. Tides are skipped when the observer is in space (no surface to compute against). Unregistered domains contribute empty arrays (`bodies`, `stars`) or are omitted entirely (`tides`, `weather`).
|
||||
|
||||
## Features
|
||||
|
||||
- Fluent registration: `registerSolar` / `registerStars` / `registerTides` / `registerWeather` chain on `this`.
|
||||
- One-shot scene assembly via `getScene(observer, time, signal?)`.
|
||||
- AbortSignal forwarded to every provider call.
|
||||
- Tides automatically skipped for `kind: "space"` observers.
|
||||
- Optional fields (`tides`, `weather`) only present when their provider is registered and produces a value.
|
||||
- Provider-agnostic — interfaces only, zero base classes.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { UniverseClient } from "@suaveplan/universe";
|
||||
|
||||
const client = new UniverseClient();
|
||||
const scene = await client.getScene(
|
||||
{ kind: "space", heliocentricVec: { x: 0, y: 0, z: 0 }, frame: "icrs" },
|
||||
new Date()
|
||||
);
|
||||
// scene.bodies === [], scene.stars === [], scene.tides/weather undefined
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { UniverseClient, type UniverseScene } from "@suaveplan/universe";
|
||||
import type {
|
||||
SolarProvider,
|
||||
StarProvider,
|
||||
WeatherProvider,
|
||||
} from "@suaveplan/universe";
|
||||
|
||||
const solar: SolarProvider = {
|
||||
async getBodies() {
|
||||
return [];
|
||||
},
|
||||
async getBody(name) {
|
||||
return {
|
||||
name,
|
||||
type: "star",
|
||||
altitude: 45,
|
||||
azimuth: 180,
|
||||
apparentMagnitude: -26.7,
|
||||
angularDiameterArcmin: 32,
|
||||
distanceAu: 1,
|
||||
heliocentricPosition: { x: 0, y: 0, z: 0 },
|
||||
};
|
||||
},
|
||||
async getSunrise() {
|
||||
return new Date("2026-01-01T06:00:00Z");
|
||||
},
|
||||
async getSunset() {
|
||||
return new Date("2026-01-01T18:00:00Z");
|
||||
},
|
||||
};
|
||||
|
||||
const stars: StarProvider = {
|
||||
async getVisibleStars() {
|
||||
return [];
|
||||
},
|
||||
async queryCone() {
|
||||
return [];
|
||||
},
|
||||
async getStar() {
|
||||
return null;
|
||||
},
|
||||
};
|
||||
|
||||
const weather: WeatherProvider = {
|
||||
async getCurrent() {
|
||||
return { temperature: 18, pressure: 1015, humidity: 60 };
|
||||
},
|
||||
};
|
||||
|
||||
const controller = new AbortController();
|
||||
const client = new UniverseClient()
|
||||
.registerSolar(solar)
|
||||
.registerStars(stars)
|
||||
.registerWeather(weather);
|
||||
|
||||
const scene: UniverseScene = await client.getScene(
|
||||
{
|
||||
kind: "surface",
|
||||
planet: {
|
||||
name: "Earth",
|
||||
mass: 1,
|
||||
radius: 1,
|
||||
polarRadius: 1,
|
||||
surfaceGravity: 1,
|
||||
rotationPeriod: 1,
|
||||
axialTilt: 23.4,
|
||||
atmosphere: null,
|
||||
satellites: [],
|
||||
},
|
||||
coord: { latitude: 51.5, longitude: -0.12 },
|
||||
},
|
||||
new Date(),
|
||||
controller.signal
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `class UniverseClient`
|
||||
|
||||
| Method | Returns | Description |
|
||||
|---|---|---|
|
||||
| `registerSolar(provider)` | `this` | Register a `SolarProvider`. |
|
||||
| `registerStars(provider)` | `this` | Register a `StarProvider`. |
|
||||
| `registerTides(provider)` | `this` | Register a `TidalProvider`. |
|
||||
| `registerWeather(provider)` | `this` | Register a `WeatherProvider`. |
|
||||
| `getScene(observer, time, signal?)` | `Promise<UniverseScene>` | Assemble the scene. Forwards `signal` to every provider. |
|
||||
|
||||
### `interface UniverseScene`
|
||||
|
||||
| Property | Type | Notes |
|
||||
|---|---|---|
|
||||
| `bodies` | `CelestialBody[]` | Empty when no solar provider. |
|
||||
| `stars` | `ObservedStar[]` | Empty when no star provider. |
|
||||
| `tides?` | `TideExtreme[]` | Omitted for space observers and when no tidal provider. |
|
||||
| `weather?` | `WeatherState` | Omitted when no weather provider. |
|
||||
|
||||
The client throws (propagates) any error a provider throws, including `AbortError` from a fired signal.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`UniverseClient` is a coordinator, not a cache. Each `getScene` call invokes every registered provider unconditionally (subject to the space-tide skip). Consumers that need memoisation, retry, or rate limiting should compose those policies into the providers themselves rather than wrapping the client. Re-exporting the four provider interfaces and `WeatherState` from this module (in addition to `@suaveplan/universe/providers/providers`) is intentional so consumers can `import { type SolarProvider } from "@suaveplan/universe"` without reaching into a sub-path.
|
||||
91
docs/geospatial/geo/universe/src/frames/frames.md
Normal file
91
docs/geospatial/geo/universe/src/frames/frames.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# Coordinate Frames
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Defines the `CoordinateFrame` discriminator (`"heliocentric" | "galactic" | "custom"`) and the conversion entry points used across the universe-family packages. The module is the single seam through which any astronomical coordinate transformation is requested, so consumers do not need to know which rotation matrix or epoch correction is in play — they hand off a `SphericalCoord`, name the source and target frames, and receive a `SphericalCoord` in the new frame.
|
||||
|
||||
The current implementation is a placeholder identity transform: `convertFrame`, `toHeliocentric`, and `toGalactic` return the input unchanged. The public surface and discriminator are stable; consumers can wire to it now and pick up real conversions when this package adds the rotation pipeline without touching any call site.
|
||||
|
||||
## Features
|
||||
|
||||
- `CoordinateFrame` string-literal union — exhaustively switchable.
|
||||
- `convertFrame(coord, from, to, observer?)` — generic frame-to-frame conversion entry point.
|
||||
- `toHeliocentric(coord, observer)` — convenience wrapper for the most common conversion (observer-relative to heliocentric).
|
||||
- `toGalactic(coord)` — observer-independent rotation into galactic coordinates.
|
||||
- Identity short-circuit when `from === to`.
|
||||
- Observer parameter accepts the full `ObserverPosition` discriminated union (surface / atmospheric / space).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { convertFrame } from "@suaveplan/universe";
|
||||
|
||||
const coord = { latitude: 10, longitude: 20 };
|
||||
const same = convertFrame(coord, "heliocentric", "heliocentric");
|
||||
// same === coord (identity short-circuit)
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
convertFrame,
|
||||
toGalactic,
|
||||
toHeliocentric,
|
||||
type CoordinateFrame,
|
||||
} from "@suaveplan/universe";
|
||||
|
||||
const target: CoordinateFrame = "galactic";
|
||||
const observer = {
|
||||
kind: "surface" as const,
|
||||
planet: {
|
||||
name: "Earth",
|
||||
mass: 1,
|
||||
radius: 1,
|
||||
polarRadius: 1,
|
||||
surfaceGravity: 1,
|
||||
rotationPeriod: 1,
|
||||
axialTilt: 23.4,
|
||||
atmosphere: null,
|
||||
satellites: [],
|
||||
},
|
||||
coord: { latitude: 51.5, longitude: -0.12 },
|
||||
};
|
||||
|
||||
const skyDirection = { latitude: 45, longitude: 270 };
|
||||
const heliocentric = toHeliocentric(skyDirection, observer);
|
||||
const galactic = toGalactic(heliocentric);
|
||||
const fromHelio = convertFrame(skyDirection, "heliocentric", target, observer);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `type CoordinateFrame`
|
||||
|
||||
`"heliocentric" | "galactic" | "custom"` — string-literal union safe to switch on with exhaustiveness checking.
|
||||
|
||||
### `convertFrame(coord, from, to, observer?)`
|
||||
|
||||
| Param | Type | Description |
|
||||
|---|---|---|
|
||||
| `coord` | `SphericalCoord` | Latitude/longitude pair in source frame. |
|
||||
| `from` | `CoordinateFrame` | Source frame. |
|
||||
| `to` | `CoordinateFrame` | Target frame. |
|
||||
| `observer` | `ObserverPosition?` | Required for any observer-dependent conversion (e.g. topocentric → heliocentric). |
|
||||
|
||||
Returns a `SphericalCoord` in the target frame. Returns `coord` unchanged when `from === to`.
|
||||
|
||||
### `toHeliocentric(coord, observer)`
|
||||
|
||||
Convenience wrapper that conceptually corresponds to `convertFrame(coord, observer-frame, "heliocentric", observer)`.
|
||||
|
||||
### `toGalactic(coord)`
|
||||
|
||||
Observer-independent rotation from equatorial / heliocentric into galactic coordinates.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The body of every function is currently the identity, which keeps the API contract stable while the rotation matrices (J2000 → galactic, ICRS → heliocentric ecliptic, axial-tilt / sidereal-time corrections) are implemented in follow-up changes. Consumers should treat `"custom"` as an extension point — any future conversion that requires caller-supplied basis vectors is expected to flow through `convertFrame(..., "custom", ...)`. Do not bake the identity behaviour into call sites: the contract is "returns the coordinate in the target frame," not "returns the coordinate unchanged."
|
||||
114
docs/geospatial/geo/universe/src/observer/observer.md
Normal file
114
docs/geospatial/geo/universe/src/observer/observer.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# Observer
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Re-exports the `ObserverPosition` discriminated union that every universe-family entry point consumes. The union has three branches — `SurfaceObserver`, `AtmosphericObserver`, and `SpaceObserver` — distinguished by a `kind` literal field so call sites can narrow exhaustively without runtime type tags or class hierarchies.
|
||||
|
||||
Centralising these types in one module gives `@suaveplan/universe/client/client`, providers, and frame-conversion utilities a single shape to consume. Because the source declarations live in `@suaveplan/types` (zero-runtime, types-only), this re-export adds no bundle weight; it exists so consumers can import observer shapes without crossing into the foundational types package directly.
|
||||
|
||||
## Features
|
||||
|
||||
- `ObserverPosition` — full discriminated union, switch on `kind`.
|
||||
- `SurfaceObserver` — planet-bound observer at a `{latitude, longitude}` coordinate.
|
||||
- `AtmosphericObserver` — same as surface but with `altitudeMetres` (planes, balloons, drones).
|
||||
- `SpaceObserver` — heliocentric vector + reference `frame` (e.g. `"icrs"`).
|
||||
- All three branches share the `kind` literal as the discriminator.
|
||||
- Pure types — zero runtime cost, tree-shaken to nothing.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import type { ObserverPosition } from "@suaveplan/universe";
|
||||
|
||||
const observer: ObserverPosition = {
|
||||
kind: "space",
|
||||
heliocentricVec: { x: 1, y: 0, z: 0 },
|
||||
frame: "icrs",
|
||||
};
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import type {
|
||||
AtmosphericObserver,
|
||||
ObserverPosition,
|
||||
SpaceObserver,
|
||||
SurfaceObserver,
|
||||
} from "@suaveplan/universe";
|
||||
|
||||
function describe(o: ObserverPosition): string {
|
||||
switch (o.kind) {
|
||||
case "surface":
|
||||
return `surface at ${o.coord.latitude},${o.coord.longitude}`;
|
||||
case "atmospheric":
|
||||
return `${o.altitudeMetres} m above ${o.planet.name}`;
|
||||
case "space":
|
||||
return `space, ${o.frame} vector ${JSON.stringify(o.heliocentricVec)}`;
|
||||
}
|
||||
}
|
||||
|
||||
const earth = {
|
||||
name: "Earth",
|
||||
mass: 1,
|
||||
radius: 1,
|
||||
polarRadius: 1,
|
||||
surfaceGravity: 1,
|
||||
rotationPeriod: 1,
|
||||
axialTilt: 23.4,
|
||||
atmosphere: null,
|
||||
satellites: [],
|
||||
};
|
||||
|
||||
const surface: SurfaceObserver = {
|
||||
kind: "surface",
|
||||
planet: earth,
|
||||
coord: { latitude: 51.5, longitude: -0.12 },
|
||||
};
|
||||
const balloon: AtmosphericObserver = {
|
||||
kind: "atmospheric",
|
||||
planet: earth,
|
||||
coord: { latitude: 51.5, longitude: -0.12 },
|
||||
altitudeMetres: 12000,
|
||||
};
|
||||
const probe: SpaceObserver = {
|
||||
kind: "space",
|
||||
heliocentricVec: { x: 1.0, y: 0.0, z: 0.0 },
|
||||
frame: "icrs",
|
||||
};
|
||||
|
||||
[surface, balloon, probe].forEach((o) => describe(o));
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `type SurfaceObserver`
|
||||
|
||||
| Property | Type | Description |
|
||||
|---|---|---|
|
||||
| `kind` | `"surface"` | Discriminator literal. |
|
||||
| `planet` | `PlanetConfig` | Host body (mass, radius, axial tilt, satellites …). |
|
||||
| `coord` | `{ latitude: number; longitude: number }` | Geographic location in degrees. |
|
||||
|
||||
### `type AtmosphericObserver`
|
||||
|
||||
Extends `SurfaceObserver` shape with an additional `altitudeMetres: number` and `kind: "atmospheric"`.
|
||||
|
||||
### `type SpaceObserver`
|
||||
|
||||
| Property | Type | Description |
|
||||
|---|---|---|
|
||||
| `kind` | `"space"` | Discriminator literal. |
|
||||
| `heliocentricVec` | `{ x: number; y: number; z: number }` | Cartesian position in AU. |
|
||||
| `frame` | `string` | Reference frame label, e.g. `"icrs"`. |
|
||||
|
||||
### `type ObserverPosition`
|
||||
|
||||
`SurfaceObserver | AtmosphericObserver | SpaceObserver` — exhaustive switch on `kind`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
These types are re-exported from `@suaveplan/types`; this module contains no runtime code and contributes nothing to the emitted bundle. Keeping the canonical declarations in `@suaveplan/types` preserves the universe-family rule that any observer-shaped value flowing through providers, the client, frame conversions, or packet codecs is structurally identical no matter which sibling package produced it.
|
||||
121
docs/geospatial/geo/universe/src/packets/packets.md
Normal file
121
docs/geospatial/geo/universe/src/packets/packets.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# Packets
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Compact little-endian binary codecs for the two highest-volume payloads in a universe scene: catalogues of observed stars and lists of observed celestial bodies. The codecs trade negligible accuracy for an order-of-magnitude reduction in bytes-on-the-wire compared to a JSON encoding, which matters when streaming thousands of stars to a renderer or syncing scene snapshots over a constrained transport.
|
||||
|
||||
Each codec is a pure pair of `pack(...)` / `unpack(...)` functions. They are deterministic, allocation-free aside from the one output `Uint8Array`, and produce identical bytes for identical input regardless of platform endianness because every `DataView` write is explicit `littleEndian = true`.
|
||||
|
||||
## Features
|
||||
|
||||
- `packStars` / `unpackStars` — encode `StarPacket[]` with quantised RA, Dec, magnitude, altitude, azimuth, and a UTF-8 spectral class.
|
||||
- `packBodies` / `unpackBodies` — encode `CelestialBodyPacket[]` with full `Float64` precision for altitude, azimuth, angular diameter, and apparent magnitude plus UTF-8 name and type.
|
||||
- Length-prefixed strings (per-element `uint32` length header) so variable-width identifiers are unambiguous.
|
||||
- Quantised star fields: RA in 1/10⁷° (uint32), Dec in 1/10⁷° (int32), magnitude × 100 with a +5 offset (uint16), altitude as int8 degrees, azimuth as uint8 (≈1.41° resolution).
|
||||
- Empty-array handling: 4-byte count-only buffer.
|
||||
- Self-describing count header so a receiver can preallocate.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { packStars, unpackStars, type StarPacket } from "@suaveplan/universe";
|
||||
|
||||
const stars: StarPacket[] = [
|
||||
{
|
||||
ra: 10,
|
||||
dec: 20,
|
||||
magnitude: 3.5,
|
||||
altitude: 45,
|
||||
azimuth: 180,
|
||||
spectralClass: "G2V",
|
||||
},
|
||||
];
|
||||
const bytes = packStars(stars);
|
||||
const round = unpackStars(bytes);
|
||||
// round[0].spectralClass === "G2V"
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
packBodies,
|
||||
packStars,
|
||||
unpackBodies,
|
||||
unpackStars,
|
||||
type CelestialBodyPacket,
|
||||
type StarPacket,
|
||||
} from "@suaveplan/universe";
|
||||
|
||||
function snapshot(
|
||||
stars: StarPacket[],
|
||||
bodies: CelestialBodyPacket[]
|
||||
): Uint8Array {
|
||||
const s = packStars(stars);
|
||||
const b = packBodies(bodies);
|
||||
const out = new Uint8Array(4 + s.length + 4 + b.length);
|
||||
new DataView(out.buffer).setUint32(0, s.length, true);
|
||||
out.set(s, 4);
|
||||
new DataView(out.buffer).setUint32(4 + s.length, b.length, true);
|
||||
out.set(b, 4 + s.length + 4);
|
||||
return out;
|
||||
}
|
||||
|
||||
function restore(buf: Uint8Array): {
|
||||
stars: StarPacket[];
|
||||
bodies: CelestialBodyPacket[];
|
||||
} {
|
||||
const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
||||
const sLen = view.getUint32(0, true);
|
||||
const stars = unpackStars(buf.subarray(4, 4 + sLen));
|
||||
const bodies = unpackBodies(buf.subarray(4 + sLen + 4));
|
||||
return { stars, bodies };
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `interface StarPacket`
|
||||
|
||||
| Field | Type | Quantisation |
|
||||
|---|---|---|
|
||||
| `ra` | `number` | uint32, `(ra + 180) × 10⁷` |
|
||||
| `dec` | `number` | int32, `dec × 10⁷` |
|
||||
| `magnitude` | `number` | uint16, `(mag + 5) × 100` |
|
||||
| `altitude` | `number` | int8 degrees |
|
||||
| `azimuth` | `number` | uint8, scaled by `255/360` |
|
||||
| `spectralClass` | `string` | UTF-8, length-prefixed |
|
||||
|
||||
### `packStars(stars: StarPacket[]): Uint8Array`
|
||||
|
||||
Returns the packed buffer. Layout: `uint32 count` then per-element `uint32 specLen` headers, then the payload region containing each spectral-class string followed by the 12-byte fixed-width quantised fields.
|
||||
|
||||
### `unpackStars(bytes: Uint8Array): StarPacket[]`
|
||||
|
||||
Inverse of `packStars`. Reverses every quantisation; small accuracy losses are inherent (azimuth ≈ ±0.7°, magnitude ≈ ±0.005, RA/Dec ≈ ±5×10⁻⁸°).
|
||||
|
||||
### `interface CelestialBodyPacket`
|
||||
|
||||
| Field | Type | Encoding |
|
||||
|---|---|---|
|
||||
| `name` | `string` | UTF-8, length-prefixed |
|
||||
| `type` | `string` | UTF-8, length-prefixed |
|
||||
| `altitude` | `number` | float64 |
|
||||
| `azimuth` | `number` | float64 |
|
||||
| `angularDiameterArcmin` | `number` | float64 |
|
||||
| `apparentMagnitude` | `number` | float64 |
|
||||
|
||||
### `packBodies(bodies: CelestialBodyPacket[]): Uint8Array`
|
||||
|
||||
Layout: `uint32 count`, per-element `uint32 nameLen` + `uint32 typeLen` headers, then per-element `name` + `type` bytes followed by 32 bytes of float64 fields.
|
||||
|
||||
### `unpackBodies(bytes: Uint8Array): CelestialBodyPacket[]`
|
||||
|
||||
Inverse of `packBodies` — fully lossless for the float64 fields.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Star encoding is quantised because telescope-grade precision is not the use case; renderers, mini-maps, and HUDs are. The `+ 180` offset on RA and `+ 5` offset on magnitude let the smallest unsigned integer types cover the full physical range without negative-number gymnastics. Body encoding stays at float64 because the population is small (handfuls of objects per scene) and keeping ephemeris precision avoids visible jitter when the same body is repeatedly encoded and decoded over a long-running session. Both codecs use little-endian writes unconditionally so the wire format is platform-independent.
|
||||
139
docs/geospatial/geo/universe/src/providers/providers.md
Normal file
139
docs/geospatial/geo/universe/src/providers/providers.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# Providers
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Provider interfaces are the integration surface between `UniverseClient` and any data source — a real-time ephemeris service, a static catalogue, a mocked provider in a test, or a deterministic game-engine implementation. Each interface exposes only the operations the client actually consumes and is shaped so a reasonable mock can be hand-written in a few lines.
|
||||
|
||||
This module is a re-export of the canonical provider contracts from `@suaveplan/types`. Keeping the declarations in `@suaveplan/types` ensures every consumer in the universe family — client, frame conversions, packet codecs, sibling packages — sees structurally identical types without forming a dependency cycle. Consumers may import from either `@suaveplan/universe` (the package root re-exports the same names) or `@suaveplan/universe/providers/providers` for explicit sub-path imports.
|
||||
|
||||
## Features
|
||||
|
||||
- `SolarProvider` — body positions, sunrise, sunset, single-body lookup.
|
||||
- `StarProvider` — visible stars for an observer, cone queries in catalogue space, single-star lookup.
|
||||
- `TidalProvider` — height prediction and high/low extreme events for a planet/coordinate.
|
||||
- `WeatherProvider` — current conditions snapshot for an observer.
|
||||
- `WeatherState` — value object returned by `WeatherProvider.getCurrent`.
|
||||
- Every async method accepts an optional `AbortSignal` so callers can cancel propagation through the entire client.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import type { SolarProvider } from "@suaveplan/universe";
|
||||
|
||||
const mockSun: SolarProvider = {
|
||||
async getBodies() {
|
||||
return [];
|
||||
},
|
||||
async getBody(name) {
|
||||
return {
|
||||
name,
|
||||
type: "star",
|
||||
altitude: 45,
|
||||
azimuth: 180,
|
||||
apparentMagnitude: -26.7,
|
||||
angularDiameterArcmin: 32,
|
||||
distanceAu: 1,
|
||||
heliocentricPosition: { x: 0, y: 0, z: 0 },
|
||||
};
|
||||
},
|
||||
async getSunrise() {
|
||||
return new Date("2026-01-01T06:00:00Z");
|
||||
},
|
||||
async getSunset() {
|
||||
return new Date("2026-01-01T18:00:00Z");
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { UniverseClient } from "@suaveplan/universe";
|
||||
import type {
|
||||
SolarProvider,
|
||||
StarProvider,
|
||||
TidalProvider,
|
||||
WeatherProvider,
|
||||
} from "@suaveplan/universe";
|
||||
|
||||
declare const realSolar: SolarProvider;
|
||||
declare const realStars: StarProvider;
|
||||
declare const realTides: TidalProvider;
|
||||
declare const realWeather: WeatherProvider;
|
||||
|
||||
const client = new UniverseClient()
|
||||
.registerSolar(realSolar)
|
||||
.registerStars(realStars)
|
||||
.registerTides(realTides)
|
||||
.registerWeather(realWeather);
|
||||
|
||||
const controller = new AbortController();
|
||||
setTimeout(() => controller.abort(), 5000);
|
||||
|
||||
const scene = await client.getScene(
|
||||
{
|
||||
kind: "surface",
|
||||
planet: {
|
||||
name: "Earth",
|
||||
mass: 1,
|
||||
radius: 1,
|
||||
polarRadius: 1,
|
||||
surfaceGravity: 1,
|
||||
rotationPeriod: 1,
|
||||
axialTilt: 23.4,
|
||||
atmosphere: null,
|
||||
satellites: [],
|
||||
},
|
||||
coord: { latitude: 51.5, longitude: -0.12 },
|
||||
},
|
||||
new Date(),
|
||||
controller.signal
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `interface SolarProvider`
|
||||
|
||||
| Method | Returns | Description |
|
||||
|---|---|---|
|
||||
| `getBodies(observer, time, signal?)` | `Promise<CelestialBody[]>` | All major bodies for the observer. |
|
||||
| `getBody(name, observer, time, signal?)` | `Promise<CelestialBody>` | Single named body. |
|
||||
| `getSunrise(observer, date, signal?)` | `Promise<Date>` | Sunrise UTC for the date. |
|
||||
| `getSunset(observer, date, signal?)` | `Promise<Date>` | Sunset UTC for the date. |
|
||||
|
||||
### `interface StarProvider`
|
||||
|
||||
| Method | Returns | Description |
|
||||
|---|---|---|
|
||||
| `getVisibleStars(observer, options?, signal?)` | `Promise<ObservedStar[]>` | Brightness-sorted stars above the observer's horizon. |
|
||||
| `queryCone(ra, dec, radiusDeg, signal?)` | `Promise<CatalogStar[]>` | Catalog-space cone query. |
|
||||
| `getStar(catalogId, signal?)` | `Promise<CatalogStar \| null>` | Lookup by catalogue identifier. |
|
||||
|
||||
### `interface TidalProvider`
|
||||
|
||||
| Method | Returns | Description |
|
||||
|---|---|---|
|
||||
| `predictHeight(planet, coord, time, signal?)` | `Promise<number>` | Tidal height in metres above chart datum. |
|
||||
| `getExtremes(planet, coord, date, signal?)` | `Promise<TideExtreme[]>` | High and low events for the date. |
|
||||
|
||||
### `interface WeatherProvider`
|
||||
|
||||
| Method | Returns | Description |
|
||||
|---|---|---|
|
||||
| `getCurrent(observer, signal?)` | `Promise<WeatherState>` | Snapshot of current conditions. |
|
||||
|
||||
### `interface WeatherState`
|
||||
|
||||
| Property | Type | Description |
|
||||
|---|---|---|
|
||||
| `temperature` | `number` | Degrees Celsius. |
|
||||
| `pressure` | `number` | Hectopascals. |
|
||||
| `humidity` | `number` | Percent (0 – 100). |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
All four contracts are interfaces — there is no abstract base class, no decorator, and no required adapter to implement them. This keeps mocks trivial and lets consumers register remote-API wrappers, deterministic game providers, in-memory caches, or stub fixtures interchangeably. `UniverseClient` does not retry, cache, or fan out; consumers that need those policies should compose them inside the provider implementations so cross-cutting behaviour is contained and testable in isolation. The `signal` parameter is plumbed unconditionally — providers should propagate it into any underlying fetch / database call so an aborted scene request cancels in-flight work.
|
||||
137
docs/geospatial/geo/universe/src/types/types.md
Normal file
137
docs/geospatial/geo/universe/src/types/types.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# Types
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Re-exports the canonical domain types shared across the universe-family packages: celestial bodies as observed from a position, catalogue and observed stars, query options for star lookups, and tide-extreme events plus their option bundles. These are pure types — zero runtime — sourced from `@suaveplan/types`, where they live so every sibling package, every provider implementation, and every transport / packet codec can refer to one structurally identical shape.
|
||||
|
||||
The module is the place to look first when wiring a new provider or codec: every value flowing through `UniverseClient`, `packStars`, `packBodies`, frame conversions, and provider methods is built from these shapes. Consumers should never redefine local copies, because providers and the client are defined to consume exactly these types and any structural drift would break interface compatibility silently.
|
||||
|
||||
## Features
|
||||
|
||||
- `CelestialBody` — observed body with altitude, azimuth, magnitude, angular diameter, distance, and heliocentric position.
|
||||
- `CatalogStar` — catalog-space star (RA, Dec, magnitude, optional spectral class / distance / proper motion / parallax).
|
||||
- `ObservedStar` — `CatalogStar` with observer-dependent altitude, azimuth, and extincted magnitude.
|
||||
- `StarQueryOptions` — filters for visible-star queries (`minAltitudeDeg`, `maxMagnitude`, `limit`).
|
||||
- `TideExtreme` — high or low water event with metric height and UTC timestamp.
|
||||
- `TidePredictionOptions` and `TideExtremeOptions` — bundle types for tidal-provider call sites.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import type { CelestialBody } from "@suaveplan/universe";
|
||||
|
||||
const venus: CelestialBody = {
|
||||
name: "Venus",
|
||||
type: "planet",
|
||||
altitude: 30,
|
||||
azimuth: 180,
|
||||
apparentMagnitude: -4.5,
|
||||
angularDiameterArcmin: 0.5,
|
||||
distanceAu: 0.28,
|
||||
heliocentricPosition: { x: 0.5, y: 0, z: 0 },
|
||||
};
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import type {
|
||||
CatalogStar,
|
||||
ObservedStar,
|
||||
StarQueryOptions,
|
||||
TideExtreme,
|
||||
} from "@suaveplan/universe";
|
||||
|
||||
const polaris: CatalogStar = {
|
||||
catalogId: "HIP_11767",
|
||||
ra: 37.95,
|
||||
dec: 89.26,
|
||||
apparentMagnitude: 2.0,
|
||||
spectralClass: "F7:Ib-IIv",
|
||||
distance: 430,
|
||||
properMotionRa: 43.5,
|
||||
properMotionDec: -11.9,
|
||||
parallax: 7.56,
|
||||
};
|
||||
|
||||
const observed: ObservedStar = {
|
||||
...polaris,
|
||||
altitude: 51.5,
|
||||
azimuth: 0,
|
||||
extinctedMagnitude: 2.1,
|
||||
};
|
||||
|
||||
const options: StarQueryOptions = {
|
||||
minAltitudeDeg: 10,
|
||||
maxMagnitude: 5,
|
||||
limit: 250,
|
||||
};
|
||||
|
||||
const extreme: TideExtreme = {
|
||||
type: "high",
|
||||
height: 5.2,
|
||||
time: new Date("2026-06-01T12:00:00Z"),
|
||||
};
|
||||
|
||||
void [observed, options, extreme];
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `interface CelestialBody`
|
||||
|
||||
| Property | Type | Description |
|
||||
|---|---|---|
|
||||
| `name` | `string` | Common name. |
|
||||
| `type` | `string` | Category (`star`, `planet`, `moon`, `dwarf-planet`, …). |
|
||||
| `altitude` | `number` | Degrees above horizon. |
|
||||
| `azimuth` | `number` | Degrees clockwise from north. |
|
||||
| `apparentMagnitude` | `number` | Visual magnitude. |
|
||||
| `angularDiameterArcmin` | `number` | Arcminutes. |
|
||||
| `distanceAu` | `number` | Distance in astronomical units. |
|
||||
| `heliocentricPosition` | `{ x: number; y: number; z: number }` | Cartesian position in AU. |
|
||||
|
||||
### `interface CatalogStar`
|
||||
|
||||
| Property | Type | Description |
|
||||
|---|---|---|
|
||||
| `catalogId` | `string` | Unique identifier (e.g. Hipparcos). |
|
||||
| `ra` | `number` | Right ascension (0 – 360°). |
|
||||
| `dec` | `number` | Declination (–90 to +90°). |
|
||||
| `apparentMagnitude` | `number` | Visual magnitude. |
|
||||
| `spectralClass?` | `string` | e.g. `"G2V"`. |
|
||||
| `distance?` | `number` | Parsecs. |
|
||||
| `properMotionRa?` | `number` | mas/yr. |
|
||||
| `properMotionDec?` | `number` | mas/yr. |
|
||||
| `parallax?` | `number` | milliarcseconds. |
|
||||
|
||||
### `interface ObservedStar` extends `CatalogStar`
|
||||
|
||||
Adds `altitude: number`, `azimuth: number`, and `extinctedMagnitude: number` (visual magnitude after atmospheric extinction).
|
||||
|
||||
### `interface StarQueryOptions`
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `minAltitudeDeg?` | `number` | `0` | Minimum altitude. |
|
||||
| `maxMagnitude?` | `number` | `6` | Faintest magnitude. |
|
||||
| `limit?` | `number` | — | Maximum results. |
|
||||
|
||||
### `interface TideExtreme`
|
||||
|
||||
| Property | Type | Description |
|
||||
|---|---|---|
|
||||
| `type` | `"high" \| "low"` | Event type. |
|
||||
| `height` | `number` | Metres above chart datum. |
|
||||
| `time` | `Date` | UTC timestamp. |
|
||||
|
||||
### `interface TidePredictionOptions` / `interface TideExtremeOptions`
|
||||
|
||||
Argument bundles consumed by `TidalProvider.predictHeight` and `TidalProvider.getExtremes`. See `@suaveplan/types` for the canonical fields (planet, coordinate, time / date).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
All declarations are `readonly`-style interfaces; consumers should treat them as immutable value objects. Providers compute these shapes; the client and codecs read them. Re-exporting (rather than duplicating) ensures this package contributes zero additional runtime bytes — the universe-family packages collectively share one canonical type for every domain concept, which is what allows `packStars(unpackStars(packStars(x)))` to round-trip through arbitrary transports without coercion glue.
|
||||
Reference in New Issue
Block a user