Packages/MoonriseKit/Sources/Answers/ReferenceAnswers.swift
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}