krz/moonrise

Night-sky companion for iPhone, Apple Watch and Vision Pro: what's up tonight, the Moon, planets, launches and missions, and a Library of the solar system. SwiftUI app with a Cloudflare Workers backend. astronomy cloudflare-workers ios night-sky space swiftui visionos watchos

Packages/MoonriseKit/Sources/Answers/ReferenceAnswers.swift

59d401dca1a327705632d1e2e20f00f807dee828
moonrise/Packages/MoonriseKit/Sources/Answers/ReferenceAnswers.swift history · blame · raw

60 lines · 2500 bytes

4 symbols in this file
 1import Foundation
 2import Library
 3import SkyEngine
 4import SkyRenderer
 5
 6/// The five deep-space probes in `Spacecraft.all`, by name.
 7public enum Probe: String, CaseIterable, Sendable {
 8    case voyager1 = "Voyager 1", voyager2 = "Voyager 2", pioneer10 = "Pioneer 10", pioneer11 = "Pioneer 11", newHorizons = "New Horizons"
 9}
10
11public enum ProbeAnswerError: Error {
12    case noData
13}
14
15/// A probe's distance from Earth and the Sun, and the one-way light time.
16public struct ProbeAnswer: Sendable {
17    public var probe: Probe
18    public var fromEarthKm: Double
19    public var fromSunAU: Double
20    /// Seconds for light to cross `fromEarthKm`.
21    public var lightTime: TimeInterval
22    public var rows: [AnswerRow]
23    public var spoken: String
24
25    public init(probe: Probe, at date: Date, locale: Locale = .current) throws {
26        let say = Phrasing(locale: locale, timeZone: .current)
27        guard let craft = Spacecraft.all.first(where: { $0.name == probe.rawValue }), let p = craft.position3(at: date) else { throw ProbeAnswerError.noData }
28        let d = p - (try SkyEngine.heliocentricEarth(at: date))
29        let au = (d * d).sum().squareRoot()
30        self.probe = probe
31        fromEarthKm = au * 149_597_870.7
32        fromSunAU = (p * p).sum().squareRoot()
33        lightTime = au * 499.004784
34        let billions = "\(say.number(fromEarthKm / 1e9, digits: 1)) billion km"
35        rows = [
36            AnswerRow("From Earth", billions),
37            AnswerRow("From the Sun", "\(say.number(fromSunAU, digits: 1)) AU"),
38            AnswerRow("Light time", say.duration(lightTime, spoken: false)),
39        ]
40        var lines = ["\(probe.rawValue) is \(billions) from Earth. Its signal takes \(say.duration(lightTime, spoken: true)) to reach us."]
41        if probe == .pioneer10 || probe == .pioneer11 { lines.append("Contact was lost, so this is a prediction.") }
42        spoken = lines.sentences(nil)
43    }
44}
45
46/// A Library page's subtitle and first three facts.
47public struct LookUpAnswer: Sendable {
48    public var page: LibraryPage
49    public var subtitle: String
50    public var rows: [AnswerRow]
51    public var spoken: String
52
53    public init(page: LibraryPage) {
54        self.page = page
55        subtitle = page.subtitle.replacingOccurrences(of: " · ", with: ", ")
56        let facts = page.facts.prefix(3)
57        rows = facts.map { AnswerRow($0.label, $0.value) }
58        spoken = (["\(page.title): \(subtitle)."] + facts.map { "\($0.label): \($0.value)." }).sentences(nil)
59    }
60}